From 5dab92a176ddb2f20af7e33c2f08d0870bdd18e3 Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Sun, 2 Nov 2025 20:35:56 -0500 Subject: [PATCH 1/4] Extend `ndarray-rand` to be able to randomly sample from `ArrayRef`. Prior to `ndarray` 0.17, the `RandomExt` trait exposed by `ndarray-rand` contained methods for both creating new arrays randomly whole-cloth (`random_using`) and sampling from existing arrays (`sample_axis_using`). With the introduction of reference types in `ndarray` 0.17, users should be able to sample from `ArrayRef` instances as well. We choose to expose an additional extension trait, `RandomRefExt`, that provides this functionality. We keep the methods on the old trait for backwards compatibility, but collapse the implementation and documentation to the new trait to maintain a single source of truth. --- ndarray-rand/src/lib.rs | 71 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/ndarray-rand/src/lib.rs b/ndarray-rand/src/lib.rs index 8ee2cda75..1d4723fed 100644 --- a/ndarray-rand/src/lib.rs +++ b/ndarray-rand/src/lib.rs @@ -29,12 +29,14 @@ //! that the items are not compatible (e.g. that a type doesn't implement a //! necessary trait). +#![warn(missing_docs)] + use crate::rand::distr::{Distribution, Uniform}; use crate::rand::rngs::SmallRng; use crate::rand::seq::index; use crate::rand::{rng, Rng, SeedableRng}; -use ndarray::{Array, Axis, RemoveAxis, ShapeBuilder}; +use ndarray::{Array, ArrayRef, Axis, RemoveAxis, ShapeBuilder}; use ndarray::{ArrayBase, Data, DataOwned, Dimension, RawData}; #[cfg(feature = "quickcheck")] use quickcheck::{Arbitrary, Gen}; @@ -124,6 +126,43 @@ where S: DataOwned, Sh: ShapeBuilder; + /// Sample `n_samples` lanes slicing along `axis` using the default RNG. + /// + /// See [`RandomRefExt::sample_axis`] for additional information. + fn sample_axis(&self, axis: Axis, n_samples: usize, strategy: SamplingStrategy) -> Array + where + A: Copy, + S: Data, + D: RemoveAxis; + + /// Sample `n_samples` lanes slicing along `axis` using the specified RNG `rng`. + /// + /// See [`RandomRefExt::sample_axis_using`] for additional information. + fn sample_axis_using( + &self, axis: Axis, n_samples: usize, strategy: SamplingStrategy, rng: &mut R, + ) -> Array + where + R: Rng + ?Sized, + A: Copy, + S: Data, + D: RemoveAxis; +} + +/// Constructors for sampling from [`ArrayRef`] with random elements. +/// +/// This trait extends ndarray’s `ArrayRef` and can not be implemented +/// for other types. +/// +/// The default RNG is a fast automatically seeded rng (currently +/// [`rand::rngs::SmallRng`], seeded from [`rand::thread_rng`]). +/// +/// Note that `SmallRng` is cheap to initialize and fast, but it may generate +/// low-quality random numbers, and reproducibility is not guaranteed. See its +/// documentation for information. You can select a different RNG with +/// [`.random_using()`](Self::random_using). +pub trait RandomRefExt +where D: Dimension +{ /// Sample `n_samples` lanes slicing along `axis` using the default RNG. /// /// If `strategy==SamplingStrategy::WithoutReplacement`, each lane can only be sampled once. @@ -168,7 +207,6 @@ where fn sample_axis(&self, axis: Axis, n_samples: usize, strategy: SamplingStrategy) -> Array where A: Copy, - S: Data, D: RemoveAxis; /// Sample `n_samples` lanes slicing along `axis` using the specified RNG `rng`. @@ -225,7 +263,6 @@ where where R: Rng + ?Sized, A: Copy, - S: Data, D: RemoveAxis; } @@ -259,7 +296,7 @@ where S: Data, D: RemoveAxis, { - self.sample_axis_using(axis, n_samples, strategy, &mut get_rng()) + (**self).sample_axis(axis, n_samples, strategy) } fn sample_axis_using(&self, axis: Axis, n_samples: usize, strategy: SamplingStrategy, rng: &mut R) -> Array @@ -268,6 +305,27 @@ where A: Copy, S: Data, D: RemoveAxis, + { + (&**self).sample_axis_using(axis, n_samples, strategy, rng) + } +} + +impl RandomRefExt for ArrayRef +where D: Dimension +{ + fn sample_axis(&self, axis: Axis, n_samples: usize, strategy: SamplingStrategy) -> Array + where + A: Copy, + D: RemoveAxis, + { + self.sample_axis_using(axis, n_samples, strategy, &mut get_rng()) + } + + fn sample_axis_using(&self, axis: Axis, n_samples: usize, strategy: SamplingStrategy, rng: &mut R) -> Array + where + R: Rng + ?Sized, + A: Copy, + D: RemoveAxis, { let indices: Vec<_> = match strategy { SamplingStrategy::WithReplacement => { @@ -284,9 +342,10 @@ where /// if lanes from the original array should only be sampled once (*without replacement*) or /// multiple times (*with replacement*). /// -/// [`sample_axis`]: RandomExt::sample_axis -/// [`sample_axis_using`]: RandomExt::sample_axis_using +/// [`sample_axis`]: RandomRefExt::sample_axis +/// [`sample_axis_using`]: RandomRefExt::sample_axis_using #[derive(Debug, Clone)] +#[allow(missing_docs)] pub enum SamplingStrategy { WithReplacement, From 8aa5dd85fdf84bfd6e213e35fc5b8ed8b1beea28 Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Sun, 2 Nov 2025 20:43:22 -0500 Subject: [PATCH 2/4] Fix clippy and docs --- ndarray-rand/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ndarray-rand/src/lib.rs b/ndarray-rand/src/lib.rs index 1d4723fed..f42e2789c 100644 --- a/ndarray-rand/src/lib.rs +++ b/ndarray-rand/src/lib.rs @@ -64,7 +64,7 @@ pub mod rand_distr /// Note that `SmallRng` is cheap to initialize and fast, but it may generate /// low-quality random numbers, and reproducibility is not guaranteed. See its /// documentation for information. You can select a different RNG with -/// [`.random_using()`](Self::random_using). +/// [`.random_using()`](RandomExt::random_using). pub trait RandomExt where S: RawData, @@ -306,7 +306,7 @@ where S: Data, D: RemoveAxis, { - (&**self).sample_axis_using(axis, n_samples, strategy, rng) + (**self).sample_axis_using(axis, n_samples, strategy, rng) } } From 26dc55d6cded9ad905a15cd8caa87de7383f15b5 Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Sun, 2 Nov 2025 20:44:56 -0500 Subject: [PATCH 3/4] One more doc fix --- ndarray-rand/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndarray-rand/src/lib.rs b/ndarray-rand/src/lib.rs index f42e2789c..7b02b8518 100644 --- a/ndarray-rand/src/lib.rs +++ b/ndarray-rand/src/lib.rs @@ -159,7 +159,7 @@ where /// Note that `SmallRng` is cheap to initialize and fast, but it may generate /// low-quality random numbers, and reproducibility is not guaranteed. See its /// documentation for information. You can select a different RNG with -/// [`.random_using()`](Self::random_using). +/// [`.sample_axis_using()`](RandomRefExt::sample_axis_using). pub trait RandomRefExt where D: Dimension { From 584a4a5e502506801f3c26e23628883324435686 Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Sun, 2 Nov 2025 20:58:04 -0500 Subject: [PATCH 4/4] A few more documentation changes --- ndarray-rand/src/lib.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/ndarray-rand/src/lib.rs b/ndarray-rand/src/lib.rs index 7b02b8518..d155695aa 100644 --- a/ndarray-rand/src/lib.rs +++ b/ndarray-rand/src/lib.rs @@ -53,13 +53,10 @@ pub mod rand_distr pub use rand_distr::*; } -/// Constructors for n-dimensional arrays with random elements. -/// -/// This trait extends ndarray’s `ArrayBase` and can not be implemented -/// for other types. +/// Extension trait for constructing n-dimensional arrays with random elements. /// /// The default RNG is a fast automatically seeded rng (currently -/// [`rand::rngs::SmallRng`], seeded from [`rand::thread_rng`]). +/// [`rand::rngs::SmallRng`], seeded from [`rand::rng`]). /// /// Note that `SmallRng` is cheap to initialize and fast, but it may generate /// low-quality random numbers, and reproducibility is not guaranteed. See its @@ -148,13 +145,10 @@ where D: RemoveAxis; } -/// Constructors for sampling from [`ArrayRef`] with random elements. -/// -/// This trait extends ndarray’s `ArrayRef` and can not be implemented -/// for other types. +/// Extension trait for sampling from [`ArrayRef`] with random elements. /// -/// The default RNG is a fast automatically seeded rng (currently -/// [`rand::rngs::SmallRng`], seeded from [`rand::thread_rng`]). +/// The default RNG is a fast, automatically seeded rng (currently +/// [`rand::rngs::SmallRng`], seeded from [`rand::rng`]). /// /// Note that `SmallRng` is cheap to initialize and fast, but it may generate /// low-quality random numbers, and reproducibility is not guaranteed. See its