1use crate::distribution::{Continuous, ContinuousCDF};
2use crate::function::gamma;
3use crate::statistics::*;
4use crate::{Result, StatsError};
5use rand::Rng;
6use std::f64;
7
8#[derive(Debug, Copy, Clone, PartialEq)]
24pub struct InverseGamma {
25 shape: f64,
26 rate: f64,
27}
28
29impl InverseGamma {
30 pub fn new(shape: f64, rate: f64) -> Result<InverseGamma> {
50 let is_nan = shape.is_nan() || rate.is_nan();
51 match (shape, rate, is_nan) {
52 (_, _, true) => Err(StatsError::BadParams),
53 (_, _, false) if shape <= 0.0 || rate <= 0.0 => Err(StatsError::BadParams),
54 (_, _, false) if shape.is_infinite() || rate.is_infinite() => {
55 Err(StatsError::BadParams)
56 }
57 (_, _, false) => Ok(InverseGamma { shape, rate }),
58 }
59 }
60
61 pub fn shape(&self) -> f64 {
72 self.shape
73 }
74
75 pub fn rate(&self) -> f64 {
86 self.rate
87 }
88}
89
90impl ::rand::distributions::Distribution<f64> for InverseGamma {
91 fn sample<R: Rng + ?Sized>(&self, r: &mut R) -> f64 {
92 1.0 / super::gamma::sample_unchecked(r, self.shape, self.rate)
93 }
94}
95
96impl ContinuousCDF<f64, f64> for InverseGamma {
97 fn cdf(&self, x: f64) -> f64 {
110 if x <= 0.0 {
111 0.0
112 } else if x.is_infinite() {
113 1.0
114 } else {
115 gamma::gamma_ur(self.shape, self.rate / x)
116 }
117 }
118
119 fn sf(&self, x: f64) -> f64 {
132 if x <= 0.0 {
133 1.0
134 } else if x.is_infinite() {
135 0.0
136 } else {
137 gamma::gamma_lr(self.shape, self.rate / x)
138 }
139 }
140}
141
142impl Min<f64> for InverseGamma {
143 fn min(&self) -> f64 {
153 0.0
154 }
155}
156
157impl Max<f64> for InverseGamma {
158 fn max(&self) -> f64 {
168 f64::INFINITY
169 }
170}
171
172impl Distribution<f64> for InverseGamma {
173 fn mean(&self) -> Option<f64> {
187 if self.shape <= 1.0 {
188 None
189 } else {
190 Some(self.rate / (self.shape - 1.0))
191 }
192 }
193 fn variance(&self) -> Option<f64> {
207 if self.shape <= 2.0 {
208 None
209 } else {
210 let val = self.rate * self.rate
211 / ((self.shape - 1.0) * (self.shape - 1.0) * (self.shape - 2.0));
212 Some(val)
213 }
214 }
215 fn entropy(&self) -> Option<f64> {
226 let entr = self.shape + self.rate.ln() + gamma::ln_gamma(self.shape)
227 - (1.0 + self.shape) * gamma::digamma(self.shape);
228 Some(entr)
229 }
230 fn skewness(&self) -> Option<f64> {
244 if self.shape <= 3.0 {
245 None
246 } else {
247 Some(4.0 * (self.shape - 2.0).sqrt() / (self.shape - 3.0))
248 }
249 }
250}
251
252impl Mode<Option<f64>> for InverseGamma {
253 fn mode(&self) -> Option<f64> {
263 Some(self.rate / (self.shape + 1.0))
264 }
265}
266
267impl Continuous<f64, f64> for InverseGamma {
268 fn pdf(&self, x: f64) -> f64 {
279 if x <= 0.0 || x.is_infinite() {
280 0.0
281 } else if ulps_eq!(self.shape, 1.0) {
282 self.rate / (x * x) * (-self.rate / x).exp()
283 } else {
284 self.rate.powf(self.shape) * x.powf(-self.shape - 1.0) * (-self.rate / x).exp()
285 / gamma::gamma(self.shape)
286 }
287 }
288
289 fn ln_pdf(&self, x: f64) -> f64 {
300 self.pdf(x).ln()
301 }
302}
303
304#[rustfmt::skip]
305#[cfg(all(test, feature = "nightly"))]
306mod tests {
307 use crate::statistics::*;
308 use crate::distribution::{ContinuousCDF, Continuous, InverseGamma};
309 use crate::distribution::internal::*;
310 use crate::consts::ACC;
311
312 fn try_create(shape: f64, rate: f64) -> InverseGamma {
313 let n = InverseGamma::new(shape, rate);
314 assert!(n.is_ok());
315 n.unwrap()
316 }
317
318 fn create_case(shape: f64, rate: f64) {
319 let n = try_create(shape, rate);
320 assert_eq!(shape, n.shape());
321 assert_eq!(rate, n.rate());
322 }
323
324 fn bad_create_case(shape: f64, rate: f64) {
325 let n = InverseGamma::new(shape, rate);
326 assert!(n.is_err());
327 }
328
329 fn get_value<F>(shape: f64, rate: f64, eval: F) -> f64
330 where F: Fn(InverseGamma) -> f64
331 {
332 let n = try_create(shape, rate);
333 eval(n)
334 }
335
336 fn test_case<F>(shape: f64, rate: f64, expected: f64, eval: F)
337 where F: Fn(InverseGamma) -> f64
338 {
339 let x = get_value(shape, rate, eval);
340 assert_eq!(expected, x);
341 }
342
343 fn test_almost<F>(shape: f64, rate: f64, expected: f64, acc: f64, eval: F)
344 where F: Fn(InverseGamma) -> f64
345 {
346 let x = get_value(shape, rate, eval);
347 assert_almost_eq!(expected, x, acc);
348 }
349
350 #[test]
351 fn test_create() {
352 create_case(0.1, 0.1);
353 create_case(1.0, 1.0);
354 }
355
356 #[test]
357 fn test_bad_create() {
358 bad_create_case(0.0, 1.0);
359 bad_create_case(-1.0, 1.0);
360 bad_create_case(-100.0, 1.0);
361 bad_create_case(f64::NEG_INFINITY, 1.0);
362 bad_create_case(f64::NAN, 1.0);
363 bad_create_case(1.0, 0.0);
364 bad_create_case(1.0, -1.0);
365 bad_create_case(1.0, -100.0);
366 bad_create_case(1.0, f64::NEG_INFINITY);
367 bad_create_case(1.0, f64::NAN);
368 bad_create_case(f64::INFINITY, 1.0);
369 bad_create_case(1.0, f64::INFINITY);
370 bad_create_case(f64::INFINITY, f64::INFINITY);
371 }
372
373 #[test]
374 fn test_mean() {
375 let mean = |x: InverseGamma| x.mean().unwrap();
376 test_almost(1.1, 0.1, 1.0, 1e-14, mean);
377 test_almost(1.1, 1.0, 10.0, 1e-14, mean);
378 }
379
380 #[test]
381 #[should_panic]
382 fn test_mean_with_shape_lte_1() {
383 let mean = |x: InverseGamma| x.mean().unwrap();
384 get_value(0.1, 0.1, mean);
385 }
386
387 #[test]
388 fn test_variance() {
389 let variance = |x: InverseGamma| x.variance().unwrap();
390 test_almost(2.1, 0.1, 0.08264462809917355371901, 1e-15, variance);
391 test_almost(2.1, 1.0, 8.264462809917355371901, 1e-13, variance);
392 }
393
394 #[test]
395 #[should_panic]
396 fn test_variance_with_shape_lte_2() {
397 let variance = |x: InverseGamma| x.variance().unwrap();
398 get_value(0.1, 0.1, variance);
399 }
400
401 #[test]
402 fn test_entropy() {
403 let entropy = |x: InverseGamma| x.entropy().unwrap();
404 test_almost(0.1, 0.1, 11.51625799319234475054, 1e-14, entropy);
405 test_almost(1.0, 1.0, 2.154431329803065721213, 1e-14, entropy);
406 }
407
408 #[test]
409 fn test_skewness() {
410 let skewness = |x: InverseGamma| x.skewness().unwrap();
411 test_almost(3.1, 0.1, 41.95235392680606187966, 1e-13, skewness);
412 test_almost(3.1, 1.0, 41.95235392680606187966, 1e-13, skewness);
413 test_case(5.0, 0.1, 3.464101615137754587055, skewness);
414 }
415
416 #[test]
417 #[should_panic]
418 fn test_skewness_with_shape_lte_3() {
419 let skewness = |x: InverseGamma| x.skewness().unwrap();
420 get_value(0.1, 0.1, skewness);
421 }
422
423 #[test]
424 fn test_mode() {
425 let mode = |x: InverseGamma| x.mode().unwrap();
426 test_case(0.1, 0.1, 0.09090909090909090909091, mode);
427 test_case(1.0, 1.0, 0.5, mode);
428 }
429
430 #[test]
431 fn test_min_max() {
432 let min = |x: InverseGamma| x.min();
433 let max = |x: InverseGamma| x.max();
434 test_case(1.0, 1.0, 0.0, min);
435 test_case(1.0, 1.0, f64::INFINITY, max);
436 }
437
438 #[test]
439 fn test_pdf() {
440 let pdf = |arg: f64| move |x: InverseGamma| x.pdf(arg);
441 test_almost(0.1, 0.1, 0.0628591853882328004197, 1e-15, pdf(1.2));
442 test_almost(0.1, 1.0, 0.0297426109178248997426, 1e-15, pdf(2.0));
443 test_case(1.0, 0.1, 0.04157808822362745501024, pdf(1.5));
444 test_case(1.0, 1.0, 0.3018043114632487660842, pdf(1.2));
445 }
446
447 #[test]
448 fn test_ln_pdf() {
449 let ln_pdf = |arg: f64| move |x: InverseGamma| x.ln_pdf(arg);
450 test_almost(0.1, 0.1, 0.0628591853882328004197f64.ln(), 1e-15, ln_pdf(1.2));
451 test_almost(0.1, 1.0, 0.0297426109178248997426f64.ln(), 1e-15, ln_pdf(2.0));
452 test_case(1.0, 0.1, 0.04157808822362745501024f64.ln(), ln_pdf(1.5));
453 test_case(1.0, 1.0, 0.3018043114632487660842f64.ln(), ln_pdf(1.2));
454 }
455
456 #[test]
457 fn test_cdf() {
458 let cdf = |arg: f64| move |x: InverseGamma| x.cdf(arg);
459 test_almost(0.1, 0.1, 0.1862151961946054271994, 1e-14, cdf(1.2));
460 test_almost(0.1, 1.0, 0.05859755410986647796141, 1e-14, cdf(2.0));
461 test_case(1.0, 0.1, 0.9355069850316177377304, cdf(1.5));
462 test_almost(1.0, 1.0, 0.4345982085070782231613, 1e-14, cdf(1.2));
463 }
464
465
466 #[test]
467 fn test_sf() {
468 let sf = |arg: f64| move |x: InverseGamma| x.sf(arg);
469 test_almost(0.1, 0.1, 0.8137848038053936, 1e-14, sf(1.2));
470 test_almost(0.1, 1.0, 0.9414024458901327, 1e-14, sf(2.0));
471 test_almost(1.0, 0.1, 0.0644930149683822, 1e-14, sf(1.5));
472 test_almost(1.0, 1.0, 0.565401791492922, 1e-14, sf(1.2));
473 }
474
475 #[test]
476 fn test_continuous() {
477 test::check_continuous_distribution(&try_create(1.0, 0.5), 0.0, 100.0);
478 test::check_continuous_distribution(&try_create(9.0, 2.0), 0.0, 100.0);
479 }
480}