1use crate::consts;
2use crate::distribution::{Continuous, ContinuousCDF};
3use crate::function::gamma;
4use crate::statistics::*;
5use std::f64;
6
7#[derive(Copy, Clone, PartialEq, Debug)]
23pub struct Weibull {
24 shape: f64,
25 scale: f64,
26 scale_pow_shape_inv: f64,
27}
28
29#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
31#[non_exhaustive]
32pub enum WeibullError {
33 ShapeInvalid,
35
36 ScaleInvalid,
38}
39
40impl std::fmt::Display for WeibullError {
41 #[cfg_attr(coverage_nightly, coverage(off))]
42 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
43 match self {
44 WeibullError::ShapeInvalid => write!(f, "Shape is NaN, zero or less than zero."),
45 WeibullError::ScaleInvalid => write!(f, "Scale is NaN, zero or less than zero."),
46 }
47 }
48}
49
50impl std::error::Error for WeibullError {}
51
52impl Weibull {
53 pub fn new(shape: f64, scale: f64) -> Result<Weibull, WeibullError> {
73 if shape.is_nan() || shape <= 0.0 {
74 return Err(WeibullError::ShapeInvalid);
75 }
76
77 if scale.is_nan() || scale <= 0.0 {
78 return Err(WeibullError::ScaleInvalid);
79 }
80
81 Ok(Weibull {
82 shape,
83 scale,
84 scale_pow_shape_inv: scale.powf(-shape),
85 })
86 }
87
88 pub fn shape(&self) -> f64 {
99 self.shape
100 }
101
102 pub fn scale(&self) -> f64 {
113 self.scale
114 }
115}
116
117impl std::fmt::Display for Weibull {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 write!(f, "Weibull({},{})", self.scale, self.shape)
120 }
121}
122
123#[cfg(feature = "rand")]
124#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
125impl ::rand::distributions::Distribution<f64> for Weibull {
126 fn sample<R: ::rand::Rng + ?Sized>(&self, rng: &mut R) -> f64 {
127 let x: f64 = rng.gen();
128 self.scale * (-x.ln()).powf(1.0 / self.shape)
129 }
130}
131
132impl ContinuousCDF<f64, f64> for Weibull {
133 fn cdf(&self, x: f64) -> f64 {
144 if x < 0.0 {
145 0.0
146 } else {
147 -(-x.powf(self.shape) * self.scale_pow_shape_inv).exp_m1()
148 }
149 }
150
151 fn sf(&self, x: f64) -> f64 {
162 if x < 0.0 {
163 1.0
164 } else {
165 (-x.powf(self.shape) * self.scale_pow_shape_inv).exp()
166 }
167 }
168
169 fn inverse_cdf(&self, p: f64) -> f64 {
180 if !(0.0..=1.0).contains(&p) {
181 panic!("x must be in [0, 1]");
182 }
183
184 (-((-p).ln_1p() / self.scale_pow_shape_inv)).powf(1.0 / self.shape)
185 }
186}
187
188impl Min<f64> for Weibull {
189 fn min(&self) -> f64 {
198 0.0
199 }
200}
201
202impl Max<f64> for Weibull {
203 fn max(&self) -> f64 {
212 f64::INFINITY
213 }
214}
215
216impl Distribution<f64> for Weibull {
217 fn mean(&self) -> Option<f64> {
228 Some(self.scale * gamma::gamma(1.0 + 1.0 / self.shape))
229 }
230
231 fn variance(&self) -> Option<f64> {
242 let mean = self.mean()?;
243 Some(self.scale * self.scale * gamma::gamma(1.0 + 2.0 / self.shape) - mean * mean)
244 }
245
246 fn entropy(&self) -> Option<f64> {
257 let entr = consts::EULER_MASCHERONI * (1.0 - 1.0 / self.shape)
258 + (self.scale / self.shape).ln()
259 + 1.0;
260 Some(entr)
261 }
262
263 fn skewness(&self) -> Option<f64> {
275 let mu = self.mean()?;
276 let sigma = self.std_dev()?;
277 let sigma2 = sigma * sigma;
278 let sigma3 = sigma2 * sigma;
279 let skew = (self.scale * self.scale * self.scale * gamma::gamma(1.0 + 3.0 / self.shape)
280 - 3.0 * sigma2 * mu
281 - (mu * mu * mu))
282 / sigma3;
283 Some(skew)
284 }
285}
286
287impl Median<f64> for Weibull {
288 fn median(&self) -> f64 {
298 self.scale * f64::consts::LN_2.powf(1.0 / self.shape)
299 }
300}
301
302impl Mode<Option<f64>> for Weibull {
303 fn mode(&self) -> Option<f64> {
317 let mode = if ulps_eq!(self.shape, 1.0) {
318 0.0
319 } else {
320 self.scale * ((self.shape - 1.0) / self.shape).powf(1.0 / self.shape)
321 };
322 Some(mode)
323 }
324}
325
326impl Continuous<f64, f64> for Weibull {
327 fn pdf(&self, x: f64) -> f64 {
338 if x < 0.0 {
339 0.0
340 } else if x == 0.0 && ulps_eq!(self.shape, 1.0) {
341 1.0 / self.scale
342 } else if x.is_infinite() {
343 0.0
344 } else {
345 self.shape
346 * (x / self.scale).powf(self.shape - 1.0)
347 * (-(x.powf(self.shape)) * self.scale_pow_shape_inv).exp()
348 / self.scale
349 }
350 }
351
352 fn ln_pdf(&self, x: f64) -> f64 {
363 if x < 0.0 {
364 f64::NEG_INFINITY
365 } else if x == 0.0 && ulps_eq!(self.shape, 1.0) {
366 0.0 - self.scale.ln()
367 } else if x.is_infinite() {
368 f64::NEG_INFINITY
369 } else {
370 self.shape.ln() + (self.shape - 1.0) * (x / self.scale).ln()
371 - x.powf(self.shape) * self.scale_pow_shape_inv
372 - self.scale.ln()
373 }
374 }
375}
376
377#[rustfmt::skip]
378#[cfg(test)]
379mod tests {
380 use super::*;
381 use crate::distribution::internal::*;
382 use crate::testing_boiler;
383
384 testing_boiler!(shape: f64, scale: f64; Weibull; WeibullError);
385
386 #[test]
387 fn test_create() {
388 create_ok(1.0, 0.1);
389 create_ok(10.0, 1.0);
390 create_ok(11.0, 10.0);
391 create_ok(12.0, f64::INFINITY);
392 }
393
394 #[test]
395 fn test_bad_create() {
396 test_create_err(f64::NAN, 1.0, WeibullError::ShapeInvalid);
397 test_create_err(1.0, f64::NAN, WeibullError::ScaleInvalid);
398 create_err(f64::NAN, f64::NAN);
399 create_err(1.0, -1.0);
400 create_err(-1.0, 1.0);
401 create_err(-1.0, -1.0);
402 create_err(0.0, 0.0);
403 create_err(0.0, 1.0);
404 create_err(1.0, 0.0);
405 }
406
407 #[test]
408 fn test_mean() {
409 let mean = |x: Weibull| x.mean().unwrap();
410 test_exact(1.0, 0.1, 0.1, mean);
411 test_exact(1.0, 1.0, 1.0, mean);
412 test_absolute(10.0, 10.0, 9.5135076986687318362924871772654021925505786260884, 1e-14, mean);
413 test_absolute(10.0, 1.0, 0.95135076986687318362924871772654021925505786260884, 1e-15, mean);
414 }
415
416 #[test]
417 fn test_variance() {
418 let variance = |x: Weibull| x.variance().unwrap();
419 test_absolute(1.0, 0.1, 0.01, 1e-16, variance);
420 test_absolute(1.0, 1.0, 1.0, 1e-14, variance);
421 test_absolute(10.0, 10.0, 1.3100455073468309147154581687505295026863354547057, 1e-12, variance);
422 test_absolute(10.0, 1.0, 0.013100455073468309147154581687505295026863354547057, 1e-14, variance);
423 }
424
425 #[test]
426 fn test_entropy() {
427 let entropy = |x: Weibull| x.entropy().unwrap();
428 test_absolute(1.0, 0.1, -1.302585092994045684018, 1e-15, entropy);
429 test_exact(1.0, 1.0, 1.0, entropy);
430 test_exact(10.0, 10.0, 1.519494098411379574546, entropy);
431 test_absolute(10.0, 1.0, -0.783090994582666109472, 1e-15, entropy);
432 }
433
434 #[test]
435 fn test_skewnewss() {
436 let skewness = |x: Weibull| x.skewness().unwrap();
437 test_absolute(1.0, 0.1, 2.0, 1e-13, skewness);
438 test_absolute(1.0, 1.0, 2.0, 1e-13, skewness);
439 test_absolute(10.0, 10.0, -0.63763713390314440916597757156663888653981696212127, 1e-11, skewness);
440 test_absolute(10.0, 1.0, -0.63763713390314440916597757156663888653981696212127, 1e-11, skewness);
441 }
442
443 #[test]
444 fn test_median() {
445 let median = |x: Weibull| x.median();
446 test_exact(1.0, 0.1, 0.069314718055994530941723212145817656807550013436026, median);
447 test_exact(1.0, 1.0, 0.69314718055994530941723212145817656807550013436026, median);
448 test_exact(10.0, 10.0, 9.6401223546778973665856033763604752124634905617583, median);
449 test_exact(10.0, 1.0, 0.96401223546778973665856033763604752124634905617583, median);
450 }
451
452 #[test]
453 fn test_mode() {
454 let mode = |x: Weibull| x.mode().unwrap();
455 test_exact(1.0, 0.1, 0.0, mode);
456 test_exact(1.0, 1.0, 0.0, mode);
457 test_exact(10.0, 10.0, 9.8951925820621439264623017041980483215553841533709, mode);
458 test_exact(10.0, 1.0, 0.98951925820621439264623017041980483215553841533709, mode);
459 }
460
461 #[test]
462 fn test_min_max() {
463 let min = |x: Weibull| x.min();
464 let max = |x: Weibull| x.max();
465 test_exact(1.0, 1.0, 0.0, min);
466 test_exact(1.0, 1.0, f64::INFINITY, max);
467 }
468
469 #[test]
470 fn test_pdf() {
471 let pdf = |arg: f64| move |x: Weibull| x.pdf(arg);
472 test_exact(1.0, 0.1, 10.0, pdf(0.0));
473 test_exact(1.0, 0.1, 0.00045399929762484851535591515560550610237918088866565, pdf(1.0));
474 test_exact(1.0, 0.1, 3.7200759760208359629596958038631183373588922923768e-43, pdf(10.0));
475 test_exact(1.0, 1.0, 1.0, pdf(0.0));
476 test_exact(1.0, 1.0, 0.36787944117144232159552377016146086744581113103177, pdf(1.0));
477 test_exact(1.0, 1.0, 0.000045399929762484851535591515560550610237918088866565, pdf(10.0));
478 test_exact(10.0, 10.0, 0.0, pdf(0.0));
479 test_absolute(10.0, 10.0, 9.9999999990000000000499999999983333333333750000000e-10, 1e-24, pdf(1.0));
480 test_exact(10.0, 10.0, 0.36787944117144232159552377016146086744581113103177, pdf(10.0));
481 test_exact(10.0, 1.0, 0.0, pdf(0.0));
482 test_exact(10.0, 1.0, 3.6787944117144232159552377016146086744581113103177, pdf(1.0));
483 test_exact(10.0, 1.0, 0.0, pdf(10.0));
484 }
485
486 #[test]
487 fn test_ln_pdf() {
488 let ln_pdf = |arg: f64| move |x: Weibull| x.ln_pdf(arg);
489 test_absolute(1.0, 0.1, 2.3025850929940456840179914546843642076011014886288, 1e-15, ln_pdf(0.0));
490 test_absolute(1.0, 0.1, -7.6974149070059543159820085453156357923988985113712, 1e-15, ln_pdf(1.0));
491 test_exact(1.0, 0.1, -97.697414907005954315982008545315635792398898511371, ln_pdf(10.0));
492 test_exact(1.0, 1.0, 0.0, ln_pdf(0.0));
493 test_exact(1.0, 1.0, -1.0, ln_pdf(1.0));
494 test_exact(1.0, 1.0, -10.0, ln_pdf(10.0));
495 test_exact(10.0, 10.0, f64::NEG_INFINITY, ln_pdf(0.0));
496 test_absolute(10.0, 10.0, -20.723265837046411156161923092159277868409913397659, 1e-14, ln_pdf(1.0));
497 test_exact(10.0, 10.0, -1.0, ln_pdf(10.0));
498 test_exact(10.0, 1.0, f64::NEG_INFINITY, ln_pdf(0.0));
499 test_absolute(10.0, 1.0, 1.3025850929940456840179914546843642076011014886288, 1e-15, ln_pdf(1.0));
500 test_exact(10.0, 1.0, -9.999999976974149070059543159820085453156357923988985113712e9, ln_pdf(10.0));
501 }
502
503 #[test]
504 fn test_cdf() {
505 let cdf = |arg: f64| move |x: Weibull| x.cdf(arg);
506 test_exact(1.0, 0.1, 0.0, cdf(0.0));
507 test_exact(1.0, 0.1, 0.99995460007023751514846440848443944938976208191113, cdf(1.0));
508 test_exact(1.0, 0.1, 0.99999999999999999999999999999999999999999996279924, cdf(10.0));
509 test_exact(1.0, 1.0, 0.0, cdf(0.0));
510 test_exact(1.0, 1.0, 0.63212055882855767840447622983853913255418886896823, cdf(1.0));
511 test_exact(1.0, 1.0, 0.99995460007023751514846440848443944938976208191113, cdf(10.0));
512 test_exact(10.0, 10.0, 0.0, cdf(0.0));
513 test_absolute(10.0, 10.0, 9.9999999995000000000166666666662500000000083333333e-11, 1e-25, cdf(1.0));
514 test_exact(10.0, 10.0, 0.63212055882855767840447622983853913255418886896823, cdf(10.0));
515 test_exact(10.0, 1.0, 0.0, cdf(0.0));
516 test_exact(10.0, 1.0, 0.63212055882855767840447622983853913255418886896823, cdf(1.0));
517 test_exact(10.0, 1.0, 1.0, cdf(10.0));
518 }
519
520 #[test]
521 fn test_sf() {
522 let sf = |arg: f64| move |x: Weibull| x.sf(arg);
523 test_exact(1.0, 0.1, 1.0, sf(0.0));
524 test_exact(1.0, 0.1, 4.5399929762484854e-5, sf(1.0));
525 test_exact(1.0, 0.1, 3.720075976020836e-44, sf(10.0));
526 test_exact(1.0, 1.0, 1.0, sf(0.0));
527 test_exact(1.0, 1.0, 0.36787944117144233, sf(1.0));
528 test_exact(1.0, 1.0, 4.5399929762484854e-5, sf(10.0));
529 test_exact(10.0, 10.0, 1.0, sf(0.0));
530 test_absolute(10.0, 10.0, 0.9999999999, 1e-25, sf(1.0));
531 test_exact(10.0, 10.0, 0.36787944117144233, sf(10.0));
532 test_exact(10.0, 1.0, 1.0, sf(0.0));
533 test_exact(10.0, 1.0, 0.36787944117144233, sf(1.0));
534 test_exact(10.0, 1.0, 0.0, sf(10.0));
535 }
536
537 #[test]
538 fn test_inverse_cdf() {
539 let func = |arg: f64| move |x: Weibull| x.inverse_cdf(x.cdf(arg));
540 test_exact(1.0, 0.1, 0.0, func(0.0));
541 test_absolute(1.0, 0.1, 1.0, 1e-13, func(1.0));
542 test_exact(1.0, 1.0, 0.0, func(0.0));
543 test_exact(1.0, 1.0, 1.0, func(1.0));
544 test_absolute(1.0, 1.0, 10.0, 1e-10, func(10.0));
545 test_exact(10.0, 10.0, 0.0, func(0.0));
546 test_absolute(10.0, 10.0, 1.0, 1e-5, func(1.0));
547 test_absolute(10.0, 10.0, 10.0, 1e-10, func(10.0));
548 test_exact(10.0, 1.0, 0.0, func(0.0));
549 test_exact(10.0, 1.0, 1.0, func(1.0));
550 }
551
552 #[test]
553 fn test_continuous() {
554 test::check_continuous_distribution(&create_ok(1.0, 0.2), 0.0, 10.0);
555 }
556}