Repository: rust-ndarray/ndarray
Pull request #1486 (2025-03-16 22:31:34)

Bump rand to 0.9.0 and rand_distr to 0.5.0
Related to #1476, but I'll leave it open until we actually do the next release.


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 3d1c1dde..98326a59 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -96,8 +96,8 @@ num-traits = { version = "0.2", default-features = false }
 num-complex = { version = "0.4", default-features = false }
 approx = { version = "0.5", default-features = false }
 quickcheck = { version = "1.0", default-features = false }
-rand = { version = "0.8.0", features = ["small_rng"] }
-rand_distr = { version = "0.4.0" }
+rand = { version = "0.9.0", features = ["small_rng"] }
+rand_distr = { version = "0.5.0" }
 itertools = { version = "0.13.0", default-features = false, features = ["use_std"] }
 cblas-sys = { version = "0.1.4", default-features = false }
 
diff --git a/ndarray-rand/Cargo.toml b/ndarray-rand/Cargo.toml
index b58e752a..72b95902 100644
--- a/ndarray-rand/Cargo.toml
+++ b/ndarray-rand/Cargo.toml
@@ -21,7 +21,7 @@ rand_distr = { workspace = true }
 quickcheck = { workspace = true, optional = true }
 
 [dev-dependencies]
-rand_isaac = "0.3.0"
+rand_isaac = "0.4.0"
 quickcheck = { workspace = true }
 
 [package.metadata.release]
diff --git a/ndarray-rand/benches/bench.rs b/ndarray-rand/benches/bench.rs
index 0e5eb2ff..364eca9f 100644
--- a/ndarray-rand/benches/bench.rs
+++ b/ndarray-rand/benches/bench.rs
@@ -13,7 +13,7 @@ use test::Bencher;
 fn uniform_f32(b: &mut Bencher)
 {
     let m = 100;
-    b.iter(|| Array::random((m, m), Uniform::new(-1f32, 1.)));
+    b.iter(|| Array::random((m, m), Uniform::new(-1f32, 1.).unwrap()));
 }
 
 #[bench]
diff --git a/ndarray-rand/src/lib.rs b/ndarray-rand/src/lib.rs
index 6671ab33..795e246d 100644
--- a/ndarray-rand/src/lib.rs
+++ b/ndarray-rand/src/lib.rs
@@ -29,10 +29,10 @@
 //! that the items are not compatible (e.g. that a type doesn't implement a
 //! necessary trait).
 
-use crate::rand::distributions::{Distribution, Uniform};
+use crate::rand::distr::{Distribution, Uniform};
 use crate::rand::rngs::SmallRng;
 use crate::rand::seq::index;
-use crate::rand::{thread_rng, Rng, SeedableRng};
+use crate::rand::{rng, Rng, SeedableRng};
 
 use ndarray::{Array, Axis, RemoveAxis, ShapeBuilder};
 use ndarray::{ArrayBase, Data, DataOwned, Dimension, RawData};
@@ -71,8 +71,8 @@ where
     /// Create an array with shape `dim` with elements drawn from
     /// `distribution` using the default RNG.
     ///
-    /// ***Panics*** if creation of the RNG fails or if the number of elements
-    /// overflows usize.
+    /// ***Panics*** if creation of the RNG fails, the number of elements
+    /// overflows usize, or the axis has zero length.
     ///
     /// ```
     /// use ndarray::Array;
@@ -80,7 +80,7 @@ where
     /// use ndarray_rand::rand_distr::Uniform;
     ///
     /// # fn main() {
-    /// let a = Array::random((2, 5), Uniform::new(0., 10.));
+    /// let a = Array::random((2, 5), Uniform::new(0., 10.).unwrap());
     /// println!("{:8.4}", a);
     /// // Example Output:
     /// // [[  8.6900,   6.9824,   3.8922,   6.5861,   2.4890],
@@ -95,7 +95,8 @@ where
     /// Create an array with shape `dim` with elements drawn from
     /// `distribution`, using a specific Rng `rng`.
     ///
-    /// ***Panics*** if the number of elements overflows usize.
+    /// ***Panics*** if the number of elements overflows usize
+    /// or the axis has zero length.
     ///
     /// ```
     /// use ndarray::Array;
@@ -110,7 +111,7 @@ where
     /// let mut rng = Isaac64Rng::seed_from_u64(seed);
     ///
     /// // Generate a random array using `rng`
-    /// let a = Array::random_using((2, 5), Uniform::new(0., 10.), &mut rng);
+    /// let a = Array::random_using((2, 5), Uniform::new(0., 10.).unwrap(), &mut rng);
     /// println!("{:8.4}", a);
     /// // Example Output:
     /// // [[  8.6900,   6.9824,   3.8922,   6.5861,   2.4890],
@@ -270,7 +271,7 @@ where
     {
         let indices: Vec<_> = match strategy {
             SamplingStrategy::WithReplacement => {
-                let distribution = Uniform::from(0..self.len_of(axis));
+                let distribution = Uniform::new(0, self.len_of(axis)).unwrap();
                 (0..n_samples).map(|_| distribution.sample(rng)).collect()
             }
             SamplingStrategy::WithoutReplacement => index::sample(rng, self.len_of(axis), n_samples).into_vec(),
@@ -308,5 +309,5 @@ impl Arbitrary for SamplingStrategy
 
 fn get_rng() -> SmallRng
 {
-    SmallRng::from_rng(thread_rng()).expect("create SmallRng from thread_rng failed")
+    SmallRng::from_rng(&mut rng())
 }


Test patch:
diff --git a/crates/numeric-tests/tests/accuracy.rs b/crates/numeric-tests/tests/accuracy.rs
index c594f020..db10d57c 100644
--- a/crates/numeric-tests/tests/accuracy.rs
+++ b/crates/numeric-tests/tests/accuracy.rs
@@ -86,7 +86,7 @@ where
 #[test]
 fn accurate_eye_f32()
 {
-    let rng = &mut SmallRng::from_entropy();
+    let rng = &mut SmallRng::from_os_rng();
     for i in 0..20 {
         let eye = Array::eye(i);
         for j in 0..20 {
@@ -99,8 +99,8 @@ fn accurate_eye_f32()
     }
     // pick a few random sizes
     for _ in 0..10 {
-        let i = rng.gen_range(15..512);
-        let j = rng.gen_range(15..512);
+        let i = rng.random_range(15..512);
+        let j = rng.random_range(15..512);
         println!("Testing size {} by {}", i, j);
         let a = gen::<f32, _>(Ix2(i, j), rng);
         let eye = Array::eye(i);
@@ -114,7 +114,7 @@ fn accurate_eye_f32()
 #[test]
 fn accurate_eye_f64()
 {
-    let rng = &mut SmallRng::from_entropy();
+    let rng = &mut SmallRng::from_os_rng();
     let abs_tol = 1e-15;
     for i in 0..20 {
         let eye = Array::eye(i);
@@ -128,8 +128,8 @@ fn accurate_eye_f64()
     }
     // pick a few random sizes
     for _ in 0..10 {
-        let i = rng.gen_range(15..512);
-        let j = rng.gen_range(15..512);
+        let i = rng.random_range(15..512);
+        let j = rng.random_range(15..512);
         println!("Testing size {} by {}", i, j);
         let a = gen::<f64, _>(Ix2(i, j), rng);
         let eye = Array::eye(i);
@@ -172,9 +172,9 @@ fn random_matrix_mul<A>(
 ) -> (Array2<A>, Array2<A>)
 where A: LinalgScalar
 {
-    let m = rng.gen_range(15..128);
-    let k = rng.gen_range(15..128);
-    let n = rng.gen_range(15..512);
+    let m = rng.random_range(15..128);
+    let k = rng.random_range(15..128);
+    let n = rng.random_range(15..512);
     let a = generator(Ix2(m, k), rng);
     let b = generator(Ix2(n, k), rng);
     let c = if use_general {
@@ -209,7 +209,7 @@ where
     A: fmt::Debug,
 {
     // pick a few random sizes
-    let mut rng = SmallRng::from_entropy();
+    let mut rng = SmallRng::from_os_rng();
     for i in 0..20 {
         let (c, reference) = random_matrix_mul(&mut rng, i > 10, use_general, gen::<A, _>);
 
@@ -241,7 +241,7 @@ where
     A: fmt::Debug,
 {
     // pick a few random sizes
-    let mut rng = SmallRng::from_entropy();
+    let mut rng = SmallRng::from_os_rng();
     for i in 0..20 {
         let (c, reference) = random_matrix_mul(&mut rng, i > 10, true, gen_complex::<A, _>);
 
@@ -259,10 +259,10 @@ where
 fn accurate_mul_with_column_f64()
 {
     // pick a few random sizes
-    let rng = &mut SmallRng::from_entropy();
+    let rng = &mut SmallRng::from_os_rng();
     for i in 0..10 {
-        let m = rng.gen_range(1..128);
-        let k = rng.gen_range(1..350);
+        let m = rng.random_range(1..128);
+        let k = rng.random_range(1..350);
         let a = gen::<f64, _>(Ix2(m, k), rng);
         let b_owner = gen::<f64, _>(Ix2(k, k), rng);
         let b_row_col;
diff --git a/ndarray-rand/tests/tests.rs b/ndarray-rand/tests/tests.rs
index d38e8636..5d322551 100644
--- a/ndarray-rand/tests/tests.rs
+++ b/ndarray-rand/tests/tests.rs
@@ -1,6 +1,6 @@
 use ndarray::{Array, Array2, ArrayView1, Axis};
 #[cfg(feature = "quickcheck")]
-use ndarray_rand::rand::{distributions::Distribution, thread_rng};
+use ndarray_rand::rand::{distr::Distribution, rng};
 
 use ndarray::ShapeBuilder;
 use ndarray_rand::rand_distr::Uniform;
@@ -13,7 +13,7 @@ fn test_dim()
     let (mm, nn) = (5, 5);
     for m in 0..mm {
         for n in 0..nn {
-            let a = Array::random((m, n), Uniform::new(0., 2.));
+            let a = Array::random((m, n), Uniform::new(0., 2.).unwrap());
             assert_eq!(a.shape(), &[m, n]);
             assert!(a.iter().all(|x| *x < 2.));
             assert!(a.iter().all(|x| *x >= 0.));
@@ -28,7 +28,7 @@ fn test_dim_f()
     let (mm, nn) = (5, 5);
     for m in 0..mm {
         for n in 0..nn {
-            let a = Array::random((m, n).f(), Uniform::new(0., 2.));
+            let a = Array::random((m, n).f(), Uniform::new(0., 2.).unwrap());
             assert_eq!(a.shape(), &[m, n]);
             assert!(a.iter().all(|x| *x < 2.));
             assert!(a.iter().all(|x| *x >= 0.));
@@ -41,7 +41,7 @@ fn test_dim_f()
 fn sample_axis_on_view()
 {
     let m = 5;
-    let a = Array::random((m, 4), Uniform::new(0., 2.));
+    let a = Array::random((m, 4), Uniform::new(0., 2.).unwrap());
     let _samples = a
         .view()
         .sample_axis(Axis(0), m, SamplingStrategy::WithoutReplacement);
@@ -52,7 +52,7 @@ fn sample_axis_on_view()
 fn oversampling_without_replacement_should_panic()
 {
     let m = 5;
-    let a = Array::random((m, 4), Uniform::new(0., 2.));
+    let a = Array::random((m, 4), Uniform::new(0., 2.).unwrap());
     let _samples = a.sample_axis(Axis(0), m + 1, SamplingStrategy::WithoutReplacement);
 }
 
@@ -60,7 +60,7 @@ quickcheck! {
     #[cfg_attr(miri, ignore)] // Takes an insufferably long time
     fn oversampling_with_replacement_is_fine(m: u8, n: u8) -> TestResult {
         let (m, n) = (m as usize, n as usize);
-        let a = Array::random((m, n), Uniform::new(0., 2.));
+        let a = Array::random((m, n), Uniform::new(0., 2.).unwrap());
         // Higher than the length of both axes
         let n_samples = m + n + 1;
 
@@ -90,12 +90,12 @@ quickcheck! {
     #[cfg_attr(miri, ignore)] // This takes *forever* with Miri
     fn sampling_behaves_as_expected(m: u8, n: u8, strategy: SamplingStrategy) -> TestResult {
         let (m, n) = (m as usize, n as usize);
-        let a = Array::random((m, n), Uniform::new(0., 2.));
-        let mut rng = &mut thread_rng();
+        let a = Array::random((m, n), Uniform::new(0., 2.).unwrap());
+        let mut rng = &mut rng();
 
         // We don't want to deal with sampling from 0-length axes in this test
         if m != 0 {
-            let n_row_samples = Uniform::from(1..m+1).sample(&mut rng);
+            let n_row_samples = Uniform::new(1, m+1).unwrap().sample(&mut rng);
             if !sampling_works(&a, strategy.clone(), Axis(0), n_row_samples) {
                 return TestResult::failed();
             }
@@ -105,7 +105,7 @@ quickcheck! {
 
         // We don't want to deal with sampling from 0-length axes in this test
         if n != 0 {
-            let n_col_samples = Uniform::from(1..n+1).sample(&mut rng);
+            let n_col_samples = Uniform::new(1, n+1).unwrap().sample(&mut rng);
             if !sampling_works(&a, strategy, Axis(1), n_col_samples) {
                 return TestResult::failed();
             }
@@ -136,7 +136,7 @@ fn is_subset(a: &Array2<f64>, b: &ArrayView1<f64>, axis: Axis) -> bool
 fn sampling_without_replacement_from_a_zero_length_axis_should_panic()
 {
     let n = 5;
-    let a = Array::random((0, n), Uniform::new(0., 2.));
+    let a = Array::random((0, n), Uniform::new(0., 2.).unwrap());
     let _samples = a.sample_axis(Axis(0), 1, SamplingStrategy::WithoutReplacement);
 }
 
@@ -145,6 +145,6 @@ fn sampling_without_replacement_from_a_zero_length_axis_should_panic()
 fn sampling_with_replacement_from_a_zero_length_axis_should_panic()
 {
     let n = 5;
-    let a = Array::random((0, n), Uniform::new(0., 2.));
+    let a = Array::random((0, n), Uniform::new(0., 2.).unwrap());
     let _samples = a.sample_axis(Axis(0), 1, SamplingStrategy::WithReplacement);
 }