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.
 
 
 
 
 
 

670 lines
18 KiB

//==============================================================================
//
// Copyright (c) 2013-
// Authors:
// * Ernst Moritz Hahn <emhahn@cs.ox.ac.uk> (University of Oxford)
//
//------------------------------------------------------------------------------
//
// This file is part of PRISM.
//
// PRISM is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// PRISM is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with PRISM; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//==============================================================================
package param;
import java.util.BitSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.TreeSet;
import explicit.MDPGeneric;
import explicit.Model;
import explicit.ModelExplicit;
import explicit.SuccessorsIterator;
import explicit.graphviz.Decorator;
import parser.Values;
import prism.ModelType;
import prism.PrismException;
import prism.PrismLog;
import strat.MDStrategy;
/**
* Represents a parametric Markov model.
* This class is used to store all types of parametric model, both models
* with and without nondeterminism, discrete- as well as continuous-time.
* This turned out the be the most convenient way to implement model checking
* for parametric models.
*/
public final class ParamModel extends ModelExplicit implements MDPGeneric<Function>
{
/** total number of nondeterministic choices over all states */
private int numTotalChoices;
/** total number of probabilistic transitions over all states */
private int numTotalTransitions;
/** the maximal number of choices per state, over all states */
private int numMaxChoices;
/** begin and end of state transitions */
private int[] rows;
/** begin and end of a distribution in a nondeterministic choice */
private int[] choices;
/** targets of distribution branches */
private int[] cols;
/** probabilities of distribution branches */
private Function[] nonZeros;
/** labels - per transition, <i>not</i> per action */
private String[] labels;
/** total sum of leaving rates for a given nondeterministic choice */
private Function[] sumRates;
/** model type */
private ModelType modelType;
/** function factory which manages functions used on transitions, etc. */
private FunctionFactory functionFactory;
/**
* Constructs a new parametric model.
*/
ParamModel()
{
numStates = 0;
numTotalChoices = 0;
numTotalTransitions = 0;
initialStates = new LinkedList<Integer>();
deadlocks = new TreeSet<Integer>();
}
/**
* Sets the type of the model.
*
* @param modelType type the model shall have
*/
void setModelType(ModelType modelType)
{
this.modelType = modelType;
}
// Accessors (for Model)
@Override
public ModelType getModelType()
{
return modelType;
}
@Override
public Values getConstantValues()
{
throw new UnsupportedOperationException();
}
@Override
public int getNumTransitions()
{
return numTotalTransitions;
}
@Override
public SuccessorsIterator getSuccessors(final int s)
{
return SuccessorsIterator.chain(new Iterator<SuccessorsIterator>() {
private int choice = 0;
private int choices = getNumChoices(s);
@Override
public boolean hasNext()
{
return choice < choices;
}
@Override
public SuccessorsIterator next()
{
return getSuccessors(s, choice++);
}
});
}
/**
* Get a SuccessorsIterator for state s and choice i.
* @param s The state
* @param i Choice index
*/
public SuccessorsIterator getSuccessors(int s, int i)
{
return new SuccessorsIterator()
{
final int start = choiceBegin(stateBegin(s) + i);
int col = start;
final int end = choiceBegin(stateBegin(s) + i + 1);
@Override
public boolean hasNext()
{
return col < end;
}
@Override
public int nextInt()
{
assert (col < end);
int i = col;
col++;
return cols[i];
}
@Override
public boolean successorsAreDistinct()
{
return false;
}
};
}
@Override
public boolean isSuccessor(int s1, int s2)
{
for (int choice = stateBegin(s1); choice < stateEnd(s1); choice++) {
for (int succ = choiceBegin(choice); succ < choiceEnd(choice); succ++) {
if (succState(succ) == s2) {
return true;
}
}
}
return false;
}
/**
* Get an iterator over the transitions from choice {@code i} of state {@code s}.
* <br>
* For DTMC/CTMCs, there is only a single choice.
*/
public Iterator<Entry<Integer, Function>> getTransitionsIterator(final int s, final int i)
{
return new Iterator<Entry<Integer, Function>>()
{
final int start = choiceBegin(stateBegin(s) + i);
int col = start;
final int end = choiceBegin(stateBegin(s) + i + 1);
@Override
public boolean hasNext()
{
return col < end;
}
@Override
public Entry<Integer, Function> next()
{
assert (col < end);
final int i = col;
col++;
return new Entry<Integer, Function>()
{
int key = cols[i];
Function value = nonZeros[i];
@Override
public Integer getKey()
{
return key;
}
@Override
public Function getValue()
{
return value;
}
@Override
public Function setValue(Function arg0)
{
throw new UnsupportedOperationException();
}
};
}
};
}
@Override
public void findDeadlocks(boolean fix) throws PrismException
{
throw new UnsupportedOperationException();
}
@Override
public void checkForDeadlocks() throws PrismException
{
throw new UnsupportedOperationException();
}
@Override
public void checkForDeadlocks(BitSet except) throws PrismException
{
throw new UnsupportedOperationException();
}
@Override
public void exportToPrismExplicitTra(PrismLog out)
{
int i, j, numChoices;
Object action;
TreeMap<Integer, Function> sorted;
// Output transitions to .tra file
if (modelType.nondeterministic()) {
out.print(numStates + " " + getNumChoices() + " " + getNumTransitions() + "\n");
} else {
out.print(numStates + " " + getNumTransitions() + "\n");
}
sorted = new TreeMap<Integer, Function>();
for (i = 0; i < numStates; i++) {
numChoices = getNumChoices(i);
for (j = 0; j < numChoices; j++) {
// Extract transitions and sort by destination state index (to match PRISM-exported files)
Iterator<Map.Entry<Integer, Function>> iter = getTransitionsIterator(i, j);
while (iter.hasNext()) {
Map.Entry<Integer, Function> e = iter.next();
sorted.put(e.getKey(), e.getValue());
}
// Print out (sorted) transitions
for (Map.Entry<Integer, Function> e : sorted.entrySet()) {
Object value;
if (e.getValue().isConstant()) {
value = e.getValue().asBigRational();
} else {
value = e.getValue();
}
if (modelType.nondeterministic()) {
out.print(i + " " + j + " " + e.getKey() + " " + value);
} else {
out.print(i + " " + e.getKey() + " " + value);
}
action = getAction(i, j);
out.print(action == null ? "\n" : (" " + action + "\n"));
}
sorted.clear();
}
}
}
@Override
public void exportTransitionsToDotFile(int i, PrismLog out, Iterable<explicit.graphviz.Decorator> decorators)
{
int numChoices = getNumChoices(i);
for (int j = 0; j < numChoices; j++) {
// action = getAction(i, j);
String nij = null;
if (modelType.nondeterministic()) {
nij = "n" + i + "_" + j;
out.print(i + " -> " + nij + " ");
explicit.graphviz.Decoration d = new explicit.graphviz.Decoration();
d.attributes().put("arrowhead", "none");
d.setLabel(Integer.toString(j));
if (decorators != null) {
for (Decorator decorator : decorators) {
d = decorator.decorateTransition(i, j, d);
}
}
out.print(d);
out.println(";");
out.print(nij + " [ shape=point,width=0.1,height=0.1,label=\"\" ];\n");
}
Iterator<Entry<Integer, Function>> it = getTransitionsIterator(i, j);
while (it.hasNext()) {
Entry<Integer, Function> e = it.next();
// Note: For CTMCs, param stores the embedded DTMC, so we output that
if (!modelType.nondeterministic()) {
out.print(i + " -> " + e.getKey() + " ");
} else {
out.print(nij + " -> " + e.getKey() + " ");
}
Object value;
if (e.getValue().isConstant()) {
value = e.getValue().asBigRational();
} else {
value = e.getValue();
}
explicit.graphviz.Decoration d = new explicit.graphviz.Decoration();
d.setLabel(value.toString());
if (decorators != null) {
for (Decorator decorator : decorators) {
if (!modelType.nondeterministic()) {
d = decorator.decorateProbability(i, e.getKey(), value, d);
} else {
d = decorator.decorateProbability(i, e.getKey(), j, value, d);
}
}
}
out.print(d);
out.println(";");
}
}
}
@Override
public void exportToDotFileWithStrat(PrismLog out, BitSet mark, int[] strat)
{
throw new UnsupportedOperationException();
}
@Override
public void exportToPrismLanguage(String filename) throws PrismException
{
throw new UnsupportedOperationException();
}
@Override
public Model constructInducedModel(MDStrategy strat)
{
throw new UnsupportedOperationException();
}
@Override
public String infoString()
{
throw new UnsupportedOperationException();
}
@Override
public String infoStringTable()
{
String s = "";
s += "States: " + numStates + " (" + getNumInitialStates() + " initial)\n";
s += "Transitions: " + getNumTransitions() + "\n";
return s;
}
// Other
public int getNumChoices(int state)
{
return stateEnd(state) - stateBegin(state);
}
@Override
public int getMaxNumChoices()
{
return numMaxChoices;
}
@Override
public int getNumChoices()
{
return numTotalChoices;
}
@Override
public int getNumTransitions(int s, int i)
{
return choiceEnd(stateBegin(s) + i) - choiceBegin(stateBegin(s) + i);
}
@Override
public Object getAction(int s, int i)
{
return null;
}
/**
* Allocates memory for subsequent construction of model.
*
* @param numStates number of states of the model
* @param numTotalChoices total number of nondeterministic choices of the model
* @param numTotalSuccessors total number of probabilistic transitions of the model
*/
void reserveMem(int numStates, int numTotalChoices, int numTotalSuccessors)
{
rows = new int[numStates + 1];
choices = new int[numTotalChoices + 1];
labels = new String[numTotalSuccessors];
cols = new int[numTotalSuccessors];
nonZeros = new Function[numTotalSuccessors];
sumRates = new Function[numTotalChoices];
}
/**
* Finish the current state.
* Starting with the 0th state, this function shall be called once all
* nondeterministic decisions of the current nth state have been added.
* Subsequent method calls of {@code finishChoice} and {@code addTransition}
* will then apply to the (n+1)th state. Notice that this method must be
* called for each state of the method, even the last one, once all its
* transitions have been added.
*/
void finishState()
{
int state = numStates;
rows[numStates + 1] = numTotalChoices;
numStates++;
numMaxChoices = Math.max(numMaxChoices, getNumChoices(state));
}
/**
* Finished the current nondeterministic choice.
* Subsequent calls of {@code addTransition} will in turn add probabilistic
* branches to the next nondeterministic choice. Notice that this method has
* to be called for all nondeterministic choices of a state, even the last
* one. Notice that DTMCs and CTMCs should only have a single
* nondeterministic choice per state.
*/
void finishChoice()
{
choices[numTotalChoices + 1] = numTotalTransitions;
numTotalChoices++;
}
/**
* Adds a probabilistic branch to the current nondeterministic choice.
* Notice that by this function the probability to leave to a given state
* shall be specified, <i>not</i> the rate to this state. Instead, the
* sum of rates leaving a certain nondeterministic decision shall be
* specified using {@code setSumLeaving}.
*
* @param toState to which state the probabilistic choice leads
* @param probFn with which probability it leads to this state
* @param action action with which the choice is labelled
*/
void addTransition(int toState, Function probFn, String action)
{
cols[numTotalTransitions] = toState;
nonZeros[numTotalTransitions] = probFn;
labels[numTotalTransitions] = action;
numTotalTransitions++;
}
/**
* Sets the total sum of leaving rate of the current nondeterministic choice.
* For discrete-time models, this function shall always be called with
* {@code leaving = 1}.
*
* @param leaving total sum of leaving rate of the current nondeterministic choice
*/
void setSumLeaving(Function leaving)
{
sumRates[numTotalChoices] = leaving;
}
/**
* Returns the number of the first nondeterministic choice of {@code state}.
*
* @param state state to return number of first nondeterministic choice of
* @return number of first nondeterministic choice of {@code state}
*/
int stateBegin(int state)
{
return rows[state];
}
/**
* Returns the number of the last nondeterministic choice of {@code state} plus one.
*
* @param state state to return number of last nondeterministic choice of
* @return number of first nondeterministic choice of {@code state} plus one
*/
int stateEnd(int state)
{
return rows[state + 1];
}
/**
* Returns the first probabilistic branch of the given nondeterministic decision.
*
* @param choice choice of which to return the first probabilitic branch
* @return number of first probabilistic branch of {@choice}
*/
int choiceBegin(int choice)
{
return choices[choice];
}
/**
* Returns the last probabilistic branch of the given nondeterministic decision plus one.
*
* @param choice choice of which to return the first probabilitic branch
* @return number of last probabilistic branch of {@choice} plus one
*/
int choiceEnd(int choice)
{
return choices[choice + 1];
}
/**
* Returns the successor state of the given probabilistic branch.
*
* @param succNr probabilistic branch to return successor state of
* @return state which probabilistic branch leads to
*/
int succState(int succNr)
{
return cols[succNr];
}
/**
* Returns the probability of the given probabilistic branch
*
* @param succNr probabilistic branch to return probability of
* @return probability of given probabilistic branch
*/
Function succProb(int succNr)
{
return nonZeros[succNr];
}
/**
* Returns the label of the given probabilistic branch
*
* @param succNr probabilistic branch to return label of
* @return label of given probabilistic branch
*/
String getLabel(int succNr)
{
return labels[succNr];
}
/**
* Returns the total sum of leaving rates of the given nondeterministic choice
*
* @param choice nondeterministic choice to return sum of leaving rates of
* @return sum of leaving rates of given nondeterministic choice
*/
Function sumLeaving(int choice)
{
return sumRates[choice];
}
/**
* Instantiates the parametric model at a given point.
* All transition probabilities, etc. will be evaluated and set as
* probabilities of the concrete model at the given point.
* <br>
* If {@code checkWellDefinedness} is {@code true}, checks that the
* instantiated probabilities are actually probabilities and graph-preserving.
* If that is not the case, this method then returns {@code null}.
*
* @param point point to instantiate model at
* @param checkWellDefinedness should we check that the model is well-defined?
* @return nonparametric model instantiated at {@code point}
*/
ParamModel instantiate(Point point, boolean checkWellDefinedness)
{
ParamModel result = new ParamModel();
result.setModelType(getModelType());
result.reserveMem(numStates, numTotalChoices, numTotalTransitions);
result.initialStates = new LinkedList<Integer>(this.initialStates);
for (int state = 0; state < numStates; state++) {
for (int choice = stateBegin(state); choice < stateEnd(state); choice++) {
for (int succ = choiceBegin(choice); succ < choiceEnd(choice); succ++) {
BigRational p = succProb(succ).evaluate(point);
if (checkWellDefinedness) {
if (p.isSpecial() || p.compareTo(BigRational.ONE) == 1 || p.signum() <= 0) {
// For graph-preservation, probabilities have to be >0 and <=1
// Note: for CTMCs, succProb yields the probabilities of the embedded
// DTMC, while the rates are available separately (sumLeaving)
return null;
}
}
result.addTransition(succState(succ), functionFactory.fromBigRational(p), labels[succ]);
}
result.setSumLeaving(functionFactory.fromBigRational(this.sumLeaving(choice).evaluate(point)));
result.finishChoice();
}
result.finishState();
}
result.functionFactory = this.functionFactory;
return result;
}
@Override
public void buildFromPrismExplicit(String filename) throws PrismException
{
throw new UnsupportedOperationException();
}
/**
* Sets the function factory to be used in this parametric model.
*
* @param functionFactory function factory to be used in this parametric model
*/
void setFunctionFactory(FunctionFactory functionFactory)
{
this.functionFactory = functionFactory;
}
/**
* Returns the function factory used in this parametric model.
*
* @return function factory used in this parametric model
*/
FunctionFactory getFunctionFactory()
{
return functionFactory;
}
}