@ -10,6 +10,7 @@
# include "storm/settings/SettingsManager.h"
# include "storm/settings/modules/GeneralSettings.h"
# include "storm/settings/modules/MinMaxEquationSolverSettings.h"
# include "storm/settings/modules/MarkovAutomatonSettings.h"
# include "storm/environment/Environment.h"
@ -21,7 +22,7 @@
# include "storm/storage/expressions/Expression.h"
# include "storm/storage/expressions/ExpressionManager.h"
# include "storm/utility/numerical.h"
//#include "storm/utility/numerical.h"
# include "storm/solver/MinMaxLinearEquationSolver.h"
# include "storm/solver/LpSolver.h"
@ -156,16 +157,400 @@ namespace storm {
void SparseMarkovAutomatonCslHelper : : computeBoundedReachabilityProbabilities ( Environment const & env , OptimizationDirection dir , storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix , std : : vector < ValueType > const & exitRates , storm : : storage : : BitVector const & goalStates , storm : : storage : : BitVector const & markovianNonGoalStates , storm : : storage : : BitVector const & probabilisticNonGoalStates , std : : vector < ValueType > & markovianNonGoalValues , std : : vector < ValueType > & probabilisticNonGoalValues , ValueType delta , uint64_t numberOfSteps , storm : : solver : : MinMaxLinearEquationSolverFactory < ValueType > const & minMaxLinearEquationSolverFactory ) {
STORM_LOG_THROW ( false , storm : : exceptions : : InvalidOperationException , " Computing bounded reachability probabilities is unsupported for this value type. " ) ;
}
template < typename ValueType , typename std : : enable_if < storm : : NumberTraits < ValueType > : : SupportsExponential , int > : : type >
std : : vector < ValueType > SparseMarkovAutomatonCslHelper : : computeBoundedUntilProbabilities ( Environment const & env , OptimizationDirection dir , storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix , std : : vector < ValueType > const & exitRateVector , storm : : storage : : BitVector const & markovianStates , storm : : storage : : BitVector const & psiStates , std : : pair < double , double > const & boundsPair , storm : : solver : : MinMaxLinearEquationSolverFactory < ValueType > const & minMaxLinearEquationSolverFactory ) {
void SparseMarkovAutomatonCslHelper : : calculateVu ( Environment const & env , std : : vector < std : : vector < ValueType > > const & relativeReachability , OptimizationDirection dir ,
uint64_t k , uint64_t node , uint64_t const kind , ValueType lambda , uint64_t probSize ,
std : : vector < std : : vector < std : : vector < ValueType > > > & unifVectors , storm : : storage : : SparseMatrix < ValueType > const & fullTransitionMatrix ,
storm : : storage : : BitVector const & markovianStates , storm : : storage : : BitVector const & psiStates ,
std : : unique_ptr < storm : : solver : : MinMaxLinearEquationSolver < ValueType > > const & solver ,
storm : : utility : : numerical : : FoxGlynnResult < ValueType > const & poisson , bool cycleFree ) {
if ( unifVectors [ 1 ] [ k ] [ node ] ! = - 1 ) { return ; } //dynamic programming. avoiding multiple calculation.
uint64_t N = unifVectors [ 1 ] . size ( ) - 1 ;
auto const & rowGroupIndices = fullTransitionMatrix . getRowGroupIndices ( ) ;
ValueType res = 0 ;
for ( uint64_t i = k ; i < N ; i + + ) {
if ( unifVectors [ 2 ] [ N - 1 - ( i - k ) ] [ node ] = = - 1 ) {
calculateUnifPlusVector ( env , N - 1 - ( i - k ) , node , 2 , lambda , probSize , relativeReachability , dir , unifVectors , fullTransitionMatrix , markovianStates , psiStates , solver , poisson , cycleFree ) ;
}
if ( i > = poisson . left & & i < = poisson . right ) {
res + = poisson . weights [ i - poisson . left ] * unifVectors [ 2 ] [ N - 1 - ( i - k ) ] [ node ] ;
}
}
unifVectors [ 1 ] [ k ] [ node ] = res ;
}
template < typename ValueType , typename std : : enable_if < storm : : NumberTraits < ValueType > : : SupportsExponential , int > : : type >
void SparseMarkovAutomatonCslHelper : : calculateUnifPlusVector ( Environment const & env , uint64_t k , uint64_t node , uint64_t const kind , ValueType lambda , uint64_t probSize ,
std : : vector < std : : vector < ValueType > > const & relativeReachability ,
OptimizationDirection dir ,
std : : vector < std : : vector < std : : vector < ValueType > > > & unifVectors ,
storm : : storage : : SparseMatrix < ValueType > const & fullTransitionMatrix ,
storm : : storage : : BitVector const & markovianStates ,
storm : : storage : : BitVector const & psiStates ,
std : : unique_ptr < storm : : solver : : MinMaxLinearEquationSolver < ValueType > > const & solver ,
storm : : utility : : numerical : : FoxGlynnResult < ValueType > const & poisson , bool cycleFree ) {
if ( unifVectors [ kind ] [ k ] [ node ] ! = - 1 ) {
return ; //already calculated
}
auto numberOfStates = fullTransitionMatrix . getRowGroupCount ( ) ;
auto numberOfProbStates = numberOfStates - markovianStates . getNumberOfSetBits ( ) ;
uint64_t N = unifVectors [ kind ] . size ( ) - 1 ;
auto const & rowGroupIndices = fullTransitionMatrix . getRowGroupIndices ( ) ;
ValueType res ;
// First Case, k==N, independent from kind of state
if ( k = = N ) {
unifVectors [ kind ] [ k ] [ node ] = 0 ;
return ;
}
//goal state, independent from kind of state
if ( psiStates [ node ] ) {
if ( kind = = 0 ) {
// Vd
res = storm : : utility : : zero < ValueType > ( ) ;
for ( uint64_t i = k ; i < N ; i + + ) {
if ( i > = poisson . left & & i < = poisson . right ) {
ValueType between = poisson . weights [ i - poisson . left ] ;
res + = between ;
}
}
unifVectors [ kind ] [ k ] [ node ] = res ;
} else {
// WU
unifVectors [ kind ] [ k ] [ node ] = 1 ;
}
return ;
}
//markovian non-goal State
if ( markovianStates [ node ] ) {
res = 0 ;
auto line = fullTransitionMatrix . getRow ( rowGroupIndices [ node ] ) ;
for ( auto & element : line ) {
uint64_t to = element . getColumn ( ) ;
if ( unifVectors [ kind ] [ k + 1 ] [ to ] = = - 1 ) {
calculateUnifPlusVector ( env , k + 1 , to , kind , lambda , probSize , relativeReachability , dir , unifVectors , fullTransitionMatrix , markovianStates , psiStates , solver , poisson , cycleFree ) ;
}
res + = element . getValue ( ) * unifVectors [ kind ] [ k + 1 ] [ to ] ;
}
unifVectors [ kind ] [ k ] [ node ] = res ;
return ;
}
//probabilistic non-goal State
if ( cycleFree ) {
//cycle free -- slight ValueIteration
res = - 1 ;
uint64_t rowStart = rowGroupIndices [ node ] ;
uint64_t rowEnd = rowGroupIndices [ node + 1 ] ;
for ( uint64_t i = rowStart ; i < rowEnd ; i + + ) {
auto line = fullTransitionMatrix . getRow ( i ) ;
ValueType between = 0 ;
for ( auto & element : line ) {
uint64_t to = element . getColumn ( ) ;
if ( to = = node ) {
continue ;
}
if ( unifVectors [ kind ] [ k ] [ to ] = = - 1 ) {
calculateUnifPlusVector ( env , k , to , kind , lambda , probSize , relativeReachability , dir ,
unifVectors , fullTransitionMatrix , markovianStates , psiStates ,
solver , poisson , cycleFree ) ;
}
between + = element . getValue ( ) * unifVectors [ kind ] [ k ] [ to ] ;
}
if ( maximize ( dir ) ) {
res = std : : max ( res , between ) ;
} else {
if ( res ! = - 1 ) {
res = std : : min ( res , between ) ;
} else {
res = between ;
}
}
}
unifVectors [ kind ] [ k ] [ node ] = res ;
return ;
}
//not cycle free - use solver technique, calling SVI per default
//solving all sub-MDP's in one iteration
std : : vector < ValueType > b ( probSize , 0 ) , x ( numberOfProbStates , 0 ) ;
//calculate b
uint64_t lineCounter = 0 ;
for ( int i = 0 ; i < numberOfStates ; i + + ) {
if ( markovianStates [ i ] ) {
continue ;
}
auto rowStart = rowGroupIndices [ i ] ;
auto rowEnd = rowGroupIndices [ i + 1 ] ;
for ( auto j = rowStart ; j < rowEnd ; j + + ) {
uint64_t stateCount = 0 ;
res = 0 ;
for ( auto & element : fullTransitionMatrix . getRow ( j ) ) {
auto to = element . getColumn ( ) ;
if ( ! markovianStates [ to ] ) {
continue ;
}
if ( unifVectors [ kind ] [ k ] [ to ] = = - 1 ) {
calculateUnifPlusVector ( env , k , to , kind , lambda , probSize , relativeReachability , dir ,
unifVectors , fullTransitionMatrix , markovianStates ,
psiStates , solver , poisson , cycleFree ) ;
}
res = res + relativeReachability [ j ] [ stateCount ] * unifVectors [ kind ] [ k ] [ to ] ;
stateCount + + ;
}
b [ lineCounter ] = res ;
lineCounter + + ;
}
}
solver - > solveEquations ( env , dir , x , b ) ;
for ( uint64_t i = 0 ; i < numberOfProbStates ; i + + ) {
auto trueI = transformIndice ( ~ markovianStates , i ) ;
unifVectors [ kind ] [ k ] [ trueI ] = x [ i ] ;
}
//end probabilistic states
}
template < typename ValueType , typename std : : enable_if < storm : : NumberTraits < ValueType > : : SupportsExponential , int > : : type >
void SparseMarkovAutomatonCslHelper : : deleteProbDiagonals ( storm : : storage : : SparseMatrix < ValueType > & transitionMatrix , storm : : storage : : BitVector const & markovianStates ) {
auto const & rowGroupIndices = transitionMatrix . getRowGroupIndices ( ) ;
for ( uint64_t i = 0 ; i < transitionMatrix . getRowGroupCount ( ) ; i + + ) {
if ( markovianStates [ i ] ) {
continue ;
}
auto from = rowGroupIndices [ i ] ;
auto to = rowGroupIndices [ i + 1 ] ;
for ( uint64_t j = from ; j < to ; j + + ) {
ValueType selfLoop = 0 ;
for ( auto & element : transitionMatrix . getRow ( j ) ) {
if ( element . getColumn ( ) = = i ) {
selfLoop = element . getValue ( ) ;
}
}
if ( selfLoop = = 0 ) {
continue ;
}
for ( auto & element : transitionMatrix . getRow ( j ) ) {
if ( element . getColumn ( ) ! = i ) {
if ( selfLoop ! = 1 ) {
element . setValue ( element . getValue ( ) / ( 1 - selfLoop ) ) ;
}
} else {
element . setValue ( 0 ) ;
}
}
}
}
}
template < typename ValueType , typename std : : enable_if < storm : : NumberTraits < ValueType > : : SupportsExponential , int > : : type >
std : : vector < ValueType > SparseMarkovAutomatonCslHelper : : unifPlus ( Environment const & env , OptimizationDirection dir ,
std : : pair < double , double > const & boundsPair ,
std : : vector < ValueType > const & exitRateVector ,
storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix ,
storm : : storage : : BitVector const & markovStates ,
storm : : storage : : BitVector const & psiStates ,
storm : : solver : : MinMaxLinearEquationSolverFactory < ValueType > const & minMaxLinearEquationSolverFactory ) {
STORM_LOG_TRACE ( " Using UnifPlus to compute bounded until probabilities. " ) ;
//bitvectors to identify different kind of states
storm : : storage : : BitVector markovianStates = markovStates ;
storm : : storage : : BitVector allStates ( markovianStates . size ( ) , true ) ;
storm : : storage : : BitVector probabilisticStates = ~ markovianStates ;
//searching for SCC on Underlying MDP to decide which algorhitm is applied
storm : : storage : : StronglyConnectedComponentDecomposition < double > sccList ( transitionMatrix , probabilisticStates , true , false ) ;
bool cycleFree = sccList . size ( ) = = 0 ;
//vectors to save calculation
std : : vector < std : : vector < std : : vector < ValueType > > > unifVectors { } ;
//transitions from goalStates will be ignored. still: they are not allowed to be probabilistic!
// to make sure we apply our formula and NOT the MDP algorithm
for ( uint64_t i = 0 ; i < psiStates . size ( ) ; i + + ) {
if ( psiStates [ i ] ) {
markovianStates . set ( i , true ) ;
probabilisticStates . set ( i , false ) ;
}
}
//transition matrix with extended with diagonal entries. Therefore, the values can be changed during uniformisation
// exitRateVector with changeable exit Rates
std : : vector < ValueType > exitRate { exitRateVector } ;
typename storm : : storage : : SparseMatrix < ValueType > fullTransitionMatrix = transitionMatrix . getSubmatrix (
true , allStates , allStates , true ) ;
// delete diagonals - needed for VI, not vor SVI
deleteProbDiagonals ( fullTransitionMatrix , markovianStates ) ;
typename storm : : storage : : SparseMatrix < ValueType > probMatrix { } ;
uint64_t probSize = 0 ;
if ( probabilisticStates . getNumberOfSetBits ( ) ! = 0 ) { //work around in case there are no prob states
probMatrix = fullTransitionMatrix . getSubmatrix ( true , probabilisticStates , probabilisticStates ,
true ) ;
probSize = probMatrix . getRowCount ( ) ;
}
// indices for transition martrix
auto & rowGroupIndices = fullTransitionMatrix . getRowGroupIndices ( ) ;
//(1) define/declare horizon, epsilon, kappa , N, lambda, maxNorm
uint64_t numberOfStates = fullTransitionMatrix . getRowGroupCount ( ) ;
double T = boundsPair . second ;
ValueType kappa = storm : : utility : : one < ValueType > ( ) / 10 ; // would be better as option-parameter
ValueType epsilon = storm : : settings : : getModule < storm : : settings : : modules : : GeneralSettings > ( ) . getPrecision ( ) ;
ValueType lambda = exitRate [ 0 ] ;
for ( ValueType act : exitRate ) {
lambda = std : : max ( act , lambda ) ;
}
uint64_t N ;
ValueType maxNorm = storm : : utility : : zero < ValueType > ( ) ;
//calculate relative ReachabilityVectors and create solver - just needed for cycles
std : : vector < ValueType > in { } ;
std : : vector < std : : vector < ValueType > > relReachability ( transitionMatrix . getRowCount ( ) , in ) ;
std : : unique_ptr < storm : : solver : : MinMaxLinearEquationSolver < ValueType > > solver ;
if ( ! cycleFree ) {
//calculate relative reachability
for ( uint64_t i = 0 ; i < numberOfStates ; i + + ) {
if ( markovianStates [ i ] ) {
continue ;
}
auto from = rowGroupIndices [ i ] ;
auto to = rowGroupIndices [ i + 1 ] ;
for ( auto j = from ; j < to ; j + + ) {
for ( auto & element : fullTransitionMatrix . getRow ( j ) ) {
if ( markovianStates [ element . getColumn ( ) ] ) {
relReachability [ j ] . push_back ( element . getValue ( ) ) ;
}
}
}
}
//create equitation solver
storm : : solver : : MinMaxLinearEquationSolverRequirements requirements = minMaxLinearEquationSolverFactory . getRequirements (
env , true , dir ) ;
requirements . clearBounds ( ) ;
STORM_LOG_THROW ( requirements . empty ( ) , storm : : exceptions : : UncheckedRequirementException ,
" Cannot establish requirements for solver. " ) ;
if ( probSize ! = 0 ) {
solver = minMaxLinearEquationSolverFactory . create ( env , probMatrix ) ;
solver - > setHasUniqueSolution ( ) ;
solver - > setBounds ( storm : : utility : : zero < ValueType > ( ) , storm : : utility : : one < ValueType > ( ) ) ;
solver - > setRequirementsChecked ( ) ;
solver - > setCachingEnabled ( true ) ;
}
}
// while not close enough to precision:
do {
maxNorm = storm : : utility : : zero < ValueType > ( ) ;
// (2) update parameter
N = ceil ( lambda * T * exp ( 2 ) - log ( kappa * epsilon ) ) ;
// (3) uniform - just applied to markovian states
for ( uint64_t i = 0 ; i < fullTransitionMatrix . getRowGroupCount ( ) ; i + + ) {
if ( ! markovianStates [ i ] | | psiStates [ i ] ) {
continue ;
}
uint64_t from = rowGroupIndices [ i ] ; //markovian state -> no Nondeterminism -> only one row
if ( exitRate [ i ] = = lambda ) {
continue ; //already unified
}
auto line = fullTransitionMatrix . getRow ( from ) ;
ValueType exitOld = exitRate [ i ] ;
ValueType exitNew = lambda ;
for ( auto & v : line ) {
if ( v . getColumn ( ) = = i ) { //diagonal element
ValueType newSelfLoop = exitNew - exitOld + v . getValue ( ) * exitOld ;
ValueType newRate = newSelfLoop / exitNew ;
v . setValue ( newRate ) ;
} else { //modify probability
ValueType propOld = v . getValue ( ) ;
ValueType propNew = propOld * exitOld / exitNew ;
v . setValue ( propNew ) ;
}
}
exitRate [ i ] = exitNew ;
}
// calculate poisson distribution
storm : : utility : : numerical : : FoxGlynnResult < ValueType > foxGlynnResult = storm : : utility : : numerical : : foxGlynn ( lambda * T , epsilon * kappa / 100 ) ;
// Scale the weights so they add up to one.
for ( auto & element : foxGlynnResult . weights ) {
element / = foxGlynnResult . totalWeight ;
}
// (4) define vectors/matrices
std : : vector < ValueType > init ( numberOfStates , - 1 ) ;
std : : vector < std : : vector < ValueType > > v = std : : vector < std : : vector < ValueType > > ( N + 1 , init ) ;
unifVectors . clear ( ) ;
unifVectors . push_back ( v ) ;
unifVectors . push_back ( v ) ;
unifVectors . push_back ( v ) ;
//define 0=vd 1=vu 2=wu
// (5) calculate vectors and maxNorm
for ( uint64_t i = 0 ; i < numberOfStates ; i + + ) {
for ( uint64_t k = N ; k < = N ; k - - ) {
calculateUnifPlusVector ( env , k , i , 0 , lambda , probSize , relReachability , dir , unifVectors ,
fullTransitionMatrix , markovianStates , psiStates , solver ,
foxGlynnResult , cycleFree ) ;
calculateUnifPlusVector ( env , k , i , 2 , lambda , probSize , relReachability , dir , unifVectors ,
fullTransitionMatrix , markovianStates , psiStates , solver ,
foxGlynnResult , cycleFree ) ;
calculateVu ( env , relReachability , dir , k , i , 1 , lambda , probSize , unifVectors ,
fullTransitionMatrix , markovianStates , psiStates , solver ,
foxGlynnResult , cycleFree ) ;
}
}
//only iterate over result vector, as the results can only get more precise
for ( uint64_t i = 0 ; i < numberOfStates ; i + + ) {
ValueType diff = std : : abs ( unifVectors [ 0 ] [ 0 ] [ i ] - unifVectors [ 1 ] [ 0 ] [ i ] ) ;
maxNorm = std : : max ( maxNorm , diff ) ;
}
// (6) double lambda
lambda = 2 * lambda ;
} while ( maxNorm > epsilon * ( 1 - kappa ) ) ;
return unifVectors [ 0 ] [ 0 ] ;
}
template < typename ValueType , typename std : : enable_if < storm : : NumberTraits < ValueType > : : SupportsExponential , int > : : type >
std : : vector < ValueType > SparseMarkovAutomatonCslHelper : : computeBoundedUntilProbabilitiesImca ( Environment const & env , OptimizationDirection dir , storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix , std : : vector < ValueType > const & exitRateVector , storm : : storage : : BitVector const & markovianStates , storm : : storage : : BitVector const & psiStates , std : : pair < double , double > const & boundsPair , storm : : solver : : MinMaxLinearEquationSolverFactory < ValueType > const & minMaxLinearEquationSolverFactory ) {
STORM_LOG_TRACE ( " Using IMCA's technique to compute bounded until probabilities. " ) ;
uint64_t numberOfStates = transitionMatrix . getRowGroupCount ( ) ;
// 'Unpack' the bounds to make them more easily accessible.
double lowerBound = boundsPair . first ;
double upperBound = boundsPair . second ;
// (1) Compute the accuracy we need to achieve the required error bound.
ValueType maxExitRate = 0 ;
for ( auto value : exitRateVector ) {
@ -220,6 +605,19 @@ namespace storm {
return result ;
}
}
template < typename ValueType , typename std : : enable_if < storm : : NumberTraits < ValueType > : : SupportsExponential , int > : : type >
std : : vector < ValueType > SparseMarkovAutomatonCslHelper : : computeBoundedUntilProbabilities ( Environment const & env , OptimizationDirection dir , storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix , std : : vector < ValueType > const & exitRateVector , storm : : storage : : BitVector const & markovianStates , storm : : storage : : BitVector const & psiStates , std : : pair < double , double > const & boundsPair , storm : : solver : : MinMaxLinearEquationSolverFactory < ValueType > const & minMaxLinearEquationSolverFactory ) {
auto const & markovAutomatonSettings = storm : : settings : : getModule < storm : : settings : : modules : : MarkovAutomatonSettings > ( ) ;
if ( markovAutomatonSettings . getTechnique ( ) = = storm : : settings : : modules : : MarkovAutomatonSettings : : BoundedReachabilityTechnique : : Imca ) {
return computeBoundedUntilProbabilitiesImca ( env , dir , transitionMatrix , exitRateVector , markovianStates , psiStates , boundsPair , minMaxLinearEquationSolverFactory ) ;
} else {
STORM_LOG_ASSERT ( markovAutomatonSettings . getTechnique ( ) = = storm : : settings : : modules : : MarkovAutomatonSettings : : BoundedReachabilityTechnique : : UnifPlus , " Unknown solution technique. " ) ;
return unifPlus ( env , dir , boundsPair , exitRateVector , transitionMatrix , markovianStates , psiStates , minMaxLinearEquationSolverFactory ) ;
}
}
template < typename ValueType , typename std : : enable_if < ! storm : : NumberTraits < ValueType > : : SupportsExponential , int > : : type >
std : : vector < ValueType > SparseMarkovAutomatonCslHelper : : computeBoundedUntilProbabilities ( Environment const & env , OptimizationDirection dir , storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix , std : : vector < ValueType > const & exitRateVector , storm : : storage : : BitVector const & markovianStates , storm : : storage : : BitVector const & psiStates , std : : pair < double , double > const & boundsPair , storm : : solver : : MinMaxLinearEquationSolverFactory < ValueType > const & minMaxLinearEquationSolverFactory ) {