Repository: rust-ndarray/ndarray (rust)

Issue:
Dot product is only a valid call for ndarray::Array2
Dot product is only a valid call for ndarray::Array2 -- even if the ArrayD is of the same dimensions.
```rust
use ndarray::prelude::*;
fn main() {
    let mat1 = Array2::from_shape_vec((3, 2), vec![3.0; 6]).unwrap();
    let mat2 = Array2::from_shape_vec((2, 3), vec![1.0; 6]).unwrap();
    let dot = mat1.dot(&mat2);
}
```
works, while
```rust
use ndarray::prelude::*;
fn main() {
    let mat1 = ArrayD::from_shape_vec(vec![3, 2], vec![3.0; 6]).unwrap();
    let mat2 = ArrayD::from_shape_vec(vec![2, 3], vec![1.0; 6]).unwrap();
    let dot = mat1.dot(&mat2);
}
```
does not
This results in a very opaque compiler error:
```rust
overflow evaluating the requirement `&ArrayBase<_, _>: Not`
consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`ndarray-issue`)
required for `&ArrayBase<_, _>` to implement `Not`
127 redundant requirements hidden
required for `&ArrayBase<OwnedRepr<{float}>, Dim<IxDynImpl>>` to implement `Not`
```
In my opinion this should either give a clearer error message or be a valid call. I am not experienced enough with rust at the moment to suggest a fix myself.


Pull request description:
Add dot product support for ArrayD
# Add dot product support for ArrayD
Hello, awesome ndarray community! 
- This is my very first PR and implements the `Dot` trait for `ArrayD` (dynamic-dimensional arrays), addressing issue #1439. 
- for now, the tests are in same location `ndarray/src/linalg/impl_linalg.rs` 
- thanks to detailed description for the issue @JulianDicken and @JamboChen, I'm not sure my implementation match with your original discussion. it would be very helpful to give me any comments/feedback! 
 

## Changes

- Added implementation of `Dot<ArrayBase<S2, IxDyn>>` for `ArrayBase<S, IxDyn>`
- Supports the following operations:
  - Vector-vector dot product (1D × 1D)
  - Matrix-matrix multiplication (2D × 2D)
  - Matrix-vector multiplication (2D × 1D)
  - Vector-matrix multiplication (1D × 2D)
- Added comprehensive test cases covering all supported operations and error cases

## Details

converts dynamic-dimensional arrays to their fixed-dimensional counterparts when needed, leveraging the existing implementations for `Array1` and `Array2`. This ensures consistent behavior with the fixed-dimensional array implementations.

## Example Usage

```rust
use ndarray::ArrayD;

// Matrix-matrix multiplication
let mat1 = ArrayD::from_shape_vec(vec![3, 2], vec![3.0; 6]).unwrap();
let mat2 = ArrayD::from_shape_vec(vec![2, 3], vec![1.0; 6]).unwrap();
let result = mat1.dot(&mat2);

// Vector-vector dot product
let vec1 = ArrayD::from_shape_vec(vec![3], vec![1.0, 2.0, 3.0]).unwrap();
let vec2 = ArrayD::from_shape_vec(vec![3], vec![4.0, 5.0, 6.0]).unwrap();
let scalar = vec1.dot(&vec2);
```

## Testing

Added test cases for:
- Matrix-matrix multiplication
- Vector-vector dot product
- Matrix-vector multiplication
- Error cases (3D arrays, incompatible dimensions)

All tests pass and maintain consistency with the existing fixed-dimensional array implementations.

Fixes #1439

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 472b171f..d0530aff 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -484,7 +484,7 @@ version = "0.15.0"
 dependencies = [
  "ndarray",
  "quickcheck",
- "rand",
+ "rand 0.9.0",
  "rand_distr",
  "rand_isaac",
 ]
@@ -547,7 +547,7 @@ dependencies = [
  "num-complex",
  "num-traits",
  "openblas-src",
- "rand",
+ "rand 0.9.0",
  "rand_distr",
 ]
 
@@ -688,7 +688,7 @@ version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
 dependencies = [
- "rand",
+ "rand 0.8.5",
 ]
 
 [[package]]
@@ -706,19 +706,28 @@ version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 dependencies = [
- "libc",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
+dependencies = [
  "rand_chacha",
- "rand_core",
+ "rand_core 0.9.3",
+ "zerocopy",
 ]
 
 [[package]]
 name = "rand_chacha"
-version = "0.3.1"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
 dependencies = [
  "ppv-lite86",
- "rand_core",
+ "rand_core 0.9.3",
 ]
 
 [[package]]
@@ -730,23 +739,32 @@ dependencies = [
  "getrandom 0.2.15",
 ]
 
+[[package]]
+name = "rand_core"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
+dependencies = [
+ "getrandom 0.3.1",
+]
+
 [[package]]
 name = "rand_distr"
-version = "0.4.3"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
+checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463"
 dependencies = [
  "num-traits",
- "rand",
+ "rand 0.9.0",
 ]
 
 [[package]]
 name = "rand_isaac"
-version = "0.3.0"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fac4373cd91b4f55722c553fb0f286edbb81ef3ff6eec7b99d1898a4110a0b28"
+checksum = "3382fc9f0aad4f2e2a56b53d9133c8c810b4dbf21e7e370e24346161a5b2c7bd"
 dependencies = [
- "rand_core",
+ "rand_core 0.9.3",
 ]
 
 [[package]]
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())
 }
diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs
index 7472d829..e0574037 100644
--- a/src/linalg/impl_linalg.rs
+++ b/src/linalg/impl_linalg.rs
@@ -14,8 +14,11 @@ use crate::numeric_util;
 
 use crate::{LinalgScalar, Zip};
 
+#[cfg(not(feature = "std"))]
+use alloc::vec;
 #[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
+
 use std::any::TypeId;
 use std::mem::MaybeUninit;
 
@@ -353,7 +356,7 @@ where
     ///
     /// If their shapes disagree, `rhs` is broadcast to the shape of `self`.
     ///
-    /// **Panics** if broadcasting isn’t possible.
+    /// **Panics** if broadcasting isn't possible.
     #[track_caller]
     pub fn scaled_add<S2, E>(&mut self, alpha: A, rhs: &ArrayBase<S2, E>)
     where
@@ -1067,3 +1070,64 @@ mod blas_tests
         }
     }
 }
+
+/// Dot product for dynamic-dimensional arrays (`ArrayD`).
+///
+/// For one-dimensional arrays, computes the vector dot product, which is the sum
+/// of the elementwise products (no conjugation of complex operands).
+/// Both arrays must have the same length.
+///
+/// For two-dimensional arrays, performs matrix multiplication. The array shapes
+/// must be compatible in the following ways:
+/// - If `self` is *M* × *N*, then `rhs` must be *N* × *K* for matrix-matrix multiplication
+/// - If `self` is *M* × *N* and `rhs` is *N*, returns a vector of length *M*
+/// - If `self` is *M* and `rhs` is *M* × *N*, returns a vector of length *N*
+/// - If both arrays are one-dimensional of length *N*, returns a scalar
+///
+/// **Panics** if:
+/// - The arrays have dimensions other than 1 or 2
+/// - The array shapes are incompatible for the operation
+/// - For vector dot product: the vectors have different lengths
+///
+impl<A, S, S2> Dot<ArrayBase<S2, IxDyn>> for ArrayBase<S, IxDyn>
+where
+    S: Data<Elem = A>,
+    S2: Data<Elem = A>,
+    A: LinalgScalar,
+{
+    type Output = Array<A, IxDyn>;
+
+    fn dot(&self, rhs: &ArrayBase<S2, IxDyn>) -> Self::Output
+    {
+        match (self.ndim(), rhs.ndim()) {
+            (1, 1) => {
+                let a = self.view().into_dimensionality::<Ix1>().unwrap();
+                let b = rhs.view().into_dimensionality::<Ix1>().unwrap();
+                let result = a.dot(&b);
+                ArrayD::from_elem(vec![], result)
+            }
+            (2, 2) => {
+                // Matrix-matrix multiplication
+                let a = self.view().into_dimensionality::<Ix2>().unwrap();
+                let b = rhs.view().into_dimensionality::<Ix2>().unwrap();
+                let result = a.dot(&b);
+                result.into_dimensionality::<IxDyn>().unwrap()
+            }
+            (2, 1) => {
+                // Matrix-vector multiplication
+                let a = self.view().into_dimensionality::<Ix2>().unwrap();
+                let b = rhs.view().into_dimensionality::<Ix1>().unwrap();
+                let result = a.dot(&b);
+                result.into_dimensionality::<IxDyn>().unwrap()
+            }
+            (1, 2) => {
+                // Vector-matrix multiplication
+                let a = self.view().into_dimensionality::<Ix1>().unwrap();
+                let b = rhs.view().into_dimensionality::<Ix2>().unwrap();
+                let result = a.dot(&b);
+                result.into_dimensionality::<IxDyn>().unwrap()
+            }
+            _ => panic!("Dot product for ArrayD is only supported for 1D and 2D arrays"),
+        }
+    }
+}


Test patch:
diff --git a/crates/blas-tests/tests/dyn.rs b/crates/blas-tests/tests/dyn.rs
new file mode 100644
index 00000000..6c0fd975
--- /dev/null
+++ b/crates/blas-tests/tests/dyn.rs
@@ -0,0 +1,80 @@
+extern crate blas_src;
+use ndarray::{linalg::Dot, Array1, Array2, ArrayD, Ix1, Ix2};
+
+#[test]
+fn test_arrayd_dot_2d()
+{
+    let mat1 = ArrayD::from_shape_vec(vec![3, 2], vec![3.0; 6]).unwrap();
+    let mat2 = ArrayD::from_shape_vec(vec![2, 3], vec![1.0; 6]).unwrap();
+
+    let result = mat1.dot(&mat2);
+
+    // Verify the result is correct
+    assert_eq!(result.ndim(), 2);
+    assert_eq!(result.shape(), &[3, 3]);
+
+    // Compare with Array2 implementation
+    let mat1_2d = Array2::from_shape_vec((3, 2), vec![3.0; 6]).unwrap();
+    let mat2_2d = Array2::from_shape_vec((2, 3), vec![1.0; 6]).unwrap();
+    let expected = mat1_2d.dot(&mat2_2d);
+
+    assert_eq!(result.into_dimensionality::<Ix2>().unwrap(), expected);
+}
+
+#[test]
+fn test_arrayd_dot_1d()
+{
+    // Test 1D array dot product
+    let vec1 = ArrayD::from_shape_vec(vec![3], vec![1.0, 2.0, 3.0]).unwrap();
+    let vec2 = ArrayD::from_shape_vec(vec![3], vec![4.0, 5.0, 6.0]).unwrap();
+
+    let result = vec1.dot(&vec2);
+
+    // Verify scalar result
+    assert_eq!(result.ndim(), 0);
+    assert_eq!(result.shape(), &[]);
+    assert_eq!(result[[]], 32.0); // 1*4 + 2*5 + 3*6
+}
+
+#[test]
+#[should_panic(expected = "Dot product for ArrayD is only supported for 1D and 2D arrays")]
+fn test_arrayd_dot_3d()
+{
+    // Test that 3D arrays are not supported
+    let arr1 = ArrayD::from_shape_vec(vec![2, 2, 2], vec![1.0; 8]).unwrap();
+    let arr2 = ArrayD::from_shape_vec(vec![2, 2, 2], vec![1.0; 8]).unwrap();
+
+    let _result = arr1.dot(&arr2); // Should panic
+}
+
+#[test]
+#[should_panic(expected = "ndarray: inputs 2 × 3 and 4 × 5 are not compatible for matrix multiplication")]
+fn test_arrayd_dot_incompatible_dims()
+{
+    // Test arrays with incompatible dimensions
+    let arr1 = ArrayD::from_shape_vec(vec![2, 3], vec![1.0; 6]).unwrap();
+    let arr2 = ArrayD::from_shape_vec(vec![4, 5], vec![1.0; 20]).unwrap();
+
+    let _result = arr1.dot(&arr2); // Should panic
+}
+
+#[test]
+fn test_arrayd_dot_matrix_vector()
+{
+    // Test matrix-vector multiplication
+    let mat = ArrayD::from_shape_vec(vec![3, 2], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]).unwrap();
+    let vec = ArrayD::from_shape_vec(vec![2], vec![1.0, 2.0]).unwrap();
+
+    let result = mat.dot(&vec);
+
+    // Verify result
+    assert_eq!(result.ndim(), 1);
+    assert_eq!(result.shape(), &[3]);
+
+    // Compare with Array2 implementation
+    let mat_2d = Array2::from_shape_vec((3, 2), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]).unwrap();
+    let vec_1d = Array1::from_vec(vec![1.0, 2.0]);
+    let expected = mat_2d.dot(&vec_1d);
+
+    assert_eq!(result.into_dimensionality::<Ix1>().unwrap(), expected);
+}
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);
 }