From dc833d8bab93a060dead7e5004aba1428daa65cd Mon Sep 17 00:00:00 2001 From: Vivian Date: Tue, 27 Jan 2026 17:49:07 +0100 Subject: [PATCH 1/6] Add `linspace_exclusive` function. Add a `linspace_exclusive` function which creates a `Linspace` iterator which is exclusive at its upper bound. This is essentially the same as `linspace(endpoint=False)` in numpy. --- src/lib.rs | 2 +- src/linspace.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 41e5ca350..c26877434 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -201,7 +201,7 @@ mod layout; mod linalg_traits; mod linspace; #[cfg(feature = "std")] -pub use crate::linspace::{linspace, range, Linspace}; +pub use crate::linspace::{linspace, linspace_exclusive, range, Linspace}; mod logspace; #[cfg(feature = "std")] pub use crate::logspace::{logspace, Logspace}; diff --git a/src/linspace.rs b/src/linspace.rs index 411c480db..38fdf9f59 100644 --- a/src/linspace.rs +++ b/src/linspace.rs @@ -90,6 +90,32 @@ where F: Float } } +/// Return an iterator of evenly spaced floats. +/// +/// The `Linspace` has `n` elements from `a` to `b` (exclusive). +/// +/// The iterator element type is `F`, where `F` must implement [`Float`], e.g. +/// [`f32`] or [`f64`]. +/// +/// **Panics** if converting `n` to type `F` fails. +#[inline] +pub fn linspace_exclusive(a: F, b: F, n: usize) -> Linspace +where F: Float +{ + let step = if n > 1 { + let num_steps = F::from(n).expect("Converting number of steps to `A` must not fail."); + (b - a) / num_steps + } else { + F::zero() + }; + Linspace { + start: a, + step, + index: 0, + len: n, + } +} + /// Return an iterator of floats from `a` to `b` (exclusive), /// incrementing by `step`. /// From 7ae478c52ab63ade71ffdcb7fcc0ca21d3e85f88 Mon Sep 17 00:00:00 2001 From: Vivian Date: Thu, 29 Jan 2026 11:44:19 +0100 Subject: [PATCH 2/6] Add range implementations of linspace and logspace --- benches/construct.rs | 4 +-- src/impl_constructors.rs | 54 +++++++++++++++++-------------- src/lib.rs | 2 +- src/linspace.rs | 70 +++++++++++++++++----------------------- src/logspace.rs | 34 +++++++++++++------ 5 files changed, 86 insertions(+), 78 deletions(-) diff --git a/benches/construct.rs b/benches/construct.rs index 71a4fb905..958eaa3b6 100644 --- a/benches/construct.rs +++ b/benches/construct.rs @@ -21,7 +21,7 @@ fn zeros_f64(bench: &mut Bencher) #[bench] fn map_regular(bench: &mut test::Bencher) { - let a = Array::linspace(0., 127., 128) + let a = Array::linspace(0.0..=127.0, 128) .into_shape_with_order((8, 16)) .unwrap(); bench.iter(|| a.map(|&x| 2. * x)); @@ -31,7 +31,7 @@ fn map_regular(bench: &mut test::Bencher) #[bench] fn map_stride(bench: &mut test::Bencher) { - let a = Array::linspace(0., 127., 256) + let a = Array::linspace(0.0..=127.0, 256) .into_shape_with_order((8, 32)) .unwrap(); let av = a.slice(s![.., ..;2]); diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index ba01e2ca3..f1a9b57ed 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -44,7 +44,8 @@ use rawpointer::PointerExt; /// /// ## Constructor methods for one-dimensional arrays. impl ArrayBase -where S: DataOwned +where + S: DataOwned, { /// Create a one-dimensional array from a vector (no copying needed). /// @@ -55,13 +56,9 @@ where S: DataOwned /// /// let array = Array::from_vec(vec![1., 2., 3., 4.]); /// ``` - pub fn from_vec(v: Vec) -> Self - { + pub fn from_vec(v: Vec) -> Self { if mem::size_of::() == 0 { - assert!( - v.len() <= isize::MAX as usize, - "Length must fit in `isize`.", - ); + assert!(v.len() <= isize::MAX as usize, "Length must fit in `isize`.",); } unsafe { Self::from_shape_vec_unchecked(v.len() as Ix, v) } } @@ -76,8 +73,7 @@ where S: DataOwned /// let array = Array::from_iter(0..10); /// ``` #[allow(clippy::should_implement_trait)] - pub fn from_iter>(iterable: I) -> Self - { + pub fn from_iter>(iterable: I) -> Self { Self::from_vec(iterable.into_iter().collect()) } @@ -99,10 +95,12 @@ where S: DataOwned /// assert!(array == arr1(&[0.0, 0.25, 0.5, 0.75, 1.0])) /// ``` #[cfg(feature = "std")] - pub fn linspace(start: A, end: A, n: usize) -> Self - where A: Float + pub fn linspace(range: R, n: usize) -> Self + where + R: std::ops::RangeBounds, + A: Float, { - Self::from(to_vec(linspace::linspace(start, end, n))) + Self::from(to_vec(linspace::linspace(range, n))) } /// Create a one-dimensional array with elements from `start` to `end` @@ -118,7 +116,8 @@ where S: DataOwned /// ``` #[cfg(feature = "std")] pub fn range(start: A, end: A, step: A) -> Self - where A: Float + where + A: Float, { Self::from(to_vec(linspace::range(start, end, step))) } @@ -145,10 +144,12 @@ where S: DataOwned /// # } /// ``` #[cfg(feature = "std")] - pub fn logspace(base: A, start: A, end: A, n: usize) -> Self - where A: Float + pub fn logspace(base: A, range: R, n: usize) -> Self + where + R: std::ops::RangeBounds, + A: Float, { - Self::from(to_vec(logspace::logspace(base, start, end, n))) + Self::from(to_vec(logspace::logspace(base, range, n))) } /// Create a one-dimensional array with `n` geometrically spaced elements @@ -180,7 +181,8 @@ where S: DataOwned /// ``` #[cfg(feature = "std")] pub fn geomspace(start: A, end: A, n: usize) -> Option - where A: Float + where + A: Float, { Some(Self::from(to_vec(geomspace::geomspace(start, end, n)?))) } @@ -188,7 +190,8 @@ where S: DataOwned /// ## Constructor methods for two-dimensional arrays. impl ArrayBase -where S: DataOwned +where + S: DataOwned, { /// Create an identity matrix of size `n` (square 2D array). /// @@ -470,14 +473,14 @@ where /// ); /// ``` pub fn from_shape_vec(shape: Sh, v: Vec) -> Result - where Sh: Into> + where + Sh: Into>, { // eliminate the type parameter Sh as soon as possible Self::from_shape_vec_impl(shape.into(), v) } - fn from_shape_vec_impl(shape: StrideShape, v: Vec) -> Result - { + fn from_shape_vec_impl(shape: StrideShape, v: Vec) -> Result { let dim = shape.dim; let is_custom = shape.strides.is_custom(); dimension::can_index_slice_with_strides(&v, &dim, &shape.strides, dimension::CanIndexCheckMode::OwnedMutable)?; @@ -513,7 +516,8 @@ where /// 5. The strides must not allow any element to be referenced by two different /// indices. pub unsafe fn from_shape_vec_unchecked(shape: Sh, v: Vec) -> Self - where Sh: Into> + where + Sh: Into>, { let shape = shape.into(); let dim = shape.dim; @@ -521,8 +525,7 @@ where Self::from_vec_dim_stride_unchecked(dim, strides, v) } - unsafe fn from_vec_dim_stride_unchecked(dim: D, strides: D, mut v: Vec) -> Self - { + unsafe fn from_vec_dim_stride_unchecked(dim: D, strides: D, mut v: Vec) -> Self { // debug check for issues that indicates wrong use of this constructor debug_assert!(dimension::can_index_slice(&v, &dim, &strides, CanIndexCheckMode::OwnedMutable).is_ok()); @@ -595,7 +598,8 @@ where /// # let _ = shift_by_two; /// ``` pub fn uninit(shape: Sh) -> ArrayBase - where Sh: ShapeBuilder + where + Sh: ShapeBuilder, { unsafe { let shape = shape.into_shape_with_order(); diff --git a/src/lib.rs b/src/lib.rs index c26877434..41e5ca350 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -201,7 +201,7 @@ mod layout; mod linalg_traits; mod linspace; #[cfg(feature = "std")] -pub use crate::linspace::{linspace, linspace_exclusive, range, Linspace}; +pub use crate::linspace::{linspace, range, Linspace}; mod logspace; #[cfg(feature = "std")] pub use crate::logspace::{logspace, Logspace}; diff --git a/src/linspace.rs b/src/linspace.rs index 38fdf9f59..8f2db1257 100644 --- a/src/linspace.rs +++ b/src/linspace.rs @@ -6,13 +6,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. #![cfg(feature = "std")] + +use std::ops::{Bound, RangeBounds}; + use num_traits::Float; /// An iterator of a sequence of evenly spaced floats. /// /// Iterator element type is `F`. -pub struct Linspace -{ +pub struct Linspace { start: F, step: F, index: usize, @@ -20,13 +22,13 @@ pub struct Linspace } impl Iterator for Linspace -where F: Float +where + F: Float, { type Item = F; #[inline] - fn next(&mut self) -> Option - { + fn next(&mut self) -> Option { if self.index >= self.len { None } else { @@ -38,19 +40,18 @@ where F: Float } #[inline] - fn size_hint(&self) -> (usize, Option) - { + fn size_hint(&self) -> (usize, Option) { let n = self.len - self.index; (n, Some(n)) } } impl DoubleEndedIterator for Linspace -where F: Float +where + F: Float, { #[inline] - fn next_back(&mut self) -> Option - { + fn next_back(&mut self) -> Option { if self.index >= self.len { None } else { @@ -71,43 +72,31 @@ impl ExactSizeIterator for Linspace where Linspace: Iterator {} /// The iterator element type is `F`, where `F` must implement [`Float`], e.g. /// [`f32`] or [`f64`]. /// -/// **Panics** if converting `n - 1` to type `F` fails. +/// ## Panics +/// - If called with a range type other than `a..b` or `a..=b`. +/// - If converting `n` to type `F` fails. #[inline] -pub fn linspace(a: F, b: F, n: usize) -> Linspace -where F: Float +pub fn linspace(range: R, n: usize) -> Linspace +where + R: RangeBounds, + F: Float, { - let step = if n > 1 { - let num_steps = F::from(n - 1).expect("Converting number of steps to `A` must not fail."); - (b - a) / num_steps - } else { - F::zero() + let (a, b, num_steps) = match (range.start_bound(), range.end_bound()) { + (Bound::Included(a), Bound::Included(b)) => { + (*a, *b, F::from(n - 1).expect("Converting number of steps to `A` must not fail.")) + } + (Bound::Included(a), Bound::Excluded(b)) => { + (*a, *b, F::from(n).expect("Converting number of steps to `A` must not fail.")) + } + _ => panic!("Only a..b and a..=b ranges are supported."), }; - Linspace { - start: a, - step, - index: 0, - len: n, - } -} -/// Return an iterator of evenly spaced floats. -/// -/// The `Linspace` has `n` elements from `a` to `b` (exclusive). -/// -/// The iterator element type is `F`, where `F` must implement [`Float`], e.g. -/// [`f32`] or [`f64`]. -/// -/// **Panics** if converting `n` to type `F` fails. -#[inline] -pub fn linspace_exclusive(a: F, b: F, n: usize) -> Linspace -where F: Float -{ - let step = if n > 1 { - let num_steps = F::from(n).expect("Converting number of steps to `A` must not fail."); + let step = if num_steps > F::zero() { (b - a) / num_steps } else { F::zero() }; + Linspace { start: a, step, @@ -127,7 +116,8 @@ where F: Float /// **Panics** if converting `((b - a) / step).ceil()` to type `F` fails. #[inline] pub fn range(a: F, b: F, step: F) -> Linspace -where F: Float +where + F: Float, { let len = b - a; let steps = F::ceil(len / step); diff --git a/src/logspace.rs b/src/logspace.rs index 463012018..a62ba4263 100644 --- a/src/logspace.rs +++ b/src/logspace.rs @@ -6,6 +6,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. #![cfg(feature = "std")] + +use std::ops::{Bound, RangeBounds}; use num_traits::Float; /// An iterator of a sequence of logarithmically spaced number. @@ -79,15 +81,27 @@ impl ExactSizeIterator for Logspace where Logspace: Iterator {} /// /// **Panics** if converting `n - 1` to type `F` fails. #[inline] -pub fn logspace(base: F, a: F, b: F, n: usize) -> Logspace -where F: Float +pub fn logspace(base: F, range: R, n: usize) -> Logspace +where + R: RangeBounds, + F: Float, { - let step = if n > 1 { - let num_steps = F::from(n - 1).expect("Converting number of steps to `A` must not fail."); + let (a, b, num_steps) = match (range.start_bound(), range.end_bound()) { + (Bound::Included(a), Bound::Included(b)) => { + (*a, *b, F::from(n - 1).expect("Converting number of steps to `A` must not fail.")) + } + (Bound::Included(a), Bound::Excluded(b)) => { + (*a, *b, F::from(n).expect("Converting number of steps to `A` must not fail.")) + } + _ => panic!("Only a..b and a..=b ranges are supported."), + }; + + let step = if num_steps > F::zero() { (b - a) / num_steps } else { F::zero() }; + Logspace { sign: base.signum(), base: base.abs(), @@ -110,23 +124,23 @@ mod tests use crate::{arr1, Array1}; use approx::assert_abs_diff_eq; - let array: Array1<_> = logspace(10.0, 0.0, 3.0, 4).collect(); + let array: Array1<_> = logspace(10.0, 0.0..=3.0, 4).collect(); assert_abs_diff_eq!(array, arr1(&[1e0, 1e1, 1e2, 1e3]), epsilon = 1e-12); - let array: Array1<_> = logspace(10.0, 3.0, 0.0, 4).collect(); + let array: Array1<_> = logspace(10.0, 3.0..=0.0, 4).collect(); assert_abs_diff_eq!(array, arr1(&[1e3, 1e2, 1e1, 1e0]), epsilon = 1e-12); - let array: Array1<_> = logspace(-10.0, 3.0, 0.0, 4).collect(); + let array: Array1<_> = logspace(-10.0, 3.0..=0.0, 4).collect(); assert_abs_diff_eq!(array, arr1(&[-1e3, -1e2, -1e1, -1e0]), epsilon = 1e-12); - let array: Array1<_> = logspace(-10.0, 0.0, 3.0, 4).collect(); + let array: Array1<_> = logspace(-10.0, 0.0..=3.0, 4).collect(); assert_abs_diff_eq!(array, arr1(&[-1e0, -1e1, -1e2, -1e3]), epsilon = 1e-12); } #[test] fn iter_forward() { - let mut iter = logspace(10.0f64, 0.0, 3.0, 4); + let mut iter = logspace(10.0f64, 0.0..=3.0, 4); assert!(iter.size_hint() == (4, Some(4))); @@ -142,7 +156,7 @@ mod tests #[test] fn iter_backward() { - let mut iter = logspace(10.0f64, 0.0, 3.0, 4); + let mut iter = logspace(10.0f64, 0.0..=3.0, 4); assert!(iter.size_hint() == (4, Some(4))); From 3dfe1146f48bdac8cae6a0080e2c3316a6163e88 Mon Sep 17 00:00:00 2001 From: Vivian Date: Mon, 2 Feb 2026 10:32:51 +0100 Subject: [PATCH 3/6] nightly fmt --- src/impl_constructors.rs | 33 +++++++++++++++------------------ src/linspace.rs | 31 +++++++++++++++---------------- src/logspace.rs | 14 ++++++-------- 3 files changed, 36 insertions(+), 42 deletions(-) diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index f1a9b57ed..18cdca921 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -44,8 +44,7 @@ use rawpointer::PointerExt; /// /// ## Constructor methods for one-dimensional arrays. impl ArrayBase -where - S: DataOwned, +where S: DataOwned { /// Create a one-dimensional array from a vector (no copying needed). /// @@ -56,7 +55,8 @@ where /// /// let array = Array::from_vec(vec![1., 2., 3., 4.]); /// ``` - pub fn from_vec(v: Vec) -> Self { + pub fn from_vec(v: Vec) -> Self + { if mem::size_of::() == 0 { assert!(v.len() <= isize::MAX as usize, "Length must fit in `isize`.",); } @@ -73,7 +73,8 @@ where /// let array = Array::from_iter(0..10); /// ``` #[allow(clippy::should_implement_trait)] - pub fn from_iter>(iterable: I) -> Self { + pub fn from_iter>(iterable: I) -> Self + { Self::from_vec(iterable.into_iter().collect()) } @@ -116,8 +117,7 @@ where /// ``` #[cfg(feature = "std")] pub fn range(start: A, end: A, step: A) -> Self - where - A: Float, + where A: Float { Self::from(to_vec(linspace::range(start, end, step))) } @@ -181,8 +181,7 @@ where /// ``` #[cfg(feature = "std")] pub fn geomspace(start: A, end: A, n: usize) -> Option - where - A: Float, + where A: Float { Some(Self::from(to_vec(geomspace::geomspace(start, end, n)?))) } @@ -190,8 +189,7 @@ where /// ## Constructor methods for two-dimensional arrays. impl ArrayBase -where - S: DataOwned, +where S: DataOwned { /// Create an identity matrix of size `n` (square 2D array). /// @@ -473,14 +471,14 @@ where /// ); /// ``` pub fn from_shape_vec(shape: Sh, v: Vec) -> Result - where - Sh: Into>, + where Sh: Into> { // eliminate the type parameter Sh as soon as possible Self::from_shape_vec_impl(shape.into(), v) } - fn from_shape_vec_impl(shape: StrideShape, v: Vec) -> Result { + fn from_shape_vec_impl(shape: StrideShape, v: Vec) -> Result + { let dim = shape.dim; let is_custom = shape.strides.is_custom(); dimension::can_index_slice_with_strides(&v, &dim, &shape.strides, dimension::CanIndexCheckMode::OwnedMutable)?; @@ -516,8 +514,7 @@ where /// 5. The strides must not allow any element to be referenced by two different /// indices. pub unsafe fn from_shape_vec_unchecked(shape: Sh, v: Vec) -> Self - where - Sh: Into>, + where Sh: Into> { let shape = shape.into(); let dim = shape.dim; @@ -525,7 +522,8 @@ where Self::from_vec_dim_stride_unchecked(dim, strides, v) } - unsafe fn from_vec_dim_stride_unchecked(dim: D, strides: D, mut v: Vec) -> Self { + unsafe fn from_vec_dim_stride_unchecked(dim: D, strides: D, mut v: Vec) -> Self + { // debug check for issues that indicates wrong use of this constructor debug_assert!(dimension::can_index_slice(&v, &dim, &strides, CanIndexCheckMode::OwnedMutable).is_ok()); @@ -598,8 +596,7 @@ where /// # let _ = shift_by_two; /// ``` pub fn uninit(shape: Sh) -> ArrayBase - where - Sh: ShapeBuilder, + where Sh: ShapeBuilder { unsafe { let shape = shape.into_shape_with_order(); diff --git a/src/linspace.rs b/src/linspace.rs index 8f2db1257..a8498a483 100644 --- a/src/linspace.rs +++ b/src/linspace.rs @@ -14,7 +14,8 @@ use num_traits::Float; /// An iterator of a sequence of evenly spaced floats. /// /// Iterator element type is `F`. -pub struct Linspace { +pub struct Linspace +{ start: F, step: F, index: usize, @@ -22,13 +23,13 @@ pub struct Linspace { } impl Iterator for Linspace -where - F: Float, +where F: Float { type Item = F; #[inline] - fn next(&mut self) -> Option { + fn next(&mut self) -> Option + { if self.index >= self.len { None } else { @@ -40,18 +41,19 @@ where } #[inline] - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { let n = self.len - self.index; (n, Some(n)) } } impl DoubleEndedIterator for Linspace -where - F: Float, +where F: Float { #[inline] - fn next_back(&mut self) -> Option { + fn next_back(&mut self) -> Option + { if self.index >= self.len { None } else { @@ -82,12 +84,10 @@ where F: Float, { let (a, b, num_steps) = match (range.start_bound(), range.end_bound()) { - (Bound::Included(a), Bound::Included(b)) => { - (*a, *b, F::from(n - 1).expect("Converting number of steps to `A` must not fail.")) - } - (Bound::Included(a), Bound::Excluded(b)) => { - (*a, *b, F::from(n).expect("Converting number of steps to `A` must not fail.")) - } + (Bound::Included(a), Bound::Included(b)) => + (*a, *b, F::from(n - 1).expect("Converting number of steps to `A` must not fail.")), + (Bound::Included(a), Bound::Excluded(b)) => + (*a, *b, F::from(n).expect("Converting number of steps to `A` must not fail.")), _ => panic!("Only a..b and a..=b ranges are supported."), }; @@ -116,8 +116,7 @@ where /// **Panics** if converting `((b - a) / step).ceil()` to type `F` fails. #[inline] pub fn range(a: F, b: F, step: F) -> Linspace -where - F: Float, +where F: Float { let len = b - a; let steps = F::ceil(len / step); diff --git a/src/logspace.rs b/src/logspace.rs index a62ba4263..7e4437407 100644 --- a/src/logspace.rs +++ b/src/logspace.rs @@ -7,8 +7,8 @@ // except according to those terms. #![cfg(feature = "std")] -use std::ops::{Bound, RangeBounds}; use num_traits::Float; +use std::ops::{Bound, RangeBounds}; /// An iterator of a sequence of logarithmically spaced number. /// @@ -82,17 +82,15 @@ impl ExactSizeIterator for Logspace where Logspace: Iterator {} /// **Panics** if converting `n - 1` to type `F` fails. #[inline] pub fn logspace(base: F, range: R, n: usize) -> Logspace -where +where R: RangeBounds, F: Float, { let (a, b, num_steps) = match (range.start_bound(), range.end_bound()) { - (Bound::Included(a), Bound::Included(b)) => { - (*a, *b, F::from(n - 1).expect("Converting number of steps to `A` must not fail.")) - } - (Bound::Included(a), Bound::Excluded(b)) => { - (*a, *b, F::from(n).expect("Converting number of steps to `A` must not fail.")) - } + (Bound::Included(a), Bound::Included(b)) => + (*a, *b, F::from(n - 1).expect("Converting number of steps to `A` must not fail.")), + (Bound::Included(a), Bound::Excluded(b)) => + (*a, *b, F::from(n).expect("Converting number of steps to `A` must not fail.")), _ => panic!("Only a..b and a..=b ranges are supported."), }; From 4d0bbc0773d3277936b819a2acd1529f1c1bab19 Mon Sep 17 00:00:00 2001 From: Vivian Date: Mon, 2 Feb 2026 10:51:21 +0100 Subject: [PATCH 4/6] Use `FiniteBounds` to only allow `a..b` and `a..=b` ranges. --- benches/iter.rs | 12 ++++++------ src/finite_bounds.rs | 38 ++++++++++++++++++++++++++++++++++++ src/impl_constructors.rs | 4 ++-- src/lib.rs | 2 ++ src/linspace.rs | 32 ++++++++++++++---------------- src/logspace.rs | 42 ++++++++++++++++------------------------ 6 files changed, 79 insertions(+), 51 deletions(-) create mode 100644 src/finite_bounds.rs diff --git a/benches/iter.rs b/benches/iter.rs index bc483c8c2..0e18f1230 100644 --- a/benches/iter.rs +++ b/benches/iter.rs @@ -47,7 +47,7 @@ fn iter_sum_2d_transpose(bench: &mut Bencher) #[bench] fn iter_filter_sum_2d_u32(bench: &mut Bencher) { - let a = Array::linspace(0., 1., 256) + let a = Array::linspace(0.0..=1.0, 256) .into_shape_with_order((16, 16)) .unwrap(); let b = a.mapv(|x| (x * 100.) as u32); @@ -58,7 +58,7 @@ fn iter_filter_sum_2d_u32(bench: &mut Bencher) #[bench] fn iter_filter_sum_2d_f32(bench: &mut Bencher) { - let a = Array::linspace(0., 1., 256) + let a = Array::linspace(0.0..=1.0, 256) .into_shape_with_order((16, 16)) .unwrap(); let b = a * 100.; @@ -69,7 +69,7 @@ fn iter_filter_sum_2d_f32(bench: &mut Bencher) #[bench] fn iter_filter_sum_2d_stride_u32(bench: &mut Bencher) { - let a = Array::linspace(0., 1., 256) + let a = Array::linspace(0.0..=1.0, 256) .into_shape_with_order((16, 16)) .unwrap(); let b = a.mapv(|x| (x * 100.) as u32); @@ -81,7 +81,7 @@ fn iter_filter_sum_2d_stride_u32(bench: &mut Bencher) #[bench] fn iter_filter_sum_2d_stride_f32(bench: &mut Bencher) { - let a = Array::linspace(0., 1., 256) + let a = Array::linspace(0.0..=1.0, 256) .into_shape_with_order((16, 16)) .unwrap(); let b = a * 100.; @@ -93,7 +93,7 @@ fn iter_filter_sum_2d_stride_f32(bench: &mut Bencher) #[bench] fn iter_rev_step_by_contiguous(bench: &mut Bencher) { - let a = Array::linspace(0., 1., 512); + let a = Array::linspace(0.0..=1.0, 512); bench.iter(|| { a.iter().rev().step_by(2).for_each(|x| { black_box(x); @@ -105,7 +105,7 @@ fn iter_rev_step_by_contiguous(bench: &mut Bencher) #[bench] fn iter_rev_step_by_discontiguous(bench: &mut Bencher) { - let mut a = Array::linspace(0., 1., 1024); + let mut a = Array::linspace(0.0..=1.0, 1024); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); bench.iter(|| { a.iter().rev().step_by(2).for_each(|x| { diff --git a/src/finite_bounds.rs b/src/finite_bounds.rs new file mode 100644 index 000000000..b2cd2aa68 --- /dev/null +++ b/src/finite_bounds.rs @@ -0,0 +1,38 @@ +use num_traits::Float; + +pub enum Bound { + Included(F), + Excluded(F), +} + +/// A version of std::ops::RangeBounds that only implements a..b and a..=b ranges. +pub trait FiniteBounds { + fn start_bound(&self) -> F; + fn end_bound(&self) -> Bound; +} + +impl FiniteBounds for std::ops::Range +where + F: Float, +{ + fn start_bound(&self) -> F { + self.start + } + + fn end_bound(&self) -> Bound { + Bound::Excluded(self.end) + } +} + +impl FiniteBounds for std::ops::RangeInclusive +where + F: Float, +{ + fn start_bound(&self) -> F { + *self.start() + } + + fn end_bound(&self) -> Bound { + Bound::Included(*self.end()) + } +} diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 18cdca921..846223f26 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -98,7 +98,7 @@ where S: DataOwned #[cfg(feature = "std")] pub fn linspace(range: R, n: usize) -> Self where - R: std::ops::RangeBounds, + R: crate::finite_bounds::FiniteBounds, A: Float, { Self::from(to_vec(linspace::linspace(range, n))) @@ -146,7 +146,7 @@ where S: DataOwned #[cfg(feature = "std")] pub fn logspace(base: A, range: R, n: usize) -> Self where - R: std::ops::RangeBounds, + R: crate::finite_bounds::FiniteBounds, A: Float, { Self::from(to_vec(logspace::logspace(base, range, n))) diff --git a/src/lib.rs b/src/lib.rs index 41e5ca350..970c3f126 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -199,6 +199,8 @@ mod indexes; mod iterators; mod layout; mod linalg_traits; +#[cfg(feature = "std")] +mod finite_bounds; mod linspace; #[cfg(feature = "std")] pub use crate::linspace::{linspace, range, Linspace}; diff --git a/src/linspace.rs b/src/linspace.rs index a8498a483..f2331888b 100644 --- a/src/linspace.rs +++ b/src/linspace.rs @@ -7,15 +7,14 @@ // except according to those terms. #![cfg(feature = "std")] -use std::ops::{Bound, RangeBounds}; +use crate::finite_bounds::{Bound, FiniteBounds}; use num_traits::Float; /// An iterator of a sequence of evenly spaced floats. /// /// Iterator element type is `F`. -pub struct Linspace -{ +pub struct Linspace { start: F, step: F, index: usize, @@ -23,13 +22,13 @@ pub struct Linspace } impl Iterator for Linspace -where F: Float +where + F: Float, { type Item = F; #[inline] - fn next(&mut self) -> Option - { + fn next(&mut self) -> Option { if self.index >= self.len { None } else { @@ -41,19 +40,18 @@ where F: Float } #[inline] - fn size_hint(&self) -> (usize, Option) - { + fn size_hint(&self) -> (usize, Option) { let n = self.len - self.index; (n, Some(n)) } } impl DoubleEndedIterator for Linspace -where F: Float +where + F: Float, { #[inline] - fn next_back(&mut self) -> Option - { + fn next_back(&mut self) -> Option { if self.index >= self.len { None } else { @@ -80,15 +78,12 @@ impl ExactSizeIterator for Linspace where Linspace: Iterator {} #[inline] pub fn linspace(range: R, n: usize) -> Linspace where - R: RangeBounds, + R: FiniteBounds, F: Float, { let (a, b, num_steps) = match (range.start_bound(), range.end_bound()) { - (Bound::Included(a), Bound::Included(b)) => - (*a, *b, F::from(n - 1).expect("Converting number of steps to `A` must not fail.")), - (Bound::Included(a), Bound::Excluded(b)) => - (*a, *b, F::from(n).expect("Converting number of steps to `A` must not fail.")), - _ => panic!("Only a..b and a..=b ranges are supported."), + (a, Bound::Included(b)) => (a, b, F::from(n - 1).expect("Converting number of steps to `A` must not fail.")), + (a, Bound::Excluded(b)) => (a, b, F::from(n).expect("Converting number of steps to `A` must not fail.")), }; let step = if num_steps > F::zero() { @@ -116,7 +111,8 @@ where /// **Panics** if converting `((b - a) / step).ceil()` to type `F` fails. #[inline] pub fn range(a: F, b: F, step: F) -> Linspace -where F: Float +where + F: Float, { let len = b - a; let steps = F::ceil(len / step); diff --git a/src/logspace.rs b/src/logspace.rs index 7e4437407..fa0192446 100644 --- a/src/logspace.rs +++ b/src/logspace.rs @@ -7,14 +7,14 @@ // except according to those terms. #![cfg(feature = "std")] +use crate::finite_bounds::{Bound, FiniteBounds}; + use num_traits::Float; -use std::ops::{Bound, RangeBounds}; /// An iterator of a sequence of logarithmically spaced number. /// /// Iterator element type is `F`. -pub struct Logspace -{ +pub struct Logspace { sign: F, base: F, start: F, @@ -24,13 +24,13 @@ pub struct Logspace } impl Iterator for Logspace -where F: Float +where + F: Float, { type Item = F; #[inline] - fn next(&mut self) -> Option - { + fn next(&mut self) -> Option { if self.index >= self.len { None } else { @@ -43,19 +43,18 @@ where F: Float } #[inline] - fn size_hint(&self) -> (usize, Option) - { + fn size_hint(&self) -> (usize, Option) { let n = self.len - self.index; (n, Some(n)) } } impl DoubleEndedIterator for Logspace -where F: Float +where + F: Float, { #[inline] - fn next_back(&mut self) -> Option - { + fn next_back(&mut self) -> Option { if self.index >= self.len { None } else { @@ -83,15 +82,12 @@ impl ExactSizeIterator for Logspace where Logspace: Iterator {} #[inline] pub fn logspace(base: F, range: R, n: usize) -> Logspace where - R: RangeBounds, + R: FiniteBounds, F: Float, { let (a, b, num_steps) = match (range.start_bound(), range.end_bound()) { - (Bound::Included(a), Bound::Included(b)) => - (*a, *b, F::from(n - 1).expect("Converting number of steps to `A` must not fail.")), - (Bound::Included(a), Bound::Excluded(b)) => - (*a, *b, F::from(n).expect("Converting number of steps to `A` must not fail.")), - _ => panic!("Only a..b and a..=b ranges are supported."), + (a, Bound::Included(b)) => (a, b, F::from(n - 1).expect("Converting number of steps to `A` must not fail.")), + (a, Bound::Excluded(b)) => (a, b, F::from(n).expect("Converting number of steps to `A` must not fail.")), }; let step = if num_steps > F::zero() { @@ -111,14 +107,12 @@ where } #[cfg(test)] -mod tests -{ +mod tests { use super::logspace; #[test] #[cfg(feature = "approx")] - fn valid() - { + fn valid() { use crate::{arr1, Array1}; use approx::assert_abs_diff_eq; @@ -136,8 +130,7 @@ mod tests } #[test] - fn iter_forward() - { + fn iter_forward() { let mut iter = logspace(10.0f64, 0.0..=3.0, 4); assert!(iter.size_hint() == (4, Some(4))); @@ -152,8 +145,7 @@ mod tests } #[test] - fn iter_backward() - { + fn iter_backward() { let mut iter = logspace(10.0f64, 0.0..=3.0, 4); assert!(iter.size_hint() == (4, Some(4))); From e750399492acfe52c9cdde1b9a42bd0dd8b9dcf0 Mon Sep 17 00:00:00 2001 From: Vivian Date: Mon, 2 Feb 2026 10:51:32 +0100 Subject: [PATCH 5/6] nightly fmt --- src/finite_bounds.rs | 24 ++++++++++++++---------- src/linspace.rs | 21 +++++++++++---------- src/logspace.rs | 30 ++++++++++++++++++------------ 3 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/finite_bounds.rs b/src/finite_bounds.rs index b2cd2aa68..565fe2bcb 100644 --- a/src/finite_bounds.rs +++ b/src/finite_bounds.rs @@ -1,38 +1,42 @@ use num_traits::Float; -pub enum Bound { +pub enum Bound +{ Included(F), Excluded(F), } /// A version of std::ops::RangeBounds that only implements a..b and a..=b ranges. -pub trait FiniteBounds { +pub trait FiniteBounds +{ fn start_bound(&self) -> F; fn end_bound(&self) -> Bound; } impl FiniteBounds for std::ops::Range -where - F: Float, +where F: Float { - fn start_bound(&self) -> F { + fn start_bound(&self) -> F + { self.start } - fn end_bound(&self) -> Bound { + fn end_bound(&self) -> Bound + { Bound::Excluded(self.end) } } impl FiniteBounds for std::ops::RangeInclusive -where - F: Float, +where F: Float { - fn start_bound(&self) -> F { + fn start_bound(&self) -> F + { *self.start() } - fn end_bound(&self) -> Bound { + fn end_bound(&self) -> Bound + { Bound::Included(*self.end()) } } diff --git a/src/linspace.rs b/src/linspace.rs index f2331888b..099cf2aa6 100644 --- a/src/linspace.rs +++ b/src/linspace.rs @@ -14,7 +14,8 @@ use num_traits::Float; /// An iterator of a sequence of evenly spaced floats. /// /// Iterator element type is `F`. -pub struct Linspace { +pub struct Linspace +{ start: F, step: F, index: usize, @@ -22,13 +23,13 @@ pub struct Linspace { } impl Iterator for Linspace -where - F: Float, +where F: Float { type Item = F; #[inline] - fn next(&mut self) -> Option { + fn next(&mut self) -> Option + { if self.index >= self.len { None } else { @@ -40,18 +41,19 @@ where } #[inline] - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { let n = self.len - self.index; (n, Some(n)) } } impl DoubleEndedIterator for Linspace -where - F: Float, +where F: Float { #[inline] - fn next_back(&mut self) -> Option { + fn next_back(&mut self) -> Option + { if self.index >= self.len { None } else { @@ -111,8 +113,7 @@ where /// **Panics** if converting `((b - a) / step).ceil()` to type `F` fails. #[inline] pub fn range(a: F, b: F, step: F) -> Linspace -where - F: Float, +where F: Float { let len = b - a; let steps = F::ceil(len / step); diff --git a/src/logspace.rs b/src/logspace.rs index fa0192446..dd1b7ae19 100644 --- a/src/logspace.rs +++ b/src/logspace.rs @@ -14,7 +14,8 @@ use num_traits::Float; /// An iterator of a sequence of logarithmically spaced number. /// /// Iterator element type is `F`. -pub struct Logspace { +pub struct Logspace +{ sign: F, base: F, start: F, @@ -24,13 +25,13 @@ pub struct Logspace { } impl Iterator for Logspace -where - F: Float, +where F: Float { type Item = F; #[inline] - fn next(&mut self) -> Option { + fn next(&mut self) -> Option + { if self.index >= self.len { None } else { @@ -43,18 +44,19 @@ where } #[inline] - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { let n = self.len - self.index; (n, Some(n)) } } impl DoubleEndedIterator for Logspace -where - F: Float, +where F: Float { #[inline] - fn next_back(&mut self) -> Option { + fn next_back(&mut self) -> Option + { if self.index >= self.len { None } else { @@ -107,12 +109,14 @@ where } #[cfg(test)] -mod tests { +mod tests +{ use super::logspace; #[test] #[cfg(feature = "approx")] - fn valid() { + fn valid() + { use crate::{arr1, Array1}; use approx::assert_abs_diff_eq; @@ -130,7 +134,8 @@ mod tests { } #[test] - fn iter_forward() { + fn iter_forward() + { let mut iter = logspace(10.0f64, 0.0..=3.0, 4); assert!(iter.size_hint() == (4, Some(4))); @@ -145,7 +150,8 @@ mod tests { } #[test] - fn iter_backward() { + fn iter_backward() + { let mut iter = logspace(10.0f64, 0.0..=3.0, 4); assert!(iter.size_hint() == (4, Some(4))); From 4a6364721aede2fc106e7e27bd873c66df14a8d2 Mon Sep 17 00:00:00 2001 From: Vivian Date: Mon, 2 Feb 2026 10:55:11 +0100 Subject: [PATCH 6/6] remove panic from comment --- src/linspace.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/linspace.rs b/src/linspace.rs index 099cf2aa6..ff52bf0c1 100644 --- a/src/linspace.rs +++ b/src/linspace.rs @@ -74,9 +74,7 @@ impl ExactSizeIterator for Linspace where Linspace: Iterator {} /// The iterator element type is `F`, where `F` must implement [`Float`], e.g. /// [`f32`] or [`f64`]. /// -/// ## Panics -/// - If called with a range type other than `a..b` or `a..=b`. -/// - If converting `n` to type `F` fails. +/// **Panics** if converting `n` to type `F` fails. #[inline] pub fn linspace(range: R, n: usize) -> Linspace where