stats_claw/distributions/positive/
weibull.rs1use super::super::{Cdf, Moments, Pdf, Quantile, Sample};
21use crate::distributions::WeibullDistribution;
22use crate::rng::SplitMix64;
23use crate::special::ln_gamma;
24
25impl WeibullDistribution {
26 fn scaled_pow(&self, x: f64) -> f64 {
28 (x / self.scale_parameter).powf(self.shape_parameter)
29 }
30}
31
32impl Pdf for WeibullDistribution {
33 fn pdf(&self, x: f64) -> f64 {
34 if x < 0.0 {
35 return 0.0;
36 }
37 let (k, lambda) = (self.shape_parameter, self.scale_parameter);
38 let z = x / lambda;
39 (k / lambda) * z.powf(k - 1.0) * (-self.scaled_pow(x)).exp()
40 }
41}
42
43impl Cdf for WeibullDistribution {
44 fn cdf(&self, x: f64) -> f64 {
45 if x < 0.0 {
46 0.0
47 } else {
48 (-self.scaled_pow(x)).exp().mul_add(-1.0, 1.0)
49 }
50 }
51}
52
53impl Quantile for WeibullDistribution {
54 fn quantile(&self, p: f64) -> f64 {
55 self.scale_parameter * (-(1.0 - p).ln()).powf(1.0 / self.shape_parameter)
56 }
57}
58
59impl Moments for WeibullDistribution {
60 fn mean(&self) -> Option<f64> {
61 Some(self.scale_parameter * gamma(1.0 + 1.0 / self.shape_parameter))
62 }
63 fn variance(&self) -> Option<f64> {
64 let g1 = gamma(1.0 + 1.0 / self.shape_parameter);
65 let g2 = gamma(1.0 + 2.0 / self.shape_parameter);
66 Some(self.scale_parameter * self.scale_parameter * g2.mul_add(1.0, -(g1 * g1)))
67 }
68}
69
70impl Sample for WeibullDistribution {
71 fn sample(&self, rng: &mut SplitMix64) -> f64 {
72 self.quantile(rng.next_f64())
73 }
74}
75
76fn gamma(x: f64) -> f64 {
78 ln_gamma(x).exp()
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
87 fn shape_one_is_exponential() {
88 let d = WeibullDistribution {
89 shape_parameter: 1.0,
90 scale_parameter: 2.0,
91 ..Default::default()
92 };
93 assert!((d.cdf(2.0) - (1.0 - (-1.0f64).exp())).abs() < 1e-12);
95 }
96
97 #[test]
99 fn mean_matches_gamma_form() {
100 let d = WeibullDistribution {
101 shape_parameter: 2.0,
102 scale_parameter: 1.0,
103 ..Default::default()
104 };
105 assert!(matches!(d.mean(), Some(m) if (m - 0.886_226_925).abs() < 1e-6));
107 }
108}