Browse Source
explicit.DTMCSparse: sparse storage variant for DTMCs [with Steffen Maercker]
explicit.DTMCSparse: sparse storage variant for DTMCs [with Steffen Maercker]
adapted from MDPSparse git-svn-id: https://www.prismmodelchecker.org/svn/prism/prism/trunk@12144 bbc10eb1-c90d-0410-af57-cb519fbb1720master
1 changed files with 399 additions and 0 deletions
@ -0,0 +1,399 @@ |
|||
//============================================================================== |
|||
// |
|||
// Copyright (c) 2002- |
|||
// Authors: |
|||
// * Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford) |
|||
// * Christian von Essen <christian.vonessen@imag.fr> (Verimag, Grenoble) |
|||
// * Steffen Maercker <maercker@tcs.inf.tu-dresden.de> (TU Dresden) |
|||
// * 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.util.AbstractMap; |
|||
import java.util.Arrays; |
|||
import java.util.BitSet; |
|||
import java.util.Iterator; |
|||
import java.util.Map.Entry; |
|||
import java.util.PrimitiveIterator.OfInt; |
|||
import java.util.function.Function; |
|||
|
|||
import common.IterableStateSet; |
|||
import common.iterable.MappingIterator; |
|||
import explicit.rewards.MCRewards; |
|||
import prism.PrismException; |
|||
import prism.PrismNotSupportedException; |
|||
|
|||
/** |
|||
* Sparse matrix (non-mutable) explicit-state representation of a DTMC. |
|||
* This is much faster to access than e.g. DTMCSimple and should also be more compact. |
|||
* The catch is that you have to create the model all in one go and then can't modify it. |
|||
*/ |
|||
public class DTMCSparse extends DTMCExplicit |
|||
{ |
|||
// Sparse matrix storing transition function (Steps) |
|||
/** Indices into probabilities/columns giving the start of the transitions for each state (distribution); |
|||
* array is of size numStates+1 and last entry is always equal to getNumTransitions() */ |
|||
private int rows[]; |
|||
/** Column (destination) indices for each transition (array of size numTransitions) */ |
|||
private int columns[]; |
|||
/** Probabilities for each transition (array of size numTransitions) */ |
|||
private double probabilities[]; |
|||
|
|||
public DTMCSparse(final DTMC dtmc) { |
|||
initialise(dtmc.getNumStates()); |
|||
for (Integer state : dtmc.getDeadlockStates()) { |
|||
deadlocks.add(state); |
|||
} |
|||
for (Integer state : dtmc.getInitialStates()) { |
|||
initialStates.add(state); |
|||
} |
|||
constantValues = dtmc.getConstantValues(); |
|||
varList = dtmc.getVarList(); |
|||
statesList = dtmc.getStatesList(); |
|||
for (String label : dtmc.getLabels()) { |
|||
labels.put(label, dtmc.getLabelStates(label)); |
|||
} |
|||
|
|||
// Copy transition function |
|||
final int numTransitions = dtmc.getNumTransitions(); |
|||
rows = new int[numStates + 1]; |
|||
rows[numStates] = numTransitions; |
|||
columns = new int[numTransitions]; |
|||
probabilities = new double[numTransitions]; |
|||
for (int state=0, column=0; state<numStates; state++) { |
|||
rows[state] = column; |
|||
for (Iterator<Entry<Integer, Double>> transitions = dtmc.getTransitionsIterator(state); transitions.hasNext();) { |
|||
final Entry<Integer, Double> transition = transitions.next(); |
|||
final double probability = transition.getValue(); |
|||
if (probability > 0) { |
|||
columns[column] = transition.getKey(); |
|||
probabilities[column] = probability; |
|||
column++; |
|||
} |
|||
} |
|||
} |
|||
predecessorRelation = dtmc.hasStoredPredecessorRelation() ? dtmc.getPredecessorRelation(null, false) : null; |
|||
} |
|||
|
|||
public DTMCSparse(final DTMC dtmc, int[] permut) { |
|||
initialise(dtmc.getNumStates()); |
|||
for (Integer state : dtmc.getDeadlockStates()) { |
|||
deadlocks.add(permut[state]); |
|||
} |
|||
for (Integer state : dtmc.getInitialStates()) { |
|||
initialStates.add(permut[state]); |
|||
} |
|||
constantValues = dtmc.getConstantValues(); |
|||
varList = dtmc.getVarList(); |
|||
statesList = null; |
|||
labels.clear(); |
|||
|
|||
// Compute the inverse of the permutation |
|||
int[] permutInv = new int[numStates]; |
|||
for (int state = 0; state < numStates; state++) { |
|||
permutInv[permut[state]] = state; |
|||
} |
|||
// Copy transition function |
|||
final int numTransitions = dtmc.getNumTransitions(); |
|||
rows = new int[numStates + 1]; |
|||
rows[numStates] = numTransitions; |
|||
columns = new int[numTransitions]; |
|||
probabilities = new double[numTransitions]; |
|||
for (int state=0, column=0; state<numStates; state++) { |
|||
rows[state] = column; |
|||
final int originalState = permutInv[state]; |
|||
for (Iterator<Entry<Integer, Double>> transitions = dtmc.getTransitionsIterator(originalState); transitions.hasNext();) { |
|||
final Entry<Integer, Double> transition = transitions.next(); |
|||
final double probability = transition.getValue(); |
|||
if (probability > 0) { |
|||
columns[column] = permut[transition.getKey()]; |
|||
probabilities[column] = probability; |
|||
column++; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
//--- Model --- |
|||
|
|||
@Override |
|||
public int getNumTransitions() |
|||
{ |
|||
return rows[numStates]; |
|||
} |
|||
|
|||
@Override |
|||
public OfInt getSuccessorsIterator(final int state) |
|||
{ |
|||
return Arrays.stream(columns, rows[state], rows[state+1]).iterator(); |
|||
} |
|||
|
|||
@Override |
|||
public SuccessorsIterator getSuccessors(int state) |
|||
{ |
|||
// We assume here that all the successor states for a given state are distinct |
|||
return SuccessorsIterator.from(getSuccessorsIterator(state), true); |
|||
} |
|||
|
|||
@Override |
|||
public boolean isSuccessor(final int s1, final int s2) |
|||
{ |
|||
for (int i=rows[s1], stop=rows[s1+1]; i < stop; i++) { |
|||
if (columns[i] == s2) { |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public boolean allSuccessorsInSet(final int state, final BitSet set) |
|||
{ |
|||
for (int i=rows[state], stop=rows[state+1]; i < stop; i++) { |
|||
if (!set.get(columns[i])) { |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
@Override |
|||
public boolean someSuccessorsInSet(final int state, final BitSet set) |
|||
{ |
|||
for (int i=rows[state], stop=rows[state+1]; i < stop; i++) { |
|||
if (set.get(columns[i])) { |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public void findDeadlocks(boolean fix) throws PrismException |
|||
{ |
|||
for (int state=0; state<numStates; state++) { |
|||
if (rows[state] == rows[state+1]) { |
|||
if (fix) { |
|||
throw new PrismException("Can't fix deadlocks in an DTMCSparse since it cannot be modified after construction"); |
|||
} |
|||
deadlocks.add(state); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void checkForDeadlocks(BitSet except) throws PrismException |
|||
{ |
|||
for (int state=0; state < numStates; state++) { |
|||
if (rows[state] == rows[state+1] && (except == null || !except.get(state))) |
|||
throw new PrismException("DTMC has a deadlock in state " + state); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
//--- ModelExplicit --- |
|||
|
|||
@Override |
|||
public void buildFromPrismExplicit(String filename) throws PrismException |
|||
{ |
|||
throw new PrismNotSupportedException("Building sparse DTMC currently not supported from PrismExplicit"); |
|||
} |
|||
|
|||
|
|||
|
|||
//--- DTMC --- |
|||
|
|||
@Override |
|||
public int getNumTransitions(int state) |
|||
{ |
|||
return rows[state + 1] - rows[state]; |
|||
} |
|||
|
|||
@Override |
|||
public Iterator<Entry<Integer, Double>> getTransitionsIterator(final int state) |
|||
{ |
|||
return new Iterator<Entry<Integer, Double>>() |
|||
{ |
|||
final int start = rows[state]; |
|||
int col = start; |
|||
final int end = rows[state + 1]; |
|||
|
|||
@Override |
|||
public boolean hasNext() |
|||
{ |
|||
return col < end; |
|||
} |
|||
|
|||
@Override |
|||
public Entry<Integer, Double> next() |
|||
{ |
|||
assert (col < end); |
|||
final int index = col; |
|||
col++; |
|||
return new AbstractMap.SimpleImmutableEntry<>(columns[index], probabilities[index]); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
@Override |
|||
public boolean prob0step(final int s, final BitSet u) |
|||
{ |
|||
boolean hasTransitionToU = false; |
|||
for (int i=rows[s], stop=rows[s+1]; i < stop; i++) { |
|||
final int successor = columns[i]; |
|||
if (u.get(successor)) { |
|||
hasTransitionToU = true; |
|||
break; |
|||
} |
|||
} |
|||
return hasTransitionToU; |
|||
} |
|||
|
|||
@Override |
|||
public boolean prob1step(final int s, final BitSet u, final BitSet v) |
|||
{ |
|||
boolean allTransitionsToU = true; |
|||
boolean hasTransitionToV = false; |
|||
for (int i=rows[s], stop=rows[s+1]; i < stop; i++) { |
|||
final int successor = columns[i]; |
|||
if (!u.get(successor)) { |
|||
// early abort, as overall result is false |
|||
allTransitionsToU = false; |
|||
break; |
|||
} |
|||
hasTransitionToV = hasTransitionToV || v.get(successor); |
|||
} |
|||
return (allTransitionsToU && hasTransitionToV); |
|||
} |
|||
|
|||
@Override |
|||
public double mvMultSingle(final int state, final double[] vect) |
|||
{ |
|||
double d = 0.0; |
|||
for (int i=rows[state], stop=rows[state+1]; i < stop; i++) { |
|||
final int target = columns[i]; |
|||
final double probability = probabilities[i]; |
|||
d += probability * vect[target]; |
|||
} |
|||
return d; |
|||
} |
|||
|
|||
@Override |
|||
public double mvMultJacSingle(final int state, final double[] vect) |
|||
{ |
|||
double diag = 1.0; |
|||
double d = 0.0; |
|||
for (int i=rows[state], stop=rows[state+1]; i < stop; i++) { |
|||
final int target = columns[i]; |
|||
final double probability = probabilities[i]; |
|||
if (target != state) { |
|||
d += probability * vect[target]; |
|||
} else { |
|||
diag -= probability; |
|||
} |
|||
} |
|||
if (diag > 0) { |
|||
d /= diag; |
|||
} |
|||
return d; |
|||
} |
|||
|
|||
@Override |
|||
public double mvMultRewSingle(final int state, final double[] vect, final MCRewards mcRewards) |
|||
{ |
|||
double d = mcRewards.getStateReward(state); |
|||
for (int i=rows[state], stop=rows[state+1]; i < stop; i++) { |
|||
final int target = columns[i]; |
|||
final double probability = probabilities[i]; |
|||
d += probability * vect[target]; |
|||
} |
|||
return d; |
|||
} |
|||
|
|||
@Override |
|||
public void vmMult(final double[] vect, final double[] result) |
|||
{ |
|||
// Initialise result to 0 |
|||
Arrays.fill(result, 0); |
|||
// Go through matrix elements (by row) |
|||
for (int state = 0; state < numStates; state++) { |
|||
for (int i=rows[state], stop=rows[state+1]; i < stop; i++) { |
|||
final int target = columns[i]; |
|||
final double probability = probabilities[i]; |
|||
result[target] += probability * vect[state]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
//--- Object --- |
|||
|
|||
@Override |
|||
public String toString() |
|||
{ |
|||
final Function<Integer, Entry<Integer, Distribution>> getDistribution = new Function<Integer, Entry<Integer, Distribution>>() |
|||
{ |
|||
@Override |
|||
public final Entry<Integer, Distribution> apply(final Integer state) |
|||
{ |
|||
final Distribution distribution = new Distribution(getTransitionsIterator(state)); |
|||
return new AbstractMap.SimpleImmutableEntry<>(state, distribution); |
|||
} |
|||
}; |
|||
String s = "trans: [ "; |
|||
final IterableStateSet states = new IterableStateSet(numStates); |
|||
final Iterator<Entry<Integer, Distribution>> distributions = new MappingIterator.From<>(states, getDistribution); |
|||
while (distributions.hasNext()) { |
|||
final Entry<Integer, Distribution> dist = distributions.next(); |
|||
s += dist.getKey() + ": " + dist.getValue(); |
|||
if (distributions.hasNext()) { |
|||
s += ", "; |
|||
} |
|||
} |
|||
return s + " ]"; |
|||
} |
|||
|
|||
@Override |
|||
public boolean equals(Object o) |
|||
{ |
|||
if (o == null || !(o instanceof DTMCSparse)) |
|||
return false; |
|||
final DTMCSparse dtmc = (DTMCSparse) o; |
|||
if (numStates != dtmc.numStates) |
|||
return false; |
|||
if (!initialStates.equals(dtmc.initialStates)) |
|||
return false; |
|||
if (!Utils.doubleArraysAreEqual(probabilities, dtmc.probabilities)) |
|||
return false; |
|||
if (!Utils.intArraysAreEqual(columns, dtmc.columns)) |
|||
return false; |
|||
if (!Utils.intArraysAreEqual(rows, dtmc.rows)) |
|||
return false; |
|||
// TODO: compare actions (complicated: null = null,null,null,...) |
|||
return true; |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue