From 9ca00470b6934c46b7e12f92ba029b86465c1e53 Mon Sep 17 00:00:00 2001 From: Graham Dennis Date: Sun, 15 Mar 2015 17:23:24 +1100 Subject: [PATCH 1/3] Replace the `IndependentSample` trait with `Distribution`. Also removed the `Sample` trait. `IndependentSample` is really a probability distribution of values, and hence renamed to `Distribution`, but with an associated `Output` type instead of being generic over a type. The `Sample` trait is a state-mutating version of `IndependentSample` and is really a random process. A random process can be infinite (e.g. a random walk) or finite (e.g. drawing from a bag without replacement). So really, a random process is best represented by an `Iterator`. Additionally all previous implementations of `Sample` simply called through to `IndependentSample.ind_sample`, so `Sample` has simply been removed. The related trait `SampleRange` has been renamed to `RangeDistribution`. --- src/distributions/exponential.rs | 24 ++++--- src/distributions/gamma.rs | 108 ++++++++++++++----------------- src/distributions/mod.rs | 86 ++++++++++-------------- src/distributions/normal.rs | 42 ++++++------ src/distributions/range.rs | 40 +++++------- src/lib.rs | 20 +++--- 6 files changed, 139 insertions(+), 181 deletions(-) diff --git a/src/distributions/exponential.rs b/src/distributions/exponential.rs index dc16dd9d4e7..be0d5d7bec7 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. /// @@ -58,10 +58,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)] @@ -79,11 +79,10 @@ 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 { + type Output = f64; + + fn sample(&self, rng: &mut R) -> f64 { let Exp1(n) = rng.gen::(); n * self.lambda_inverse } @@ -91,16 +90,15 @@ 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(); for _ in 0..1000 { assert!(exp.sample(&mut rng) >= 0.0); - assert!(exp.ind_sample(&mut rng) >= 0.0); } } #[test] @@ -122,12 +120,12 @@ mod bench { use self::test::Bencher; use std::mem::size_of; use super::Exp; - use distributions::Sample; + use distributions::Distribution; #[bench] fn rand_exp(b: &mut Bencher) { let mut rng = ::test::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/src/distributions/gamma.rs b/src/distributions/gamma.rs index 0cb9fdf511e..7523ae7fb60 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); /// ``` /// @@ -130,34 +130,30 @@ 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 Distribution for Gamma { + type Output = f64; -impl IndependentSample for Gamma { - fn ind_sample(&self, rng: &mut R) -> f64 { + 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 { + type Output = f64; + + 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 { + type Output = f64; + + fn sample(&self, rng: &mut R) -> f64 { loop { let StandardNormal(x) = rng.gen::(); let v_cbrt = 1.0 + self.c * x; @@ -188,10 +184,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)] @@ -221,18 +217,17 @@ 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 { + type Output = f64; + + 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) } } } @@ -246,10 +241,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)] @@ -275,12 +270,11 @@ 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 { + type Output = f64; + + fn sample(&self, rng: &mut R) -> f64 { + self.numer.sample(rng) / self.denom.sample(rng) * self.dof_ratio } } @@ -290,10 +284,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)] @@ -313,46 +307,42 @@ 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 { + type Output = f64; + + 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(); for _ in 0..1000 { chi.sample(&mut rng); - chi.ind_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(); for _ in 0..1000 { chi.sample(&mut rng); - chi.ind_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(); for _ in 0..1000 { chi.sample(&mut rng); - chi.ind_sample(&mut rng); } } #[test] @@ -363,21 +353,19 @@ 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(); for _ in 0..1000 { f.sample(&mut rng); - f.ind_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(); for _ in 0..1000 { t.sample(&mut rng); - t.ind_sample(&mut rng); } } } @@ -387,7 +375,7 @@ mod bench { extern crate test; use self::test::Bencher; use std::mem::size_of; - use distributions::IndependentSample; + use distributions::Distribution; use super::Gamma; @@ -398,7 +386,7 @@ mod bench { 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; @@ -411,7 +399,7 @@ mod bench { 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/src/distributions/mod.rs b/src/distributions/mod.rs index e3b8e0f23bd..35b2fe8320e 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -11,11 +11,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. +//! distribution of the generated values, e.g. the mean and standard deviation +//! of a normal distribution. The `Distribution` trait allows for generating values +//! from a distribution that is independent of the number of samples generated, +//! i.e. sampling "with replacement". use std::num::{Float, Int}; use std::marker; @@ -32,50 +31,37 @@ pub mod gamma; pub mod normal; pub mod exponential; -/// Types that can be used to create a random instance of `Support`. -pub trait Sample { - /// Generate a random value of `Support`, using `rng` as the - /// source of randomness. - fn sample(&mut self, rng: &mut R) -> Support; -} - -/// `Sample`s that do not require keeping track of state. +/// Type that can be used to create a random instance of `Output`. /// /// Since no state is recorded, each sample is (statistically) /// independent of all others, assuming the `Rng` used has this /// property. -// 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`. -pub trait IndependentSample: Sample { - /// Generate a random value. - fn ind_sample(&self, &mut R) -> Support; +pub trait Distribution { + type Output; + /// Generate a random value of `Output`, using `rng` as the + /// source of randomness. + fn sample(&self, rng: &mut R) -> ::Output; } -/// A wrapper for generating types that implement `Rand` via the -/// `Sample` & `IndependentSample` traits. -pub struct RandSample { - _marker: marker::PhantomData Sup>, +/// A wrapper for generating types that implement `Distribution` via the +/// `Rand` trait. +pub struct RandDistribution { + _marker: marker::PhantomData T>, } -impl Copy for RandSample {} -impl Clone for RandSample { +impl Copy for RandDistribution {} +impl Clone for RandDistribution { fn clone(&self) -> Self { *self } } -impl Sample for RandSample { - fn sample(&mut self, rng: &mut R) -> Sup { self.ind_sample(rng) } +impl Distribution for RandDistribution { + type Output = T; + fn sample(&self, rng: &mut R) -> T { rng.gen() } } -impl IndependentSample for RandSample { - fn ind_sample(&self, rng: &mut R) -> Sup { - rng.gen() - } -} - -impl RandSample { - pub fn new() -> RandSample { - RandSample { _marker: marker::PhantomData } +impl RandDistribution { + pub fn new() -> RandDistribution { + RandDistribution { _marker: marker::PhantomData } } } @@ -94,15 +80,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' }, @@ -111,7 +96,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)); /// } /// ``` pub struct WeightedChoice<'a, T:'a> { @@ -155,18 +140,16 @@ 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> Distribution for WeightedChoice<'a, T> { + type Output = T; -impl<'a, T: Clone> IndependentSample for WeightedChoice<'a, T> { - fn ind_sample(&self, rng: &mut R) -> 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 { @@ -272,7 +255,7 @@ fn ziggurat( mod tests { use {Rng, Rand}; - use super::{RandSample, WeightedChoice, Weighted, Sample, IndependentSample}; + use super::{RandDistribution, WeightedChoice, Weighted, Distribution}; #[derive(PartialEq, Debug)] struct ConstRand(usize); @@ -296,10 +279,9 @@ mod tests { #[test] fn test_rand_sample() { - let mut rand_sample = RandSample::::new(); + let rand_sample = RandDistribution::::new(); assert_eq!(rand_sample.sample(&mut ::test::rng()), ConstRand(0)); - assert_eq!(rand_sample.ind_sample(&mut ::test::rng()), ConstRand(0)); } #[test] fn test_weighted_choice() { @@ -317,7 +299,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) } }} } diff --git a/src/distributions/normal.rs b/src/distributions/normal.rs index e35cba29b5d..50607798e50 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). @@ -74,11 +74,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)] @@ -102,11 +102,10 @@ 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 { + type Output = f64; + + fn sample(&self, rng: &mut R) -> f64 { let StandardNormal(n) = rng.gen::(); self.mean + self.std_dev * n } @@ -121,11 +120,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)] @@ -145,27 +144,25 @@ 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 { + type Output = f64; + + 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(); for _ in 0..1000 { norm.sample(&mut rng); - norm.ind_sample(&mut rng); } } #[test] @@ -177,11 +174,10 @@ 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(); for _ in 0..1000 { lnorm.sample(&mut rng); - lnorm.ind_sample(&mut rng); } } #[test] @@ -196,13 +192,13 @@ mod bench { extern crate test; use self::test::Bencher; use std::mem::size_of; - use distributions::{Sample}; + use distributions::Distribution; use super::Normal; #[bench] fn rand_normal(b: &mut Bencher) { let mut rng = ::test::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/range.rs b/src/distributions/range.rs index 6f8bc7aef75..1e569541a20 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -15,12 +15,12 @@ use std::num::Wrapping as w; use Rng; -use distributions::{Sample, IndependentSample}; +use distributions::Distribution; /// Sample values uniformly between two bounds. /// /// This gives a uniform distribution (assuming the RNG used to sample -/// it is itself uniform & the `SampleRange` implementation for the +/// it is itself uniform & the `RangeDistribution` implementation for the /// given type is correct), even for edge cases like `low = 0u8`, /// `high = 170u8`, for which a naive modulo operation would return /// numbers less than 85 with double the probability to those greater @@ -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); /// } @@ -53,29 +53,27 @@ pub struct Range { accept_zone: X } -impl Range { +impl Range { /// Create a new `Range` instance that samples uniformly from /// `[low, high)`. Panics if `low >= high`. pub fn new(low: X, high: X) -> Range { assert!(low < high, "Range::new called with `low >= high`"); - SampleRange::construct_range(low, high) + RangeDistribution::construct_range(low, high) } } -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 { - SampleRange::sample_range(self, rng) +impl Distribution for Range { + type Output = Sup; + + fn sample(&self, rng: &mut R) -> Sup { + RangeDistribution::sample_range(self, rng) } } /// The helper trait for types that have a sensible way to sample /// uniformly between two values. This should not be used directly, /// and is only to facilitate `Range`. -pub trait SampleRange { +pub trait RangeDistribution { /// Construct the `Range` object that `sample_range` /// requires. This should not ever be called directly, only via /// `Range::new`, which will check that `low < high`, so this @@ -89,7 +87,7 @@ pub trait SampleRange { macro_rules! integer_impl { ($ty:ty, $unsigned:ident) => { - impl SampleRange for $ty { + impl RangeDistribution for $ty { // we play free and fast with unsigned vs signed here // (when $ty is signed), but that's fine, since the // contract of this macro is for $ty and $unsigned to be @@ -143,7 +141,7 @@ integer_impl! { usize, usize } macro_rules! float_impl { ($ty:ty) => { - impl SampleRange for $ty { + impl RangeDistribution for $ty { fn construct_range(low: $ty, high: $ty) -> Range<$ty> { Range { low: low, @@ -163,7 +161,7 @@ float_impl! { f64 } #[cfg(test)] mod tests { - use distributions::{Sample, IndependentSample}; + use distributions::Distribution; use super::Range as Range; #[should_panic] @@ -187,12 +185,10 @@ mod tests { (10, 127), (::std::$ty::MIN, ::std::$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); - assert!(low <= v && v < high); } } )* @@ -213,12 +209,10 @@ 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); - assert!(low <= v && v < high); } } )* diff --git a/src/lib.rs b/src/lib.rs index 90f56d88ebb..7f04c1b9d55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,7 +94,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.); @@ -104,8 +104,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; //! } @@ -137,7 +137,7 @@ //! //! ``` //! use rand::Rng; -//! use rand::distributions::{IndependentSample, Range}; +//! use rand::distributions::{Distribution, Range}; //! //! struct SimulationResult { //! win: bool, @@ -147,10 +147,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); @@ -246,8 +246,8 @@ use IsaacRng as IsaacWordRng; #[cfg(target_pointer_width = "64")] use Isaac64Rng as IsaacWordRng; -use distributions::{Range, IndependentSample}; -use distributions::range::SampleRange; +use distributions::{Range, Distribution}; +use distributions::range::RangeDistribution; pub mod distributions; pub mod isaac; @@ -437,9 +437,9 @@ pub trait Rng : Sized { /// let m: f64 = rng.gen_range(-40.0f64, 1.3e5f64); /// println!("{}", m); /// ``` - fn gen_range(&mut self, low: T, high: T) -> T { + fn gen_range(&mut self, low: T, high: T) -> T { 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 e6e78bd62451c7bae3cb20dc6c79f584193945d3 Mon Sep 17 00:00:00 2001 From: Graham Dennis Date: Sun, 22 Mar 2015 10:14:18 +1100 Subject: [PATCH 2/3] Add `Constant`, a distribution always returning a fixed value. --- src/distributions/mod.rs | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 35b2fe8320e..31e4a27dc40 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -65,6 +65,27 @@ impl RandDistribution { } } +/// A distribution that always returns the same value. +/// +/// # Example +/// +/// ```rust +/// use rand::distributions::{Distribution, Constant}; +/// +/// let d = Constant(42); +/// let v = d.sample(&mut rand::thread_rng()); +/// assert_eq!(v, 42); +/// ``` +pub struct Constant(pub T); + +impl Distribution for Constant { + type Output = T; + + fn sample(&self, _: &mut R) -> T { + self.0.clone() + } +} + /// A value with a particular weight for use with `WeightedChoice`. #[derive(Copy)] #[derive(Clone)] @@ -255,7 +276,7 @@ fn ziggurat( mod tests { use {Rng, Rand}; - use super::{RandDistribution, WeightedChoice, Weighted, Distribution}; + use super::{RandDistribution, WeightedChoice, Weighted, Distribution, Constant}; #[derive(PartialEq, Debug)] struct ConstRand(usize); @@ -283,6 +304,16 @@ mod tests { assert_eq!(rand_sample.sample(&mut ::test::rng()), ConstRand(0)); } + + #[test] + fn test_constant() { + let d = Constant(11); + + assert_eq!(d.sample(&mut ::test::rng()), 11); + assert_eq!(d.sample(&mut ::test::rng()), 11); + assert_eq!(d.sample(&mut ::test::rng()), 11); + } + #[test] fn test_weighted_choice() { // this makes assumptions about the internal implementation of From 0ef544e27cb95aa0bfdd4e15fb19abd6fa3aba2f Mon Sep 17 00:00:00 2001 From: Graham Dennis Date: Sun, 13 Sep 2015 21:10:06 +1000 Subject: [PATCH 3/3] Fix compilation against Rust 1.2 --- Cargo.toml | 1 + src/distributions/mod.rs | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 79ae8c735cf..a67cef609a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ keywords = ["random"] [dependencies] log = "0.3.0" libc = "0.1.1" +num = "0.1.27" [dev-dependencies.rand_macros] path = "rand_macros" diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 31e4a27dc40..66e505eb83f 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -16,7 +16,9 @@ //! from a distribution that is independent of the number of samples generated, //! i.e. sampling "with replacement". -use std::num::{Float, Int}; +extern crate num; + +use self::num::traits::{Float, CheckedAdd}; use std::marker; use {Rng, Rand}; @@ -142,7 +144,7 @@ impl<'a, T: Clone> WeightedChoice<'a, T> { // weights so we can binary search. This *could* drop elements // with weight == 0 as an optimisation. for item in items.iter_mut() { - running_total = match running_total.checked_add(item.weight) { + running_total = match running_total.checked_add(&item.weight) { Some(n) => n, None => panic!("WeightedChoice::new called with a total weight \ larger than a u32 can contain")