1use crate::distribution::{Continuous, ContinuousCDF};
2use crate::function::gamma;
3use crate::statistics::*;
4use std::f64;
5
6#[derive(Copy, Clone, PartialEq, Debug)]
22pub struct InverseGamma {
23 shape: f64,
24 rate: f64,
25}
26
27#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
29#[non_exhaustive]
30pub enum InverseGammaError {
31 ShapeInvalid,
33
34 RateInvalid,
36}
37
38impl std::fmt::Display for InverseGammaError {
39 #[cfg_attr(coverage_nightly, coverage(off))]
40 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
41 match self {
42 InverseGammaError::ShapeInvalid => {
43 write!(f, "Shape is NaN, infinite, zero or less than zero")
44 }
45 InverseGammaError::RateInvalid => {
46 write!(f, "Rate is NaN, infinite, zero or less than zero")
47 }
48 }
49 }
50}
51
52impl std::error::Error for InverseGammaError {}
53
54impl InverseGamma {
55 pub fn new(shape: f64, rate: f64) -> Result<InverseGamma, InverseGammaError> {
75 if shape.is_nan() || shape.is_infinite() || shape <= 0.0 {
76 return Err(InverseGammaError::ShapeInvalid);
77 }
78
79 if rate.is_nan() || rate.is_infinite() || rate <= 0.0 {
80 return Err(InverseGammaError::RateInvalid);
81 }
82
83 Ok(InverseGamma { shape, rate })
84 }
85
86 pub fn shape(&self) -> f64 {
97 self.shape
98 }
99
100 pub fn rate(&self) -> f64 {
111 self.rate
112 }
113}
114
115impl std::fmt::Display for InverseGamma {
116 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117 write!(f, "Inv-Gamma({}, {})", self.shape, self.rate)
118 }
119}
120
121#[cfg(feature = "rand")]
122#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
123impl ::rand::distributions::Distribution<f64> for InverseGamma {
124 fn sample<R: ::rand::Rng + ?Sized>(&self, r: &mut R) -> f64 {
125 1.0 / super::gamma::sample_unchecked(r, self.shape, self.rate)
126 }
127}
128
129impl ContinuousCDF<f64, f64> for InverseGamma {
130 fn cdf(&self, x: f64) -> f64 {
143 if x <= 0.0 {
144 0.0
145 } else if x.is_infinite() {
146 1.0
147 } else {
148 gamma::gamma_ur(self.shape, self.rate / x)
149 }
150 }
151
152 fn sf(&self, x: f64) -> f64 {
165 if x <= 0.0 {
166 1.0
167 } else if x.is_infinite() {
168 0.0
169 } else {
170 gamma::gamma_lr(self.shape, self.rate / x)
171 }
172 }
173}
174
175impl Min<f64> for InverseGamma {
176 fn min(&self) -> f64 {
186 0.0
187 }
188}
189
190impl Max<f64> for InverseGamma {
191 fn max(&self) -> f64 {
201 f64::INFINITY
202 }
203}
204
205impl Distribution<f64> for InverseGamma {
206 fn mean(&self) -> Option<f64> {
220 if self.shape <= 1.0 {
221 None
222 } else {
223 Some(self.rate / (self.shape - 1.0))
224 }
225 }
226
227 fn variance(&self) -> Option<f64> {
241 if self.shape <= 2.0 {
242 None
243 } else {
244 let val = self.rate * self.rate
245 / ((self.shape - 1.0) * (self.shape - 1.0) * (self.shape - 2.0));
246 Some(val)
247 }
248 }
249
250 fn entropy(&self) -> Option<f64> {
261 let entr = self.shape + self.rate.ln() + gamma::ln_gamma(self.shape)
262 - (1.0 + self.shape) * gamma::digamma(self.shape);
263 Some(entr)
264 }
265
266 fn skewness(&self) -> Option<f64> {
280 if self.shape <= 3.0 {
281 None
282 } else {
283 Some(4.0 * (self.shape - 2.0).sqrt() / (self.shape - 3.0))
284 }
285 }
286}
287
288impl Mode<Option<f64>> for InverseGamma {
289 fn mode(&self) -> Option<f64> {
299 Some(self.rate / (self.shape + 1.0))
300 }
301}
302
303impl Continuous<f64, f64> for InverseGamma {
304 fn pdf(&self, x: f64) -> f64 {
315 if x <= 0.0 || x.is_infinite() {
316 0.0
317 } else if ulps_eq!(self.shape, 1.0) {
318 self.rate / (x * x) * (-self.rate / x).exp()
319 } else {
320 self.rate.powf(self.shape) * x.powf(-self.shape - 1.0) * (-self.rate / x).exp()
321 / gamma::gamma(self.shape)
322 }
323 }
324
325 fn ln_pdf(&self, x: f64) -> f64 {
336 self.pdf(x).ln()
337 }
338}
339
340#[rustfmt::skip]
341#[cfg(test)]
342mod tests {
343 use super::*;
344 use crate::distribution::internal::*;
345 use crate::testing_boiler;
346
347 testing_boiler!(shape: f64, rate: f64; InverseGamma; InverseGammaError);
348
349 #[test]
350 fn test_create() {
351 create_ok(0.1, 0.1);
352 create_ok(1.0, 1.0);
353 }
354
355 #[test]
356 fn test_bad_create() {
357 test_create_err(0.0, 1.0, InverseGammaError::ShapeInvalid);
358 test_create_err(1.0, -1.0, InverseGammaError::RateInvalid);
359 create_err(-1.0, 1.0);
360 create_err(-100.0, 1.0);
361 create_err(f64::NEG_INFINITY, 1.0);
362 create_err(f64::NAN, 1.0);
363 create_err(1.0, 0.0);
364 create_err(1.0, -100.0);
365 create_err(1.0, f64::NEG_INFINITY);
366 create_err(1.0, f64::NAN);
367 create_err(f64::INFINITY, 1.0);
368 create_err(1.0, f64::INFINITY);
369 create_err(f64::INFINITY, f64::INFINITY);
370 }
371
372 #[test]
373 fn test_mean() {
374 let mean = |x: InverseGamma| x.mean().unwrap();
375 test_absolute(1.1, 0.1, 1.0, 1e-14, mean);
376 test_absolute(1.1, 1.0, 10.0, 1e-14, mean);
377 }
378
379 #[test]
380 fn test_mean_with_shape_lte_1() {
381 test_none(0.1, 0.1, |dist| dist.mean());
382 }
383
384 #[test]
385 fn test_variance() {
386 let variance = |x: InverseGamma| x.variance().unwrap();
387 test_absolute(2.1, 0.1, 0.08264462809917355371901, 1e-15, variance);
388 test_absolute(2.1, 1.0, 8.264462809917355371901, 1e-13, variance);
389 }
390
391 #[test]
392 fn test_variance_with_shape_lte_2() {
393 test_none(0.1, 0.1, |dist| dist.variance());
394 }
395
396 #[test]
397 fn test_entropy() {
398 let entropy = |x: InverseGamma| x.entropy().unwrap();
399 test_absolute(0.1, 0.1, 11.51625799319234475054, 1e-14, entropy);
400 test_absolute(1.0, 1.0, 2.154431329803065721213, 1e-14, entropy);
401 }
402
403 #[test]
404 fn test_skewness() {
405 let skewness = |x: InverseGamma| x.skewness().unwrap();
406 test_absolute(3.1, 0.1, 41.95235392680606187966, 1e-13, skewness);
407 test_absolute(3.1, 1.0, 41.95235392680606187966, 1e-13, skewness);
408 test_exact(5.0, 0.1, 3.464101615137754587055, skewness);
409 }
410
411 #[test]
412 fn test_skewness_with_shape_lte_3() {
413 test_none(0.1, 0.1, |dist| dist.skewness());
414 }
415
416 #[test]
417 fn test_mode() {
418 let mode = |x: InverseGamma| x.mode().unwrap();
419 test_exact(0.1, 0.1, 0.09090909090909090909091, mode);
420 test_exact(1.0, 1.0, 0.5, mode);
421 }
422
423 #[test]
424 fn test_min_max() {
425 let min = |x: InverseGamma| x.min();
426 let max = |x: InverseGamma| x.max();
427 test_exact(1.0, 1.0, 0.0, min);
428 test_exact(1.0, 1.0, f64::INFINITY, max);
429 }
430
431 #[test]
432 fn test_pdf() {
433 let pdf = |arg: f64| move |x: InverseGamma| x.pdf(arg);
434 test_absolute(0.1, 0.1, 0.0628591853882328004197, 1e-15, pdf(1.2));
435 test_absolute(0.1, 1.0, 0.0297426109178248997426, 1e-15, pdf(2.0));
436 test_exact(1.0, 0.1, 0.04157808822362745501024, pdf(1.5));
437 test_exact(1.0, 1.0, 0.3018043114632487660842, pdf(1.2));
438 }
439
440 #[test]
441 fn test_ln_pdf() {
442 let ln_pdf = |arg: f64| move |x: InverseGamma| x.ln_pdf(arg);
443 test_absolute(0.1, 0.1, 0.0628591853882328004197f64.ln(), 1e-15, ln_pdf(1.2));
444 test_absolute(0.1, 1.0, 0.0297426109178248997426f64.ln(), 1e-15, ln_pdf(2.0));
445 test_exact(1.0, 0.1, 0.04157808822362745501024f64.ln(), ln_pdf(1.5));
446 test_exact(1.0, 1.0, 0.3018043114632487660842f64.ln(), ln_pdf(1.2));
447 }
448
449 #[test]
450 fn test_cdf() {
451 let cdf = |arg: f64| move |x: InverseGamma| x.cdf(arg);
452 test_absolute(0.1, 0.1, 0.1862151961946054271994, 1e-14, cdf(1.2));
453 test_absolute(0.1, 1.0, 0.05859755410986647796141, 1e-14, cdf(2.0));
454 test_exact(1.0, 0.1, 0.9355069850316177377304, cdf(1.5));
455 test_absolute(1.0, 1.0, 0.4345982085070782231613, 1e-14, cdf(1.2));
456 }
457
458
459 #[test]
460 fn test_sf() {
461 let sf = |arg: f64| move |x: InverseGamma| x.sf(arg);
462 test_absolute(0.1, 0.1, 0.8137848038053936, 1e-14, sf(1.2));
463 test_absolute(0.1, 1.0, 0.9414024458901327, 1e-14, sf(2.0));
464 test_absolute(1.0, 0.1, 0.0644930149683822, 1e-14, sf(1.5));
465 test_absolute(1.0, 1.0, 0.565401791492922, 1e-14, sf(1.2));
466 }
467
468 #[test]
469 fn test_continuous() {
470 test::check_continuous_distribution(&create_ok(1.0, 0.5), 0.0, 100.0);
471 test::check_continuous_distribution(&create_ok(9.0, 2.0), 0.0, 100.0);
472 }
473}