Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add value-stability tests #888

Merged
merged 11 commits into from Sep 16, 2019
7 changes: 7 additions & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,13 @@ A [separate changelog is kept for rand_core](rand_core/CHANGELOG.md).

You may also find the [Upgrade Guide](https://rust-random.github.io/book/update.html) useful.

## [0.7.2] - 2019-09-16
### Fixes
- Fix dependency on `rand_core` 0.5.1 (#890)

### Additions
- Unit tests for value stability of distributions added (#888)

## [0.7.1] - 2019-09-13
### Fixes
- Fix `no_std` behaviour, appropriately enable c2-chacha's `std` feature (#844)
Expand Down
6 changes: 4 additions & 2 deletions Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "rand"
version = "0.7.1"
version = "0.7.2"
authors = ["The Rand Project Developers", "The Rust Project Developers"]
license = "MIT OR Apache-2.0"
readme = "README.md"
Expand Down Expand Up @@ -30,6 +30,8 @@ serde1 = [] # does nothing, deprecated
std = ["rand_core/std", "rand_chacha/std", "alloc", "getrandom"]
alloc = ["rand_core/alloc"] # enables Vec and Box support (without std)
# re-export optional WASM dependencies to avoid breakage:
# Warning: wasm-bindgen and stdweb features will be removed in rand 0.8;
# recommended to activate via the getrandom crate instead.
wasm-bindgen = ["getrandom_package/wasm-bindgen"]
stdweb = ["getrandom_package/stdweb"]
getrandom = ["getrandom_package", "rand_core/getrandom"]
Expand All @@ -54,7 +56,7 @@ members = [
]

[dependencies]
rand_core = { path = "rand_core", version = "0.5" }
rand_core = { path = "rand_core", version = "0.5.1" }
rand_pcg = { path = "rand_pcg", version = "0.2", optional = true }
# Do not depend on 'getrandom_package' directly; use the 'getrandom' feature!
# This is a dependency because: we forward wasm feature flags
Expand Down
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -93,8 +93,10 @@ Optionally, the following dependencies can be enabled:
- `log` enables logging via the `log` crate
- `stdweb` implies `getrandom/stdweb` to enable
`getrandom` support on `wasm32-unknown-unknown`
(will be removed in rand 0.8; activate via `getrandom` crate instead)
- `wasm-bindgen` implies `getrandom/wasm-bindgen` to enable
`getrandom` support on `wasm32-unknown-unknown`
(will be removed in rand 0.8; activate via `getrandom` crate instead)

Additionally, these features configure Rand:

Expand Down
18 changes: 18 additions & 0 deletions rand_distr/src/binomial.rs
Expand Up @@ -326,4 +326,22 @@ mod test {
fn test_binomial_invalid_lambda_neg() {
Binomial::new(20, -10.0).unwrap();
}

#[test]
fn value_stability() {
fn test_samples(n: u64, p: f64, expected: &[u64]) {
let distr = Binomial::new(n, p).unwrap();
let mut rng = crate::test::rng(353);
let mut buf = [0; 4];
for x in &mut buf {
*x = rng.sample(&distr);
}
assert_eq!(buf, expected);
}

// We have multiple code paths: np < 10, p > 0.5
test_samples(2, 0.7, &[1, 1, 2, 1]);
test_samples(20, 0.3, &[7, 7, 5, 7]);
test_samples(2000, 0.6, &[1194, 1208, 1192, 1210]);
}
}
24 changes: 22 additions & 2 deletions rand_distr/src/cauchy.rs
Expand Up @@ -72,8 +72,7 @@ where Standard: Distribution<N>

#[cfg(test)]
mod test {
use crate::Distribution;
use super::Cauchy;
use super::*;

fn median(mut numbers: &mut [f64]) -> f64 {
sort(&mut numbers);
Expand Down Expand Up @@ -117,4 +116,25 @@ mod test {
fn test_cauchy_invalid_scale_neg() {
Cauchy::new(0.0, -10.0).unwrap();
}

#[test]
fn value_stability() {
fn test_samples<N: Float + core::fmt::Debug>(m: N, s: N, expected: &[N])
where Standard: Distribution<N> {
let distr = Cauchy::new(m, s).unwrap();
let mut rng = crate::test::rng(353);
let mut buf = [m; 4];
for x in &mut buf {
*x = rng.sample(&distr);
}
assert_eq!(buf, expected);
}

// Warning: in a few cases, results vary slightly between different
// platforms, presumably due to differences in precision of system impls
// of the tan function. We work around this by avoiding these values.
test_samples(100f64, 10.0, &[77.93369152808678, 90.1606912098641,
125.31516221323625, 86.10217834773925]);
test_samples(10f32, 7.0, &[15.023088, -5.446413, 3.7092876, 3.112482]);
}
}
13 changes: 11 additions & 2 deletions rand_distr/src/dirichlet.rs
Expand Up @@ -107,8 +107,7 @@ where StandardNormal: Distribution<N>, Exp1: Distribution<N>, Open01: Distributi

#[cfg(test)]
mod test {
use super::Dirichlet;
use crate::Distribution;
use super::*;

#[test]
fn test_dirichlet() {
Expand Down Expand Up @@ -151,4 +150,14 @@ mod test {
fn test_dirichlet_invalid_alpha() {
Dirichlet::new_with_size(0.0f64, 2).unwrap();
}

#[test]
fn value_stability() {
let mut rng = crate::test::rng(223);
assert_eq!(rng.sample(Dirichlet::new(vec![1.0, 2.0, 3.0]).unwrap()),
vec![0.12941567177708177, 0.4702121891675036, 0.4003721390554146]);
assert_eq!(rng.sample(Dirichlet::new_with_size(8.0, 5).unwrap()),
vec![0.17684200044809556, 0.29915953935953055,
0.1832858056608014, 0.1425623503573967, 0.19815030417417595]);
}
}
27 changes: 25 additions & 2 deletions rand_distr/src/exponential.rs
Expand Up @@ -121,8 +121,7 @@ where Exp1: Distribution<N>

#[cfg(test)]
mod test {
use crate::Distribution;
use super::Exp;
use super::*;

#[test]
fn test_exp() {
Expand All @@ -142,4 +141,28 @@ mod test {
fn test_exp_invalid_lambda_neg() {
Exp::new(-10.0).unwrap();
}

#[test]
fn value_stability() {
fn test_samples<N: Float + core::fmt::Debug, D: Distribution<N>>
(distr: D, zero: N, expected: &[N])
{
let mut rng = crate::test::rng(223);
let mut buf = [zero; 4];
for x in &mut buf {
*x = rng.sample(&distr);
}
assert_eq!(buf, expected);
}

test_samples(Exp1, 0f32, &[1.079617, 1.8325565, 0.04601166, 0.34471703]);
test_samples(Exp1, 0f64, &[1.0796170642388276, 1.8325565304274,
0.04601166186842716, 0.3447170217100157]);

test_samples(Exp::new(2.0).unwrap(), 0f32,
&[0.5398085, 0.91627824, 0.02300583, 0.17235851]);
test_samples(Exp::new(1.0).unwrap(), 0f64, &[
1.0796170642388276, 1.8325565304274,
0.04601166186842716, 0.3447170217100157]);
}
}
59 changes: 57 additions & 2 deletions rand_distr/src/gamma.rs
Expand Up @@ -417,8 +417,7 @@ where StandardNormal: Distribution<N>, Exp1: Distribution<N>, Open01: Distributi

#[cfg(test)]
mod test {
use crate::Distribution;
use super::{Beta, ChiSquared, StudentT, FisherF};
use super::*;

#[test]
fn test_chi_squared_one() {
Expand Down Expand Up @@ -482,4 +481,60 @@ mod test {
fn test_beta_invalid_dof() {
Beta::new(0., 0.).unwrap();
}

#[test]
fn value_stability() {
fn test_samples<N: Float + core::fmt::Debug, D: Distribution<N>>
(distr: D, zero: N, expected: &[N])
{
let mut rng = crate::test::rng(223);
let mut buf = [zero; 4];
for x in &mut buf {
*x = rng.sample(&distr);
}
assert_eq!(buf, expected);
}

// Gamma has 3 cases: shape == 1, shape < 1, shape > 1
test_samples(Gamma::new(1.0, 5.0).unwrap(), 0f32,
&[5.398085, 9.162783, 0.2300583, 1.7235851]);
test_samples(Gamma::new(0.8, 5.0).unwrap(), 0f32,
&[0.5051203, 0.9048302, 3.095812, 1.8566116]);
test_samples(Gamma::new(1.1, 5.0).unwrap(), 0f64, &[
7.783878094584059, 1.4939528171618057,
8.638017638857592, 3.0949337228829004]);

// ChiSquared has 2 cases: k == 1, k != 1
test_samples(ChiSquared::new(1.0).unwrap(), 0f64, &[
0.4893526200348249, 1.635249736808788,
0.5013580219361969, 0.1457735613733489]);
test_samples(ChiSquared::new(0.1).unwrap(), 0f64, &[
0.014824404726978617, 0.021602123937134326,
0.0000003431429746851693, 0.00000002291755769542258]);
test_samples(ChiSquared::new(10.0).unwrap(), 0f32,
&[12.693656, 6.812016, 11.082001, 12.436167]);

// FisherF has same special cases as ChiSquared on each param
test_samples(FisherF::new(1.0, 13.5).unwrap(), 0f32,
&[0.32283646, 0.048049655, 0.0788893, 1.817178]);
test_samples(FisherF::new(1.0, 1.0).unwrap(), 0f32,
&[0.29925257, 3.4392934, 9.567652, 0.020074]);
test_samples(FisherF::new(0.7, 13.5).unwrap(), 0f64, &[
3.3196593155045124, 0.3409169916262829,
0.03377989856426519, 0.00004041672861036937]);

// StudentT has same special cases as ChiSquared
test_samples(StudentT::new(1.0).unwrap(), 0f32,
&[0.54703987, -1.8545331, 3.093162, -0.14168274]);
test_samples(StudentT::new(1.1).unwrap(), 0f64, &[
0.7729195887949754, 1.2606210611616204,
-1.7553606501113175, -2.377641221169782]);

// Beta has same special cases as Gamma on each param
test_samples(Beta::new(1.0, 0.8).unwrap(), 0f32,
&[0.6444564, 0.357635, 0.4110078, 0.7347192]);
test_samples(Beta::new(0.7, 1.2).unwrap(), 0f64, &[
0.6433129944095513, 0.5373371199711573,
0.10313293199269491, 0.002472280249144378]);
}
}
35 changes: 33 additions & 2 deletions rand_distr/src/normal.rs
Expand Up @@ -185,8 +185,7 @@ where StandardNormal: Distribution<N>

#[cfg(test)]
mod tests {
use crate::Distribution;
use super::{Normal, LogNormal};
use super::*;

#[test]
fn test_normal() {
Expand Down Expand Up @@ -216,4 +215,36 @@ mod tests {
fn test_log_normal_invalid_sd() {
LogNormal::new(10.0, -1.0).unwrap();
}

#[test]
fn value_stability() {
fn test_samples<N: Float + core::fmt::Debug, D: Distribution<N>>
(distr: D, zero: N, expected: &[N])
{
let mut rng = crate::test::rng(213);
let mut buf = [zero; 4];
for x in &mut buf {
*x = rng.sample(&distr);
}
assert_eq!(buf, expected);
}

test_samples(StandardNormal, 0f32,
&[-0.11844189, 0.781378, 0.06563994, -1.1932899]);
test_samples(StandardNormal, 0f64, &[
-0.11844188827977231, 0.7813779637772346,
0.06563993969580051, -1.1932899004186373]);

test_samples(Normal::new(0.0, 1.0).unwrap(), 0f32,
&[-0.11844189, 0.781378, 0.06563994, -1.1932899]);
test_samples(Normal::new(2.0, 0.5).unwrap(), 0f64, &[
1.940779055860114, 2.3906889818886174,
2.0328199698479, 1.4033550497906813]);

test_samples(LogNormal::new(0.0, 1.0).unwrap(), 0f32,
&[0.88830346, 2.1844804, 1.0678421, 0.30322206]);
test_samples(LogNormal::new(2.0, 0.5).unwrap(), 0f64, &[
6.964174338639032, 10.921015733601452,
7.6355881556915906, 4.068828213584092]);
}
}
23 changes: 21 additions & 2 deletions rand_distr/src/pareto.rs
Expand Up @@ -66,8 +66,7 @@ where OpenClosed01: Distribution<N>

#[cfg(test)]
mod tests {
use crate::Distribution;
use super::Pareto;
use super::*;

#[test]
#[should_panic]
Expand All @@ -86,4 +85,24 @@ mod tests {
assert!(r >= scale);
}
}

#[test]
fn value_stability() {
fn test_samples<N: Float + core::fmt::Debug, D: Distribution<N>>
(distr: D, zero: N, expected: &[N])
{
let mut rng = crate::test::rng(213);
let mut buf = [zero; 4];
for x in &mut buf {
*x = rng.sample(&distr);
}
assert_eq!(buf, expected);
}

test_samples(Pareto::new(1.0, 1.0).unwrap(), 0f32,
&[1.0423688, 2.1235929, 4.132709, 1.4679428]);
test_samples(Pareto::new(2.0, 0.5).unwrap(), 0f64, &[
9.019295276219136, 4.3097126018270595,
6.837815045397157, 105.8826669383772]);
}
}
22 changes: 20 additions & 2 deletions rand_distr/src/poisson.rs
Expand Up @@ -134,8 +134,7 @@ where Standard: Distribution<N>

#[cfg(test)]
mod test {
use crate::Distribution;
use super::Poisson;
use super::*;

#[test]
fn test_poisson_10() {
Expand Down Expand Up @@ -230,4 +229,23 @@ mod test {
fn test_poisson_invalid_lambda_neg() {
Poisson::new(-10.0).unwrap();
}

#[test]
fn value_stability() {
fn test_samples<N: Float + core::fmt::Debug, D: Distribution<N>>
(distr: D, zero: N, expected: &[N])
{
let mut rng = crate::test::rng(223);
let mut buf = [zero; 4];
for x in &mut buf {
*x = rng.sample(&distr);
}
assert_eq!(buf, expected);
}

// Special cases: < 12, >= 12
test_samples(Poisson::new(7.0).unwrap(), 0f32, &[5.0, 11.0, 6.0, 5.0]);
test_samples(Poisson::new(7.0).unwrap(), 0f64, &[9.0, 5.0, 7.0, 6.0]);
test_samples(Poisson::new(27.0).unwrap(), 0f32, &[28.0, 32.0, 36.0, 36.0]);
}
}
23 changes: 21 additions & 2 deletions rand_distr/src/weibull.rs
Expand Up @@ -63,8 +63,7 @@ where OpenClosed01: Distribution<N>

#[cfg(test)]
mod tests {
use crate::Distribution;
use super::Weibull;
use super::*;

#[test]
#[should_panic]
Expand All @@ -83,4 +82,24 @@ mod tests {
assert!(r >= 0.);
}
}

#[test]
fn value_stability() {
fn test_samples<N: Float + core::fmt::Debug, D: Distribution<N>>
(distr: D, zero: N, expected: &[N])
{
let mut rng = crate::test::rng(213);
let mut buf = [zero; 4];
for x in &mut buf {
*x = rng.sample(&distr);
}
assert_eq!(buf, expected);
}

test_samples(Weibull::new(1.0, 1.0).unwrap(), 0f32,
&[0.041495778, 0.7531094, 1.4189332, 0.38386202]);
test_samples(Weibull::new(2.0, 0.5).unwrap(), 0f64, &[
1.1343478702739669, 0.29470010050655226,
0.7556151370284702, 7.877212340241561]);
}
}