@ -14,6 +14,8 @@
# include "storm/models/symbolic/StandardRewardModel.h"
# include "storm/modelchecker/prctl/helper/SparseMdpEndComponentInformation.h"
# include "storm/modelchecker/prctl/helper/DsMpiUpperRewardBoundsComputer.h"
# include "storm/modelchecker/prctl/helper/BaierUpperRewardBoundsComputer.h"
# include "storm/modelchecker/results/SymbolicQualitativeCheckResult.h"
# include "storm/modelchecker/results/SymbolicQuantitativeCheckResult.h"
# include "storm/modelchecker/results/HybridQuantitativeCheckResult.h"
@ -32,6 +34,7 @@ namespace storm {
boost : : optional < SparseMdpEndComponentInformation < ValueType > > ecInformation ;
boost : : optional < std : : vector < uint64_t > > initialScheduler ;
storm : : storage : : BitVector properMaybeStates ;
boost : : optional < std : : vector < ValueType > > oneStepTargetProbabilities ;
} ;
template < typename ValueType >
@ -66,15 +69,17 @@ namespace storm {
}
template < typename ValueType >
void eliminateExtendedStatesFromExplicitRepresentation ( std : : pair < storm : : storage : : SparseMatrix < ValueType > , std : : vector < ValueType > > & explicitRepresentation , std : : vector < uint64_t > & scheduler , storm : : storage : : BitVector const & properMaybeStates ) {
void eliminateExtendedStatesFromExplicitRepresentation ( std : : pair < storm : : storage : : SparseMatrix < ValueType > , std : : vector < ValueType > > & explicitRepresentation , boost : : optional < std : : vector < uint64_t > > & scheduler , storm : : storage : : BitVector const & properMaybeStates ) {
if ( scheduler ) {
// Eliminate superfluous entries from the scheduler.
uint64_t position = 0 ;
for ( auto state : properMaybeStates ) {
scheduler [ position ] = scheduler [ state ] ;
scheduler . get ( ) [ position ] = scheduler . get ( ) [ state ] ;
position + + ;
}
scheduler . resize ( properMaybeStates . getNumberOfSetBits ( ) ) ;
scheduler . shrink_to_fit ( ) ;
scheduler . get ( ) . resize ( properMaybeStates . getNumberOfSetBits ( ) ) ;
scheduler . get ( ) . shrink_to_fit ( ) ;
}
// Treat the matrix.
explicitRepresentation . first = explicitRepresentation . first . getSubmatrix ( true , properMaybeStates , properMaybeStates ) ;
@ -103,7 +108,7 @@ namespace storm {
oneStepProbabilities = std : : move ( subvector ) ;
} else {
STORM_LOG_DEBUG ( " Not eliminating ECs as there are none. " ) ;
eliminateExtendedStatesFromExplicitRepresentation ( explicitRepresentation , solverRequirementsData . initialScheduler . get ( ) , solverRequirementsData . properMaybeStates ) ;
eliminateExtendedStatesFromExplicitRepresentation ( explicitRepresentation , solverRequirementsData . initialScheduler , solverRequirementsData . properMaybeStates ) ;
oneStepProbabilities = explicitRepresentation . first . getConstrainedRowGroupSumVector ( solverRequirementsData . properMaybeStates , targetStates ) ;
}
}
@ -192,14 +197,11 @@ namespace storm {
storm : : dd : : Add < DdType , ValueType > subvector = submatrix * prob1StatesAsColumn ;
subvector = subvector . sumAbstract ( model . getColumnVariables ( ) ) ;
// Before cutting the non-maybe columns, we need to compute the sizes of the row groups.
std : : vector < uint_fast64_t > rowGroupSizes = submatrix . notZero ( ) . existsAbstract ( model . getColumnVariables ( ) ) . template toAdd < uint_fast64_t > ( ) . sumAbstract ( model . getNondeterminismVariables ( ) ) . toVector ( odd ) ;
// Finally cut away all columns targeting non-maybe states.
submatrix * = maybeStatesAdd . swapVariables ( model . getRowColumnMetaVariablePairs ( ) ) ;
// Translate the symbolic matrix/vector to their explicit representations and solve the equation system.
explicitRepresentation = submatrix . toMatrixVector ( subvector , std : : move ( rowGroupSizes ) , model . getNondeterminismVariables ( ) , odd , odd ) ;
explicitRepresentation = submatrix . toMatrixVector ( subvector , model . getNondeterminismVariables ( ) , odd , odd ) ;
if ( requirements . requiresValidInitialScheduler ( ) ) {
solverRequirementsData . initialScheduler = computeValidInitialSchedulerForUntilProbabilities < ValueType > ( explicitRepresentation . first , explicitRepresentation . second ) ;
@ -280,9 +282,6 @@ namespace storm {
storm : : dd : : Add < DdType , ValueType > prob1StatesAsColumn = psiStates . template toAdd < ValueType > ( ) . swapVariables ( model . getRowColumnMetaVariablePairs ( ) ) ;
storm : : dd : : Add < DdType , ValueType > subvector = ( submatrix * prob1StatesAsColumn ) . sumAbstract ( model . getColumnVariables ( ) ) ;
// Before cutting the non-maybe columns, we need to compute the sizes of the row groups.
std : : vector < uint_fast64_t > rowGroupSizes = submatrix . notZero ( ) . existsAbstract ( model . getColumnVariables ( ) ) . template toAdd < uint_fast64_t > ( ) . sumAbstract ( model . getNondeterminismVariables ( ) ) . toVector ( odd ) ;
// Finally cut away all columns targeting non-maybe states.
submatrix * = maybeStatesAdd . swapVariables ( model . getRowColumnMetaVariablePairs ( ) ) ;
@ -290,7 +289,7 @@ namespace storm {
std : : vector < ValueType > x ( maybeStates . getNonZeroCount ( ) , storm : : utility : : zero < ValueType > ( ) ) ;
// Translate the symbolic matrix/vector to their explicit representations.
std : : pair < storm : : storage : : SparseMatrix < ValueType > , std : : vector < ValueType > > explicitRepresentation = submatrix . toMatrixVector ( subvector , std : : move ( rowGroupSizes ) , model . getNondeterminismVariables ( ) , odd , odd ) ;
std : : pair < storm : : storage : : SparseMatrix < ValueType > , std : : vector < ValueType > > explicitRepresentation = submatrix . toMatrixVector ( subvector , model . getNondeterminismVariables ( ) , odd , odd ) ;
std : : unique_ptr < storm : : solver : : MinMaxLinearEquationSolver < ValueType > > solver = linearEquationSolverFactory . create ( std : : move ( explicitRepresentation . first ) ) ;
solver - > repeatedMultiply ( dir , x , & explicitRepresentation . second , stepBound ) ;
@ -338,12 +337,8 @@ namespace storm {
// Create the solution vector.
std : : vector < ValueType > x ( model . getNumberOfStates ( ) , storm : : utility : : zero < ValueType > ( ) ) ;
// Before cutting the non-maybe columns, we need to compute the sizes of the row groups.
storm : : dd : : Add < DdType , uint_fast64_t > stateActionAdd = ( transitionMatrix . notZero ( ) . existsAbstract ( model . getColumnVariables ( ) ) | | totalRewardVector . notZero ( ) ) . template toAdd < uint_fast64_t > ( ) ;
std : : vector < uint_fast64_t > rowGroupSizes = stateActionAdd . sumAbstract ( model . getNondeterminismVariables ( ) ) . toVector ( odd ) ;
// Translate the symbolic matrix/vector to their explicit representations.
std : : pair < storm : : storage : : SparseMatrix < ValueType > , std : : vector < ValueType > > explicitRepresentation = transitionMatrix . toMatrixVector ( totalRewardVector , std : : move ( rowGroupSizes ) , model . getNondeterminismVariables ( ) , odd , odd ) ;
std : : pair < storm : : storage : : SparseMatrix < ValueType > , std : : vector < ValueType > > explicitRepresentation = transitionMatrix . toMatrixVector ( totalRewardVector , model . getNondeterminismVariables ( ) , odd , odd ) ;
// Perform the matrix-vector multiplication.
std : : unique_ptr < storm : : solver : : MinMaxLinearEquationSolver < ValueType > > solver = linearEquationSolverFactory . create ( std : : move ( explicitRepresentation . first ) ) ;
@ -386,7 +381,12 @@ namespace storm {
}
template < typename ValueType >
void eliminateEndComponentsAndTargetStatesReachabilityRewards ( std : : pair < storm : : storage : : SparseMatrix < ValueType > , std : : vector < ValueType > > & explicitRepresentation , SolverRequirementsData < ValueType > & solverRequirementsData ) {
std : : vector < ValueType > computeOneStepTargetProbabilitiesFromExtendedExplicitRepresentation ( storm : : storage : : SparseMatrix < ValueType > const & extendedMatrix , storm : : storage : : BitVector const & properMaybeStates , storm : : storage : : BitVector const & targetStates ) {
return extendedMatrix . getConstrainedRowGroupSumVector ( properMaybeStates , targetStates ) ;
}
template < typename ValueType >
void eliminateEndComponentsAndTargetStatesReachabilityRewards ( std : : pair < storm : : storage : : SparseMatrix < ValueType > , std : : vector < ValueType > > & explicitRepresentation , SolverRequirementsData < ValueType > & solverRequirementsData , storm : : storage : : BitVector const & targetStates , bool computeOneStepTargetProbabilities ) {
// Get easier handles to the data.
auto & transitionMatrix = explicitRepresentation . first ;
@ -431,12 +431,31 @@ namespace storm {
// Only do more work if there are actually end-components.
if ( doDecomposition & & ! endComponentDecomposition . empty ( ) ) {
STORM_LOG_DEBUG ( " Eliminating " < < endComponentDecomposition . size ( ) < < " EC(s). " ) ;
std : : vector < ValueType > subvector ;
SparseMdpEndComponentInformation < ValueType > : : eliminateEndComponents ( endComponentDecomposition , transitionMatrix , rewardVector , solverRequirementsData . properMaybeStates , transitionMatrix , subvector ) ;
if ( computeOneStepTargetProbabilities ) {
solverRequirementsData . oneStepTargetProbabilities = std : : vector < ValueType > ( solverRequirementsData . properMaybeStates . getNumberOfSetBits ( ) , storm : : utility : : zero < ValueType > ( ) ) ;
}
std : : vector < ValueType > subvector ( solverRequirementsData . properMaybeStates . getNumberOfSetBits ( ) , storm : : utility : : zero < ValueType > ( ) ) ;
SparseMdpEndComponentInformation < ValueType > : : eliminateEndComponents ( endComponentDecomposition , transitionMatrix , solverRequirementsData . properMaybeStates , computeOneStepTargetProbabilities ? & targetStates : nullptr , nullptr , & rewardVector , transitionMatrix , computeOneStepTargetProbabilities ? & solverRequirementsData . oneStepTargetProbabilities . get ( ) : nullptr , & subvector ) ;
rewardVector = std : : move ( subvector ) ;
} else {
STORM_LOG_DEBUG ( " Not eliminating ECs as there are none. " ) ;
eliminateExtendedStatesFromExplicitRepresentation ( explicitRepresentation , solverRequirementsData . initialScheduler . get ( ) , solverRequirementsData . properMaybeStates ) ;
if ( computeOneStepTargetProbabilities ) {
solverRequirementsData . oneStepTargetProbabilities = computeOneStepTargetProbabilitiesFromExtendedExplicitRepresentation ( explicitRepresentation . first , solverRequirementsData . properMaybeStates , targetStates ) ;
}
eliminateExtendedStatesFromExplicitRepresentation ( explicitRepresentation , solverRequirementsData . initialScheduler , solverRequirementsData . properMaybeStates ) ;
}
}
template < typename ValueType >
void setUpperRewardBounds ( storm : : solver : : MinMaxLinearEquationSolver < ValueType > & solver , storm : : OptimizationDirection const & direction , storm : : storage : : SparseMatrix < ValueType > const & submatrix , std : : vector < ValueType > const & choiceRewards , std : : vector < ValueType > const & oneStepTargetProbabilities ) {
// For the min-case, we use DS-MPI, for the max-case variant 2 of the Baier et al. paper (CAV'17).
if ( direction = = storm : : OptimizationDirection : : Minimize ) {
DsMpiMdpUpperRewardBoundsComputer < ValueType > dsmpi ( submatrix , choiceRewards , oneStepTargetProbabilities ) ;
solver . setUpperBounds ( dsmpi . computeUpperBounds ( ) ) ;
} else {
BaierUpperRewardBoundsComputer < ValueType > baier ( submatrix , choiceRewards , oneStepTargetProbabilities ) ;
solver . setUpperBound ( baier . computeUpperBound ( ) ) ;
}
}
@ -484,6 +503,11 @@ namespace storm {
clearedRequirements . clearValidInitialScheduler ( ) ;
}
clearedRequirements . clearLowerBounds ( ) ;
if ( clearedRequirements . requiresUpperBounds ( ) ) {
STORM_LOG_DEBUG ( " Computing upper bounds, because the solver requires it. " ) ;
extendMaybeStates = true ;
clearedRequirements . clearUpperBounds ( ) ;
}
STORM_LOG_THROW ( clearedRequirements . empty ( ) , storm : : exceptions : : UncheckedRequirementException , " Cannot establish requirements for solver. " ) ;
}
@ -500,42 +524,39 @@ namespace storm {
// (a) transitions from non-maybe states, and
// (b) the choices in the transition matrix that lead to a state that is neither a maybe state
// nor a target state ('infinity choices').
storm : : dd : : Add < DdType , ValueType > choiceFilterAdd = ( transitionMatrixBdd & & maybeStatesWithTargetStates . renameVariables ( model . getRowVariables ( ) , model . getColumnVariables ( ) ) ) . existsAbstract ( model . getColumnVariables ( ) ) . template toAdd < ValueType > ( ) ;
storm : : dd : : Add < DdType , ValueType > submatrix = transitionMatrix * maybeStatesAdd * choiceFilterAdd ;
storm : : dd : : Add < DdType , ValueType > choiceFilterAdd = maybeStatesAdd * ( transitionMatrixBdd & & maybeStatesWithTargetStates . renameVariables ( model . getRowVariables ( ) , model . getColumnVariables ( ) ) ) . existsAbstract ( model . getColumnVariables ( ) ) . template toAdd < ValueType > ( ) ;
storm : : dd : : Add < DdType , ValueType > submatrix = transitionMatrix * choiceFilterAdd ;
// Then compute the reward vector to use in the computation.
storm : : dd : : Add < DdType , ValueType > subvector = rewardModel . getTotalRewardVector ( maybeStatesAdd , submatrix , model . getColumnVariables ( ) ) ;
if ( ! rewardModel . hasStateActionRewards ( ) & & ! rewardModel . hasTransitionRewards ( ) ) {
// If the reward model neither has state-action nor transition rewards, we need to multiply
// it with the legal nondetermism encodings in each state.
subvector * = choiceFilterAdd ;
}
// Before cutting the non-maybe columns, we need to compute the sizes of the row groups.
storm : : dd : : Add < DdType , uint_fast64_t > stateActionAdd = submatrix . notZero ( ) . existsAbstract ( model . getColumnVariables ( ) ) . template toAdd < uint_fast64_t > ( ) ;
std : : vector < uint_fast64_t > rowGroupSizes = stateActionAdd . sumAbstract ( model . getNondeterminismVariables ( ) ) . toVector ( odd ) ;
storm : : dd : : Add < DdType , ValueType > subvector = rewardModel . getTotalRewardVector ( maybeStatesAdd , choiceFilterAdd , submatrix , model . getColumnVariables ( ) ) ;
// Finally cut away all columns targeting non-maybe states (or non-(maybe or target) states, respectively).
submatrix * = extendMaybeStates ? maybeStatesWithTargetStates . swapVariables ( model . getRowColumnMetaVariablePairs ( ) ) . template toAdd < ValueType > ( ) : maybeStatesAdd . swapVariables ( model . getRowColumnMetaVariablePairs ( ) ) ;
// Translate the symbolic matrix/vector to their explicit representations.
std : : pair < storm : : storage : : SparseMatrix < ValueType > , std : : vector < ValueType > > explicitRepresentation = submatrix . toMatrixVector ( subvector , std : : move ( rowGroupSizes ) , model . getNondeterminismVariables ( ) , odd , odd ) ;
std : : pair < storm : : storage : : SparseMatrix < ValueType > , std : : vector < ValueType > > explicitRepresentation = submatrix . toMatrixVector ( subvector , model . getNondeterminismVariables ( ) , odd , odd ) ;
// Fulfill the solver's requirements.
SolverRequirementsData < ValueType > solverRequirementsData ;
if ( requirements . requiresNoEndComponents ( ) | | requirements . requiresValidInitialScheduler ( ) ) {
if ( extendMaybeStates ) {
storm : : storage : : BitVector targetStates = computeTargetStatesForReachabilityRewardsFromExplicitRepresentation ( explicitRepresentation . first ) ;
solverRequirementsData . properMaybeStates = ~ targetStates ;
if ( requirements . requiresNoEndComponents ( ) ) {
eliminateEndComponentsAndTargetStatesReachabilityRewards ( explicitRepresentation , solverRequirementsData ) ;
eliminateEndComponentsAndTargetStatesReachabilityRewards ( explicitRepresentation , solverRequirementsData , targetStates , requirements . requiresUpperBounds ( ) ) ;
} else {
if ( requirements . requiresValidInitialScheduler ( ) ) {
// Compute a valid initial scheduler.
solverRequirementsData . initialScheduler = computeValidInitialSchedulerForReachabilityRewards < ValueType > ( explicitRepresentation . first , solverRequirementsData . properMaybeStates , targetStates ) ;
}
if ( requirements . requiresUpperBounds ( ) ) {
solverRequirementsData . oneStepTargetProbabilities = computeOneStepTargetProbabilitiesFromExtendedExplicitRepresentation ( explicitRepresentation . first , solverRequirementsData . properMaybeStates , targetStates ) ;
}
// Since we needed the transitions to target states to be translated as well for the computation
// of the scheduler, we have to get rid of them now.
eliminateExtendedStatesFromExplicitRepresentation ( explicitRepresentation , solverRequirementsData . initialScheduler . get ( ) , solverRequirementsData . properMaybeStates ) ;
eliminateExtendedStatesFromExplicitRepresentation ( explicitRepresentation , solverRequirementsData . initialScheduler , solverRequirementsData . properMaybeStates ) ;
}
}
@ -543,7 +564,14 @@ namespace storm {
std : : vector < ValueType > x ( explicitRepresentation . first . getRowGroupCount ( ) , storm : : utility : : zero < ValueType > ( ) ) ;
// Now solve the resulting equation system.
std : : unique_ptr < storm : : solver : : MinMaxLinearEquationSolver < ValueType > > solver = linearEquationSolverFactory . create ( std : : move ( explicitRepresentation . first ) ) ;
std : : unique_ptr < storm : : solver : : MinMaxLinearEquationSolver < ValueType > > solver = linearEquationSolverFactory . create ( ) ;
// If the solver requires upper bounds, compute them now.
if ( requirements . requiresUpperBounds ( ) ) {
setUpperRewardBounds ( * solver , dir , explicitRepresentation . first , explicitRepresentation . second , solverRequirementsData . oneStepTargetProbabilities . get ( ) ) ;
}
solver - > setMatrix ( std : : move ( explicitRepresentation . first ) ) ;
// Move the scheduler to the solver.
if ( solverRequirementsData . initialScheduler ) {