1- use crate :: { Distribution , InverseGaussian , StandardNormal , StandardUniform } ;
1+ use crate :: { Distribution , InverseGaussian , InverseGaussianError , StandardNormal , StandardUniform } ;
22use core:: fmt;
33use num_traits:: Float ;
44use rand:: Rng ;
@@ -8,6 +8,8 @@ use rand::Rng;
88pub enum Error {
99 /// `alpha <= 0` or `nan`.
1010 AlphaNegativeOrNull ,
11+ /// `alpha` is `inf` (or, if subnormals are disabled, too close to the maximum finite float value)
12+ AlphaInfinite ,
1113 /// `|beta| >= alpha` or `nan`.
1214 AbsoluteBetaNotLessThanAlpha ,
1315}
@@ -18,6 +20,9 @@ impl fmt::Display for Error {
1820 Error :: AlphaNegativeOrNull => {
1921 "alpha <= 0 or is NaN in normal inverse Gaussian distribution"
2022 }
23+ Error :: AlphaInfinite => {
24+ "alpha is +infinity (or too close to the maximum finite value, if subnormal numbers are not supported) in normal inverse Gaussian distribution"
25+ }
2126 Error :: AbsoluteBetaNotLessThanAlpha => {
2227 "|beta| >= alpha or is NaN in normal inverse Gaussian distribution"
2328 }
6873{
6974 /// Construct a new `NormalInverseGaussian` distribution with the given alpha (tail heaviness) and
7075 /// beta (asymmetry) parameters.
76+ ///
77+ /// Note: If subnormal numbers are not supported or are disabled, this sampler may panic or produce
78+ /// incorrect output if alpha is close to the maximum finite float value.
7179 pub fn new ( alpha : F , beta : F ) -> Result < NormalInverseGaussian < F > , Error > {
7280 if !( alpha > F :: zero ( ) ) {
7381 return Err ( Error :: AlphaNegativeOrNull ) ;
@@ -76,12 +84,16 @@ where
7684 if !( beta. abs ( ) < alpha) {
7785 return Err ( Error :: AbsoluteBetaNotLessThanAlpha ) ;
7886 }
79-
80- let gamma = ( alpha * alpha - beta * beta) . sqrt ( ) ;
81-
87+ // Note: this calculation method for gamma = sqrt(alpha * alpha - beta * beta)
88+ // avoids overflow if alpha is large, ensuring gamma <= alpha, which implies
89+ // (assuming IEEE754 with subnormals) mu = 1.0 / gamma >= 1 / F::max_value() > 0.
90+ let r = beta / alpha;
91+ let gamma = alpha * ( F :: one ( ) - r * r) . sqrt ( ) ;
8292 let mu = F :: one ( ) / gamma;
83-
84- let inverse_gaussian = InverseGaussian :: new ( mu, F :: one ( ) ) . unwrap ( ) ;
93+ let inverse_gaussian = InverseGaussian :: new ( mu, F :: one ( ) ) . map_err ( |x| match x {
94+ InverseGaussianError :: MeanNegativeOrNull => Error :: AlphaInfinite ,
95+ InverseGaussianError :: ShapeNegativeOrNull => unreachable ! ( ) ,
96+ } ) ?;
8597
8698 Ok ( Self {
8799 beta,
@@ -124,6 +136,7 @@ mod tests {
124136 assert ! ( NormalInverseGaussian :: new( -1.0 , 1.0 ) . is_err( ) ) ;
125137 assert ! ( NormalInverseGaussian :: new( -1.0 , -1.0 ) . is_err( ) ) ;
126138 assert ! ( NormalInverseGaussian :: new( 1.0 , 2.0 ) . is_err( ) ) ;
139+ assert ! ( NormalInverseGaussian :: new( f64 :: INFINITY , 2.0 ) . is_err( ) ) ;
127140 assert ! ( NormalInverseGaussian :: new( 2.0 , 1.0 ) . is_ok( ) ) ;
128141 }
129142
0 commit comments