r2rs-nmath 0.1.1

Statistics programming for Rust based on R's nmath package
Documentation
mod d;
mod p;
mod q;
mod r;

use strafe_type::{
    FloatConstraint, LogProbability64, Positive64, Probability64, Rational64, Real64,
};

pub(crate) use self::{d::*, p::*, q::*, r::*};
use crate::traits::{Distribution, RNG};

/// # The Exponential Distribution
///
/// ## Description:
///
/// Density, distribution function, quantile function and random
/// generation for the exponential distribution with rate ‘rate’
/// (i.e., mean ‘1/rate’).
///
/// ## Arguments:
///
/// * rate.
/// * scale: 1 / rate
///
/// ## Details:
///
/// If ‘rate’ is not specified, it assumes the default value of ‘1’.
///
/// The exponential distribution with rate lambda has density
///
/// $ f(x) = lambda {e}^{- lambda x} $
///
/// for $x >= 0$.
///
/// ## Density Plot
///
/// ```rust
/// # use r2rs_base::traits::StatisticalSlice;
/// # use r2rs_nmath::{distribution::ExponentialBuilder, traits::Distribution};
/// # use strafe_plot::prelude::{IntoDrawingArea, Line, Plot, PlotOptions, SVGBackend, BLACK};
/// # use strafe_type::FloatConstraint;
/// let exp = ExponentialBuilder::new().build();
/// let x = <[f64]>::sequence(-0.5, 4.0, 1000);
/// let y = x
///     .iter()
///     .map(|x| exp.density(x).unwrap())
///     .collect::<Vec<_>>();
///
/// let root = SVGBackend::new("density.svg", (1024, 768)).into_drawing_area();
/// Plot::new()
///     .with_options(PlotOptions {
///         x_axis_label: "x".to_string(),
///         y_axis_label: "density".to_string(),
///         ..Default::default()
///     })
///     .with_plottable(Line {
///         x,
///         y,
///         color: BLACK,
///         ..Default::default()
///     })
///     .plot(&root)
///     .unwrap();
/// # use std::fs::rename;
/// #     drop(root);
/// #     rename(
/// #             format!("density.svg"),
/// #             format!("src/distribution/exp/doctest_out/density.svg"),
/// #     )
/// #     .unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = embed_doc_image::embed_image!("density", "src/distribution/exp/doctest_out/density.svg")))]
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = "![Density][density]"))]
///
/// ## Note:
///
/// The cumulative hazard $H(t) = - \text{log}(1 - F(t))$ is ‘-pexp(t, r, lower
/// = FALSE, log = TRUE)’.
///
/// ## Source:
///
/// ‘dexp’, ‘pexp’ and ‘qexp’ are all calculated from numerically
/// stable versions of the definitions.
///
/// ‘rexp’ uses
///
/// Ahrens, J. H. and Dieter, U. (1972).  Computer methods for
/// sampling from the exponential and normal distributions.
/// _Communications of the ACM_, *15*, 873-882.
///
/// ## References:
///
/// Becker, R. A., Chambers, J. M. and Wilks, A. R. (1988) _The New S
/// Language_.  Wadsworth & Brooks/Cole.
///
/// Johnson, N. L., Kotz, S. and Balakrishnan, N. (1995) _Continuous
/// Univariate Distributions_, volume 1, chapter 19.  Wiley, New York.
///
/// ## See Also:
///
/// ‘exp’ for the exponential function.
///
/// Distributions for other standard distributions, including ‘dgamma’
/// for the gamma distribution and ‘dweibull’ for the Weibull
/// distribution, both of which generalize the exponential.
///
/// ## Examples:
///
/// ```rust
/// # use r2rs_nmath::distribution::ExponentialBuilder;
/// # use r2rs_nmath::traits::Distribution;
/// # let exp = ExponentialBuilder::new().build();
/// println!("{}", (-1.0_f64).exp());
/// println!("{}", exp.density(1));
/// # use std::{fs::File, io::Write};
/// # let mut f = File::create("src/distribution/exp/doctest_out/dens_1.md").unwrap();
/// # writeln!(f, "```output").unwrap();
/// # writeln!(f, "{}", (-1.0_f64).exp()).unwrap();
/// # writeln!(f, "{}", exp.density(1)).unwrap();
/// # writeln!(f, "```").unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = include_str!("doctest_out/dens_1.md")))]
///
/// A fast way to generate *sorted*  U\[0,1\]  random numbers
/// ```rust
/// # use r2rs_nmath::{
/// #     distribution::ExponentialBuilder,
/// #     rng::MersenneTwister,
/// #     traits::{Distribution, RNG},
/// # };
/// # use r2rs_stats::traits::StatArray;
/// # use strafe_plot::prelude::{
/// #     full_palette::GREY_500, IntoDrawingArea, Line, Plot, PlotOptions, Points, SVGBackend, BLACK,
/// # };
/// # use strafe_type::FloatConstraint;
/// let rsunif = |n| {
///     let mut rng = MersenneTwister::new();
///     rng.set_seed(1);
///     let exp = ExponentialBuilder::new().build();
///     let ce = (0..n)
///         .map(|_| exp.random_sample(&mut rng).unwrap())
///         .collect::<Vec<_>>()
///         .cumsum();
///     let ce_max = ce[n - 1];
///     ce.into_iter().map(|ce| ce / ce_max).collect::<Vec<_>>()
/// };
///
/// let x = (0..1000).map(|x| x as f64).collect::<Vec<_>>();
/// let y = rsunif(1000);
///
/// let root = SVGBackend::new("rsunif.svg", (1024, 768)).into_drawing_area();
/// Plot::new()
///     .with_options(PlotOptions {
///         x_axis_label: "index".to_string(),
///         y_axis_label: "rsunif".to_string(),
///         ..Default::default()
///     })
///     .with_plottable(Line {
///         x: vec![0.0, 1000.0],
///         y: vec![0.0, 1.0],
///         color: GREY_500,
///         ..Default::default()
///     })
///     .with_plottable(Points {
///         x,
///         y,
///         color: BLACK,
///         ..Default::default()
///     })
///     .plot(&root)
///     .unwrap();
/// # use std::fs::rename;
/// #     drop(root);
/// #     rename(
/// #             format!("rsunif.svg"),
/// #             format!("src/distribution/exp/doctest_out/rsunif.svg"),
/// #     )
/// #     .unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = embed_doc_image::embed_image!("rsunif", "src/distribution/exp/doctest_out/rsunif.svg")))]
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = "![Density][rsunif]"))]
pub struct Exponential {
    scale: Positive64,
}

impl Distribution for Exponential {
    fn density<R: Into<Real64>>(&self, x: R) -> Real64 {
        dexp(x, self.scale, false)
    }

    fn log_density<R: Into<Real64>>(&self, x: R) -> Real64 {
        dexp(x, self.scale, true)
    }

    fn probability<R: Into<Real64>>(&self, q: R, lower_tail: bool) -> Probability64 {
        pexp(q, self.scale, lower_tail)
    }

    fn log_probability<R: Into<Real64>>(&self, q: R, lower_tail: bool) -> LogProbability64 {
        log_pexp(q, self.scale, lower_tail)
    }

    fn quantile<P: Into<Probability64>>(&self, p: P, lower_tail: bool) -> Real64 {
        qexp(p, self.scale, lower_tail)
    }

    fn log_quantile<LP: Into<LogProbability64>>(&self, p: LP, lower_tail: bool) -> Real64 {
        log_qexp(p, self.scale, lower_tail)
    }

    fn random_sample<R: RNG>(&self, rng: &mut R) -> Real64 {
        rexp(self.scale, rng)
    }
}

pub struct ExponentialBuilder {
    scale: Option<Positive64>,
}

impl ExponentialBuilder {
    pub fn new() -> Self {
        Self { scale: None }
    }

    pub fn with_rate<R: Into<Rational64>>(&mut self, rate: R) -> &mut Self {
        self.scale = Some((1.0 / rate.into().unwrap()).into());
        self
    }

    pub fn with_scale<P: Into<Positive64>>(&mut self, scale: P) -> &mut Self {
        self.scale = Some(scale.into());
        self
    }

    pub fn build(&self) -> Exponential {
        let scale = self.scale.unwrap_or(1.0.into());

        Exponential { scale }
    }
}

#[cfg(test)]
mod tests;

#[cfg(all(test, feature = "enable_proptest"))]
mod proptests;

#[cfg(all(test, feature = "enable_covtest"))]
mod covtests;