1use crate::distribution::{Continuous, ContinuousCDF};
2use crate::function::gamma;
3use crate::is_zero;
4use crate::statistics::*;
5use crate::{consts, Result, StatsError};
6use rand::Rng;
7use std::f64;
8
9#[derive(Debug, Copy, Clone, PartialEq)]
25pub struct Weibull {
26 shape: f64,
27 scale: f64,
28 scale_pow_shape_inv: f64,
29}
30
31impl Weibull {
32 pub fn new(shape: f64, scale: f64) -> Result<Weibull> {
52 let is_nan = shape.is_nan() || scale.is_nan();
53 match (shape, scale, is_nan) {
54 (_, _, true) => Err(StatsError::BadParams),
55 (_, _, false) if shape <= 0.0 || scale <= 0.0 => Err(StatsError::BadParams),
56 (_, _, false) => Ok(Weibull {
57 shape,
58 scale,
59 scale_pow_shape_inv: scale.powf(-shape),
60 }),
61 }
62 }
63
64 pub fn shape(&self) -> f64 {
75 self.shape
76 }
77
78 pub fn scale(&self) -> f64 {
89 self.scale
90 }
91}
92
93impl ::rand::distributions::Distribution<f64> for Weibull {
94 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
95 let x: f64 = rng.gen();
96 self.scale * (-x.ln()).powf(1.0 / self.shape)
97 }
98}
99
100impl ContinuousCDF<f64, f64> for Weibull {
101 fn cdf(&self, x: f64) -> f64 {
112 if x < 0.0 {
113 0.0
114 } else {
115 -(-x.powf(self.shape) * self.scale_pow_shape_inv).exp_m1()
116 }
117 }
118
119 fn sf(&self, x: f64) -> f64 {
130 if x < 0.0 {
131 1.0
132 } else {
133 (-x.powf(self.shape) * self.scale_pow_shape_inv).exp()
134 }
135 }
136}
137
138impl Min<f64> for Weibull {
139 fn min(&self) -> f64 {
148 0.0
149 }
150}
151
152impl Max<f64> for Weibull {
153 fn max(&self) -> f64 {
162 f64::INFINITY
163 }
164}
165
166impl Distribution<f64> for Weibull {
167 fn mean(&self) -> Option<f64> {
178 Some(self.scale * gamma::gamma(1.0 + 1.0 / self.shape))
179 }
180 fn variance(&self) -> Option<f64> {
191 let mean = self.mean()?;
192 Some(self.scale * self.scale * gamma::gamma(1.0 + 2.0 / self.shape) - mean * mean)
193 }
194 fn entropy(&self) -> Option<f64> {
205 let entr = consts::EULER_MASCHERONI * (1.0 - 1.0 / self.shape)
206 + (self.scale / self.shape).ln()
207 + 1.0;
208 Some(entr)
209 }
210 fn skewness(&self) -> Option<f64> {
222 let mu = self.mean()?;
223 let sigma = self.std_dev()?;
224 let sigma2 = sigma * sigma;
225 let sigma3 = sigma2 * sigma;
226 let skew = (self.scale * self.scale * self.scale * gamma::gamma(1.0 + 3.0 / self.shape)
227 - 3.0 * sigma2 * mu
228 - (mu * mu * mu))
229 / sigma3;
230 Some(skew)
231 }
232}
233
234impl Median<f64> for Weibull {
235 fn median(&self) -> f64 {
245 self.scale * f64::consts::LN_2.powf(1.0 / self.shape)
246 }
247}
248
249impl Mode<Option<f64>> for Weibull {
250 fn mode(&self) -> Option<f64> {
264 let mode = if ulps_eq!(self.shape, 1.0) {
265 0.0
266 } else {
267 self.scale * ((self.shape - 1.0) / self.shape).powf(1.0 / self.shape)
268 };
269 Some(mode)
270 }
271}
272
273impl Continuous<f64, f64> for Weibull {
274 fn pdf(&self, x: f64) -> f64 {
285 if x < 0.0 {
286 0.0
287 } else if is_zero(x) && ulps_eq!(self.shape, 1.0) {
288 1.0 / self.scale
289 } else if x.is_infinite() {
290 0.0
291 } else {
292 self.shape
293 * (x / self.scale).powf(self.shape - 1.0)
294 * (-(x.powf(self.shape)) * self.scale_pow_shape_inv).exp()
295 / self.scale
296 }
297 }
298
299 fn ln_pdf(&self, x: f64) -> f64 {
310 if x < 0.0 {
311 f64::NEG_INFINITY
312 } else if is_zero(x) && ulps_eq!(self.shape, 1.0) {
313 0.0 - self.scale.ln()
314 } else if x.is_infinite() {
315 f64::NEG_INFINITY
316 } else {
317 self.shape.ln() + (self.shape - 1.0) * (x / self.scale).ln()
318 - x.powf(self.shape) * self.scale_pow_shape_inv
319 - self.scale.ln()
320 }
321 }
322}
323
324#[rustfmt::skip]
325#[cfg(all(test, feature = "nightly"))]
326mod tests {
327 use crate::statistics::*;
328 use crate::distribution::{ContinuousCDF, Continuous, Weibull};
329 use crate::distribution::internal::*;
330 use crate::consts::ACC;
331
332 fn try_create(shape: f64, scale: f64) -> Weibull {
333 let n = Weibull::new(shape, scale);
334 assert!(n.is_ok());
335 n.unwrap()
336 }
337
338 fn create_case(shape: f64, scale: f64) {
339 let n = try_create(shape, scale);
340 assert_eq!(shape, n.shape());
341 assert_eq!(scale, n.scale());
342 }
343
344 fn bad_create_case(shape: f64, scale: f64) {
345 let n = Weibull::new(shape, scale);
346 assert!(n.is_err());
347 }
348
349 fn get_value<F>(shape: f64, scale: f64, eval: F) -> f64
350 where F: Fn(Weibull) -> f64
351 {
352 let n = try_create(shape, scale);
353 eval(n)
354 }
355
356 fn test_case<F>(shape: f64, scale: f64, expected: f64, eval: F)
357 where F: Fn(Weibull) -> f64
358 {
359 let x = get_value(shape, scale, eval);
360 assert_eq!(expected, x);
361 }
362
363 fn test_almost<F>(shape: f64, scale: f64, expected: f64, acc: f64, eval: F)
364 where F: Fn(Weibull) -> f64
365 {
366 let x = get_value(shape, scale, eval);
367 assert_almost_eq!(expected, x, acc);
368 }
369
370 #[test]
371 fn test_create() {
372 create_case(1.0, 0.1);
373 create_case(10.0, 1.0);
374 create_case(11.0, 10.0);
375 create_case(12.0, f64::INFINITY);
376 }
377
378 #[test]
379 fn test_bad_create() {
380 bad_create_case(f64::NAN, 1.0);
381 bad_create_case(1.0, f64::NAN);
382 bad_create_case(f64::NAN, f64::NAN);
383 bad_create_case(1.0, -1.0);
384 bad_create_case(-1.0, 1.0);
385 bad_create_case(-1.0, -1.0);
386 bad_create_case(0.0, 0.0);
387 bad_create_case(0.0, 1.0);
388 bad_create_case(1.0, 0.0);
389 }
390
391 #[test]
392 fn test_mean() {
393 let mean = |x: Weibull| x.mean().unwrap();
394 test_case(1.0, 0.1, 0.1, mean);
395 test_case(1.0, 1.0, 1.0, mean);
396 test_almost(10.0, 10.0, 9.5135076986687318362924871772654021925505786260884, 1e-14, mean);
397 test_almost(10.0, 1.0, 0.95135076986687318362924871772654021925505786260884, 1e-15, mean);
398 }
399
400 #[test]
401 fn test_variance() {
402 let variance = |x: Weibull| x.variance().unwrap();
403 test_almost(1.0, 0.1, 0.01, 1e-16, variance);
404 test_almost(1.0, 1.0, 1.0, 1e-14, variance);
405 test_almost(10.0, 10.0, 1.3100455073468309147154581687505295026863354547057, 1e-12, variance);
406 test_almost(10.0, 1.0, 0.013100455073468309147154581687505295026863354547057, 1e-14, variance);
407 }
408
409 #[test]
410 fn test_entropy() {
411 let entropy = |x: Weibull| x.entropy().unwrap();
412 test_almost(1.0, 0.1, -1.302585092994045684018, 1e-15, entropy);
413 test_case(1.0, 1.0, 1.0, entropy);
414 test_case(10.0, 10.0, 1.519494098411379574546, entropy);
415 test_almost(10.0, 1.0, -0.783090994582666109472, 1e-15, entropy);
416 }
417
418 #[test]
419 fn test_skewnewss() {
420 let skewness = |x: Weibull| x.skewness().unwrap();
421 test_almost(1.0, 0.1, 2.0, 1e-13, skewness);
422 test_almost(1.0, 1.0, 2.0, 1e-13, skewness);
423 test_almost(10.0, 10.0, -0.63763713390314440916597757156663888653981696212127, 1e-11, skewness);
424 test_almost(10.0, 1.0, -0.63763713390314440916597757156663888653981696212127, 1e-11, skewness);
425 }
426
427 #[test]
428 fn test_median() {
429 let median = |x: Weibull| x.median();
430 test_case(1.0, 0.1, 0.069314718055994530941723212145817656807550013436026, median);
431 test_case(1.0, 1.0, 0.69314718055994530941723212145817656807550013436026, median);
432 test_case(10.0, 10.0, 9.6401223546778973665856033763604752124634905617583, median);
433 test_case(10.0, 1.0, 0.96401223546778973665856033763604752124634905617583, median);
434 }
435
436 #[test]
437 fn test_mode() {
438 let mode = |x: Weibull| x.mode().unwrap();
439 test_case(1.0, 0.1, 0.0, mode);
440 test_case(1.0, 1.0, 0.0, mode);
441 test_case(10.0, 10.0, 9.8951925820621439264623017041980483215553841533709, mode);
442 test_case(10.0, 1.0, 0.98951925820621439264623017041980483215553841533709, mode);
443 }
444
445 #[test]
446 fn test_min_max() {
447 let min = |x: Weibull| x.min();
448 let max = |x: Weibull| x.max();
449 test_case(1.0, 1.0, 0.0, min);
450 test_case(1.0, 1.0, f64::INFINITY, max);
451 }
452
453 #[test]
454 fn test_pdf() {
455 let pdf = |arg: f64| move |x: Weibull| x.pdf(arg);
456 test_case(1.0, 0.1, 10.0, pdf(0.0));
457 test_case(1.0, 0.1, 0.00045399929762484851535591515560550610237918088866565, pdf(1.0));
458 test_case(1.0, 0.1, 3.7200759760208359629596958038631183373588922923768e-43, pdf(10.0));
459 test_case(1.0, 1.0, 1.0, pdf(0.0));
460 test_case(1.0, 1.0, 0.36787944117144232159552377016146086744581113103177, pdf(1.0));
461 test_case(1.0, 1.0, 0.000045399929762484851535591515560550610237918088866565, pdf(10.0));
462 test_case(10.0, 10.0, 0.0, pdf(0.0));
463 test_almost(10.0, 10.0, 9.9999999990000000000499999999983333333333750000000e-10, 1e-24, pdf(1.0));
464 test_case(10.0, 10.0, 0.36787944117144232159552377016146086744581113103177, pdf(10.0));
465 test_case(10.0, 1.0, 0.0, pdf(0.0));
466 test_case(10.0, 1.0, 3.6787944117144232159552377016146086744581113103177, pdf(1.0));
467 test_case(10.0, 1.0, 0.0, pdf(10.0));
468 }
469
470 #[test]
471 fn test_ln_pdf() {
472 let ln_pdf = |arg: f64| move |x: Weibull| x.ln_pdf(arg);
473 test_almost(1.0, 0.1, 2.3025850929940456840179914546843642076011014886288, 1e-15, ln_pdf(0.0));
474 test_almost(1.0, 0.1, -7.6974149070059543159820085453156357923988985113712, 1e-15, ln_pdf(1.0));
475 test_case(1.0, 0.1, -97.697414907005954315982008545315635792398898511371, ln_pdf(10.0));
476 test_case(1.0, 1.0, 0.0, ln_pdf(0.0));
477 test_case(1.0, 1.0, -1.0, ln_pdf(1.0));
478 test_case(1.0, 1.0, -10.0, ln_pdf(10.0));
479 test_case(10.0, 10.0, f64::NEG_INFINITY, ln_pdf(0.0));
480 test_almost(10.0, 10.0, -20.723265837046411156161923092159277868409913397659, 1e-14, ln_pdf(1.0));
481 test_case(10.0, 10.0, -1.0, ln_pdf(10.0));
482 test_case(10.0, 1.0, f64::NEG_INFINITY, ln_pdf(0.0));
483 test_almost(10.0, 1.0, 1.3025850929940456840179914546843642076011014886288, 1e-15, ln_pdf(1.0));
484 test_case(10.0, 1.0, -9.999999976974149070059543159820085453156357923988985113712e9, ln_pdf(10.0));
485 }
486
487 #[test]
488 fn test_cdf() {
489 let cdf = |arg: f64| move |x: Weibull| x.cdf(arg);
490 test_case(1.0, 0.1, 0.0, cdf(0.0));
491 test_case(1.0, 0.1, 0.99995460007023751514846440848443944938976208191113, cdf(1.0));
492 test_case(1.0, 0.1, 0.99999999999999999999999999999999999999999996279924, cdf(10.0));
493 test_case(1.0, 1.0, 0.0, cdf(0.0));
494 test_case(1.0, 1.0, 0.63212055882855767840447622983853913255418886896823, cdf(1.0));
495 test_case(1.0, 1.0, 0.99995460007023751514846440848443944938976208191113, cdf(10.0));
496 test_case(10.0, 10.0, 0.0, cdf(0.0));
497 test_almost(10.0, 10.0, 9.9999999995000000000166666666662500000000083333333e-11, 1e-25, cdf(1.0));
498 test_case(10.0, 10.0, 0.63212055882855767840447622983853913255418886896823, cdf(10.0));
499 test_case(10.0, 1.0, 0.0, cdf(0.0));
500 test_case(10.0, 1.0, 0.63212055882855767840447622983853913255418886896823, cdf(1.0));
501 test_case(10.0, 1.0, 1.0, cdf(10.0));
502 }
503
504 #[test]
505 fn test_sf() {
506 let sf = |arg: f64| move |x: Weibull| x.sf(arg);
507 test_case(1.0, 0.1, 1.0, sf(0.0));
508 test_case(1.0, 0.1, 4.5399929762484854e-5, sf(1.0));
509 test_case(1.0, 0.1, 3.720075976020836e-44, sf(10.0));
510 test_case(1.0, 1.0, 1.0, sf(0.0));
511 test_case(1.0, 1.0, 0.36787944117144233, sf(1.0));
512 test_case(1.0, 1.0, 4.5399929762484854e-5, sf(10.0));
513 test_case(10.0, 10.0, 1.0, sf(0.0));
514 test_almost(10.0, 10.0, 0.9999999999, 1e-25, sf(1.0));
515 test_case(10.0, 10.0, 0.36787944117144233, sf(10.0));
516 test_case(10.0, 1.0, 1.0, sf(0.0));
517 test_case(10.0, 1.0, 0.36787944117144233, sf(1.0));
518 test_case(10.0, 1.0, 0.0, sf(10.0));
519 }
520
521 #[test]
522 fn test_continuous() {
523 test::check_continuous_distribution(&try_create(1.0, 0.2), 0.0, 10.0);
524 }
525}