@ -85,136 +85,200 @@ namespace storm {
template < class SparseMdpModelType >
void SparseMdpRewardBoundedPcaaWeightVectorChecker < SparseMdpModelType > : : computeEpochSolution ( typename MultiDimensionalRewardUnfolding < ValueType , false > : : Epoch const & epoch , std : : vector < ValueType > const & weightVector , EpochCheckingData & cachedData ) {
auto & epochModel = rewardUnfolding . setCurrentEpoch ( epoch ) ;
updateCachedData ( epochModel , cachedData , weightVector ) ;
+ + numCheckedEpochs ;
swEqBuilding . start ( ) ;
+ + numCheckedEpochs ;
swEpochModelBuild . start ( ) ;
auto & epochModel = rewardUnfolding . setCurrentEpoch ( epoch ) ;
swEpochModelBuild . stop ( ) ;
swEpochModelAnalysis . start ( ) ;
std : : vector < typename MultiDimensionalRewardUnfolding < ValueType , false > : : SolutionType > result ;
result . reserve ( epochModel . epochInStates . getNumberOfSetBits ( ) ) ;
uint64_t solutionSize = this - > objectives . size ( ) + 1 ;
// Formulate a min-max equation system max(A*x+b)=x for the weighted sum of the objectives
swAux1 . start ( ) ;
assert ( cachedData . bMinMax . capacity ( ) > = epochModel . epochMatrix . getRowCount ( ) ) ;
assert ( cachedData . xMinMax . size ( ) = = epochModel . epochMatrix . getRowGroupCount ( ) ) ;
cachedData . bMinMax . assign ( epochModel . epochMatrix . getRowCount ( ) , storm : : utility : : zero < ValueType > ( ) ) ;
for ( uint64_t objIndex = 0 ; objIndex < this - > objectives . size ( ) ; + + objIndex ) {
ValueType weight = storm : : solver : : minimize ( this - > objectives [ objIndex ] . formula - > getOptimalityType ( ) ) ? - weightVector [ objIndex ] : weightVector [ objIndex ] ;
if ( ! storm : : utility : : isZero ( weight ) ) {
std : : vector < ValueType > const & objectiveReward = epochModel . objectiveRewards [ objIndex ] ;
for ( auto const & choice : epochModel . objectiveRewardFilter [ objIndex ] ) {
cachedData . bMinMax [ choice ] + = weight * objectiveReward [ choice ] ;
// If the epoch matrix is empty we do not need to solve linear equation systems
if ( epochModel . epochMatrix . getEntryCount ( ) = = 0 ) {
std : : vector < ValueType > weights = weightVector ;
for ( uint64_t objIndex = 0 ; objIndex < this - > objectives . size ( ) ; + + objIndex ) {
if ( storm : : solver : : minimize ( this - > objectives [ objIndex ] . formula - > getOptimalityType ( ) ) ) {
weights [ objIndex ] * = - storm : : utility : : one < ValueType > ( ) ;
}
}
}
auto stepSolutionIt = epochModel . stepSolutions . begin ( ) ;
for ( auto const & choice : epochModel . stepChoices ) {
cachedData . bMinMax [ choice ] + = stepSolutionIt - > front ( ) ;
+ + stepSolutionIt ;
}
swAux1 . stop ( ) ;
// Invoke the min max solver
swEqBuilding . stop ( ) ;
swMinMaxSolving . start ( ) ;
cachedData . minMaxSolver - > solveEquations ( cachedData . xMinMax , cachedData . bMinMax ) ;
swMinMaxSolving . stop ( ) ;
swEqBuilding . start ( ) ;
for ( auto const & state : epochModel . epochInStates ) {
result . emplace_back ( ) ;
result . back ( ) . reserve ( solutionSize ) ;
result . back ( ) . push_back ( cachedData . xMinMax [ state ] ) ;
}
// Check whether the linear equation solver needs to be updated
auto const & choices = cachedData . minMaxSolver - > getSchedulerChoices ( ) ;
if ( cachedData . schedulerChoices ! = choices ) {
std : : vector < uint64_t > choicesTmp = choices ;
cachedData . minMaxSolver - > setInitialScheduler ( std : : move ( choicesTmp ) ) ;
swAux2 . start ( ) ;
+ + numSchedChanges ;
cachedData . schedulerChoices = choices ;
storm : : solver : : GeneralLinearEquationSolverFactory < ValueType > linEqSolverFactory ;
bool needEquationSystem = linEqSolverFactory . getEquationProblemFormat ( ) = = storm : : solver : : LinearEquationSolverProblemFormat : : EquationSystem ;
storm : : storage : : SparseMatrix < ValueType > subMatrix = epochModel . epochMatrix . selectRowsFromRowGroups ( choices , needEquationSystem ) ;
if ( needEquationSystem ) {
subMatrix . convertToEquationSystem ( ) ;
}
cachedData . linEqSolver = linEqSolverFactory . create ( std : : move ( subMatrix ) ) ;
cachedData . linEqSolver - > setCachingEnabled ( true ) ;
swAux2 . stop ( ) ;
}
// Formulate for each objective the linear equation system induced by the performed choices
swAux3 . start ( ) ;
assert ( cachedData . bLinEq . size ( ) = = choices . size ( ) ) ;
for ( uint64_t objIndex = 0 ; objIndex < this - > objectives . size ( ) ; + + objIndex ) {
auto const & obj = this - > objectives [ objIndex ] ;
std : : vector < ValueType > const & objectiveReward = epochModel . objectiveRewards [ objIndex ] ;
auto rowGroupIndexIt = epochModel . epochMatrix . getRowGroupIndices ( ) . begin ( ) ;
auto choiceIt = choices . begin ( ) ;
auto stepChoiceIt = epochModel . stepChoices . begin ( ) ;
auto stepSolutionIt = epochModel . stepSolutions . begin ( ) ;
std : : vector < ValueType > & x = cachedData . xLinEq [ objIndex ] ;
auto xIt = x . begin ( ) ;
for ( auto & b_i : cachedData . bLinEq ) {
uint64_t i = * rowGroupIndexIt + * choiceIt ;
if ( epochModel . objectiveRewardFilter [ objIndex ] . get ( i ) ) {
b_i = objectiveReward [ i ] ;
} else {
b_i = storm : : utility : : zero < ValueType > ( ) ;
}
while ( * stepChoiceIt < i ) {
+ + stepChoiceIt ;
+ + stepSolutionIt ;
}
if ( i = = * stepChoiceIt ) {
b_i + = ( * stepSolutionIt ) [ objIndex + 1 ] ;
+ + stepChoiceIt ;
+ + stepSolutionIt ;
auto stepChoiceIt = epochModel . stepChoices . begin ( ) ;
for ( auto const & state : epochModel . epochInStates ) {
// Obtain the best choice for this state according to the weighted combination of objectives
ValueType bestValue ;
uint64_t bestChoice = std : : numeric_limits < uint64_t > : : max ( ) ;
auto bestChoiceStepSolutionIt = epochModel . stepSolutions . end ( ) ;
uint64_t lastChoice = epochModel . epochMatrix . getRowGroupIndices ( ) [ state + 1 ] ;
bool firstChoice = true ;
for ( uint64_t choice = epochModel . epochMatrix . getRowGroupIndices ( ) [ state ] ; choice < lastChoice ; + + choice ) {
ValueType choiceValue = storm : : utility : : zero < ValueType > ( ) ;
// Obtain the (weighted) objective rewards
for ( uint64_t objIndex = 0 ; objIndex < this - > objectives . size ( ) ; + + objIndex ) {
if ( epochModel . objectiveRewardFilter [ objIndex ] . get ( choice ) ) {
choiceValue + = weights [ objIndex ] * epochModel . objectiveRewards [ objIndex ] [ choice ] ;
}
}
// Obtain the step solution if this is a step choice
while ( * stepChoiceIt < choice ) {
+ + stepChoiceIt ;
+ + stepSolutionIt ;
}
if ( * stepChoiceIt = = choice ) {
choiceValue + = stepSolutionIt - > front ( ) ;
// Check if this choice is better
if ( firstChoice | | choiceValue > bestValue ) {
bestValue = std : : move ( choiceValue ) ;
bestChoice = choice ;
bestChoiceStepSolutionIt = stepSolutionIt ;
}
} else if ( firstChoice | | choiceValue > bestValue ) {
bestValue = std : : move ( choiceValue ) ;
bestChoice = choice ;
bestChoiceStepSolutionIt = epochModel . stepSolutions . end ( ) ;
}
firstChoice = false ;
}
// We can already set x_i correctly if row i is empty.
// Appearingly, some linear equation solvers struggle to converge otherwise.
if ( epochModel . epochMatrix . getRow ( i ) . getNumberOfEntries ( ) = = 0 ) {
* xIt = b_i ;
// Insert the solution w.r.t. this choice
result . emplace_back ( ) ;
result . back ( ) . reserve ( solutionSize ) ;
result . back ( ) . push_back ( std : : move ( bestValue ) ) ;
if ( bestChoiceStepSolutionIt ! = epochModel . stepSolutions . end ( ) ) {
for ( uint64_t objIndex = 0 ; objIndex < this - > objectives . size ( ) ; + + objIndex ) {
if ( epochModel . objectiveRewardFilter [ objIndex ] . get ( bestChoice ) ) {
result . back ( ) . push_back ( ( epochModel . objectiveRewards [ objIndex ] [ bestChoice ] + ( * bestChoiceStepSolutionIt ) [ objIndex + 1 ] ) ) ;
} else {
result . back ( ) . push_back ( ( * bestChoiceStepSolutionIt ) [ objIndex + 1 ] ) ;
}
}
} else {
for ( uint64_t objIndex = 0 ; objIndex < this - > objectives . size ( ) ; + + objIndex ) {
if ( epochModel . objectiveRewardFilter [ objIndex ] . get ( bestChoice ) ) {
result . back ( ) . push_back ( ( epochModel . objectiveRewards [ objIndex ] [ bestChoice ] ) ) ;
} else {
result . back ( ) . push_back ( storm : : utility : : zero < ValueType > ( ) ) ;
}
}
}
+ + xIt ;
+ + rowGroupIndexIt ;
+ + choiceIt ;
}
assert ( x . size ( ) = = choices . size ( ) ) ;
auto req = cachedData . linEqSolver - > getRequirements ( ) ;
cachedData . linEqSolver - > clearBounds ( ) ;
if ( obj . lowerResultBound ) {
req . clearLowerBounds ( ) ;
cachedData . linEqSolver - > setLowerBound ( * obj . lowerResultBound ) ;
} else {
updateCachedData ( epochModel , cachedData , weightVector ) ;
// Formulate a min-max equation system max(A*x+b)=x for the weighted sum of the objectives
assert ( cachedData . bMinMax . capacity ( ) > = epochModel . epochMatrix . getRowCount ( ) ) ;
assert ( cachedData . xMinMax . size ( ) = = epochModel . epochMatrix . getRowGroupCount ( ) ) ;
cachedData . bMinMax . assign ( epochModel . epochMatrix . getRowCount ( ) , storm : : utility : : zero < ValueType > ( ) ) ;
for ( uint64_t objIndex = 0 ; objIndex < this - > objectives . size ( ) ; + + objIndex ) {
ValueType weight = storm : : solver : : minimize ( this - > objectives [ objIndex ] . formula - > getOptimalityType ( ) ) ? - weightVector [ objIndex ] : weightVector [ objIndex ] ;
if ( ! storm : : utility : : isZero ( weight ) ) {
std : : vector < ValueType > const & objectiveReward = epochModel . objectiveRewards [ objIndex ] ;
for ( auto const & choice : epochModel . objectiveRewardFilter [ objIndex ] ) {
cachedData . bMinMax [ choice ] + = weight * objectiveReward [ choice ] ;
}
}
}
if ( obj . upperResultBound ) {
cachedData . linEqSolver - > setUpperBound ( * obj . upperResultBound ) ;
req . clearUpperBounds ( ) ;
auto stepSolutionIt = epochModel . stepSolutions . begin ( ) ;
for ( auto const & choice : epochModel . stepChoices ) {
cachedData . bMinMax [ choice ] + = stepSolutionIt - > front ( ) ;
+ + stepSolutionIt ;
}
STORM_LOG_THROW ( req . empty ( ) , storm : : exceptions : : UncheckedRequirementException , " At least one requirement of the LinearEquationSolver was not met. " ) ;
swEqBuilding . stop ( ) ;
swLinEqSolving . start ( ) ;
cachedData . linEqSolver - > solveEquations ( x , cachedData . bLinEq ) ;
swLinEqSolving . stop ( ) ;
swEqBuilding . start ( ) ;
auto resultIt = result . begin ( ) ;
// Invoke the min max solver
cachedData . minMaxSolver - > solveEquations ( cachedData . xMinMax , cachedData . bMinMax ) ;
for ( auto const & state : epochModel . epochInStates ) {
resultIt - > push_back ( x [ state ] ) ;
+ + resultIt ;
result . emplace_back ( ) ;
result . back ( ) . reserve ( solutionSize ) ;
result . back ( ) . push_back ( cachedData . xMinMax [ state ] ) ;
}
// Check whether the linear equation solver needs to be updated
auto const & choices = cachedData . minMaxSolver - > getSchedulerChoices ( ) ;
if ( cachedData . schedulerChoices ! = choices ) {
std : : vector < uint64_t > choicesTmp = choices ;
cachedData . minMaxSolver - > setInitialScheduler ( std : : move ( choicesTmp ) ) ;
+ + numSchedChanges ;
cachedData . schedulerChoices = choices ;
storm : : solver : : GeneralLinearEquationSolverFactory < ValueType > linEqSolverFactory ;
bool needEquationSystem = linEqSolverFactory . getEquationProblemFormat ( ) = = storm : : solver : : LinearEquationSolverProblemFormat : : EquationSystem ;
storm : : storage : : SparseMatrix < ValueType > subMatrix = epochModel . epochMatrix . selectRowsFromRowGroups ( choices , needEquationSystem ) ;
if ( needEquationSystem ) {
subMatrix . convertToEquationSystem ( ) ;
}
cachedData . linEqSolver = linEqSolverFactory . create ( std : : move ( subMatrix ) ) ;
cachedData . linEqSolver - > setCachingEnabled ( true ) ;
}
// Formulate for each objective the linear equation system induced by the performed choices
assert ( cachedData . bLinEq . size ( ) = = choices . size ( ) ) ;
for ( uint64_t objIndex = 0 ; objIndex < this - > objectives . size ( ) ; + + objIndex ) {
auto const & obj = this - > objectives [ objIndex ] ;
std : : vector < ValueType > const & objectiveReward = epochModel . objectiveRewards [ objIndex ] ;
auto rowGroupIndexIt = epochModel . epochMatrix . getRowGroupIndices ( ) . begin ( ) ;
auto choiceIt = choices . begin ( ) ;
auto stepChoiceIt = epochModel . stepChoices . begin ( ) ;
auto stepSolutionIt = epochModel . stepSolutions . begin ( ) ;
std : : vector < ValueType > & x = cachedData . xLinEq [ objIndex ] ;
auto xIt = x . begin ( ) ;
for ( auto & b_i : cachedData . bLinEq ) {
uint64_t i = * rowGroupIndexIt + * choiceIt ;
if ( epochModel . objectiveRewardFilter [ objIndex ] . get ( i ) ) {
b_i = objectiveReward [ i ] ;
} else {
b_i = storm : : utility : : zero < ValueType > ( ) ;
}
while ( * stepChoiceIt < i ) {
+ + stepChoiceIt ;
+ + stepSolutionIt ;
}
if ( i = = * stepChoiceIt ) {
b_i + = ( * stepSolutionIt ) [ objIndex + 1 ] ;
+ + stepChoiceIt ;
+ + stepSolutionIt ;
}
// We can already set x_i correctly if row i is empty.
// Appearingly, some linear equation solvers struggle to converge otherwise.
if ( epochModel . epochMatrix . getRow ( i ) . getNumberOfEntries ( ) = = 0 ) {
* xIt = b_i ;
}
+ + xIt ;
+ + rowGroupIndexIt ;
+ + choiceIt ;
}
assert ( x . size ( ) = = choices . size ( ) ) ;
auto req = cachedData . linEqSolver - > getRequirements ( ) ;
cachedData . linEqSolver - > clearBounds ( ) ;
if ( obj . lowerResultBound ) {
req . clearLowerBounds ( ) ;
cachedData . linEqSolver - > setLowerBound ( * obj . lowerResultBound ) ;
}
if ( obj . upperResultBound ) {
cachedData . linEqSolver - > setUpperBound ( * obj . upperResultBound ) ;
req . clearUpperBounds ( ) ;
}
STORM_LOG_THROW ( req . empty ( ) , storm : : exceptions : : UncheckedRequirementException , " At least one requirement of the LinearEquationSolver was not met. " ) ;
cachedData . linEqSolver - > solveEquations ( x , cachedData . bLinEq ) ;
auto resultIt = result . begin ( ) ;
for ( auto const & state : epochModel . epochInStates ) {
resultIt - > push_back ( x [ state ] ) ;
+ + resultIt ;
}
}
}
swEqBuilding . stop ( ) ;
swAux3 . stop ( ) ;
rewardUnfolding . setSolutionForCurrentEpoch ( std : : move ( result ) ) ;
swEpochModelAnalysis . stop ( ) ;
}
template < class SparseMdpModelType >
void SparseMdpRewardBoundedPcaaWeightVectorChecker < SparseMdpModelType > : : updateCachedData ( typename MultiDimensionalRewardUnfolding < ValueType , false > : : EpochModel const & epochModel , EpochCheckingData & cachedData , std : : vector < ValueType > const & weightVector ) {
if ( epochModel . epochMatrixChanged ) {
swDataUpdate . start ( ) ;
// Update the cached MinMaxSolver data
cachedData . bMinMax . resize ( epochModel . epochMatrix . getRowCount ( ) ) ;
@ -249,7 +313,6 @@ namespace storm {
for ( auto & x_o : cachedData . xLinEq ) {
x_o . assign ( epochModel . epochMatrix . getRowGroupCount ( ) , storm : : utility : : zero < ValueType > ( ) ) ;
}
swDataUpdate . stop ( ) ;
}
}