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.
 
 
 
 
 
 

608 lines
17 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.util.BitSet;
import parser.ast.Expression;
import parser.ast.ExpressionProb;
import parser.ast.ExpressionReward;
import parser.ast.ExpressionSS;
import parser.ast.RewardStruct;
import prism.ModelType;
import prism.PrismException;
import prism.PrismSettings;
import explicit.rewards.ConstructRewards;
import explicit.rewards.MCRewards;
import explicit.rewards.MDPRewards;
/**
* Super class for explicit-state probabilistic model checkers
*/
public class ProbModelChecker extends StateModelChecker
{
// Flags/settings
// (NB: defaults do not necessarily coincide with PRISM)
// Method used to solve linear equation systems
protected LinEqMethod linEqMethod = LinEqMethod.GAUSS_SEIDEL;
// Method used to solve MDPs
protected MDPSolnMethod mdpSolnMethod = MDPSolnMethod.GAUSS_SEIDEL;
// Iterative numerical method termination criteria
protected TermCrit termCrit = TermCrit.RELATIVE;
// Parameter for iterative numerical method termination criteria
protected double termCritParam = 1e-8;
// Max iterations for numerical solution
protected int maxIters = 100000;
// Use precomputation algorithms in model checking?
protected boolean precomp = true;
protected boolean prob0 = true;
protected boolean prob1 = true;
// Direction of convergence for value iteration (lfp/gfp)
protected ValIterDir valIterDir = ValIterDir.BELOW;
// Method used for numerical solution
protected SolnMethod solnMethod = SolnMethod.VALUE_ITERATION;
// Adversary export
protected boolean exportAdv = false;
protected String exportAdvFilename;
// Enums for flags/settings
// Method used for numerical solution
public enum LinEqMethod {
POWER, JACOBI, GAUSS_SEIDEL, BACKWARDS_GAUSS_SEIDEL, JOR, SOR, BACKWARDS_SOR;
public String fullName()
{
switch (this) {
case POWER:
return "Power method";
case JACOBI:
return "Jacobi";
case GAUSS_SEIDEL:
return "Gauss-Seidel";
case BACKWARDS_GAUSS_SEIDEL:
return "Backwards Gauss-Seidel";
case JOR:
return "JOR";
case SOR:
return "SOR";
case BACKWARDS_SOR:
return "Backwards SOR";
default:
return this.toString();
}
}
};
// Method used for solving MDPs
public enum MDPSolnMethod {
VALUE_ITERATION, GAUSS_SEIDEL, POLICY_ITERATION, MODIFIED_POLICY_ITERATION, LINEAR_PROGRAMMING;
public String fullName()
{
switch (this) {
case VALUE_ITERATION:
return "Value iteration";
case GAUSS_SEIDEL:
return "Gauss-Seidel";
case POLICY_ITERATION:
return "Policy iteration";
case MODIFIED_POLICY_ITERATION:
return "Modified policy iteration";
case LINEAR_PROGRAMMING:
return "Linear programming";
default:
return this.toString();
}
}
};
// Iterative numerical method termination criteria
public enum TermCrit {
ABSOLUTE, RELATIVE
};
// Direction of convergence for value iteration (lfp/gfp)
public enum ValIterDir {
BELOW, ABOVE
};
// Method used for numerical solution
public enum SolnMethod {
VALUE_ITERATION, GAUSS_SEIDEL, POLICY_ITERATION, MODIFIED_POLICY_ITERATION, LINEAR_PROGRAMMING
};
// Settings methods
/**
* Set settings from a PRISMSettings object.
*/
public void setSettings(PrismSettings settings) throws PrismException
{
String s;
// PRISM_LIN_EQ_METHOD
s = settings.getString(PrismSettings.PRISM_LIN_EQ_METHOD);
if (s.equals("Power")) {
setLinEqMethod(LinEqMethod.POWER);
} else if (s.equals("Jacobi")) {
setLinEqMethod(LinEqMethod.JACOBI);
} else if (s.equals("Gauss-Seidel")) {
setLinEqMethod(LinEqMethod.GAUSS_SEIDEL);
} else if (s.equals("Backwards Gauss-Seidel")) {
setLinEqMethod(LinEqMethod.BACKWARDS_GAUSS_SEIDEL);
} else if (s.equals("JOR")) {
setLinEqMethod(LinEqMethod.JOR);
} else if (s.equals("SOR")) {
setLinEqMethod(LinEqMethod.SOR);
} else if (s.equals("Backwards SOR")) {
setLinEqMethod(LinEqMethod.BACKWARDS_SOR);
} else {
throw new PrismException("Explicit engine does not support linear equation solution method \"" + s + "\"");
}
// PRISM_MDP_SOLN_METHOD
s = settings.getString(PrismSettings.PRISM_MDP_SOLN_METHOD);
if (s.equals("Value iteration")) {
setMDPSolnMethod(MDPSolnMethod.VALUE_ITERATION);
} else if (s.equals("Gauss-Seidel")) {
setMDPSolnMethod(MDPSolnMethod.GAUSS_SEIDEL);
} else if (s.equals("Policy iteration")) {
setMDPSolnMethod(MDPSolnMethod.POLICY_ITERATION);
} else if (s.equals("Modified policy iteration")) {
setMDPSolnMethod(MDPSolnMethod.MODIFIED_POLICY_ITERATION);
} else if (s.equals("Linear programming")) {
setMDPSolnMethod(MDPSolnMethod.LINEAR_PROGRAMMING);
} else {
throw new PrismException("Explicit engine does not support MDP solution method \"" + s + "\"");
}
// PRISM_TERM_CRIT
s = settings.getString(PrismSettings.PRISM_TERM_CRIT);
if (s.equals("Absolute")) {
setTermCrit(TermCrit.ABSOLUTE);
} else if (s.equals("Relative")) {
setTermCrit(TermCrit.RELATIVE);
} else {
throw new PrismException("Unknown termination criterion \"" + s + "\"");
}
// PRISM_TERM_CRIT_PARAM
setTermCritParam(settings.getDouble(PrismSettings.PRISM_TERM_CRIT_PARAM));
// PRISM_MAX_ITERS
setMaxIters(settings.getInteger(PrismSettings.PRISM_MAX_ITERS));
// PRISM_PRECOMPUTATION
setPrecomp(settings.getBoolean(PrismSettings.PRISM_PRECOMPUTATION));
// PRISM_PROB0
setProb0(settings.getBoolean(PrismSettings.PRISM_PROB0));
// PRISM_PROB1
setProb1(settings.getBoolean(PrismSettings.PRISM_PROB1));
// PRISM_FAIRNESS
if (settings.getBoolean(PrismSettings.PRISM_FAIRNESS)) {
throw new PrismException("The explicit engine does not support model checking MDPs under fairness");
}
// PRISM_EXPORT_ADV
s = settings.getString(PrismSettings.PRISM_EXPORT_ADV);
if (!(s.equals("None")))
setExportAdv(true);
// PRISM_EXPORT_ADV_FILENAME
setExportAdvFilename(settings.getString(PrismSettings.PRISM_EXPORT_ADV_FILENAME));
}
/**
* Inherit settings (and other info) from another model checker object.
*/
public void inheritSettings(ProbModelChecker other)
{
super.inheritSettings(other);
setTermCrit(other.getTermCrit());
setTermCritParam(other.getTermCritParam());
setMaxIters(other.getMaxIters());
setPrecomp(other.getPrecomp());
setProb0(other.getProb0());
setProb1(other.getProb1());
setValIterDir(other.getValIterDir());
setSolnMethod(other.getSolnMethod());
}
/**
* Print summary of current settings.
*/
public void printSettings()
{
super.printSettings();
mainLog.print("linEqMethod = " + linEqMethod + " ");
mainLog.print("mdpSolnMethod = " + mdpSolnMethod + " ");
mainLog.print("termCrit = " + termCrit + " ");
mainLog.print("termCritParam = " + termCritParam + " ");
mainLog.print("maxIters = " + maxIters + " ");
mainLog.print("precomp = " + precomp + " ");
mainLog.print("prob0 = " + prob0 + " ");
mainLog.print("prob1 = " + prob1 + " ");
mainLog.print("valIterDir = " + valIterDir + " ");
mainLog.print("solnMethod = " + solnMethod + " ");
}
// Set methods for flags/settings
/**
* Set verbosity level, i.e. amount of output produced.
*/
public void setVerbosity(int verbosity)
{
this.verbosity = verbosity;
}
/**
* Set method used to solve linear equation systems.
*/
public void setLinEqMethod(LinEqMethod linEqMethod)
{
this.linEqMethod = linEqMethod;
}
/**
* Set method used to solve MDPs.
*/
public void setMDPSolnMethod(MDPSolnMethod mdpSolnMethod)
{
this.mdpSolnMethod = mdpSolnMethod;
}
/**
* Set termination criteria type for numerical iterative methods.
*/
public void setTermCrit(TermCrit termCrit)
{
this.termCrit = termCrit;
}
/**
* Set termination criteria parameter (epsilon) for numerical iterative methods.
*/
public void setTermCritParam(double termCritParam)
{
this.termCritParam = termCritParam;
}
/**
* Set maximum number of iterations for numerical iterative methods.
*/
public void setMaxIters(int maxIters)
{
this.maxIters = maxIters;
}
/**
* Set whether or not to use precomputation (Prob0, Prob1, etc.).
*/
public void setPrecomp(boolean precomp)
{
this.precomp = precomp;
}
/**
* Set whether or not to use Prob0 precomputation
*/
public void setProb0(boolean prob0)
{
this.prob0 = prob0;
}
/**
* Set whether or not to use Prob1 precomputation
*/
public void setProb1(boolean prob1)
{
this.prob1 = prob1;
}
/**
* Set direction of convergence for value iteration (lfp/gfp).
*/
public void setValIterDir(ValIterDir valIterDir)
{
this.valIterDir = valIterDir;
}
/**
* Set method used for numerical solution.
*/
public void setSolnMethod(SolnMethod solnMethod)
{
this.solnMethod = solnMethod;
}
public void setExportAdv(boolean exportAdv)
{
this.exportAdv = exportAdv;
}
public void setExportAdvFilename(String exportAdvFilename)
{
this.exportAdvFilename = exportAdvFilename;
}
// Get methods for flags/settings
public int getVerbosity()
{
return verbosity;
}
public LinEqMethod getLinEqMethod()
{
return linEqMethod;
}
public MDPSolnMethod getMDPSolnMethod()
{
return mdpSolnMethod;
}
public TermCrit getTermCrit()
{
return termCrit;
}
public double getTermCritParam()
{
return termCritParam;
}
public int getMaxIters()
{
return maxIters;
}
public boolean getPrecomp()
{
return precomp;
}
public boolean getProb0()
{
return prob0;
}
public boolean getProb1()
{
return prob1;
}
public ValIterDir getValIterDir()
{
return valIterDir;
}
public SolnMethod getSolnMethod()
{
return solnMethod;
}
// Model checking functions
@Override
public StateValues checkExpression(Model model, Expression expr) throws PrismException
{
StateValues res;
// P operator
if (expr instanceof ExpressionProb) {
res = checkExpressionProb(model, (ExpressionProb) expr);
}
// R operator
else if (expr instanceof ExpressionReward) {
res = checkExpressionReward(model, (ExpressionReward) expr);
}
// S operator
else if (expr instanceof ExpressionSS) {
throw new PrismException("Explicit engine does not yet handle the S operator");
}
// Otherwise, use the superclass
else {
res = super.checkExpression(model, expr);
}
return res;
}
/**
* Model check a P operator expression and return the values for all states.
*/
protected StateValues checkExpressionProb(Model model, ExpressionProb expr) throws PrismException
{
Expression pb; // Probability bound (expression)
double p = 0; // 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();
StateValues probs = null;
// Get info from prob operator
relOp = expr.getRelOp();
pb = expr.getProb();
if (pb != null) {
p = pb.evaluateDouble(constantValues);
if (p < 0 || p > 1)
throw new PrismException("Invalid probability bound " + p + " in P operator");
}
// For nondeterministic models, determine whether min or max probabilities needed
if (modelType.nondeterministic()) {
if (relOp.equals(">") || relOp.equals(">=") || relOp.equals("min=")) {
// min
min = true;
} else if (relOp.equals("<") || relOp.equals("<=") || relOp.equals("max=")) {
// max
min = false;
} else {
throw new PrismException("Can't use \"P=?\" for nondeterministic models; use \"Pmin=?\" or \"Pmax=?\"");
}
}
// Compute probabilities
switch (modelType) {
case CTMC:
probs = ((CTMCModelChecker) this).checkProbPathFormula(model, expr.getExpression());
break;
case CTMDP:
probs = ((CTMDPModelChecker) this).checkProbPathFormula(model, expr.getExpression(), min);
break;
case DTMC:
probs = ((DTMCModelChecker) this).checkProbPathFormula(model, expr.getExpression());
break;
case MDP:
probs = ((MDPModelChecker) this).checkProbPathFormula(model, expr.getExpression(), min);
break;
/*case STPG:
probs = ((STPGModelChecker) this).checkProbPathFormula(model, expr.getExpression(), min);
break;*/
default:
throw new PrismException("Cannot model check " + expr + " for a " + modelType);
}
// Print out probabilities
if (getVerbosity() > 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 {
BitSet sol = probs.getBitSetFromInterval(relOp, p);
probs.clear();
return StateValues.createFromBitSet(sol, model);
}
}
/**
* Model check an R operator expression and return the values for all states.
*/
protected StateValues checkExpressionReward(Model model, ExpressionReward expr) throws PrismException
{
Object rs; // Reward struct index
RewardStruct rewStruct = null; // Reward struct object
Expression rb; // Reward bound (expression)
double r = 0; // Reward bound (actual value)
String relOp; // Relational operator
boolean min = false; // For nondeterministic models, are we finding min (true) or max (false) rewards
ModelType modelType = model.getModelType();
StateValues rews = null;
MCRewards mcRewards = null;
MDPRewards mdpRewards = null;
int i;
// Get info from reward operator
rs = expr.getRewardStructIndex();
relOp = expr.getRelOp();
rb = expr.getReward();
if (rb != null) {
r = rb.evaluateDouble(constantValues);
if (r < 0)
throw new PrismException("Invalid reward bound " + r + " in R[] formula");
}
// For nondeterministic models, determine whether min or max rewards needed
if (modelType.nondeterministic()) {
if (relOp.equals(">") || relOp.equals(">=") || relOp.equals("min=")) {
// min
min = true;
} else if (relOp.equals("<") || relOp.equals("<=") || relOp.equals("max=")) {
// max
min = false;
} else {
throw new PrismException("Can't use \"R=?\" for nondeterministic models; use \"Rmin=?\" or \"Rmax=?\"");
}
}
// Get reward info
if (modulesFile == null)
throw new PrismException("No model file to obtain reward structures");
if (modulesFile.getNumRewardStructs() == 0)
throw new PrismException("Model has no rewards specified");
if (rs == null) {
rewStruct = modulesFile.getRewardStruct(0);
} else if (rs instanceof Expression) {
i = ((Expression) rs).evaluateInt(constantValues);
rs = new Integer(i); // for better error reporting below
rewStruct = modulesFile.getRewardStruct(i - 1);
} else if (rs instanceof String) {
rewStruct = modulesFile.getRewardStructByName((String) rs);
}
if (rewStruct == null)
throw new PrismException("Invalid reward structure index \"" + rs + "\"");
// Build rewards
ConstructRewards constructRewards = new ConstructRewards(mainLog);
switch (modelType) {
case CTMC:
case DTMC:
mcRewards = constructRewards.buildMCRewardStructure((DTMC) model, rewStruct, constantValues);
break;
case MDP:
mdpRewards = constructRewards.buildMDPRewardStructure((MDP) model, rewStruct, constantValues);
break;
default:
throw new PrismException("Cannot build rewards for " + modelType + "s");
}
// Compute rewards
mainLog.println("Building reward structure...");
switch (modelType) {
case CTMC:
rews = ((CTMCModelChecker) this).checkRewardFormula(model, mcRewards, expr.getExpression());
break;
case DTMC:
rews = ((DTMCModelChecker) this).checkRewardFormula(model, mcRewards, expr.getExpression());
break;
case MDP:
rews = ((MDPModelChecker) this).checkRewardFormula(model, mdpRewards, expr.getExpression(), min);
break;
default:
throw new PrismException("Cannot model check " + expr + " for " + modelType + "s");
}
// Print out probabilities
if (getVerbosity() > 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 {
BitSet sol = rews.getBitSetFromInterval(relOp, r);
rews.clear();
return StateValues.createFromBitSet(sol, model);
}
}
}