rand::distributions -> distr; split uniform module (#1470)

This commit is contained in:
Diggory Hardy
2024-07-23 14:14:11 +01:00
committed by GitHub
parent 605476c4e4
commit 2d5948d264
40 changed files with 2012 additions and 1918 deletions
+1
View File
@@ -18,6 +18,7 @@ You may also find the [Upgrade Guide](https://rust-random.github.io/book/update.
- Enable feature `small_rng` by default (#1455) - Enable feature `small_rng` by default (#1455)
- Allow `UniformFloat::new` samples and `UniformFloat::sample_single` to yield `high` (#1462) - Allow `UniformFloat::new` samples and `UniformFloat::sample_single` to yield `high` (#1462)
- Fix portability of `rand::distributions::Slice` (#1469) - Fix portability of `rand::distributions::Slice` (#1469)
- Rename `rand::distributions` to `rand::distr` (#1470)
## [0.9.0-alpha.1] - 2024-03-18 ## [0.9.0-alpha.1] - 2024-03-18
- Add the `Slice::num_choices` method to the Slice distribution (#1402) - Add the `Slice::num_choices` method to the Slice distribution (#1402)
+1 -1
View File
@@ -17,7 +17,7 @@ A Rust library for random number generation, featuring:
([see the book](https://rust-random.github.io/book/crates.html)) ([see the book](https://rust-random.github.io/book/crates.html))
- Fast implementations of the best-in-class [cryptographic](https://rust-random.github.io/book/guide-rngs.html#cryptographically-secure-pseudo-random-number-generators-csprngs) and - Fast implementations of the best-in-class [cryptographic](https://rust-random.github.io/book/guide-rngs.html#cryptographically-secure-pseudo-random-number-generators-csprngs) and
[non-cryptographic](https://rust-random.github.io/book/guide-rngs.html#basic-pseudo-random-number-generators-prngs) generators [non-cryptographic](https://rust-random.github.io/book/guide-rngs.html#basic-pseudo-random-number-generators-prngs) generators
- A flexible [`distributions`](https://docs.rs/rand/*/rand/distributions/index.html) module - A flexible [`distributions`](https://docs.rs/rand/*/rand/distr/index.html) module
- Samplers for a large number of random number distributions via our own - Samplers for a large number of random number distributions via our own
[`rand_distr`](https://docs.rs/rand_distr) and via [`rand_distr`](https://docs.rs/rand_distr) and via
the [`statrs`](https://docs.rs/statrs/0.13.0/statrs/) the [`statrs`](https://docs.rs/statrs/0.13.0/statrs/)
+2 -2
View File
@@ -15,8 +15,8 @@ criterion = "0.5"
criterion-cycles-per-byte = "0.6" criterion-cycles-per-byte = "0.6"
[[bench]] [[bench]]
name = "distributions" name = "distr"
path = "src/distributions.rs" path = "src/distr.rs"
harness = false harness = false
[[bench]] [[bench]]
+3 -3
View File
@@ -16,8 +16,8 @@ extern crate test;
const RAND_BENCH_N: u64 = 1000; const RAND_BENCH_N: u64 = 1000;
use rand::distributions::{Alphanumeric, Open01, OpenClosed01, Standard, Uniform}; use rand::distr::{Alphanumeric, Open01, OpenClosed01, Standard, Uniform};
use rand::distributions::uniform::{UniformInt, UniformSampler}; use rand::distr::uniform::{UniformInt, UniformSampler};
use core::mem::size_of; use core::mem::size_of;
use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8}; use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8};
use core::time::Duration; use core::time::Duration;
@@ -253,7 +253,7 @@ gen_range_float!(gen_range_f32, f32, -20000.0f32, 100000.0);
gen_range_float!(gen_range_f64, f64, 123.456f64, 7890.12); gen_range_float!(gen_range_f64, f64, 123.456f64, 7890.12);
// In src/distributions/uniform.rs, we say: // In src/distr/uniform.rs, we say:
// Implementation of [`uniform_single`] is optional, and is only useful when // Implementation of [`uniform_single`] is optional, and is only useful when
// the implementation can be faster than `Self::new(low, high).sample(rng)`. // the implementation can be faster than `Self::new(low, high).sample(rng)`.
+1 -1
View File
@@ -14,7 +14,7 @@ const RAND_BENCH_N: u64 = 1000;
use test::Bencher; use test::Bencher;
use rand::distributions::{Bernoulli, Distribution, Standard}; use rand::distr::{Bernoulli, Distribution, Standard};
use rand::prelude::*; use rand::prelude::*;
use rand_pcg::{Pcg32, Pcg64Mcg}; use rand_pcg::{Pcg32, Pcg64Mcg};
+1 -1
View File
@@ -10,7 +10,7 @@
extern crate test; extern crate test;
use rand::distributions::WeightedIndex; use rand::distr::WeightedIndex;
use rand::Rng; use rand::Rng;
use test::Bencher; use test::Bencher;
+1 -1
View File
@@ -10,7 +10,7 @@
use core::time::Duration; use core::time::Duration;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use rand::distributions::uniform::{SampleRange, Uniform}; use rand::distr::uniform::{SampleRange, Uniform};
use rand::prelude::*; use rand::prelude::*;
use rand_chacha::ChaCha8Rng; use rand_chacha::ChaCha8Rng;
use rand_pcg::{Pcg32, Pcg64}; use rand_pcg::{Pcg32, Pcg64};
+1 -1
View File
@@ -14,7 +14,7 @@
use core::time::Duration; use core::time::Duration;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use rand::distributions::uniform::{SampleUniform, Uniform, UniformSampler}; use rand::distr::uniform::{SampleUniform, Uniform, UniformSampler};
use rand::prelude::*; use rand::prelude::*;
use rand_chacha::ChaCha8Rng; use rand_chacha::ChaCha8Rng;
use rand_pcg::{Pcg32, Pcg64}; use rand_pcg::{Pcg32, Pcg64};
+1 -1
View File
@@ -23,7 +23,7 @@
//! We can use the above fact to estimate the value of π: pick many points in //! We can use the above fact to estimate the value of π: pick many points in
//! the square at random, calculate the fraction that fall within the circle, //! the square at random, calculate the fraction that fall within the circle,
//! and multiply this fraction by 4. //! and multiply this fraction by 4.
use rand::distributions::{Distribution, Uniform}; use rand::distr::{Distribution, Uniform};
fn main() { fn main() {
let range = Uniform::new(-1.0f64, 1.0).unwrap(); let range = Uniform::new(-1.0f64, 1.0).unwrap();
+1 -1
View File
@@ -26,7 +26,7 @@
//! //!
//! [Monty Hall Problem]: https://en.wikipedia.org/wiki/Monty_Hall_problem //! [Monty Hall Problem]: https://en.wikipedia.org/wiki/Monty_Hall_problem
use rand::distributions::{Distribution, Uniform}; use rand::distr::{Distribution, Uniform};
use rand::Rng; use rand::Rng;
struct SimulationResult { struct SimulationResult {
+1 -1
View File
@@ -38,7 +38,7 @@
//! over BATCH_SIZE trials. Manually batching also turns out to be faster //! over BATCH_SIZE trials. Manually batching also turns out to be faster
//! for the nondeterministic version of this program as well. //! for the nondeterministic version of this program as well.
use rand::distributions::{Distribution, Uniform}; use rand::distr::{Distribution, Uniform};
use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
use rayon::prelude::*; use rayon::prelude::*;
+2 -2
View File
@@ -8,7 +8,7 @@
Implements a full suite of random number distribution sampling routines. Implements a full suite of random number distribution sampling routines.
This crate is a superset of the [rand::distributions] module, including support This crate is a superset of the [rand::distr] module, including support
for sampling from Beta, Binomial, Cauchy, ChiSquared, Dirichlet, Exponential, for sampling from Beta, Binomial, Cauchy, ChiSquared, Dirichlet, Exponential,
FisherF, Gamma, Geometric, Hypergeometric, InverseGaussian, LogNormal, Normal, FisherF, Gamma, Geometric, Hypergeometric, InverseGaussian, LogNormal, Normal,
Pareto, PERT, Poisson, StudentT, Triangular and Weibull distributions. Sampling Pareto, PERT, Poisson, StudentT, Triangular and Weibull distributions. Sampling
@@ -46,7 +46,7 @@ can be enabled. (Note that any other crate depending on `num-traits` with the
[statrs]: https://github.com/boxtown/statrs [statrs]: https://github.com/boxtown/statrs
[rand::distributions]: https://rust-random.github.io/rand/rand/distributions/index.html [rand::distr]: https://rust-random.github.io/rand/rand/distr/index.html
## License ## License
+1 -1
View File
@@ -4,7 +4,7 @@ use crate::Distribution;
use core::fmt; use core::fmt;
#[allow(unused_imports)] #[allow(unused_imports)]
use num_traits::Float; use num_traits::Float;
use rand::distributions::uniform::Uniform; use rand::distr::uniform::Uniform;
use rand::Rng; use rand::Rng;
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
+4 -4
View File
@@ -27,8 +27,8 @@
//! //!
//! ## Re-exports //! ## Re-exports
//! //!
//! This crate is a super-set of the [`rand::distributions`] module. See the //! This crate is a super-set of the [`rand::distr`] module. See the
//! [`rand::distributions`] module documentation for an overview of the core //! [`rand::distr`] module documentation for an overview of the core
//! [`Distribution`] trait and implementations. //! [`Distribution`] trait and implementations.
//! //!
//! The following are re-exported: //! The following are re-exported:
@@ -93,7 +93,7 @@ extern crate std;
#[allow(unused)] #[allow(unused)]
use rand::Rng; use rand::Rng;
pub use rand::distributions::{ pub use rand::distr::{
uniform, Alphanumeric, Bernoulli, BernoulliError, DistIter, Distribution, Open01, OpenClosed01, uniform, Alphanumeric, Bernoulli, BernoulliError, DistIter, Distribution, Open01, OpenClosed01,
Standard, Uniform, Standard, Uniform,
}; };
@@ -129,7 +129,7 @@ pub use self::weibull::{Error as WeibullError, Weibull};
pub use self::zeta::{Error as ZetaError, Zeta}; pub use self::zeta::{Error as ZetaError, Zeta};
pub use self::zipf::{Error as ZipfError, Zipf}; pub use self::zipf::{Error as ZipfError, Zipf};
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub use rand::distributions::{WeightError, WeightedIndex}; pub use rand::distr::{WeightError, WeightedIndex};
pub use student_t::StudentT; pub use student_t::StudentT;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub use weighted_alias::WeightedAliasIndex; pub use weighted_alias::WeightedAliasIndex;
+1 -1
View File
@@ -10,7 +10,7 @@
use crate::ziggurat_tables; use crate::ziggurat_tables;
use num_traits::Float; use num_traits::Float;
use rand::distributions::hidden_export::IntoFloat; use rand::distr::hidden_export::IntoFloat;
use rand::Rng; use rand::Rng;
/// Calculates ln(gamma(x)) (natural logarithm of the gamma /// Calculates ln(gamma(x)) (natural logarithm of the gamma
+3 -3
View File
@@ -14,8 +14,8 @@ use core::ops::SubAssign;
use super::WeightError; use super::WeightError;
use crate::Distribution; use crate::Distribution;
use alloc::vec::Vec; use alloc::vec::Vec;
use rand::distributions::uniform::{SampleBorrow, SampleUniform}; use rand::distr::uniform::{SampleBorrow, SampleUniform};
use rand::distributions::Weight; use rand::distr::Weight;
use rand::Rng; use rand::Rng;
#[cfg(feature = "serde1")] #[cfg(feature = "serde1")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -30,7 +30,7 @@ use serde::{Deserialize, Serialize};
/// ///
/// # Key differences /// # Key differences
/// ///
/// The main distinction between [`WeightedTreeIndex<W>`] and [`rand::distributions::WeightedIndex<W>`] /// The main distinction between [`WeightedTreeIndex<W>`] and [`rand::distr::WeightedIndex<W>`]
/// lies in the internal representation of weights. In [`WeightedTreeIndex<W>`], /// lies in the internal representation of weights. In [`WeightedTreeIndex<W>`],
/// weights are structured as a tree, which is optimized for frequent updates of the weights. /// weights are structured as a tree, which is optimized for frequent updates of the weights.
/// ///
+1 -1
View File
@@ -11,7 +11,7 @@
use crate::{Distribution, Standard}; use crate::{Distribution, Standard};
use core::fmt; use core::fmt;
use num_traits::Float; use num_traits::Float;
use rand::{distributions::OpenClosed01, Rng}; use rand::{distr::OpenClosed01, Rng};
/// The [Zeta distribution](https://en.wikipedia.org/wiki/Zeta_distribution) `Zeta(s)`. /// The [Zeta distribution](https://en.wikipedia.org/wiki/Zeta_distribution) `Zeta(s)`.
/// ///
@@ -8,7 +8,7 @@
//! The Bernoulli distribution `Bernoulli(p)`. //! The Bernoulli distribution `Bernoulli(p)`.
use crate::distributions::Distribution; use crate::distr::Distribution;
use crate::Rng; use crate::Rng;
use core::fmt; use core::fmt;
@@ -31,7 +31,7 @@ use serde::{Deserialize, Serialize};
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use rand::distributions::{Bernoulli, Distribution}; /// use rand::distr::{Bernoulli, Distribution};
/// ///
/// let d = Bernoulli::new(0.3).unwrap(); /// let d = Bernoulli::new(0.3).unwrap();
/// let v = d.sample(&mut rand::thread_rng()); /// let v = d.sample(&mut rand::thread_rng());
@@ -153,7 +153,7 @@ impl Distribution<bool> for Bernoulli {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::Bernoulli; use super::Bernoulli;
use crate::distributions::Distribution; use crate::distr::Distribution;
use crate::Rng; use crate::Rng;
#[test] #[test]
@@ -49,7 +49,7 @@ pub trait Distribution<T> {
/// ///
/// ``` /// ```
/// use rand::thread_rng; /// use rand::thread_rng;
/// use rand::distributions::{Distribution, Alphanumeric, Uniform, Standard}; /// use rand::distr::{Distribution, Alphanumeric, Uniform, Standard};
/// ///
/// let mut rng = thread_rng(); /// let mut rng = thread_rng();
/// ///
@@ -89,7 +89,7 @@ pub trait Distribution<T> {
/// ///
/// ``` /// ```
/// use rand::thread_rng; /// use rand::thread_rng;
/// use rand::distributions::{Distribution, Uniform}; /// use rand::distr::{Distribution, Uniform};
/// ///
/// let mut rng = thread_rng(); /// let mut rng = thread_rng();
/// ///
@@ -201,12 +201,12 @@ pub trait DistString {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::distributions::{Distribution, Uniform}; use crate::distr::{Distribution, Uniform};
use crate::Rng; use crate::Rng;
#[test] #[test]
fn test_distributions_iter() { fn test_distributions_iter() {
use crate::distributions::Open01; use crate::distr::Open01;
let mut rng = crate::test::rng(210); let mut rng = crate::test::rng(210);
let distr = Open01; let distr = Open01;
let mut iter = Distribution::<f32>::sample_iter(distr, &mut rng); let mut iter = Distribution::<f32>::sample_iter(distr, &mut rng);
@@ -248,7 +248,7 @@ mod tests {
#[test] #[test]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn test_dist_string() { fn test_dist_string() {
use crate::distributions::{Alphanumeric, DistString, Standard}; use crate::distr::{Alphanumeric, DistString, Standard};
use core::str; use core::str;
let mut rng = crate::test::rng(213); let mut rng = crate::test::rng(213);
@@ -8,8 +8,8 @@
//! Basic floating-point number distributions //! Basic floating-point number distributions
use crate::distributions::utils::{FloatAsSIMD, FloatSIMDUtils, IntAsSIMD}; use crate::distr::utils::{FloatAsSIMD, FloatSIMDUtils, IntAsSIMD};
use crate::distributions::{Distribution, Standard}; use crate::distr::{Distribution, Standard};
use crate::Rng; use crate::Rng;
use core::mem; use core::mem;
#[cfg(feature = "simd_support")] #[cfg(feature = "simd_support")]
@@ -33,15 +33,15 @@ use serde::{Deserialize, Serialize};
/// # Example /// # Example
/// ``` /// ```
/// use rand::{thread_rng, Rng}; /// use rand::{thread_rng, Rng};
/// use rand::distributions::OpenClosed01; /// use rand::distr::OpenClosed01;
/// ///
/// let val: f32 = thread_rng().sample(OpenClosed01); /// let val: f32 = thread_rng().sample(OpenClosed01);
/// println!("f32 from (0, 1): {}", val); /// println!("f32 from (0, 1): {}", val);
/// ``` /// ```
/// ///
/// [`Standard`]: crate::distributions::Standard /// [`Standard`]: crate::distr::Standard
/// [`Open01`]: crate::distributions::Open01 /// [`Open01`]: crate::distr::Open01
/// [`Uniform`]: crate::distributions::uniform::Uniform /// [`Uniform`]: crate::distr::uniform::Uniform
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct OpenClosed01; pub struct OpenClosed01;
@@ -60,15 +60,15 @@ pub struct OpenClosed01;
/// # Example /// # Example
/// ``` /// ```
/// use rand::{thread_rng, Rng}; /// use rand::{thread_rng, Rng};
/// use rand::distributions::Open01; /// use rand::distr::Open01;
/// ///
/// let val: f32 = thread_rng().sample(Open01); /// let val: f32 = thread_rng().sample(Open01);
/// println!("f32 from (0, 1): {}", val); /// println!("f32 from (0, 1): {}", val);
/// ``` /// ```
/// ///
/// [`Standard`]: crate::distributions::Standard /// [`Standard`]: crate::distr::Standard
/// [`OpenClosed01`]: crate::distributions::OpenClosed01 /// [`OpenClosed01`]: crate::distr::OpenClosed01
/// [`Uniform`]: crate::distributions::uniform::Uniform /// [`Uniform`]: crate::distr::uniform::Uniform
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct Open01; pub struct Open01;
@@ -8,7 +8,7 @@
//! The implementations of the `Standard` distribution for integer types. //! The implementations of the `Standard` distribution for integer types.
use crate::distributions::{Distribution, Standard}; use crate::distr::{Distribution, Standard};
use crate::Rng; use crate::Rng;
#[cfg(all(target_arch = "x86", feature = "simd_support"))] #[cfg(all(target_arch = "x86", feature = "simd_support"))]
use core::arch::x86::__m512i; use core::arch::x86::__m512i;
@@ -172,7 +172,7 @@ use crate::Rng;
/// ``` /// ```
/// # #![allow(dead_code)] /// # #![allow(dead_code)]
/// use rand::Rng; /// use rand::Rng;
/// use rand::distributions::{Distribution, Standard}; /// use rand::distr::{Distribution, Standard};
/// ///
/// struct MyF32 { /// struct MyF32 {
/// x: f32, /// x: f32,
@@ -188,7 +188,7 @@ use crate::Rng;
/// ## Example usage /// ## Example usage
/// ``` /// ```
/// use rand::prelude::*; /// use rand::prelude::*;
/// use rand::distributions::Standard; /// use rand::distr::Standard;
/// ///
/// let val: f32 = StdRng::from_os_rng().sample(Standard); /// let val: f32 = StdRng::from_os_rng().sample(Standard);
/// println!("f32 from [0, 1): {}", val); /// println!("f32 from [0, 1): {}", val);
@@ -14,8 +14,8 @@ use core::char;
use core::num::Wrapping; use core::num::Wrapping;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use crate::distributions::DistString; use crate::distr::DistString;
use crate::distributions::{Distribution, Standard, Uniform}; use crate::distr::{Distribution, Standard, Uniform};
use crate::Rng; use crate::Rng;
use core::mem::{self, MaybeUninit}; use core::mem::{self, MaybeUninit};
@@ -35,7 +35,7 @@ use serde::{Deserialize, Serialize};
/// ///
/// ``` /// ```
/// use rand::{Rng, thread_rng}; /// use rand::{Rng, thread_rng};
/// use rand::distributions::Alphanumeric; /// use rand::distr::Alphanumeric;
/// ///
/// let mut rng = thread_rng(); /// let mut rng = thread_rng();
/// let chars: String = (0..7).map(|_| rng.sample(Alphanumeric) as char).collect(); /// let chars: String = (0..7).map(|_| rng.sample(Alphanumeric) as char).collect();
@@ -45,7 +45,7 @@ use serde::{Deserialize, Serialize};
/// The [`DistString`] trait provides an easier method of generating /// The [`DistString`] trait provides an easier method of generating
/// a random `String`, and offers more efficient allocation: /// a random `String`, and offers more efficient allocation:
/// ``` /// ```
/// use rand::distributions::{Alphanumeric, DistString}; /// use rand::distr::{Alphanumeric, DistString};
/// let string = Alphanumeric.sample_string(&mut rand::thread_rng(), 16); /// let string = Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
/// println!("Random string: {}", string); /// println!("Random string: {}", string);
/// ``` /// ```
@@ -8,7 +8,7 @@
use core::num::NonZeroUsize; use core::num::NonZeroUsize;
use crate::distributions::{Distribution, Uniform}; use crate::distr::{Distribution, Uniform};
use crate::Rng; use crate::Rng;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use alloc::string::String; use alloc::string::String;
@@ -63,7 +63,7 @@ impl UniformUsize {
/// ///
/// ``` /// ```
/// use rand::Rng; /// use rand::Rng;
/// use rand::distributions::Slice; /// use rand::distr::Slice;
/// ///
/// let vowels = ['a', 'e', 'i', 'o', 'u']; /// let vowels = ['a', 'e', 'i', 'o', 'u'];
/// let vowels_dist = Slice::new(&vowels).unwrap(); /// let vowels_dist = Slice::new(&vowels).unwrap();
@@ -146,10 +146,7 @@ pub struct EmptySlice;
impl core::fmt::Display for EmptySlice { impl core::fmt::Display for EmptySlice {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!( write!(f, "Tried to create a `distr::Slice` with an empty slice")
f,
"Tried to create a `distributions::Slice` with an empty slice"
)
} }
} }
+581
View File
@@ -0,0 +1,581 @@
// Copyright 2018-2020 Developers of the Rand project.
// Copyright 2017 The Rust Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! A distribution uniformly sampling numbers within a given range.
//!
//! [`Uniform`] is the standard distribution to sample uniformly from a range;
//! e.g. `Uniform::new_inclusive(1, 6).unwrap()` can sample integers from 1 to 6, like a
//! standard die. [`Rng::gen_range`] supports any type supported by [`Uniform`].
//!
//! This distribution is provided with support for several primitive types
//! (all integer and floating-point types) as well as [`std::time::Duration`],
//! and supports extension to user-defined types via a type-specific *back-end*
//! implementation.
//!
//! The types [`UniformInt`], [`UniformFloat`] and [`UniformDuration`] are the
//! back-ends supporting sampling from primitive integer and floating-point
//! ranges as well as from [`std::time::Duration`]; these types do not normally
//! need to be used directly (unless implementing a derived back-end).
//!
//! # Example usage
//!
//! ```
//! use rand::{Rng, thread_rng};
//! use rand::distr::Uniform;
//!
//! let mut rng = thread_rng();
//! let side = Uniform::new(-10.0, 10.0).unwrap();
//!
//! // sample between 1 and 10 points
//! for _ in 0..rng.gen_range(1..=10) {
//! // sample a point from the square with sides -10 - 10 in two dimensions
//! let (x, y) = (rng.sample(side), rng.sample(side));
//! println!("Point: {}, {}", x, y);
//! }
//! ```
//!
//! # Extending `Uniform` to support a custom type
//!
//! To extend [`Uniform`] to support your own types, write a back-end which
//! implements the [`UniformSampler`] trait, then implement the [`SampleUniform`]
//! helper trait to "register" your back-end. See the `MyF32` example below.
//!
//! At a minimum, the back-end needs to store any parameters needed for sampling
//! (e.g. the target range) and implement `new`, `new_inclusive` and `sample`.
//! Those methods should include an assertion to check the range is valid (i.e.
//! `low < high`). The example below merely wraps another back-end.
//!
//! The `new`, `new_inclusive`, `sample_single` and `sample_single_inclusive`
//! functions use arguments of
//! type `SampleBorrow<X>` to support passing in values by reference or
//! by value. In the implementation of these functions, you can choose to
//! simply use the reference returned by [`SampleBorrow::borrow`], or you can choose
//! to copy or clone the value, whatever is appropriate for your type.
//!
//! ```
//! use rand::prelude::*;
//! use rand::distr::uniform::{Uniform, SampleUniform,
//! UniformSampler, UniformFloat, SampleBorrow, Error};
//!
//! struct MyF32(f32);
//!
//! #[derive(Clone, Copy, Debug)]
//! struct UniformMyF32(UniformFloat<f32>);
//!
//! impl UniformSampler for UniformMyF32 {
//! type X = MyF32;
//!
//! fn new<B1, B2>(low: B1, high: B2) -> Result<Self, Error>
//! where B1: SampleBorrow<Self::X> + Sized,
//! B2: SampleBorrow<Self::X> + Sized
//! {
//! UniformFloat::<f32>::new(low.borrow().0, high.borrow().0).map(UniformMyF32)
//! }
//! fn new_inclusive<B1, B2>(low: B1, high: B2) -> Result<Self, Error>
//! where B1: SampleBorrow<Self::X> + Sized,
//! B2: SampleBorrow<Self::X> + Sized
//! {
//! UniformFloat::<f32>::new_inclusive(low.borrow().0, high.borrow().0).map(UniformMyF32)
//! }
//! fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
//! MyF32(self.0.sample(rng))
//! }
//! }
//!
//! impl SampleUniform for MyF32 {
//! type Sampler = UniformMyF32;
//! }
//!
//! let (low, high) = (MyF32(17.0f32), MyF32(22.0f32));
//! let uniform = Uniform::new(low, high).unwrap();
//! let x = uniform.sample(&mut thread_rng());
//! ```
//!
//! [`SampleUniform`]: crate::distr::uniform::SampleUniform
//! [`UniformSampler`]: crate::distr::uniform::UniformSampler
//! [`UniformInt`]: crate::distr::uniform::UniformInt
//! [`UniformFloat`]: crate::distr::uniform::UniformFloat
//! [`UniformDuration`]: crate::distr::uniform::UniformDuration
//! [`SampleBorrow::borrow`]: crate::distr::uniform::SampleBorrow::borrow
#[path = "uniform_float.rs"]
mod float;
#[doc(inline)]
pub use float::UniformFloat;
#[path = "uniform_int.rs"]
mod int;
#[doc(inline)]
pub use int::UniformInt;
#[path = "uniform_other.rs"]
mod other;
#[doc(inline)]
pub use other::{UniformChar, UniformDuration};
use core::fmt;
use core::ops::{Range, RangeInclusive};
use crate::distr::Distribution;
use crate::{Rng, RngCore};
/// Error type returned from [`Uniform::new`] and `new_inclusive`.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
/// `low > high`, or equal in case of exclusive range.
EmptyRange,
/// Input or range `high - low` is non-finite. Not relevant to integer types.
NonFinite,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Error::EmptyRange => "low > high (or equal if exclusive) in uniform distribution",
Error::NonFinite => "Non-finite range in uniform distribution",
})
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
#[cfg(feature = "serde1")]
use serde::{Deserialize, Serialize};
/// Sample values uniformly between two bounds.
///
/// [`Uniform::new`] and [`Uniform::new_inclusive`] construct a uniform
/// distribution sampling from the given range; these functions may do extra
/// work up front to make sampling of multiple values faster. If only one sample
/// from the range is required, [`Rng::gen_range`] can be more efficient.
///
/// When sampling from a constant range, many calculations can happen at
/// compile-time and all methods should be fast; for floating-point ranges and
/// the full range of integer types, this should have comparable performance to
/// the `Standard` distribution.
///
/// Steps are taken to avoid bias, which might be present in naive
/// implementations; for example `rng.gen::<u8>() % 170` samples from the range
/// `[0, 169]` but is twice as likely to select numbers less than 85 than other
/// values. Further, the implementations here give more weight to the high-bits
/// generated by the RNG than the low bits, since with some RNGs the low-bits
/// are of lower quality than the high bits.
///
/// Implementations must sample in `[low, high)` range for
/// `Uniform::new(low, high)`, i.e., excluding `high`. In particular, care must
/// be taken to ensure that rounding never results values `< low` or `>= high`.
///
/// # Example
///
/// ```
/// use rand::distr::{Distribution, Uniform};
///
/// let between = Uniform::try_from(10..10000).unwrap();
/// let mut rng = rand::thread_rng();
/// let mut sum = 0;
/// for _ in 0..1000 {
/// sum += between.sample(&mut rng);
/// }
/// println!("{}", sum);
/// ```
///
/// For a single sample, [`Rng::gen_range`] may be preferred:
///
/// ```
/// use rand::Rng;
///
/// let mut rng = rand::thread_rng();
/// println!("{}", rng.gen_range(0..10));
/// ```
///
/// [`new`]: Uniform::new
/// [`new_inclusive`]: Uniform::new_inclusive
/// [`Rng::gen_range`]: Rng::gen_range
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde1", serde(bound(serialize = "X::Sampler: Serialize")))]
#[cfg_attr(
feature = "serde1",
serde(bound(deserialize = "X::Sampler: Deserialize<'de>"))
)]
pub struct Uniform<X: SampleUniform>(X::Sampler);
impl<X: SampleUniform> Uniform<X> {
/// Create a new `Uniform` instance, which samples uniformly from the half
/// open range `[low, high)` (excluding `high`).
///
/// For discrete types (e.g. integers), samples will always be strictly less
/// than `high`. For (approximations of) continuous types (e.g. `f32`, `f64`),
/// samples may equal `high` due to loss of precision but may not be
/// greater than `high`.
///
/// Fails if `low >= high`, or if `low`, `high` or the range `high - low` is
/// non-finite. In release mode, only the range is checked.
pub fn new<B1, B2>(low: B1, high: B2) -> Result<Uniform<X>, Error>
where
B1: SampleBorrow<X> + Sized,
B2: SampleBorrow<X> + Sized,
{
X::Sampler::new(low, high).map(Uniform)
}
/// Create a new `Uniform` instance, which samples uniformly from the closed
/// range `[low, high]` (inclusive).
///
/// Fails if `low > high`, or if `low`, `high` or the range `high - low` is
/// non-finite. In release mode, only the range is checked.
pub fn new_inclusive<B1, B2>(low: B1, high: B2) -> Result<Uniform<X>, Error>
where
B1: SampleBorrow<X> + Sized,
B2: SampleBorrow<X> + Sized,
{
X::Sampler::new_inclusive(low, high).map(Uniform)
}
}
impl<X: SampleUniform> Distribution<X> for Uniform<X> {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> X {
self.0.sample(rng)
}
}
/// Helper trait for creating objects using the correct implementation of
/// [`UniformSampler`] for the sampling type.
///
/// See the [module documentation] on how to implement [`Uniform`] range
/// sampling for a custom type.
///
/// [module documentation]: crate::distr::uniform
pub trait SampleUniform: Sized {
/// The `UniformSampler` implementation supporting type `X`.
type Sampler: UniformSampler<X = Self>;
}
/// Helper trait handling actual uniform sampling.
///
/// See the [module documentation] on how to implement [`Uniform`] range
/// sampling for a custom type.
///
/// Implementation of [`sample_single`] is optional, and is only useful when
/// the implementation can be faster than `Self::new(low, high).sample(rng)`.
///
/// [module documentation]: crate::distr::uniform
/// [`sample_single`]: UniformSampler::sample_single
pub trait UniformSampler: Sized {
/// The type sampled by this implementation.
type X;
/// Construct self, with inclusive lower bound and exclusive upper bound `[low, high)`.
///
/// For discrete types (e.g. integers), samples will always be strictly less
/// than `high`. For (approximations of) continuous types (e.g. `f32`, `f64`),
/// samples may equal `high` due to loss of precision but may not be
/// greater than `high`.
///
/// Usually users should not call this directly but prefer to use
/// [`Uniform::new`].
fn new<B1, B2>(low: B1, high: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized;
/// Construct self, with inclusive bounds `[low, high]`.
///
/// Usually users should not call this directly but prefer to use
/// [`Uniform::new_inclusive`].
fn new_inclusive<B1, B2>(low: B1, high: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized;
/// Sample a value.
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X;
/// Sample a single value uniformly from a range with inclusive lower bound
/// and exclusive upper bound `[low, high)`.
///
/// For discrete types (e.g. integers), samples will always be strictly less
/// than `high`. For (approximations of) continuous types (e.g. `f32`, `f64`),
/// samples may equal `high` due to loss of precision but may not be
/// greater than `high`.
///
/// By default this is implemented using
/// `UniformSampler::new(low, high).sample(rng)`. However, for some types
/// more optimal implementations for single usage may be provided via this
/// method (which is the case for integers and floats).
/// Results may not be identical.
///
/// Note that to use this method in a generic context, the type needs to be
/// retrieved via `SampleUniform::Sampler` as follows:
/// ```
/// use rand::{thread_rng, distr::uniform::{SampleUniform, UniformSampler}};
/// # #[allow(unused)]
/// fn sample_from_range<T: SampleUniform>(lb: T, ub: T) -> T {
/// let mut rng = thread_rng();
/// <T as SampleUniform>::Sampler::sample_single(lb, ub, &mut rng).unwrap()
/// }
/// ```
fn sample_single<R: Rng + ?Sized, B1, B2>(
low: B1,
high: B2,
rng: &mut R,
) -> Result<Self::X, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let uniform: Self = UniformSampler::new(low, high)?;
Ok(uniform.sample(rng))
}
/// Sample a single value uniformly from a range with inclusive lower bound
/// and inclusive upper bound `[low, high]`.
///
/// By default this is implemented using
/// `UniformSampler::new_inclusive(low, high).sample(rng)`. However, for
/// some types more optimal implementations for single usage may be provided
/// via this method.
/// Results may not be identical.
fn sample_single_inclusive<R: Rng + ?Sized, B1, B2>(
low: B1,
high: B2,
rng: &mut R,
) -> Result<Self::X, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let uniform: Self = UniformSampler::new_inclusive(low, high)?;
Ok(uniform.sample(rng))
}
}
impl<X: SampleUniform> TryFrom<Range<X>> for Uniform<X> {
type Error = Error;
fn try_from(r: Range<X>) -> Result<Uniform<X>, Error> {
Uniform::new(r.start, r.end)
}
}
impl<X: SampleUniform> TryFrom<RangeInclusive<X>> for Uniform<X> {
type Error = Error;
fn try_from(r: ::core::ops::RangeInclusive<X>) -> Result<Uniform<X>, Error> {
Uniform::new_inclusive(r.start(), r.end())
}
}
/// Helper trait similar to [`Borrow`] but implemented
/// only for [`SampleUniform`] and references to [`SampleUniform`]
/// in order to resolve ambiguity issues.
///
/// [`Borrow`]: std::borrow::Borrow
pub trait SampleBorrow<Borrowed> {
/// Immutably borrows from an owned value. See [`Borrow::borrow`]
///
/// [`Borrow::borrow`]: std::borrow::Borrow::borrow
fn borrow(&self) -> &Borrowed;
}
impl<Borrowed> SampleBorrow<Borrowed> for Borrowed
where
Borrowed: SampleUniform,
{
#[inline(always)]
fn borrow(&self) -> &Borrowed {
self
}
}
impl<'a, Borrowed> SampleBorrow<Borrowed> for &'a Borrowed
where
Borrowed: SampleUniform,
{
#[inline(always)]
fn borrow(&self) -> &Borrowed {
self
}
}
/// Range that supports generating a single sample efficiently.
///
/// Any type implementing this trait can be used to specify the sampled range
/// for `Rng::gen_range`.
pub trait SampleRange<T> {
/// Generate a sample from the given range.
fn sample_single<R: RngCore + ?Sized>(self, rng: &mut R) -> Result<T, Error>;
/// Check whether the range is empty.
fn is_empty(&self) -> bool;
}
impl<T: SampleUniform + PartialOrd> SampleRange<T> for Range<T> {
#[inline]
fn sample_single<R: RngCore + ?Sized>(self, rng: &mut R) -> Result<T, Error> {
T::Sampler::sample_single(self.start, self.end, rng)
}
#[inline]
fn is_empty(&self) -> bool {
!(self.start < self.end)
}
}
impl<T: SampleUniform + PartialOrd> SampleRange<T> for RangeInclusive<T> {
#[inline]
fn sample_single<R: RngCore + ?Sized>(self, rng: &mut R) -> Result<T, Error> {
T::Sampler::sample_single_inclusive(self.start(), self.end(), rng)
}
#[inline]
fn is_empty(&self) -> bool {
!(self.start() <= self.end())
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::time::Duration;
#[test]
#[cfg(feature = "serde1")]
fn test_uniform_serialization() {
let unit_box: Uniform<i32> = Uniform::new(-1, 1).unwrap();
let de_unit_box: Uniform<i32> =
bincode::deserialize(&bincode::serialize(&unit_box).unwrap()).unwrap();
assert_eq!(unit_box.0, de_unit_box.0);
let unit_box: Uniform<f32> = Uniform::new(-1., 1.).unwrap();
let de_unit_box: Uniform<f32> =
bincode::deserialize(&bincode::serialize(&unit_box).unwrap()).unwrap();
assert_eq!(unit_box.0, de_unit_box.0);
}
#[test]
fn test_custom_uniform() {
use crate::distr::uniform::{SampleBorrow, SampleUniform, UniformFloat, UniformSampler};
#[derive(Clone, Copy, PartialEq, PartialOrd)]
struct MyF32 {
x: f32,
}
#[derive(Clone, Copy, Debug)]
struct UniformMyF32(UniformFloat<f32>);
impl UniformSampler for UniformMyF32 {
type X = MyF32;
fn new<B1, B2>(low: B1, high: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
UniformFloat::<f32>::new(low.borrow().x, high.borrow().x).map(UniformMyF32)
}
fn new_inclusive<B1, B2>(low: B1, high: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
UniformSampler::new(low, high)
}
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
MyF32 {
x: self.0.sample(rng),
}
}
}
impl SampleUniform for MyF32 {
type Sampler = UniformMyF32;
}
let (low, high) = (MyF32 { x: 17.0f32 }, MyF32 { x: 22.0f32 });
let uniform = Uniform::new(low, high).unwrap();
let mut rng = crate::test::rng(804);
for _ in 0..100 {
let x: MyF32 = rng.sample(uniform);
assert!(low <= x && x < high);
}
}
#[test]
fn value_stability() {
fn test_samples<T: SampleUniform + Copy + fmt::Debug + PartialEq>(
lb: T,
ub: T,
expected_single: &[T],
expected_multiple: &[T],
) where
Uniform<T>: Distribution<T>,
{
let mut rng = crate::test::rng(897);
let mut buf = [lb; 3];
for x in &mut buf {
*x = T::Sampler::sample_single(lb, ub, &mut rng).unwrap();
}
assert_eq!(&buf, expected_single);
let distr = Uniform::new(lb, ub).unwrap();
for x in &mut buf {
*x = rng.sample(&distr);
}
assert_eq!(&buf, expected_multiple);
}
// We test on a sub-set of types; possibly we should do more.
// TODO: SIMD types
test_samples(11u8, 219, &[17, 66, 214], &[181, 93, 165]);
test_samples(11u32, 219, &[17, 66, 214], &[181, 93, 165]);
test_samples(
0f32,
1e-2f32,
&[0.0003070104, 0.0026630748, 0.00979833],
&[0.008194133, 0.00398172, 0.007428536],
);
test_samples(
-1e10f64,
1e10f64,
&[-4673848682.871551, 6388267422.932352, 4857075081.198343],
&[1173375212.1808167, 1917642852.109581, 2365076174.3153973],
);
test_samples(
Duration::new(2, 0),
Duration::new(4, 0),
&[
Duration::new(2, 532615131),
Duration::new(3, 638826742),
Duration::new(3, 485707508),
],
&[
Duration::new(3, 117337521),
Duration::new(3, 191764285),
Duration::new(3, 236507617),
],
);
}
#[test]
fn uniform_distributions_can_be_compared() {
assert_eq!(
Uniform::new(1.0, 2.0).unwrap(),
Uniform::new(1.0, 2.0).unwrap()
);
// To cover UniformInt
assert_eq!(
Uniform::new(1_u32, 2_u32).unwrap(),
Uniform::new(1_u32, 2_u32).unwrap()
);
}
}
+450
View File
@@ -0,0 +1,450 @@
// Copyright 2018-2020 Developers of the Rand project.
// Copyright 2017 The Rust Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! `UniformFloat` implementation
use super::{Error, SampleBorrow, SampleUniform, UniformSampler};
use crate::distr::float::IntoFloat;
use crate::distr::utils::{BoolAsSIMD, FloatAsSIMD, FloatSIMDUtils, IntAsSIMD};
use crate::Rng;
#[cfg(feature = "simd_support")]
use core::simd::prelude::*;
// #[cfg(feature = "simd_support")]
// use core::simd::{LaneCount, SupportedLaneCount};
#[cfg(feature = "serde1")]
use serde::{Deserialize, Serialize};
/// The back-end implementing [`UniformSampler`] for floating-point types.
///
/// Unless you are implementing [`UniformSampler`] for your own type, this type
/// should not be used directly, use [`Uniform`] instead.
///
/// # Implementation notes
///
/// Instead of generating a float in the `[0, 1)` range using [`Standard`], the
/// `UniformFloat` implementation converts the output of an PRNG itself. This
/// way one or two steps can be optimized out.
///
/// The floats are first converted to a value in the `[1, 2)` interval using a
/// transmute-based method, and then mapped to the expected range with a
/// multiply and addition. Values produced this way have what equals 23 bits of
/// random digits for an `f32`, and 52 for an `f64`.
///
/// [`new`]: UniformSampler::new
/// [`new_inclusive`]: UniformSampler::new_inclusive
/// [`Standard`]: crate::distr::Standard
/// [`Uniform`]: super::Uniform
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct UniformFloat<X> {
low: X,
scale: X,
}
macro_rules! uniform_float_impl {
($($meta:meta)?, $ty:ty, $uty:ident, $f_scalar:ident, $u_scalar:ident, $bits_to_discard:expr) => {
$(#[cfg($meta)])?
impl UniformFloat<$ty> {
/// Construct, reducing `scale` as required to ensure that rounding
/// can never yield values greater than `high`.
///
/// Note: though it may be tempting to use a variant of this method
/// to ensure that samples from `[low, high)` are always strictly
/// less than `high`, this approach may be very slow where
/// `scale.abs()` is much smaller than `high.abs()`
/// (example: `low=0.99999999997819644, high=1.`).
fn new_bounded(low: $ty, high: $ty, mut scale: $ty) -> Self {
let max_rand = <$ty>::splat(1.0 as $f_scalar - $f_scalar::EPSILON);
loop {
let mask = (scale * max_rand + low).gt_mask(high);
if !mask.any() {
break;
}
scale = scale.decrease_masked(mask);
}
debug_assert!(<$ty>::splat(0.0).all_le(scale));
UniformFloat { low, scale }
}
}
$(#[cfg($meta)])?
impl SampleUniform for $ty {
type Sampler = UniformFloat<$ty>;
}
$(#[cfg($meta)])?
impl UniformSampler for UniformFloat<$ty> {
type X = $ty;
fn new<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
#[cfg(debug_assertions)]
if !(low.all_finite()) || !(high.all_finite()) {
return Err(Error::NonFinite);
}
if !(low.all_lt(high)) {
return Err(Error::EmptyRange);
}
let scale = high - low;
if !(scale.all_finite()) {
return Err(Error::NonFinite);
}
Ok(Self::new_bounded(low, high, scale))
}
fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
#[cfg(debug_assertions)]
if !(low.all_finite()) || !(high.all_finite()) {
return Err(Error::NonFinite);
}
if !low.all_le(high) {
return Err(Error::EmptyRange);
}
let max_rand = <$ty>::splat(1.0 as $f_scalar - $f_scalar::EPSILON);
let scale = (high - low) / max_rand;
if !scale.all_finite() {
return Err(Error::NonFinite);
}
Ok(Self::new_bounded(low, high, scale))
}
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
// Generate a value in the range [1, 2)
let value1_2 = (rng.random::<$uty>() >> $uty::splat($bits_to_discard)).into_float_with_exponent(0);
// Get a value in the range [0, 1) to avoid overflow when multiplying by scale
let value0_1 = value1_2 - <$ty>::splat(1.0);
// We don't use `f64::mul_add`, because it is not available with
// `no_std`. Furthermore, it is slower for some targets (but
// faster for others). However, the order of multiplication and
// addition is important, because on some platforms (e.g. ARM)
// it will be optimized to a single (non-FMA) instruction.
value0_1 * self.scale + self.low
}
#[inline]
fn sample_single<R: Rng + ?Sized, B1, B2>(low_b: B1, high_b: B2, rng: &mut R) -> Result<Self::X, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
Self::sample_single_inclusive(low_b, high_b, rng)
}
#[inline]
fn sample_single_inclusive<R: Rng + ?Sized, B1, B2>(low_b: B1, high_b: B2, rng: &mut R) -> Result<Self::X, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
#[cfg(debug_assertions)]
if !low.all_finite() || !high.all_finite() {
return Err(Error::NonFinite);
}
if !low.all_le(high) {
return Err(Error::EmptyRange);
}
let scale = high - low;
if !scale.all_finite() {
return Err(Error::NonFinite);
}
// Generate a value in the range [1, 2)
let value1_2 =
(rng.random::<$uty>() >> $uty::splat($bits_to_discard)).into_float_with_exponent(0);
// Get a value in the range [0, 1) to avoid overflow when multiplying by scale
let value0_1 = value1_2 - <$ty>::splat(1.0);
// Doing multiply before addition allows some architectures
// to use a single instruction.
Ok(value0_1 * scale + low)
}
}
};
}
uniform_float_impl! { , f32, u32, f32, u32, 32 - 23 }
uniform_float_impl! { , f64, u64, f64, u64, 64 - 52 }
#[cfg(feature = "simd_support")]
uniform_float_impl! { feature = "simd_support", f32x2, u32x2, f32, u32, 32 - 23 }
#[cfg(feature = "simd_support")]
uniform_float_impl! { feature = "simd_support", f32x4, u32x4, f32, u32, 32 - 23 }
#[cfg(feature = "simd_support")]
uniform_float_impl! { feature = "simd_support", f32x8, u32x8, f32, u32, 32 - 23 }
#[cfg(feature = "simd_support")]
uniform_float_impl! { feature = "simd_support", f32x16, u32x16, f32, u32, 32 - 23 }
#[cfg(feature = "simd_support")]
uniform_float_impl! { feature = "simd_support", f64x2, u64x2, f64, u64, 64 - 52 }
#[cfg(feature = "simd_support")]
uniform_float_impl! { feature = "simd_support", f64x4, u64x4, f64, u64, 64 - 52 }
#[cfg(feature = "simd_support")]
uniform_float_impl! { feature = "simd_support", f64x8, u64x8, f64, u64, 64 - 52 }
#[cfg(test)]
mod tests {
use super::*;
use crate::distr::{utils::FloatSIMDScalarUtils, Uniform};
use crate::rngs::mock::StepRng;
#[test]
#[cfg_attr(miri, ignore)] // Miri is too slow
fn test_floats() {
let mut rng = crate::test::rng(252);
let mut zero_rng = StepRng::new(0, 0);
let mut max_rng = StepRng::new(0xffff_ffff_ffff_ffff, 0);
macro_rules! t {
($ty:ty, $f_scalar:ident, $bits_shifted:expr) => {{
let v: &[($f_scalar, $f_scalar)] = &[
(0.0, 100.0),
(-1e35, -1e25),
(1e-35, 1e-25),
(-1e35, 1e35),
(<$f_scalar>::from_bits(0), <$f_scalar>::from_bits(3)),
(-<$f_scalar>::from_bits(10), -<$f_scalar>::from_bits(1)),
(-<$f_scalar>::from_bits(5), 0.0),
(-<$f_scalar>::from_bits(7), -0.0),
(0.1 * $f_scalar::MAX, $f_scalar::MAX),
(-$f_scalar::MAX * 0.2, $f_scalar::MAX * 0.7),
];
for &(low_scalar, high_scalar) in v.iter() {
for lane in 0..<$ty>::LEN {
let low = <$ty>::splat(0.0 as $f_scalar).replace(lane, low_scalar);
let high = <$ty>::splat(1.0 as $f_scalar).replace(lane, high_scalar);
let my_uniform = Uniform::new(low, high).unwrap();
let my_incl_uniform = Uniform::new_inclusive(low, high).unwrap();
for _ in 0..100 {
let v = rng.sample(my_uniform).extract(lane);
assert!(low_scalar <= v && v <= high_scalar);
let v = rng.sample(my_incl_uniform).extract(lane);
assert!(low_scalar <= v && v <= high_scalar);
let v =
<$ty as SampleUniform>::Sampler::sample_single(low, high, &mut rng)
.unwrap()
.extract(lane);
assert!(low_scalar <= v && v <= high_scalar);
let v = <$ty as SampleUniform>::Sampler::sample_single_inclusive(
low, high, &mut rng,
)
.unwrap()
.extract(lane);
assert!(low_scalar <= v && v <= high_scalar);
}
assert_eq!(
rng.sample(Uniform::new_inclusive(low, low).unwrap())
.extract(lane),
low_scalar
);
assert_eq!(zero_rng.sample(my_uniform).extract(lane), low_scalar);
assert_eq!(zero_rng.sample(my_incl_uniform).extract(lane), low_scalar);
assert_eq!(
<$ty as SampleUniform>::Sampler::sample_single(
low,
high,
&mut zero_rng
)
.unwrap()
.extract(lane),
low_scalar
);
assert_eq!(
<$ty as SampleUniform>::Sampler::sample_single_inclusive(
low,
high,
&mut zero_rng
)
.unwrap()
.extract(lane),
low_scalar
);
assert!(max_rng.sample(my_uniform).extract(lane) <= high_scalar);
assert!(max_rng.sample(my_incl_uniform).extract(lane) <= high_scalar);
// sample_single cannot cope with max_rng:
// assert!(<$ty as SampleUniform>::Sampler
// ::sample_single(low, high, &mut max_rng).unwrap()
// .extract(lane) <= high_scalar);
assert!(
<$ty as SampleUniform>::Sampler::sample_single_inclusive(
low,
high,
&mut max_rng
)
.unwrap()
.extract(lane)
<= high_scalar
);
// Don't run this test for really tiny differences between high and low
// since for those rounding might result in selecting high for a very
// long time.
if (high_scalar - low_scalar) > 0.0001 {
let mut lowering_max_rng = StepRng::new(
0xffff_ffff_ffff_ffff,
(-1i64 << $bits_shifted) as u64,
);
assert!(
<$ty as SampleUniform>::Sampler::sample_single(
low,
high,
&mut lowering_max_rng
)
.unwrap()
.extract(lane)
<= high_scalar
);
}
}
}
assert_eq!(
rng.sample(Uniform::new_inclusive($f_scalar::MAX, $f_scalar::MAX).unwrap()),
$f_scalar::MAX
);
assert_eq!(
rng.sample(Uniform::new_inclusive(-$f_scalar::MAX, -$f_scalar::MAX).unwrap()),
-$f_scalar::MAX
);
}};
}
t!(f32, f32, 32 - 23);
t!(f64, f64, 64 - 52);
#[cfg(feature = "simd_support")]
{
t!(f32x2, f32, 32 - 23);
t!(f32x4, f32, 32 - 23);
t!(f32x8, f32, 32 - 23);
t!(f32x16, f32, 32 - 23);
t!(f64x2, f64, 64 - 52);
t!(f64x4, f64, 64 - 52);
t!(f64x8, f64, 64 - 52);
}
}
#[test]
fn test_float_overflow() {
assert_eq!(Uniform::try_from(f64::MIN..f64::MAX), Err(Error::NonFinite));
}
#[test]
#[should_panic]
fn test_float_overflow_single() {
let mut rng = crate::test::rng(252);
rng.gen_range(f64::MIN..f64::MAX);
}
#[test]
#[cfg(all(feature = "std", panic = "unwind"))]
fn test_float_assertions() {
use super::SampleUniform;
fn range<T: SampleUniform>(low: T, high: T) -> Result<T, Error> {
let mut rng = crate::test::rng(253);
T::Sampler::sample_single(low, high, &mut rng)
}
macro_rules! t {
($ty:ident, $f_scalar:ident) => {{
let v: &[($f_scalar, $f_scalar)] = &[
($f_scalar::NAN, 0.0),
(1.0, $f_scalar::NAN),
($f_scalar::NAN, $f_scalar::NAN),
(1.0, 0.5),
($f_scalar::MAX, -$f_scalar::MAX),
($f_scalar::INFINITY, $f_scalar::INFINITY),
($f_scalar::NEG_INFINITY, $f_scalar::NEG_INFINITY),
($f_scalar::NEG_INFINITY, 5.0),
(5.0, $f_scalar::INFINITY),
($f_scalar::NAN, $f_scalar::INFINITY),
($f_scalar::NEG_INFINITY, $f_scalar::NAN),
($f_scalar::NEG_INFINITY, $f_scalar::INFINITY),
];
for &(low_scalar, high_scalar) in v.iter() {
for lane in 0..<$ty>::LEN {
let low = <$ty>::splat(0.0 as $f_scalar).replace(lane, low_scalar);
let high = <$ty>::splat(1.0 as $f_scalar).replace(lane, high_scalar);
assert!(range(low, high).is_err());
assert!(Uniform::new(low, high).is_err());
assert!(Uniform::new_inclusive(low, high).is_err());
assert!(Uniform::new(low, low).is_err());
}
}
}};
}
t!(f32, f32);
t!(f64, f64);
#[cfg(feature = "simd_support")]
{
t!(f32x2, f32);
t!(f32x4, f32);
t!(f32x8, f32);
t!(f32x16, f32);
t!(f64x2, f64);
t!(f64x4, f64);
t!(f64x8, f64);
}
}
#[test]
fn test_uniform_from_std_range() {
let r = Uniform::try_from(2.0f64..7.0).unwrap();
assert_eq!(r.0.low, 2.0);
assert_eq!(r.0.scale, 5.0);
}
#[test]
fn test_uniform_from_std_range_bad_limits() {
#![allow(clippy::reversed_empty_ranges)]
assert!(Uniform::try_from(100.0..10.0).is_err());
assert!(Uniform::try_from(100.0..100.0).is_err());
}
#[test]
fn test_uniform_from_std_range_inclusive() {
let r = Uniform::try_from(2.0f64..=7.0).unwrap();
assert_eq!(r.0.low, 2.0);
assert!(r.0.scale > 5.0);
assert!(r.0.scale < 5.0 + 1e-14);
}
#[test]
fn test_uniform_from_std_range_inclusive_bad_limits() {
#![allow(clippy::reversed_empty_ranges)]
assert!(Uniform::try_from(100.0..=10.0).is_err());
assert!(Uniform::try_from(100.0..=99.0).is_err());
}
}
+521
View File
@@ -0,0 +1,521 @@
// Copyright 2018-2020 Developers of the Rand project.
// Copyright 2017 The Rust Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! `UniformInt` implementation
use super::{Error, SampleBorrow, SampleUniform, UniformSampler};
use crate::distr::utils::WideningMultiply;
#[cfg(feature = "simd_support")]
use crate::distr::{Distribution, Standard};
use crate::Rng;
#[cfg(feature = "simd_support")]
use core::simd::prelude::*;
#[cfg(feature = "simd_support")]
use core::simd::{LaneCount, SupportedLaneCount};
#[cfg(feature = "serde1")]
use serde::{Deserialize, Serialize};
/// The back-end implementing [`UniformSampler`] for integer types.
///
/// Unless you are implementing [`UniformSampler`] for your own type, this type
/// should not be used directly, use [`Uniform`] instead.
///
/// # Implementation notes
///
/// For simplicity, we use the same generic struct `UniformInt<X>` for all
/// integer types `X`. This gives us only one field type, `X`; to store unsigned
/// values of this size, we take use the fact that these conversions are no-ops.
///
/// For a closed range, the number of possible numbers we should generate is
/// `range = (high - low + 1)`. To avoid bias, we must ensure that the size of
/// our sample space, `zone`, is a multiple of `range`; other values must be
/// rejected (by replacing with a new random sample).
///
/// As a special case, we use `range = 0` to represent the full range of the
/// result type (i.e. for `new_inclusive($ty::MIN, $ty::MAX)`).
///
/// The optimum `zone` is the largest product of `range` which fits in our
/// (unsigned) target type. We calculate this by calculating how many numbers we
/// must reject: `reject = (MAX + 1) % range = (MAX - range + 1) % range`. Any (large)
/// product of `range` will suffice, thus in `sample_single` we multiply by a
/// power of 2 via bit-shifting (faster but may cause more rejections).
///
/// The smallest integer PRNGs generate is `u32`. For 8- and 16-bit outputs we
/// use `u32` for our `zone` and samples (because it's not slower and because
/// it reduces the chance of having to reject a sample). In this case we cannot
/// store `zone` in the target type since it is too large, however we know
/// `ints_to_reject < range <= $uty::MAX`.
///
/// An alternative to using a modulus is widening multiply: After a widening
/// multiply by `range`, the result is in the high word. Then comparing the low
/// word against `zone` makes sure our distribution is uniform.
///
/// [`Uniform`]: super::Uniform
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct UniformInt<X> {
pub(super) low: X,
pub(super) range: X,
thresh: X, // effectively 2.pow(max(64, uty_bits)) % range
}
macro_rules! uniform_int_impl {
($ty:ty, $uty:ty, $sample_ty:ident) => {
impl SampleUniform for $ty {
type Sampler = UniformInt<$ty>;
}
impl UniformSampler for UniformInt<$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 $uty to be
// "bit-equal", so casting between them is a no-op.
type X = $ty;
#[inline] // if the range is constant, this helps LLVM to do the
// calculations at compile-time.
fn new<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
if !(low < high) {
return Err(Error::EmptyRange);
}
UniformSampler::new_inclusive(low, high - 1)
}
#[inline] // if the range is constant, this helps LLVM to do the
// calculations at compile-time.
fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
if !(low <= high) {
return Err(Error::EmptyRange);
}
let range = high.wrapping_sub(low).wrapping_add(1) as $uty;
let thresh = if range > 0 {
let range = $sample_ty::from(range);
(range.wrapping_neg() % range)
} else {
0
};
Ok(UniformInt {
low,
range: range as $ty, // type: $uty
thresh: thresh as $uty as $ty, // type: $sample_ty
})
}
/// Sample from distribution, Lemire's method, unbiased
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
let range = self.range as $uty as $sample_ty;
if range == 0 {
return rng.random();
}
let thresh = self.thresh as $uty as $sample_ty;
let hi = loop {
let (hi, lo) = rng.random::<$sample_ty>().wmul(range);
if lo >= thresh {
break hi;
}
};
self.low.wrapping_add(hi as $ty)
}
#[inline]
fn sample_single<R: Rng + ?Sized, B1, B2>(
low_b: B1,
high_b: B2,
rng: &mut R,
) -> Result<Self::X, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
if !(low < high) {
return Err(Error::EmptyRange);
}
Self::sample_single_inclusive(low, high - 1, rng)
}
/// Sample single value, Canon's method, biased
///
/// In the worst case, bias affects 1 in `2^n` samples where n is
/// 56 (`i8`), 48 (`i16`), 96 (`i32`), 64 (`i64`), 128 (`i128`).
#[cfg(not(feature = "unbiased"))]
#[inline]
fn sample_single_inclusive<R: Rng + ?Sized, B1, B2>(
low_b: B1,
high_b: B2,
rng: &mut R,
) -> Result<Self::X, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
if !(low <= high) {
return Err(Error::EmptyRange);
}
let range = high.wrapping_sub(low).wrapping_add(1) as $uty as $sample_ty;
if range == 0 {
// Range is MAX+1 (unrepresentable), so we need a special case
return Ok(rng.random());
}
// generate a sample using a sensible integer type
let (mut result, lo_order) = rng.random::<$sample_ty>().wmul(range);
// if the sample is biased...
if lo_order > range.wrapping_neg() {
// ...generate a new sample to reduce bias...
let (new_hi_order, _) = (rng.random::<$sample_ty>()).wmul(range as $sample_ty);
// ... incrementing result on overflow
let is_overflow = lo_order.checked_add(new_hi_order as $sample_ty).is_none();
result += is_overflow as $sample_ty;
}
Ok(low.wrapping_add(result as $ty))
}
/// Sample single value, Canon's method, unbiased
#[cfg(feature = "unbiased")]
#[inline]
fn sample_single_inclusive<R: Rng + ?Sized, B1, B2>(
low_b: B1,
high_b: B2,
rng: &mut R,
) -> Result<Self::X, Error>
where
B1: SampleBorrow<$ty> + Sized,
B2: SampleBorrow<$ty> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
if !(low <= high) {
return Err(Error::EmptyRange);
}
let range = high.wrapping_sub(low).wrapping_add(1) as $uty as $sample_ty;
if range == 0 {
// Range is MAX+1 (unrepresentable), so we need a special case
return Ok(rng.random());
}
let (mut result, mut lo) = rng.random::<$sample_ty>().wmul(range);
// In contrast to the biased sampler, we use a loop:
while lo > range.wrapping_neg() {
let (new_hi, new_lo) = (rng.random::<$sample_ty>()).wmul(range);
match lo.checked_add(new_hi) {
Some(x) if x < $sample_ty::MAX => {
// Anything less than MAX: last term is 0
break;
}
None => {
// Overflow: last term is 1
result += 1;
break;
}
_ => {
// Unlikely case: must check next sample
lo = new_lo;
continue;
}
}
}
Ok(low.wrapping_add(result as $ty))
}
}
};
}
uniform_int_impl! { i8, u8, u32 }
uniform_int_impl! { i16, u16, u32 }
uniform_int_impl! { i32, u32, u32 }
uniform_int_impl! { i64, u64, u64 }
uniform_int_impl! { i128, u128, u128 }
uniform_int_impl! { isize, usize, usize }
uniform_int_impl! { u8, u8, u32 }
uniform_int_impl! { u16, u16, u32 }
uniform_int_impl! { u32, u32, u32 }
uniform_int_impl! { u64, u64, u64 }
uniform_int_impl! { usize, usize, usize }
uniform_int_impl! { u128, u128, u128 }
#[cfg(feature = "simd_support")]
macro_rules! uniform_simd_int_impl {
($ty:ident, $unsigned:ident) => {
// The "pick the largest zone that can fit in an `u32`" optimization
// is less useful here. Multiple lanes complicate things, we don't
// know the PRNG's minimal output size, and casting to a larger vector
// is generally a bad idea for SIMD performance. The user can still
// implement it manually.
#[cfg(feature = "simd_support")]
impl<const LANES: usize> SampleUniform for Simd<$ty, LANES>
where
LaneCount<LANES>: SupportedLaneCount,
Simd<$unsigned, LANES>:
WideningMultiply<Output = (Simd<$unsigned, LANES>, Simd<$unsigned, LANES>)>,
Standard: Distribution<Simd<$unsigned, LANES>>,
{
type Sampler = UniformInt<Simd<$ty, LANES>>;
}
#[cfg(feature = "simd_support")]
impl<const LANES: usize> UniformSampler for UniformInt<Simd<$ty, LANES>>
where
LaneCount<LANES>: SupportedLaneCount,
Simd<$unsigned, LANES>:
WideningMultiply<Output = (Simd<$unsigned, LANES>, Simd<$unsigned, LANES>)>,
Standard: Distribution<Simd<$unsigned, LANES>>,
{
type X = Simd<$ty, LANES>;
#[inline] // if the range is constant, this helps LLVM to do the
// calculations at compile-time.
fn new<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized
{
let low = *low_b.borrow();
let high = *high_b.borrow();
if !(low.simd_lt(high).all()) {
return Err(Error::EmptyRange);
}
UniformSampler::new_inclusive(low, high - Simd::splat(1))
}
#[inline] // if the range is constant, this helps LLVM to do the
// calculations at compile-time.
fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized
{
let low = *low_b.borrow();
let high = *high_b.borrow();
if !(low.simd_le(high).all()) {
return Err(Error::EmptyRange);
}
// NOTE: all `Simd` operations are inherently wrapping,
// see https://doc.rust-lang.org/std/simd/struct.Simd.html
let range: Simd<$unsigned, LANES> = ((high - low) + Simd::splat(1)).cast();
// We must avoid divide-by-zero by using 0 % 1 == 0.
let not_full_range = range.simd_gt(Simd::splat(0));
let modulo = not_full_range.select(range, Simd::splat(1));
let ints_to_reject = range.wrapping_neg() % modulo;
Ok(UniformInt {
low,
// These are really $unsigned values, but store as $ty:
range: range.cast(),
thresh: ints_to_reject.cast(),
})
}
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
let range: Simd<$unsigned, LANES> = self.range.cast();
let thresh: Simd<$unsigned, LANES> = self.thresh.cast();
// This might seem very slow, generating a whole new
// SIMD vector for every sample rejection. For most uses
// though, the chance of rejection is small and provides good
// general performance. With multiple lanes, that chance is
// multiplied. To mitigate this, we replace only the lanes of
// the vector which fail, iteratively reducing the chance of
// rejection. The replacement method does however add a little
// overhead. Benchmarking or calculating probabilities might
// reveal contexts where this replacement method is slower.
let mut v: Simd<$unsigned, LANES> = rng.random();
loop {
let (hi, lo) = v.wmul(range);
let mask = lo.simd_ge(thresh);
if mask.all() {
let hi: Simd<$ty, LANES> = hi.cast();
// wrapping addition
let result = self.low + hi;
// `select` here compiles to a blend operation
// When `range.eq(0).none()` the compare and blend
// operations are avoided.
let v: Simd<$ty, LANES> = v.cast();
return range.simd_gt(Simd::splat(0)).select(result, v);
}
// Replace only the failing lanes
v = mask.select(v, rng.random());
}
}
}
};
// bulk implementation
($(($unsigned:ident, $signed:ident)),+) => {
$(
uniform_simd_int_impl!($unsigned, $unsigned);
uniform_simd_int_impl!($signed, $unsigned);
)+
};
}
#[cfg(feature = "simd_support")]
uniform_simd_int_impl! { (u8, i8), (u16, i16), (u32, i32), (u64, i64) }
#[cfg(test)]
mod tests {
use super::*;
use crate::distr::Uniform;
#[test]
fn test_uniform_bad_limits_equal_int() {
assert_eq!(Uniform::new(10, 10), Err(Error::EmptyRange));
}
#[test]
fn test_uniform_good_limits_equal_int() {
let mut rng = crate::test::rng(804);
let dist = Uniform::new_inclusive(10, 10).unwrap();
for _ in 0..20 {
assert_eq!(rng.sample(dist), 10);
}
}
#[test]
fn test_uniform_bad_limits_flipped_int() {
assert_eq!(Uniform::new(10, 5), Err(Error::EmptyRange));
}
#[test]
#[cfg_attr(miri, ignore)] // Miri is too slow
fn test_integers() {
let mut rng = crate::test::rng(251);
macro_rules! t {
($ty:ident, $v:expr, $le:expr, $lt:expr) => {{
for &(low, high) in $v.iter() {
let my_uniform = Uniform::new(low, high).unwrap();
for _ in 0..1000 {
let v: $ty = rng.sample(my_uniform);
assert!($le(low, v) && $lt(v, high));
}
let my_uniform = Uniform::new_inclusive(low, high).unwrap();
for _ in 0..1000 {
let v: $ty = rng.sample(my_uniform);
assert!($le(low, v) && $le(v, high));
}
let my_uniform = Uniform::new(&low, high).unwrap();
for _ in 0..1000 {
let v: $ty = rng.sample(my_uniform);
assert!($le(low, v) && $lt(v, high));
}
let my_uniform = Uniform::new_inclusive(&low, &high).unwrap();
for _ in 0..1000 {
let v: $ty = rng.sample(my_uniform);
assert!($le(low, v) && $le(v, high));
}
for _ in 0..1000 {
let v = <$ty as SampleUniform>::Sampler::sample_single(low, high, &mut rng).unwrap();
assert!($le(low, v) && $lt(v, high));
}
for _ in 0..1000 {
let v = <$ty as SampleUniform>::Sampler::sample_single_inclusive(low, high, &mut rng).unwrap();
assert!($le(low, v) && $le(v, high));
}
}
}};
// scalar bulk
($($ty:ident),*) => {{
$(t!(
$ty,
[(0, 10), (10, 127), ($ty::MIN, $ty::MAX)],
|x, y| x <= y,
|x, y| x < y
);)*
}};
// simd bulk
($($ty:ident),* => $scalar:ident) => {{
$(t!(
$ty,
[
($ty::splat(0), $ty::splat(10)),
($ty::splat(10), $ty::splat(127)),
($ty::splat($scalar::MIN), $ty::splat($scalar::MAX)),
],
|x: $ty, y| x.simd_le(y).all(),
|x: $ty, y| x.simd_lt(y).all()
);)*
}};
}
t!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, i128, u128);
#[cfg(feature = "simd_support")]
{
t!(u8x4, u8x8, u8x16, u8x32, u8x64 => u8);
t!(i8x4, i8x8, i8x16, i8x32, i8x64 => i8);
t!(u16x2, u16x4, u16x8, u16x16, u16x32 => u16);
t!(i16x2, i16x4, i16x8, i16x16, i16x32 => i16);
t!(u32x2, u32x4, u32x8, u32x16 => u32);
t!(i32x2, i32x4, i32x8, i32x16 => i32);
t!(u64x2, u64x4, u64x8 => u64);
t!(i64x2, i64x4, i64x8 => i64);
}
}
#[test]
fn test_uniform_from_std_range() {
let r = Uniform::try_from(2u32..7).unwrap();
assert_eq!(r.0.low, 2);
assert_eq!(r.0.range, 5);
}
#[test]
fn test_uniform_from_std_range_bad_limits() {
#![allow(clippy::reversed_empty_ranges)]
assert!(Uniform::try_from(100..10).is_err());
assert!(Uniform::try_from(100..100).is_err());
}
#[test]
fn test_uniform_from_std_range_inclusive() {
let r = Uniform::try_from(2u32..=6).unwrap();
assert_eq!(r.0.low, 2);
assert_eq!(r.0.range, 5);
}
#[test]
fn test_uniform_from_std_range_inclusive_bad_limits() {
#![allow(clippy::reversed_empty_ranges)]
assert!(Uniform::try_from(100..=10).is_err());
assert!(Uniform::try_from(100..=99).is_err());
}
}
+322
View File
@@ -0,0 +1,322 @@
// Copyright 2018-2020 Developers of the Rand project.
// Copyright 2017 The Rust Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! `UniformChar`, `UniformDuration` implementations
use super::{Error, SampleBorrow, SampleUniform, Uniform, UniformInt, UniformSampler};
use crate::distr::Distribution;
use crate::Rng;
use core::time::Duration;
#[cfg(feature = "serde1")]
use serde::{Deserialize, Serialize};
impl SampleUniform for char {
type Sampler = UniformChar;
}
/// The back-end implementing [`UniformSampler`] for `char`.
///
/// Unless you are implementing [`UniformSampler`] for your own type, this type
/// should not be used directly, use [`Uniform`] instead.
///
/// This differs from integer range sampling since the range `0xD800..=0xDFFF`
/// are used for surrogate pairs in UCS and UTF-16, and consequently are not
/// valid Unicode code points. We must therefore avoid sampling values in this
/// range.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct UniformChar {
sampler: UniformInt<u32>,
}
/// UTF-16 surrogate range start
const CHAR_SURROGATE_START: u32 = 0xD800;
/// UTF-16 surrogate range size
const CHAR_SURROGATE_LEN: u32 = 0xE000 - CHAR_SURROGATE_START;
/// Convert `char` to compressed `u32`
fn char_to_comp_u32(c: char) -> u32 {
match c as u32 {
c if c >= CHAR_SURROGATE_START => c - CHAR_SURROGATE_LEN,
c => c,
}
}
impl UniformSampler for UniformChar {
type X = char;
#[inline] // if the range is constant, this helps LLVM to do the
// calculations at compile-time.
fn new<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = char_to_comp_u32(*low_b.borrow());
let high = char_to_comp_u32(*high_b.borrow());
let sampler = UniformInt::<u32>::new(low, high);
sampler.map(|sampler| UniformChar { sampler })
}
#[inline] // if the range is constant, this helps LLVM to do the
// calculations at compile-time.
fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = char_to_comp_u32(*low_b.borrow());
let high = char_to_comp_u32(*high_b.borrow());
let sampler = UniformInt::<u32>::new_inclusive(low, high);
sampler.map(|sampler| UniformChar { sampler })
}
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
let mut x = self.sampler.sample(rng);
if x >= CHAR_SURROGATE_START {
x += CHAR_SURROGATE_LEN;
}
// SAFETY: x must not be in surrogate range or greater than char::MAX.
// This relies on range constructors which accept char arguments.
// Validity of input char values is assumed.
unsafe { core::char::from_u32_unchecked(x) }
}
}
/// Note: the `String` is potentially left with excess capacity if the range
/// includes non ascii chars; optionally the user may call
/// `string.shrink_to_fit()` afterwards.
#[cfg(feature = "alloc")]
impl crate::distr::DistString for Uniform<char> {
fn append_string<R: Rng + ?Sized>(
&self,
rng: &mut R,
string: &mut alloc::string::String,
len: usize,
) {
// Getting the hi value to assume the required length to reserve in string.
let mut hi = self.0.sampler.low + self.0.sampler.range - 1;
if hi >= CHAR_SURROGATE_START {
hi += CHAR_SURROGATE_LEN;
}
// Get the utf8 length of hi to minimize extra space.
let max_char_len = char::from_u32(hi).map(char::len_utf8).unwrap_or(4);
string.reserve(max_char_len * len);
string.extend(self.sample_iter(rng).take(len))
}
}
/// The back-end implementing [`UniformSampler`] for `Duration`.
///
/// Unless you are implementing [`UniformSampler`] for your own types, this type
/// should not be used directly, use [`Uniform`] instead.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct UniformDuration {
mode: UniformDurationMode,
offset: u32,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
enum UniformDurationMode {
Small {
secs: u64,
nanos: Uniform<u32>,
},
Medium {
nanos: Uniform<u64>,
},
Large {
max_secs: u64,
max_nanos: u32,
secs: Uniform<u64>,
},
}
impl SampleUniform for Duration {
type Sampler = UniformDuration;
}
impl UniformSampler for UniformDuration {
type X = Duration;
#[inline]
fn new<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
if !(low < high) {
return Err(Error::EmptyRange);
}
UniformDuration::new_inclusive(low, high - Duration::new(0, 1))
}
#[inline]
fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
if !(low <= high) {
return Err(Error::EmptyRange);
}
let low_s = low.as_secs();
let low_n = low.subsec_nanos();
let mut high_s = high.as_secs();
let mut high_n = high.subsec_nanos();
if high_n < low_n {
high_s -= 1;
high_n += 1_000_000_000;
}
let mode = if low_s == high_s {
UniformDurationMode::Small {
secs: low_s,
nanos: Uniform::new_inclusive(low_n, high_n)?,
}
} else {
let max = high_s
.checked_mul(1_000_000_000)
.and_then(|n| n.checked_add(u64::from(high_n)));
if let Some(higher_bound) = max {
let lower_bound = low_s * 1_000_000_000 + u64::from(low_n);
UniformDurationMode::Medium {
nanos: Uniform::new_inclusive(lower_bound, higher_bound)?,
}
} else {
// An offset is applied to simplify generation of nanoseconds
let max_nanos = high_n - low_n;
UniformDurationMode::Large {
max_secs: high_s,
max_nanos,
secs: Uniform::new_inclusive(low_s, high_s)?,
}
}
};
Ok(UniformDuration {
mode,
offset: low_n,
})
}
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Duration {
match self.mode {
UniformDurationMode::Small { secs, nanos } => {
let n = nanos.sample(rng);
Duration::new(secs, n)
}
UniformDurationMode::Medium { nanos } => {
let nanos = nanos.sample(rng);
Duration::new(nanos / 1_000_000_000, (nanos % 1_000_000_000) as u32)
}
UniformDurationMode::Large {
max_secs,
max_nanos,
secs,
} => {
// constant folding means this is at least as fast as `Rng::sample(Range)`
let nano_range = Uniform::new(0, 1_000_000_000).unwrap();
loop {
let s = secs.sample(rng);
let n = nano_range.sample(rng);
if !(s == max_secs && n > max_nanos) {
let sum = n + self.offset;
break Duration::new(s, sum);
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(feature = "serde1")]
fn test_serialization_uniform_duration() {
let distr = UniformDuration::new(Duration::from_secs(10), Duration::from_secs(60)).unwrap();
let de_distr: UniformDuration =
bincode::deserialize(&bincode::serialize(&distr).unwrap()).unwrap();
assert_eq!(distr, de_distr);
}
#[test]
#[cfg_attr(miri, ignore)] // Miri is too slow
fn test_char() {
let mut rng = crate::test::rng(891);
let mut max = core::char::from_u32(0).unwrap();
for _ in 0..100 {
let c = rng.gen_range('A'..='Z');
assert!(c.is_ascii_uppercase());
max = max.max(c);
}
assert_eq!(max, 'Z');
let d = Uniform::new(
core::char::from_u32(0xD7F0).unwrap(),
core::char::from_u32(0xE010).unwrap(),
)
.unwrap();
for _ in 0..100 {
let c = d.sample(&mut rng);
assert!((c as u32) < 0xD800 || (c as u32) > 0xDFFF);
}
#[cfg(feature = "alloc")]
{
use crate::distr::DistString;
let string1 = d.sample_string(&mut rng, 100);
assert_eq!(string1.capacity(), 300);
let string2 = Uniform::new(
core::char::from_u32(0x0000).unwrap(),
core::char::from_u32(0x0080).unwrap(),
)
.unwrap()
.sample_string(&mut rng, 100);
assert_eq!(string2.capacity(), 100);
let string3 = Uniform::new_inclusive(
core::char::from_u32(0x0000).unwrap(),
core::char::from_u32(0x0080).unwrap(),
)
.unwrap()
.sample_string(&mut rng, 100);
assert_eq!(string3.capacity(), 200);
}
}
#[test]
#[cfg_attr(miri, ignore)] // Miri is too slow
fn test_durations() {
let mut rng = crate::test::rng(253);
let v = &[
(Duration::new(10, 50000), Duration::new(100, 1234)),
(Duration::new(0, 100), Duration::new(1, 50)),
(Duration::new(0, 0), Duration::new(u64::MAX, 999_999_999)),
];
for &(low, high) in v.iter() {
let my_uniform = Uniform::new(low, high).unwrap();
for _ in 0..1000 {
let v = rng.sample(my_uniform);
assert!(low <= v && v < high);
}
}
}
}
@@ -8,8 +8,8 @@
//! Weighted index sampling //! Weighted index sampling
use crate::distributions::uniform::{SampleBorrow, SampleUniform, UniformSampler}; use crate::distr::uniform::{SampleBorrow, SampleUniform, UniformSampler};
use crate::distributions::Distribution; use crate::distr::Distribution;
use crate::Rng; use crate::Rng;
use core::fmt; use core::fmt;
@@ -59,7 +59,7 @@ use serde::{Deserialize, Serialize};
/// ///
/// ``` /// ```
/// use rand::prelude::*; /// use rand::prelude::*;
/// use rand::distributions::WeightedIndex; /// use rand::distr::WeightedIndex;
/// ///
/// let choices = ['a', 'b', 'c']; /// let choices = ['a', 'b', 'c'];
/// let weights = [2, 1, 1]; /// let weights = [2, 1, 1];
@@ -78,7 +78,7 @@ use serde::{Deserialize, Serialize};
/// } /// }
/// ``` /// ```
/// ///
/// [`Uniform<X>`]: crate::distributions::Uniform /// [`Uniform<X>`]: crate::distr::Uniform
/// [`RngCore`]: crate::RngCore /// [`RngCore`]: crate::RngCore
/// [`rand_distr::weighted_alias`]: https://docs.rs/rand_distr/*/rand_distr/weighted_alias/index.html /// [`rand_distr::weighted_alias`]: https://docs.rs/rand_distr/*/rand_distr/weighted_alias/index.html
/// [`rand_distr::weighted_tree`]: https://docs.rs/rand_distr/*/rand_distr/weighted_tree/index.html /// [`rand_distr::weighted_tree`]: https://docs.rs/rand_distr/*/rand_distr/weighted_tree/index.html
@@ -101,7 +101,7 @@ impl<X: SampleUniform + PartialOrd> WeightedIndex<X> {
/// - [`WeightError::InsufficientNonZero`] when the sum of all weights is zero. /// - [`WeightError::InsufficientNonZero`] when the sum of all weights is zero.
/// - [`WeightError::Overflow`] when the sum of all weights overflows. /// - [`WeightError::Overflow`] when the sum of all weights overflows.
/// ///
/// [`Uniform<X>`]: crate::distributions::uniform::Uniform /// [`Uniform<X>`]: crate::distr::uniform::Uniform
pub fn new<I>(weights: I) -> Result<WeightedIndex<X>, WeightError> pub fn new<I>(weights: I) -> Result<WeightedIndex<X>, WeightError>
where where
I: IntoIterator, I: IntoIterator,
@@ -306,7 +306,7 @@ impl<X: SampleUniform + PartialOrd + Clone> WeightedIndex<X> {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// use rand::distributions::WeightedIndex; /// use rand::distr::WeightedIndex;
/// ///
/// let weights = [0, 1, 2]; /// let weights = [0, 1, 2];
/// let dist = WeightedIndex::new(&weights).unwrap(); /// let dist = WeightedIndex::new(&weights).unwrap();
@@ -341,7 +341,7 @@ impl<X: SampleUniform + PartialOrd + Clone> WeightedIndex<X> {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// use rand::distributions::WeightedIndex; /// use rand::distr::WeightedIndex;
/// ///
/// let weights = [1, 2, 3]; /// let weights = [1, 2, 3];
/// let mut dist = WeightedIndex::new(&weights).unwrap(); /// let mut dist = WeightedIndex::new(&weights).unwrap();
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -17,7 +17,7 @@
//! To get you started quickly, the easiest and highest-level way to get //! To get you started quickly, the easiest and highest-level way to get
//! a random value is to use [`random()`]; alternatively you can use //! a random value is to use [`random()`]; alternatively you can use
//! [`thread_rng()`]. The [`Rng`] trait provides a useful API on all RNGs, while //! [`thread_rng()`]. The [`Rng`] trait provides a useful API on all RNGs, while
//! the [`distributions`] and [`seq`] modules provide further //! the [`distr`] and [`seq`] modules provide further
//! functionality on top of RNGs. //! functionality on top of RNGs.
//! //!
//! ``` //! ```
@@ -97,7 +97,7 @@ macro_rules! error { ($($x:tt)*) => (
pub use rand_core::{CryptoRng, RngCore, SeedableRng, TryCryptoRng, TryRngCore}; pub use rand_core::{CryptoRng, RngCore, SeedableRng, TryCryptoRng, TryRngCore};
// Public modules // Public modules
pub mod distributions; pub mod distr;
pub mod prelude; pub mod prelude;
mod rng; mod rng;
pub mod rngs; pub mod rngs;
@@ -109,7 +109,7 @@ pub use crate::rngs::thread::thread_rng;
pub use rng::{Fill, Rng}; pub use rng::{Fill, Rng};
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] #[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
use crate::distributions::{Distribution, Standard}; use crate::distr::{Distribution, Standard};
/// Generates a random value using the thread-local random number generator. /// Generates a random value using the thread-local random number generator.
/// ///
@@ -153,7 +153,7 @@ use crate::distributions::{Distribution, Standard};
/// } /// }
/// ``` /// ```
/// ///
/// [`Standard`]: distributions::Standard /// [`Standard`]: distr::Standard
/// [`ThreadRng`]: rngs::ThreadRng /// [`ThreadRng`]: rngs::ThreadRng
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] #[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
#[inline] #[inline]
+1 -1
View File
@@ -19,7 +19,7 @@
//! ``` //! ```
#[doc(no_inline)] #[doc(no_inline)]
pub use crate::distributions::Distribution; pub use crate::distr::Distribution;
#[cfg(feature = "small_rng")] #[cfg(feature = "small_rng")]
#[doc(no_inline)] #[doc(no_inline)]
pub use crate::rngs::SmallRng; pub use crate::rngs::SmallRng;
+15 -15
View File
@@ -9,8 +9,8 @@
//! [`Rng`] trait //! [`Rng`] trait
use crate::distributions::uniform::{SampleRange, SampleUniform}; use crate::distr::uniform::{SampleRange, SampleUniform};
use crate::distributions::{self, Distribution, Standard}; use crate::distr::{self, Distribution, Standard};
use core::num::Wrapping; use core::num::Wrapping;
use core::{mem, slice}; use core::{mem, slice};
use rand_core::RngCore; use rand_core::RngCore;
@@ -86,7 +86,7 @@ pub trait Rng: RngCore {
/// rng.fill(&mut arr2); // array fill /// rng.fill(&mut arr2); // array fill
/// ``` /// ```
/// ///
/// [`Standard`]: distributions::Standard /// [`Standard`]: distr::Standard
#[inline] #[inline]
fn random<T>(&mut self) -> T fn random<T>(&mut self) -> T
where where
@@ -125,7 +125,7 @@ pub trait Rng: RngCore {
/// println!("{}", n); /// println!("{}", n);
/// ``` /// ```
/// ///
/// [`Uniform`]: distributions::uniform::Uniform /// [`Uniform`]: distr::uniform::Uniform
#[track_caller] #[track_caller]
fn gen_range<T, R>(&mut self, range: R) -> T fn gen_range<T, R>(&mut self, range: R) -> T
where where
@@ -139,7 +139,7 @@ pub trait Rng: RngCore {
/// Generate values via an iterator /// Generate values via an iterator
/// ///
/// This is a just a wrapper over [`Rng::sample_iter`] using /// This is a just a wrapper over [`Rng::sample_iter`] using
/// [`distributions::Standard`]. /// [`distr::Standard`].
/// ///
/// Note: this method consumes its argument. Use /// Note: this method consumes its argument. Use
/// `(&mut rng).gen_iter()` to avoid consuming the RNG. /// `(&mut rng).gen_iter()` to avoid consuming the RNG.
@@ -154,7 +154,7 @@ pub trait Rng: RngCore {
/// assert_eq!(&v, &[1, 2, 3, 4, 5]); /// assert_eq!(&v, &[1, 2, 3, 4, 5]);
/// ``` /// ```
#[inline] #[inline]
fn gen_iter<T>(self) -> distributions::DistIter<Standard, Self, T> fn gen_iter<T>(self) -> distr::DistIter<Standard, Self, T>
where where
Self: Sized, Self: Sized,
Standard: Distribution<T>, Standard: Distribution<T>,
@@ -168,7 +168,7 @@ pub trait Rng: RngCore {
/// ///
/// ``` /// ```
/// use rand::{thread_rng, Rng}; /// use rand::{thread_rng, Rng};
/// use rand::distributions::Uniform; /// use rand::distr::Uniform;
/// ///
/// let mut rng = thread_rng(); /// let mut rng = thread_rng();
/// let x = rng.sample(Uniform::new(10u32, 15).unwrap()); /// let x = rng.sample(Uniform::new(10u32, 15).unwrap());
@@ -189,7 +189,7 @@ pub trait Rng: RngCore {
/// ///
/// ``` /// ```
/// use rand::{thread_rng, Rng}; /// use rand::{thread_rng, Rng};
/// use rand::distributions::{Alphanumeric, Uniform, Standard}; /// use rand::distr::{Alphanumeric, Uniform, Standard};
/// ///
/// let mut rng = thread_rng(); /// let mut rng = thread_rng();
/// ///
@@ -213,7 +213,7 @@ pub trait Rng: RngCore {
/// println!("Not a 6; rolling again!"); /// println!("Not a 6; rolling again!");
/// } /// }
/// ``` /// ```
fn sample_iter<T, D>(self, distr: D) -> distributions::DistIter<D, Self, T> fn sample_iter<T, D>(self, distr: D) -> distr::DistIter<D, Self, T>
where where
D: Distribution<T>, D: Distribution<T>,
Self: Sized, Self: Sized,
@@ -259,11 +259,11 @@ pub trait Rng: RngCore {
/// ///
/// If `p < 0` or `p > 1`. /// If `p < 0` or `p > 1`.
/// ///
/// [`Bernoulli`]: distributions::Bernoulli /// [`Bernoulli`]: distr::Bernoulli
#[inline] #[inline]
#[track_caller] #[track_caller]
fn gen_bool(&mut self, p: f64) -> bool { fn gen_bool(&mut self, p: f64) -> bool {
match distributions::Bernoulli::new(p) { match distr::Bernoulli::new(p) {
Ok(d) => self.sample(d), Ok(d) => self.sample(d),
Err(_) => panic!("p={:?} is outside range [0.0, 1.0]", p), Err(_) => panic!("p={:?} is outside range [0.0, 1.0]", p),
} }
@@ -291,11 +291,11 @@ pub trait Rng: RngCore {
/// println!("{}", rng.gen_ratio(2, 3)); /// println!("{}", rng.gen_ratio(2, 3));
/// ``` /// ```
/// ///
/// [`Bernoulli`]: distributions::Bernoulli /// [`Bernoulli`]: distr::Bernoulli
#[inline] #[inline]
#[track_caller] #[track_caller]
fn gen_ratio(&mut self, numerator: u32, denominator: u32) -> bool { fn gen_ratio(&mut self, numerator: u32, denominator: u32) -> bool {
match distributions::Bernoulli::from_ratio(numerator, denominator) { match distr::Bernoulli::from_ratio(numerator, denominator) {
Ok(d) => self.sample(d), Ok(d) => self.sample(d),
Err(_) => panic!( Err(_) => panic!(
"p={}/{} is outside range [0.0, 1.0]", "p={}/{} is outside range [0.0, 1.0]",
@@ -554,7 +554,7 @@ mod test {
#[test] #[test]
fn test_rng_trait_object() { fn test_rng_trait_object() {
use crate::distributions::{Distribution, Standard}; use crate::distr::{Distribution, Standard};
let mut rng = rng(109); let mut rng = rng(109);
let mut r = &mut rng as &mut dyn RngCore; let mut r = &mut rng as &mut dyn RngCore;
r.next_u32(); r.next_u32();
@@ -566,7 +566,7 @@ mod test {
#[test] #[test]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn test_rng_boxed_trait() { fn test_rng_boxed_trait() {
use crate::distributions::{Distribution, Standard}; use crate::distr::{Distribution, Standard};
let rng = rng(110); let rng = rng(110);
let mut r = Box::new(rng) as Box<dyn RngCore>; let mut r = Box::new(rng) as Box<dyn RngCore>;
r.next_u32(); r.next_u32();
+2 -2
View File
@@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize};
/// Other integer types (64-bit and smaller) are produced via cast from `u64`. /// Other integer types (64-bit and smaller) are produced via cast from `u64`.
/// ///
/// Other types are produced via their implementation of [`Rng`](crate::Rng) or /// Other types are produced via their implementation of [`Rng`](crate::Rng) or
/// [`Distribution`](crate::distributions::Distribution). /// [`Distribution`](crate::distr::Distribution).
/// Output values may not be intuitive and may change in future releases but /// Output values may not be intuitive and may change in future releases but
/// are considered /// are considered
/// [portable](https://rust-random.github.io/book/portability.html). /// [portable](https://rust-random.github.io/book/portability.html).
@@ -95,7 +95,7 @@ mod tests {
#[test] #[test]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn test_bool() { fn test_bool() {
use crate::{distributions::Standard, Rng}; use crate::{distr::Standard, Rng};
// If this result ever changes, update doc on StepRng! // If this result ever changes, update doc on StepRng!
let rng = StepRng::new(0, 1 << 31); let rng = StepRng::new(0, 1 << 31);
+4 -47
View File
@@ -7,19 +7,16 @@
// except according to those terms. // except according to those terms.
//! Low-level API for sampling indices //! Low-level API for sampling indices
use super::gen_index;
#[cfg(feature = "alloc")]
use alloc::vec::{self, Vec}; use alloc::vec::{self, Vec};
use core::slice; use core::slice;
use core::{hash::Hash, ops::AddAssign}; use core::{hash::Hash, ops::AddAssign};
// BTreeMap is not as fast in tests, but better than nothing. // BTreeMap is not as fast in tests, but better than nothing.
#[cfg(feature = "std")] #[cfg(feature = "std")]
use super::WeightError; use super::WeightError;
use crate::distributions::uniform::SampleUniform; use crate::distr::uniform::SampleUniform;
#[cfg(feature = "alloc")] use crate::distr::{Distribution, Uniform};
use crate::distributions::{Distribution, Uniform};
use crate::Rng; use crate::Rng;
#[cfg(all(feature = "alloc", not(feature = "std")))] #[cfg(not(feature = "std"))]
use alloc::collections::BTreeSet; use alloc::collections::BTreeSet;
#[cfg(feature = "serde1")] #[cfg(feature = "serde1")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -30,7 +27,6 @@ use std::collections::HashSet;
/// ///
/// Multiple internal representations are possible. /// Multiple internal representations are possible.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
#[cfg(feature = "alloc")]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub enum IndexVec { pub enum IndexVec {
#[doc(hidden)] #[doc(hidden)]
@@ -39,7 +35,6 @@ pub enum IndexVec {
USize(Vec<usize>), USize(Vec<usize>),
} }
#[cfg(feature = "alloc")]
impl IndexVec { impl IndexVec {
/// Returns the number of indices /// Returns the number of indices
#[inline] #[inline]
@@ -90,7 +85,6 @@ impl IndexVec {
} }
} }
#[cfg(feature = "alloc")]
impl IntoIterator for IndexVec { impl IntoIterator for IndexVec {
type IntoIter = IndexVecIntoIter; type IntoIter = IndexVecIntoIter;
type Item = usize; type Item = usize;
@@ -105,7 +99,6 @@ impl IntoIterator for IndexVec {
} }
} }
#[cfg(feature = "alloc")]
impl PartialEq for IndexVec { impl PartialEq for IndexVec {
fn eq(&self, other: &IndexVec) -> bool { fn eq(&self, other: &IndexVec) -> bool {
use self::IndexVec::*; use self::IndexVec::*;
@@ -122,7 +115,6 @@ impl PartialEq for IndexVec {
} }
} }
#[cfg(feature = "alloc")]
impl From<Vec<u32>> for IndexVec { impl From<Vec<u32>> for IndexVec {
#[inline] #[inline]
fn from(v: Vec<u32>) -> Self { fn from(v: Vec<u32>) -> Self {
@@ -130,7 +122,6 @@ impl From<Vec<u32>> for IndexVec {
} }
} }
#[cfg(feature = "alloc")]
impl From<Vec<usize>> for IndexVec { impl From<Vec<usize>> for IndexVec {
#[inline] #[inline]
fn from(v: Vec<usize>) -> Self { fn from(v: Vec<usize>) -> Self {
@@ -171,7 +162,6 @@ impl<'a> Iterator for IndexVecIter<'a> {
impl<'a> ExactSizeIterator for IndexVecIter<'a> {} impl<'a> ExactSizeIterator for IndexVecIter<'a> {}
/// Return type of `IndexVec::into_iter`. /// Return type of `IndexVec::into_iter`.
#[cfg(feature = "alloc")]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum IndexVecIntoIter { pub enum IndexVecIntoIter {
#[doc(hidden)] #[doc(hidden)]
@@ -180,7 +170,6 @@ pub enum IndexVecIntoIter {
USize(vec::IntoIter<usize>), USize(vec::IntoIter<usize>),
} }
#[cfg(feature = "alloc")]
impl Iterator for IndexVecIntoIter { impl Iterator for IndexVecIntoIter {
type Item = usize; type Item = usize;
@@ -203,7 +192,6 @@ impl Iterator for IndexVecIntoIter {
} }
} }
#[cfg(feature = "alloc")]
impl ExactSizeIterator for IndexVecIntoIter {} impl ExactSizeIterator for IndexVecIntoIter {}
/// Randomly sample exactly `amount` distinct indices from `0..length`, and /// Randomly sample exactly `amount` distinct indices from `0..length`, and
@@ -228,7 +216,6 @@ impl ExactSizeIterator for IndexVecIntoIter {}
/// to adapt the internal `sample_floyd` implementation. /// to adapt the internal `sample_floyd` implementation.
/// ///
/// Panics if `amount > length`. /// Panics if `amount > length`.
#[cfg(feature = "alloc")]
#[track_caller] #[track_caller]
pub fn sample<R>(rng: &mut R, length: usize, amount: usize) -> IndexVec pub fn sample<R>(rng: &mut R, length: usize, amount: usize) -> IndexVec
where where
@@ -271,33 +258,6 @@ where
} }
} }
/// Randomly sample exactly `N` distinct indices from `0..len`, and
/// return them in random order (fully shuffled).
///
/// This is implemented via Floyd's algorithm. Time complexity is `O(N^2)`
/// and memory complexity is `O(N)`.
///
/// Returns `None` if (and only if) `N > len`.
pub fn sample_array<R, const N: usize>(rng: &mut R, len: usize) -> Option<[usize; N]>
where
R: Rng + ?Sized,
{
if N > len {
return None;
}
// Floyd's algorithm
let mut indices = [0; N];
for (i, j) in (len - N..len).enumerate() {
let t = gen_index(rng, j + 1);
if let Some(pos) = indices[0..i].iter().position(|&x| x == t) {
indices[pos] = j;
}
indices[i] = t;
}
Some(indices)
}
/// Randomly sample exactly `amount` distinct indices from `0..length`, and /// Randomly sample exactly `amount` distinct indices from `0..length`, and
/// return them in an arbitrary order (there is no guarantee of shuffling or /// return them in an arbitrary order (there is no guarantee of shuffling or
/// ordering). The weights are to be provided by the input function `weights`, /// ordering). The weights are to be provided by the input function `weights`,
@@ -432,7 +392,6 @@ where
/// The output values are fully shuffled. (Overhead is under 50%.) /// The output values are fully shuffled. (Overhead is under 50%.)
/// ///
/// This implementation uses `O(amount)` memory and `O(amount^2)` time. /// This implementation uses `O(amount)` memory and `O(amount^2)` time.
#[cfg(feature = "alloc")]
fn sample_floyd<R>(rng: &mut R, length: u32, amount: u32) -> IndexVec fn sample_floyd<R>(rng: &mut R, length: u32, amount: u32) -> IndexVec
where where
R: Rng + ?Sized, R: Rng + ?Sized,
@@ -464,7 +423,6 @@ where
/// performance in all cases). /// performance in all cases).
/// ///
/// Set-up is `O(length)` time and memory and shuffling is `O(amount)` time. /// Set-up is `O(length)` time and memory and shuffling is `O(amount)` time.
#[cfg(feature = "alloc")]
fn sample_inplace<R>(rng: &mut R, length: u32, amount: u32) -> IndexVec fn sample_inplace<R>(rng: &mut R, length: u32, amount: u32) -> IndexVec
where where
R: Rng + ?Sized, R: Rng + ?Sized,
@@ -483,6 +441,7 @@ where
trait UInt: Copy + PartialOrd + Ord + PartialEq + Eq + SampleUniform + Hash + AddAssign { trait UInt: Copy + PartialOrd + Ord + PartialEq + Eq + SampleUniform + Hash + AddAssign {
fn zero() -> Self; fn zero() -> Self;
#[cfg_attr(feature = "alloc", allow(dead_code))]
fn one() -> Self; fn one() -> Self;
fn as_usize(self) -> usize; fn as_usize(self) -> usize;
} }
@@ -530,7 +489,6 @@ impl UInt for usize {
/// ///
/// This function is generic over X primarily so that results are value-stable /// This function is generic over X primarily so that results are value-stable
/// over 32-bit and 64-bit platforms. /// over 32-bit and 64-bit platforms.
#[cfg(feature = "alloc")]
fn sample_rejection<X: UInt, R>(rng: &mut R, length: X, amount: X) -> IndexVec fn sample_rejection<X: UInt, R>(rng: &mut R, length: X, amount: X) -> IndexVec
where where
R: Rng + ?Sized, R: Rng + ?Sized,
@@ -555,7 +513,6 @@ where
IndexVec::from(indices) IndexVec::from(indices)
} }
#[cfg(feature = "alloc")]
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
+42 -3
View File
@@ -19,7 +19,7 @@
//! //!
//! Also see: //! Also see:
//! //!
//! * [`crate::distributions::WeightedIndex`] distribution which provides //! * [`crate::distr::WeightedIndex`] distribution which provides
//! weighted index sampling. //! weighted index sampling.
//! //!
//! In order to make results reproducible across 32-64 bit architectures, all //! In order to make results reproducible across 32-64 bit architectures, all
@@ -31,11 +31,13 @@ mod increasing_uniform;
mod iterator; mod iterator;
mod slice; mod slice;
pub mod index; #[cfg(feature = "alloc")]
#[path = "index.rs"]
mod index_;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
#[doc(no_inline)] #[doc(no_inline)]
pub use crate::distributions::WeightError; pub use crate::distr::WeightError;
pub use iterator::IteratorRandom; pub use iterator::IteratorRandom;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub use slice::SliceChooseIter; pub use slice::SliceChooseIter;
@@ -54,3 +56,40 @@ fn gen_index<R: Rng + ?Sized>(rng: &mut R, ubound: usize) -> usize {
rng.gen_range(0..ubound) rng.gen_range(0..ubound)
} }
} }
/// Low-level API for sampling indices
pub mod index {
use super::gen_index;
use crate::Rng;
#[cfg(feature = "alloc")]
#[doc(inline)]
pub use super::index_::*;
/// Randomly sample exactly `N` distinct indices from `0..len`, and
/// return them in random order (fully shuffled).
///
/// This is implemented via Floyd's algorithm. Time complexity is `O(N^2)`
/// and memory complexity is `O(N)`.
///
/// Returns `None` if (and only if) `N > len`.
pub fn sample_array<R, const N: usize>(rng: &mut R, len: usize) -> Option<[usize; N]>
where
R: Rng + ?Sized,
{
if N > len {
return None;
}
// Floyd's algorithm
let mut indices = [0; N];
for (i, j) in (len - N..len).enumerate() {
let t = gen_index(rng, j + 1);
if let Some(pos) = indices[0..i].iter().position(|&x| x == t) {
indices[pos] = j;
}
indices[i] = t;
}
Some(indices)
}
}
+8 -8
View File
@@ -11,9 +11,9 @@
use super::increasing_uniform::IncreasingUniform; use super::increasing_uniform::IncreasingUniform;
use super::{gen_index, index}; use super::{gen_index, index};
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use crate::distributions::uniform::{SampleBorrow, SampleUniform}; use crate::distr::uniform::{SampleBorrow, SampleUniform};
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use crate::distributions::{Weight, WeightError}; use crate::distr::{Weight, WeightError};
use crate::Rng; use crate::Rng;
use core::ops::{Index, IndexMut}; use core::ops::{Index, IndexMut};
@@ -137,7 +137,7 @@ pub trait IndexedRandom: Index<usize> {
/// ///
/// For slices of length `n`, complexity is `O(n)`. /// For slices of length `n`, complexity is `O(n)`.
/// For more information about the underlying algorithm, /// For more information about the underlying algorithm,
/// see [`distributions::WeightedIndex`]. /// see [`distr::WeightedIndex`].
/// ///
/// See also [`choose_weighted_mut`]. /// See also [`choose_weighted_mut`].
/// ///
@@ -154,7 +154,7 @@ pub trait IndexedRandom: Index<usize> {
/// ``` /// ```
/// [`choose`]: IndexedRandom::choose /// [`choose`]: IndexedRandom::choose
/// [`choose_weighted_mut`]: IndexedMutRandom::choose_weighted_mut /// [`choose_weighted_mut`]: IndexedMutRandom::choose_weighted_mut
/// [`distributions::WeightedIndex`]: crate::distributions::WeightedIndex /// [`distr::WeightedIndex`]: crate::distr::WeightedIndex
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn choose_weighted<R, F, B, X>( fn choose_weighted<R, F, B, X>(
&self, &self,
@@ -167,7 +167,7 @@ pub trait IndexedRandom: Index<usize> {
B: SampleBorrow<X>, B: SampleBorrow<X>,
X: SampleUniform + Weight + PartialOrd<X>, X: SampleUniform + Weight + PartialOrd<X>,
{ {
use crate::distributions::{Distribution, WeightedIndex}; use crate::distr::{Distribution, WeightedIndex};
let distr = WeightedIndex::new((0..self.len()).map(|idx| weight(&self[idx])))?; let distr = WeightedIndex::new((0..self.len()).map(|idx| weight(&self[idx])))?;
Ok(&self[distr.sample(rng)]) Ok(&self[distr.sample(rng)])
} }
@@ -268,13 +268,13 @@ pub trait IndexedMutRandom: IndexedRandom + IndexMut<usize> {
/// ///
/// For slices of length `n`, complexity is `O(n)`. /// For slices of length `n`, complexity is `O(n)`.
/// For more information about the underlying algorithm, /// For more information about the underlying algorithm,
/// see [`distributions::WeightedIndex`]. /// see [`distr::WeightedIndex`].
/// ///
/// See also [`choose_weighted`]. /// See also [`choose_weighted`].
/// ///
/// [`choose_mut`]: IndexedMutRandom::choose_mut /// [`choose_mut`]: IndexedMutRandom::choose_mut
/// [`choose_weighted`]: IndexedRandom::choose_weighted /// [`choose_weighted`]: IndexedRandom::choose_weighted
/// [`distributions::WeightedIndex`]: crate::distributions::WeightedIndex /// [`distr::WeightedIndex`]: crate::distr::WeightedIndex
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn choose_weighted_mut<R, F, B, X>( fn choose_weighted_mut<R, F, B, X>(
&mut self, &mut self,
@@ -287,7 +287,7 @@ pub trait IndexedMutRandom: IndexedRandom + IndexMut<usize> {
B: SampleBorrow<X>, B: SampleBorrow<X>,
X: SampleUniform + Weight + PartialOrd<X>, X: SampleUniform + Weight + PartialOrd<X>,
{ {
use crate::distributions::{Distribution, WeightedIndex}; use crate::distr::{Distribution, WeightedIndex};
let distr = WeightedIndex::new((0..self.len()).map(|idx| weight(&self[idx])))?; let distr = WeightedIndex::new((0..self.len()).map(|idx| weight(&self[idx])))?;
let index = distr.sample(rng); let index = distr.sample(rng);
Ok(&mut self[index]) Ok(&mut self[index])
+1 -1
View File
@@ -10,7 +10,7 @@
# except according to those terms. # except according to those terms.
# This creates the tables used for distributions implemented using the # This creates the tables used for distributions implemented using the
# ziggurat algorithm in `rand::distributions;`. They are # ziggurat algorithm in `rand::distr;`. They are
# (basically) the tables as used in the ZIGNOR variant (Doornik 2005). # (basically) the tables as used in the ZIGNOR variant (Doornik 2005).
# They are changed rarely, so the generated file should be checked in # They are changed rarely, so the generated file should be checked in
# to git. # to git.