From d75629aff0525f45f013ced8d0bc84c4929b79c4 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 21 Dec 2017 12:39:29 +0000 Subject: [PATCH 01/14] Replace Sample and IndependentSample with Distribution This is heavily inspired by #27 by @GrahamDennis but simpler trait and maintains backwards compatibility with deprecations. --- benches/distributions/exponential.rs | 4 +- benches/distributions/gamma.rs | 6 +- benches/distributions/normal.rs | 4 +- src/distributions/exponential.rs | 19 ++-- src/distributions/gamma.rs | 97 +++++++--------- src/distributions/mod.rs | 159 +++++++++++++++++++++------ src/distributions/normal.rs | 36 +++--- src/distributions/range.rs | 24 ++-- src/lib.rs | 16 +-- 9 files changed, 215 insertions(+), 150 deletions(-) diff --git a/benches/distributions/exponential.rs b/benches/distributions/exponential.rs index 152615d7ba3..57c581a810d 100644 --- a/benches/distributions/exponential.rs +++ b/benches/distributions/exponential.rs @@ -2,12 +2,12 @@ use std::mem::size_of; use test::Bencher; use rand; use rand::distributions::exponential::Exp; -use rand::distributions::Sample; +use rand::distributions::Distribution; #[bench] fn rand_exp(b: &mut Bencher) { let mut rng = rand::weak_rng(); - let mut exp = Exp::new(2.71828 * 3.14159); + let exp = Exp::new(2.71828 * 3.14159); b.iter(|| { for _ in 0..::RAND_BENCH_N { diff --git a/benches/distributions/gamma.rs b/benches/distributions/gamma.rs index bf3fd367a9b..47c501f35fd 100644 --- a/benches/distributions/gamma.rs +++ b/benches/distributions/gamma.rs @@ -1,7 +1,7 @@ use std::mem::size_of; use test::Bencher; use rand; -use rand::distributions::IndependentSample; +use rand::distributions::Distribution; use rand::distributions::gamma::Gamma; #[bench] @@ -11,7 +11,7 @@ fn bench_gamma_large_shape(b: &mut Bencher) { b.iter(|| { for _ in 0..::RAND_BENCH_N { - gamma.ind_sample(&mut rng); + gamma.sample(&mut rng); } }); b.bytes = size_of::() as u64 * ::RAND_BENCH_N; @@ -24,7 +24,7 @@ fn bench_gamma_small_shape(b: &mut Bencher) { b.iter(|| { for _ in 0..::RAND_BENCH_N { - gamma.ind_sample(&mut rng); + gamma.sample(&mut rng); } }); b.bytes = size_of::() as u64 * ::RAND_BENCH_N; diff --git a/benches/distributions/normal.rs b/benches/distributions/normal.rs index 1c858b19b39..509f954857e 100644 --- a/benches/distributions/normal.rs +++ b/benches/distributions/normal.rs @@ -1,13 +1,13 @@ use std::mem::size_of; use test::Bencher; use rand; -use rand::distributions::Sample; +use rand::distributions::Distribution; use rand::distributions::normal::Normal; #[bench] fn rand_normal(b: &mut Bencher) { let mut rng = rand::weak_rng(); - let mut normal = Normal::new(-2.71828, 3.14159); + let normal = Normal::new(-2.71828, 3.14159); b.iter(|| { for _ in 0..::RAND_BENCH_N { diff --git a/src/distributions/exponential.rs b/src/distributions/exponential.rs index d3e48fc84f8..cbc0a4d8019 100644 --- a/src/distributions/exponential.rs +++ b/src/distributions/exponential.rs @@ -11,7 +11,7 @@ //! The exponential distribution. use {Rng, Rand}; -use distributions::{ziggurat, ziggurat_tables, Sample, IndependentSample}; +use distributions::{ziggurat, ziggurat_tables, Distribution}; /// A wrapper around an `f64` to generate Exp(1) random numbers. /// @@ -65,10 +65,10 @@ impl Rand for Exp1 { /// # Example /// /// ```rust -/// use rand::distributions::{Exp, IndependentSample}; +/// use rand::distributions::{Exp, Distribution}; /// /// let exp = Exp::new(2.0); -/// let v = exp.ind_sample(&mut rand::thread_rng()); +/// let v = exp.sample(&mut rand::thread_rng()); /// println!("{} is from a Exp(2) distribution", v); /// ``` #[derive(Clone, Copy, Debug)] @@ -87,11 +87,8 @@ impl Exp { } } -impl Sample for Exp { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for Exp { - fn ind_sample(&self, rng: &mut R) -> f64 { +impl Distribution for Exp { + fn sample(&self, rng: &mut R) -> f64 { let Exp1(n) = rng.gen::(); n * self.lambda_inverse } @@ -99,16 +96,16 @@ impl IndependentSample for Exp { #[cfg(test)] mod test { - use distributions::{Sample, IndependentSample}; + use distributions::Distribution; use super::Exp; #[test] fn test_exp() { - let mut exp = Exp::new(10.0); + let exp = Exp::new(10.0); let mut rng = ::test::rng(221); for _ in 0..1000 { assert!(exp.sample(&mut rng) >= 0.0); - assert!(exp.ind_sample(&mut rng) >= 0.0); + assert!(exp.sample(&mut rng) >= 0.0); } } #[test] diff --git a/src/distributions/gamma.rs b/src/distributions/gamma.rs index 9c4ac45aa44..d6e6cca41a4 100644 --- a/src/distributions/gamma.rs +++ b/src/distributions/gamma.rs @@ -17,7 +17,7 @@ use self::ChiSquaredRepr::*; use {Rng, Open01}; use super::normal::StandardNormal; -use super::{IndependentSample, Sample, Exp}; +use super::{Distribution, Exp}; /// The Gamma distribution `Gamma(shape, scale)` distribution. /// @@ -38,10 +38,10 @@ use super::{IndependentSample, Sample, Exp}; /// # Example /// /// ```rust -/// use rand::distributions::{IndependentSample, Gamma}; +/// use rand::distributions::{Distribution, Gamma}; /// /// let gamma = Gamma::new(2.0, 5.0); -/// let v = gamma.ind_sample(&mut rand::thread_rng()); +/// let v = gamma.sample(&mut rand::thread_rng()); /// println!("{} is from a Gamma(2, 5) distribution", v); /// ``` /// @@ -133,34 +133,24 @@ impl GammaLargeShape { } } -impl Sample for Gamma { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl Sample for GammaSmallShape { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl Sample for GammaLargeShape { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} - -impl IndependentSample for Gamma { - fn ind_sample(&self, rng: &mut R) -> f64 { +impl Distribution for Gamma { + fn sample(&self, rng: &mut R) -> f64 { match self.repr { - Small(ref g) => g.ind_sample(rng), - One(ref g) => g.ind_sample(rng), - Large(ref g) => g.ind_sample(rng), + Small(ref g) => g.sample(rng), + One(ref g) => g.sample(rng), + Large(ref g) => g.sample(rng), } } } -impl IndependentSample for GammaSmallShape { - fn ind_sample(&self, rng: &mut R) -> f64 { +impl Distribution for GammaSmallShape { + fn sample(&self, rng: &mut R) -> f64 { let Open01(u) = rng.gen::>(); - self.large_shape.ind_sample(rng) * u.powf(self.inv_shape) + self.large_shape.sample(rng) * u.powf(self.inv_shape) } } -impl IndependentSample for GammaLargeShape { - fn ind_sample(&self, rng: &mut R) -> f64 { +impl Distribution for GammaLargeShape { + fn sample(&self, rng: &mut R) -> f64 { loop { let StandardNormal(x) = rng.gen::(); let v_cbrt = 1.0 + self.c * x; @@ -191,10 +181,10 @@ impl IndependentSample for GammaLargeShape { /// # Example /// /// ```rust -/// use rand::distributions::{ChiSquared, IndependentSample}; +/// use rand::distributions::{ChiSquared, Distribution}; /// /// let chi = ChiSquared::new(11.0); -/// let v = chi.ind_sample(&mut rand::thread_rng()); +/// let v = chi.sample(&mut rand::thread_rng()); /// println!("{} is from a χ²(11) distribution", v) /// ``` #[derive(Clone, Copy, Debug)] @@ -224,18 +214,15 @@ impl ChiSquared { ChiSquared { repr: repr } } } -impl Sample for ChiSquared { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for ChiSquared { - fn ind_sample(&self, rng: &mut R) -> f64 { +impl Distribution for ChiSquared { + fn sample(&self, rng: &mut R) -> f64 { match self.repr { DoFExactlyOne => { // k == 1 => N(0,1)^2 let StandardNormal(norm) = rng.gen::(); norm * norm } - DoFAnythingElse(ref g) => g.ind_sample(rng) + DoFAnythingElse(ref g) => g.sample(rng) } } } @@ -249,10 +236,10 @@ impl IndependentSample for ChiSquared { /// # Example /// /// ```rust -/// use rand::distributions::{FisherF, IndependentSample}; +/// use rand::distributions::{FisherF, Distribution}; /// /// let f = FisherF::new(2.0, 32.0); -/// let v = f.ind_sample(&mut rand::thread_rng()); +/// let v = f.sample(&mut rand::thread_rng()); /// println!("{} is from an F(2, 32) distribution", v) /// ``` #[derive(Clone, Copy, Debug)] @@ -278,12 +265,9 @@ impl FisherF { } } } -impl Sample for FisherF { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for FisherF { - fn ind_sample(&self, rng: &mut R) -> f64 { - self.numer.ind_sample(rng) / self.denom.ind_sample(rng) * self.dof_ratio +impl Distribution for FisherF { + fn sample(&self, rng: &mut R) -> f64 { + self.numer.sample(rng) / self.denom.sample(rng) * self.dof_ratio } } @@ -293,10 +277,10 @@ impl IndependentSample for FisherF { /// # Example /// /// ```rust -/// use rand::distributions::{StudentT, IndependentSample}; +/// use rand::distributions::{StudentT, Distribution}; /// /// let t = StudentT::new(11.0); -/// let v = t.ind_sample(&mut rand::thread_rng()); +/// let v = t.sample(&mut rand::thread_rng()); /// println!("{} is from a t(11) distribution", v) /// ``` #[derive(Clone, Copy, Debug)] @@ -316,46 +300,43 @@ impl StudentT { } } } -impl Sample for StudentT { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for StudentT { - fn ind_sample(&self, rng: &mut R) -> f64 { +impl Distribution for StudentT { + fn sample(&self, rng: &mut R) -> f64 { let StandardNormal(norm) = rng.gen::(); - norm * (self.dof / self.chi.ind_sample(rng)).sqrt() + norm * (self.dof / self.chi.sample(rng)).sqrt() } } #[cfg(test)] mod test { - use distributions::{Sample, IndependentSample}; + use distributions::Distribution; use super::{ChiSquared, StudentT, FisherF}; #[test] fn test_chi_squared_one() { - let mut chi = ChiSquared::new(1.0); + let chi = ChiSquared::new(1.0); let mut rng = ::test::rng(201); for _ in 0..1000 { chi.sample(&mut rng); - chi.ind_sample(&mut rng); + chi.sample(&mut rng); } } #[test] fn test_chi_squared_small() { - let mut chi = ChiSquared::new(0.5); + let chi = ChiSquared::new(0.5); let mut rng = ::test::rng(202); for _ in 0..1000 { chi.sample(&mut rng); - chi.ind_sample(&mut rng); + chi.sample(&mut rng); } } #[test] fn test_chi_squared_large() { - let mut chi = ChiSquared::new(30.0); + let chi = ChiSquared::new(30.0); let mut rng = ::test::rng(203); for _ in 0..1000 { chi.sample(&mut rng); - chi.ind_sample(&mut rng); + chi.sample(&mut rng); } } #[test] @@ -366,21 +347,21 @@ mod test { #[test] fn test_f() { - let mut f = FisherF::new(2.0, 32.0); + let f = FisherF::new(2.0, 32.0); let mut rng = ::test::rng(204); for _ in 0..1000 { f.sample(&mut rng); - f.ind_sample(&mut rng); + f.sample(&mut rng); } } #[test] fn test_t() { - let mut t = StudentT::new(11.0); + let t = StudentT::new(11.0); let mut rng = ::test::rng(205); for _ in 0..1000 { t.sample(&mut rng); - t.ind_sample(&mut rng); + t.sample(&mut rng); } } } diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 9eb2e464baf..089c8017d43 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2013-2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // https://rust-lang.org/COPYRIGHT. // @@ -10,12 +10,10 @@ //! Sampling from random distributions. //! -//! This is a generalization of `Rand` to allow parameters to control the -//! exact properties of the generated values, e.g. the mean and standard -//! deviation of a normal distribution. The `Sample` trait is the most -//! general, and allows for generating values that change some state -//! internally. The `IndependentSample` trait is for generating values -//! that do not need to record state. +//! A distribution may have internal state describing the distribution of +//! generated values; for example `Range` needs to know its upper and lower +//! bounds. Distributions use the `Distribution` trait to yield values: call +//! `distr.sample(&mut rng)` to get a random variable. use core::marker; @@ -41,6 +39,7 @@ pub mod exponential; mod ziggurat_tables; /// Types that can be used to create a random instance of `Support`. +#[deprecated(since="0.5.0", note="use Distribution instead")] pub trait Sample { /// Generate a random value of `Support`, using `rng` as the /// source of randomness. @@ -55,13 +54,95 @@ pub trait Sample { // FIXME maybe having this separate is overkill (the only reason is to // take &self rather than &mut self)? or maybe this should be the // trait called `Sample` and the other should be `DependentSample`. +#[allow(deprecated)] +#[deprecated(since="0.5.0", note="use Distribution instead")] pub trait IndependentSample: Sample { /// Generate a random value. fn ind_sample(&self, &mut R) -> Support; } +#[allow(deprecated)] +mod impls { + use {Rng, Rand}; + use distributions::{Distribution, Sample, IndependentSample, + RandSample, WeightedChoice}; + #[cfg(feature="std")] + use distributions::exponential::Exp; + #[cfg(feature="std")] + use distributions::gamma::{Gamma, ChiSquared, FisherF, StudentT}; + #[cfg(feature="std")] + use distributions::normal::{Normal, LogNormal}; + use distributions::range::{Range, SampleRange}; + + impl Sample for RandSample { + fn sample(&mut self, rng: &mut R) -> Sup { + Distribution::sample(self, rng) + } + } + impl IndependentSample for RandSample { + fn ind_sample(&self, rng: &mut R) -> Sup { + Distribution::sample(self, rng) + } + } + + impl<'a, T: Clone> Sample for WeightedChoice<'a, T> { + fn sample(&mut self, rng: &mut R) -> T { + Distribution::sample(self, rng) + } + } + impl<'a, T: Clone> IndependentSample for WeightedChoice<'a, T> { + fn ind_sample(&self, rng: &mut R) -> T { + Distribution::sample(self, rng) + } + } + + impl Sample for Range { + fn sample(&mut self, rng: &mut R) -> Sup { + Distribution::sample(self, rng) + } + } + impl IndependentSample for Range { + fn ind_sample(&self, rng: &mut R) -> Sup { + Distribution::sample(self, rng) + } + } + + #[cfg(feature="std")] + macro_rules! impl_f64 { + ($($name: ident), *) => { + $( + impl Sample for $name { + fn sample(&mut self, rng: &mut R) -> f64 { + Distribution::sample(self, rng) + } + } + impl IndependentSample for $name { + fn ind_sample(&self, rng: &mut R) -> f64 { + Distribution::sample(self, rng) + } + } + )* + } + } + #[cfg(feature="std")] + impl_f64!(Exp, Gamma, ChiSquared, FisherF, StudentT, Normal, LogNormal); +} + +/// Types (distributions) that can be used to create a random instance of `T`. +pub trait Distribution { + /// Generate a random value of `T`, using `rng` as the + /// source of randomness. + fn sample(&self, rng: &mut R) -> T; +} + +impl<'a, T, D: Distribution> Distribution for &'a D { + fn sample(&self, rng: &mut R) -> T { + (*self).sample(rng) + } +} + /// A wrapper for generating types that implement `Rand` via the -/// `Sample` & `IndependentSample` traits. +/// `Distribution` trait. #[derive(Debug)] pub struct RandSample { _marker: marker::PhantomData Sup>, @@ -72,12 +153,8 @@ impl Clone for RandSample { fn clone(&self) -> Self { *self } } -impl Sample for RandSample { - fn sample(&mut self, rng: &mut R) -> Sup { self.ind_sample(rng) } -} - -impl IndependentSample for RandSample { - fn ind_sample(&self, rng: &mut R) -> Sup { +impl Distribution for RandSample { + fn sample(&self, rng: &mut R) -> Sup { rng.gen() } } @@ -102,15 +179,14 @@ pub struct Weighted { /// Each item has an associated weight that influences how likely it /// is to be chosen: higher weight is more likely. /// -/// The `Clone` restriction is a limitation of the `Sample` and -/// `IndependentSample` traits. Note that `&T` is (cheaply) `Clone` for -/// all `T`, as is `u32`, so one can store references or indices into -/// another vector. +/// The `Clone` restriction is a limitation of the `Distribution` trait. +/// Note that `&T` is (cheaply) `Clone` for all `T`, as is `u32`, so one can +/// store references or indices into another vector. /// /// # Example /// /// ```rust -/// use rand::distributions::{Weighted, WeightedChoice, IndependentSample}; +/// use rand::distributions::{Weighted, WeightedChoice, Distribution}; /// /// let mut items = vec!(Weighted { weight: 2, item: 'a' }, /// Weighted { weight: 4, item: 'b' }, @@ -119,7 +195,7 @@ pub struct Weighted { /// let mut rng = rand::thread_rng(); /// for _ in 0..16 { /// // on average prints 'a' 4 times, 'b' 8 and 'c' twice. -/// println!("{}", wc.ind_sample(&mut rng)); +/// println!("{}", wc.sample(&mut rng)); /// } /// ``` #[derive(Debug)] @@ -165,18 +241,14 @@ impl<'a, T: Clone> WeightedChoice<'a, T> { } } -impl<'a, T: Clone> Sample for WeightedChoice<'a, T> { - fn sample(&mut self, rng: &mut R) -> T { self.ind_sample(rng) } -} - -impl<'a, T: Clone> IndependentSample for WeightedChoice<'a, T> { - fn ind_sample(&self, rng: &mut R) -> T { +impl<'a, T: Clone> Distribution for WeightedChoice<'a, T> { + fn sample(&self, rng: &mut R) -> T { // we want to find the first element that has cumulative // weight > sample_weight, which we do by binary since the // cumulative weights of self.items are sorted. // choose a weight in [0, total_weight) - let sample_weight = self.weight_range.ind_sample(rng); + let sample_weight = self.weight_range.sample(rng); // short circuit when it's the first item if sample_weight < self.items[0].weight { @@ -281,7 +353,7 @@ fn ziggurat( mod tests { use {Rng, Rand}; use impls; - use super::{RandSample, WeightedChoice, Weighted, Sample, IndependentSample}; + use super::{RandSample, WeightedChoice, Weighted, Distribution}; #[derive(PartialEq, Debug)] struct ConstRand(usize); @@ -309,10 +381,10 @@ mod tests { #[test] fn test_rand_sample() { - let mut rand_sample = RandSample::::new(); + let rand_sample = RandSample::::new(); assert_eq!(rand_sample.sample(&mut ::test::rng(231)), ConstRand(0)); - assert_eq!(rand_sample.ind_sample(&mut ::test::rng(232)), ConstRand(0)); + assert_eq!(rand_sample.sample(&mut ::test::rng(232)), ConstRand(0)); } #[test] @@ -331,7 +403,7 @@ mod tests { let mut rng = CountingRng { i: 0 }; for &val in expected.iter() { - assert_eq!(wc.ind_sample(&mut rng), val) + assert_eq!(wc.sample(&mut rng), val) } }} } @@ -411,4 +483,29 @@ mod tests { Weighted { weight: x, item: 2 }, Weighted { weight: 1, item: 3 }]); } + + #[test] #[allow(deprecated)] + fn test_backwards_compat_sample() { + use distributions::{Sample, IndependentSample}; + + struct Constant { val: T } + impl Sample for Constant { + fn sample(&mut self, _: &mut R) -> T { self.val } + } + impl IndependentSample for Constant { + fn ind_sample(&self, _: &mut R) -> T { self.val } + } + + let mut sampler = Constant{ val: 293 }; + assert_eq!(sampler.sample(&mut ::test::rng(233)), 293); + assert_eq!(sampler.ind_sample(&mut ::test::rng(234)), 293); + } + + #[cfg(feature="std")] + #[test] #[allow(deprecated)] + fn test_backwards_compat_exp() { + use distributions::{IndependentSample, Exp}; + let sampler = Exp::new(1.0); + sampler.ind_sample(&mut ::test::rng(235)); + } } diff --git a/src/distributions/normal.rs b/src/distributions/normal.rs index 71d498b573a..336a5fa9e9f 100644 --- a/src/distributions/normal.rs +++ b/src/distributions/normal.rs @@ -11,7 +11,7 @@ //! The normal and derived distributions. use {Rng, Rand, Open01}; -use distributions::{ziggurat, ziggurat_tables, Sample, IndependentSample}; +use distributions::{ziggurat, ziggurat_tables, Distribution}; /// A wrapper around an `f64` to generate N(0, 1) random numbers /// (a.k.a. a standard normal, or Gaussian). @@ -81,11 +81,11 @@ impl Rand for StandardNormal { /// # Example /// /// ```rust -/// use rand::distributions::{Normal, IndependentSample}; +/// use rand::distributions::{Normal, Distribution}; /// /// // mean 2, standard deviation 3 /// let normal = Normal::new(2.0, 3.0); -/// let v = normal.ind_sample(&mut rand::thread_rng()); +/// let v = normal.sample(&mut rand::thread_rng()); /// println!("{} is from a N(2, 9) distribution", v) /// ``` #[derive(Clone, Copy, Debug)] @@ -110,11 +110,8 @@ impl Normal { } } } -impl Sample for Normal { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for Normal { - fn ind_sample(&self, rng: &mut R) -> f64 { +impl Distribution for Normal { + fn sample(&self, rng: &mut R) -> f64 { let StandardNormal(n) = rng.gen::(); self.mean + self.std_dev * n } @@ -129,11 +126,11 @@ impl IndependentSample for Normal { /// # Example /// /// ```rust -/// use rand::distributions::{LogNormal, IndependentSample}; +/// use rand::distributions::{LogNormal, Distribution}; /// /// // mean 2, standard deviation 3 /// let log_normal = LogNormal::new(2.0, 3.0); -/// let v = log_normal.ind_sample(&mut rand::thread_rng()); +/// let v = log_normal.sample(&mut rand::thread_rng()); /// println!("{} is from an ln N(2, 9) distribution", v) /// ``` #[derive(Clone, Copy, Debug)] @@ -154,27 +151,24 @@ impl LogNormal { LogNormal { norm: Normal::new(mean, std_dev) } } } -impl Sample for LogNormal { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for LogNormal { - fn ind_sample(&self, rng: &mut R) -> f64 { - self.norm.ind_sample(rng).exp() +impl Distribution for LogNormal { + fn sample(&self, rng: &mut R) -> f64 { + self.norm.sample(rng).exp() } } #[cfg(test)] mod tests { - use distributions::{Sample, IndependentSample}; + use distributions::Distribution; use super::{Normal, LogNormal}; #[test] fn test_normal() { - let mut norm = Normal::new(10.0, 10.0); + let norm = Normal::new(10.0, 10.0); let mut rng = ::test::rng(210); for _ in 0..1000 { norm.sample(&mut rng); - norm.ind_sample(&mut rng); + norm.sample(&mut rng); } } #[test] @@ -186,11 +180,11 @@ mod tests { #[test] fn test_log_normal() { - let mut lnorm = LogNormal::new(10.0, 10.0); + let lnorm = LogNormal::new(10.0, 10.0); let mut rng = ::test::rng(211); for _ in 0..1000 { lnorm.sample(&mut rng); - lnorm.ind_sample(&mut rng); + lnorm.sample(&mut rng); } } #[test] diff --git a/src/distributions/range.rs b/src/distributions/range.rs index 051fb7a85ab..36cd7ecd86f 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -15,7 +15,7 @@ use core::num::Wrapping as w; use Rng; -use distributions::{Sample, IndependentSample}; +use distributions::Distribution; /// Sample values uniformly between two bounds. /// @@ -34,14 +34,14 @@ use distributions::{Sample, IndependentSample}; /// # Example /// /// ```rust -/// use rand::distributions::{IndependentSample, Range}; +/// use rand::distributions::{Distribution, Range}; /// /// fn main() { /// let between = Range::new(10, 10000); /// let mut rng = rand::thread_rng(); /// let mut sum = 0; /// for _ in 0..1000 { -/// sum += between.ind_sample(&mut rng); +/// sum += between.sample(&mut rng); /// } /// println!("{}", sum); /// } @@ -62,12 +62,8 @@ impl Range { } } -impl Sample for Range { - #[inline] - fn sample(&mut self, rng: &mut R) -> Sup { self.ind_sample(rng) } -} -impl IndependentSample for Range { - fn ind_sample(&self, rng: &mut R) -> Sup { +impl Distribution for Range { + fn sample(&self, rng: &mut R) -> Sup { SampleRange::sample_range(self, rng) } } @@ -169,7 +165,7 @@ float_impl! { f64 } #[cfg(test)] mod tests { - use distributions::{Sample, IndependentSample}; + use distributions::Distribution; use super::Range as Range; #[should_panic] @@ -193,11 +189,11 @@ mod tests { (10, 127), (::core::$ty::MIN, ::core::$ty::MAX)]; for &(low, high) in v.iter() { - let mut sampler: Range<$ty> = Range::new(low, high); + let sampler: Range<$ty> = Range::new(low, high); for _ in 0..1000 { let v = sampler.sample(&mut rng); assert!(low <= v && v < high); - let v = sampler.ind_sample(&mut rng); + let v = sampler.sample(&mut rng); assert!(low <= v && v < high); } } @@ -223,11 +219,11 @@ mod tests { (1e-35, 1e-25), (-1e35, 1e35)]; for &(low, high) in v.iter() { - let mut sampler: Range<$ty> = Range::new(low, high); + let sampler: Range<$ty> = Range::new(low, high); for _ in 0..1000 { let v = sampler.sample(&mut rng); assert!(low <= v && v < high); - let v = sampler.ind_sample(&mut rng); + let v = sampler.sample(&mut rng); assert!(low <= v && v < high); } } diff --git a/src/lib.rs b/src/lib.rs index b848e241469..44ac5774bff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,7 +110,7 @@ //! and multiply this fraction by 4. //! //! ``` -//! use rand::distributions::{IndependentSample, Range}; +//! use rand::distributions::{Distribution, Range}; //! //! fn main() { //! let between = Range::new(-1f64, 1.); @@ -120,8 +120,8 @@ //! let mut in_circle = 0; //! //! for _ in 0..total { -//! let a = between.ind_sample(&mut rng); -//! let b = between.ind_sample(&mut rng); +//! let a = between.sample(&mut rng); +//! let b = between.sample(&mut rng); //! if a*a + b*b <= 1. { //! in_circle += 1; //! } @@ -153,7 +153,7 @@ //! //! ``` //! use rand::Rng; -//! use rand::distributions::{IndependentSample, Range}; +//! use rand::distributions::{Distribution, Range}; //! //! struct SimulationResult { //! win: bool, @@ -163,10 +163,10 @@ //! // Run a single simulation of the Monty Hall problem. //! fn simulate(random_door: &Range, rng: &mut R) //! -> SimulationResult { -//! let car = random_door.ind_sample(rng); +//! let car = random_door.sample(rng); //! //! // This is our initial choice -//! let mut choice = random_door.ind_sample(rng); +//! let mut choice = random_door.sample(rng); //! //! // The game host opens a door //! let open = game_host_open(car, choice, rng); @@ -286,7 +286,7 @@ use prng::IsaacRng as IsaacWordRng; #[cfg(target_pointer_width = "64")] use prng::Isaac64Rng as IsaacWordRng; -use distributions::{Range, IndependentSample}; +use distributions::{Distribution, Range}; use distributions::range::SampleRange; #[cfg(feature="std")] use reseeding::ReseedingRng; @@ -544,7 +544,7 @@ pub trait Rng { /// ``` fn gen_range(&mut self, low: T, high: T) -> T where Self: Sized { assert!(low < high, "Rng.gen_range called with low >= high"); - Range::new(low, high).ind_sample(self) + Range::new(low, high).sample(self) } /// Return a bool with a 1 in n chance of true From f6be07edfb6ccd663e2cd8142a48d2cd42713f41 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 22 Dec 2017 13:51:10 +0000 Subject: [PATCH 02/14] Add SampleRng::sample function --- src/lib.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 44ac5774bff..2dac320a073 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -483,6 +483,21 @@ pub trait Rng { Ok(self.fill_bytes(dest)) } + /// Sample a new value, using the given distribution. + /// + /// ### Example + /// + /// ```rust + /// use rand::{thread_rng, Rng}; + /// use rand::distributions::Range; + /// + /// let mut rng = thread_rng(); + /// let x: i32 = rng.sample(Range::new(10, 15)); + /// ``` + fn sample>(&mut self, distr: D) -> T where Self: Sized { + distr.sample(self) + } + /// Return a random value of a `Rand` type. /// /// # Example From 4b71d258fa389093ddd71c330b5404cc8fc8b961 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 8 Feb 2018 17:58:17 +0000 Subject: [PATCH 03/14] Remove the RandSample wrapper type --- src/distributions/mod.rs | 63 ++++------------------------------------ 1 file changed, 5 insertions(+), 58 deletions(-) diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 089c8017d43..f82de266810 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -15,9 +15,7 @@ //! bounds. Distributions use the `Distribution` trait to yield values: call //! `distr.sample(&mut rng)` to get a random variable. -use core::marker; - -use {Rng, Rand}; +use Rng; pub use self::range::Range; #[cfg(feature="std")] @@ -63,9 +61,9 @@ pub trait IndependentSample: Sample { #[allow(deprecated)] mod impls { - use {Rng, Rand}; + use Rng; use distributions::{Distribution, Sample, IndependentSample, - RandSample, WeightedChoice}; + WeightedChoice}; #[cfg(feature="std")] use distributions::exponential::Exp; #[cfg(feature="std")] @@ -74,17 +72,6 @@ mod impls { use distributions::normal::{Normal, LogNormal}; use distributions::range::{Range, SampleRange}; - impl Sample for RandSample { - fn sample(&mut self, rng: &mut R) -> Sup { - Distribution::sample(self, rng) - } - } - impl IndependentSample for RandSample { - fn ind_sample(&self, rng: &mut R) -> Sup { - Distribution::sample(self, rng) - } - } - impl<'a, T: Clone> Sample for WeightedChoice<'a, T> { fn sample(&mut self, rng: &mut R) -> T { Distribution::sample(self, rng) @@ -141,30 +128,6 @@ impl<'a, T, D: Distribution> Distribution for &'a D { } } -/// A wrapper for generating types that implement `Rand` via the -/// `Distribution` trait. -#[derive(Debug)] -pub struct RandSample { - _marker: marker::PhantomData Sup>, -} - -impl Copy for RandSample {} -impl Clone for RandSample { - fn clone(&self) -> Self { *self } -} - -impl Distribution for RandSample { - fn sample(&self, rng: &mut R) -> Sup { - rng.gen() - } -} - -impl RandSample { - pub fn new() -> RandSample { - RandSample { _marker: marker::PhantomData } - } -} - /// A value with a particular weight for use with `WeightedChoice`. #[derive(Copy, Clone, Debug)] pub struct Weighted { @@ -351,17 +314,9 @@ fn ziggurat( #[cfg(test)] mod tests { - use {Rng, Rand}; + use Rng; use impls; - use super::{RandSample, WeightedChoice, Weighted, Distribution}; - - #[derive(PartialEq, Debug)] - struct ConstRand(usize); - impl Rand for ConstRand { - fn rand(_: &mut R) -> ConstRand { - ConstRand(0) - } - } + use super::{WeightedChoice, Weighted, Distribution}; // 0, 1, 2, 3, ... struct CountingRng { i: u32 } @@ -379,14 +334,6 @@ mod tests { } } - #[test] - fn test_rand_sample() { - let rand_sample = RandSample::::new(); - - assert_eq!(rand_sample.sample(&mut ::test::rng(231)), ConstRand(0)); - assert_eq!(rand_sample.sample(&mut ::test::rng(232)), ConstRand(0)); - } - #[test] fn test_weighted_choice() { // this makes assumptions about the internal implementation of From b5514655a4d814b1b66d4f016a052817110e3bcb Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 10 Feb 2018 11:49:41 +0000 Subject: [PATCH 04/14] Remove impl of Rand SeedableRng impls Replaces rng.gen() to seed new RNGs with from_rng(&mut rng) --- benches/generators.rs | 8 ++++---- src/lib.rs | 3 ++- src/rand_impls.rs | 8 +------- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/benches/generators.rs b/benches/generators.rs index 044d7b24e89..0c3d483471d 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -9,7 +9,7 @@ const BYTES_LEN: usize = 1024; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{Rng, NewRng, StdRng, OsRng, JitterRng}; +use rand::{Rng, SeedableRng, NewRng, StdRng, OsRng, JitterRng}; use rand::{XorShiftRng, Hc128Rng, IsaacRng, Isaac64Rng, ChaChaRng}; macro_rules! gen_bytes { @@ -40,7 +40,7 @@ macro_rules! gen_uint { ($fnn:ident, $ty:ty, $gen:ident) => { #[bench] fn $fnn(b: &mut Bencher) { - let mut rng: $gen = OsRng::new().unwrap().gen(); + let mut rng = $gen::new().unwrap(); b.iter(|| { for _ in 0..RAND_BENCH_N { black_box(rng.gen::<$ty>()); @@ -95,9 +95,9 @@ macro_rules! init_gen { ($fnn:ident, $gen:ident) => { #[bench] fn $fnn(b: &mut Bencher) { - let mut rng: XorShiftRng = OsRng::new().unwrap().gen(); + let mut rng = XorShiftRng::new().unwrap(); b.iter(|| { - let r2: $gen = rng.gen(); + let r2 = $gen::from_rng(&mut rng).unwrap(); black_box(r2); }); } diff --git a/src/lib.rs b/src/lib.rs index 2dac320a073..1f5fc3e2461 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -949,7 +949,8 @@ impl SeedableRng for StdRng { /// This will seed the generator with randomness from thread_rng. #[cfg(feature="std")] pub fn weak_rng() -> XorShiftRng { - thread_rng().gen() + XorShiftRng::from_rng(&mut thread_rng()).unwrap_or_else(|err| + panic!("weak_rng failed: {:?}", err)) } diff --git a/src/rand_impls.rs b/src/rand_impls.rs index 9b84358c47f..7d457f2aed9 100644 --- a/src/rand_impls.rs +++ b/src/rand_impls.rs @@ -12,7 +12,7 @@ use core::{char, mem}; -use {Rand, Rng, SeedableRng}; +use {Rand, Rng}; impl Rand for isize { #[inline] @@ -246,12 +246,6 @@ impl Rand for Option { } } -impl Rand for T { - fn rand(rng: &mut R) -> Self { - Self::from_rng(rng).unwrap() - } -} - #[cfg(test)] mod tests { use impls; From 9865266044d901d439c1cdd35fb3bf5a05753e69 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 10 Feb 2018 11:34:54 +0000 Subject: [PATCH 05/14] Add the Uniform distribution --- src/distributions/mod.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index f82de266810..76170e5aec5 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -15,7 +15,7 @@ //! bounds. Distributions use the `Distribution` trait to yield values: call //! `distr.sample(&mut rng)` to get a random variable. -use Rng; +use {Rng, Rand}; pub use self::range::Range; #[cfg(feature="std")] @@ -128,6 +128,21 @@ impl<'a, T, D: Distribution> Distribution for &'a D { } } +/// A generic random value distribution. Generates values for various types +/// with numerically uniform distribution. +/// +/// TODO: document implementations +/// TODO: add example +#[derive(Debug)] +pub struct Uniform; + +impl Rand for T where Uniform: Distribution { + fn rand(rng: &mut R) -> Self { + Uniform.sample(rng) + } +} + + /// A value with a particular weight for use with `WeightedChoice`. #[derive(Copy, Clone, Debug)] pub struct Weighted { From 9eb4c2f336bf070b3f078821703027ac07c71a76 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 6 Jan 2018 15:10:00 +0000 Subject: [PATCH 06/14] Move float sampling from Rand to distributions Move default sampling to Default distribution Make Open01 and Closed01 distributions instead of wrappers --- src/distributions/float.rs | 199 ++++++++++++++++++++++++++++++++++++ src/distributions/gamma.rs | 10 +- src/distributions/mod.rs | 18 +++- src/distributions/normal.rs | 8 +- src/lib.rs | 55 +--------- src/rand_impls.rs | 152 --------------------------- 6 files changed, 230 insertions(+), 212 deletions(-) create mode 100644 src/distributions/float.rs diff --git a/src/distributions/float.rs b/src/distributions/float.rs new file mode 100644 index 00000000000..03de883ad07 --- /dev/null +++ b/src/distributions/float.rs @@ -0,0 +1,199 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// https://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Basic floating-point number distributions + + +/// A distribution to sample floating point numbers uniformly in the open +/// interval `(0, 1)` (not including either endpoint). +/// +/// See also: [`Closed01`] for the closed `[0, 1]`; [`Uniform`] for the +/// half-open `[0, 1)`. +/// +/// # Example +/// ```rust +/// use rand::{weak_rng, Rng}; +/// use rand::distributions::Open01; +/// +/// let val: f32 = weak_rng().sample(Open01); +/// println!("f32 from (0,1): {}", val); +/// ``` +/// +/// [`Uniform`]: struct.Uniform.html +/// [`Closed01`]: struct.Closed01.html +#[derive(Clone, Copy, Debug)] +pub struct Open01; + +/// A distribution to sample floating point numbers uniformly in the closed +/// interval `[0, 1]` (including both endpoints). +/// +/// See also: [`Open01`] for the open `(0, 1)`; [`Uniform`] for the half-open +/// `[0, 1)`. +/// +/// # Example +/// ```rust +/// use rand::{weak_rng, Rng}; +/// use rand::distributions::Closed01; +/// +/// let val: f32 = weak_rng().sample(Closed01); +/// println!("f32 from [0,1]: {}", val); +/// ``` +/// +/// [`Uniform`]: struct.Uniform.html +/// [`Open01`]: struct.Open01.html +#[derive(Clone, Copy, Debug)] +pub struct Closed01; + + +macro_rules! float_impls { + ($mod_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident) => { + mod $mod_name { + use Rng; + use distributions::{Distribution, Uniform}; + use super::{Open01, Closed01}; + + const SCALE: $ty = (1u64 << $mantissa_bits) as $ty; + + impl Distribution<$ty> for Uniform { + /// Generate a floating point number in the half-open + /// interval `[0,1)`. + /// + /// See `Closed01` for the closed interval `[0,1]`, + /// and `Open01` for the open interval `(0,1)`. + #[inline] + fn sample(&self, rng: &mut R) -> $ty { + rng.$method_name() + } + } + impl Distribution<$ty> for Open01 { + #[inline] + fn sample(&self, rng: &mut R) -> $ty { + // add 0.5 * epsilon, so that smallest number is + // greater than 0, and largest number is still + // less than 1, specifically 1 - 0.5 * epsilon. + rng.$method_name() + 0.5 / SCALE + } + } + impl Distribution<$ty> for Closed01 { + #[inline] + fn sample(&self, rng: &mut R) -> $ty { + // rescale so that 1.0 - epsilon becomes 1.0 + // precisely. + rng.$method_name() * SCALE / (SCALE - 1.0) + } + } + } + } +} +float_impls! { f64_rand_impls, f64, 52, next_f64 } +float_impls! { f32_rand_impls, f32, 23, next_f32 } + + +#[cfg(test)] +mod tests { + use {Rng, impls}; + use distributions::{Open01, Closed01}; + + const EPSILON32: f32 = ::core::f32::EPSILON; + const EPSILON64: f64 = ::core::f64::EPSILON; + + struct ConstantRng(u64); + impl Rng for ConstantRng { + fn next_u32(&mut self) -> u32 { + let ConstantRng(v) = *self; + v as u32 + } + fn next_u64(&mut self) -> u64 { + let ConstantRng(v) = *self; + v + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_u64(self, dest) + } + } + + #[test] + fn floating_point_edge_cases() { + let mut zeros = ConstantRng(0); + assert_eq!(zeros.gen::(), 0.0); + assert_eq!(zeros.gen::(), 0.0); + + let mut one = ConstantRng(1); + assert_eq!(one.gen::(), EPSILON32); + assert_eq!(one.gen::(), EPSILON64); + + let mut max = ConstantRng(!0); + assert_eq!(max.gen::(), 1.0 - EPSILON32); + assert_eq!(max.gen::(), 1.0 - EPSILON64); + } + + #[test] + fn fp_closed_edge_cases() { + let mut zeros = ConstantRng(0); + assert_eq!(zeros.sample::(Closed01), 0.0); + assert_eq!(zeros.sample::(Closed01), 0.0); + + let mut one = ConstantRng(1); + let one32 = one.sample::(Closed01); + let one64 = one.sample::(Closed01); + assert!(EPSILON32 < one32 && one32 < EPSILON32 * 1.01); + assert!(EPSILON64 < one64 && one64 < EPSILON64 * 1.01); + + let mut max = ConstantRng(!0); + assert_eq!(max.sample::(Closed01), 1.0); + assert_eq!(max.sample::(Closed01), 1.0); + } + + #[test] + fn fp_open_edge_cases() { + let mut zeros = ConstantRng(0); + assert_eq!(zeros.sample::(Open01), 0.0 + EPSILON32 / 2.0); + assert_eq!(zeros.sample::(Open01), 0.0 + EPSILON64 / 2.0); + + let mut one = ConstantRng(1); + let one32 = one.sample::(Open01); + let one64 = one.sample::(Open01); + assert!(EPSILON32 < one32 && one32 < EPSILON32 * 2.0); + assert!(EPSILON64 < one64 && one64 < EPSILON64 * 2.0); + + let mut max = ConstantRng(!0); + assert_eq!(max.sample::(Open01), 1.0 - EPSILON32 / 2.0); + assert_eq!(max.sample::(Open01), 1.0 - EPSILON64 / 2.0); + } + + #[test] + fn rand_open() { + // this is unlikely to catch an incorrect implementation that + // generates exactly 0 or 1, but it keeps it sane. + let mut rng = ::test::rng(510); + for _ in 0..1_000 { + // strict inequalities + let f: f64 = rng.sample(Open01); + assert!(0.0 < f && f < 1.0); + + let f: f32 = rng.sample(Open01); + assert!(0.0 < f && f < 1.0); + } + } + + #[test] + fn rand_closed() { + let mut rng = ::test::rng(511); + for _ in 0..1_000 { + // strict inequalities + let f: f64 = rng.sample(Closed01); + assert!(0.0 <= f && f <= 1.0); + + let f: f32 = rng.sample(Closed01); + assert!(0.0 <= f && f <= 1.0); + } + } +} diff --git a/src/distributions/gamma.rs b/src/distributions/gamma.rs index d6e6cca41a4..a76bf2b9a5f 100644 --- a/src/distributions/gamma.rs +++ b/src/distributions/gamma.rs @@ -15,9 +15,9 @@ use self::GammaRepr::*; use self::ChiSquaredRepr::*; -use {Rng, Open01}; -use super::normal::StandardNormal; -use super::{Distribution, Exp}; +use {Rng}; +use distributions::normal::StandardNormal; +use distributions::{Distribution, Exp, Open01}; /// The Gamma distribution `Gamma(shape, scale)` distribution. /// @@ -144,7 +144,7 @@ impl Distribution for Gamma { } impl Distribution for GammaSmallShape { fn sample(&self, rng: &mut R) -> f64 { - let Open01(u) = rng.gen::>(); + let u: f64 = rng.sample(Open01); self.large_shape.sample(rng) * u.powf(self.inv_shape) } @@ -159,7 +159,7 @@ impl Distribution for GammaLargeShape { } let v = v_cbrt * v_cbrt * v_cbrt; - let Open01(u) = rng.gen::>(); + let u: f64 = rng.sample(Open01); let x_sqr = x * x; if u < 1.0 - 0.0331 * x_sqr * x_sqr || diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 76170e5aec5..708c41165c9 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -17,6 +17,7 @@ use {Rng, Rand}; +pub use self::float::{Open01, Closed01}; pub use self::range::Range; #[cfg(feature="std")] pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; @@ -33,6 +34,7 @@ pub mod normal; #[cfg(feature="std")] pub mod exponential; +mod float; #[cfg(feature="std")] mod ziggurat_tables; @@ -131,8 +133,22 @@ impl<'a, T, D: Distribution> Distribution for &'a D { /// A generic random value distribution. Generates values for various types /// with numerically uniform distribution. /// +/// For floating-point numbers, this generates values from the half-open range +/// `[0, 1)` (excluding 1). See also [`Open01`] and [`Closed01`] for alternatives. +/// /// TODO: document implementations -/// TODO: add example +/// +/// # Example +/// ```rust +/// use rand::{weak_rng, Rng}; +/// use rand::distributions::Uniform; +/// +/// let val: f32 = weak_rng().sample(Uniform); +/// println!("f32 from [0,1): {}", val); +/// ``` +/// +/// [`Open01`]: struct.Open01.html +/// [`Closed01`]: struct.Closed01.html #[derive(Debug)] pub struct Uniform; diff --git a/src/distributions/normal.rs b/src/distributions/normal.rs index 336a5fa9e9f..3b24390b41e 100644 --- a/src/distributions/normal.rs +++ b/src/distributions/normal.rs @@ -10,8 +10,8 @@ //! The normal and derived distributions. -use {Rng, Rand, Open01}; -use distributions::{ziggurat, ziggurat_tables, Distribution}; +use {Rng, Rand}; +use distributions::{ziggurat, ziggurat_tables, Distribution, Open01}; /// A wrapper around an `f64` to generate N(0, 1) random numbers /// (a.k.a. a standard normal, or Gaussian). @@ -54,8 +54,8 @@ impl Rand for StandardNormal { let mut y = 0.0f64; while -2.0 * y < x * x { - let Open01(x_) = rng.gen::>(); - let Open01(y_) = rng.gen::>(); + let x_: f64 = rng.sample(Open01); + let y_: f64 = rng.sample(Open01); x = x_.ln() / ziggurat_tables::ZIG_NORM_R; y = y_.ln(); diff --git a/src/lib.rs b/src/lib.rs index 1f5fc3e2461..257cb9295e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -320,6 +320,9 @@ mod prng; /// /// ## Built-in Implementations /// +/// `Rand` is implemented for any type supporting the [`Uniform`] distribution. +/// That includes: floating point numbers. +/// /// This crate implements `Rand` for various primitive types. Assuming the /// provided `Rng` is well-behaved, these implementations generate values with /// the following ranges and distributions: @@ -331,15 +334,6 @@ mod prng; /// `0xD800...0xDFFF` (the surrogate code points). This includes /// unassigned/reserved code points. /// * `bool`: Generates `false` or `true`, each with probability 0.5. -/// * Floating point types (`f32` and `f64`): Uniformly distributed in the -/// half-open range `[0, 1)`. (The [`Open01`], [`Closed01`], [`Exp1`], and -/// [`StandardNormal`] wrapper types produce floating point numbers with -/// alternative ranges or distributions.) -/// -/// [`Open01`]: struct.Open01.html -/// [`Closed01`]: struct.Closed01.html -/// [`Exp1`]: distributions/exponential/struct.Exp1.html -/// [`StandardNormal`]: distributions/normal/struct.StandardNormal.html /// /// The following aggregate types also implement `Rand` as long as their /// component types implement it: @@ -348,6 +342,8 @@ mod prng; /// independently, using its own `Rand` implementation. /// * `Option`: Returns `None` with probability 0.5; otherwise generates a /// random `T` and returns `Some(T)`. +/// +/// [`Uniform`]: distributions/struct.Uniform.html pub trait Rand : Sized { /// Generates a random instance of this type using the specified source of /// randomness. @@ -403,9 +399,6 @@ pub trait Rng { /// random number generator which can generate numbers satisfying /// the requirements directly can overload this for performance. /// It is required that the return value lies in `[0, 1)`. - /// - /// See `Closed01` for the closed interval `[0,1]`, and - /// `Open01` for the open interval `(0,1)`. fn next_f32(&mut self) -> f32 { const UPPER_MASK: u32 = 0x3F800000; const LOWER_MASK: u32 = 0x7FFFFF; @@ -421,9 +414,6 @@ pub trait Rng { /// random number generator which can generate numbers satisfying /// the requirements directly can overload this for performance. /// It is required that the return value lies in `[0, 1)`. - /// - /// See `Closed01` for the closed interval `[0,1]`, and - /// `Open01` for the open interval `(0,1)`. fn next_f64(&mut self) -> f64 { const UPPER_MASK: u64 = 0x3FF0000000000000; const LOWER_MASK: u64 = 0xFFFFFFFFFFFFF; @@ -863,41 +853,6 @@ impl NewRng for R { } } -/// A wrapper for generating floating point numbers uniformly in the -/// open interval `(0,1)` (not including either endpoint). -/// -/// Use `Closed01` for the closed interval `[0,1]`, and the default -/// `Rand` implementation for `f32` and `f64` for the half-open -/// `[0,1)`. -/// -/// # Example -/// ```rust -/// use rand::{random, Open01}; -/// -/// let Open01(val) = random::>(); -/// println!("f32 from (0,1): {}", val); -/// ``` -#[derive(Debug)] -pub struct Open01(pub F); - -/// A wrapper for generating floating point numbers uniformly in the -/// closed interval `[0,1]` (including both endpoints). -/// -/// Use `Open01` for the closed interval `(0,1)`, and the default -/// `Rand` implementation of `f32` and `f64` for the half-open -/// `[0,1)`. -/// -/// # Example -/// -/// ```rust -/// use rand::{random, Closed01}; -/// -/// let Closed01(val) = random::>(); -/// println!("f32 from [0,1]: {}", val); -/// ``` -#[derive(Debug)] -pub struct Closed01(pub F); - /// The standard RNG. This is designed to be efficient on the current /// platform. /// diff --git a/src/rand_impls.rs b/src/rand_impls.rs index 7d457f2aed9..af13c587f8e 100644 --- a/src/rand_impls.rs +++ b/src/rand_impls.rs @@ -109,48 +109,6 @@ impl Rand for u128 { } -macro_rules! float_impls { - ($mod_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident) => { - mod $mod_name { - use {Rand, Rng, Open01, Closed01}; - - // 1.0 / epsilon - const SCALE: $ty = (1u64 << $mantissa_bits) as $ty; - - impl Rand for $ty { - /// Generate a floating point number in the half-open - /// interval `[0,1)`. - /// - /// See `Closed01` for the closed interval `[0,1]`, - /// and `Open01` for the open interval `(0,1)`. - #[inline] - fn rand(rng: &mut R) -> $ty { - rng.$method_name() - } - } - impl Rand for Open01<$ty> { - #[inline] - fn rand(rng: &mut R) -> Open01<$ty> { - // add 0.5 * epsilon, so that smallest number is - // greater than 0, and largest number is still - // less than 1, specifically 1 - 0.5 * epsilon. - Open01(rng.$method_name() + 0.5 / SCALE) - } - } - impl Rand for Closed01<$ty> { - #[inline] - fn rand(rng: &mut R) -> Closed01<$ty> { - // rescale so that 1.0 - epsilon becomes 1.0 - // precisely. - Closed01(rng.$method_name() * SCALE / (SCALE - 1.0)) - } - } - } - } -} -float_impls! { f64_rand_impls, f64, 52, next_f64 } -float_impls! { f32_rand_impls, f32, 23, next_f32 } - impl Rand for char { #[inline] fn rand(rng: &mut R) -> char { @@ -245,113 +203,3 @@ impl Rand for Option { } } } - -#[cfg(test)] -mod tests { - use impls; - use {Rng, Open01, Closed01}; - - const EPSILON32: f32 = ::core::f32::EPSILON; - const EPSILON64: f64 = ::core::f64::EPSILON; - - struct ConstantRng(u64); - impl Rng for ConstantRng { - fn next_u32(&mut self) -> u32 { - let ConstantRng(v) = *self; - v as u32 - } - fn next_u64(&mut self) -> u64 { - let ConstantRng(v) = *self; - v - } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - impls::fill_bytes_via_u64(self, dest) - } - } - - #[test] - fn floating_point_edge_cases() { - let mut zeros = ConstantRng(0); - assert_eq!(zeros.gen::(), 0.0); - assert_eq!(zeros.gen::(), 0.0); - - let mut one = ConstantRng(1); - assert_eq!(one.gen::(), EPSILON32); - assert_eq!(one.gen::(), EPSILON64); - - let mut max = ConstantRng(!0); - assert_eq!(max.gen::(), 1.0 - EPSILON32); - assert_eq!(max.gen::(), 1.0 - EPSILON64); - } - - #[test] - fn fp_closed_edge_cases() { - let mut zeros = ConstantRng(0); - let Closed01(zero32) = zeros.gen::>(); - let Closed01(zero64) = zeros.gen::>(); - assert_eq!(zero32, 0.0); - assert_eq!(zero64, 0.0); - - let mut one = ConstantRng(1); - let Closed01(one32) = one.gen::>(); - let Closed01(one64) = one.gen::>(); - assert!(EPSILON32 < one32 && one32 < EPSILON32 * 1.01); - assert!(EPSILON64 < one64 && one64 < EPSILON64 * 1.01); - - let mut max = ConstantRng(!0); - let Closed01(max32) = max.gen::>(); - let Closed01(max64) = max.gen::>(); - assert_eq!(max32, 1.0); - assert_eq!(max64, 1.0); - } - - #[test] - fn fp_open_edge_cases() { - let mut zeros = ConstantRng(0); - let Open01(zero32) = zeros.gen::>(); - let Open01(zero64) = zeros.gen::>(); - assert_eq!(zero32, 0.0 + EPSILON32 / 2.0); - assert_eq!(zero64, 0.0 + EPSILON64 / 2.0); - - let mut one = ConstantRng(1); - let Open01(one32) = one.gen::>(); - let Open01(one64) = one.gen::>(); - assert!(EPSILON32 < one32 && one32 < EPSILON32 * 2.0); - assert!(EPSILON64 < one64 && one64 < EPSILON64 * 2.0); - - let mut max = ConstantRng(!0); - let Open01(max32) = max.gen::>(); - let Open01(max64) = max.gen::>(); - assert_eq!(max32, 1.0 - EPSILON32 / 2.0); - assert_eq!(max64, 1.0 - EPSILON64 / 2.0); - } - - #[test] - fn rand_open() { - // this is unlikely to catch an incorrect implementation that - // generates exactly 0 or 1, but it keeps it sane. - let mut rng = ::test::rng(501); - for _ in 0..1_000 { - // strict inequalities - let Open01(f) = rng.gen::>(); - assert!(0.0 < f && f < 1.0); - - let Open01(f) = rng.gen::>(); - assert!(0.0 < f && f < 1.0); - } - } - - #[test] - fn rand_closed() { - let mut rng = ::test::rng(502); - for _ in 0..1_000 { - // strict inequalities - let Closed01(f) = rng.gen::>(); - assert!(0.0 <= f && f <= 1.0); - - let Closed01(f) = rng.gen::>(); - assert!(0.0 <= f && f <= 1.0); - } - } -} From 307cbc0f78ea1657eb0085736a3f13a06ba97c89 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 5 Jan 2018 15:44:15 +0000 Subject: [PATCH 07/14] Make Exp1 a distribution; also expose in distributions module --- src/distributions/exponential.rs | 30 ++++++++++++++++-------------- src/distributions/mod.rs | 2 +- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/distributions/exponential.rs b/src/distributions/exponential.rs index cbc0a4d8019..f32efc09f67 100644 --- a/src/distributions/exponential.rs +++ b/src/distributions/exponential.rs @@ -10,10 +10,12 @@ //! The exponential distribution. -use {Rng, Rand}; +use {Rng}; use distributions::{ziggurat, ziggurat_tables, Distribution}; -/// A wrapper around an `f64` to generate Exp(1) random numbers. +/// Samples floating-point numbers according to the exponential distribution, +/// with rate parameter `λ = 1`. This is equivalent to `Exp::new(1.0)` or +/// sampling with `-rng.gen::().ln()`, but faster. /// /// See `Exp` for the general exponential distribution. /// @@ -27,20 +29,20 @@ use distributions::{ziggurat, ziggurat_tables, Distribution}; /// College, Oxford /// /// # Example -/// /// ```rust -/// use rand::distributions::exponential::Exp1; +/// use rand::{weak_rng, Rng}; +/// use rand::distributions::Exp1; /// -/// let Exp1(x) = rand::random(); -/// println!("{}", x); +/// let val: f64 = weak_rng().sample(Exp1); +/// println!("{}", val); /// ``` #[derive(Clone, Copy, Debug)] -pub struct Exp1(pub f64); +pub struct Exp1; // This could be done via `-rng.gen::().ln()` but that is slower. -impl Rand for Exp1 { +impl Distribution for Exp1 { #[inline] - fn rand(rng: &mut R) -> Exp1 { + fn sample(&self, rng: &mut R) -> f64 { #[inline] fn pdf(x: f64) -> f64 { (-x).exp() @@ -50,10 +52,10 @@ impl Rand for Exp1 { ziggurat_tables::ZIG_EXP_R - rng.gen::().ln() } - Exp1(ziggurat(rng, false, - &ziggurat_tables::ZIG_EXP_X, - &ziggurat_tables::ZIG_EXP_F, - pdf, zero_case)) + ziggurat(rng, false, + &ziggurat_tables::ZIG_EXP_X, + &ziggurat_tables::ZIG_EXP_F, + pdf, zero_case) } } @@ -89,7 +91,7 @@ impl Exp { impl Distribution for Exp { fn sample(&self, rng: &mut R) -> f64 { - let Exp1(n) = rng.gen::(); + let n: f64 = rng.sample(Exp1); n * self.lambda_inverse } } diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 708c41165c9..60ad16972e6 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -24,7 +24,7 @@ pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; #[cfg(feature="std")] pub use self::normal::{Normal, LogNormal}; #[cfg(feature="std")] -pub use self::exponential::Exp; +pub use self::exponential::{Exp, Exp1}; pub mod range; #[cfg(feature="std")] From 84aff68132c5257a921af6e2ba488adc7f1e3977 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 5 Jan 2018 15:52:55 +0000 Subject: [PATCH 08/14] Make StandardNormal a distribution; also expose in distributions module --- src/distributions/gamma.rs | 6 +++--- src/distributions/mod.rs | 2 +- src/distributions/normal.rs | 33 ++++++++++++++++----------------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/distributions/gamma.rs b/src/distributions/gamma.rs index a76bf2b9a5f..b418a9a89f4 100644 --- a/src/distributions/gamma.rs +++ b/src/distributions/gamma.rs @@ -152,7 +152,7 @@ impl Distribution for GammaSmallShape { impl Distribution for GammaLargeShape { fn sample(&self, rng: &mut R) -> f64 { loop { - let StandardNormal(x) = rng.gen::(); + let x = rng.sample(StandardNormal); let v_cbrt = 1.0 + self.c * x; if v_cbrt <= 0.0 { // a^3 <= 0 iff a <= 0 continue @@ -219,7 +219,7 @@ impl Distribution for ChiSquared { match self.repr { DoFExactlyOne => { // k == 1 => N(0,1)^2 - let StandardNormal(norm) = rng.gen::(); + let norm = rng.sample(StandardNormal); norm * norm } DoFAnythingElse(ref g) => g.sample(rng) @@ -302,7 +302,7 @@ impl StudentT { } impl Distribution for StudentT { fn sample(&self, rng: &mut R) -> f64 { - let StandardNormal(norm) = rng.gen::(); + let norm = rng.sample(StandardNormal); norm * (self.dof / self.chi.sample(rng)).sqrt() } } diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 60ad16972e6..5810983a227 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -22,7 +22,7 @@ pub use self::range::Range; #[cfg(feature="std")] pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; #[cfg(feature="std")] -pub use self::normal::{Normal, LogNormal}; +pub use self::normal::{Normal, LogNormal, StandardNormal}; #[cfg(feature="std")] pub use self::exponential::{Exp, Exp1}; diff --git a/src/distributions/normal.rs b/src/distributions/normal.rs index 3b24390b41e..9e50c1c7896 100644 --- a/src/distributions/normal.rs +++ b/src/distributions/normal.rs @@ -10,11 +10,12 @@ //! The normal and derived distributions. -use {Rng, Rand}; +use {Rng}; use distributions::{ziggurat, ziggurat_tables, Distribution, Open01}; -/// A wrapper around an `f64` to generate N(0, 1) random numbers -/// (a.k.a. a standard normal, or Gaussian). +/// Samples floating-point numbers according to the normal distribution +/// `N(0, 1)` (a.k.a. a standard normal, or Gaussian). This is equivalent to +/// `Normal::new(0.0, 1.0)` but faster. /// /// See `Normal` for the general normal distribution. /// @@ -26,18 +27,18 @@ use distributions::{ziggurat, ziggurat_tables, Distribution, Open01}; /// College, Oxford /// /// # Example -/// /// ```rust -/// use rand::distributions::normal::StandardNormal; +/// use rand::{weak_rng, Rng}; +/// use rand::distributions::StandardNormal; /// -/// let StandardNormal(x) = rand::random(); -/// println!("{}", x); +/// let val: f64 = weak_rng().sample(StandardNormal); +/// println!("{}", val); /// ``` #[derive(Clone, Copy, Debug)] -pub struct StandardNormal(pub f64); +pub struct StandardNormal; -impl Rand for StandardNormal { - fn rand(rng: &mut R) -> StandardNormal { +impl Distribution for StandardNormal { + fn sample(&self, rng: &mut R) -> f64 { #[inline] fn pdf(x: f64) -> f64 { (-x*x/2.0).exp() @@ -64,12 +65,10 @@ impl Rand for StandardNormal { if u < 0.0 { x - ziggurat_tables::ZIG_NORM_R } else { ziggurat_tables::ZIG_NORM_R - x } } - StandardNormal(ziggurat( - rng, - true, // this is symmetric - &ziggurat_tables::ZIG_NORM_X, - &ziggurat_tables::ZIG_NORM_F, - pdf, zero_case)) + ziggurat(rng, true, // this is symmetric + &ziggurat_tables::ZIG_NORM_X, + &ziggurat_tables::ZIG_NORM_F, + pdf, zero_case) } } @@ -112,7 +111,7 @@ impl Normal { } impl Distribution for Normal { fn sample(&self, rng: &mut R) -> f64 { - let StandardNormal(n) = rng.gen::(); + let n = rng.sample(StandardNormal); self.mean + self.std_dev * n } } From 68d7bf0f788482776faa0ee704f0530523ce9c4e Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 10 Feb 2018 12:14:45 +0000 Subject: [PATCH 09/14] Move integer sampling from Rand to the Uniform distribution --- src/distributions/integer.rs | 110 +++++++++++++++++++++++++++++++++++ src/distributions/mod.rs | 16 ++++- src/lib.rs | 2 - src/rand_impls.rs | 97 +----------------------------- 4 files changed, 126 insertions(+), 99 deletions(-) create mode 100644 src/distributions/integer.rs diff --git a/src/distributions/integer.rs b/src/distributions/integer.rs new file mode 100644 index 00000000000..a947efc91cb --- /dev/null +++ b/src/distributions/integer.rs @@ -0,0 +1,110 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// https://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The implementations of the `Uniform` distribution for integer types. + +use core::mem; + +use {Rng}; +use distributions::{Distribution, Uniform}; + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> isize { + if mem::size_of::() == 4 { + rng.gen::() as isize + } else { + rng.gen::() as isize + } + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> i8 { + rng.next_u32() as i8 + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> i16 { + rng.next_u32() as i16 + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> i32 { + rng.next_u32() as i32 + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> i64 { + rng.next_u64() as i64 + } +} + +#[cfg(feature = "i128_support")] +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> i128 { + rng.gen::() as i128 + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> usize { + if mem::size_of::() == 4 { + rng.gen::() as usize + } else { + rng.gen::() as usize + } + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> u8 { + rng.next_u32() as u8 + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> u16 { + rng.next_u32() as u16 + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> u32 { + rng.next_u32() + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> u64 { + rng.next_u64() + } +} + +#[cfg(feature = "i128_support")] +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> u128 { + ((rng.next_u64() as u128) << 64) | (rng.next_u64() as u128) + } +} diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 5810983a227..7e7114db78e 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -35,6 +35,7 @@ pub mod normal; pub mod exponential; mod float; +mod integer; #[cfg(feature="std")] mod ziggurat_tables; @@ -136,7 +137,18 @@ impl<'a, T, D: Distribution> Distribution for &'a D { /// For floating-point numbers, this generates values from the half-open range /// `[0, 1)` (excluding 1). See also [`Open01`] and [`Closed01`] for alternatives. /// -/// TODO: document implementations +/// ## Built-in Implementations +/// +/// This crate implements the distribution `Uniform` for various primitive +/// types. Assuming the provided `Rng` is well-behaved, these implementations +/// generate values with the following ranges and distributions: +/// +/// * Integers (`i32`, `u32`, `isize`, `usize`, etc.): Uniformly distributed +/// over all values of the type. +/// * Floating point types (`f32` and `f64`): Uniformly distributed in the +/// half-open range `[0, 1)`. (The [`Open01`], [`Closed01`], [`Exp1`], and +/// [`StandardNormal`] distributions produce floating point numbers with +/// alternative ranges or distributions.) /// /// # Example /// ```rust @@ -149,6 +161,8 @@ impl<'a, T, D: Distribution> Distribution for &'a D { /// /// [`Open01`]: struct.Open01.html /// [`Closed01`]: struct.Closed01.html +/// [`Exp1`]: struct.Exp1.html +/// [`StandardNormal`]: struct.StandardNormal.html #[derive(Debug)] pub struct Uniform; diff --git a/src/lib.rs b/src/lib.rs index 257cb9295e9..60fc66e1a86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -327,8 +327,6 @@ mod prng; /// provided `Rng` is well-behaved, these implementations generate values with /// the following ranges and distributions: /// -/// * Integers (`i32`, `u32`, `isize`, `usize`, etc.): Uniformly distributed -/// over all values of the type. /// * `char`: Uniformly distributed over all Unicode scalar values, i.e. all /// code points in the range `0...0x10_FFFF`, except for the range /// `0xD800...0xDFFF` (the surrogate code points). This includes diff --git a/src/rand_impls.rs b/src/rand_impls.rs index af13c587f8e..065422a8553 100644 --- a/src/rand_impls.rs +++ b/src/rand_impls.rs @@ -10,105 +10,10 @@ //! The implementations of `Rand` for the built-in types. -use core::{char, mem}; +use core::{char}; use {Rand, Rng}; -impl Rand for isize { - #[inline] - fn rand(rng: &mut R) -> isize { - if mem::size_of::() == 4 { - rng.gen::() as isize - } else { - rng.gen::() as isize - } - } -} - -impl Rand for i8 { - #[inline] - fn rand(rng: &mut R) -> i8 { - rng.next_u32() as i8 - } -} - -impl Rand for i16 { - #[inline] - fn rand(rng: &mut R) -> i16 { - rng.next_u32() as i16 - } -} - -impl Rand for i32 { - #[inline] - fn rand(rng: &mut R) -> i32 { - rng.next_u32() as i32 - } -} - -impl Rand for i64 { - #[inline] - fn rand(rng: &mut R) -> i64 { - rng.next_u64() as i64 - } -} - -#[cfg(feature = "i128_support")] -impl Rand for i128 { - #[inline] - fn rand(rng: &mut R) -> i128 { - rng.gen::() as i128 - } -} - -impl Rand for usize { - #[inline] - fn rand(rng: &mut R) -> usize { - if mem::size_of::() == 4 { - rng.gen::() as usize - } else { - rng.gen::() as usize - } - } -} - -impl Rand for u8 { - #[inline] - fn rand(rng: &mut R) -> u8 { - rng.next_u32() as u8 - } -} - -impl Rand for u16 { - #[inline] - fn rand(rng: &mut R) -> u16 { - rng.next_u32() as u16 - } -} - -impl Rand for u32 { - #[inline] - fn rand(rng: &mut R) -> u32 { - rng.next_u32() - } -} - -impl Rand for u64 { - #[inline] - fn rand(rng: &mut R) -> u64 { - rng.next_u64() - } -} - -#[cfg(feature = "i128_support")] -impl Rand for u128 { - #[inline] - fn rand(rng: &mut R) -> u128 { - ((rng.next_u64() as u128) << 64) | (rng.next_u64() as u128) - } -} - - impl Rand for char { #[inline] fn rand(rng: &mut R) -> char { From 4582d4efe559eed29d226d8086d0824fe0c21da6 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 10 Feb 2018 12:22:22 +0000 Subject: [PATCH 10/14] Move char and bool sampling from Rand to Uniform --- src/distributions/mod.rs | 6 ++++++ src/distributions/other.rs | 40 ++++++++++++++++++++++++++++++++++++++ src/lib.rs | 12 +----------- src/rand_impls.rs | 26 ------------------------- 4 files changed, 47 insertions(+), 37 deletions(-) create mode 100644 src/distributions/other.rs diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 7e7114db78e..844e8bb092a 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -36,6 +36,7 @@ pub mod exponential; mod float; mod integer; +mod other; #[cfg(feature="std")] mod ziggurat_tables; @@ -145,6 +146,11 @@ impl<'a, T, D: Distribution> Distribution for &'a D { /// /// * Integers (`i32`, `u32`, `isize`, `usize`, etc.): Uniformly distributed /// over all values of the type. +/// * `char`: Uniformly distributed over all Unicode scalar values, i.e. all +/// code points in the range `0...0x10_FFFF`, except for the range +/// `0xD800...0xDFFF` (the surrogate code points). This includes +/// unassigned/reserved code points. +/// * `bool`: Generates `false` or `true`, each with probability 0.5. /// * Floating point types (`f32` and `f64`): Uniformly distributed in the /// half-open range `[0, 1)`. (The [`Open01`], [`Closed01`], [`Exp1`], and /// [`StandardNormal`] distributions produce floating point numbers with diff --git a/src/distributions/other.rs b/src/distributions/other.rs new file mode 100644 index 00000000000..07cb4795387 --- /dev/null +++ b/src/distributions/other.rs @@ -0,0 +1,40 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// https://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The implementations of the `Uniform` distribution for other built-in types. + +use core::char; + +use {Rng}; +use distributions::{Distribution, Uniform}; + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> char { + // a char is 21 bits + const CHAR_MASK: u32 = 0x001f_ffff; + loop { + // Rejection sampling. About 0.2% of numbers with at most + // 21-bits are invalid codepoints (surrogates), so this + // will succeed first go almost every time. + match char::from_u32(rng.next_u32() & CHAR_MASK) { + Some(c) => return c, + None => {} + } + } + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> bool { + rng.gen::() & 1 == 1 + } +} diff --git a/src/lib.rs b/src/lib.rs index 60fc66e1a86..f4a2f6b513f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -321,17 +321,7 @@ mod prng; /// ## Built-in Implementations /// /// `Rand` is implemented for any type supporting the [`Uniform`] distribution. -/// That includes: floating point numbers. -/// -/// This crate implements `Rand` for various primitive types. Assuming the -/// provided `Rng` is well-behaved, these implementations generate values with -/// the following ranges and distributions: -/// -/// * `char`: Uniformly distributed over all Unicode scalar values, i.e. all -/// code points in the range `0...0x10_FFFF`, except for the range -/// `0xD800...0xDFFF` (the surrogate code points). This includes -/// unassigned/reserved code points. -/// * `bool`: Generates `false` or `true`, each with probability 0.5. +/// That includes: integers, floating point numbers, char, boolean. /// /// The following aggregate types also implement `Rand` as long as their /// component types implement it: diff --git a/src/rand_impls.rs b/src/rand_impls.rs index 065422a8553..09feb7d77e1 100644 --- a/src/rand_impls.rs +++ b/src/rand_impls.rs @@ -10,34 +10,8 @@ //! The implementations of `Rand` for the built-in types. -use core::{char}; - use {Rand, Rng}; -impl Rand for char { - #[inline] - fn rand(rng: &mut R) -> char { - // a char is 21 bits - const CHAR_MASK: u32 = 0x001f_ffff; - loop { - // Rejection sampling. About 0.2% of numbers with at most - // 21-bits are invalid codepoints (surrogates), so this - // will succeed first go almost every time. - match char::from_u32(rng.next_u32() & CHAR_MASK) { - Some(c) => return c, - None => {} - } - } - } -} - -impl Rand for bool { - #[inline] - fn rand(rng: &mut R) -> bool { - rng.gen::() & 1 == 1 - } -} - macro_rules! tuple_impl { // use variables to indicate the arity of the tuple ($($tyvar:ident),* ) => { From 85c092d598bcb99e918a2f663b399b09976e15d6 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 10 Feb 2018 14:54:54 +0000 Subject: [PATCH 11/14] Move remaining Rand impls to the Uniform distribution --- src/distributions/mod.rs | 8 ++++ src/distributions/other.rs | 75 ++++++++++++++++++++++++++++++++++ src/lib.rs | 17 ++------ src/rand_impls.rs | 84 -------------------------------------- 4 files changed, 86 insertions(+), 98 deletions(-) delete mode 100644 src/rand_impls.rs diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 844e8bb092a..09ff80143e3 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -156,6 +156,14 @@ impl<'a, T, D: Distribution> Distribution for &'a D { /// [`StandardNormal`] distributions produce floating point numbers with /// alternative ranges or distributions.) /// +/// The following aggregate types also implement the distribution `Uniform` as +/// long as their component types implement it: +/// +/// * Tuples and arrays: Each element of the tuple or array is generated +/// independently, using the `Uniform` distribution recursively. +/// * `Option`: Returns `None` with probability 0.5; otherwise generates a +/// random `T` and returns `Some(T)`. +/// /// # Example /// ```rust /// use rand::{weak_rng, Rng}; diff --git a/src/distributions/other.rs b/src/distributions/other.rs index 07cb4795387..5a6c64b3a57 100644 --- a/src/distributions/other.rs +++ b/src/distributions/other.rs @@ -38,3 +38,78 @@ impl Distribution for Uniform { rng.gen::() & 1 == 1 } } + +macro_rules! tuple_impl { + // use variables to indicate the arity of the tuple + ($($tyvar:ident),* ) => { + // the trailing commas are for the 1 tuple + impl< $( $tyvar ),* > + Distribution<( $( $tyvar ),* , )> + for Uniform + where $( Uniform: Distribution<$tyvar> ),* + { + #[inline] + fn sample(&self, _rng: &mut R) -> ( $( $tyvar ),* , ) { + ( + // use the $tyvar's to get the appropriate number of + // repeats (they're not actually needed) + $( + _rng.gen::<$tyvar>() + ),* + , + ) + } + } + } +} + +impl Distribution<()> for Uniform { + #[inline] + fn sample(&self, _: &mut R) -> () { () } +} +tuple_impl!{A} +tuple_impl!{A, B} +tuple_impl!{A, B, C} +tuple_impl!{A, B, C, D} +tuple_impl!{A, B, C, D, E} +tuple_impl!{A, B, C, D, E, F} +tuple_impl!{A, B, C, D, E, F, G} +tuple_impl!{A, B, C, D, E, F, G, H} +tuple_impl!{A, B, C, D, E, F, G, H, I} +tuple_impl!{A, B, C, D, E, F, G, H, I, J} +tuple_impl!{A, B, C, D, E, F, G, H, I, J, K} +tuple_impl!{A, B, C, D, E, F, G, H, I, J, K, L} + +macro_rules! array_impl { + // recursive, given at least one type parameter: + {$n:expr, $t:ident, $($ts:ident,)*} => { + array_impl!{($n - 1), $($ts,)*} + + impl Distribution<[T; $n]> for Uniform where Uniform: Distribution { + #[inline] + fn sample(&self, _rng: &mut R) -> [T; $n] { + [_rng.gen::<$t>(), $(_rng.gen::<$ts>()),*] + } + } + }; + // empty case: + {$n:expr,} => { + impl Distribution<[T; $n]> for Uniform { + fn sample(&self, _rng: &mut R) -> [T; $n] { [] } + } + }; +} + +array_impl!{32, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,} + +impl Distribution> for Uniform where Uniform: Distribution { + #[inline] + fn sample(&self, rng: &mut R) -> Option { + // UFCS is needed here: https://github.com/rust-lang/rust/issues/24066 + if rng.gen::() { + Some(rng.gen()) + } else { + None + } + } +} diff --git a/src/lib.rs b/src/lib.rs index f4a2f6b513f..6e896c01aba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -312,24 +312,13 @@ pub mod isaac { // private modules mod le; mod error; -mod rand_impls; mod prng; /// A type that can be randomly generated using an `Rng`. -/// -/// ## Built-in Implementations -/// -/// `Rand` is implemented for any type supporting the [`Uniform`] distribution. -/// That includes: integers, floating point numbers, char, boolean. -/// -/// The following aggregate types also implement `Rand` as long as their -/// component types implement it: -/// -/// * Tuples and arrays: Each element of the tuple or array is generated -/// independently, using its own `Rand` implementation. -/// * `Option`: Returns `None` with probability 0.5; otherwise generates a -/// random `T` and returns `Some(T)`. +/// +/// This is merely an adaptor around the [`Uniform`] distribution for +/// convenience and backwards-compatibility. /// /// [`Uniform`]: distributions/struct.Uniform.html pub trait Rand : Sized { diff --git a/src/rand_impls.rs b/src/rand_impls.rs deleted file mode 100644 index 09feb7d77e1..00000000000 --- a/src/rand_impls.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// https://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The implementations of `Rand` for the built-in types. - -use {Rand, Rng}; - -macro_rules! tuple_impl { - // use variables to indicate the arity of the tuple - ($($tyvar:ident),* ) => { - // the trailing commas are for the 1 tuple - impl< - $( $tyvar : Rand ),* - > Rand for ( $( $tyvar ),* , ) { - - #[inline] - fn rand(_rng: &mut R) -> ( $( $tyvar ),* , ) { - ( - // use the $tyvar's to get the appropriate number of - // repeats (they're not actually needed) - $( - _rng.gen::<$tyvar>() - ),* - , - ) - } - } - } -} - -impl Rand for () { - #[inline] - fn rand(_: &mut R) -> () { () } -} -tuple_impl!{A} -tuple_impl!{A, B} -tuple_impl!{A, B, C} -tuple_impl!{A, B, C, D} -tuple_impl!{A, B, C, D, E} -tuple_impl!{A, B, C, D, E, F} -tuple_impl!{A, B, C, D, E, F, G} -tuple_impl!{A, B, C, D, E, F, G, H} -tuple_impl!{A, B, C, D, E, F, G, H, I} -tuple_impl!{A, B, C, D, E, F, G, H, I, J} -tuple_impl!{A, B, C, D, E, F, G, H, I, J, K} -tuple_impl!{A, B, C, D, E, F, G, H, I, J, K, L} - -macro_rules! array_impl { - {$n:expr, $t:ident, $($ts:ident,)*} => { - array_impl!{($n - 1), $($ts,)*} - - impl Rand for [T; $n] where T: Rand { - #[inline] - fn rand(_rng: &mut R) -> [T; $n] { - [_rng.gen::<$t>(), $(_rng.gen::<$ts>()),*] - } - } - }; - {$n:expr,} => { - impl Rand for [T; $n] { - fn rand(_rng: &mut R) -> [T; $n] { [] } - } - }; -} - -array_impl!{32, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,} - -impl Rand for Option { - #[inline] - fn rand(rng: &mut R) -> Option { - if rng.gen() { - Some(rng.gen()) - } else { - None - } - } -} From c203ca041e95ba86ce99508c7a455997240993ad Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 10 Feb 2018 14:59:30 +0000 Subject: [PATCH 12/14] Make Rng::gen() and random() use Uniform. Deprecate Random and rand_derive. This breaks rand_derive because gen() no longer supports derived types. --- Cargo.toml | 3 --- src/distributions/mod.rs | 5 +++-- src/lib.rs | 22 ++++++++++++++-------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index efe1c0d2ea4..49ecd95aa07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,9 +43,6 @@ serde_derive = {version="1", optional=true} # see: https://github.com/rust-lang/cargo/issues/1596 bincode = "0.9" -[workspace] -members = ["rand-derive"] - [target.'cfg(target_os = "cloudabi")'.dependencies] cloudabi = "0.0.3" diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 09ff80143e3..1bcb09c3f34 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -15,7 +15,7 @@ //! bounds. Distributions use the `Distribution` trait to yield values: call //! `distr.sample(&mut rng)` to get a random variable. -use {Rng, Rand}; +use Rng; pub use self::float::{Open01, Closed01}; pub use self::range::Range; @@ -180,7 +180,8 @@ impl<'a, T, D: Distribution> Distribution for &'a D { #[derive(Debug)] pub struct Uniform; -impl Rand for T where Uniform: Distribution { +#[allow(deprecated)] +impl ::Rand for T where Uniform: Distribution { fn rand(rng: &mut R) -> Self { Uniform.sample(rng) } diff --git a/src/lib.rs b/src/lib.rs index 6e896c01aba..fa9c8a943f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,8 @@ //! Utilities for random number generation //! //! The key functions are `random()` and `Rng::gen()`. These are polymorphic and -//! so can be used to generate any type that implements `Rand`. Type inference +//! so can be used to generate any type supporting the [`Uniform`] distribution +//! (i.e. `T` where `Uniform`: `Distribution`). Type inference //! means that often a simple call to `rand::random()` or `rng.gen()` will //! suffice, but sometimes an annotation is required, e.g. //! `rand::random::()`. @@ -236,6 +237,8 @@ //! keep_wins as f32 / total_keeps as f32); //! } //! ``` +//! +//! [`Uniform`]: distributions/struct.Uniform.html #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", html_favicon_url = "https://www.rust-lang.org/favicon.ico", @@ -286,7 +289,7 @@ use prng::IsaacRng as IsaacWordRng; #[cfg(target_pointer_width = "64")] use prng::Isaac64Rng as IsaacWordRng; -use distributions::{Distribution, Range}; +use distributions::{Distribution, Uniform, Range}; use distributions::range::SampleRange; #[cfg(feature="std")] use reseeding::ReseedingRng; @@ -321,6 +324,7 @@ mod prng; /// convenience and backwards-compatibility. /// /// [`Uniform`]: distributions/struct.Uniform.html +#[deprecated(since="0.5.0", note="replaced by distributions::Uniform")] pub trait Rand : Sized { /// Generates a random instance of this type using the specified source of /// randomness. @@ -465,7 +469,9 @@ pub trait Rng { distr.sample(self) } - /// Return a random value of a `Rand` type. + /// Return a random value supporting the [`Uniform`] distribution. + /// + /// [`Uniform`]: struct.Uniform.html /// /// # Example /// @@ -478,8 +484,8 @@ pub trait Rng { /// println!("{:?}", rng.gen::<(f64, bool)>()); /// ``` #[inline(always)] - fn gen(&mut self) -> T where Self: Sized { - Rand::rand(self) + fn gen(&mut self) -> T where Self: Sized, Uniform: Distribution { + Uniform.sample(self) } /// Return an iterator that will yield an infinite number of randomly @@ -496,7 +502,7 @@ pub trait Rng { /// println!("{:?}", rng.gen_iter::<(f64, bool)>().take(5) /// .collect::>()); /// ``` - fn gen_iter<'a, T: Rand>(&'a mut self) -> Generator<'a, T, Self> where Self: Sized { + fn gen_iter<'a, T>(&'a mut self) -> Generator<'a, T, Self> where Self: Sized, Uniform: Distribution { Generator { rng: self, _marker: marker::PhantomData } } @@ -696,7 +702,7 @@ pub struct Generator<'a, T, R:'a> { _marker: marker::PhantomData T>, } -impl<'a, T: Rand, R: Rng> Iterator for Generator<'a, T, R> { +impl<'a, T, R: Rng> Iterator for Generator<'a, T, R> where Uniform: Distribution { type Item = T; fn next(&mut self) -> Option { @@ -1131,7 +1137,7 @@ impl Rng for EntropyRng { /// [`Rand`]: trait.Rand.html #[cfg(feature="std")] #[inline] -pub fn random() -> T { +pub fn random() -> T where Uniform: Distribution { thread_rng().gen() } From 29a9c79c245d07fa2597a50f5084961fcf2d5555 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 16 Feb 2018 10:39:04 +0000 Subject: [PATCH 13/14] Add example: distribution with erased Rng --- src/distributions/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 1bcb09c3f34..8dca964c511 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -173,6 +173,18 @@ impl<'a, T, D: Distribution> Distribution for &'a D { /// println!("f32 from [0,1): {}", val); /// ``` /// +/// With dynamic dispatch (type erasure of `Rng`): +/// +/// ```rust +/// use rand::{thread_rng, Rng}; +/// use rand::distributions::Uniform; +/// +/// let mut rng = thread_rng(); +/// let mut erased_rng: &mut Rng = &mut rng; +/// let val: f32 = (&mut erased_rng).sample(Uniform); +/// println!("f32 from [0,1): {}", val); +/// ``` +/// /// [`Open01`]: struct.Open01.html /// [`Closed01`]: struct.Closed01.html /// [`Exp1`]: struct.Exp1.html From 6b974755bdeec49b2fb0246968ab97c36c30f962 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 16 Feb 2018 11:03:59 +0000 Subject: [PATCH 14/14] Remove rand_derive CI tests --- .travis.yml | 2 -- appveyor.yml | 1 - 2 files changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index d582eb33018..dee09e69499 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ matrix: script: - cargo test - cargo build --no-default-features # we cannot exclude doc tests - - cargo test --manifest-path rand-derive/Cargo.toml - rust: stable - rust: stable os: osx @@ -30,7 +29,6 @@ script: - cargo test --tests --no-default-features - cargo test --features serde-1,log - cargo test --tests --no-default-features --features=serde-1 - - cargo test --manifest-path rand-derive/Cargo.toml env: global: diff --git a/appveyor.yml b/appveyor.yml index 9751dacc5d7..033f7098320 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -38,4 +38,3 @@ test_script: - cargo test --features nightly - cargo test --tests --no-default-features --features=alloc - cargo test --tests --no-default-features --features=serde-1 - - cargo test --manifest-path rand-derive/Cargo.toml