1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//! Exponential distribution module.
//!
//! Contains extension methods for the Exp struct
//! found in the rand crate. This is provided through
//! traits added within the containing stats module.

use stats::dist::Distribution;
use rand::Rng;
use rand::distributions::{Sample, IndependentSample};
use rand::distributions::exponential::Exp1;

/// An Exponential random variable.
#[derive(Debug, Clone, Copy)]
pub struct Exponential {
    lambda: f64,
}

/// The default Exponential random variable.
///
/// The defaults are:
///
/// - lambda = 1
impl Default for Exponential {
    fn default() -> Exponential {
        Exponential { lambda: 1f64 }
    }
}

impl Exponential {
    /// Constructs a new Exponential random variable with given
    /// lambda parameter.
    pub fn new(lambda: f64) -> Exponential {
        Exponential { lambda: lambda }
    }

    /// Returns the lambda parameter.
    pub fn lambda(&self) -> f64 {
        self.lambda
    }
}

impl Distribution<f64> for Exponential {
    /// The pdf of the exponential distribution.
    ///
    /// # Examples
    ///
    /// ```
    /// use rusty_machine::stats::dist::Exponential;
    /// use rusty_machine::stats::dist::Distribution;
    ///
    /// // Construct an exponential with lambda parameter 7.0.
    /// let exp = Exponential::new(7f64);
    ///
    /// let pdf_zero = exp.pdf(0f64);
    /// assert!((pdf_zero - exp.lambda()).abs() < 1e-20);
    /// ```
    fn pdf(&self, x: f64) -> f64 {
        assert!(x >= 0., "Input to pdf must be positive for exponential.");
        (-x * self.lambda).exp() * self.lambda
    }

    /// The log pdf of the exponential distribution.
    ///
    /// # Examples
    ///
    /// ```
    /// // Construct an exponential with lambda parameter 5.0.
    /// use rusty_machine::stats::dist::Exponential;
    /// use rusty_machine::stats::dist::Distribution;
    ///
    /// // Construct an exponential with lambda parameter 5.0.
    /// let exp = Exponential::new(5f64);
    ///
    /// let log_pdf = exp.logpdf(3f64);
    ///
    /// assert!((log_pdf - exp.lambda().ln() + exp.lambda() * 3f64).abs() < 1e-20);
    /// ```
    fn logpdf(&self, x: f64) -> f64 {
        assert!(x >= 0.,
                "Input to log pdf must be positive for exponential.");
        self.lambda.ln() - (x * self.lambda)
    }

    /// The cdf of the exponential distribution.
    ///
    /// # Examples
    ///
    /// ```
    /// use rusty_machine::stats::dist::Exponential;
    /// use rusty_machine::stats::dist::Distribution;
    ///
    /// // Construct an exponential with lambda parameter 5.0.
    /// let exp = Exponential::new(5f64);
    ///
    /// let cdf_zero = exp.cdf(0f64);
    ///
    /// assert!((cdf_zero).abs() < 1e-20);
    /// ```
    fn cdf(&self, x: f64) -> f64 {
        assert!(x >= 0., "Input to cdf must be positive for exponential.");
        1.0 - (-x * self.lambda).exp()
    }
}

impl Sample<f64> for Exponential {
    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 {
        self.ind_sample(rng)
    }
}

impl IndependentSample<f64> for Exponential {
    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
        let Exp1(n) = rng.gen::<Exp1>();
        n / self.lambda
    }
}