@ -5,6 +5,8 @@
# include <memory>
# include <boost/optional.hpp>
# include "storm/environment/solver/MinMaxSolverEnvironment.h"
# include "storm/models/sparse/Mdp.h"
# include "storm/modelchecker/prctl/helper/rewardbounded/MultiDimensionalRewardUnfolding.h"
# include "storm/storage/expressions/ExpressionManager.h"
@ -104,6 +106,33 @@ namespace storm {
return std : : make_shared < storm : : logic : : ProbabilityOperatorFormula > ( newBoundedUntil , boundedUntilOperator . getOperatorInformation ( ) ) ;
}
/// Increases the precision of solver results
void increasePrecision ( storm : : Environment & env ) {
STORM_LOG_DEBUG ( " Increasing precision of underlying solver. " ) ;
auto factor = storm : : utility : : convertNumber < storm : : RationalNumber , std : : string > ( " 0.1 " ) ;
env . solver ( ) . setLinearEquationSolverPrecision ( static_cast < storm : : RationalNumber > ( env . solver ( ) . getPrecisionOfLinearEquationSolver ( env . solver ( ) . getLinearEquationSolverType ( ) ) . first . get ( ) * factor ) ) ;
env . solver ( ) . minMax ( ) . setPrecision ( env . solver ( ) . minMax ( ) . getPrecision ( ) * factor ) ;
}
/// Computes a lower/ upper bound on the actual result of a minmax or linear equation solver
template < typename ValueType >
std : : pair < ValueType , ValueType > getLowerUpperBound ( storm : : Environment const & env , ValueType const & value , bool minMax = true ) {
ValueType prec ;
bool relative ;
if ( minMax ) {
prec = storm : : utility : : convertNumber < ValueType > ( env . solver ( ) . minMax ( ) . getPrecision ( ) ) ;
relative = env . solver ( ) . minMax ( ) . getRelativeTerminationCriterion ( ) ;
} else {
prec = storm : : utility : : convertNumber < ValueType > ( env . solver ( ) . getPrecisionOfLinearEquationSolver ( env . solver ( ) . getLinearEquationSolverType ( ) ) . first . get ( ) ) ;
relative = env . solver ( ) . getPrecisionOfLinearEquationSolver ( env . solver ( ) . getLinearEquationSolverType ( ) ) . second . get ( ) ;
}
if ( relative ) {
return std : : make_pair < ValueType , ValueType > ( value * ( 1 / ( prec + 1 ) ) , value * ( 1 + prec / ( prec + 1 ) ) ) ;
} else {
return std : : make_pair < ValueType , ValueType > ( value - prec , value + prec ) ;
}
}
template < typename ModelType >
uint64_t QuantileHelper < ModelType > : : getDimension ( ) const {
return quantileFormula . getSubformula ( ) . asProbabilityOperatorFormula ( ) . getSubformula ( ) . asBoundedUntilFormula ( ) . getDimension ( ) ;
@ -158,15 +187,21 @@ namespace storm {
template < typename ModelType >
std : : vector < std : : vector < typename ModelType : : ValueType > > QuantileHelper < ModelType > : : computeQuantile ( Environment const & env ) {
std : : vector < std : : vector < ValueType > > result ;
Environment envCpy = env ; // It might be necessary to increase the precision during the computation
if ( getOpenDimensions ( ) . getNumberOfSetBits ( ) = = 1 ) {
uint64_t dimension = * getOpenDimensions ( ) . begin ( ) ;
auto const & boundedUntilOperator = quantileFormula . getSubformula ( ) . asProbabilityOperatorFormula ( ) ;
bool zeroSatisfiesFormula = boundedUntilOperator . getBound ( ) . isSatisfied ( compute LimitValue( env , storm : : storage : : BitVector ( getDimension ( ) , false ) ) ) ;
bool infSatisfiesFormula = boundedUntilOperator . getBound ( ) . isSatisfied ( compute LimitValue( env , getOpenDimensions ( ) ) ) ;
bool zeroSatisfiesFormula = check LimitValue( envCpy , storm : : storage : : BitVector ( getDimension ( ) , false ) ) ;
bool infSatisfiesFormula = check LimitValue( envCpy , getOpenDimensions ( ) ) ;
if ( zeroSatisfiesFormula ! = infSatisfiesFormula ) {
auto quantileRes = computeQuantileForDimension ( env , dimension ) ;
result = { { storm : : utility : : convertNumber < ValueType > ( quantileRes . first ) * quantileRes . second } } ;
while ( true ) {
auto quantileRes = computeQuantileForDimension ( envCpy , dimension ) ;
if ( quantileRes ) {
result = { { storm : : utility : : convertNumber < ValueType > ( quantileRes - > first ) * quantileRes - > second } } ;
break ;
}
increasePrecision ( envCpy ) ;
}
} else if ( zeroSatisfiesFormula ) {
// thus also infSatisfiesFormula is true, i.e., all bound values satisfy the formula
if ( storm : : solver : : minimize ( getOptimizationDirForDimension ( dimension ) ) ) {
@ -179,7 +214,7 @@ namespace storm {
result = { { } } ;
}
} else if ( getOpenDimensions ( ) . getNumberOfSetBits ( ) = = 2 ) {
result = computeTwoDimensionalQuantile ( env ) ;
result = computeTwoDimensionalQuantile ( envCpy ) ;
} else {
STORM_LOG_THROW ( false , storm : : exceptions : : NotSupportedException , " The quantile formula considers " < < getOpenDimensions ( ) . getNumberOfSetBits ( ) < < " open dimensions. Only one- or two-dimensional quantiles are supported. " ) ;
}
@ -218,7 +253,7 @@ namespace storm {
}
template < typename ModelType >
std : : vector < std : : vector < typename ModelType : : ValueType > > QuantileHelper < ModelType > : : computeTwoDimensionalQuantile ( Environment const & env ) {
std : : vector < std : : vector < typename ModelType : : ValueType > > QuantileHelper < ModelType > : : computeTwoDimensionalQuantile ( Environment & env ) {
std : : vector < std : : vector < ValueType > > result ;
auto const & probOpFormula = quantileFormula . getSubformula ( ) . asProbabilityOperatorFormula ( ) ;
@ -240,15 +275,13 @@ namespace storm {
if ( lowerCostBounds [ 0 ] = = lowerCostBounds [ 1 ] ) {
// TODO: Assert that we have a reasonable probability bound. STORM_LOG_THROW(storm::solver::minimize(optimizationDirections[0])
bool infInfProbSatisfiesFormula = probOpFormula . getBound ( ) . isSatisfied ( compute LimitValue( env , storm : : storage : : BitVector ( getDimension ( ) , false ) ) ) ;
bool zeroZeroProbSatisfiesFormula = probOpFormula . getBound ( ) . isSatisfied ( compute LimitValue( env , dimensionsAsBitVector [ 0 ] | dimensionsAsBitVector [ 1 ] ) ) ;
bool infInfProbSatisfiesFormula = check LimitValue( env , storm : : storage : : BitVector ( getDimension ( ) , false ) ) ;
bool zeroZeroProbSatisfiesFormula = check LimitValue( env , dimensionsAsBitVector [ 0 ] | dimensionsAsBitVector [ 1 ] ) ;
if ( infInfProbSatisfiesFormula ! = zeroZeroProbSatisfiesFormula ) {
std : : vector < std : : pair < int64_t , typename ModelType : : ValueType > > singleQuantileValues ;
std : : vector < std : : vector < int64_t > > resultPoints ;
const int64_t infinity = std : : numeric_limits < int64_t > ( ) . max ( ) ; // use this value to represent infinity in a result point
for ( uint64_t i = 0 ; i < 2 ; + + i ) {
// find out whether the bounds B_i = 0 and B_1-i = infinity satisfy the formula
bool zeroInfSatisfiesFormula = probOpFormula . getBound ( ) . isSatisfied ( compute LimitValue( env , dimensionsAsBitVector [ 1 - i ] ) ) ;
bool zeroInfSatisfiesFormula = check LimitValue( env , dimensionsAsBitVector [ 1 - i ] ) ;
std : : cout < < " Formula sat is " < < zeroInfSatisfiesFormula < < " and lower bound is " < < storm : : logic : : isLowerBound ( probabilityBound ) < < std : : endl ;
if ( zeroInfSatisfiesFormula = = storm : : solver : : minimize ( optimizationDirections [ 0 ] ) ) {
// There is bound value b such that the point B_i=0 and B_1-i = b is part of the result
@ -256,7 +289,7 @@ namespace storm {
} else {
// Compute quantile where 1-i is set to infinity
std : : cout < < " Computing quantile for single dimension " < < dimensions [ i ] < < std : : endl ;
singleQuantileValues . push_back ( computeQuantileForDimension ( env , dimensions [ i ] ) ) ;
singleQuantileValues . push_back ( computeQuantileForDimension ( env , dimensions [ i ] ) . get ( ) ) ;
std : : cout < < " .. Result is " < < singleQuantileValues . back ( ) . first < < std : : endl ;
if ( ! storm : : solver : : minimize ( optimizationDirections [ i ] ) ) {
// When maximizing bounds, the computed single dimensional quantile value is sat for all values of the other bound.
@ -270,93 +303,10 @@ namespace storm {
}
}
std : : cout < < " Found starting point. Beginning 2D exploration " < < std : : endl ;
MultiDimensionalRewardUnfolding < ValueType , true > rewardUnfolding ( model , transformBoundedUntilOperator ( quantileFormula . getSubformula ( ) . asProbabilityOperatorFormula ( ) , std : : vector < BoundTransformation > ( getDimension ( ) , BoundTransformation : : None ) ) ) ;
// initialize data that will be needed for each epoch
auto lowerBound = rewardUnfolding . getLowerObjectiveBound ( ) ;
auto upperBound = rewardUnfolding . getUpperObjectiveBound ( ) ;
std : : vector < ValueType > x , b ;
std : : unique_ptr < storm : : solver : : MinMaxLinearEquationSolver < ValueType > > minMaxSolver ;
std : : vector < int64_t > epochValues = { ( int64_t ) singleQuantileValues [ 0 ] . first , ( int64_t ) singleQuantileValues [ 1 ] . first } ; // signed int is needed to allow lower bounds >-1 (aka >=0).
std : : cout < < " Starting quantile exploration with: ( " < < epochValues [ 0 ] < < " , " < < epochValues [ 1 ] < < " ). " < < std : : endl ;
std : : set < typename EpochManager : : Epoch > exploredEpochs ;
while ( true ) {
auto currEpoch = rewardUnfolding . getStartEpoch ( true ) ;
for ( uint64_t i = 0 ; i < 2 ; + + i ) {
if ( epochValues [ i ] > = 0 ) {
rewardUnfolding . getEpochManager ( ) . setDimensionOfEpoch ( currEpoch , dimensions [ i ] , ( uint64_t ) epochValues [ i ] ) ;
} else {
rewardUnfolding . getEpochManager ( ) . setBottomDimension ( currEpoch , dimensions [ i ] ) ;
}
}
auto epochOrder = rewardUnfolding . getEpochComputationOrder ( currEpoch ) ;
for ( auto const & epoch : epochOrder ) {
if ( exploredEpochs . count ( epoch ) = = 0 ) {
auto & epochModel = rewardUnfolding . setCurrentEpoch ( epoch ) ;
rewardUnfolding . setSolutionForCurrentEpoch ( epochModel . analyzeSingleObjective ( env , quantileFormula . getSubformula ( ) . asProbabilityOperatorFormula ( ) . getOptimalityType ( ) , x , b , minMaxSolver , lowerBound , upperBound ) ) ;
exploredEpochs . insert ( epoch ) ;
}
}
ValueType currValue = rewardUnfolding . getInitialStateResult ( currEpoch ) ;
std : : cout < < " Numeric result for epoch " < < rewardUnfolding . getEpochManager ( ) . toString ( currEpoch ) < < " is " < < currValue < < std : : endl ;
bool propertySatisfied = probOpFormula . getBound ( ) . isSatisfied ( currValue ) ;
// If the reward bounds should be as small as possible, we should stop as soon as the property is satisfied.
// If the reward bounds should be as large as possible, we should stop as soon as the property is violated and then go a step backwards
if ( storm : : solver : : minimize ( optimizationDirections [ 0 ] ) = = propertySatisfied ) {
// We found another point for the result!
resultPoints . push_back ( epochValues ) ;
std : : cout < < " Found another result point: ( " < < epochValues [ 0 ] < < " , " < < epochValues [ 1 ] < < " ). " < < std : : endl ;
if ( epochValues [ 1 ] = = singleQuantileValues [ 1 ] . first ) {
break ;
} else {
+ + epochValues [ 0 ] ;
epochValues [ 1 ] = singleQuantileValues [ 1 ] . first ;
}
} else {
+ + epochValues [ 1 ] ;
}
}
// Translate the result points to the 'original' domain
for ( auto & p : resultPoints ) {
std : : vector < ValueType > convertedP ;
for ( uint64_t i = 0 ; i < 2 ; + + i ) {
if ( p [ i ] = = infinity ) {
convertedP . push_back ( storm : : utility : : infinity < ValueType > ( ) ) ;
} else {
if ( lowerCostBounds [ i ] ) {
// Translate > to >=
+ + p [ i ] ;
}
if ( i = = 1 & & storm : : solver : : maximize ( optimizationDirections [ i ] ) ) {
// When maximizing, we actually searched for each x-value the smallest y-value that leads to a property violation. Hence, decreasing y by one means property satisfaction
- - p [ i ] ;
}
if ( p [ i ] < 0 ) {
// Skip this point
convertedP . clear ( ) ;
continue ;
}
convertedP . push_back ( storm : : utility : : convertNumber < ValueType > ( p [ i ] ) * rewardUnfolding . getDimension ( dimensions [ i ] ) . scalingFactor ) ;
}
}
if ( ! convertedP . empty ( ) ) {
result . push_back ( convertedP ) ;
}
}
// When maximizing, there are border cases where one dimension can be arbitrarily large
for ( uint64_t i = 0 ; i < 2 ; + + i ) {
if ( storm : : solver : : maximize ( optimizationDirections [ i ] ) & & singleQuantileValues [ i ] . first > 0 ) {
std : : vector < ValueType > newResultPoint ( 2 ) ;
newResultPoint [ i ] = storm : : utility : : convertNumber < ValueType > ( singleQuantileValues [ i ] . first - 1 ) * singleQuantileValues [ i ] . second ;
newResultPoint [ 1 - i ] = storm : : utility : : infinity < ValueType > ( ) ;
result . push_back ( newResultPoint ) ;
}
std : : vector < int64_t > currentEpochValues = { ( int64_t ) singleQuantileValues [ 0 ] . first , ( int64_t ) singleQuantileValues [ 1 ] . first } ; // signed int is needed to allow lower bounds >-1 (aka >=0).
while ( ! exploreTwoDimensionalQuantile ( env , singleQuantileValues , currentEpochValues , result ) ) {
increasePrecision ( env ) ;
}
filterDominatedPoints ( result , optimizationDirections ) ;
} else if ( infInfProbSatisfiesFormula ) {
// then also zeroZeroProb satisfies the formula, i.e., all bound values satisfy the formula
if ( storm : : solver : : minimize ( optimizationDirections [ 0 ] ) ) {
@ -378,7 +328,129 @@ namespace storm {
}
template < typename ModelType >
std : : pair < uint64_t , typename ModelType : : ValueType > QuantileHelper < ModelType > : : computeQuantileForDimension ( Environment const & env , uint64_t dimension ) const {
bool QuantileHelper < ModelType > : : exploreTwoDimensionalQuantile ( Environment const & env , std : : vector < std : : pair < int64_t , typename ModelType : : ValueType > > const & startEpochValues , std : : vector < int64_t > & currentEpochValues , std : : vector < std : : vector < ValueType > > & resultPoints ) const {
// Initialize some data for easy access
auto const & probOpFormula = quantileFormula . getSubformula ( ) . asProbabilityOperatorFormula ( ) ;
auto const & boundedUntilFormula = probOpFormula . getSubformula ( ) . asBoundedUntilFormula ( ) ;
std : : vector < storm : : storage : : BitVector > dimensionsAsBitVector ;
std : : vector < uint64_t > dimensions ;
std : : vector < storm : : solver : : OptimizationDirection > optimizationDirections ;
std : : vector < bool > lowerCostBounds ;
for ( auto const & dirVar : quantileFormula . getBoundVariables ( ) ) {
dimensionsAsBitVector . push_back ( getDimensionsForVariable ( dirVar . second ) ) ;
STORM_LOG_THROW ( dimensionsAsBitVector . back ( ) . getNumberOfSetBits ( ) = = 1 , storm : : exceptions : : NotSupportedException , " There is not exactly one reward bound referring to quantile variable ' " < < dirVar . second . getName ( ) < < " '. " ) ;
dimensions . push_back ( * dimensionsAsBitVector . back ( ) . begin ( ) ) ;
lowerCostBounds . push_back ( boundedUntilFormula . hasLowerBound ( dimensions . back ( ) ) ) ;
optimizationDirections . push_back ( dirVar . first ) ;
}
STORM_LOG_ASSERT ( dimensions . size ( ) = = 2 , " Expected to have exactly two open dimensions. " ) ;
MultiDimensionalRewardUnfolding < ValueType , true > rewardUnfolding ( model , transformBoundedUntilOperator ( quantileFormula . getSubformula ( ) . asProbabilityOperatorFormula ( ) , std : : vector < BoundTransformation > ( getDimension ( ) , BoundTransformation : : None ) ) ) ;
// initialize data that will be needed for each epoch
auto lowerBound = rewardUnfolding . getLowerObjectiveBound ( ) ;
auto upperBound = rewardUnfolding . getUpperObjectiveBound ( ) ;
std : : vector < ValueType > x , b ;
std : : unique_ptr < storm : : solver : : MinMaxLinearEquationSolver < ValueType > > minMaxSolver ;
std : : cout < < " Starting quantile exploration with: ( " < < currentEpochValues [ 0 ] < < " , " < < currentEpochValues [ 1 ] < < " ). " < < std : : endl ;
if ( currentEpochValues [ 0 ] < 0 & & currentEpochValues [ 1 ] < 0 ) {
// This case can only happen in these cases:
assert ( lowerCostBounds [ 0 ] ) ;
assert ( currentEpochValues [ 0 ] = = - 1 ) ;
assert ( currentEpochValues [ 1 ] = = - 1 ) ;
// This case has been checked already, so we can skip it.
// Skipping this is actually necessary, since the rewardUnfolding will handle formulas like [F{"a"}>A,{"b"}>B "init"] incorrectly if A=B=-1.
+ + currentEpochValues [ 1 ] ;
}
std : : set < typename EpochManager : : Epoch > exploredEpochs ;
while ( true ) {
auto currEpoch = rewardUnfolding . getStartEpoch ( true ) ;
for ( uint64_t i = 0 ; i < 2 ; + + i ) {
if ( currentEpochValues [ i ] > = 0 ) {
rewardUnfolding . getEpochManager ( ) . setDimensionOfEpoch ( currEpoch , dimensions [ i ] , ( uint64_t ) currentEpochValues [ i ] ) ;
} else {
rewardUnfolding . getEpochManager ( ) . setBottomDimension ( currEpoch , dimensions [ i ] ) ;
}
}
auto epochOrder = rewardUnfolding . getEpochComputationOrder ( currEpoch ) ;
for ( auto const & epoch : epochOrder ) {
if ( exploredEpochs . count ( epoch ) = = 0 ) {
auto & epochModel = rewardUnfolding . setCurrentEpoch ( epoch ) ;
rewardUnfolding . setSolutionForCurrentEpoch ( epochModel . analyzeSingleObjective ( env , quantileFormula . getSubformula ( ) . asProbabilityOperatorFormula ( ) . getOptimalityType ( ) , x , b , minMaxSolver , lowerBound , upperBound ) ) ;
exploredEpochs . insert ( epoch ) ;
}
}
ValueType currValue = rewardUnfolding . getInitialStateResult ( currEpoch ) ;
std : : cout < < " Numeric result for epoch " < < rewardUnfolding . getEpochManager ( ) . toString ( currEpoch ) < < " is " < < currValue < < std : : endl ;
bool propertySatisfied ;
if ( env . solver ( ) . isForceSoundness ( ) ) {
auto lowerUpperValue = getLowerUpperBound ( env , currValue ) ;
propertySatisfied = probOpFormula . getBound ( ) . isSatisfied ( lowerUpperValue . first ) ;
if ( propertySatisfied ! = probOpFormula . getBound ( ) . isSatisfied ( lowerUpperValue . second ) ) {
// unclear result due to insufficient precision.
return false ;
}
} else {
propertySatisfied = probOpFormula . getBound ( ) . isSatisfied ( currValue ) ;
}
// If the reward bounds should be as small as possible, we should stop as soon as the property is satisfied.
// If the reward bounds should be as large as possible, we should stop as soon as the property is violated and then go a step backwards
if ( storm : : solver : : minimize ( optimizationDirections [ 0 ] ) = = propertySatisfied ) {
// We found another point for the result! Translate it to the original domain
auto point = currentEpochValues ;
std : : vector < ValueType > convertedPoint ;
for ( uint64_t i = 0 ; i < 2 ; + + i ) {
if ( lowerCostBounds [ i ] ) {
// Translate > to >=
+ + point [ i ] ;
}
if ( i = = 1 & & storm : : solver : : maximize ( optimizationDirections [ i ] ) ) {
// When maximizing, we actually searched for each x-value the smallest y-value that leads to a property violation. Hence, decreasing y by one means property satisfaction
- - point [ i ] ;
}
if ( point [ i ] < 0 ) {
// Skip this point
convertedPoint . clear ( ) ;
continue ;
}
convertedPoint . push_back ( storm : : utility : : convertNumber < ValueType > ( point [ i ] ) * rewardUnfolding . getDimension ( dimensions [ i ] ) . scalingFactor ) ;
}
if ( ! convertedPoint . empty ( ) ) {
std : : cout < < " Found another result point: ( " < < convertedPoint [ 0 ] < < " , " < < convertedPoint [ 1 ] < < " ). " < < std : : endl ;
resultPoints . push_back ( std : : move ( convertedPoint ) ) ;
}
if ( currentEpochValues [ 1 ] = = startEpochValues [ 1 ] . first ) {
break ;
} else {
+ + currentEpochValues [ 0 ] ;
currentEpochValues [ 1 ] = startEpochValues [ 1 ] . first ;
}
} else {
+ + currentEpochValues [ 1 ] ;
}
}
// When maximizing, there are border cases where one dimension can be arbitrarily large
for ( uint64_t i = 0 ; i < 2 ; + + i ) {
if ( storm : : solver : : maximize ( optimizationDirections [ i ] ) & & startEpochValues [ i ] . first > 0 ) {
std : : vector < ValueType > newResultPoint ( 2 ) ;
newResultPoint [ i ] = storm : : utility : : convertNumber < ValueType > ( startEpochValues [ i ] . first - 1 ) * startEpochValues [ i ] . second ;
newResultPoint [ 1 - i ] = storm : : utility : : infinity < ValueType > ( ) ;
resultPoints . push_back ( newResultPoint ) ;
}
}
filterDominatedPoints ( resultPoints , optimizationDirections ) ;
return true ;
}
template < typename ModelType >
boost : : optional < std : : pair < uint64_t , typename ModelType : : ValueType > > QuantileHelper < ModelType > : : computeQuantileForDimension ( Environment const & env , uint64_t dimension ) const {
// We assume that there is one bound value that violates the quantile and one bound value that satisfies it.
// Let all other open bounds approach infinity
@ -391,7 +463,6 @@ namespace storm {
auto transformedFormula = transformBoundedUntilOperator ( quantileFormula . getSubformula ( ) . asProbabilityOperatorFormula ( ) , std : : vector < BoundTransformation > ( getDimension ( ) , BoundTransformation : : None ) ) ;
MultiDimensionalRewardUnfolding < ValueType , true > rewardUnfolding ( model , transformedFormula , infinityVariables ) ;
bool upperCostBound = quantileFormula . getSubformula ( ) . asProbabilityOperatorFormula ( ) . getSubformula ( ) . asBoundedUntilFormula ( ) . hasUpperBound ( dimension ) ;
bool minimizingRewardBound = storm : : solver : : minimize ( getOptimizationDirForDimension ( dimension ) ) ;
@ -416,7 +487,18 @@ namespace storm {
}
ValueType currValue = rewardUnfolding . getInitialStateResult ( currEpoch ) ;
std : : cout < < " Numeric result for epoch " < < rewardUnfolding . getEpochManager ( ) . toString ( currEpoch ) < < " is " < < currValue < < std : : endl ;
bool propertySatisfied = transformedFormula - > getBound ( ) . isSatisfied ( currValue ) ;
bool propertySatisfied ;
if ( env . solver ( ) . isForceSoundness ( ) ) {
auto lowerUpperValue = getLowerUpperBound ( env , currValue ) ;
propertySatisfied = transformedFormula - > getBound ( ) . isSatisfied ( lowerUpperValue . first ) ;
if ( propertySatisfied ! = transformedFormula - > getBound ( ) . isSatisfied ( lowerUpperValue . second ) ) {
// unclear result due to insufficient precision.
return boost : : none ;
}
} else {
propertySatisfied = transformedFormula - > getBound ( ) . isSatisfied ( currValue ) ;
}
// If the reward bound should be as small as possible, we should stop as soon as the property is satisfied.
// If the reward bound should be as large as possible, we should stop as soon as sthe property is violated and then go a step backwards
if ( minimizingRewardBound & & propertySatisfied ) {
@ -438,7 +520,28 @@ namespace storm {
}
+ + epochValue ;
}
return { epochValue , rewardUnfolding . getDimension ( dimension ) . scalingFactor } ;
return std : : make_pair ( epochValue , rewardUnfolding . getDimension ( dimension ) . scalingFactor ) ;
}
template < typename ModelType >
bool QuantileHelper < ModelType > : : checkLimitValue ( Environment & env , storm : : storage : : BitVector const & infDimensions ) const {
auto const & probabilityBound = quantileFormula . getSubformula ( ) . asProbabilityOperatorFormula ( ) . getBound ( ) ;
// Increase the precision until we get a conclusive result
while ( true ) {
ValueType numericResult = computeLimitValue ( env , infDimensions ) ;
if ( env . solver ( ) . isForceSoundness ( ) ) {
auto lowerUpper = getLowerUpperBound ( env , numericResult ) ;
bool lowerSat = probabilityBound . isSatisfied ( lowerUpper . first ) ;
bool upperSat = probabilityBound . isSatisfied ( lowerUpper . second ) ;
if ( lowerSat = = upperSat ) {
return lowerSat ;
} else {
increasePrecision ( env ) ;
}
} else {
return probabilityBound . isSatisfied ( numericResult ) ;
}
}
}
template < typename ModelType >
@ -463,7 +566,11 @@ namespace storm {
auto transformedFormula = transformBoundedUntilOperator ( quantileFormula . getSubformula ( ) . asProbabilityOperatorFormula ( ) , bts ) ;
MultiDimensionalRewardUnfolding < ValueType , true > rewardUnfolding ( model , transformedFormula , infinityVariables ) ;
if ( ! rewardUnfolding . getProb1Objectives ( ) . empty ( ) ) {
assert ( rewardUnfolding . getProb1Objectives ( ) . size ( ) = = 1 ) ;
// The probability is one.
return storm : : utility : : one < ValueType > ( ) ;
}
// Get lower and upper bounds for the solution.
auto lowerBound = rewardUnfolding . getLowerObjectiveBound ( ) ;
auto upperBound = rewardUnfolding . getUpperObjectiveBound ( ) ;
@ -482,7 +589,7 @@ namespace storm {
ValueType numericResult = rewardUnfolding . getInitialStateResult ( initEpoch ) ;
STORM_LOG_TRACE ( " Limit probability for infinity dimensions " < < infDimensions < < " is " < < numericResult < < " . " ) ;
return transformedFormula - > getBound ( ) . isSatisfied ( numericResult ) ;
return numericResult ;
}
template class QuantileHelper < storm : : models : : sparse : : Mdp < double > > ;