@ -39,7 +39,6 @@
# include "storm/transformer/EndComponentEliminator.h"
# include "storm/environment/solver/MinMaxSolverEnvironment.h"
# include "storm/environment/solver/LongRunAverageSolverEnvironment.h"
# include "storm/exceptions/InvalidStateException.h"
# include "storm/exceptions/InvalidPropertyException.h"
@ -1208,464 +1207,6 @@ namespace storm {
return MDPSparseModelCheckingHelperReturnType < ValueType > ( std : : move ( result ) , std : : move ( scheduler ) ) ;
}
template < typename ValueType >
MDPSparseModelCheckingHelperReturnType < ValueType > SparseMdpPrctlHelper < ValueType > : : computeLongRunAverageProbabilities ( Environment const & env , storm : : solver : : SolveGoal < ValueType > & & goal , storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix , storm : : storage : : SparseMatrix < ValueType > const & backwardTransitions , storm : : storage : : BitVector const & psiStates , bool produceScheduler ) {
// If there are no goal states, we avoid the computation and directly return zero.
if ( psiStates . empty ( ) ) {
return std : : vector < ValueType > ( transitionMatrix . getRowGroupCount ( ) , storm : : utility : : zero < ValueType > ( ) ) ;
}
// Likewise, if all bits are set, we can avoid the computation and set.
if ( psiStates . full ( ) ) {
return std : : vector < ValueType > ( transitionMatrix . getRowGroupCount ( ) , storm : : utility : : one < ValueType > ( ) ) ;
}
// Reduce long run average probabilities to long run average rewards by
// building a reward model assigning one reward to every psi state
std : : vector < ValueType > stateRewards ( psiStates . size ( ) , storm : : utility : : zero < ValueType > ( ) ) ;
storm : : utility : : vector : : setVectorValues ( stateRewards , psiStates , storm : : utility : : one < ValueType > ( ) ) ;
storm : : models : : sparse : : StandardRewardModel < ValueType > rewardModel ( std : : move ( stateRewards ) ) ;
return computeLongRunAverageRewards ( env , std : : move ( goal ) , transitionMatrix , backwardTransitions , rewardModel , produceScheduler ) ;
}
template < typename ValueType >
template < typename RewardModelType >
MDPSparseModelCheckingHelperReturnType < ValueType > SparseMdpPrctlHelper < ValueType > : : computeLongRunAverageRewards ( Environment const & env , storm : : solver : : SolveGoal < ValueType > & & goal , storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix , storm : : storage : : SparseMatrix < ValueType > const & backwardTransitions , RewardModelType const & rewardModel , bool produceScheduler ) {
uint64_t numberOfStates = transitionMatrix . getRowGroupCount ( ) ;
std : : unique_ptr < storm : : storage : : Scheduler < ValueType > > scheduler ;
if ( produceScheduler ) {
scheduler = std : : make_unique < storm : : storage : : Scheduler < ValueType > > ( numberOfStates ) ;
}
// Start by decomposing the MDP into its MECs.
storm : : storage : : MaximalEndComponentDecomposition < ValueType > mecDecomposition ( transitionMatrix , backwardTransitions ) ;
// Get some data members for convenience.
std : : vector < uint_fast64_t > const & nondeterministicChoiceIndices = transitionMatrix . getRowGroupIndices ( ) ;
ValueType zero = storm : : utility : : zero < ValueType > ( ) ;
//first calculate LRA for the Maximal End Components.
storm : : storage : : BitVector statesInMecs ( numberOfStates ) ;
std : : vector < uint_fast64_t > stateToMecIndexMap ( transitionMatrix . getColumnCount ( ) ) ;
std : : vector < ValueType > lraValuesForEndComponents ( mecDecomposition . size ( ) , zero ) ;
auto underlyingSolverEnvironment = env ;
if ( env . solver ( ) . isForceSoundness ( ) ) {
// For sound computations, the error in the MECS plus the error in the remaining system should be less than the user defined precsion.
underlyingSolverEnvironment . solver ( ) . lra ( ) . setPrecision ( env . solver ( ) . lra ( ) . getPrecision ( ) / storm : : utility : : convertNumber < storm : : RationalNumber > ( 2 ) ) ;
underlyingSolverEnvironment . solver ( ) . minMax ( ) . setPrecision ( env . solver ( ) . lra ( ) . getPrecision ( ) / storm : : utility : : convertNumber < storm : : RationalNumber > ( 2 ) ) ;
underlyingSolverEnvironment . solver ( ) . minMax ( ) . setRelativeTerminationCriterion ( env . solver ( ) . lra ( ) . getRelativeTerminationCriterion ( ) ) ;
}
for ( uint_fast64_t currentMecIndex = 0 ; currentMecIndex < mecDecomposition . size ( ) ; + + currentMecIndex ) {
storm : : storage : : MaximalEndComponent const & mec = mecDecomposition [ currentMecIndex ] ;
lraValuesForEndComponents [ currentMecIndex ] = computeLraForMaximalEndComponent ( underlyingSolverEnvironment , goal . direction ( ) , transitionMatrix , rewardModel , mec , scheduler ) ;
// Gather information for later use.
for ( auto const & stateChoicesPair : mec ) {
statesInMecs . set ( stateChoicesPair . first ) ;
stateToMecIndexMap [ stateChoicesPair . first ] = currentMecIndex ;
}
}
// For fast transition rewriting, we build some auxiliary data structures.
storm : : storage : : BitVector statesNotContainedInAnyMec = ~ statesInMecs ;
uint_fast64_t firstAuxiliaryStateIndex = statesNotContainedInAnyMec . getNumberOfSetBits ( ) ;
uint_fast64_t lastStateNotInMecs = 0 ;
uint_fast64_t numberOfStatesNotInMecs = 0 ;
std : : vector < uint_fast64_t > statesNotInMecsBeforeIndex ;
statesNotInMecsBeforeIndex . reserve ( numberOfStates ) ;
for ( auto state : statesNotContainedInAnyMec ) {
while ( lastStateNotInMecs < = state ) {
statesNotInMecsBeforeIndex . push_back ( numberOfStatesNotInMecs ) ;
+ + lastStateNotInMecs ;
}
+ + numberOfStatesNotInMecs ;
}
// Finally, we are ready to create the SSP matrix and right-hand side of the SSP.
std : : vector < ValueType > b ;
uint64_t numberOfSspStates = numberOfStatesNotInMecs + mecDecomposition . size ( ) ;
typename storm : : storage : : SparseMatrixBuilder < ValueType > sspMatrixBuilder ( 0 , numberOfSspStates , 0 , false , true , numberOfSspStates ) ;
// If the source state is not contained in any MEC, we copy its choices (and perform the necessary modifications).
uint_fast64_t currentChoice = 0 ;
for ( auto state : statesNotContainedInAnyMec ) {
sspMatrixBuilder . newRowGroup ( currentChoice ) ;
for ( uint_fast64_t choice = nondeterministicChoiceIndices [ state ] ; choice < nondeterministicChoiceIndices [ state + 1 ] ; + + choice , + + currentChoice ) {
std : : vector < ValueType > auxiliaryStateToProbabilityMap ( mecDecomposition . size ( ) ) ;
b . push_back ( storm : : utility : : zero < ValueType > ( ) ) ;
for ( auto element : transitionMatrix . getRow ( choice ) ) {
if ( statesNotContainedInAnyMec . get ( element . getColumn ( ) ) ) {
// If the target state is not contained in an MEC, we can copy over the entry.
sspMatrixBuilder . addNextValue ( currentChoice , statesNotInMecsBeforeIndex [ element . getColumn ( ) ] , element . getValue ( ) ) ;
} else {
// If the target state is contained in MEC i, we need to add the probability to the corresponding field in the vector
// so that we are able to write the cumulative probability to the MEC into the matrix.
auxiliaryStateToProbabilityMap [ stateToMecIndexMap [ element . getColumn ( ) ] ] + = element . getValue ( ) ;
}
}
// Now insert all (cumulative) probability values that target an MEC.
for ( uint_fast64_t mecIndex = 0 ; mecIndex < auxiliaryStateToProbabilityMap . size ( ) ; + + mecIndex ) {
if ( ! storm : : utility : : isZero ( auxiliaryStateToProbabilityMap [ mecIndex ] ) ) {
sspMatrixBuilder . addNextValue ( currentChoice , firstAuxiliaryStateIndex + mecIndex , auxiliaryStateToProbabilityMap [ mecIndex ] ) ;
}
}
}
}
std : : vector < std : : pair < uint_fast64_t , uint_fast64_t > > sspMecChoicesToOriginalMap ; // for scheduler extraction
// Now we are ready to construct the choices for the auxiliary states.
for ( uint_fast64_t mecIndex = 0 ; mecIndex < mecDecomposition . size ( ) ; + + mecIndex ) {
storm : : storage : : MaximalEndComponent const & mec = mecDecomposition [ mecIndex ] ;
sspMatrixBuilder . newRowGroup ( currentChoice ) ;
for ( auto const & stateChoicesPair : mec ) {
uint_fast64_t state = stateChoicesPair . first ;
storm : : storage : : FlatSet < uint_fast64_t > const & choicesInMec = stateChoicesPair . second ;
for ( uint_fast64_t choice = nondeterministicChoiceIndices [ state ] ; choice < nondeterministicChoiceIndices [ state + 1 ] ; + + choice ) {
// If the choice is not contained in the MEC itself, we have to add a similar distribution to the auxiliary state.
if ( choicesInMec . find ( choice ) = = choicesInMec . end ( ) ) {
std : : vector < ValueType > auxiliaryStateToProbabilityMap ( mecDecomposition . size ( ) ) ;
b . push_back ( storm : : utility : : zero < ValueType > ( ) ) ;
for ( auto element : transitionMatrix . getRow ( choice ) ) {
if ( statesNotContainedInAnyMec . get ( element . getColumn ( ) ) ) {
// If the target state is not contained in an MEC, we can copy over the entry.
sspMatrixBuilder . addNextValue ( currentChoice , statesNotInMecsBeforeIndex [ element . getColumn ( ) ] , element . getValue ( ) ) ;
} else {
// If the target state is contained in MEC i, we need to add the probability to the corresponding field in the vector
// so that we are able to write the cumulative probability to the MEC into the matrix.
auxiliaryStateToProbabilityMap [ stateToMecIndexMap [ element . getColumn ( ) ] ] + = element . getValue ( ) ;
}
}
// Now insert all (cumulative) probability values that target an MEC.
for ( uint_fast64_t targetMecIndex = 0 ; targetMecIndex < auxiliaryStateToProbabilityMap . size ( ) ; + + targetMecIndex ) {
if ( ! storm : : utility : : isZero ( auxiliaryStateToProbabilityMap [ targetMecIndex ] ) ) {
sspMatrixBuilder . addNextValue ( currentChoice , firstAuxiliaryStateIndex + targetMecIndex , auxiliaryStateToProbabilityMap [ targetMecIndex ] ) ;
}
}
if ( produceScheduler ) {
sspMecChoicesToOriginalMap . emplace_back ( state , choice - nondeterministicChoiceIndices [ state ] ) ;
}
+ + currentChoice ;
}
}
}
// For each auxiliary state, there is the option to achieve the reward value of the LRA associated with the MEC.
+ + currentChoice ;
b . push_back ( lraValuesForEndComponents [ mecIndex ] ) ;
if ( produceScheduler ) {
// Insert some invalid values
sspMecChoicesToOriginalMap . emplace_back ( std : : numeric_limits < uint_fast64_t > : : max ( ) , std : : numeric_limits < uint_fast64_t > : : max ( ) ) ;
}
}
// Finalize the matrix and solve the corresponding system of equations.
storm : : storage : : SparseMatrix < ValueType > sspMatrix = sspMatrixBuilder . build ( currentChoice , numberOfSspStates , numberOfSspStates ) ;
// Check for requirements of the solver.
storm : : solver : : GeneralMinMaxLinearEquationSolverFactory < ValueType > minMaxLinearEquationSolverFactory ;
storm : : solver : : MinMaxLinearEquationSolverRequirements requirements = minMaxLinearEquationSolverFactory . getRequirements ( underlyingSolverEnvironment , true , true , goal . direction ( ) , false , produceScheduler ) ;
requirements . clearBounds ( ) ;
STORM_LOG_THROW ( ! requirements . hasEnabledCriticalRequirement ( ) , storm : : exceptions : : UncheckedRequirementException , " Solver requirements " + requirements . getEnabledRequirementsAsString ( ) + " not checked. " ) ;
std : : vector < ValueType > sspResult ( numberOfSspStates ) ;
goal . restrictRelevantValues ( statesNotContainedInAnyMec ) ;
std : : unique_ptr < storm : : solver : : MinMaxLinearEquationSolver < ValueType > > solver = storm : : solver : : configureMinMaxLinearEquationSolver ( underlyingSolverEnvironment , std : : move ( goal ) , minMaxLinearEquationSolverFactory , sspMatrix ) ;
solver - > setLowerBound ( storm : : utility : : zero < ValueType > ( ) ) ;
solver - > setUpperBound ( * std : : max_element ( lraValuesForEndComponents . begin ( ) , lraValuesForEndComponents . end ( ) ) ) ;
solver - > setHasUniqueSolution ( ) ;
solver - > setHasNoEndComponents ( ) ;
solver - > setTrackScheduler ( produceScheduler ) ;
solver - > setRequirementsChecked ( ) ;
solver - > solveEquations ( underlyingSolverEnvironment , sspResult , b ) ;
// Prepare result vector.
std : : vector < ValueType > result ( numberOfStates , zero ) ;
// Set the values for states not contained in MECs.
storm : : utility : : vector : : setVectorValues ( result , statesNotContainedInAnyMec , sspResult ) ;
// Set the values for all states in MECs.
for ( auto state : statesInMecs ) {
result [ state ] = sspResult [ firstAuxiliaryStateIndex + stateToMecIndexMap [ state ] ] ;
}
if ( produceScheduler & & solver - > hasScheduler ( ) ) {
// Translate result for ssp matrix to original model
auto const & sspChoices = solver - > getSchedulerChoices ( ) ;
uint64_t sspState = 0 ;
for ( auto state : statesNotContainedInAnyMec ) {
scheduler - > setChoice ( sspChoices [ sspState ] , state ) ;
+ + sspState ;
}
// The other sspStates correspond to MECS in the original system.
uint_fast64_t rowOffset = sspMatrix . getRowGroupIndices ( ) [ sspState ] ;
for ( uint_fast64_t mecIndex = 0 ; mecIndex < mecDecomposition . size ( ) ; + + mecIndex ) {
// Obtain the state and choice of the original model to which the selected choice corresponds.
auto const & originalStateChoice = sspMecChoicesToOriginalMap [ sspMatrix . getRowGroupIndices ( ) [ sspState ] + sspChoices [ sspState ] - rowOffset ] ;
// Check if the best choice is to stay in this MEC
if ( originalStateChoice . first = = std : : numeric_limits < uint_fast64_t > : : max ( ) ) {
STORM_LOG_ASSERT ( sspMatrix . getRow ( sspState , sspChoices [ sspState ] ) . getNumberOfEntries ( ) = = 0 , " Expected empty row at choice that stays in MEC. " ) ;
// In this case, no further operations are necessary. The scheduler has already been set to the optimal choices during the call of computeLraForMaximalEndComponent.
} else {
// The best choice is to leave this MEC via the selected state and choice.
scheduler - > setChoice ( originalStateChoice . second , originalStateChoice . first ) ;
// The remaining states in this MEC need to reach this state with probability 1.
storm : : storage : : BitVector exitStateAsBitVector ( transitionMatrix . getRowGroupCount ( ) , false ) ;
exitStateAsBitVector . set ( originalStateChoice . first , true ) ;
storm : : storage : : BitVector otherStatesAsBitVector ( transitionMatrix . getRowGroupCount ( ) , false ) ;
for ( auto const & stateChoices : mecDecomposition [ mecIndex ] ) {
if ( stateChoices . first ! = originalStateChoice . first ) {
otherStatesAsBitVector . set ( stateChoices . first , true ) ;
}
}
storm : : utility : : graph : : computeSchedulerProb1E ( otherStatesAsBitVector , transitionMatrix , backwardTransitions , otherStatesAsBitVector , exitStateAsBitVector , * scheduler ) ;
}
+ + sspState ;
}
assert ( sspState = = sspMatrix . getRowGroupCount ( ) ) ;
} else {
STORM_LOG_ERROR_COND ( ! produceScheduler , " Requested to produce a scheduler, but no scheduler was generated. " ) ;
}
return MDPSparseModelCheckingHelperReturnType < ValueType > ( std : : move ( result ) , std : : move ( scheduler ) ) ;
}
template < typename ValueType >
template < typename RewardModelType >
ValueType SparseMdpPrctlHelper < ValueType > : : computeLraForMaximalEndComponent ( Environment const & env , OptimizationDirection dir , storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix , RewardModelType const & rewardModel , storm : : storage : : MaximalEndComponent const & mec , std : : unique_ptr < storm : : storage : : Scheduler < ValueType > > & scheduler ) {
// If the mec only consists of a single state, we compute the LRA value directly
if ( + + mec . begin ( ) = = mec . end ( ) ) {
uint64_t state = mec . begin ( ) - > first ;
auto choiceIt = mec . begin ( ) - > second . begin ( ) ;
ValueType result = rewardModel . getTotalStateActionReward ( state , * choiceIt , transitionMatrix ) ;
uint_fast64_t bestChoice = * choiceIt ;
for ( + + choiceIt ; choiceIt ! = mec . begin ( ) - > second . end ( ) ; + + choiceIt ) {
ValueType choiceValue = rewardModel . getTotalStateActionReward ( state , * choiceIt , transitionMatrix ) ;
if ( storm : : solver : : minimize ( dir ) ) {
if ( result > choiceValue ) {
result = std : : move ( choiceValue ) ;
bestChoice = * choiceIt ;
}
} else {
if ( result < choiceValue ) {
result = std : : move ( choiceValue ) ;
bestChoice = * choiceIt ;
}
}
}
if ( scheduler ) {
scheduler - > setChoice ( bestChoice - transitionMatrix . getRowGroupIndices ( ) [ state ] , state ) ;
}
return result ;
}
// Solve MEC with the method specified in the settings
storm : : solver : : LraMethod method = env . solver ( ) . lra ( ) . getNondetLraMethod ( ) ;
if ( ( storm : : NumberTraits < ValueType > : : IsExact | | env . solver ( ) . isForceExact ( ) ) & & env . solver ( ) . lra ( ) . isNondetLraMethodSetFromDefault ( ) & & method ! = storm : : solver : : LraMethod : : LinearProgramming ) {
STORM_LOG_INFO ( " Selecting 'LP' as the solution technique for long-run properties to guarantee exact results. If you want to override this, please explicitly specify a different LRA method. " ) ;
method = storm : : solver : : LraMethod : : LinearProgramming ;
} else if ( env . solver ( ) . isForceSoundness ( ) & & env . solver ( ) . lra ( ) . isNondetLraMethodSetFromDefault ( ) & & method ! = storm : : solver : : LraMethod : : ValueIteration ) {
STORM_LOG_INFO ( " Selecting 'VI' as the solution technique for long-run properties to guarantee sound results. If you want to override this, please explicitly specify a different LRA method. " ) ;
method = storm : : solver : : LraMethod : : ValueIteration ;
}
STORM_LOG_ERROR_COND ( scheduler = = nullptr | | method = = storm : : solver : : LraMethod : : ValueIteration , " Scheduler generation not supported for the chosen LRA method. Try value-iteration. " ) ;
if ( method = = storm : : solver : : LraMethod : : LinearProgramming ) {
return computeLraForMaximalEndComponentLP ( env , dir , transitionMatrix , rewardModel , mec ) ;
} else if ( method = = storm : : solver : : LraMethod : : ValueIteration ) {
return computeLraForMaximalEndComponentVI ( env , dir , transitionMatrix , rewardModel , mec , scheduler ) ;
} else {
STORM_LOG_THROW ( false , storm : : exceptions : : InvalidSettingsException , " Unsupported technique. " ) ;
}
}
template < typename ValueType >
template < typename RewardModelType >
ValueType SparseMdpPrctlHelper < ValueType > : : computeLraForMaximalEndComponentVI ( Environment const & env , OptimizationDirection dir , storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix , RewardModelType const & rewardModel , storm : : storage : : MaximalEndComponent const & mec , std : : unique_ptr < storm : : storage : : Scheduler < ValueType > > & scheduler ) {
// Initialize data about the mec
storm : : storage : : BitVector mecStates ( transitionMatrix . getRowGroupCount ( ) , false ) ;
storm : : storage : : BitVector mecChoices ( transitionMatrix . getRowCount ( ) , false ) ;
for ( auto const & stateChoicesPair : mec ) {
mecStates . set ( stateChoicesPair . first ) ;
for ( auto const & choice : stateChoicesPair . second ) {
mecChoices . set ( choice ) ;
}
}
boost : : container : : flat_map < uint64_t , uint64_t > toSubModelStateMapping ;
uint64_t currState = 0 ;
toSubModelStateMapping . reserve ( mecStates . getNumberOfSetBits ( ) ) ;
for ( auto const & mecState : mecStates ) {
toSubModelStateMapping . insert ( std : : pair < uint64_t , uint64_t > ( mecState , currState ) ) ;
+ + currState ;
}
// Get a transition matrix that only considers the states and choices within the MEC
storm : : storage : : SparseMatrixBuilder < ValueType > mecTransitionBuilder ( mecChoices . getNumberOfSetBits ( ) , mecStates . getNumberOfSetBits ( ) , 0 , true , true , mecStates . getNumberOfSetBits ( ) ) ;
std : : vector < ValueType > choiceRewards ;
choiceRewards . reserve ( mecChoices . getNumberOfSetBits ( ) ) ;
uint64_t currRow = 0 ;
ValueType selfLoopProb = storm : : utility : : convertNumber < ValueType > ( env . solver ( ) . lra ( ) . getAperiodicFactor ( ) ) ;
ValueType scalingFactor = storm : : utility : : one < ValueType > ( ) - selfLoopProb ;
for ( auto const & mecState : mecStates ) {
mecTransitionBuilder . newRowGroup ( currRow ) ;
uint64_t groupStart = transitionMatrix . getRowGroupIndices ( ) [ mecState ] ;
uint64_t groupEnd = transitionMatrix . getRowGroupIndices ( ) [ mecState + 1 ] ;
for ( uint64_t choice = mecChoices . getNextSetIndex ( groupStart ) ; choice < groupEnd ; choice = mecChoices . getNextSetIndex ( choice + 1 ) ) {
bool insertedDiagElement = false ;
for ( auto const & entry : transitionMatrix . getRow ( choice ) ) {
uint64_t column = toSubModelStateMapping [ entry . getColumn ( ) ] ;
if ( ! insertedDiagElement & & entry . getColumn ( ) > mecState ) {
mecTransitionBuilder . addNextValue ( currRow , toSubModelStateMapping [ mecState ] , selfLoopProb ) ;
insertedDiagElement = true ;
}
if ( ! insertedDiagElement & & entry . getColumn ( ) = = mecState ) {
mecTransitionBuilder . addNextValue ( currRow , column , selfLoopProb + scalingFactor * entry . getValue ( ) ) ;
insertedDiagElement = true ;
} else {
mecTransitionBuilder . addNextValue ( currRow , column , scalingFactor * entry . getValue ( ) ) ;
}
}
if ( ! insertedDiagElement ) {
mecTransitionBuilder . addNextValue ( currRow , toSubModelStateMapping [ mecState ] , selfLoopProb ) ;
}
// Compute the rewards obtained for this choice
choiceRewards . push_back ( scalingFactor * rewardModel . getTotalStateActionReward ( mecState , choice , transitionMatrix ) ) ;
+ + currRow ;
}
}
auto mecTransitions = mecTransitionBuilder . build ( ) ;
STORM_LOG_ASSERT ( mecTransitions . isProbabilistic ( ) , " The MEC-Matrix is not probabilistic. " ) ;
// start the iterations
ValueType precision = storm : : utility : : convertNumber < ValueType > ( env . solver ( ) . lra ( ) . getPrecision ( ) ) / scalingFactor ;
bool relative = env . solver ( ) . lra ( ) . getRelativeTerminationCriterion ( ) ;
std : : vector < ValueType > x ( mecTransitions . getRowGroupCount ( ) , storm : : utility : : zero < ValueType > ( ) ) ;
std : : vector < ValueType > xPrime = x ;
auto multiplier = storm : : solver : : MultiplierFactory < ValueType > ( ) . create ( env , mecTransitions ) ;
ValueType maxDiff , minDiff ;
uint64_t iter = 0 ;
boost : : optional < uint64_t > maxIter ;
if ( env . solver ( ) . lra ( ) . isMaximalIterationCountSet ( ) ) {
maxIter = env . solver ( ) . lra ( ) . getMaximalIterationCount ( ) ;
}
while ( ! maxIter . is_initialized ( ) | | iter < maxIter . get ( ) ) {
+ + iter ;
// Compute the obtained rewards for the next step
multiplier - > multiplyAndReduce ( env , dir , x , & choiceRewards , x ) ;
// update xPrime and check for convergence
// to avoid large (and numerically unstable) x-values, we substract a reference value.
auto xIt = x . begin ( ) ;
auto xPrimeIt = xPrime . begin ( ) ;
ValueType refVal = * xIt ;
maxDiff = * xIt - * xPrimeIt ;
minDiff = maxDiff ;
* xIt - = refVal ;
* xPrimeIt = * xIt ;
for ( + + xIt , + + xPrimeIt ; xIt ! = x . end ( ) ; + + xIt , + + xPrimeIt ) {
ValueType diff = * xIt - * xPrimeIt ;
maxDiff = std : : max ( maxDiff , diff ) ;
minDiff = std : : min ( minDiff , diff ) ;
* xIt - = refVal ;
* xPrimeIt = * xIt ;
}
if ( ( maxDiff - minDiff ) < = ( relative ? ( precision * minDiff ) : precision ) ) {
break ;
}
if ( storm : : utility : : resources : : isTerminate ( ) ) {
break ;
}
}
if ( maxIter . is_initialized ( ) & & iter = = maxIter . get ( ) ) {
STORM_LOG_WARN ( " LRA computation did not converge within " < < iter < < " iterations. " ) ;
} else {
STORM_LOG_TRACE ( " LRA computation converged after " < < iter < < " iterations. " ) ;
}
if ( scheduler ) {
std : : vector < uint_fast64_t > localMecChoices ( mecTransitions . getRowGroupCount ( ) , 0 ) ;
multiplier - > multiplyAndReduce ( env , dir , x , & choiceRewards , x , & localMecChoices ) ;
auto localMecChoiceIt = localMecChoices . begin ( ) ;
for ( auto const & mecState : mecStates ) {
// Get the choice index of the selected mec choice with respect to the global transition matrix.
uint_fast64_t globalChoice = mecChoices . getNextSetIndex ( transitionMatrix . getRowGroupIndices ( ) [ mecState ] ) ;
for ( uint_fast64_t i = 0 ; i < * localMecChoiceIt ; + + i ) {
globalChoice = mecChoices . getNextSetIndex ( globalChoice + 1 ) ;
}
STORM_LOG_ASSERT ( globalChoice < transitionMatrix . getRowGroupIndices ( ) [ mecState + 1 ] , " Invalid global choice for mec state. " ) ;
scheduler - > setChoice ( globalChoice - transitionMatrix . getRowGroupIndices ( ) [ mecState ] , mecState ) ;
+ + localMecChoiceIt ;
}
}
return ( maxDiff + minDiff ) / ( storm : : utility : : convertNumber < ValueType > ( 2.0 ) * scalingFactor ) ;
}
template < typename ValueType >
template < typename RewardModelType >
ValueType SparseMdpPrctlHelper < ValueType > : : computeLraForMaximalEndComponentLP ( Environment const & env , OptimizationDirection dir , storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix , RewardModelType const & rewardModel , storm : : storage : : MaximalEndComponent const & mec ) {
std : : shared_ptr < storm : : solver : : LpSolver < ValueType > > solver = storm : : utility : : solver : : getLpSolver < ValueType > ( " LRA for MEC " ) ;
solver - > setOptimizationDirection ( invert ( dir ) ) ;
// First, we need to create the variables for the problem.
std : : map < uint_fast64_t , storm : : expressions : : Variable > stateToVariableMap ;
for ( auto const & stateChoicesPair : mec ) {
std : : string variableName = " h " + std : : to_string ( stateChoicesPair . first ) ;
stateToVariableMap [ stateChoicesPair . first ] = solver - > addUnboundedContinuousVariable ( variableName ) ;
}
storm : : expressions : : Variable lambda = solver - > addUnboundedContinuousVariable ( " L " , 1 ) ;
solver - > update ( ) ;
// Now we encode the problem as constraints.
for ( auto const & stateChoicesPair : mec ) {
uint_fast64_t state = stateChoicesPair . first ;
// Now, based on the type of the state, create a suitable constraint.
for ( auto choice : stateChoicesPair . second ) {
storm : : expressions : : Expression constraint = - lambda ;
for ( auto element : transitionMatrix . getRow ( choice ) ) {
constraint = constraint + stateToVariableMap . at ( element . getColumn ( ) ) * solver - > getConstant ( element . getValue ( ) ) ;
}
typename RewardModelType : : ValueType r = rewardModel . getTotalStateActionReward ( state , choice , transitionMatrix ) ;
constraint = solver - > getConstant ( r ) + constraint ;
if ( dir = = OptimizationDirection : : Minimize ) {
constraint = stateToVariableMap . at ( state ) < = constraint ;
} else {
constraint = stateToVariableMap . at ( state ) > = constraint ;
}
solver - > addConstraint ( " state " + std : : to_string ( state ) + " , " + std : : to_string ( choice ) , constraint ) ;
}
}
solver - > optimize ( ) ;
return solver - > getContinuousValue ( lambda ) ;
}
template < typename ValueType >
std : : unique_ptr < CheckResult > SparseMdpPrctlHelper < ValueType > : : computeConditionalProbabilities ( Environment const & env , storm : : solver : : SolveGoal < ValueType > & & goal , storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix , storm : : storage : : SparseMatrix < ValueType > const & backwardTransitions , storm : : storage : : BitVector const & targetStates , storm : : storage : : BitVector const & conditionStates ) {
@ -1818,10 +1359,6 @@ namespace storm {
template std : : vector < double > SparseMdpPrctlHelper < double > : : computeCumulativeRewards ( Environment const & env , storm : : solver : : SolveGoal < double > & & goal , storm : : storage : : SparseMatrix < double > const & transitionMatrix , storm : : models : : sparse : : StandardRewardModel < double > const & rewardModel , uint_fast64_t stepBound ) ;
template MDPSparseModelCheckingHelperReturnType < double > SparseMdpPrctlHelper < double > : : computeReachabilityRewards ( Environment const & env , storm : : solver : : SolveGoal < double > & & goal , storm : : storage : : SparseMatrix < double > const & transitionMatrix , storm : : storage : : SparseMatrix < double > const & backwardTransitions , storm : : models : : sparse : : StandardRewardModel < double > const & rewardModel , storm : : storage : : BitVector const & targetStates , bool qualitative , bool produceScheduler , ModelCheckerHint const & hint ) ;
template MDPSparseModelCheckingHelperReturnType < double > SparseMdpPrctlHelper < double > : : computeTotalRewards ( Environment const & env , storm : : solver : : SolveGoal < double > & & goal , storm : : storage : : SparseMatrix < double > const & transitionMatrix , storm : : storage : : SparseMatrix < double > const & backwardTransitions , storm : : models : : sparse : : StandardRewardModel < double > const & rewardModel , bool qualitative , bool produceScheduler , ModelCheckerHint const & hint ) ;
template MDPSparseModelCheckingHelperReturnType < double > SparseMdpPrctlHelper < double > : : computeLongRunAverageRewards ( Environment const & env , storm : : solver : : SolveGoal < double > & & goal , storm : : storage : : SparseMatrix < double > const & transitionMatrix , storm : : storage : : SparseMatrix < double > const & backwardTransitions , storm : : models : : sparse : : StandardRewardModel < double > const & rewardModel , bool produceScheduler ) ;
template double SparseMdpPrctlHelper < double > : : computeLraForMaximalEndComponent ( Environment const & env , OptimizationDirection dir , storm : : storage : : SparseMatrix < double > const & transitionMatrix , storm : : models : : sparse : : StandardRewardModel < double > const & rewardModel , storm : : storage : : MaximalEndComponent const & mec , std : : unique_ptr < storm : : storage : : Scheduler < double > > & scheduler ) ;
template double SparseMdpPrctlHelper < double > : : computeLraForMaximalEndComponentVI ( Environment const & env , OptimizationDirection dir , storm : : storage : : SparseMatrix < double > const & transitionMatrix , storm : : models : : sparse : : StandardRewardModel < double > const & rewardModel , storm : : storage : : MaximalEndComponent const & mec , std : : unique_ptr < storm : : storage : : Scheduler < double > > & scheduler ) ;
template double SparseMdpPrctlHelper < double > : : computeLraForMaximalEndComponentLP ( Environment const & env , OptimizationDirection dir , storm : : storage : : SparseMatrix < double > const & transitionMatrix , storm : : models : : sparse : : StandardRewardModel < double > const & rewardModel , storm : : storage : : MaximalEndComponent const & mec ) ;
# ifdef STORM_HAVE_CARL
template class SparseMdpPrctlHelper < storm : : RationalNumber > ;
@ -1829,10 +1366,6 @@ namespace storm {
template std : : vector < storm : : RationalNumber > SparseMdpPrctlHelper < storm : : RationalNumber > : : computeCumulativeRewards ( Environment const & env , storm : : solver : : SolveGoal < storm : : RationalNumber > & & goal , storm : : storage : : SparseMatrix < storm : : RationalNumber > const & transitionMatrix , storm : : models : : sparse : : StandardRewardModel < storm : : RationalNumber > const & rewardModel , uint_fast64_t stepBound ) ;
template MDPSparseModelCheckingHelperReturnType < storm : : RationalNumber > SparseMdpPrctlHelper < storm : : RationalNumber > : : computeReachabilityRewards ( Environment const & env , storm : : solver : : SolveGoal < storm : : RationalNumber > & & goal , storm : : storage : : SparseMatrix < storm : : RationalNumber > const & transitionMatrix , storm : : storage : : SparseMatrix < storm : : RationalNumber > const & backwardTransitions , storm : : models : : sparse : : StandardRewardModel < storm : : RationalNumber > const & rewardModel , storm : : storage : : BitVector const & targetStates , bool qualitative , bool produceScheduler , ModelCheckerHint const & hint ) ;
template MDPSparseModelCheckingHelperReturnType < storm : : RationalNumber > SparseMdpPrctlHelper < storm : : RationalNumber > : : computeTotalRewards ( Environment const & env , storm : : solver : : SolveGoal < storm : : RationalNumber > & & goal , storm : : storage : : SparseMatrix < storm : : RationalNumber > const & transitionMatrix , storm : : storage : : SparseMatrix < storm : : RationalNumber > const & backwardTransitions , storm : : models : : sparse : : StandardRewardModel < storm : : RationalNumber > const & rewardModel , bool qualitative , bool produceScheduler , ModelCheckerHint const & hint ) ;
template MDPSparseModelCheckingHelperReturnType < storm : : RationalNumber > SparseMdpPrctlHelper < storm : : RationalNumber > : : computeLongRunAverageRewards ( Environment const & env , storm : : solver : : SolveGoal < storm : : RationalNumber > & & goal , storm : : storage : : SparseMatrix < storm : : RationalNumber > const & transitionMatrix , storm : : storage : : SparseMatrix < storm : : RationalNumber > const & backwardTransitions , storm : : models : : sparse : : StandardRewardModel < storm : : RationalNumber > const & rewardModel , bool produceScheduler ) ;
template storm : : RationalNumber SparseMdpPrctlHelper < storm : : RationalNumber > : : computeLraForMaximalEndComponent ( Environment const & env , OptimizationDirection dir , storm : : storage : : SparseMatrix < storm : : RationalNumber > const & transitionMatrix , storm : : models : : sparse : : StandardRewardModel < storm : : RationalNumber > const & rewardModel , storm : : storage : : MaximalEndComponent const & mec , std : : unique_ptr < storm : : storage : : Scheduler < storm : : RationalNumber > > & scheduler ) ;
template storm : : RationalNumber SparseMdpPrctlHelper < storm : : RationalNumber > : : computeLraForMaximalEndComponentVI ( Environment const & env , OptimizationDirection dir , storm : : storage : : SparseMatrix < storm : : RationalNumber > const & transitionMatrix , storm : : models : : sparse : : StandardRewardModel < storm : : RationalNumber > const & rewardModel , storm : : storage : : MaximalEndComponent const & mec , std : : unique_ptr < storm : : storage : : Scheduler < storm : : RationalNumber > > & scheduler ) ;
template storm : : RationalNumber SparseMdpPrctlHelper < storm : : RationalNumber > : : computeLraForMaximalEndComponentLP ( Environment const & env , OptimizationDirection dir , storm : : storage : : SparseMatrix < storm : : RationalNumber > const & transitionMatrix , storm : : models : : sparse : : StandardRewardModel < storm : : RationalNumber > const & rewardModel , storm : : storage : : MaximalEndComponent const & mec ) ;
# endif
}
}