diff --git a/Cargo.toml b/Cargo.toml index ee6b6ba2e94..7596ce2ec7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,6 +79,7 @@ rand_hc = { path = "rand_hc", version = "0.1" } [dev-dependencies] # This has a histogram implementation used for testing uniformity. average = "0.9.2" +rand_pcg = { path = "rand_pcg", version = "0.1" } # Only for benches: rand_hc = { path = "rand_hc", version = "0.1" } rand_xoshiro = { path = "rand_xoshiro", version = "0.1" } diff --git a/src/distributions/cauchy.rs b/src/distributions/cauchy.rs index 9ffd5601ae6..3ec9fee9651 100644 --- a/src/distributions/cauchy.rs +++ b/src/distributions/cauchy.rs @@ -68,31 +68,24 @@ mod test { #[test] #[cfg(not(miri))] // Miri doesn't support transcendental functions - fn test_cauchy_median() { + fn test_cauchy_averages() { + // NOTE: given that the variance and mean are undefined, + // this test does not have any rigorous statistical meaning. let cauchy = Cauchy::new(10.0, 5.0); - let mut rng = ::test::rng(123); + let mut rng = crate::test::rng(123); let mut numbers: [f64; 1000] = [0.0; 1000]; + let mut sum = 0.0; for i in 0..1000 { numbers[i] = cauchy.sample(&mut rng); + sum += numbers[i]; } let median = median(&mut numbers); println!("Cauchy median: {}", median); - assert!((median - 10.0).abs() < 0.5); // not 100% certain, but probable enough - } - - #[test] - #[cfg(not(miri))] // Miri doesn't support transcendental functions - fn test_cauchy_mean() { - let cauchy = Cauchy::new(10.0, 5.0); - let mut rng = ::test::rng(123); - let mut sum = 0.0; - for _ in 0..1000 { - sum += cauchy.sample(&mut rng); - } + assert!((median - 10.0).abs() < 0.4); // not 100% certain, but probable enough let mean = sum / 1000.0; println!("Cauchy mean: {}", mean); // for a Cauchy distribution the mean should not converge - assert!((mean - 10.0).abs() > 0.5); // not 100% certain, but probable enough + assert!((mean - 10.0).abs() > 0.4); // not 100% certain, but probable enough } #[test] diff --git a/src/distributions/unit_circle.rs b/src/distributions/unit_circle.rs index d078f182d6f..ddd3caf71df 100644 --- a/src/distributions/unit_circle.rs +++ b/src/distributions/unit_circle.rs @@ -84,10 +84,17 @@ mod tests { #[test] fn value_stability() { - let mut rng = ::test::rng(2); - let dist = UnitCircle::new(); - assert_eq!(dist.sample(&mut rng), [0.968253135419177, 0.2499717299034683]); - assert_eq!(dist.sample(&mut rng), [-0.2083028196300198, 0.9780643819985387]); - assert_eq!(dist.sample(&mut rng), [0.4284767927057159, -0.9035527865667964]); + let mut rng = crate::test::rng(2); + let expected = [ + [-0.9965658683520504, -0.08280380447614634], + [-0.9790853270389644, -0.20345004884984505], + [-0.8449189758898707, 0.5348943112253227], + ]; + let samples = [ + UnitCircle.sample(&mut rng), + UnitCircle.sample(&mut rng), + UnitCircle.sample(&mut rng), + ]; + assert_eq!(samples, expected); } } diff --git a/src/distributions/unit_sphere.rs b/src/distributions/unit_sphere.rs index 4d895ed2359..3cd94d6dbcc 100644 --- a/src/distributions/unit_sphere.rs +++ b/src/distributions/unit_sphere.rs @@ -79,13 +79,17 @@ mod tests { #[test] fn value_stability() { - let mut rng = ::test::rng(2); - let dist = UnitSphereSurface::new(); - assert_eq!(dist.sample(&mut rng), - [0.9754391834492048, 0.12388255134190126, -0.18213048307455182]); - assert_eq!(dist.sample(&mut rng), - [0.45487349593184284, 0.5619516852114866, -0.6908693119445342]); - assert_eq!(dist.sample(&mut rng), - [-0.8374011645099787, 0.5296803977011814, 0.13489983679919715]); + let mut rng = crate::test::rng(2); + let expected = [ + [0.03247542860231647, -0.7830477442152738, 0.6211131755296027], + [-0.09978440840914075, 0.9706650829833128, -0.21875184231323952], + [0.2735582468624679, 0.9435374242279655, -0.1868234852870203], + ]; + let samples = [ + UnitSphereSurface.sample(&mut rng), + UnitSphereSurface.sample(&mut rng), + UnitSphereSurface.sample(&mut rng), + ]; + assert_eq!(samples, expected); } } diff --git a/src/lib.rs b/src/lib.rs index 975805418c4..bd2dcae72a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -561,10 +561,12 @@ mod test { use super::*; #[cfg(all(not(feature="std"), feature="alloc"))] use alloc::boxed::Box; + /// Construct a deterministic RNG with the given seed pub fn rng(seed: u64) -> impl RngCore { - // For tests, we want a statistically good, fast, reproducible RNG. We - // do not need cryptographic strength. ChaCha8 is a good choice. - rand_chacha::ChaCha8Rng::seed_from_u64(seed) + // For tests, we want a statistically good, fast, reproducible RNG. + // PCG32 will do fine, and will be easy to embed if we ever need to. + const INC: u64 = 11634580027462260723; + rand_pcg::Pcg32::new(seed, INC) } #[test] diff --git a/src/seq/mod.rs b/src/seq/mod.rs index e4cca760f47..f1302f20bef 100644 --- a/src/seq/mod.rs +++ b/src/seq/mod.rs @@ -613,8 +613,10 @@ mod test { counts[permutation] += 1; } for count in counts.iter() { - let err = *count - 10000i32 / 24; - assert!(-50 <= err && err <= 50); + // Binomial(10000, 1/24) with average 416.667 + // Octave: binocdf(n, 10000, 1/24) + // 99.9% chance samples lie within this range: + assert!(352 <= *count && *count <= 483, "count: {}", count); } }