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.
1345 lines
43 KiB
1345 lines
43 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 explicit;
|
|
|
|
import java.io.File;
|
|
import java.util.*;
|
|
|
|
import prism.*;
|
|
import explicit.StateValues;
|
|
import explicit.rewards.*;
|
|
import parser.ast.*;
|
|
import parser.type.TypeDouble;
|
|
|
|
/**
|
|
* Explicit-state model checker for discrete-time Markov chains (DTMCs).
|
|
*/
|
|
public class DTMCModelChecker extends ProbModelChecker
|
|
{
|
|
// Model checking functions
|
|
|
|
/**
|
|
* Compute probabilities for the contents of a P operator.
|
|
*/
|
|
protected StateValues checkProbPathFormula(Model model, Expression expr) throws PrismException
|
|
{
|
|
// Test whether this is a simple path formula (i.e. PCTL)
|
|
// and then pass control to appropriate method.
|
|
if (expr.isSimplePathFormula()) {
|
|
return checkProbPathFormulaSimple(model, expr);
|
|
} else {
|
|
throw new PrismException("Explicit engine does not yet handle LTL-style path formulas");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compute probabilities for a simple, non-LTL path operator.
|
|
*/
|
|
protected StateValues checkProbPathFormulaSimple(Model model, Expression expr) throws PrismException
|
|
{
|
|
StateValues probs = null;
|
|
|
|
// Negation/parentheses
|
|
if (expr instanceof ExpressionUnaryOp) {
|
|
ExpressionUnaryOp exprUnary = (ExpressionUnaryOp) expr;
|
|
// Parentheses
|
|
if (exprUnary.getOperator() == ExpressionUnaryOp.PARENTH) {
|
|
// Recurse
|
|
probs = checkProbPathFormulaSimple(model, exprUnary.getOperand());
|
|
}
|
|
// Negation
|
|
else if (exprUnary.getOperator() == ExpressionUnaryOp.NOT) {
|
|
// Compute, then subtract from 1
|
|
probs = checkProbPathFormulaSimple(model, exprUnary.getOperand());
|
|
probs.timesConstant(-1.0);
|
|
probs.plusConstant(1.0);
|
|
}
|
|
}
|
|
// Temporal operators
|
|
else if (expr instanceof ExpressionTemporal) {
|
|
ExpressionTemporal exprTemp = (ExpressionTemporal) expr;
|
|
// Next
|
|
if (exprTemp.getOperator() == ExpressionTemporal.P_X) {
|
|
throw new PrismException("The explicit engine does not yet handle the next operator");
|
|
}
|
|
// Until
|
|
if (exprTemp.getOperator() == ExpressionTemporal.P_U) {
|
|
if (exprTemp.hasBounds()) {
|
|
probs = checkProbBoundedUntil(model, exprTemp);
|
|
} else {
|
|
probs = checkProbUntil(model, exprTemp);
|
|
}
|
|
}
|
|
// Anything else - convert to until and recurse
|
|
else {
|
|
probs = checkProbPathFormulaSimple(model, exprTemp.convertToUntilForm());
|
|
}
|
|
}
|
|
|
|
if (probs == null)
|
|
throw new PrismException("Unrecognised path operator in P operator");
|
|
|
|
return probs;
|
|
}
|
|
|
|
/**
|
|
* Compute probabilities for a bounded until operator.
|
|
*/
|
|
protected StateValues checkProbBoundedUntil(Model model, ExpressionTemporal expr) throws PrismException
|
|
{
|
|
int time;
|
|
BitSet b1, b2;
|
|
StateValues probs = null;
|
|
ModelCheckerResult res = null;
|
|
|
|
// get info from bounded until
|
|
time = expr.getUpperBound().evaluateInt(constantValues);
|
|
if (expr.upperBoundIsStrict())
|
|
time--;
|
|
if (time < 0) {
|
|
String bound = expr.upperBoundIsStrict() ? "<" + (time + 1) : "<=" + time;
|
|
throw new PrismException("Invalid bound " + bound + " in bounded until formula");
|
|
}
|
|
|
|
// model check operands first
|
|
b1 = checkExpression(model, expr.getOperand1()).getBitSet();
|
|
b2 = checkExpression(model, expr.getOperand2()).getBitSet();
|
|
|
|
// compute probabilities
|
|
|
|
// a trivial case: "U<=0"
|
|
if (time == 0) {
|
|
// prob is 1 in b2 states, 0 otherwise
|
|
probs = StateValues.createFromBitSetAsDoubles(b2, model);
|
|
} else {
|
|
res = computeBoundedUntilProbs((DTMC) model, b1, b2, time);
|
|
probs = StateValues.createFromDoubleArray(res.soln, model);
|
|
}
|
|
|
|
return probs;
|
|
}
|
|
|
|
/**
|
|
* Compute probabilities for an (unbounded) until operator.
|
|
*/
|
|
protected StateValues checkProbUntil(Model model, ExpressionTemporal expr) throws PrismException
|
|
{
|
|
BitSet b1, b2;
|
|
StateValues probs = null;
|
|
ModelCheckerResult res = null;
|
|
|
|
// model check operands first
|
|
b1 = checkExpression(model, expr.getOperand1()).getBitSet();
|
|
b2 = checkExpression(model, expr.getOperand2()).getBitSet();
|
|
|
|
// print out some info about num states
|
|
// mainLog.print("\nb1 = " + JDD.GetNumMintermsString(b1,
|
|
// allDDRowVars.n()));
|
|
// mainLog.print(" states, b2 = " + JDD.GetNumMintermsString(b2,
|
|
// allDDRowVars.n()) + " states\n");
|
|
|
|
res = computeUntilProbs((DTMC) model, b1, b2);
|
|
probs = StateValues.createFromDoubleArray(res.soln, model);
|
|
|
|
return probs;
|
|
}
|
|
|
|
/**
|
|
* Compute rewards for the contents of an R operator.
|
|
*/
|
|
protected StateValues checkRewardFormula(Model model, MCRewards modelRewards, Expression expr) throws PrismException
|
|
{
|
|
StateValues rewards = null;
|
|
|
|
if (expr instanceof ExpressionTemporal) {
|
|
ExpressionTemporal exprTemp = (ExpressionTemporal) expr;
|
|
switch (exprTemp.getOperator()) {
|
|
case ExpressionTemporal.R_F:
|
|
rewards = checkRewardReach(model, modelRewards, exprTemp);
|
|
break;
|
|
default:
|
|
throw new PrismException("Explicit engine does not yet handle the " + exprTemp.getOperatorSymbol() + " operator in the R operator");
|
|
}
|
|
}
|
|
|
|
if (rewards == null)
|
|
throw new PrismException("Unrecognised operator in R operator");
|
|
|
|
return rewards;
|
|
}
|
|
|
|
/**
|
|
* Compute rewards for a reachability reward operator.
|
|
*/
|
|
protected StateValues checkRewardReach(Model model, MCRewards modelRewards, ExpressionTemporal expr) throws PrismException
|
|
{
|
|
BitSet b;
|
|
StateValues rewards = null;
|
|
ModelCheckerResult res = null;
|
|
|
|
// model check operand first
|
|
b = checkExpression(model, expr.getOperand2()).getBitSet();
|
|
|
|
// print out some info about num states
|
|
// mainLog.print("\nb = " + JDD.GetNumMintermsString(b1,
|
|
// allDDRowVars.n()));
|
|
|
|
res = computeReachRewards((DTMC) model, modelRewards, b);
|
|
rewards = StateValues.createFromDoubleArray(res.soln, model);
|
|
|
|
return rewards;
|
|
}
|
|
|
|
/**
|
|
* Compute steady-state probabilities for an S operator.
|
|
*/
|
|
protected StateValues checkSteadyStateFormula(Model model, Expression expr) throws PrismException
|
|
{
|
|
BitSet b;
|
|
StateValues probs = null;
|
|
ModelCheckerResult res = null;
|
|
|
|
// Model check operand first
|
|
b = checkExpression(model, expr).getBitSet();
|
|
|
|
double multProbs[] = Utils.bitsetToDoubleArray(b, model.getNumStates());
|
|
res = computeSteadyStateBackwardsProbs((DTMC) model, multProbs);
|
|
probs = StateValues.createFromDoubleArray(res.soln, model);
|
|
|
|
return probs;
|
|
}
|
|
|
|
// Steady-state/transient probability computation
|
|
|
|
/**
|
|
* Compute steady-state probability distribution (forwards).
|
|
* Start from initial state (or uniform distribution over multiple initial states).
|
|
*/
|
|
public StateValues doSteadyState(DTMC dtmc) throws PrismException
|
|
{
|
|
return doSteadyState(dtmc, (StateValues) null);
|
|
}
|
|
|
|
/**
|
|
* Compute steady-state probability distribution (forwards).
|
|
* Optionally, use the passed in file initDistFile to give the initial probability distribution (time 0).
|
|
* If null, start from initial state (or uniform distribution over multiple initial states).
|
|
*/
|
|
public StateValues doSteadyState(DTMC dtmc, File initDistFile) throws PrismException
|
|
{
|
|
StateValues initDist = readDistributionFromFile(initDistFile, dtmc);
|
|
return doSteadyState(dtmc, initDist);
|
|
}
|
|
|
|
/**
|
|
* Compute steady-state probability distribution (forwards).
|
|
* Optionally, use the passed in vector initDist as the initial probability distribution (time 0).
|
|
* If null, start from initial state (or uniform distribution over multiple initial states).
|
|
* For reasons of efficiency, when a vector is passed in, it will be trampled over,
|
|
* so if you wanted it, take a copy.
|
|
* @param dtmc The DTMC
|
|
* @param initDist Initial distribution (will be overwritten)
|
|
*/
|
|
public StateValues doSteadyState(DTMC dtmc, StateValues initDist) throws PrismException
|
|
{
|
|
StateValues initDistNew = (initDist == null) ? buildInitialDistribution(dtmc) : initDist;
|
|
ModelCheckerResult res = computeSteadyStateProbs(dtmc, initDistNew.getDoubleArray());
|
|
return StateValues.createFromDoubleArray(res.soln, dtmc);
|
|
}
|
|
|
|
/**
|
|
* Compute transient probability distribution (forwards).
|
|
* Optionally, use the passed in vector initDist as the initial probability distribution (time step 0).
|
|
* If null, start from initial state (or uniform distribution over multiple initial states).
|
|
* For reasons of efficiency, when a vector is passed in, it will be trampled over,
|
|
* so if you wanted it, take a copy.
|
|
* @param dtmc The DTMC
|
|
* @param k Time step
|
|
* @param initDist Initial distribution (will be overwritten)
|
|
*/
|
|
public StateValues doTransient(DTMC dtmc, int k, double initDist[]) throws PrismException
|
|
{
|
|
throw new PrismException("Not implemented yet");
|
|
}
|
|
|
|
// Utility methods for probability distributions
|
|
|
|
/**
|
|
* Generate a probability distribution, stored as a StateValues object, from a file.
|
|
* If {@code distFile} is null, so is the return value.
|
|
*/
|
|
public StateValues readDistributionFromFile(File distFile, Model model) throws PrismException
|
|
{
|
|
StateValues dist = null;
|
|
|
|
if (distFile != null) {
|
|
mainLog.println("\nImporting probability distribution from file \"" + distFile + "\"...");
|
|
// Build an empty vector
|
|
dist = new StateValues(TypeDouble.getInstance(), model);
|
|
// Populate vector from file
|
|
dist.readFromFile(distFile);
|
|
}
|
|
|
|
return dist;
|
|
}
|
|
|
|
/**
|
|
* Build a probability distribution, stored as a StateValues object,
|
|
* from the initial states info of the current model: either probability 1 for
|
|
* the (single) initial state or equiprobable over multiple initial states.
|
|
* The type of storage (MTBDD or double vector) matches the current engine.
|
|
*/
|
|
public StateValues buildInitialDistribution(Model model) throws PrismException
|
|
{
|
|
StateValues dist = null;
|
|
|
|
// Build an empty vector
|
|
dist = new StateValues(TypeDouble.getInstance(), model);
|
|
// Populate vector (equiprobable over initial states)
|
|
double d = 1.0 / model.getNumInitialStates();
|
|
for (int in : model.getInitialStates()) {
|
|
dist.setDoubleValue(in, d);
|
|
}
|
|
|
|
return dist;
|
|
}
|
|
|
|
// Numerical computation functions
|
|
|
|
/**
|
|
* Compute reachability probabilities.
|
|
* i.e. compute the probability of reaching a state in {@code target}.
|
|
* @param dtmc The DTMC
|
|
* @param target Target states
|
|
*/
|
|
public ModelCheckerResult computeReachProbs(DTMC dtmc, BitSet target) throws PrismException
|
|
{
|
|
return computeReachProbs(dtmc, null, target, null, null);
|
|
}
|
|
|
|
/**
|
|
* Compute until probabilities.
|
|
* i.e. compute the probability of reaching a state in {@code target},
|
|
* while remaining in those in @{code remain}.
|
|
* @param dtmc The DTMC
|
|
* @param remain Remain in these states (optional: null means "all")
|
|
* @param target Target states
|
|
*/
|
|
public ModelCheckerResult computeUntilProbs(DTMC dtmc, BitSet remain, BitSet target) throws PrismException
|
|
{
|
|
return computeReachProbs(dtmc, remain, target, null, null);
|
|
}
|
|
|
|
/**
|
|
* Compute reachability/until probabilities.
|
|
* i.e. compute the min/max probability of reaching a state in {@code target},
|
|
* while remaining in those in @{code remain}.
|
|
* @param dtmc The DTMC
|
|
* @param remain Remain in these states (optional: null means "all")
|
|
* @param target Target states
|
|
* @param init Optionally, an initial solution vector (may be overwritten)
|
|
* @param known Optionally, a set of states for which the exact answer is known
|
|
* Note: if 'known' is specified (i.e. is non-null, 'init' must also be given and is used for the exact values.
|
|
*/
|
|
public ModelCheckerResult computeReachProbs(DTMC dtmc, BitSet remain, BitSet target, double init[], BitSet known) throws PrismException
|
|
{
|
|
ModelCheckerResult res = null;
|
|
BitSet no, yes;
|
|
int i, n, numYes, numNo;
|
|
long timer, timerProb0, timerProb1;
|
|
// Local copy of setting
|
|
LinEqMethod linEqMethod = this.linEqMethod;
|
|
|
|
// Switch to a supported method, if necessary
|
|
if (!(linEqMethod == LinEqMethod.POWER || linEqMethod == LinEqMethod.GAUSS_SEIDEL)) {
|
|
linEqMethod = LinEqMethod.GAUSS_SEIDEL;
|
|
mainLog.printWarning("Switching to linear equation solution method \"" + linEqMethod.fullName() + "\"");
|
|
}
|
|
|
|
// Start probabilistic reachability
|
|
timer = System.currentTimeMillis();
|
|
mainLog.println("Starting probabilistic reachability...");
|
|
|
|
// Check for deadlocks in non-target state (because breaks e.g. prob1)
|
|
dtmc.checkForDeadlocks(target);
|
|
|
|
// Store num states
|
|
n = dtmc.getNumStates();
|
|
|
|
// Optimise by enlarging target set (if more info is available)
|
|
if (init != null && known != null) {
|
|
BitSet targetNew = new BitSet(n);
|
|
for (i = 0; i < n; i++) {
|
|
targetNew.set(i, target.get(i) || (known.get(i) && init[i] == 1.0));
|
|
}
|
|
target = targetNew;
|
|
}
|
|
|
|
// Precomputation
|
|
timerProb0 = System.currentTimeMillis();
|
|
if (precomp && prob0) {
|
|
no = prob0(dtmc, remain, target);
|
|
} else {
|
|
no = new BitSet();
|
|
}
|
|
timerProb0 = System.currentTimeMillis() - timerProb0;
|
|
timerProb1 = System.currentTimeMillis();
|
|
if (precomp && prob1) {
|
|
yes = prob1(dtmc, remain, target);
|
|
} else {
|
|
yes = (BitSet) target.clone();
|
|
}
|
|
timerProb1 = System.currentTimeMillis() - timerProb1;
|
|
|
|
// Print results of precomputation
|
|
numYes = yes.cardinality();
|
|
numNo = no.cardinality();
|
|
mainLog.println("target=" + target.cardinality() + ", yes=" + numYes + ", no=" + numNo + ", maybe=" + (n - (numYes + numNo)));
|
|
|
|
// Compute probabilities
|
|
switch (linEqMethod) {
|
|
case POWER:
|
|
res = computeReachProbsValIter(dtmc, no, yes, init, known);
|
|
break;
|
|
case GAUSS_SEIDEL:
|
|
res = computeReachProbsGaussSeidel(dtmc, no, yes, init, known);
|
|
break;
|
|
default:
|
|
throw new PrismException("Unknown linear equation solution method " + linEqMethod.fullName());
|
|
}
|
|
|
|
// Finished probabilistic reachability
|
|
timer = System.currentTimeMillis() - timer;
|
|
mainLog.println("Probabilistic reachability took " + timer / 1000.0 + " seconds.");
|
|
|
|
// Update time taken
|
|
res.timeTaken = timer / 1000.0;
|
|
res.timeProb0 = timerProb0 / 1000.0;
|
|
res.timePre = (timerProb0 + timerProb1) / 1000.0;
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Prob0 precomputation algorithm.
|
|
* i.e. determine the states of a DTMC which, with probability 0,
|
|
* reach a state in {@code target}, while remaining in those in @{code remain}.
|
|
* @param mdp The MDP
|
|
* @param remain Remain in these states (optional: null means "all")
|
|
* @param target Target states
|
|
*/
|
|
public BitSet prob0(DTMC dtmc, BitSet remain, BitSet target)
|
|
{
|
|
int n, iters;
|
|
BitSet u, soln, unknown;
|
|
boolean u_done;
|
|
long timer;
|
|
|
|
// Start precomputation
|
|
timer = System.currentTimeMillis();
|
|
mainLog.println("Starting Prob0...");
|
|
|
|
// Special case: no target states
|
|
if (target.cardinality() == 0) {
|
|
soln = new BitSet(dtmc.getNumStates());
|
|
soln.set(0, dtmc.getNumStates());
|
|
return soln;
|
|
}
|
|
|
|
// Initialise vectors
|
|
n = dtmc.getNumStates();
|
|
u = new BitSet(n);
|
|
soln = new BitSet(n);
|
|
|
|
// Determine set of states actually need to perform computation for
|
|
unknown = new BitSet();
|
|
unknown.set(0, n);
|
|
unknown.andNot(target);
|
|
if (remain != null)
|
|
unknown.and(remain);
|
|
|
|
// Fixed point loop
|
|
iters = 0;
|
|
u_done = false;
|
|
// Least fixed point - should start from 0 but we optimise by
|
|
// starting from 'target', thus bypassing first iteration
|
|
u.or(target);
|
|
soln.or(target);
|
|
while (!u_done) {
|
|
iters++;
|
|
// Single step of Prob0
|
|
dtmc.prob0step(unknown, u, soln);
|
|
// Check termination
|
|
u_done = soln.equals(u);
|
|
// u = soln
|
|
u.clear();
|
|
u.or(soln);
|
|
}
|
|
|
|
// Negate
|
|
u.flip(0, n);
|
|
|
|
// Finished precomputation
|
|
timer = System.currentTimeMillis() - timer;
|
|
mainLog.print("Prob0");
|
|
mainLog.println(" took " + iters + " iterations and " + timer / 1000.0 + " seconds.");
|
|
|
|
return u;
|
|
}
|
|
|
|
/**
|
|
* Prob1 precomputation algorithm.
|
|
* i.e. determine the states of a DTMC which, with probability 1,
|
|
* reach a state in {@code target}, while remaining in those in @{code remain}.
|
|
* @param mdp The MDP
|
|
* @param remain Remain in these states (optional: null means "all")
|
|
* @param target Target states
|
|
*/
|
|
public BitSet prob1(DTMC dtmc, BitSet remain, BitSet target)
|
|
{
|
|
int n, iters;
|
|
BitSet u, v, soln, unknown;
|
|
boolean u_done, v_done;
|
|
long timer;
|
|
|
|
// Start precomputation
|
|
timer = System.currentTimeMillis();
|
|
mainLog.println("Starting Prob1...");
|
|
|
|
// Special case: no target states
|
|
if (target.cardinality() == 0) {
|
|
return new BitSet(dtmc.getNumStates());
|
|
}
|
|
|
|
// Initialise vectors
|
|
n = dtmc.getNumStates();
|
|
u = new BitSet(n);
|
|
v = new BitSet(n);
|
|
soln = new BitSet(n);
|
|
|
|
// Determine set of states actually need to perform computation for
|
|
unknown = new BitSet();
|
|
unknown.set(0, n);
|
|
unknown.andNot(target);
|
|
if (remain != null)
|
|
unknown.and(remain);
|
|
|
|
// Nested fixed point loop
|
|
iters = 0;
|
|
u_done = false;
|
|
// Greatest fixed point
|
|
u.set(0, n);
|
|
while (!u_done) {
|
|
v_done = false;
|
|
// Least fixed point - should start from 0 but we optimise by
|
|
// starting from 'target', thus bypassing first iteration
|
|
v.clear();
|
|
v.or(target);
|
|
soln.clear();
|
|
soln.or(target);
|
|
while (!v_done) {
|
|
iters++;
|
|
// Single step of Prob1
|
|
dtmc.prob1step(unknown, u, v, soln);
|
|
// Check termination (inner)
|
|
v_done = soln.equals(v);
|
|
// v = soln
|
|
v.clear();
|
|
v.or(soln);
|
|
}
|
|
// Check termination (outer)
|
|
u_done = v.equals(u);
|
|
// u = v
|
|
u.clear();
|
|
u.or(v);
|
|
}
|
|
|
|
// Finished precomputation
|
|
timer = System.currentTimeMillis() - timer;
|
|
mainLog.print("Prob1");
|
|
mainLog.println(" took " + iters + " iterations and " + timer / 1000.0 + " seconds.");
|
|
|
|
return u;
|
|
}
|
|
|
|
/**
|
|
* Compute reachability probabilities using value iteration.
|
|
* @param dtmc The DTMC
|
|
* @param no Probability 0 states
|
|
* @param yes Probability 1 states
|
|
* @param init Optionally, an initial solution vector (will be overwritten)
|
|
* @param known Optionally, a set of states for which the exact answer is known
|
|
* Note: if 'known' is specified (i.e. is non-null, 'init' must also be given and is used for the exact values.
|
|
*/
|
|
protected ModelCheckerResult computeReachProbsValIter(DTMC dtmc, BitSet no, BitSet yes, double init[], BitSet known) throws PrismException
|
|
{
|
|
ModelCheckerResult res;
|
|
BitSet unknown;
|
|
int i, n, iters;
|
|
double soln[], soln2[], tmpsoln[], initVal;
|
|
boolean done;
|
|
long timer;
|
|
|
|
// Start value iteration
|
|
timer = System.currentTimeMillis();
|
|
mainLog.println("Starting value iteration...");
|
|
|
|
// Store num states
|
|
n = dtmc.getNumStates();
|
|
|
|
// Create solution vector(s)
|
|
soln = new double[n];
|
|
soln2 = (init == null) ? new double[n] : init;
|
|
|
|
// Initialise solution vectors. Use (where available) the following in order of preference:
|
|
// (1) exact answer, if already known; (2) 1.0/0.0 if in yes/no; (3) passed in initial value; (4) initVal
|
|
// where initVal is 0.0 or 1.0, depending on whether we converge from below/above.
|
|
initVal = (valIterDir == ValIterDir.BELOW) ? 0.0 : 1.0;
|
|
if (init != null) {
|
|
if (known != null) {
|
|
for (i = 0; i < n; i++)
|
|
soln[i] = soln2[i] = known.get(i) ? init[i] : yes.get(i) ? 1.0 : no.get(i) ? 0.0 : init[i];
|
|
} else {
|
|
for (i = 0; i < n; i++)
|
|
soln[i] = soln2[i] = yes.get(i) ? 1.0 : no.get(i) ? 0.0 : init[i];
|
|
}
|
|
} else {
|
|
for (i = 0; i < n; i++)
|
|
soln[i] = soln2[i] = yes.get(i) ? 1.0 : no.get(i) ? 0.0 : initVal;
|
|
}
|
|
|
|
// Determine set of states actually need to compute values for
|
|
unknown = new BitSet();
|
|
unknown.set(0, n);
|
|
unknown.andNot(yes);
|
|
unknown.andNot(no);
|
|
if (known != null)
|
|
unknown.andNot(known);
|
|
|
|
// Start iterations
|
|
iters = 0;
|
|
done = false;
|
|
while (!done && iters < maxIters) {
|
|
iters++;
|
|
// Matrix-vector multiply
|
|
dtmc.mvMult(soln, soln2, unknown, false);
|
|
// Check termination
|
|
done = PrismUtils.doublesAreClose(soln, soln2, termCritParam, termCrit == TermCrit.ABSOLUTE);
|
|
// Swap vectors for next iter
|
|
tmpsoln = soln;
|
|
soln = soln2;
|
|
soln2 = tmpsoln;
|
|
}
|
|
|
|
// Finished value iteration
|
|
timer = System.currentTimeMillis() - timer;
|
|
mainLog.print("Value iteration");
|
|
mainLog.println(" took " + iters + " iterations and " + timer / 1000.0 + " seconds.");
|
|
|
|
// Non-convergence is an error
|
|
if (!done) {
|
|
String msg = "Iterative method did not converge within " + iters + " iterations.";
|
|
msg += "\nConsider using a different numerical method or increasing the maximum number of iterations";
|
|
throw new PrismException(msg);
|
|
}
|
|
|
|
// Return results
|
|
res = new ModelCheckerResult();
|
|
res.soln = soln;
|
|
res.numIters = iters;
|
|
res.timeTaken = timer / 1000.0;
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Compute reachability probabilities using Gauss-Seidel.
|
|
* @param dtmc The DTMC
|
|
* @param no Probability 0 states
|
|
* @param yes Probability 1 states
|
|
* @param init Optionally, an initial solution vector (will be overwritten)
|
|
* @param known Optionally, a set of states for which the exact answer is known
|
|
* Note: if 'known' is specified (i.e. is non-null, 'init' must also be given and is used for the exact values.
|
|
*/
|
|
protected ModelCheckerResult computeReachProbsGaussSeidel(DTMC dtmc, BitSet no, BitSet yes, double init[], BitSet known) throws PrismException
|
|
{
|
|
ModelCheckerResult res;
|
|
BitSet unknown;
|
|
int i, n, iters;
|
|
double soln[], initVal, maxDiff;
|
|
boolean done;
|
|
long timer;
|
|
|
|
// Start value iteration
|
|
timer = System.currentTimeMillis();
|
|
mainLog.println("Starting Gauss-Seidel...");
|
|
|
|
// Store num states
|
|
n = dtmc.getNumStates();
|
|
|
|
// Create solution vector
|
|
soln = (init == null) ? new double[n] : init;
|
|
|
|
// Initialise solution vector. Use (where available) the following in order of preference:
|
|
// (1) exact answer, if already known; (2) 1.0/0.0 if in yes/no; (3) passed in initial value; (4) initVal
|
|
// where initVal is 0.0 or 1.0, depending on whether we converge from below/above.
|
|
initVal = (valIterDir == ValIterDir.BELOW) ? 0.0 : 1.0;
|
|
if (init != null) {
|
|
if (known != null) {
|
|
for (i = 0; i < n; i++)
|
|
soln[i] = known.get(i) ? init[i] : yes.get(i) ? 1.0 : no.get(i) ? 0.0 : init[i];
|
|
} else {
|
|
for (i = 0; i < n; i++)
|
|
soln[i] = yes.get(i) ? 1.0 : no.get(i) ? 0.0 : init[i];
|
|
}
|
|
} else {
|
|
for (i = 0; i < n; i++)
|
|
soln[i] = yes.get(i) ? 1.0 : no.get(i) ? 0.0 : initVal;
|
|
}
|
|
|
|
// Determine set of states actually need to compute values for
|
|
unknown = new BitSet();
|
|
unknown.set(0, n);
|
|
unknown.andNot(yes);
|
|
unknown.andNot(no);
|
|
if (known != null)
|
|
unknown.andNot(known);
|
|
|
|
// Start iterations
|
|
iters = 0;
|
|
done = false;
|
|
while (!done && iters < maxIters) {
|
|
iters++;
|
|
// Matrix-vector multiply
|
|
maxDiff = dtmc.mvMultGS(soln, unknown, false, termCrit == TermCrit.ABSOLUTE);
|
|
// Check termination
|
|
done = maxDiff < termCritParam;
|
|
}
|
|
|
|
// Finished Gauss-Seidel
|
|
timer = System.currentTimeMillis() - timer;
|
|
mainLog.print("Gauss-Seidel");
|
|
mainLog.println(" took " + iters + " iterations and " + timer / 1000.0 + " seconds.");
|
|
|
|
// Non-convergence is an error
|
|
if (!done) {
|
|
String msg = "Iterative method did not converge within " + iters + " iterations.";
|
|
msg += "\nConsider using a different numerical method or increasing the maximum number of iterations";
|
|
throw new PrismException(msg);
|
|
}
|
|
|
|
// Return results
|
|
res = new ModelCheckerResult();
|
|
res.soln = soln;
|
|
res.numIters = iters;
|
|
res.timeTaken = timer / 1000.0;
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Compute bounded reachability probabilities.
|
|
* i.e. compute the probability of reaching a state in {@code target} within k steps.
|
|
* @param dtmc The DTMC
|
|
* @param target Target states
|
|
* @param k Bound
|
|
*/
|
|
public ModelCheckerResult computeBoundedReachProbs(DTMC dtmc, BitSet target, int k) throws PrismException
|
|
{
|
|
return computeBoundedReachProbs(dtmc, null, target, k, null, null);
|
|
}
|
|
|
|
/**
|
|
* Compute bounded until probabilities.
|
|
* i.e. compute the probability of reaching a state in {@code target},
|
|
* within k steps, and while remaining in states in @{code remain}.
|
|
* @param dtmc The DTMC
|
|
* @param remain Remain in these states (optional: null means "all")
|
|
* @param target Target states
|
|
* @param k Bound
|
|
*/
|
|
public ModelCheckerResult computeBoundedUntilProbs(DTMC dtmc, BitSet remain, BitSet target, int k) throws PrismException
|
|
{
|
|
return computeBoundedReachProbs(dtmc, remain, target, k, null, null);
|
|
}
|
|
|
|
/**
|
|
* Compute bounded reachability/until probabilities.
|
|
* i.e. compute the probability of reaching a state in {@code target},
|
|
* within k steps, and while remaining in states in @{code remain}.
|
|
* @param dtmc The DTMC
|
|
* @param remain Remain in these states (optional: null means "all")
|
|
* @param target Target states
|
|
* @param k Bound
|
|
* @param init Initial solution vector - pass null for default
|
|
* @param results Optional array of size b+1 to store (init state) results for each step (null if unused)
|
|
*/
|
|
public ModelCheckerResult computeBoundedReachProbs(DTMC dtmc, BitSet remain, BitSet target, int k, double init[], double results[]) throws PrismException
|
|
{
|
|
// TODO: implement until
|
|
|
|
ModelCheckerResult res = null;
|
|
int i, n, iters;
|
|
double soln[], soln2[], tmpsoln[];
|
|
long timer;
|
|
|
|
// Start bounded probabilistic reachability
|
|
timer = System.currentTimeMillis();
|
|
mainLog.println("Starting bounded probabilistic reachability...");
|
|
|
|
// Store num states
|
|
n = dtmc.getNumStates();
|
|
|
|
// Create solution vector(s)
|
|
soln = new double[n];
|
|
soln2 = (init == null) ? new double[n] : init;
|
|
|
|
// Initialise solution vectors. Use passed in initial vector, if present
|
|
if (init != null) {
|
|
for (i = 0; i < n; i++)
|
|
soln[i] = soln2[i] = target.get(i) ? 1.0 : init[i];
|
|
} else {
|
|
for (i = 0; i < n; i++)
|
|
soln[i] = soln2[i] = target.get(i) ? 1.0 : 0.0;
|
|
}
|
|
// Store intermediate results if required
|
|
// (compute min/max value over initial states for first step)
|
|
if (results != null) {
|
|
// TODO: whether this is min or max should be specified somehow
|
|
results[0] = Utils.minMaxOverArraySubset(soln2, dtmc.getInitialStates(), true);
|
|
}
|
|
|
|
// Start iterations
|
|
iters = 0;
|
|
while (iters < k) {
|
|
|
|
iters++;
|
|
// Matrix-vector multiply
|
|
dtmc.mvMult(soln, soln2, target, true);
|
|
// Store intermediate results if required
|
|
// (compute min/max value over initial states for this step)
|
|
if (results != null) {
|
|
// TODO: whether this is min or max should be specified somehow
|
|
results[iters] = Utils.minMaxOverArraySubset(soln2, dtmc.getInitialStates(), true);
|
|
}
|
|
// Swap vectors for next iter
|
|
tmpsoln = soln;
|
|
soln = soln2;
|
|
soln2 = tmpsoln;
|
|
}
|
|
|
|
// Finished bounded probabilistic reachability
|
|
timer = System.currentTimeMillis() - timer;
|
|
mainLog.print("Bounded probabilistic reachability");
|
|
mainLog.println(" took " + iters + " iterations and " + timer / 1000.0 + " seconds.");
|
|
|
|
// Return results
|
|
res = new ModelCheckerResult();
|
|
res.soln = soln;
|
|
res.lastSoln = soln2;
|
|
res.numIters = iters;
|
|
res.timeTaken = timer / 1000.0;
|
|
res.timePre = 0.0;
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Compute expected reachability rewards.
|
|
* @param dtmc The DTMC
|
|
* @param mcRewards The rewards
|
|
* @param target Target states
|
|
*/
|
|
public ModelCheckerResult computeReachRewards(DTMC dtmc, MCRewards mcRewards, BitSet target) throws PrismException
|
|
{
|
|
return computeReachRewards(dtmc, mcRewards, target, null, null);
|
|
}
|
|
|
|
/**
|
|
* Compute expected reachability rewards.
|
|
* @param dtmc The DTMC
|
|
* @param mcRewards The rewards
|
|
* @param target Target states
|
|
* @param init Optionally, an initial solution vector (may be overwritten)
|
|
* @param known Optionally, a set of states for which the exact answer is known
|
|
* Note: if 'known' is specified (i.e. is non-null, 'init' must also be given and is used for the exact values.
|
|
*/
|
|
public ModelCheckerResult computeReachRewards(DTMC dtmc, MCRewards mcRewards, BitSet target, double init[], BitSet known) throws PrismException
|
|
{
|
|
ModelCheckerResult res = null;
|
|
BitSet inf;
|
|
int i, n, numTarget, numInf;
|
|
long timer, timerProb1;
|
|
// Local copy of setting
|
|
LinEqMethod linEqMethod = this.linEqMethod;
|
|
|
|
// Switch to a supported method, if necessary
|
|
if (!(linEqMethod == LinEqMethod.POWER)) {
|
|
linEqMethod = LinEqMethod.POWER;
|
|
mainLog.printWarning("Switching to linear equation solution method \"" + linEqMethod.fullName() + "\"");
|
|
}
|
|
|
|
// Start expected reachability
|
|
timer = System.currentTimeMillis();
|
|
mainLog.println("Starting expected reachability...");
|
|
|
|
// Check for deadlocks in non-target state (because breaks e.g. prob1)
|
|
dtmc.checkForDeadlocks(target);
|
|
|
|
// Store num states
|
|
n = dtmc.getNumStates();
|
|
|
|
// Optimise by enlarging target set (if more info is available)
|
|
if (init != null && known != null) {
|
|
BitSet targetNew = new BitSet(n);
|
|
for (i = 0; i < n; i++) {
|
|
targetNew.set(i, target.get(i) || (known.get(i) && init[i] == 0.0));
|
|
}
|
|
target = targetNew;
|
|
}
|
|
|
|
// Precomputation (not optional)
|
|
timerProb1 = System.currentTimeMillis();
|
|
inf = prob1(dtmc, null, target);
|
|
inf.flip(0, n);
|
|
timerProb1 = System.currentTimeMillis() - timerProb1;
|
|
|
|
// Print results of precomputation
|
|
numTarget = target.cardinality();
|
|
numInf = inf.cardinality();
|
|
mainLog.println("target=" + numTarget + ", inf=" + numInf + ", rest=" + (n - (numTarget + numInf)));
|
|
|
|
// Compute rewards
|
|
switch (linEqMethod) {
|
|
case POWER:
|
|
res = computeReachRewardsValIter(dtmc, mcRewards, target, inf, init, known);
|
|
break;
|
|
default:
|
|
throw new PrismException("Unknown linear equation solution method " + linEqMethod.fullName());
|
|
}
|
|
|
|
// Finished expected reachability
|
|
timer = System.currentTimeMillis() - timer;
|
|
mainLog.println("Expected reachability took " + timer / 1000.0 + " seconds.");
|
|
|
|
// Update time taken
|
|
res.timeTaken = timer / 1000.0;
|
|
res.timePre = timerProb1 / 1000.0;
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Compute expected reachability rewards using value iteration.
|
|
* @param dtmc The DTMC
|
|
* @param mcRewards The rewards
|
|
* @param target Target states
|
|
* @param inf States for which reward is infinite
|
|
* @param init Optionally, an initial solution vector (will be overwritten)
|
|
* @param known Optionally, a set of states for which the exact answer is known
|
|
* Note: if 'known' is specified (i.e. is non-null, 'init' must also be given and is used for the exact values.
|
|
*/
|
|
protected ModelCheckerResult computeReachRewardsValIter(DTMC dtmc, MCRewards mcRewards, BitSet target, BitSet inf, double init[], BitSet known)
|
|
throws PrismException
|
|
{
|
|
ModelCheckerResult res;
|
|
BitSet unknown;
|
|
int i, n, iters;
|
|
double soln[], soln2[], tmpsoln[];
|
|
boolean done;
|
|
long timer;
|
|
|
|
// Start value iteration
|
|
timer = System.currentTimeMillis();
|
|
mainLog.println("Starting value iteration...");
|
|
|
|
// Store num states
|
|
n = dtmc.getNumStates();
|
|
|
|
// Create solution vector(s)
|
|
soln = new double[n];
|
|
soln2 = (init == null) ? new double[n] : init;
|
|
|
|
// Initialise solution vectors. Use (where available) the following in order of preference:
|
|
// (1) exact answer, if already known; (2) 0.0/infinity if in target/inf; (3) passed in initial value; (4) 0.0
|
|
if (init != null) {
|
|
if (known != null) {
|
|
for (i = 0; i < n; i++)
|
|
soln[i] = soln2[i] = known.get(i) ? init[i] : target.get(i) ? 0.0 : inf.get(i) ? Double.POSITIVE_INFINITY : init[i];
|
|
} else {
|
|
for (i = 0; i < n; i++)
|
|
soln[i] = soln2[i] = target.get(i) ? 0.0 : inf.get(i) ? Double.POSITIVE_INFINITY : init[i];
|
|
}
|
|
} else {
|
|
for (i = 0; i < n; i++)
|
|
soln[i] = soln2[i] = target.get(i) ? 0.0 : inf.get(i) ? Double.POSITIVE_INFINITY : 0.0;
|
|
}
|
|
|
|
// Determine set of states actually need to compute values for
|
|
unknown = new BitSet();
|
|
unknown.set(0, n);
|
|
unknown.andNot(target);
|
|
unknown.andNot(inf);
|
|
if (known != null)
|
|
unknown.andNot(known);
|
|
|
|
// Start iterations
|
|
iters = 0;
|
|
done = false;
|
|
while (!done && iters < maxIters) {
|
|
//mainLog.println(soln);
|
|
iters++;
|
|
// Matrix-vector multiply
|
|
dtmc.mvMultRew(soln, mcRewards, soln2, unknown, false);
|
|
// Check termination
|
|
done = PrismUtils.doublesAreClose(soln, soln2, termCritParam, termCrit == TermCrit.ABSOLUTE);
|
|
// Swap vectors for next iter
|
|
tmpsoln = soln;
|
|
soln = soln2;
|
|
soln2 = tmpsoln;
|
|
}
|
|
|
|
// Finished value iteration
|
|
timer = System.currentTimeMillis() - timer;
|
|
mainLog.print("Value iteration");
|
|
mainLog.println(" took " + iters + " iterations and " + timer / 1000.0 + " seconds.");
|
|
|
|
// Non-convergence is an error
|
|
if (!done) {
|
|
String msg = "Iterative method did not converge within " + iters + " iterations.";
|
|
msg += "\nConsider using a different numerical method or increasing the maximum number of iterations";
|
|
throw new PrismException(msg);
|
|
}
|
|
|
|
// Return results
|
|
res = new ModelCheckerResult();
|
|
res.soln = soln;
|
|
res.numIters = iters;
|
|
res.timeTaken = timer / 1000.0;
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Compute (forwards) steady-state probabilities
|
|
* i.e. compute the long-run probability of being in each state,
|
|
* assuming the initial distribution {@code initDist}.
|
|
* For space efficiency, the initial distribution vector will be modified and values over-written,
|
|
* so if you wanted it, take a copy.
|
|
* @param dtmc The DTMC
|
|
* @param initDist Initial distribution (will be overwritten)
|
|
*/
|
|
public ModelCheckerResult computeSteadyStateProbs(DTMC dtmc, double initDist[]) throws PrismException
|
|
{
|
|
ModelCheckerResult res;
|
|
BitSet startNot, bscc;
|
|
double probBSCCs[], solnProbs[], reachProbs[];
|
|
int n, numBSCCs = 0, allInOneBSCC;
|
|
long timer;
|
|
|
|
timer = System.currentTimeMillis();
|
|
|
|
// Store num states
|
|
n = dtmc.getNumStates();
|
|
// Create results vector
|
|
solnProbs = new double[n];
|
|
|
|
// Compute bottom strongly connected components (BSCCs)
|
|
SCCComputer sccComputer = SCCComputer.createSCCComputer(sccMethod, dtmc);
|
|
sccComputer.computeBSCCs();
|
|
List<BitSet> bsccs = sccComputer.getBSCCs();
|
|
BitSet notInBSCCs = sccComputer.getNotInBSCCs();
|
|
numBSCCs = bsccs.size();
|
|
|
|
// See which states in the initial distribution do *not* have non-zero prob
|
|
startNot = new BitSet();
|
|
for (int i = 0; i < n; i++) {
|
|
if (initDist[i] == 0)
|
|
startNot.set(i);
|
|
}
|
|
// Determine whether initial states are all in a single BSCC
|
|
allInOneBSCC = -1;
|
|
for (int b = 0; b < numBSCCs; b++) {
|
|
if (!bsccs.get(b).intersects(startNot)) {
|
|
allInOneBSCC = b;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If all initial states are in a single BSCC, it's easy...
|
|
// Just compute steady-state probabilities for the BSCC
|
|
if (allInOneBSCC != -1) {
|
|
mainLog.println("\nInitial states all in one BSCC (so no reachability probabilities computed)");
|
|
bscc = bsccs.get(allInOneBSCC);
|
|
computeSteadyStateProbsForBSCC(dtmc, bscc, solnProbs);
|
|
}
|
|
|
|
// Otherwise, have to consider all the BSCCs
|
|
else {
|
|
|
|
// Compute probability of reaching each BSCC from initial distribution
|
|
probBSCCs = new double[numBSCCs];
|
|
for (int b = 0; b < numBSCCs; b++) {
|
|
mainLog.println("\nComputing probability of reaching BSCC " + (b + 1));
|
|
bscc = bsccs.get(b);
|
|
// Compute probabilities
|
|
reachProbs = computeUntilProbs(dtmc, notInBSCCs, bscc).soln;
|
|
// Compute probability of reaching BSCC, which is dot product of
|
|
// vectors for initial distribution and probabilities of reaching it
|
|
probBSCCs[b] = 0.0;
|
|
for (int i = 0; i < n; i++) {
|
|
probBSCCs[b] += initDist[i] * reachProbs[i];
|
|
}
|
|
mainLog.print("\nProbability of reaching BSCC " + (b + 1) + ": " + probBSCCs[b] + "\n");
|
|
}
|
|
|
|
// Compute steady-state probabilities for each BSCC
|
|
for (int b = 0; b < numBSCCs; b++) {
|
|
mainLog.println("\nComputing steady-state probabilities for BSCC " + (b + 1));
|
|
bscc = bsccs.get(b);
|
|
// Compute steady-state probabilities for the BSCC
|
|
computeSteadyStateProbsForBSCC(dtmc, bscc, solnProbs);
|
|
// Multiply by BSCC reach prob
|
|
for (int i = bscc.nextSetBit(0); i >= 0; i = bscc.nextSetBit(i + 1))
|
|
solnProbs[i] *= probBSCCs[b];
|
|
}
|
|
}
|
|
|
|
// Return results
|
|
res = new ModelCheckerResult();
|
|
res.soln = solnProbs;
|
|
timer = System.currentTimeMillis() - timer;
|
|
res.timeTaken = timer / 1000.0;
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Perform (backwards) steady-state probabilities, as required for (e.g. CSL) model checking.
|
|
* Compute, for each initial state s, the sum over all states s'
|
|
* of the steady-state probability of being in s'
|
|
* multiplied by the corresponding probability in the vector {@code multProbs}.
|
|
* If {@code multProbs} is null, it is assumed to be all 1s.
|
|
* @param dtmc The DTMC
|
|
* @param multProbs Multiplication vector (optional: null means all 1s)
|
|
*/
|
|
public ModelCheckerResult computeSteadyStateBackwardsProbs(DTMC dtmc, double multProbs[]) throws PrismException
|
|
{
|
|
ModelCheckerResult res;
|
|
BitSet bscc;
|
|
double probBSCCs[], ssProbs[], reachProbs[], soln[];
|
|
int n, numBSCCs = 0;
|
|
long timer;
|
|
|
|
timer = System.currentTimeMillis();
|
|
|
|
// Store num states
|
|
n = dtmc.getNumStates();
|
|
|
|
// Compute bottom strongly connected components (BSCCs)
|
|
SCCComputer sccComputer = SCCComputer.createSCCComputer(sccMethod, dtmc);
|
|
sccComputer.computeBSCCs();
|
|
List<BitSet> bsccs = sccComputer.getBSCCs();
|
|
BitSet notInBSCCs = sccComputer.getNotInBSCCs();
|
|
numBSCCs = bsccs.size();
|
|
|
|
// Compute steady-state probability for each BSCC...
|
|
probBSCCs = new double[numBSCCs];
|
|
ssProbs = new double[n];
|
|
for (int b = 0; b < numBSCCs; b++) {
|
|
mainLog.println("\nComputing steady state probabilities for BSCC " + (b + 1));
|
|
bscc = bsccs.get(b);
|
|
// Compute steady-state probabilities for the BSCC
|
|
computeSteadyStateProbsForBSCC(dtmc, bscc, ssProbs);
|
|
// Compute weighted sum of probabilities with multProbs
|
|
probBSCCs[b] = 0.0;
|
|
if (multProbs == null) {
|
|
for (int i = bscc.nextSetBit(0); i >= 0; i = bscc.nextSetBit(i + 1)) {
|
|
probBSCCs[b] += ssProbs[i];
|
|
}
|
|
} else {
|
|
for (int i = bscc.nextSetBit(0); i >= 0; i = bscc.nextSetBit(i + 1)) {
|
|
probBSCCs[b] += multProbs[i] * ssProbs[i];
|
|
}
|
|
}
|
|
mainLog.print("\nValue for BSCC " + (b + 1) + ": " + probBSCCs[b] + "\n");
|
|
}
|
|
|
|
// Create/initialise prob vector
|
|
soln = new double[n];
|
|
for (int i = 0; i < n; i++) {
|
|
soln[i] = 0.0;
|
|
}
|
|
|
|
// If every state is in a BSCC, it's much easier...
|
|
if (notInBSCCs.isEmpty()) {
|
|
mainLog.println("\nAll states are in BSCCs (so no reachability probabilities computed)");
|
|
for (int b = 0; b < numBSCCs; b++) {
|
|
bscc = bsccs.get(b);
|
|
for (int i = bscc.nextSetBit(0); i >= 0; i = bscc.nextSetBit(i + 1))
|
|
soln[i] += probBSCCs[b];
|
|
}
|
|
}
|
|
|
|
// Otherwise we have to do more work...
|
|
else {
|
|
// Compute probabilities of reaching each BSCC...
|
|
for (int b = 0; b < numBSCCs; b++) {
|
|
// Skip BSCCs with zero probability
|
|
if (probBSCCs[b] == 0.0)
|
|
continue;
|
|
mainLog.println("\nComputing probabilities of reaching BSCC " + (b + 1));
|
|
bscc = bsccs.get(b);
|
|
// Compute probabilities
|
|
reachProbs = computeUntilProbs(dtmc, notInBSCCs, bscc).soln;
|
|
// Multiply by value for BSCC, add to total
|
|
for (int i = 0; i < n; i++) {
|
|
soln[i] += reachProbs[i] * probBSCCs[b];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return results
|
|
res = new ModelCheckerResult();
|
|
res.soln = soln;
|
|
timer = System.currentTimeMillis() - timer;
|
|
res.timeTaken = timer / 1000.0;
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Compute steady-state probabilities for a BSCC
|
|
* i.e. compute the long-run probability of being in each state of the BSCC.
|
|
* No initial distribution is specified since it does not affect the result.
|
|
* The result will be stored in the relevant portion of a full vector,
|
|
* whose size equals the number of states in the DTMC.
|
|
* Optionally, pass in an existing vector to be used for this purpose.
|
|
* @param dtmc The DTMC
|
|
* @param bscc The BSCC to be analysed
|
|
* @param result Storage for result (ignored if null)
|
|
*/
|
|
public ModelCheckerResult computeSteadyStateProbsForBSCC(DTMC dtmc, BitSet bscc, double result[]) throws PrismException
|
|
{
|
|
ModelCheckerResult res;
|
|
int n, iters;
|
|
double soln[], soln2[], tmpsoln[];
|
|
boolean done;
|
|
long timer;
|
|
|
|
// Start value iteration
|
|
timer = System.currentTimeMillis();
|
|
mainLog.println("Starting value iteration...");
|
|
|
|
// Store num states
|
|
n = dtmc.getNumStates();
|
|
|
|
// Create solution vector(s)
|
|
// Use the passed in vector, if present
|
|
soln = result == null ? new double[n] : result;
|
|
soln2 = new double[n];
|
|
|
|
// Initialise solution vectors. Equiprobable for BSCC states.
|
|
double equiprob = 1.0 / bscc.cardinality();
|
|
for (int i = bscc.nextSetBit(0); i >= 0; i = bscc.nextSetBit(i + 1))
|
|
soln[i] = soln2[i] = equiprob;
|
|
|
|
// Start iterations
|
|
iters = 0;
|
|
done = false;
|
|
while (!done && iters < maxIters) {
|
|
iters++;
|
|
// Matrix-vector multiply
|
|
dtmc.vmMult(soln, soln2);
|
|
// Check termination
|
|
done = PrismUtils.doublesAreClose(soln, soln2, termCritParam, termCrit == TermCrit.ABSOLUTE);
|
|
// Swap vectors for next iter
|
|
tmpsoln = soln;
|
|
soln = soln2;
|
|
soln2 = tmpsoln;
|
|
}
|
|
|
|
// Finished value iteration
|
|
timer = System.currentTimeMillis() - timer;
|
|
mainLog.print("Value iteration");
|
|
mainLog.println(" took " + iters + " iterations and " + timer / 1000.0 + " seconds.");
|
|
|
|
// Non-convergence is an error
|
|
if (!done) {
|
|
String msg = "Iterative method did not converge within " + iters + " iterations.";
|
|
msg += "\nConsider using a different numerical method or increasing the maximum number of iterations";
|
|
throw new PrismException(msg);
|
|
}
|
|
|
|
// Return results
|
|
res = new ModelCheckerResult();
|
|
res.soln = soln;
|
|
res.numIters = iters;
|
|
res.timeTaken = timer / 1000.0;
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Compute transient probabilities
|
|
* i.e. compute the probability of being in each state at time step {@code k},
|
|
* assuming the initial distribution {@code initDist}.
|
|
* For space efficiency, the initial distribution vector will be modified and values over-written,
|
|
* so if you wanted it, take a copy.
|
|
* @param dtmc The DTMC
|
|
* @param k Time step
|
|
* @param initDist Initial distribution (will be overwritten)
|
|
*/
|
|
public ModelCheckerResult computeTransientProbs(DTMC dtmc, int k, double initDist[]) throws PrismException
|
|
{
|
|
throw new PrismException("Not implemented yet");
|
|
}
|
|
|
|
/**
|
|
* Simple test program.
|
|
*/
|
|
public static void main(String args[])
|
|
{
|
|
DTMCModelChecker mc;
|
|
DTMCSimple dtmc;
|
|
ModelCheckerResult res;
|
|
BitSet target;
|
|
Map<String, BitSet> labels;
|
|
try {
|
|
mc = new DTMCModelChecker();
|
|
dtmc = new DTMCSimple();
|
|
dtmc.buildFromPrismExplicit(args[0]);
|
|
//System.out.println(dtmc);
|
|
labels = mc.loadLabelsFile(args[1]);
|
|
//System.out.println(labels);
|
|
target = labels.get(args[2]);
|
|
if (target == null)
|
|
throw new PrismException("Unknown label \"" + args[2] + "\"");
|
|
for (int i = 3; i < args.length; i++) {
|
|
if (args[i].equals("-nopre"))
|
|
mc.setPrecomp(false);
|
|
}
|
|
res = mc.computeReachProbs(dtmc, target);
|
|
System.out.println(res.soln[0]);
|
|
} catch (PrismException e) {
|
|
System.out.println(e);
|
|
}
|
|
}
|
|
}
|