@ -16,6 +16,7 @@ 
			
		
	
		
			
				
					# include  "storm/environment/solver/TopologicalSolverEnvironment.h" 
  
			
		
	
		
			
				
					# include  "storm/environment/solver/LongRunAverageSolverEnvironment.h" 
  
			
		
	
		
			
				
					# include  "storm/environment/solver/EigenSolverEnvironment.h" 
  
			
		
	
		
			
				
					# include  "storm/environment/solver/TimeBoundedSolverEnvironment.h" 
  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					# include  "storm/utility/macros.h" 
  
			
		
	
		
			
				
					# include  "storm/utility/vector.h" 
  
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -26,6 +27,7 @@ 
			
		
	
		
			
				
					# include  "storm/storage/expressions/Expression.h" 
  
			
		
	
		
			
				
					# include  "storm/storage/expressions/ExpressionManager.h" 
  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					# include  "storm/solver/Multiplier.h" 
  
			
		
	
		
			
				
					# include  "storm/solver/MinMaxLinearEquationSolver.h" 
  
			
		
	
		
			
				
					# include  "storm/solver/LpSolver.h" 
  
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -37,7 +39,309 @@ 
			
		
	
		
			
				
					namespace  storm  {  
			
		
	
		
			
				
					    namespace  modelchecker  {  
			
		
	
		
			
				
					        namespace  helper  {  
			
		
	
		
			
				
					             
			
		
	
		
			
				
					            template < typename  ValueType >  
			
		
	
		
			
				
					            class  UnifPlusHelper  {  
			
		
	
		
			
				
					            public :  
			
		
	
		
			
				
					                UnifPlusHelper ( storm : : storage : : SparseMatrix < ValueType >  const &  transitionMatrix ,  std : : vector < ValueType >  const &  exitRateVector ,  storm : : storage : : BitVector  const &  markovianStates )  :  transitionMatrix ( transitionMatrix ) ,  exitRateVector ( exitRateVector ) ,  markovianStates ( markovianStates )  {  
			
		
	
		
			
				
					                    // Intentionally left empty
  
			
		
	
		
			
				
					                }  
			
		
	
		
			
				
					                 
			
		
	
		
			
				
					                std : : vector < ValueType >  computeBoundedUntilProbabilities ( storm : : Environment  const &  env ,  OptimizationDirection  dir ,  storm : : storage : : BitVector  const &  phiStates ,  storm : : storage : : BitVector  const &  psiStates ,  ValueType  const &  upperTimeBound )  {  
			
		
	
		
			
				
					                    // Since there is no lower time bound, we can treat the psiStates as if they are absorbing.
  
			
		
	
		
			
				
					                     
			
		
	
		
			
				
					                    // Compute some important subsets of states
  
			
		
	
		
			
				
					                    storm : : storage : : BitVector  maybeStates  =  ~ ( getProb0States ( dir ,  phiStates ,  psiStates )  |  psiStates ) ;  
			
		
	
		
			
				
					                    storm : : storage : : BitVector  markovianMaybeStates  =  markovianStates  &  maybeStates ;  
			
		
	
		
			
				
					                    storm : : storage : : BitVector  probabilisticMaybeStates  =  ~ markovianStates  &  maybeStates ;  
			
		
	
		
			
				
					                     
			
		
	
		
			
				
					                    // Catch the case where this is query can be solved by solving the untimed variant instead.
  
			
		
	
		
			
				
					                    // This is the case if there is no Markovian maybe state (e.g. if the initial state is already a psi state) of if the time bound is infinity.
  
			
		
	
		
			
				
					                    if  ( markovianMaybeStates . empty ( )  | |  storm : : utility : : isInfinity ( upperTimeBound ) )  {  
			
		
	
		
			
				
					                        return  SparseMarkovAutomatonCslHelper : : computeUntilProbabilities < ValueType > ( env ,  dir ,  transitionMatrix ,  transitionMatrix . transpose ( true ) ,  phiStates ,  psiStates ,  false ,  false ) . values ;  
			
		
	
		
			
				
					                    }  
			
		
	
		
			
				
					                     
			
		
	
		
			
				
					                    // Split the transitions into various part
  
			
		
	
		
			
				
					                    // Transitions from Markovian maybe states to all other maybe states. Insert Diagonal entries to apply uniformization later.
  
			
		
	
		
			
				
					                    storm : : storage : : SparseMatrix < ValueType >  markovianToMaybeTransitions  =  transitionMatrix . getSubmatrix ( true ,  markovianMaybeStates ,  maybeStates ,  true ) ;  
			
		
	
		
			
				
					                    // The probabilities to go from a Markovian state to a psi state in one step
  
			
		
	
		
			
				
					                    std : : vector < std : : pair < uint64_t ,  ValueType > >  markovianToPsiProbabilities  =  getSparseOneStepProbabilities ( markovianMaybeStates ,  psiStates ) ;  
			
		
	
		
			
				
					                    // Transitions from probabilistic maybe states to probabilistic maybe states.
  
			
		
	
		
			
				
					                    storm : : storage : : SparseMatrix < ValueType >  probabilisticToProbabilisticTransitions  =  transitionMatrix . getSubmatrix ( true ,  probabilisticMaybeStates ,  probabilisticMaybeStates ,  false ) ;  
			
		
	
		
			
				
					                    // Transitions from probabilistic maybe states to Markovian maybe states.
  
			
		
	
		
			
				
					                    storm : : storage : : SparseMatrix < ValueType >  probabilisticToMarkovianTransitions  =  transitionMatrix . getSubmatrix ( true ,  probabilisticMaybeStates ,  markovianMaybeStates ,  false ) ;  
			
		
	
		
			
				
					                    // The probabilities to go from a probabilistic state to a psi state in one step
  
			
		
	
		
			
				
					                    std : : vector < std : : pair < uint64_t ,  ValueType > >  probabilisticToPsiProbabilities  =  getSparseOneStepProbabilities ( probabilisticMaybeStates ,  psiStates ) ;  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					                    // Get the exit rates restricted to only markovian maybe states.
  
			
		
	
		
			
				
					                    std : : vector < ValueType >  markovianExitRates  =  storm : : utility : : vector : : filterVector ( exitRateVector ,  markovianMaybeStates ) ;  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					                    // Obtain parameters of the algorithm
  
			
		
	
		
			
				
					                    // Truncation error
  
			
		
	
		
			
				
					                    ValueType  kappa  =  storm : : utility : : convertNumber < ValueType > ( env . solver ( ) . timeBounded ( ) . getUnifPlusKappa ( ) ) ;  
			
		
	
		
			
				
					                    // Precision to be achieved
  
			
		
	
		
			
				
					                    ValueType  epsilon  =  storm : : utility : : convertNumber < ValueType > ( env . solver ( ) . timeBounded ( ) . getPrecision ( ) ) ;  
			
		
	
		
			
				
					                    bool  relativePrecision  =  env . solver ( ) . timeBounded ( ) . getRelativeTerminationCriterion ( ) ;  
			
		
	
		
			
				
					                    // Uniformization rate
  
			
		
	
		
			
				
					                    ValueType  lambda  =  * std : : max_element ( markovianExitRates . begin ( ) ,  markovianExitRates . end ( ) ) ;  
			
		
	
		
			
				
					                    STORM_LOG_DEBUG ( " Initial lambda is  "  < <  lambda  < <  " . " ) ;  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					                    // Uniformize the Markovian transitions for the first time
  
			
		
	
		
			
				
					                    uniformize ( markovianToMaybeTransitions ,  markovianToPsiProbabilities ,  markovianExitRates ,  lambda ) ;  
			
		
	
		
			
				
					                     
			
		
	
		
			
				
					                    // Set up a solver for the transitions between probabilistic states (if there are some)
  
			
		
	
		
			
				
					                    auto  solver  =  setUpProbabilisticStatesSolver ( env ,  dir ,  probabilisticToProbabilisticTransitions ) ;  
			
		
	
		
			
				
					                     
			
		
	
		
			
				
					                    // Allocate auxiliary memory that can be used during the iterations
  
			
		
	
		
			
				
					                    std : : vector < ValueType >  maybeStatesValuesLower ( maybeStates . getNumberOfSetBits ( ) ,  storm : : utility : : zero < ValueType > ( ) ) ;  // should be zero initially
  
			
		
	
		
			
				
					                    std : : vector < ValueType >  maybeStatesValuesWeightedUpper ( maybeStates . getNumberOfSetBits ( ) ,  storm : : utility : : zero < ValueType > ( ) ) ;  // should be zero initially
  
			
		
	
		
			
				
					                    std : : vector < ValueType >  maybeStatesValuesUpper ( maybeStates . getNumberOfSetBits ( ) ,  storm : : utility : : zero < ValueType > ( ) ) ;  // should be zero initially
  
			
		
	
		
			
				
					                    std : : vector < ValueType >  nextMarkovianStateValues  =  std : : move ( markovianExitRates ) ;  // At this point, the markovianExitRates are no longer needed, so we 'move' them away instead of allocating new memory
  
			
		
	
		
			
				
					                    std : : vector < ValueType >  nextProbabilisticStateValues ( probabilisticToProbabilisticTransitions . getRowGroupCount ( ) ) ;  
			
		
	
		
			
				
					                    std : : vector < ValueType >  eqSysRhs ( probabilisticToProbabilisticTransitions . getRowCount ( ) ) ;  
			
		
	
		
			
				
					                     
			
		
	
		
			
				
					                    // Start the outer iterations which increase the uniformization rate until lower and upper bound on the result vector is sufficiently small
  
			
		
	
		
			
				
					                    storm : : utility : : ProgressMeasurement  progressIterations ( " iterations " ) ;  
			
		
	
		
			
				
					                    uint64_t  iteration  =  0 ;  
			
		
	
		
			
				
					                    progressIterations . startNewMeasurement ( iteration ) ;  
			
		
	
		
			
				
					                    bool  converged  =  false ;  
			
		
	
		
			
				
					                    while  ( ! converged )  {  
			
		
	
		
			
				
					                        // Maximal step size
  
			
		
	
		
			
				
					                        uint64_t  N  =  storm : : utility : : ceil ( lambda  *  upperTimeBound  *  std : : exp ( 2 )  -  storm : : utility : : log ( kappa  *  epsilon ) ) ;  
			
		
	
		
			
				
					                         
			
		
	
		
			
				
					                        // Compute poisson distribution.
  
			
		
	
		
			
				
					                        // The division by 8 is similar to what is done for CTMCs (probably to reduce numerical impacts?)
  
			
		
	
		
			
				
					                        auto  foxGlynnResult  =  storm : : utility : : numerical : : foxGlynn ( lambda  *  upperTimeBound ,  epsilon  *  kappa  /  storm : : utility : : convertNumber < ValueType > ( 8.0 ) ) ;  
			
		
	
		
			
				
					                        // Scale the weights so they sum to one.
  
			
		
	
		
			
				
					                        storm : : utility : : vector : : scaleVectorInPlace ( foxGlynnResult . weights ,  storm : : utility : : one < ValueType > ( )  /  foxGlynnResult . totalWeight ) ;  
			
		
	
		
			
				
					                         
			
		
	
		
			
				
					                        // Set up multiplier
  
			
		
	
		
			
				
					                        auto  markovianToMaybeMultiplier  =  storm : : solver : : MultiplierFactory < ValueType > ( ) . create ( env ,  markovianToMaybeTransitions ) ;  
			
		
	
		
			
				
					                        auto  probabilisticToMarkovianMultiplier  =  storm : : solver : : MultiplierFactory < ValueType > ( ) . create ( env ,  probabilisticToMarkovianTransitions ) ;  
			
		
	
		
			
				
					                         
			
		
	
		
			
				
					                        //Perform inner iterations
  
			
		
	
		
			
				
					                        // Iteration k = N will be performed by implicitly assuming value 0 for all states.
  
			
		
	
		
			
				
					                        STORM_LOG_ASSERT ( ! storm : : utility : : vector : : hasNonZeroEntry ( maybeStatesValuesUpper ) ,  " Current values need to be initialized with zero. " ) ;  
			
		
	
		
			
				
					                        // Iterations k < N
  
			
		
	
		
			
				
					                        for  ( bool  computeLowerBound  :  { false ,  true } )  {  
			
		
	
		
			
				
					                            ValueType  targetValue  =  computeLowerBound  ?  storm : : utility : : zero < ValueType > ( )  :  storm : : utility : : one < ValueType > ( ) ;  
			
		
	
		
			
				
					                            storm : : utility : : ProgressMeasurement  progressSteps ( " steps in iteration  "  +  std : : to_string ( iteration )  +  "  for  "  +  std : : string ( computeLowerBound  ?  " lower "  :  " upper " )  +  "  bounds. " ) ;  
			
		
	
		
			
				
					                            progressSteps . setMaxCount ( N ) ;  
			
		
	
		
			
				
					                            progressSteps . startNewMeasurement ( 0 ) ;  
			
		
	
		
			
				
					                            for  ( int64_t  k  =  N  -  1 ;  k  > =  0 ;  - - k )  {  
			
		
	
		
			
				
					                                auto &  maybeStatesValues  =  computeLowerBound  ?  maybeStatesValuesLower  :  maybeStatesValuesWeightedUpper ;  
			
		
	
		
			
				
					                                 
			
		
	
		
			
				
					                                // Compute the values at Markovian maybe states.
  
			
		
	
		
			
				
					                                if  ( static_cast < uint64_t > ( k )  = =  N  -  1 )  {  
			
		
	
		
			
				
					                                    // If we are in the very first (inner) iteration, we have to set set all values to zero, since we are in the 'last' time epoch before the bound is exceeded.
  
			
		
	
		
			
				
					                                    std : : fill ( nextMarkovianStateValues . begin ( ) ,  nextMarkovianStateValues . end ( ) ,  storm : : utility : : zero < ValueType > ( ) ) ;  
			
		
	
		
			
				
					                                }  else  {  
			
		
	
		
			
				
					                                    markovianToMaybeMultiplier - > multiply ( env ,  maybeStatesValues ,  nullptr ,  nextMarkovianStateValues ) ;  
			
		
	
		
			
				
					                                    for  ( auto  const &  oneStepProb  :  markovianToPsiProbabilities )  {  
			
		
	
		
			
				
					                                        nextMarkovianStateValues [ oneStepProb . first ]  + =  oneStepProb . second  *  targetValue ;  
			
		
	
		
			
				
					                                    }  
			
		
	
		
			
				
					                                }  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					                                // Update the value when reaching a psi state.
  
			
		
	
		
			
				
					                                // This has to be done after updating the Markovian state values since we needed the 'old' target value above.
  
			
		
	
		
			
				
					                                if  ( computeLowerBound  & &  static_cast < uint64_t > ( k )  > =  foxGlynnResult . left  & &  static_cast < uint64_t > ( k )  < = foxGlynnResult . right )  {  
			
		
	
		
			
				
					                                    targetValue  + =  foxGlynnResult . weights [ k  -  foxGlynnResult . left ] ;  
			
		
	
		
			
				
					                                }  
			
		
	
		
			
				
					                                 
			
		
	
		
			
				
					                                // Compute the values at probabilistic states.
  
			
		
	
		
			
				
					                                probabilisticToMarkovianMultiplier - > multiply ( env ,  nextMarkovianStateValues ,  nullptr ,  eqSysRhs ) ;  
			
		
	
		
			
				
					                                for  ( auto  const &  oneStepProb  :  probabilisticToPsiProbabilities )  {  
			
		
	
		
			
				
					                                    eqSysRhs [ oneStepProb . first ]  + =  oneStepProb . second  *  targetValue ;  
			
		
	
		
			
				
					                                }  
			
		
	
		
			
				
					                                if  ( solver )  {  
			
		
	
		
			
				
					                                    solver - > solveEquations ( env ,  dir ,  nextProbabilisticStateValues ,  eqSysRhs ) ;  
			
		
	
		
			
				
					                                }  else  {  
			
		
	
		
			
				
					                                    storm : : utility : : vector : : reduceVectorMinOrMax ( dir ,  eqSysRhs ,  nextMarkovianStateValues ,  probabilisticToProbabilisticTransitions . getRowGroupIndices ( ) ) ;  
			
		
	
		
			
				
					                                }  
			
		
	
		
			
				
					                                 
			
		
	
		
			
				
					                                // Create the new values for the maybestates
  
			
		
	
		
			
				
					                                // Fuse the results together
  
			
		
	
		
			
				
					                                storm : : utility : : vector : : setVectorValues ( maybeStatesValues ,  markovianMaybeStates ,  nextMarkovianStateValues ) ;  
			
		
	
		
			
				
					                                storm : : utility : : vector : : setVectorValues ( maybeStatesValues ,  probabilisticMaybeStates ,  nextProbabilisticStateValues ) ;  
			
		
	
		
			
				
					                                if  ( ! computeLowerBound )  {  
			
		
	
		
			
				
					                                    // Add the scaled values to the actual result vector
  
			
		
	
		
			
				
					                                    uint64_t  i  =  N - 1 - k ;  
			
		
	
		
			
				
					                                    if  ( i  > =  foxGlynnResult . left  & &  i  < =  foxGlynnResult . right )  {  
			
		
	
		
			
				
					                                        ValueType  const &  weight  =  foxGlynnResult . weights [ i  -  foxGlynnResult . left ] ;  
			
		
	
		
			
				
					                                        storm : : utility : : vector : : addScaledVector ( maybeStatesValuesUpper ,  maybeStatesValuesWeightedUpper ,  weight ) ;  
			
		
	
		
			
				
					                                    }  
			
		
	
		
			
				
					                                }  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					                                progressSteps . updateProgress ( N - k ) ;  
			
		
	
		
			
				
					                            }  
			
		
	
		
			
				
					                            // Check if the lower and upper bound are sufficiently close to each other
  
			
		
	
		
			
				
					                            // TODO: apply this only to relevant values?
  
			
		
	
		
			
				
					                            converged  =  checkConvergence ( maybeStatesValuesLower ,  maybeStatesValuesUpper ,  boost : : none ,  epsilon ,  relativePrecision ,  kappa ) ;  
			
		
	
		
			
				
					                            if  ( converged )  {  
			
		
	
		
			
				
					                                break ;  
			
		
	
		
			
				
					                            }  
			
		
	
		
			
				
					                        }  
			
		
	
		
			
				
					                         
			
		
	
		
			
				
					                        if  ( ! converged )  {  
			
		
	
		
			
				
					                            // Increase the uniformization rate and prepare the next run
  
			
		
	
		
			
				
					                             
			
		
	
		
			
				
					                            // Double lambda.
  
			
		
	
		
			
				
					                            ValueType  oldLambda  =  lambda ;  
			
		
	
		
			
				
					                            lambda  * =  storm : : utility : : convertNumber < ValueType > ( 2.0 ) ;  
			
		
	
		
			
				
					                            STORM_LOG_DEBUG ( " Increased lambda to  "  < <  lambda  < <  " . " ) ;  
			
		
	
		
			
				
					                             
			
		
	
		
			
				
					                            if  ( relativePrecision )  {  
			
		
	
		
			
				
					                                // Reduce kappa a bit
  
			
		
	
		
			
				
					                                ValueType  minValue  =  * std : : min_element ( maybeStatesValuesUpper . begin ( ) ,  maybeStatesValuesUpper . end ( ) ) ;  
			
		
	
		
			
				
					                                kappa  * =  std : : max ( storm : : utility : : convertNumber < ValueType ,  std : : string > ( " 1/10 " ) ,  minValue ) ;  
			
		
	
		
			
				
					                            }  
			
		
	
		
			
				
					                             
			
		
	
		
			
				
					                            // Apply uniformization with new rate
  
			
		
	
		
			
				
					                            uniformize ( markovianToMaybeTransitions ,  markovianToPsiProbabilities ,  oldLambda ,  lambda ) ;  
			
		
	
		
			
				
					                             
			
		
	
		
			
				
					                            // Reset the values of the maybe states to zero.
  
			
		
	
		
			
				
					                            std : : fill ( maybeStatesValuesUpper . begin ( ) ,  maybeStatesValuesUpper . end ( ) ,  storm : : utility : : zero < ValueType > ( ) ) ;  
			
		
	
		
			
				
					                        }  
			
		
	
		
			
				
					                        progressIterations . updateProgress ( + + iteration ) ;  
			
		
	
		
			
				
					                    }  
			
		
	
		
			
				
					                     
			
		
	
		
			
				
					                    // We take the average of the lower and upper bounds
  
			
		
	
		
			
				
					                    auto  two  =  storm : : utility : : convertNumber < ValueType > ( 2.0 ) ;  
			
		
	
		
			
				
					                    storm : : utility : : vector : : applyPointwise < ValueType ,  ValueType ,  ValueType > ( maybeStatesValuesLower ,  maybeStatesValuesUpper ,  maybeStatesValuesLower ,  [ & two ]  ( ValueType  const &  a ,  ValueType  const &  b )  - >  ValueType  {  return  ( a  +  b )  /  two ;  } ) ;  
			
		
	
		
			
				
					     
			
		
	
		
			
				
					                    std : : vector < ValueType >  result ( transitionMatrix . getRowGroupCount ( ) ,  storm : : utility : : zero < ValueType > ( ) ) ;  
			
		
	
		
			
				
					                    storm : : utility : : vector : : setVectorValues ( result ,  psiStates ,  storm : : utility : : one < ValueType > ( ) ) ;  
			
		
	
		
			
				
					                    storm : : utility : : vector : : setVectorValues ( result ,  maybeStates ,  maybeStatesValuesLower ) ;  
			
		
	
		
			
				
					                    return  result ;  
			
		
	
		
			
				
					                }  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					            private :  
			
		
	
		
			
				
					                 
			
		
	
		
			
				
					                bool  checkConvergence ( std : : vector < ValueType >  const &  lower ,  std : : vector < ValueType >  const &  upper ,  boost : : optional < storm : : storage : : BitVector >  const &  relevantValues ,  ValueType  const &  epsilon ,  bool  relative ,  ValueType &  kappa )  {  
			
		
	
		
			
				
					                    if  ( ! relative )  {  
			
		
	
		
			
				
					                        if  ( relevantValues )  {  
			
		
	
		
			
				
					                            return  storm : : utility : : vector : : equalModuloPrecision ( lower ,  upper ,  relevantValues . get ( ) ,  epsilon  *  ( storm : : utility : : one < ValueType > ( )  -  kappa ) ,  false ) ;  
			
		
	
		
			
				
					                        }  else  {  
			
		
	
		
			
				
					                            return  storm : : utility : : vector : : equalModuloPrecision ( lower ,  upper ,  epsilon  *  ( storm : : utility : : one < ValueType > ( )  -  kappa ) ,  false ) ;  
			
		
	
		
			
				
					                        }  
			
		
	
		
			
				
					                    }  
			
		
	
		
			
				
					                    ValueType  truncationError  =  epsilon  *  kappa ;  
			
		
	
		
			
				
					                    ValueType  twoTimestruncationError  =  storm : : utility : : convertNumber < ValueType > ( 2.0 )  *  truncationError ;  
			
		
	
		
			
				
					                    for  ( uint64_t  i  =  0 ;  i  <  lower . size ( ) ;  + + i )  {  
			
		
	
		
			
				
					                        if  ( lower [ i ]  = =  upper [ i ] )  {  
			
		
	
		
			
				
					                            continue ;  
			
		
	
		
			
				
					                        }  
			
		
	
		
			
				
					                        if  ( lower [ i ]  < =  truncationError )  {  
			
		
	
		
			
				
					                            return  false ;  
			
		
	
		
			
				
					                        }  
			
		
	
		
			
				
					                        ValueType  absDiff  =  upper [ i ]  -  lower [ i ]  +  twoTimestruncationError ;  
			
		
	
		
			
				
					                        ValueType  relDiff  =  absDiff  /  ( lower [ i ]  -  truncationError ) ;  
			
		
	
		
			
				
					                        if  ( relDiff  >  epsilon )  {  
			
		
	
		
			
				
					                            return  false ;  
			
		
	
		
			
				
					                        }  
			
		
	
		
			
				
					                        STORM_LOG_ASSERT ( absDiff  >  storm : : utility : : zero < ValueType > ( ) ,  " Upper bound  "  < <  upper [ i ]  < <  "  is smaller than lower bound  "  < <  lower [ i ]  < <  " . " ) ;  
			
		
	
		
			
				
					                    }  
			
		
	
		
			
				
					                    return  true ;  
			
		
	
		
			
				
					                }  
			
		
	
		
			
				
					                 
			
		
	
		
			
				
					                void  uniformize ( storm : : storage : : SparseMatrix < ValueType > &  matrix ,  std : : vector < std : : pair < uint64_t ,  ValueType > > &  oneSteps ,   std : : vector < ValueType >  const &  oldRates ,  ValueType  uniformizationRate )  {  
			
		
	
		
			
				
					                    for  ( uint64_t  row  =  0 ;  row  <  matrix . getRowCount ( ) ;  + + row )  {  
			
		
	
		
			
				
					                        ValueType  const &  oldExitRate  =  oldRates [ row ] ;  
			
		
	
		
			
				
					                        if  ( oldExitRate  = =  uniformizationRate )  {  
			
		
	
		
			
				
					                            // Already uniformized.
  
			
		
	
		
			
				
					                            continue ;  
			
		
	
		
			
				
					                        }  
			
		
	
		
			
				
					                        for  ( auto &  v  :  matrix . getRow ( row ) )  {  
			
		
	
		
			
				
					                            if  ( v . getColumn ( )  = =  row )  {  
			
		
	
		
			
				
					                                ValueType  newSelfLoop  =  uniformizationRate  -  oldExitRate  +  v . getValue ( )  *  oldExitRate ;  
			
		
	
		
			
				
					                                v . setValue ( newSelfLoop  /  uniformizationRate ) ;  
			
		
	
		
			
				
					                            }  else  {  
			
		
	
		
			
				
					                                v . setValue ( v . getValue ( )  *  oldExitRate  /  uniformizationRate ) ;  
			
		
	
		
			
				
					                            }  
			
		
	
		
			
				
					                        }  
			
		
	
		
			
				
					                    }  
			
		
	
		
			
				
					                    for  ( auto &  oneStep  :  oneSteps )  {  
			
		
	
		
			
				
					                        oneStep . second  * =  oldRates [ oneStep . first ]  /  uniformizationRate ;  
			
		
	
		
			
				
					                    }  
			
		
	
		
			
				
					                }  
			
		
	
		
			
				
					                 
			
		
	
		
			
				
					                void  uniformize ( storm : : storage : : SparseMatrix < ValueType > &  matrix ,  std : : vector < std : : pair < uint64_t ,  ValueType > > &  oneSteps ,  ValueType  oldUniformizationRate ,  ValueType  newUniformizationRate )  {  
			
		
	
		
			
				
					                    if  ( oldUniformizationRate  ! =  newUniformizationRate )  {  
			
		
	
		
			
				
					                        assert ( oldUniformizationRate  <  newUniformizationRate ) ;  
			
		
	
		
			
				
					                        ValueType  rateDiff  =  newUniformizationRate  -  oldUniformizationRate ;  
			
		
	
		
			
				
					                        ValueType  rateFraction  =  oldUniformizationRate  /  newUniformizationRate ;  
			
		
	
		
			
				
					                        for  ( uint64_t  row  =  0 ;  row  <  matrix . getRowCount ( ) ;  + + row )  {  
			
		
	
		
			
				
					                            for  ( auto &  v  :  matrix . getRow ( row ) )  {  
			
		
	
		
			
				
					                                if  ( v . getColumn ( )  = =  row )  {  
			
		
	
		
			
				
					                                    ValueType  newSelfLoop  =  rateDiff  +  v . getValue ( )  *  oldUniformizationRate ;  
			
		
	
		
			
				
					                                    v . setValue ( newSelfLoop  /  newUniformizationRate ) ;  
			
		
	
		
			
				
					                                }  else  {  
			
		
	
		
			
				
					                                    v . setValue ( v . getValue ( )  *  rateFraction ) ;  
			
		
	
		
			
				
					                                }  
			
		
	
		
			
				
					                            }  
			
		
	
		
			
				
					                        }  
			
		
	
		
			
				
					                        for  ( auto &  oneStep  :  oneSteps )  {  
			
		
	
		
			
				
					                            oneStep . second  * =  rateFraction ;  
			
		
	
		
			
				
					                        }  
			
		
	
		
			
				
					                    }  
			
		
	
		
			
				
					                }  
			
		
	
		
			
				
					                 
			
		
	
		
			
				
					                std : : unique_ptr < storm : : solver : : MinMaxLinearEquationSolver < ValueType > >  setUpProbabilisticStatesSolver ( storm : : Environment  const &  env ,  OptimizationDirection  dir ,  storm : : storage : : SparseMatrix < ValueType >  const &  transitions )  const  {  
			
		
	
		
			
				
					                    std : : unique_ptr < storm : : solver : : MinMaxLinearEquationSolver < ValueType > >  solver ;  
			
		
	
		
			
				
					                    if  ( transitions . getNonzeroEntryCount ( )  >  0 )  {  
			
		
	
		
			
				
					                        storm : : solver : : GeneralMinMaxLinearEquationSolverFactory < ValueType >  factory ;  
			
		
	
		
			
				
					                        solver  =  factory . create ( env ,  transitions ) ;  
			
		
	
		
			
				
					                        solver - > setHasUniqueSolution ( true ) ;  // Assume non-zeno MA
  
			
		
	
		
			
				
					                        solver - > setHasNoEndComponents ( true ) ;  // assume non-zeno MA
  
			
		
	
		
			
				
					                        solver - > setLowerBound ( storm : : utility : : zero < ValueType > ( ) ) ;  
			
		
	
		
			
				
					                        solver - > setUpperBound ( storm : : utility : : one < ValueType > ( ) ) ;  
			
		
	
		
			
				
					                        solver - > setCachingEnabled ( true ) ;  
			
		
	
		
			
				
					                        solver - > setRequirementsChecked ( true ) ;  
			
		
	
		
			
				
					                        auto  req  =  solver - > getRequirements ( env ,  dir ) ;  
			
		
	
		
			
				
					                        req . clearBounds ( ) ;  
			
		
	
		
			
				
					                        req . clearUniqueSolution ( ) ;  
			
		
	
		
			
				
					                        STORM_LOG_THROW ( ! req . hasEnabledCriticalRequirement ( ) ,  storm : : exceptions : : UncheckedRequirementException ,  " The solver requirement  "  < <  req . getEnabledRequirementsAsString ( )  < <  "  has not been checked. " ) ;  
			
		
	
		
			
				
					                    }  
			
		
	
		
			
				
					                    return  solver ;  
			
		
	
		
			
				
					                }  
			
		
	
		
			
				
					                 
			
		
	
		
			
				
					                storm : : storage : : BitVector  getProb0States ( OptimizationDirection  dir ,  storm : : storage : : BitVector  const &  phiStates ,  storm : : storage : : BitVector  const &  psiStates )  const  {  
			
		
	
		
			
				
					                    if  ( dir  = =  storm : : solver : : OptimizationDirection : : Maximize )  {  
			
		
	
		
			
				
					                        return  storm : : utility : : graph : : performProb0A ( transitionMatrix . transpose ( true ) ,  phiStates ,  psiStates ) ;  
			
		
	
		
			
				
					                    }  else  {  
			
		
	
		
			
				
					                        return  storm : : utility : : graph : : performProb0E ( transitionMatrix ,  transitionMatrix . getRowGroupIndices ( ) ,  transitionMatrix . transpose ( true ) ,  phiStates ,  psiStates ) ;  
			
		
	
		
			
				
					                    }  
			
		
	
		
			
				
					                }  
			
		
	
		
			
				
					                 
			
		
	
		
			
				
					                /*!
  
			
		
	
		
			
				
					                 *  Returns  a  vector  with  pairs  of  state  indices  and  non - zero  probabilities  to  move  from  the  corresponding  state  to  a  target  state .  
			
		
	
		
			
				
					                 *  The  state  indices  are  with  respect  to  the  number  of  states  satisfying  the  sourceStateConstraint ,  i . e .  the  indices  are  in  the  range  [ 0 ,  sourceStateConstraint . getNumberOfSetBits ( ) )  
			
		
	
		
			
				
					                 */  
			
		
	
		
			
				
					                std : : vector < std : : pair < uint64_t ,  ValueType > >  getSparseOneStepProbabilities ( storm : : storage : : BitVector  const &  sourceStateConstraint ,  storm : : storage : : BitVector  const &  targetStateConstraint )  const  {  
			
		
	
		
			
				
					                    auto  denseResult  =  transitionMatrix . getConstrainedRowGroupSumVector ( sourceStateConstraint ,  targetStateConstraint ) ;  
			
		
	
		
			
				
					                    std : : vector < std : : pair < uint64_t ,  ValueType > >  sparseResult ;  
			
		
	
		
			
				
					                    for  ( uint64  i  =  0 ;  i  <  denseResult . size ( ) ;  + + i )  {  
			
		
	
		
			
				
					                        auto  const &  val  =  denseResult [ i ] ;  
			
		
	
		
			
				
					                        if  ( ! storm : : utility : : isZero ( val ) )  {  
			
		
	
		
			
				
					                            sparseResult . emplace_back ( i ,  val ) ;  
			
		
	
		
			
				
					                        }  
			
		
	
		
			
				
					                    }  
			
		
	
		
			
				
					                    return  sparseResult ;  
			
		
	
		
			
				
					                }  
			
		
	
		
			
				
					                 
			
		
	
		
			
				
					                void  performMarkovianStep ( storm : : Environment  const &  env ,  storm : : storage : : SparseMatrix < ValueType >  const &  markovianTransitions ,  std : : vector < std : : pair < uint64_t ,  double > >  const &  oneStepToGoalProbabilities ,  std : : vector < ValueType >  const &  currentMaybeStatesValues ,  ValueType  const &  currentGoalValue )  const  {  
			
		
	
		
			
				
					                    // Set up a multiplier for the transitions emerging at Markovian states
  
			
		
	
		
			
				
					                    auto  multiplier  =  storm : : solver : : MultiplierFactory < ValueType > ( ) . create ( env ,  markovianTransitions ) ;  
			
		
	
		
			
				
					                }  
			
		
	
		
			
				
					                 
			
		
	
		
			
				
					                storm : : storage : : SparseMatrix < ValueType >  const &  transitionMatrix ;  
			
		
	
		
			
				
					                std : : vector < ValueType >  const &  exitRateVector ;  
			
		
	
		
			
				
					                storm : : storage : : BitVector  const &  markovianStates ;  
			
		
	
		
			
				
					            } ;  
			
		
	
		
			
				
					             
			
		
	
		
			
				
					             
			
		
	
		
			
				
					             
			
		
	
		
			
				
					             
			
		
	
		
			
				
					            /**
  
			
		
	
		
			
				
					             *  Data  structure  holding  result  vectors  ( vLower ,  vUpper ,  wUpper )  for  Unif + .  
			
		
	
		
			
				
					             */  
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -186,7 +490,7 @@ namespace storm { 
			
		
	
		
			
				
					                            }  
			
		
	
		
			
				
					                             
			
		
	
		
			
				
					                            if  ( resVectorNew [ successor ]  = =  - 1 )  {  
			
		
	
		
			
				
					                                calculateUnifPlusVector ( env ,  k ,  successor ,  calcLower ,  lambda ,  numberOfProbabilisticStat es ,  relativeReachability ,  dir ,  unifVectors ,  fullTransitionMatrix ,  markovianStates ,  psiStates ,  solver ,   poisson ,  cycleFree ) ;  
			
		
	
		
			
				
					                                calculateUnifPlusVector ( env ,  k ,  successor ,  calcLower ,  lambda ,  numberOfProbabilisticChoic es ,  relativeReachability ,  dir ,  unifVectors ,  fullTransitionMatrix ,  markovianStates ,  psiStates ,  solver ,   poisson ,  cycleFree ) ;  
			
		
	
		
			
				
					                            }  
			
		
	
		
			
				
					                            res  + =  relativeReachability [ j ] [ stateCount ]  *  resVectorNew [ successor ] ;  
			
		
	
		
			
				
					                            + + stateCount ;  
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -608,18 +912,27 @@ namespace storm { 
			
		
	
		
			
				
					             
			
		
	
		
			
				
					            template  < typename  ValueType ,  typename  std : : enable_if < storm : : NumberTraits < ValueType > : : SupportsExponential ,  int > : : type >  
			
		
	
		
			
				
					            std : : vector < ValueType >  SparseMarkovAutomatonCslHelper : : computeBoundedUntilProbabilities ( Environment  const &  env ,  OptimizationDirection  dir ,  storm : : storage : : SparseMatrix < ValueType >  const &  transitionMatrix ,  std : : vector < ValueType >  const &  exitRateVector ,  storm : : storage : : BitVector  const &  markovianStates ,  storm : : storage : : BitVector  const &  phiStates ,  storm : : storage : : BitVector  const &  psiStates ,  std : : pair < double ,  double >  const &  boundsPair )  {  
			
		
	
		
			
				
					                auto  const &  settings  =  storm : : settings : : getModule < storm : : settings : : modules : : MinMaxEquationSolverSettings > ( ) ;  
			
		
	
		
			
				
					                if  ( settings . getMarkovAutomatonBoundedReachabilityMethod ( )  = =  storm : : settings : : modules : : MinMaxEquationSolverSettings : : MarkovAutomatonBoundedReachabilityMethod : : Imca )  {  
			
		
	
		
			
				
					                    return  computeBoundedUntilProbabilitiesImca ( env ,  dir ,  transitionMatrix ,  exitRateVector ,  markovianStates ,  psiStates ,  boundsPair ) ;  
			
		
	
		
			
				
					                // Choose the applicable method
  
			
		
	
		
			
				
					                auto  method  =  env . solver ( ) . timeBounded ( ) . getMaMethod ( ) ;  
			
		
	
		
			
				
					                if  ( method  = =  storm : : solver : : MaBoundedReachabilityMethod : : Imca )  {  
			
		
	
		
			
				
					                    if  ( ! phiStates . full ( ) )  {  
			
		
	
		
			
				
					                        STORM_LOG_WARN ( " Using Unif+ method because IMCA method does not support (phi Until psi) for non-trivial phi " ) ;  
			
		
	
		
			
				
					                        method  =  storm : : solver : : MaBoundedReachabilityMethod : : UnifPlus ;  
			
		
	
		
			
				
					                    }  
			
		
	
		
			
				
					                }  else  {  
			
		
	
		
			
				
					                    STORM_LOG_ASSERT ( settings . getMarkovAutomatonBoundedReachabilityMethod ( )  = =  storm : : settings : : modules : : MinMaxEquationSolverSettings : : MarkovAutomatonBoundedReachabilityMethod : : UnifPlus ,  " Unknown solution method. " ) ;  
			
		
	
		
			
				
					                    STORM_LOG_ASSERT ( method  = =  storm : : solver : : MaBoundedReachabilityMethod : : UnifPlus ,  " Unknown solution method. " ) ;  
			
		
	
		
			
				
					                    if  ( ! storm : : utility : : isZero ( boundsPair . first ) )  {  
			
		
	
		
			
				
					                        STORM_LOG_WARN ( " Using IMCA method because Unif+ does not support a lower bound > 0. " ) ;  
			
		
	
		
			
				
					                        return  computeBoundedUntilProbabilitiesImca ( env ,  dir ,  transitionMatrix ,  exitRateVector ,  markovianStates ,  psiStates ,  boundsPair ) ;  
			
		
	
		
			
				
					                    }  else  {  
			
		
	
		
			
				
					                        return  computeBoundedUntilProbabilitiesUnifPlus ( env ,  dir ,  transitionMatrix ,  exitRateVector ,  markovianStates ,  psiStates ,  boundsPair ) ;  
			
		
	
		
			
				
					                        method  =  storm : : solver : : MaBoundedReachabilityMethod : : Imca ;  
			
		
	
		
			
				
					                    }  
			
		
	
		
			
				
					                }  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					                if  ( method  = =  storm : : solver : : MaBoundedReachabilityMethod : : Imca )  {  
			
		
	
		
			
				
					                    return  computeBoundedUntilProbabilitiesImca ( env ,  dir ,  transitionMatrix ,  exitRateVector ,  markovianStates ,  psiStates ,  boundsPair ) ;  
			
		
	
		
			
				
					                }  else  {  
			
		
	
		
			
				
					                        UnifPlusHelper < ValueType >  helper ( transitionMatrix ,  exitRateVector ,  markovianStates ) ;  
			
		
	
		
			
				
					                        return  helper . computeBoundedUntilProbabilities ( env ,  dir ,  phiStates ,  psiStates ,  boundsPair . second ) ;  
			
		
	
		
			
				
					                }  
			
		
	
		
			
				
					            }  
			
		
	
		
			
				
					               
			
		
	
		
			
				
					            template  < typename  ValueType ,  typename  std : : enable_if < ! storm : : NumberTraits < ValueType > : : SupportsExponential ,  int > : : type >