@ -1,13 +1,11 @@
# include <vector>
# include <tuple>
# include <cmath>
# include "storm/utility/numerical.h"
# include <cmath>
# include <boost/math/constants/constants.hpp>
# include "storm/utility/macros.h"
# include "storm/utility/constants.h"
# include "storm/exceptions/InvalidArgumentException.h"
# include "storm/exceptions/OutOfRangeException.h"
# include "storm/exceptions/PrecisionExceededException.h"
namespace storm {
@ -15,79 +13,93 @@ namespace storm {
namespace numerical {
template < typename ValueType >
FoxGlynnResult < ValueType > : : FoxGlynnResult ( ) : left ( 0 ) , right ( 0 ) , totalWeight ( 0 ) {
FoxGlynnResult < ValueType > : : FoxGlynnResult ( ) : left ( 0 ) , right ( 0 ) , totalWeight ( storm : : utility : : zero < ValueType > ( ) ) {
// Intentionally left empty.
}
/*!
* The following implementation of Fox and Glynn ' s algorithm is taken from David Jansen ' s patched version
* in MRMC , which is based on his paper :
*
* https : //pms.cs.ru.nl/iris-diglib/src/getContent.php?id=2011-Jansen-UnderstandingFoxGlynn
*
* We have only adapted the code to match more of C + + ' s and our coding guidelines .
*/
template < typename ValueType >
FoxGlynnResult < ValueType > foxGlynnFinder ( ValueType lambda , ValueType epsilon ) {
int left , right ;
FoxGlynn * pFG ;
ValueType tau = std : : numeric_limits < ValueType > : : min ( ) ;
ValueType omega = std : : numeric_limits < ValueType > : : max ( ) ;
ValueType const sqrt_2_pi = boost : : math : : constants : : root_two_pi < ValueType > ( ) ;
ValueType const log10_e = std : : log10 ( boost : : math : : constants : : e < ValueType > ( ) ) ;
/* tau is only used in underflow checks, which we are going to do in the
logarithm domain . */
uint64_t m = static_cast < uint64_t > ( lambda ) ;
int64_t left = 0 ;
int64_t right = 0 ;
// tau is only used in underflow checks, which we are going to do in the logarithm domain.
tau = log ( tau ) ;
/* In error bound comparisons, we always compare with epsilon*sqrt_2_pi.*/
// In error bound comparisons, we always compare with epsilon*sqrt_2_pi.
epsilon * = sqrt_2_pi ;
/****Compute pFG->left truncation point****/
if ( m < 25 )
{
/* for lambda below 25 the exponential can be smaller than tau */
/* if that is the case we expect underflows and warn the user */
if ( - lambda < = tau )
{
printf ( " ERROR: Fox-Glynn: 0 < lambda < 25, underflow near Poi(%g, "
" 0) = %.2e. The results are UNRELIABLE. \n " ,
lambda , exp ( - lambda ) ) ;
// Compute left truncation point.
if ( m < 25 ) {
// For lambda below 25 the exponential can be smaller than tau. If that is the case we expect
// underflows and warn the user.
if ( - lambda < = tau ) {
STORM_LOG_WARN ( " Fox-Glynn: 0 < lambda < 25, underflow near Poi( " < < lambda < < " , 0) = " < < std : : exp ( - lambda ) < < " . The results are unreliable. " ) ;
}
/* zero is used as left truncation point for lambda <= 25 */
// Zero is used as left truncation point for lambda <= 25.
left = 0 ;
}
/* compute the left truncation point for lambda >= 25 */
/* for lambda < 25 we use zero as left truncation point */
else
{
const double bl = ( 1 + 1 / lambda ) * exp ( ( 1 / lambda ) * 0.125 ) ;
const double sqrt_lambda = sqrt ( lambda ) ;
int k ;
/*Start looking for the left truncation point*/
/* start search at k=4 (taken from original Fox-Glynn paper) */
/* increase the left truncation point until we fulfil the error
condition */
for ( k = 4 ; TRUE ; k + + ) {
double max_err ;
left = m - ( int ) ceil ( k * sqrt_lambda + 0.5 ) ;
/* for small lambda the above calculation can yield negative truncation points, crop them here */
if ( left < = 0 ) {
} else {
// Compute the left truncation point for lambda >= 25 (for lambda < 25 we use zero as left truncation point).
ValueType const bl = ( 1 + 1 / lambda ) * std : : exp ( ( 1 / lambda ) * 0.125 ) ;
ValueType const sqrt_lambda = std : : sqrt ( lambda ) ;
int64_t k ;
// Start looking for the left truncation point:
// * start search at k=4 (taken from original Fox-Glynn paper)
// * increase the left truncation point until we fulfil the error condition
for ( k = 4 ; ; + + k ) {
ValueType max_err ;
left = m - static_cast < int64_t > ( std : : ceil ( k * sqrt_lambda + 0.5 ) ) ;
// For small lambda the above calculation can yield negative truncation points, crop them here.
if ( left < = 0 ) {
left = 0 ;
break ;
}
/* Note that Propositions 2-4 in Fox--Glynn mix up notation: they
write Phi where they mean 1 - Phi . ( In Corollaries 1 and 2 , phi is
used correctly again . ) */
// Note that Propositions 2-4 in Fox--Glynn mix up notation: they write Phi where they mean
// 1 - Phi. (In Corollaries 1 and 2, phi is used correctly again.)
max_err = bl * exp ( - 0.5 * ( k * k ) ) / k ;
if ( max_err * 2 < = epsilon ) {
/* If the error on the left hand side is smaller, we can be
more lenient on the right hand side . To this end , we now set
epsilon to the part of the error that has not yet been eaten
up by the left - hand truncation . */
if ( max_err * 2 < = epsilon ) {
// If the error on the left hand side is smaller, we can be more lenient on the right hand
// side. To this end, we now set epsilon to the part of the error that has not yet been eaten
// up by the left-hand truncation.
epsilon - = max_err ;
break ;
}
}
/*Finally the left truncation point is found*/
// Finally the left truncation point is found.
}
/****Compute pFG->right truncation point****/
// Compute right truncation point.
{
double lambda_max ;
int m_max , k ;
/*According to Fox-Glynn, if lambda < 400 we should take lambda = 400,
otherwise use the original value . This is for computing the right truncation point */
if ( m < 400 ) {
lambda_max = lambda_400 ;
ValueType lambda_max ;
int64_t m_max , k ;
// According to Fox-Glynn, if lambda < 400 we should take lambda = 400, otherwise use the original
// value. This is for computing the right truncation point.
if ( m < 400 ) {
lambda_max = 400 ;
m_max = 400 ;
epsilon * = 0.662608824988162441697980 ;
/* i.e. al = (1+1/400) * exp(1/16) * sqrt_2; epsilon /= al; */
@ -97,184 +109,157 @@ namespace storm {
epsilon * = ( 1 - 1 / ( lambda + 1 ) ) * 0.664265347050632847802225 ;
/* i.e. al = (1+1/lambda) * exp(1/16) * sqrt_2; epsilon /= al; */
}
/* find right truncation point */
/* This loop is a modification to the original Fox-Glynn paper.
The search for the right truncation point is only terminated by
the error condition and not by the stop index from the FG paper .
This can yield more accurate results if necessary . */
for ( k = 4 ; TRUE ; k + + )
{
/* dkl_inv is between 1 - 1e-33 and 1 if lambda_max >= 400 and
k > = 4 ; this will always be rounded to 1.0 . We therefore leave the
factor out .
double dkl_inv = 1 - exp ( - 266 / 401.0 * ( k * sqrt ( 2 * lambda_max ) + 1.5 ) ) ;
*/
if ( k * epsilon /* actually: "k * (dkl_inv*epsilon/al)", which
has been calculated above */ > = exp ( - 0.5 * ( k * k ) ) )
// Find right truncation point.
// This loop is a modification to the original Fox-Glynn paper.
// The search for the right truncation point is only terminated by the error condition and not by
// the stop index from the FG paper. This can yield more accurate results if necessary.
for ( k = 4 ; ; + + k ) {
// dkl_inv is between 1 - 1e-33 and 1 if lambda_max >= 400 and k >= 4; this will always be
// rounded to 1.0. We therefore leave the factor out.
// double dkl_inv=1 - exp(-266/401.0 * (k*sqrt(2*lambda_max) + 1.5));
// actually: "k * (dkl_inv*epsilon/al) >= exp(-0.5 * k^2)", but epsilon has been changed appropriately.
if ( k * epsilon > = exp ( - 0.5 * ( k * k ) ) ) {
break ;
}
}
right = m_max + ( int ) ceil ( k * sqrt ( 2 * lambda_max ) + 0.5 ) ;
if ( right > m_max + ( int ) ceil ( ( lambda_max + 1 ) * 0.5 ) ) {
printf ( " ERROR: Fox-Glynn: right = %d >> lambda = %g, cannot "
" bound the right tail. The results are "
" UNRELIABLE. \n " , right , lambda_max ) ;
right = m_max + static_cast < int64_t > ( std : : ceil ( k * std : : sqrt ( 2 * lambda_max ) + 0.5 ) ) ;
if ( right > m_max + static_cast < int64_t > ( std : : ceil ( ( lambda_max + 1 ) * 0.5 ) ) ) {
STORM_LOG_WARN ( " Fox-Glynn: right = " < < right < < " >> lambda = " < < lambda_max < < " , cannot bound the right tail. The results are unreliable. " ) ;
}
}
/*Time to set the initial value for weights*/
pFG = calloc ( ( size_t ) 1 , sizeof ( FoxGlynn ) +
( right - left ) * sizeof ( pFG - > weights [ 0 ] ) ) ;
if ( NULL = = pFG ) {
err_msg_3 ( err_MEMORY , " finder(%d,%g,_,%g,_) " , m , lambda , omega , NULL ) ;
}
pFG - > right = right ;
pFG - > left = left ;
pFG - > weights [ m - left ] = omega / ( 1.0e+10 * ( right - left ) ) ;
// Time to set the initial value for weights.
FoxGlynnResult < ValueType > fgresult ;
fgresult . left = static_cast < uint64_t > ( left ) ;
fgresult . right = static_cast < uint64_t > ( right ) ;
fgresult . weights . resize ( fgresult . right - fgresult . left + 1 ) ;
if ( m > = 25 )
{
/* perform underflow check */
double result , log_c_m_inf ;
int i ;
/* we are going to compare with tau - log(w[m]) */
tau - = log ( pFG - > weights [ m - left ] ) ;
/*We take the c_m_inf = 0.14627 / sqrt( m ), as for lambda >= 25
c_m = 1 / ( sqrt ( 2.0 * pi * m ) ) * exp ( m - lambda - 1 / ( 12.0 * m ) ) = > c_m_inf */
/* Note that m-lambda is in the interval (-1,0],
and - 1 / ( 12 * m ) is in [ - 1 / ( 12 * 25 ) , 0 ) .
So , exp ( m - lambda - 1 / ( 12 * m ) ) is in ( exp ( - 1 - 1 / ( 12 * 25 ) ) , exp ( 0 ) ) .
Therefore , we can improve the lower bound on c_m to
exp ( - 1 - 1 / ( 12 * 25 ) ) / sqrt ( 2 * pi ) = ~ 0.14627 . Its logarithm is
- 1 - 1 / ( 12 * 25 ) - log ( 2 * pi ) * 0.5 = ~ - 1.922272 ( rounded towards
- infinity ) . */
fgresult . weights [ m - left ] = omega / ( 1.0e+10 * ( right - left ) ) ;
if ( m > = 25 ) {
// Perform underflow check.
ValueType result , log_c_m_inf ;
int64_t i ;
// we are going to compare with tau - log(w[m]).
tau - = std : : log ( fgresult . weights [ m - left ] ) ;
// We take the c_m_inf = 0.14627 / sqrt( m ), as for lambda >= 25
// c_m = 1 / ( sqrt( 2.0 * pi * m ) ) * exp( m - lambda - 1 / ( 12.0 * m ) ) => c_m_inf.
// Note that m-lambda is in the interval (-1,0], and -1/(12*m) is in [-1/(12*25),0).
// So, exp(m-lambda - 1/(12*m)) is in (exp(-1-1/(12*25)),exp(0)).
// Therefore, we can improve the lower bound on c_m to exp(-1-1/(12*25)) / sqrt(2*pi) = ~0.14627.
// Its logarithm is -1 - 1/(12*25) - log(2*pi) * 0.5 = ~ -1.922272 (rounded towards -infinity).
log_c_m_inf = - 1.922272 - log ( ( double ) m ) * 0.5 ;
/* We use FG's Proposition 6 directly (and not Corollary 4 i and ii),
as k_prime may be too large if pFG - > left = = 0. */
// We use FG's Proposition 6 directly (and not Corollary 4 i and ii), as k_prime may be too large
// if pFG->left == 0.
i = m - left ;
if ( i < = left /* equivalent to 2*i <= m,
equivalent to i < = lambda / 2 */ )
{
/* Use Proposition 6 (i). Note that Fox--Glynn are off by one in
the proof of this proposition ; they sum up to i - 1 , but should have
summed up to i . */
// Equivalent to 2*i <= m, equivalent to i <= lambda/2.
if ( i < = left ) {
// Use Proposition 6 (i). Note that Fox--Glynn are off by one in the proof of this proposition;
// they sum up to i-1, but should have summed up to i. */
result = log_c_m_inf
- i * ( i + 1 ) * ( 0.5 + ( 2 * i + 1 ) / ( 6 * lambda ) ) / lambda ;
}
else
{
/* Use Corollary 4 (iii). Note that k_prime <= sqrt(m+1)/m is a
misprint for k_prime < = m / sqrt ( m + 1 ) , which is equivalent to
left > = 0 , which holds trivially . */
} else {
// Use Corollary 4 (iii). Note that k_prime <= sqrt(m+1)/m is a misprint for k_prime <= m/sqrt(m+1),
// which is equivalent to left >= 0, which holds trivially.
result = - lambda ;
if ( 0 ! = left ) {
/* also use Proposition 6 (ii) */
if ( left ! = 0 ) {
// Also use Proposition 6 (ii).
double result_1 = log_c_m_inf + i * log ( 1 - i / ( double ) ( m + 1 ) ) ;
/*Take the maximum*/
if ( result_1 > result )
// Take the maximum.
if ( result_1 > result ) {
result = result_1 ;
}
}
}
if ( result < = tau )
{
const int log10_result = ( int ) floor ( result * log10_e ) ;
printf ( " ERROR: Fox-Glynn: lambda >= 25, underflow near Poi(%g,%d) "
" <= %.2fe%+d. The results are UNRELIABLE. \n " ,
lambda , left , exp ( result - log10_result / log10_e ) ,
log10_result ) ;
if ( result < = tau ) {
int64_t const log10_result = static_cast < int64_t > ( std : : floor ( result * log10_e ) ) ;
STORM_LOG_WARN ( " Fox-Glynn: lambda >= 25, underflow near Poi( " < < lambda < < " , " < < left < < " ) <= " < < std : : exp ( result - log10_result / log10_e ) < < log10_result < < " . The results are unreliable. " ) ;
}
/*We still have to perform an underflow check for the right truncation point when lambda >= 400*/
if ( m > = 400 )
{
/* Use Proposition 5 of Fox--Glynn */
// We still have to perform an underflow check for the right truncation point when lambda >= 400.
if ( m > = 400 ) {
// Use Proposition 5 of Fox--Glynn.
i = right - m ;
result = log_c_m_inf - i * ( i + 1 ) / ( 2 * lambda ) ;
if ( result < = tau )
{
const int log10_result = ( int ) floor ( result * log10_e ) ;
printf ( " ERROR: Fox-Glynn: lambda >= 400, underflow near "
" Poi(%g,%d) <= %.2fe%+d. The results are "
" UNRELIABLE. \n " , lambda , right ,
exp ( result - log10_result / log10_e ) ,
log10_result ) ;
if ( result < = tau ) {
int64_t const log10_result = static_cast < int64_t > ( std : : floor ( result * log10_e ) ) ;
STORM_LOG_WARN ( " Fox-Glynn: lambda >= 25, underflow near Poi( " < < lambda < < " , " < < right < < " ) <= " < < std : : exp ( result - log10_result / log10_e ) < < log10_result < < " . The results are unreliable. " ) ;
}
}
}
return pFG ;
return fgresult ;
}
template < typename ValueType >
FoxGlynnResult < ValueType > foxGlynnWeighter ( ValueType lambda , ValueType epsilon ) {
/*The magic m point*/
const uint64_t m = ( int ) floor ( lambda ) ;
int j , t ;
FoxGlynn * pFG ;
pFG = finder ( m , lambda , tau , omega , epsilon ) ;
if ( NULL = = pFG ) {
err_msg_4 ( err_CALLBY , " weighter(%g,%g,%g,%g) " , lambda ,
tau , omega , epsilon , NULL ) ;
ValueType tau = std : : numeric_limits < ValueType > : : min ( ) ;
// The magic m point.
uint64_t m = static_cast < uint64_t > ( lambda ) ;
int64_t j , t ;
FoxGlynnResult < ValueType > result = foxGlynnFinder ( lambda , epsilon ) ;
// Fill the left side of the array.
for ( j = m - result . left ; j > 0 ; - - j ) {
result . weights [ j - 1 ] = ( j + result . left ) / lambda * result . weights [ j ] ;
}
/*Fill the left side of the array*/
for ( j = m - pFG - > left ; j > 0 ; j - - )
pFG - > weights [ j - 1 ] = ( j + pFG - > left ) / lambda * pFG - > weights [ j ] ;
t = result . right - result . left ;
t = pFG - > right - pFG - > left ;
/*Fill the right side of the array, have two cases lambda < 400 & lambda >= 400*/
if ( m < 400 )
{
/*Perform the underflow check, according to Fox-Glynn*/
if ( pFG - > right > 600 )
{
printf ( " ERROR: Fox-Glynn: pFG->right > 600, underflow is possible \n " ) ;
freeFG ( pFG ) ;
return NULL ;
}
/*Compute weights*/
for ( j = m - pFG - > left ; j < t ; j + + )
{
double q = lambda / ( j + 1 + pFG - > left ) ;
if ( pFG - > weights [ j ] > tau / q )
{
pFG - > weights [ j + 1 ] = q * pFG - > weights [ j ] ;
} else {
// Fill the right side of the array, have two cases lambda < 400 & lambda >= 400.
if ( m < 400 ) {
// Perform the underflow check, according to Fox-Glynn.
STORM_LOG_THROW ( result . right < = 600 , storm : : exceptions : : PrecisionExceededException , " Fox-Glynn: " < < result . right < < " > 600, underflow is possible. " ) ;
// Compute weights.
for ( j = m - result . left ; j < t ; + + j ) {
ValueType q = lambda / ( j + 1 + result . left ) ;
if ( result . weights [ j ] > tau / q ) {
result . weights [ j + 1 ] = q * result . weights [ j ] ;
} else {
t = j ;
pFG - > right = j + pFG - > left ;
break ; /*It's time to compute W*/
result . right = j + result . left ;
// It's time to compute W.
break ;
}
}
} else {
/*Compute weights*/
for ( j = m - pFG - > left ; j < t ; j + + )
pFG - > weights [ j + 1 ] = lambda / ( j + 1 + pFG - > left ) * pFG - > weights [ j ] ;
} else {
// Compute weights.
for ( j = m - result . left ; j < t ; + + j ) {
result . weights [ j + 1 ] = lambda / ( j + 1 + result . left ) * result . weights [ j ] ;
}
}
/*It is time to compute the normalization weight W*/
pFG - > total_weight = 0.0 ;
// It is time to compute the normalization weight W.
result . totalWeight = storm : : utility : : zero < ValueType > ( ) ;
j = 0 ;
/* t was set above */
while ( j < t )
{
if ( pFG - > weights [ j ] < = pFG - > weights [ t ] )
{
pFG - > total_weight + = pFG - > weights [ j ] ;
// t was set above.
while ( j < t ) {
if ( result . weights [ j ] < = result . weights [ t ] ) {
result . totalWeight + = result . weights [ j ] ;
j + + ;
} else {
pFG - > total_weight + = pFG - > weights [ t ] ;
} else {
result . totalWeight + = result . weights [ t ] ;
t - - ;
}
}
pFG - > total_weight + = pFG - > weights [ j ] ;
result . totalWeight + = result . weights [ j ] ;
/* printf("Fox-Glynn: ltp = %d, rtp = %d, w = %10.15le \n", pFG->left, pFG->right, pFG->total_weight); */
STORM_LOG_TRACE ( " Fox-Glynn: ltp = " < < result . left < < " , rtp = " < < result . right < < " , w = " < < result . totalWeight < < " . " ) ;
return pFG ;
return result ;
}
template < typename ValueType >