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.
807 lines
28 KiB
807 lines
28 KiB
//==============================================================================
|
|
//
|
|
// Copyright (c) 2002-
|
|
// Authors:
|
|
// * Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford)
|
|
// * Joachim Klein <klein@tcs.inf.tu-dresden.de> (TU Dresden)
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
// 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.awt.Point;
|
|
import java.io.PrintStream;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.BitSet;
|
|
import java.util.Iterator;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Stack;
|
|
import java.util.Vector;
|
|
|
|
import parser.State;
|
|
import parser.VarList;
|
|
import parser.ast.Declaration;
|
|
import parser.ast.DeclarationInt;
|
|
import parser.ast.Expression;
|
|
import parser.ast.ExpressionBinaryOp;
|
|
import parser.ast.ExpressionLabel;
|
|
import parser.ast.ExpressionTemporal;
|
|
import parser.ast.ExpressionUnaryOp;
|
|
import parser.type.TypeBool;
|
|
import parser.type.TypePathBool;
|
|
import prism.ModelType;
|
|
import prism.PrismComponent;
|
|
import prism.PrismException;
|
|
import prism.PrismLangException;
|
|
import prism.PrismNotSupportedException;
|
|
import prism.PrismUtils;
|
|
import acceptance.AcceptanceGenRabin;
|
|
import acceptance.AcceptanceOmega;
|
|
import acceptance.AcceptanceRabin;
|
|
import acceptance.AcceptanceStreett;
|
|
import acceptance.AcceptanceType;
|
|
import automata.DA;
|
|
import automata.LTL2DA;
|
|
|
|
import common.IterableStateSet;
|
|
|
|
/**
|
|
* LTL model checking functionality
|
|
*/
|
|
public class LTLModelChecker extends PrismComponent
|
|
{
|
|
/** Make LTL product accessible as a Product */
|
|
public class LTLProduct<M extends Model> extends Product<M>
|
|
{
|
|
private int daSize;
|
|
private int invMap[];
|
|
private AcceptanceOmega acceptance;
|
|
|
|
public LTLProduct(M productModel, M originalModel, AcceptanceOmega acceptance, int daSize, int[] invMap)
|
|
{
|
|
super(productModel, originalModel);
|
|
this.daSize = daSize;
|
|
this.invMap = invMap;
|
|
this.acceptance = acceptance;
|
|
}
|
|
|
|
@Override
|
|
public int getModelState(int productState)
|
|
{
|
|
return invMap[productState] / daSize;
|
|
}
|
|
|
|
@Override
|
|
public int getAutomatonState(int productState)
|
|
{
|
|
return invMap[productState] % daSize;
|
|
}
|
|
|
|
public AcceptanceOmega getAcceptance() {
|
|
return acceptance;
|
|
}
|
|
|
|
public void setAcceptance(AcceptanceOmega acceptance) {
|
|
this.acceptance = acceptance;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a new LTLModelChecker, inherit basic state from parent (unless null).
|
|
*/
|
|
public LTLModelChecker(PrismComponent parent) throws PrismException
|
|
{
|
|
super(parent);
|
|
}
|
|
|
|
/**
|
|
* Returns {@code true} if expression {@code expr} is a formula that can be handled by
|
|
* LTLModelChecker for the given ModelType.
|
|
*/
|
|
public static boolean isSupportedLTLFormula(ModelType modelType, Expression expr) throws PrismLangException
|
|
{
|
|
if (!expr.isPathFormula(true)) {
|
|
return false;
|
|
}
|
|
if (Expression.containsTemporalTimeBounds(expr)) {
|
|
if (modelType.continuousTime()) {
|
|
// Only support temporal bounds for discrete time models
|
|
return false;
|
|
}
|
|
|
|
if (!expr.isSimplePathFormula()) {
|
|
// Only support temporal bounds for simple path formulas
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Extract maximal state formula from an LTL path formula, model check them (with passed in model checker) and
|
|
* replace them with ExpressionLabel objects L0, L1, etc. Expression passed in is modified directly, but the result
|
|
* is also returned. As an optimisation, expressions that results in true/false for all states are converted to an
|
|
* actual true/false, and duplicate results (or their negations) reuse the same label. BitSets giving the states which
|
|
* satisfy each label are put into the vector labelDDs, which should be empty when this function is called.
|
|
*/
|
|
public Expression checkMaximalStateFormulas(ProbModelChecker mc, Model model, Expression expr, Vector<BitSet> labelBS) throws PrismException
|
|
{
|
|
// A state formula
|
|
if (expr.getType() instanceof TypeBool) {
|
|
// Model check state formula for all states
|
|
StateValues sv = mc.checkExpression(model, expr, null);
|
|
BitSet bs = sv.getBitSet();
|
|
// Detect special cases (true, false) for optimisation
|
|
if (bs.isEmpty()) {
|
|
return Expression.False();
|
|
}
|
|
if (bs.cardinality() == model.getNumStates()) {
|
|
return Expression.True();
|
|
}
|
|
// See if we already have an identical result
|
|
// (in which case, reuse it)
|
|
int i = labelBS.indexOf(bs);
|
|
if (i != -1) {
|
|
sv.clear();
|
|
return new ExpressionLabel("L" + i);
|
|
}
|
|
// Also, see if we already have the negation of this result
|
|
// (in which case, reuse it)
|
|
BitSet bsNeg = new BitSet(model.getNumStates());
|
|
bsNeg.set(0, model.getNumStates());
|
|
bsNeg.andNot(bs);
|
|
i = labelBS.indexOf(bsNeg);
|
|
if (i != -1) {
|
|
sv.clear();
|
|
return Expression.Not(new ExpressionLabel("L" + i));
|
|
}
|
|
// Otherwise, add result to list, return new label
|
|
labelBS.add(bs);
|
|
return new ExpressionLabel("L" + (labelBS.size() - 1));
|
|
}
|
|
// A path formula (recurse, modify, return)
|
|
else if (expr.getType() instanceof TypePathBool) {
|
|
if (expr instanceof ExpressionBinaryOp) {
|
|
ExpressionBinaryOp exprBinOp = (ExpressionBinaryOp) expr;
|
|
exprBinOp.setOperand1(checkMaximalStateFormulas(mc, model, exprBinOp.getOperand1(), labelBS));
|
|
exprBinOp.setOperand2(checkMaximalStateFormulas(mc, model, exprBinOp.getOperand2(), labelBS));
|
|
} else if (expr instanceof ExpressionUnaryOp) {
|
|
ExpressionUnaryOp exprUnOp = (ExpressionUnaryOp) expr;
|
|
exprUnOp.setOperand(checkMaximalStateFormulas(mc, model, exprUnOp.getOperand(), labelBS));
|
|
} else if (expr instanceof ExpressionTemporal) {
|
|
ExpressionTemporal exprTemp = (ExpressionTemporal) expr;
|
|
if (exprTemp.getOperand1() != null) {
|
|
exprTemp.setOperand1(checkMaximalStateFormulas(mc, model, exprTemp.getOperand1(), labelBS));
|
|
}
|
|
if (exprTemp.getOperand2() != null) {
|
|
exprTemp.setOperand2(checkMaximalStateFormulas(mc, model, exprTemp.getOperand2(), labelBS));
|
|
}
|
|
}
|
|
}
|
|
return expr;
|
|
}
|
|
|
|
/**
|
|
* Construct a deterministic automaton (DA) for an LTL formula, having first extracted maximal state formulas
|
|
* and model checked them with the passed in model checker. The maximal state formulas are assigned labels
|
|
* (L0, L1, etc.) which become the atomic propositions in the resulting DA. BitSets giving the states which
|
|
* satisfy each label are put into the vector {@code labelBS}, which should be empty when this function is called.
|
|
*
|
|
* @param mc a ProbModelChecker, used for checking maximal state formulas
|
|
* @param model the model
|
|
* @param expr a path expression, i.e. the LTL formula
|
|
* @param labelBS empty vector to be filled with BitSets for subformulas
|
|
* @param allowedAcceptance the allowed acceptance types
|
|
* @return the DA
|
|
*/
|
|
public DA<BitSet,? extends AcceptanceOmega> constructDAForLTLFormula(ProbModelChecker mc, Model model, Expression expr, Vector<BitSet> labelBS, AcceptanceType... allowedAcceptance) throws PrismException
|
|
{
|
|
Expression ltl;
|
|
DA<BitSet,? extends AcceptanceOmega> da;
|
|
long time;
|
|
|
|
if (Expression.containsTemporalTimeBounds(expr)) {
|
|
if (model.getModelType().continuousTime()) {
|
|
throw new PrismException("Automaton construction for time-bounded operators not supported for " + model.getModelType()+".");
|
|
}
|
|
|
|
if (expr.isSimplePathFormula()) {
|
|
// Convert simple path formula to canonical form,
|
|
// DA is then generated by LTL2RabinLibrary.
|
|
// The conversion to canonical form has to happen here, because once
|
|
// checkMaximalStateFormulas has been called, the formula should not be modified
|
|
// anymore, as converters may expect that the generated labels for maximal state
|
|
// formulas only appear positively
|
|
expr = Expression.convertSimplePathFormulaToCanonicalForm(expr);
|
|
} else {
|
|
throw new PrismNotSupportedException("Time-bounded operators not supported in LTL: " + expr);
|
|
}
|
|
}
|
|
|
|
// Model check maximal state formulas
|
|
ltl = checkMaximalStateFormulas(mc, model, expr.deepCopy(), labelBS);
|
|
|
|
// Convert LTL formula to deterministic automaton
|
|
mainLog.println("\nBuilding deterministic automaton (for " + ltl + ")...");
|
|
time = System.currentTimeMillis();
|
|
LTL2DA ltl2da = new LTL2DA(this);
|
|
da = ltl2da.convertLTLFormulaToDA(ltl, mc.getConstantValues(), allowedAcceptance);
|
|
mainLog.println(da.getAutomataType()+" has " + da.size() + " states, " + da.getAcceptance().getSizeStatistics() + ".");
|
|
da.checkForCanonicalAPs(labelBS.size());
|
|
time = System.currentTimeMillis() - time;
|
|
mainLog.println("Time for "+da.getAutomataType()+" translation: " + time / 1000.0 + " seconds.");
|
|
// If required, export DA
|
|
if (settings.getExportPropAut()) {
|
|
mainLog.println("Exporting " + da.getAutomataType() + " to file \"" + settings.getExportPropAutFilename() + "\"...");
|
|
PrintStream out = PrismUtils.newPrintStream(settings.getExportPropAutFilename());
|
|
da.print(out, settings.getExportPropAutType());
|
|
out.close();
|
|
}
|
|
|
|
return da;
|
|
}
|
|
|
|
/**
|
|
* Generate a deterministic automaton for the given LTL formula
|
|
* and construct the product of this automaton with a Markov chain.
|
|
*
|
|
* @param mc a ProbModelChecker, used for checking maximal state formulas
|
|
* @param model the model
|
|
* @param expr a path expression
|
|
* @param statesOfInterest the set of states for which values should be calculated (null = all states)
|
|
* @param allowedAcceptance the allowed acceptance types
|
|
* @return the product with the DA
|
|
*/
|
|
public LTLProduct<DTMC> constructProductMC(ProbModelChecker mc, DTMC model, Expression expr, BitSet statesOfInterest, AcceptanceType... allowedAcceptance) throws PrismException
|
|
{
|
|
// Convert LTL formula to automaton
|
|
Vector<BitSet> labelBS = new Vector<BitSet>();
|
|
DA<BitSet,? extends AcceptanceOmega> da;
|
|
da = constructDAForLTLFormula(mc, model, expr, labelBS, allowedAcceptance);
|
|
|
|
// Build product of model and automaton
|
|
mainLog.println("\nConstructing MC-"+da.getAutomataType()+" product...");
|
|
LTLProduct<DTMC> product = constructProductModel(da, model, labelBS, statesOfInterest);
|
|
mainLog.print("\n" + product.getProductModel().infoStringTable());
|
|
|
|
return product;
|
|
}
|
|
|
|
/**
|
|
* Generate a deterministic automaton for the given LTL formula
|
|
* and construct the product of this automaton with an MDP.
|
|
*
|
|
* @param mc a ProbModelChecker, used for checking maximal state formulas
|
|
* @param model the model
|
|
* @param expr a path expression
|
|
* @param statesOfInterest the set of states for which values should be calculated (null = all states)
|
|
* @param allowedAcceptance the allowed acceptance conditions
|
|
* @return the product with the DA
|
|
* @throws PrismException
|
|
*/
|
|
public LTLProduct<MDP> constructProductMDP(ProbModelChecker mc, MDP model, Expression expr, BitSet statesOfInterest, AcceptanceType... allowedAcceptance) throws PrismException
|
|
{
|
|
// Convert LTL formula to automaton
|
|
Vector<BitSet> labelBS = new Vector<BitSet>();
|
|
DA<BitSet,? extends AcceptanceOmega> da;
|
|
da = constructDAForLTLFormula(mc, model, expr, labelBS, allowedAcceptance);
|
|
|
|
// Build product of model and automaton
|
|
mainLog.println("\nConstructing MDP-"+da.getAutomataType()+" product...");
|
|
LTLProduct<MDP> product = constructProductModel(da, model, labelBS, statesOfInterest);
|
|
mainLog.print("\n" + product.getProductModel().infoStringTable());
|
|
|
|
return product;
|
|
}
|
|
|
|
/**
|
|
* Generate a deterministic automaton for the given LTL formula
|
|
* and construct the product of this automaton with an STPG.
|
|
*
|
|
* @param mc a ProbModelChecker, used for checking maximal state formulas
|
|
* @param model the model
|
|
* @param expr a path expression
|
|
* @param statesOfInterest the set of states for which values should be calculated (null = all states)
|
|
* @param allowedAcceptance the allowed acceptance conditions
|
|
* @return the product with the DA
|
|
* @throws PrismException
|
|
*/
|
|
public LTLProduct<STPG> constructProductSTPG(ProbModelChecker mc, STPG model, Expression expr, BitSet statesOfInterest, AcceptanceType... allowedAcceptance) throws PrismException
|
|
{
|
|
// Convert LTL formula to automaton
|
|
Vector<BitSet> labelBS = new Vector<BitSet>();
|
|
DA<BitSet,? extends AcceptanceOmega> da;
|
|
da = constructDAForLTLFormula(mc, model, expr, labelBS, allowedAcceptance);
|
|
|
|
// Build product of model and automaton
|
|
mainLog.println("\nConstructing STPG-"+da.getAutomataType()+" product...");
|
|
LTLProduct<STPG> product = constructProductModel(da, model, labelBS, statesOfInterest);
|
|
mainLog.print("\n" + product.getProductModel().infoStringTable());
|
|
|
|
return product;
|
|
}
|
|
|
|
/**
|
|
* Generate a deterministic automaton for the given LTL formula
|
|
* and construct the product of this automaton with a model.
|
|
*
|
|
* @param mc a ProbModelChecker, used for checking maximal state formulas
|
|
* @param model the model
|
|
* @param expr a path expression
|
|
* @param statesOfInterest the set of states for which values should be calculated (null = all states)
|
|
* @param allowedAcceptance the allowed acceptance conditions
|
|
* @return the product with the DA
|
|
* @throws PrismException
|
|
*/
|
|
public <M extends Model> LTLProduct<M> constructProductModel(ProbModelChecker mc, M model, Expression expr, BitSet statesOfInterest, AcceptanceType... allowedAcceptance) throws PrismException
|
|
{
|
|
// Convert LTL formula to automaton
|
|
Vector<BitSet> labelBS = new Vector<BitSet>();
|
|
DA<BitSet,? extends AcceptanceOmega> da;
|
|
da = constructDAForLTLFormula(mc, model, expr, labelBS, allowedAcceptance);
|
|
|
|
// Build product of model and automaton
|
|
mainLog.println("\nConstructing " + model.getModelType() + "-" + da.getAutomataType() + " product...");
|
|
LTLProduct<M> product = constructProductModel(da, model, labelBS, statesOfInterest);
|
|
mainLog.print("\n" + product.getProductModel().infoStringTable());
|
|
|
|
return product;
|
|
}
|
|
|
|
/**
|
|
* Construct the product of a DA and a model.
|
|
* @param da The DA
|
|
* @param model The model
|
|
* @param labelBS BitSets giving the set of states for each AP in the DA
|
|
* @param statesOfInterest the set of states for which values should be calculated (null = all states)
|
|
* @return The product model
|
|
*/
|
|
public <M extends Model> LTLProduct<M> constructProductModel(DA<BitSet,? extends AcceptanceOmega> da, M model, Vector<BitSet> labelBS, BitSet statesOfInterest) throws PrismException
|
|
{
|
|
ModelType modelType = model.getModelType();
|
|
int daSize = da.size();
|
|
int numAPs = da.getAPList().size();
|
|
int modelNumStates = model.getNumStates();
|
|
int prodNumStates = modelNumStates * daSize;
|
|
int s_1, s_2, q_1, q_2;
|
|
BitSet s_labels = new BitSet(numAPs);
|
|
List<State> prodStatesList = null, daStatesList = null;
|
|
|
|
VarList newVarList = null;
|
|
|
|
if (model.getVarList() != null) {
|
|
VarList varList = model.getVarList();
|
|
// Create a (new, unique) name for the variable that will represent DA states
|
|
String daVar = "_da";
|
|
while (varList.getIndex(daVar) != -1) {
|
|
daVar = "_" + daVar;
|
|
}
|
|
|
|
newVarList = (VarList) varList.clone();
|
|
// NB: if DA only has one state, we add an extra dummy state
|
|
Declaration decl = new Declaration(daVar, new DeclarationInt(Expression.Int(0), Expression.Int(Math.max(da.size() - 1, 1))));
|
|
newVarList.addVar(0, decl, 1, model.getConstantValues());
|
|
}
|
|
|
|
// Create a (simple, mutable) model of the appropriate type
|
|
ModelSimple prodModel = null;
|
|
switch (modelType) {
|
|
case DTMC: {
|
|
DTMCSimple dtmcProd = new DTMCSimple();
|
|
dtmcProd.setVarList(newVarList);
|
|
prodModel = dtmcProd;
|
|
break;
|
|
}
|
|
case MDP: {
|
|
MDPSimple mdpProd = new MDPSimple();
|
|
mdpProd.setVarList(newVarList);
|
|
prodModel = mdpProd;
|
|
break;
|
|
}
|
|
case STPG: {
|
|
STPGExplicit stpgProd = new STPGExplicit();
|
|
stpgProd.setVarList(newVarList);
|
|
prodModel = stpgProd;
|
|
break;
|
|
}
|
|
default:
|
|
throw new PrismNotSupportedException("Model construction not supported for " + modelType + "s");
|
|
}
|
|
|
|
// Encoding:
|
|
// each state s' = <s, q> = s * daSize + q
|
|
// s(s') = s' / daSize
|
|
// q(s') = s' % daSize
|
|
|
|
LinkedList<Point> queue = new LinkedList<Point>();
|
|
int map[] = new int[prodNumStates];
|
|
Arrays.fill(map, -1);
|
|
|
|
if (model.getStatesList() != null) {
|
|
prodStatesList = new ArrayList<State>();
|
|
daStatesList = new ArrayList<State>(da.size());
|
|
for (int i = 0; i < da.size(); i++) {
|
|
daStatesList.add(new State(1).setValue(0, i));
|
|
}
|
|
}
|
|
|
|
// We need results for all states of the original model in statesOfInterest
|
|
// We thus explore states of the product starting from these states.
|
|
// These are designated as initial states of the product model
|
|
// (a) to ensure reachability is done for these states; and
|
|
// (b) to later identify the corresponding product state for the original states
|
|
// of interest
|
|
for (int s_0 : new IterableStateSet(statesOfInterest, model.getNumStates())) {
|
|
// Get BitSet representing APs (labels) satisfied by state s_0
|
|
for (int k = 0; k < numAPs; k++) {
|
|
s_labels.set(k, labelBS.get(Integer.parseInt(da.getAPList().get(k).substring(1))).get(s_0));
|
|
}
|
|
// Find corresponding initial state in DA
|
|
int q_0 = da.getEdgeDestByLabel(da.getStartState(), s_labels);
|
|
if (q_0 < 0) {
|
|
throw new PrismException("The deterministic automaton is not complete (state " + da.getStartState() + ")");
|
|
}
|
|
// Add (initial) state to product
|
|
queue.add(new Point(s_0, q_0));
|
|
switch (modelType) {
|
|
case STPG:
|
|
((STPGExplicit) prodModel).addState(((STPG) model).getPlayer(s_0));
|
|
break;
|
|
default:
|
|
prodModel.addState();
|
|
break;
|
|
}
|
|
prodModel.addInitialState(prodModel.getNumStates() - 1);
|
|
map[s_0 * daSize + q_0] = prodModel.getNumStates() - 1;
|
|
if (prodStatesList != null) {
|
|
// Store state information for the product
|
|
prodStatesList.add(new State(daStatesList.get(q_0), model.getStatesList().get(s_0)));
|
|
}
|
|
}
|
|
|
|
// Product states
|
|
BitSet visited = new BitSet(prodNumStates);
|
|
while (!queue.isEmpty()) {
|
|
Point p = queue.pop();
|
|
s_1 = p.x;
|
|
q_1 = p.y;
|
|
visited.set(s_1 * daSize + q_1);
|
|
|
|
// Go through transitions from state s_1 in original model
|
|
int numChoices = (model instanceof NondetModel) ? ((NondetModel) model).getNumChoices(s_1) : 1;
|
|
for (int j = 0; j < numChoices; j++) {
|
|
Iterator<Map.Entry<Integer, Double>> iter;
|
|
switch (modelType) {
|
|
case DTMC:
|
|
iter = ((DTMC) model).getTransitionsIterator(s_1);
|
|
break;
|
|
case MDP:
|
|
iter = ((MDP) model).getTransitionsIterator(s_1, j);
|
|
break;
|
|
case STPG:
|
|
iter = ((STPG) model).getTransitionsIterator(s_1, j);
|
|
break;
|
|
default:
|
|
throw new PrismNotSupportedException("Product construction not implemented for " + modelType + "s");
|
|
}
|
|
Distribution prodDistr = null;
|
|
if (modelType.nondeterministic()) {
|
|
prodDistr = new Distribution();
|
|
}
|
|
while (iter.hasNext()) {
|
|
Map.Entry<Integer, Double> e = iter.next();
|
|
s_2 = e.getKey();
|
|
double prob = e.getValue();
|
|
// Get BitSet representing APs (labels) satisfied by successor state s_2
|
|
for (int k = 0; k < numAPs; k++) {
|
|
s_labels.set(k, labelBS.get(Integer.parseInt(da.getAPList().get(k).substring(1))).get(s_2));
|
|
}
|
|
// Find corresponding successor in DA
|
|
q_2 = da.getEdgeDestByLabel(q_1, s_labels);
|
|
if (q_2 < 0) {
|
|
throw new PrismException("The deterministic automaton is not complete (state " + q_1 + ")");
|
|
}
|
|
// Add state/transition to model
|
|
if (!visited.get(s_2 * daSize + q_2) && map[s_2 * daSize + q_2] == -1) {
|
|
queue.add(new Point(s_2, q_2));
|
|
switch (modelType) {
|
|
case STPG:
|
|
((STPGExplicit) prodModel).addState(((STPG) model).getPlayer(s_2));
|
|
break;
|
|
default:
|
|
prodModel.addState();
|
|
break;
|
|
}
|
|
map[s_2 * daSize + q_2] = prodModel.getNumStates() - 1;
|
|
if (prodStatesList != null) {
|
|
// Store state information for the product
|
|
prodStatesList.add(new State(daStatesList.get(q_2), model.getStatesList().get(s_2)));
|
|
}
|
|
}
|
|
switch (modelType) {
|
|
case DTMC:
|
|
((DTMCSimple) prodModel).setProbability(map[s_1 * daSize + q_1], map[s_2 * daSize + q_2], prob);
|
|
break;
|
|
case MDP:
|
|
case STPG:
|
|
prodDistr.set(map[s_2 * daSize + q_2], prob);
|
|
break;
|
|
default:
|
|
throw new PrismNotSupportedException("Product construction not implemented for " + modelType + "s");
|
|
}
|
|
}
|
|
switch (modelType) {
|
|
case MDP:
|
|
((MDPSimple) prodModel).addActionLabelledChoice(map[s_1 * daSize + q_1], prodDistr, ((MDP) model).getAction(s_1, j));
|
|
break;
|
|
case STPG:
|
|
((STPGExplicit) prodModel).addActionLabelledChoice(map[s_1 * daSize + q_1], prodDistr, ((STPG) model).getAction(s_1, j));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build a mapping from state indices to states (s,q), encoded as (s * daSize + q)
|
|
int invMap[] = new int[prodModel.getNumStates()];
|
|
for (int i = 0; i < map.length; i++) {
|
|
if (map[i] != -1) {
|
|
invMap[map[i]] = i;
|
|
}
|
|
}
|
|
|
|
prodModel.findDeadlocks(false);
|
|
|
|
if (prodStatesList != null) {
|
|
prodModel.setStatesList(prodStatesList);
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
LTLProduct<M> product = new LTLProduct<M>((M) prodModel, model, null, daSize, invMap);
|
|
|
|
// generate acceptance for the product model by lifting
|
|
product.setAcceptance(liftAcceptance(product, da.getAcceptance()));
|
|
|
|
// lift the labels
|
|
for (String label : model.getLabels()) {
|
|
BitSet liftedLabel = product.liftFromModel(model.getLabelStates(label));
|
|
prodModel.addLabel(label, liftedLabel);
|
|
}
|
|
|
|
return product;
|
|
}
|
|
|
|
/**
|
|
* Find the set of states that belong to accepting BSCCs in a model wrt an acceptance condition.
|
|
* @param model The model
|
|
* @param acceptance The acceptance condition
|
|
*/
|
|
public BitSet findAcceptingBSCCs(Model model, AcceptanceOmega acceptance) throws PrismException
|
|
{
|
|
// Compute bottom strongly connected components (BSCCs)
|
|
SCCComputer sccComputer = SCCComputer.createSCCComputer(this, model);
|
|
sccComputer.computeBSCCs();
|
|
List<BitSet> bsccs = sccComputer.getBSCCs();
|
|
|
|
BitSet result = new BitSet();
|
|
|
|
for (BitSet bscc : bsccs) {
|
|
if (acceptance.isBSCCAccepting(bscc)) {
|
|
// this BSCC is accepting
|
|
result.or(bscc);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Compute the set of states in end components of the model that are accepting
|
|
* with regard to the acceptance condition.
|
|
* @param model the model
|
|
* @param acceptance the acceptance condition
|
|
* @return BitSet with the set of states that are accepting
|
|
*/
|
|
public BitSet findAcceptingECStates(NondetModel model, AcceptanceOmega acceptance) throws PrismException
|
|
{
|
|
if (acceptance instanceof AcceptanceRabin) {
|
|
return findAcceptingECStatesForRabin(model, (AcceptanceRabin) acceptance);
|
|
} else if (acceptance instanceof AcceptanceStreett) {
|
|
return findAcceptingECStatesForStreett(model, (AcceptanceStreett) acceptance);
|
|
} else if (acceptance instanceof AcceptanceGenRabin) {
|
|
return findAcceptingECStatesForGeneralizedRabin(model, (AcceptanceGenRabin) acceptance);
|
|
}
|
|
throw new PrismNotSupportedException("Computing end components for acceptance type '"+acceptance.getTypeName()+"' currently not supported (explicit engine).");
|
|
}
|
|
|
|
/**
|
|
* Find the set of states in accepting end components (ECs) in a nondeterministic model wrt a Rabin acceptance condition.
|
|
* @param model The model
|
|
* @param acceptance The acceptance condition
|
|
*/
|
|
public BitSet findAcceptingECStatesForRabin(NondetModel model, AcceptanceRabin acceptance) throws PrismException
|
|
{
|
|
BitSet allAcceptingStates = new BitSet();
|
|
int numStates = model.getNumStates();
|
|
|
|
// Go through the DRA acceptance pairs (L_i, K_i)
|
|
for (int i = 0; i < acceptance.size(); i++) {
|
|
// Find model states *not* satisfying L_i
|
|
BitSet bitsetLi = acceptance.get(i).getL();
|
|
BitSet statesLi_not = new BitSet();
|
|
for (int s = 0; s < numStates; s++) {
|
|
if (!bitsetLi.get(s)) {
|
|
statesLi_not.set(s);
|
|
}
|
|
}
|
|
// Skip pairs with empty !L_i
|
|
if (statesLi_not.cardinality() == 0)
|
|
continue;
|
|
// Compute accepting maximum end components (MECs) in !L_i
|
|
ECComputer ecComputer = ECComputer.createECComputer(this, model);
|
|
ecComputer.computeMECStates(statesLi_not, acceptance.get(i).getK());
|
|
List<BitSet> mecs = ecComputer.getMECStates();
|
|
// Union MEC states
|
|
for (BitSet mec : mecs) {
|
|
allAcceptingStates.or(mec);
|
|
}
|
|
}
|
|
|
|
return allAcceptingStates;
|
|
}
|
|
|
|
/**
|
|
* Find the set of states in accepting end components (ECs) in a nondeterministic model wrt a Streett acceptance condition.
|
|
* @param model The model
|
|
* @param acceptance The Streett acceptance condition
|
|
*/
|
|
public BitSet findAcceptingECStatesForStreett(NondetModel model, AcceptanceStreett acceptance) throws PrismException
|
|
{
|
|
class ECandPairs {
|
|
BitSet MEC;
|
|
BitSet activePairs;
|
|
}
|
|
|
|
BitSet allAcceptingStates = new BitSet();
|
|
BitSet allPairs = new BitSet();
|
|
allPairs.set(0, acceptance.size());
|
|
|
|
Stack<ECandPairs> todo = new Stack<ECandPairs>();
|
|
ECComputer ecComputer = ECComputer.createECComputer(this, model);
|
|
ecComputer.computeMECStates();
|
|
for (BitSet mecs : ecComputer.getMECStates()) {
|
|
ECandPairs ecp = new ECandPairs();
|
|
ecp.MEC = mecs;
|
|
ecp.activePairs = allPairs;
|
|
todo.push(ecp);
|
|
}
|
|
|
|
while (!todo.empty()) {
|
|
ECandPairs ecp = todo.pop();
|
|
BitSet newActivePairs = (BitSet)ecp.activePairs.clone();
|
|
BitSet restrict = null;
|
|
|
|
// check for acceptance
|
|
boolean allAccepting = true;
|
|
for (int pair = ecp.activePairs.nextSetBit(0);
|
|
pair != -1;
|
|
pair = ecp.activePairs.nextSetBit(pair + 1)) {
|
|
|
|
if (!acceptance.get(pair).isBSCCAccepting(ecp.MEC)) {
|
|
// this pair is not accepting
|
|
if (restrict == null) {
|
|
restrict = (BitSet)ecp.MEC.clone();
|
|
}
|
|
restrict.andNot(acceptance.get(pair).getR());
|
|
newActivePairs.clear(pair);
|
|
allAccepting = false;
|
|
}
|
|
}
|
|
|
|
if (allAccepting) {
|
|
allAcceptingStates.or(ecp.MEC);
|
|
} else if (restrict.isEmpty()) {
|
|
// nothing to do
|
|
} else {
|
|
ecComputer = ECComputer.createECComputer(this, model);
|
|
ecComputer.computeMECStates(restrict);
|
|
for (BitSet mecs : ecComputer.getMECStates()) {
|
|
ECandPairs newEcp = new ECandPairs();
|
|
newEcp.MEC = mecs;
|
|
newEcp.activePairs = newActivePairs;
|
|
todo.push(newEcp);
|
|
}
|
|
}
|
|
}
|
|
|
|
return allAcceptingStates;
|
|
}
|
|
|
|
/**
|
|
* Find the set of states in accepting end components (ECs) in a nondeterministic model wrt a Generalized Rabin acceptance condition.
|
|
* @param model The model
|
|
* @param acceptance The acceptance condition
|
|
*/
|
|
public BitSet findAcceptingECStatesForGeneralizedRabin(NondetModel model, AcceptanceGenRabin acceptance) throws PrismException
|
|
{
|
|
BitSet allAcceptingStates = new BitSet();
|
|
int numStates = model.getNumStates();
|
|
|
|
// Go through the GR acceptance pairs (L_i, K_i_1, ..., K_i_n)
|
|
for (int i = 0; i < acceptance.size(); i++) {
|
|
|
|
// Find model states *not* satisfying L_i
|
|
BitSet bitsetLi = acceptance.get(i).getL();
|
|
BitSet statesLi_not = new BitSet();
|
|
for (int s = 0; s < numStates; s++) {
|
|
if (!bitsetLi.get(s)) {
|
|
statesLi_not.set(s);
|
|
}
|
|
}
|
|
// Skip pairs with empty !L_i
|
|
if (statesLi_not.cardinality() == 0)
|
|
continue;
|
|
// Compute maximum end components (MECs) in !L_i
|
|
ECComputer ecComputer = ECComputer.createECComputer(this, model);
|
|
ecComputer.computeMECStates(statesLi_not);
|
|
List<BitSet> mecs = ecComputer.getMECStates();
|
|
// Check which MECs contain a state from each K_i_j
|
|
int n = acceptance.get(i).getNumK();
|
|
for (BitSet mec : mecs) {
|
|
boolean allj = true;
|
|
for (int j = 0; j < n; j++) {
|
|
if (!mec.intersects(acceptance.get(i).getK(j))) {
|
|
allj = false;
|
|
break;
|
|
}
|
|
}
|
|
if (allj) {
|
|
allAcceptingStates.or(mec);
|
|
}
|
|
}
|
|
}
|
|
|
|
return allAcceptingStates;
|
|
}
|
|
|
|
/** Lift the acceptance condition from the automaton to the product states. */
|
|
private AcceptanceOmega liftAcceptance(final LTLProduct<?> product, AcceptanceOmega acceptance)
|
|
{
|
|
// make a copy of the acceptance condition
|
|
AcceptanceOmega lifted = acceptance.clone();
|
|
|
|
// lift state sets
|
|
lifted.lift(new AcceptanceOmega.LiftBitSet() {
|
|
@Override
|
|
public BitSet lift(BitSet states)
|
|
{
|
|
return product.liftFromAutomaton(states);
|
|
}
|
|
});
|
|
|
|
return lifted;
|
|
}
|
|
|
|
}
|