@ -192,10 +192,10 @@ namespace storm {
}
}
template < typename ValueType >
template < typename ValueType >
void computeSchedulerProb1 ( storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix , storm : : storage : : SparseMatrix < ValueType > const & backwardTransitions , storm : : storage : : BitVector const & consideredStates , storm : : storage : : BitVector & statesToReach , std : : vector < uint64_t > & choices ) {
void computeSchedulerProb1 ( storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix , storm : : storage : : SparseMatrix < ValueType > const & backwardTransitions , storm : : storage : : BitVector const & consideredStates , storm : : storage : : BitVector const & statesToReach , std : : vector < uint64_t > & choices , storm : : storage : : BitVector const * allowedChoices = nullptr ) {
std : : vector < uint64_t > stack ;
std : : vector < uint64_t > stack ;
storm : : storage : : BitVector & processedStates = statesToReach ;
storm : : storage : : BitVector processedStates = statesToReach ;
stack . insert ( stack . end ( ) , processedStates . begin ( ) , processedStates . end ( ) ) ;
stack . insert ( stack . end ( ) , processedStates . begin ( ) , processedStates . end ( ) ) ;
uint64_t currentState = 0 ;
uint64_t currentState = 0 ;
@ -209,8 +209,8 @@ namespace storm {
// Find a choice leading to an already processed state (such a choice has to exist since this is a predecessor of the currentState)
// Find a choice leading to an already processed state (such a choice has to exist since this is a predecessor of the currentState)
auto const & groupStart = transitionMatrix . getRowGroupIndices ( ) [ predecessor ] ;
auto const & groupStart = transitionMatrix . getRowGroupIndices ( ) [ predecessor ] ;
auto const & groupEnd = transitionMatrix . getRowGroupIndices ( ) [ predecessor + 1 ] ;
auto const & groupEnd = transitionMatrix . getRowGroupIndices ( ) [ predecessor + 1 ] ;
uint64_t row = groupStart ;
for ( ; row < groupEnd ; + + row ) {
uint64_t row = allowedChoices ? allowedChoices - > getNextSetIndex ( groupStart ) : groupStart ;
for ( ; row < groupEnd ; row = allowedChoices ? allowedChoices - > getNextSetIndex ( row + 1 ) : row + 1 ) {
bool hasSuccessorInProcessedStates = false ;
bool hasSuccessorInProcessedStates = false ;
for ( auto const & successorOfPredecessor : transitionMatrix . getRow ( row ) ) {
for ( auto const & successorOfPredecessor : transitionMatrix . getRow ( row ) ) {
if ( processedStates . get ( successorOfPredecessor . getColumn ( ) ) ) {
if ( processedStates . get ( successorOfPredecessor . getColumn ( ) ) ) {
@ -225,13 +225,37 @@ namespace storm {
break ;
break ;
}
}
}
}
STORM_LOG_ASSERT ( row < groupEnd , " Unable to find choice at a predecessor of a processed state that leads to a processed state. " ) ;
STORM_LOG_ASSERT ( allowedChoices | | row < groupEnd , " Unable to find choice at a predecessor of a processed state that leads to a processed state. " ) ;
}
}
}
}
}
}
STORM_LOG_ASSERT ( consideredStates . isSubsetOf ( processedStates ) , " Not all states have been processed. " ) ;
STORM_LOG_ASSERT ( consideredStates . isSubsetOf ( processedStates ) , " Not all states have been processed. " ) ;
}
}
template < typename ValueType >
void computeSchedulerProb0 ( storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix , storm : : storage : : SparseMatrix < ValueType > const & backwardTransitions , storm : : storage : : BitVector const & consideredStates , storm : : storage : : BitVector const & statesToAvoid , storm : : storage : : BitVector const & allowedChoices , std : : vector < uint64_t > & choices ) {
for ( auto state : consideredStates ) {
auto const & groupStart = transitionMatrix . getRowGroupIndices ( ) [ state ] ;
auto const & groupEnd = transitionMatrix . getRowGroupIndices ( ) [ state + 1 ] ;
bool choiceFound = false ;
for ( uint64_t row = allowedChoices . getNextSetIndex ( groupStart ) ; row < groupEnd ; row = allowedChoices . getNextSetIndex ( row + 1 ) ) {
choiceFound = true ;
for ( auto const & element : transitionMatrix . getRow ( row ) ) {
if ( statesToAvoid . get ( element . getColumn ( ) ) ) {
choiceFound = false ;
break ;
}
}
if ( choiceFound ) {
choices [ state ] = row - groupStart ;
break ;
}
}
STORM_LOG_ASSERT ( choiceFound , " Unable to find choice for a state. " ) ;
}
}
template < typename ValueType >
template < typename ValueType >
std : : vector < uint64_t > computeValidInitialScheduler ( storm : : storage : : SparseMatrix < ValueType > const & matrix , storm : : storage : : BitVector const & rowsWithSumLessOne ) {
std : : vector < uint64_t > computeValidInitialScheduler ( storm : : storage : : SparseMatrix < ValueType > const & matrix , storm : : storage : : BitVector const & rowsWithSumLessOne ) {
std : : vector < uint64_t > result ( matrix . getRowGroupCount ( ) ) ;
std : : vector < uint64_t > result ( matrix . getRowGroupCount ( ) ) ;
@ -249,42 +273,28 @@ namespace storm {
return result ;
return result ;
}
}
/*!
/*!
* Computes a scheduler taking the choices from the given set only finitely often
* Computes a scheduler taking the choices from the given set only finitely often
* @ param safeStates it is assumed that reaching such a state is unproblematic . The choice for these states is not set .
* @ pre such a scheduler exists
* @ pre such a scheduler exists
*/
*/
template < typename ValueType >
template < typename ValueType >
std : : vector < uint64_t > computeSchedulerFinitelyOften ( storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix , storm : : storage : : SparseMatrix < ValueType > const & backwardTransitions , storm : : storage : : BitVector const & finitelyOftenChoices ) {
std : : vector < uint64_t > result ( transitionMatrix . getRowGroupCount ( ) , 0 ) ;
auto badStates = transitionMatrix . getRowGroupFilter ( finitelyOftenChoices , true ) ;
void computeSchedulerFinitelyOften ( storm : : storage : : SparseMatrix < ValueType > const & transitionMatrix , storm : : storage : : SparseMatrix < ValueType > const & backwardTransitions , storm : : storage : : BitVector const & finitelyOftenChoices , storm : : storage : : BitVector safeStates , std : : vector < uint64_t > & choices ) {
auto badStates = transitionMatrix . getRowGroupFilter ( finitelyOftenChoices , true ) & ~ safeStates ;
// badStates shall only be reached finitely often
// badStates shall only be reached finitely often
auto reachBadWithProbGreater0AStates = storm : : utility : : graph : : performProbGreater0A ( transitionMatrix , transitionMatrix . getRowGroupIndices ( ) , backwardTransitions , ~ badStates , badStates , false , 0 , ~ finitelyOftenChoices ) ;
// States in ~reachBadWithProbGreater0AStates can avoid bad states forever by only taking ~finitelyOftenChoices. We compute a scheduler for these states achieving exactly this
auto avoidBadStates = ~ reachBadWithProbGreater0AStates ;
for ( auto state : avoidBadStates ) {
auto const & groupStart = transitionMatrix . getRowGroupIndices ( ) [ state ] ;
auto const & groupEnd = transitionMatrix . getRowGroupIndices ( ) [ state + 1 ] ;
for ( auto choice = finitelyOftenChoices . getNextUnsetIndex ( groupStart ) ; choice < groupEnd ; choice = finitelyOftenChoices . getNextUnsetIndex ( choice + 1 ) ) {
bool allSuccessorsAvoidBadStates = true ;
for ( auto const & element : transitionMatrix . getRow ( choice ) ) {
if ( ! avoidBadStates . get ( element . getColumn ( ) ) ) {
allSuccessorsAvoidBadStates = false ;
break ;
}
}
if ( allSuccessorsAvoidBadStates ) {
result [ state ] = choice - groupStart ;
break ;
}
}
}
// Finally, we need to take care of states that will reach a bad state with prob greater 0 (including the bad states themselves).
auto reachBadWithProbGreater0AStates = storm : : utility : : graph : : performProbGreater0A ( transitionMatrix , transitionMatrix . getRowGroupIndices ( ) , backwardTransitions , ~ safeStates , badStates , false , 0 , ~ finitelyOftenChoices ) ;
// States in ~reachBadWithProbGreater0AStates can avoid bad states forever by only taking ~finitelyOftenChoices.
// We compute a scheduler for these states achieving exactly this (but we exclude the safe states)
auto avoidBadStates = ~ reachBadWithProbGreater0AStates & ~ safeStates ;
computeSchedulerProb0 ( transitionMatrix , backwardTransitions , avoidBadStates , reachBadWithProbGreater0AStates , ~ finitelyOftenChoices , choices ) ;
// We need to take care of states that will reach a bad state with prob greater 0 (including the bad states themselves).
// due to the precondition, we know that it has to be possible to eventually avoid the bad states for ever.
// due to the precondition, we know that it has to be possible to eventually avoid the bad states for ever.
// Perform a backwards search from the avoid states and store choices with prob. 1
// Perform a backwards search from the avoid states and store choices with prob. 1
computeSchedulerProb1 ( transitionMatrix , backwardTransitions , reachBadWithProbGreater0AStates , avoidBadStates , result ) ;
return result ;
computeSchedulerProb1 ( transitionMatrix , backwardTransitions , reachBadWithProbGreater0AStates , avoidBadStates | safeStates , choices ) ;
}
}
template < class SparseModelType >
template < class SparseModelType >
@ -312,10 +322,19 @@ namespace storm {
template < class SparseModelType >
template < class SparseModelType >
void StandardPcaaWeightVectorChecker < SparseModelType > : : unboundedWeightedPhase ( Environment const & env , std : : vector < ValueType > const & weightedRewardVector , std : : vector < ValueType > const & weightVector ) {
void StandardPcaaWeightVectorChecker < SparseModelType > : : unboundedWeightedPhase ( Environment const & env , std : : vector < ValueType > const & weightedRewardVector , std : : vector < ValueType > const & weightVector ) {
if ( this - > objectivesWithNoUpperTimeBound . empty ( ) | | ( this - > lraObjectives . empty ( ) & & ! storm : : utility : : vector : : hasNonZeroEntry ( weightedRewardVector ) ) ) {
this - > weightedResult = std : : vector < ValueType > ( transitionMatrix . getRowGroupCount ( ) , storm : : utility : : zero < ValueType > ( ) ) ;
// Catch the case where all values on the RHS of the MinMax equation system are zero.
if ( this - > objectivesWithNoUpperTimeBound . empty ( ) | | ( ( this - > lraObjectives . empty ( ) | | ! storm : : utility : : vector : : hasNonZeroEntry ( lraMecDecomposition - > auxMecValues ) ) & & ! storm : : utility : : vector : : hasNonZeroEntry ( weightedRewardVector ) ) ) {
this - > weightedResult . assign ( transitionMatrix . getRowGroupCount ( ) , storm : : utility : : zero < ValueType > ( ) ) ;
storm : : storage : : BitVector statesInLraMec ( transitionMatrix . getRowGroupCount ( ) , false ) ;
if ( this - > lraMecDecomposition ) {
for ( auto const & mec : this - > lraMecDecomposition - > mecs ) {
for ( auto const & sc : mec ) {
statesInLraMec . set ( sc . first , true ) ;
}
}
}
// Get an arbitrary scheduler that yields finite reward for all objectives
// Get an arbitrary scheduler that yields finite reward for all objectives
this - > optimalChoices = computeSchedulerFinitelyOften ( transitionMatrix , transitionMatrix . transpose ( true ) , ~ actionsWithoutRewardInUnboundedPhase ) ;
computeSchedulerFinitelyOften ( transitionMatrix , transitionMatrix . transpose ( true ) , ~ actionsWithoutRewardInUnboundedPhase , statesInLraMec , this - > optimalChoices ) ;
return ;
return ;
}
}
@ -674,39 +693,80 @@ namespace storm {
uint_fast64_t origChoice = ecQuotient - > ecqToOriginalChoiceMapping [ ecqChoice ] ;
uint_fast64_t origChoice = ecQuotient - > ecqToOriginalChoiceMapping [ ecqChoice ] ;
auto const & origStates = ecQuotient - > ecqToOriginalStateMapping [ ecqState ] ;
auto const & origStates = ecQuotient - > ecqToOriginalStateMapping [ ecqState ] ;
STORM_LOG_ASSERT ( ! origStates . empty ( ) , " Unexpected empty set of original states. " ) ;
STORM_LOG_ASSERT ( ! origStates . empty ( ) , " Unexpected empty set of original states. " ) ;
bool origStatesNeedSchedulerComputation = false ;
if ( ecQuotient - > ecqStayInEcChoices . get ( ecqChoice ) & & ! ecqStateToOptimalMecMap . empty ( ) ) {
// The current ecqState represents an elimnated EC and we need to stay in this EC and we need to make sure that optimal MEC decisions are performed within this EC.
STORM_LOG_ASSERT ( ecqStateToOptimalMecMap . count ( ecqState ) > 0 , " No Lra Mec associated to given eliminated EC " ) ;
auto const & lraMec = lraMecDecomposition - > mecs [ ecqStateToOptimalMecMap . at ( ecqState ) ] ;
if ( lraMec . size ( ) = = origStates . size ( ) ) {
// LRA mec and eliminated EC coincide
for ( auto const & state : origStates ) {
STORM_LOG_ASSERT ( lraMec . containsState ( state ) , " Expected state to be contained in the lra mec. " ) ;
// Note that the optimal choice for this state has already been set in the infinite horizon phase.
unprocessedStates . set ( state , false ) ;
originalSolution [ state ] = ecqSolution [ ecqState ] ;
if ( ecQuotient - > ecqStayInEcChoices . get ( ecqChoice ) ) {
// We stay in the current state(s) forever (End component)
// We need to set choices in a way that (i) the optimal LRA Mec is reached (if there is any) and (ii) 0 total reward is collected.
if ( ! ecqStateToOptimalMecMap . empty ( ) ) {
// The current ecqState represents an elimnated EC and we need to stay in this EC and we need to make sure that optimal MEC decisions are performed within this EC.
STORM_LOG_ASSERT ( ecqStateToOptimalMecMap . count ( ecqState ) > 0 , " No Lra Mec associated to given eliminated EC " ) ;
auto const & lraMec = lraMecDecomposition - > mecs [ ecqStateToOptimalMecMap . at ( ecqState ) ] ;
if ( lraMec . size ( ) = = origStates . size ( ) ) {
// LRA mec and eliminated EC coincide
for ( auto const & state : origStates ) {
STORM_LOG_ASSERT ( lraMec . containsState ( state ) , " Expected state to be contained in the lra mec. " ) ;
// Note that the optimal choice for this state has already been set in the infinite horizon phase.
unprocessedStates . set ( state , false ) ;
originalSolution [ state ] = ecqSolution [ ecqState ] ;
}
} else {
// LRA mec is proper subset of eliminated ec. There are also other states for which we have to set choices leading to the LRA MEC inside.
STORM_LOG_ASSERT ( lraMec . size ( ) < origStates . size ( ) , " Lra Mec should be a proper subset of the eliminated ec. " ) ;
for ( auto const & state : origStates ) {
if ( lraMec . containsState ( state ) ) {
ecStatesToReach . set ( state , true ) ;
// Note that the optimal choice for this state has already been set in the infinite horizon phase.
} else {
ecStatesToProcess . set ( state , true ) ;
}
unprocessedStates . set ( state , false ) ;
originalSolution [ state ] = ecqSolution [ ecqState ] ;
}
computeSchedulerProb1 ( transitionMatrix , backwardsTransitions , ecStatesToProcess , ecStatesToReach , originalOptimalChoices ) ;
// Clear bitvectors for next ecqState.
ecStatesToProcess . clear ( ) ;
ecStatesToReach . clear ( ) ;
}
}
} else {
} else {
// LRA mec is proper subset of eliminated ec. There are also other states for which we have to set choices leading to the LRA MEC inside.
STORM_LOG_ASSERT ( lraMec . size ( ) < origStates . size ( ) , " Lra Mec should be a proper subset of the eliminated ec. " ) ;
origStatesNeedSchedulerComputation = true ;
// If there is no LRA Mec to reach, we just need to make sure that finite total reward is collected for all objectives
// In this branch our BitVectors have a slightly different meaning, so we create more readable aliases
storm : : storage : : BitVector & ecStatesToAvoid = ecStatesToReach ;
bool needSchedulerComputation = false ;
STORM_LOG_ASSERT ( storm : : utility : : isZero ( ecqSolution [ ecqState ] ) , " Solution for state that stays inside EC must be zero. Got " < < ecqSolution [ ecqState ] < < " instead. " ) ;
for ( auto const & state : origStates ) {
for ( auto const & state : origStates ) {
if ( lraMec . containsState ( state ) ) {
ecStatesToReach . set ( state , true ) ;
// Note that the optimal choice for this state has already been set in the infinite horizon phase.
originalSolution [ state ] = storm : : utility : : zero < ValueType > ( ) ; // i.e. ecqSolution[ecqState];
ecStatesToProcess . set ( state , true ) ;
}
auto validChoices = transitionMatrix . getRowFilter ( ecStatesToProcess , ecStatesToProcess ) ;
auto valid0RewardChoices = validChoices & actionsWithoutRewardInUnboundedPhase ;
for ( auto const & state : origStates ) {
auto groupStart = transitionMatrix . getRowGroupIndices ( ) [ state ] ;
auto groupEnd = transitionMatrix . getRowGroupIndices ( ) [ state + 1 ] ;
auto nextValidChoice = valid0RewardChoices . getNextSetIndex ( groupStart ) ;
if ( nextValidChoice < groupEnd ) {
originalOptimalChoices [ state ] = nextValidChoice - groupStart ;
} else {
} else {
ecStatesToProcess . set ( state , true ) ;
// this state should not be reached infinitely often
ecStatesToAvoid . set ( state , true ) ;
needSchedulerComputation = true ;
}
}
unprocessedStates . set ( state , false ) ;
originalSolution [ state ] = ecqSolution [ ecqState ] ;
}
}
if ( needSchedulerComputation ) {
// There are ec states which we should not visit infinitely often
auto ecStatesThatCanAvoid = storm : : utility : : graph : : performProbGreater0A ( transitionMatrix , transitionMatrix . getRowGroupIndices ( ) , backwardsTransitions , ecStatesToProcess , ecStatesToAvoid , false , 0 , valid0RewardChoices ) ;
ecStatesThatCanAvoid . complement ( ) ;
// Set the choice for all states that can achieve value 0
computeSchedulerProb0 ( transitionMatrix , backwardsTransitions , ecStatesThatCanAvoid , ecStatesToAvoid , valid0RewardChoices , originalOptimalChoices ) ;
// Set the choice for all remaining states
computeSchedulerProb1 ( transitionMatrix , backwardsTransitions , ecStatesToProcess & ~ ecStatesToAvoid , ecStatesToAvoid , originalOptimalChoices , & validChoices ) ;
}
ecStatesToAvoid . clear ( ) ;
ecStatesToProcess . clear ( ) ;
}
}
} else {
} else {
// We eventually leave the current state(s)
// In this case, we can safely take the origChoice at the corresponding state (say 's').
// In this case, we can safely take the origChoice at the corresponding state (say 's').
// For all other origStates associated with ecqState (if there are any), we make sure that the state 's' is reached almost surely.
// For all other origStates associated with ecqState (if there are any), we make sure that the state 's' is reached almost surely.
if ( origStates . size ( ) > 1 ) {
if ( origStates . size ( ) > 1 ) {
origStatesNeedSchedulerComputation = true ;
for ( auto const & state : origStates ) {
for ( auto const & state : origStates ) {
// Check if the orig choice originates from this state
// Check if the orig choice originates from this state
auto groupStart = transitionMatrix . getRowGroupIndices ( ) [ state ] ;
auto groupStart = transitionMatrix . getRowGroupIndices ( ) [ state ] ;
@ -721,6 +781,10 @@ namespace storm {
unprocessedStates . set ( state , false ) ;
unprocessedStates . set ( state , false ) ;
originalSolution [ state ] = ecqSolution [ ecqState ] ;
originalSolution [ state ] = ecqSolution [ ecqState ] ;
}
}
computeSchedulerProb1 ( transitionMatrix , backwardsTransitions , ecStatesToProcess , ecStatesToReach , originalOptimalChoices ) ;
// Clear bitvectors for next ecqState.
ecStatesToProcess . clear ( ) ;
ecStatesToReach . clear ( ) ;
} else {
} else {
// There is just one state so we take the associated choice.
// There is just one state so we take the associated choice.
auto state = * origStates . begin ( ) ;
auto state = * origStates . begin ( ) ;
@ -731,19 +795,29 @@ namespace storm {
unprocessedStates . set ( state , false ) ;
unprocessedStates . set ( state , false ) ;
}
}
}
}
if ( origStatesNeedSchedulerComputation ) {
computeSchedulerProb1 ( transitionMatrix , backwardsTransitions , ecStatesToProcess , ecStatesToReach , originalOptimalChoices ) ;
// These states have only been altered if the origStatesNeedSchedulerComputation flag has been set to true. Hence, they only need to be cleared in this if-branch.
ecStatesToProcess . clear ( ) ;
ecStatesToReach . clear ( ) ;
}
}
}
// The states that still not have been processed, there is no associated state of the ec quotient.
// The states that still not have been processed, there is no associated state of the ec quotient.
// This is because the value for these states will be 0 under all (lra optimal-) schedulers.
// This is because the value for these states will be 0 under all (lra optimal-) schedulers.
// We thus do not change the choices at these states (to remain LRA optimality) and just set the result to 0
storm : : utility : : vector : : setVectorValues ( originalSolution , unprocessedStates , storm : : utility : : zero < ValueType > ( ) ) ;
storm : : utility : : vector : : setVectorValues ( originalSolution , unprocessedStates , storm : : utility : : zero < ValueType > ( ) ) ;
// Get a set of states for which we know that no reward (for all objectives) will be collected
if ( this - > lraMecDecomposition ) {
// In this case, all unprocessed non-lra mec states should reach an (unprocessed) lra mec
for ( auto const & mec : this - > lraMecDecomposition - > mecs ) {
for ( auto const & sc : mec ) {
if ( unprocessedStates . get ( sc . first ) ) {
ecStatesToReach . set ( sc . first , true ) ;
}
}
}
} else {
ecStatesToReach = unprocessedStates & totalReward0EStates ;
// Set a scheduler for the ecStates that we want to reach
computeSchedulerProb0 ( transitionMatrix , backwardsTransitions , ecStatesToReach , ~ unprocessedStates | ~ totalReward0EStates , actionsWithoutRewardInUnboundedPhase , originalOptimalChoices ) ;
}
unprocessedStates & = ~ ecStatesToReach ;
// Set a scheduler for the remaining states
computeSchedulerProb1 ( transitionMatrix , backwardsTransitions , unprocessedStates , ecStatesToReach , originalOptimalChoices ) ;
}
}