From 1a93b2cea8bad94516c5fb43039d9c57cdc0018c Mon Sep 17 00:00:00 2001 From: Nil Goyette Date: Mon, 18 Nov 2019 09:56:34 -0500 Subject: [PATCH 001/651] Add zip_mut_with layour benches --- benches/iter.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/benches/iter.rs b/benches/iter.rs index 289f1fb50..24bda95b9 100644 --- a/benches/iter.rs +++ b/benches/iter.rs @@ -370,3 +370,23 @@ fn iter_axis_chunks_5_iter_sum(bench: &mut Bencher) { .sum::() }); } + +pub fn zip_mut_with(data: &Array3, out: &mut Array3) { + out.zip_mut_with(&data, |o, &i| { + *o = i; + }); +} + +#[bench] +fn zip_mut_with_cc(b: &mut Bencher) { + let data: Array3 = Array3::zeros((ISZ, ISZ, ISZ)); + let mut out = Array3::zeros(data.dim()); + b.iter(|| black_box(zip_mut_with(&data, &mut out))); +} + +#[bench] +fn zip_mut_with_ff(b: &mut Bencher) { + let data: Array3 = Array3::zeros((ISZ, ISZ, ISZ).f()); + let mut out = Array3::zeros(data.dim().f()); + b.iter(|| black_box(zip_mut_with(&data, &mut out))); +} From 59eadb56f0f36d38754dd3399a8548f4661818ca Mon Sep 17 00:00:00 2001 From: Nil Goyette Date: Mon, 18 Nov 2019 10:16:07 -0500 Subject: [PATCH 002/651] zip_mut_with_same_shape also accept 'f' layout --- src/impl_methods.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 027f5a8af..7ccf7d13c 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -6,7 +6,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::cmp; use std::ptr as std_ptr; use std::slice; @@ -1917,18 +1916,20 @@ where F: FnMut(&mut A, &B), { debug_assert_eq!(self.shape(), rhs.shape()); - if let Some(self_s) = self.as_slice_mut() { - if let Some(rhs_s) = rhs.as_slice() { - let len = cmp::min(self_s.len(), rhs_s.len()); - let s = &mut self_s[..len]; - let r = &rhs_s[..len]; - for i in 0..len { - f(&mut s[i], &r[i]); + + // Same shape and order should have same strides + if self.strides() == rhs.strides() { + if let Some(self_s) = self.as_slice_memory_order_mut() { + if let Some(rhs_s) = rhs.as_slice_memory_order() { + for (s, r) in self_s.iter_mut().zip(rhs_s) { + f(s, &r); + } + return; } - return; } } - // otherwise, fall back to the outer iter + + // Otherwise, fall back to the outer iter self.zip_mut_with_by_rows(rhs, f); } From f5bd781df41b79d2e108df5e97a81ca72d719437 Mon Sep 17 00:00:00 2001 From: Nil Goyette Date: Thu, 28 Nov 2019 13:44:30 -0500 Subject: [PATCH 003/651] Dimension::strides_equivalent --- src/dimension/dimension_trait.rs | 19 +++++++++++++++++++ src/impl_methods.rs | 3 +-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 4bfe7c0b2..0bc03f9d5 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -229,6 +229,25 @@ pub trait Dimension: !end_iteration } + /// Returns `true` iff `strides1` and `strides2` are equivalent for the + /// shape `self`. + /// + /// The strides are equivalent if, for each axis with length > 1, the + /// strides are equal. + /// + /// Note: Returns `false` if any of the ndims don't match. + #[doc(hidden)] + fn strides_equivalent(&self, strides1: &Self, strides2: &D) -> bool + where + D: Dimension, + { + let shape_ndim = self.ndim(); + shape_ndim == strides1.ndim() + && shape_ndim == strides2.ndim() + && izip!(self.slice(), strides1.slice(), strides2.slice()) + .all(|(&d, &s1, &s2)| d <= 1 || s1 as isize == s2 as isize) + } + #[doc(hidden)] /// Return stride offset for index. fn stride_offset(index: &Self, strides: &Self) -> isize { diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 7ccf7d13c..78c4dd0af 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1917,8 +1917,7 @@ where { debug_assert_eq!(self.shape(), rhs.shape()); - // Same shape and order should have same strides - if self.strides() == rhs.strides() { + if self.dim.strides_equivalent(&self.strides, &rhs.strides) { if let Some(self_s) = self.as_slice_memory_order_mut() { if let Some(rhs_s) = rhs.as_slice_memory_order() { for (s, r) in self_s.iter_mut().zip(rhs_s) { From c1032fc713d74c8da4dfe56aaa13931571b22c6f Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 4 Dec 2019 22:05:06 -0500 Subject: [PATCH 004/651] Fix clippy (beta version) warnings --- src/impl_constructors.rs | 2 ++ src/impl_methods.rs | 28 +++++++++++++++++++++---- src/impl_raw_views.rs | 38 +++++++++++++++++++++------------- src/impl_views/constructors.rs | 8 +++++-- src/impl_views/indexing.rs | 4 ++++ src/iterators/windows.rs | 2 +- 6 files changed, 61 insertions(+), 21 deletions(-) diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 1f8b9708e..024a1e3b4 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -425,6 +425,8 @@ where /// Creates an array from a vector and interpret it according to the /// provided shape and strides. (No cloning of elements needed.) /// + /// # Safety + /// /// The caller must ensure that the following conditions are met: /// /// 1. The ndim of `dim` and `strides` must be the same. diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 027f5a8af..10b2be01a 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -572,6 +572,10 @@ where /// Return a reference to the element at `index`. /// /// **Note:** only unchecked for non-debug builds of ndarray. + /// + /// # Safety + /// + /// The caller must ensure that the index is in-bounds. #[inline] pub unsafe fn uget(&self, index: I) -> &A where @@ -587,8 +591,16 @@ where /// /// Return a mutable reference to the element at `index`. /// - /// **Note:** Only unchecked for non-debug builds of ndarray.
- /// **Note:** (For `ArcArray`) The array must be uniquely held when mutating it. + /// **Note:** Only unchecked for non-debug builds of ndarray. + /// + /// # Safety + /// + /// The caller must ensure that: + /// + /// 1. the index is in-bounds and + /// + /// 2. the data is uniquely held by the array. (This property is guaranteed + /// for `Array` and `ArrayViewMut`, but not for `ArcArray` or `CowArray`.) #[inline] pub unsafe fn uget_mut(&mut self, index: I) -> &mut A where @@ -622,8 +634,16 @@ where /// /// Indices may be equal. /// - /// **Note:** only unchecked for non-debug builds of ndarray.
- /// **Note:** (For `ArcArray`) The array must be uniquely held. + /// **Note:** only unchecked for non-debug builds of ndarray. + /// + /// # Safety + /// + /// The caller must ensure that: + /// + /// 1. both `index1 and `index2` are in-bounds and + /// + /// 2. the data is uniquely held by the array. (This property is guaranteed + /// for `Array` and `ArrayViewMut`, but not for `ArcArray` or `CowArray`.) pub unsafe fn uswap(&mut self, index1: I, index2: I) where S: DataMut, diff --git a/src/impl_raw_views.rs b/src/impl_raw_views.rs index d10c7909e..22e76277a 100644 --- a/src/impl_raw_views.rs +++ b/src/impl_raw_views.rs @@ -31,7 +31,9 @@ where /// Create an `RawArrayView` from shape information and a raw pointer /// to the elements. /// - /// Unsafe because caller is responsible for ensuring all of the following: + /// # Safety + /// + /// The caller is responsible for ensuring all of the following: /// /// * `ptr` must be non-null, and it must be safe to [`.offset()`] `ptr` by /// zero. @@ -77,10 +79,12 @@ where /// Converts to a read-only view of the array. /// - /// **Warning** from a safety standpoint, this is equivalent to - /// dereferencing a raw pointer for every element in the array. You must - /// ensure that all of the data is valid, ensure that the pointer is - /// aligned, and choose the correct lifetime. + /// # Safety + /// + /// From a safety standpoint, this is equivalent to dereferencing a raw + /// pointer for every element in the array. You must ensure that all of the + /// data is valid, ensure that the pointer is aligned, and choose the + /// correct lifetime. #[inline] pub unsafe fn deref_into_view<'a>(self) -> ArrayView<'a, A, D> { debug_assert!( @@ -163,7 +167,9 @@ where /// Create an `RawArrayViewMut` from shape information and a raw /// pointer to the elements. /// - /// Unsafe because caller is responsible for ensuring all of the following: + /// # Safety + /// + /// The caller is responsible for ensuring all of the following: /// /// * `ptr` must be non-null, and it must be safe to [`.offset()`] `ptr` by /// zero. @@ -215,10 +221,12 @@ where /// Converts to a read-only view of the array. /// - /// **Warning** from a safety standpoint, this is equivalent to - /// dereferencing a raw pointer for every element in the array. You must - /// ensure that all of the data is valid, ensure that the pointer is - /// aligned, and choose the correct lifetime. + /// # Safety + /// + /// From a safety standpoint, this is equivalent to dereferencing a raw + /// pointer for every element in the array. You must ensure that all of the + /// data is valid, ensure that the pointer is aligned, and choose the + /// correct lifetime. #[inline] pub unsafe fn deref_into_view<'a>(self) -> ArrayView<'a, A, D> { debug_assert!( @@ -230,10 +238,12 @@ where /// Converts to a mutable view of the array. /// - /// **Warning** from a safety standpoint, this is equivalent to - /// dereferencing a raw pointer for every element in the array. You must - /// ensure that all of the data is valid, ensure that the pointer is - /// aligned, and choose the correct lifetime. + /// # Safety + /// + /// From a safety standpoint, this is equivalent to dereferencing a raw + /// pointer for every element in the array. You must ensure that all of the + /// data is valid, ensure that the pointer is aligned, and choose the + /// correct lifetime. #[inline] pub unsafe fn deref_into_view_mut<'a>(self) -> ArrayViewMut<'a, A, D> { debug_assert!( diff --git a/src/impl_views/constructors.rs b/src/impl_views/constructors.rs index efa854e51..e2244dd09 100644 --- a/src/impl_views/constructors.rs +++ b/src/impl_views/constructors.rs @@ -65,7 +65,9 @@ where /// Create an `ArrayView` from shape information and a raw pointer to /// the elements. /// - /// Unsafe because caller is responsible for ensuring all of the following: + /// # Safety + /// + /// The caller is responsible for ensuring all of the following: /// /// * The elements seen by moving `ptr` according to the shape and strides /// must live at least as long as `'a` and must not be not mutably @@ -159,7 +161,9 @@ where /// Create an `ArrayViewMut` from shape information and a /// raw pointer to the elements. /// - /// Unsafe because caller is responsible for ensuring all of the following: + /// # Safety + /// + /// The caller is responsible for ensuring all of the following: /// /// * The elements seen by moving `ptr` according to the shape and strides /// must live at least as long as `'a` and must not be aliased for the diff --git a/src/impl_views/indexing.rs b/src/impl_views/indexing.rs index b03c9a9c5..d8ed956f2 100644 --- a/src/impl_views/indexing.rs +++ b/src/impl_views/indexing.rs @@ -90,6 +90,10 @@ pub trait IndexLonger { /// [1]: struct.ArrayBase.html#method.uget /// /// **Note:** only unchecked for non-debug builds of ndarray. + /// + /// # Safety + /// + /// The caller must ensure that the index is in-bounds. unsafe fn uget(self, index: I) -> Self::Output; } diff --git a/src/iterators/windows.rs b/src/iterators/windows.rs index e0abbc537..2d9095303 100644 --- a/src/iterators/windows.rs +++ b/src/iterators/windows.rs @@ -41,7 +41,7 @@ impl<'a, A, D: Dimension> Windows<'a, A, D> { unsafe { Windows { - base: ArrayView::from_shape_ptr(size.clone().strides(a.strides), a.ptr.as_ptr()), + base: ArrayView::from_shape_ptr(size.strides(a.strides), a.ptr.as_ptr()), window, strides: window_strides, } From fdeb35a9ebc937453db7eae8ff54ca400603e8de Mon Sep 17 00:00:00 2001 From: Hameer Abbasi Date: Mon, 30 Dec 2019 17:32:57 +0100 Subject: [PATCH 005/651] Update BLAS dependency. (#768) --- .gitignore | 1 + Cargo.toml | 2 +- blas-tests/Cargo.toml | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 1e7caa9ea..ac671d8d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ Cargo.lock target/ +.idea/ diff --git a/Cargo.toml b/Cargo.toml index 3da63d1d7..6c911e1a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ approx = { version = "0.3.2", optional = true } # Use via the `blas` crate feature! cblas-sys = { version = "0.1.4", optional = true, default-features = false } -blas-src = { version = "0.2.0", optional = true, default-features = false } +blas-src = { version = "0.4.0", optional = true, default-features = false } matrixmultiply = { version = "0.2.0" } serde = { version = "1.0", optional = true } diff --git a/blas-tests/Cargo.toml b/blas-tests/Cargo.toml index 9853ac634..524f8ca74 100644 --- a/blas-tests/Cargo.toml +++ b/blas-tests/Cargo.toml @@ -10,7 +10,7 @@ test = false [dev-dependencies] approx = "0.3.2" ndarray = { path = "../", features = ["approx", "blas"] } -blas-src = { version = "0.2.0", default-features = false, features = ["openblas"] } -openblas-src = { version = "0.6.0", default-features = false, features = ["cblas", "system"] } +blas-src = { version = "0.4.0", default-features = false, features = ["openblas"] } +openblas-src = { version = "0.7.0", default-features = false, features = ["cblas", "system"] } defmac = "0.2" num-traits = "0.2" From 5b5f5be218171dd4c69fbaa4e8ceccd6cd02b876 Mon Sep 17 00:00:00 2001 From: Hameer Abbasi Date: Mon, 30 Dec 2019 22:42:50 +0100 Subject: [PATCH 006/651] Fix compilation warnings. (#770) --- src/error.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/error.rs b/src/error.rs index cb896223f..79acdd8b3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -70,9 +70,11 @@ impl PartialEq for ShapeError { } } -impl Error for ShapeError { - fn description(&self) -> &str { - match self.kind() { +impl Error for ShapeError {} + +impl fmt::Display for ShapeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let description = match self.kind() { ErrorKind::IncompatibleShape => "incompatible shapes", ErrorKind::IncompatibleLayout => "incompatible memory layout", ErrorKind::RangeLimited => "the shape does not fit in type limits", @@ -80,19 +82,14 @@ impl Error for ShapeError { ErrorKind::Unsupported => "unsupported operation", ErrorKind::Overflow => "arithmetic overflow", ErrorKind::__Incomplete => "this error variant is not in use", - } - } -} - -impl fmt::Display for ShapeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ShapeError/{:?}: {}", self.kind(), self.description()) + }; + write!(f, "ShapeError/{:?}: {}", self.kind(), description) } } impl fmt::Debug for ShapeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ShapeError/{:?}: {}", self.kind(), self.description()) + write!(f, "{}", self) } } From db1a8a3f5a84bc9d683946e51669092044ffb7fb Mon Sep 17 00:00:00 2001 From: Hameer Abbasi Date: Wed, 1 Jan 2020 15:01:04 +0100 Subject: [PATCH 007/651] Update README with correct dependencies. (#772) --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index e551987c0..3f5c87c91 100644 --- a/README.rst +++ b/README.rst @@ -81,8 +81,8 @@ provider:: [dependencies] ndarray = { version = "0.13.0", features = ["blas"] } - blas-src = { version = "0.2.0", default-features = false, features = ["openblas"] } - openblas-src = { version = "0.6.0", default-features = false, features = ["cblas", "system"] } + blas-src = { version = "0.4.0", default-features = false, features = ["openblas"] } + openblas-src = { version = "0.7.0", default-features = false, features = ["cblas", "system"] } Recent Changes From 88c810d17e110a0f5860f5f7c63cdbab31d500a2 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 20 Jan 2020 19:20:59 -0500 Subject: [PATCH 008/651] Clarify BLAS versions for old releases in README --- README.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.rst b/README.rst index 3f5c87c91..cd3e369ae 100644 --- a/README.rst +++ b/README.rst @@ -84,6 +84,15 @@ provider:: blas-src = { version = "0.4.0", default-features = false, features = ["openblas"] } openblas-src = { version = "0.7.0", default-features = false, features = ["cblas", "system"] } +For official releases of ``ndarray``, the versions are: + +=========== ============ ================ +``ndarray`` ``blas-src`` ``openblas-src`` +=========== ============ ================ +0.13.0 0.2.0 0.6.0 +0.12.\* 0.2.0 0.6.0 +0.11.\* 0.1.2 0.5.0 +=========== ============ ================ Recent Changes -------------- From bca45b98845ce1863219839d4afd353b5770db1f Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 20 Jan 2020 19:26:12 -0500 Subject: [PATCH 009/651] Revert "Update README with correct dependencies. (#772)" This reverts commit db1a8a3f5a84bc9d683946e51669092044ffb7fb. We should keep the dependency versions listed in the README consistent with the `ndarray` version listed in the same README. --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index cd3e369ae..f1a3524ad 100644 --- a/README.rst +++ b/README.rst @@ -81,8 +81,8 @@ provider:: [dependencies] ndarray = { version = "0.13.0", features = ["blas"] } - blas-src = { version = "0.4.0", default-features = false, features = ["openblas"] } - openblas-src = { version = "0.7.0", default-features = false, features = ["cblas", "system"] } + blas-src = { version = "0.2.0", default-features = false, features = ["openblas"] } + openblas-src = { version = "0.6.0", default-features = false, features = ["cblas", "system"] } For official releases of ``ndarray``, the versions are: From 2f06327c52ea63216ed305786535a55abe3326a0 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 20 Jan 2020 22:38:21 -0500 Subject: [PATCH 010/651] Fix description of required layout for into_shape (#767) Previously, the first paragraph of the docs indicated that any contiguous array was accepted, while the second paragraph indicated that only c- or f-contiguous arrays were accepted. The second paragraph is correct. This commit fixes the description in the first paragraph to match. --- src/impl_methods.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 10b2be01a..788388aca 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1443,8 +1443,8 @@ where } /// Transform the array into `shape`; any shape with the same number of - /// elements is accepted, but the source array or view must be - /// contiguous, otherwise we cannot rearrange the dimension. + /// elements is accepted, but the source array or view must be in standard + /// or column-major (Fortran) layout. /// /// **Errors** if the shapes don't have the same number of elements.
/// **Errors** if the input array is not c- or f-contiguous. From 4e08989d6c7ce4f20aa0582526446556ea75a7e8 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 5 Feb 2020 20:48:45 -0500 Subject: [PATCH 011/651] Fix flatten example in ndarray_for_numpy_users The old example was missing `.cloned()`. Additionally, it didn't clearly indicate that `use std::iter::FromIterator` is necessary. --- src/doc/ndarray_for_numpy_users/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index d76de9130..b061d8e67 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -536,7 +536,7 @@ //! `a[:,np.newaxis]` or `np.expand_dims(a, axis=1)` | [`a.insert_axis(Axis(1))`][.insert_axis()] | create an array from `a`, inserting a new axis 1 //! `a.transpose()` or `a.T` | [`a.t()`][.t()] or [`a.reversed_axes()`][.reversed_axes()] | transpose of array `a` (view for `.t()` or by-move for `.reversed_axes()`) //! `np.diag(a)` | [`a.diag()`][.diag()] | view the diagonal of `a` -//! `a.flatten()` | [`Array::from_iter(a.iter())`][::from_iter()] | create a 1-D array by flattening `a` +//! `a.flatten()` | [`use std::iter::FromIterator; Array::from_iter(a.iter().cloned())`][::from_iter()] | create a 1-D array by flattening `a` //! //! ## Iteration //! From f52f26686c68ce66de4cbccf1e258cb78ab8f882 Mon Sep 17 00:00:00 2001 From: Lucas Pluvinage Date: Mon, 30 Mar 2020 01:30:20 +0200 Subject: [PATCH 012/651] Dimension::Larger should implement RemoveAxis --- src/dimension/dimension_trait.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 0bc03f9d5..4f03d3aac 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -75,7 +75,7 @@ pub trait Dimension: /// Next smaller dimension (if applicable) type Smaller: Dimension; /// Next larger dimension - type Larger: Dimension; + type Larger: Dimension + RemoveAxis; /// Returns the number of dimensions (number of axes). fn ndim(&self) -> usize; From ab249dedca74f0961499b00bde24ce35acf1316c Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 12 Apr 2020 12:26:55 +0200 Subject: [PATCH 013/651] Revert "Update BLAS dependency. (#768)" Reason for revert: breaking change, will be re-applied after minor release is out. This reverts commit fdeb35a9ebc937453db7eae8ff54ca400603e8de. --- .gitignore | 1 - Cargo.toml | 2 +- blas-tests/Cargo.toml | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index ac671d8d2..1e7caa9ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ Cargo.lock target/ -.idea/ diff --git a/Cargo.toml b/Cargo.toml index 6c911e1a5..3da63d1d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ approx = { version = "0.3.2", optional = true } # Use via the `blas` crate feature! cblas-sys = { version = "0.1.4", optional = true, default-features = false } -blas-src = { version = "0.4.0", optional = true, default-features = false } +blas-src = { version = "0.2.0", optional = true, default-features = false } matrixmultiply = { version = "0.2.0" } serde = { version = "1.0", optional = true } diff --git a/blas-tests/Cargo.toml b/blas-tests/Cargo.toml index 524f8ca74..9853ac634 100644 --- a/blas-tests/Cargo.toml +++ b/blas-tests/Cargo.toml @@ -10,7 +10,7 @@ test = false [dev-dependencies] approx = "0.3.2" ndarray = { path = "../", features = ["approx", "blas"] } -blas-src = { version = "0.4.0", default-features = false, features = ["openblas"] } -openblas-src = { version = "0.7.0", default-features = false, features = ["cblas", "system"] } +blas-src = { version = "0.2.0", default-features = false, features = ["openblas"] } +openblas-src = { version = "0.6.0", default-features = false, features = ["cblas", "system"] } defmac = "0.2" num-traits = "0.2" From bc81c51b97146f477a5cf6551d27d1c6e499b054 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 12 Apr 2020 12:44:59 +0200 Subject: [PATCH 014/651] DOC: changelog for 0.13.1 --- RELEASES.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 5d8f51a2e..071d12464 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,47 @@ +Version 0.13.1 (2020-04) +=========================== + +New features +------------ + +- New *amazing* slicing methods `multi_slice_*` by [@jturner314] + https://github.com/rust-ndarray/ndarray/pull/717 +- New method `.cast()` for raw views by [@bluss] + https://github.com/rust-ndarray/ndarray/pull/734 +- New aliases `ArcArray1`, `ArcArray2` by [@d-dorazio] + https://github.com/rust-ndarray/ndarray/pull/741 +- New array constructor `from_shape_simple_fn` by [@bluss] + https://github.com/rust-ndarray/ndarray/pull/728 +- `Dimension::Larger` now requires `RemoveAxis` by [@TheLortex] + https://github.com/rust-ndarray/ndarray/pull/792 + + +Enhancements +------------ + +- Remove itertools as dependency by [@bluss] + https://github.com/rust-ndarray/ndarray/pull/730 +- Improve `zip_mut_with` (and thus arithmetic ops) for f-order arrays by [@nilgoyette] + https://github.com/rust-ndarray/ndarray/pull/754 +- Implement `fold` for `IndicesIter` by [@jturner314] + https://github.com/rust-ndarray/ndarray/pull/733 + +API changes +----------- + +- Remove alignment restriction on raw views by [@jturner314] + https://github.com/rust-ndarray/ndarray/pull/738 + +Other changes +------------- + +- Fix documentation in ndarray for numpy users by [@jturner314] +- Improve blas version documentation by [@jturner314] +- Doc improvements by [@mockersf] https://github.com/rust-ndarray/ndarray/pull/751 +- Doc and lint related improvements by [@viniciusd] https://github.com/rust-ndarray/ndarray/pull/750 +- Release management by [@bluss] + + Version 0.13.0 (2019-09-23) =========================== @@ -895,3 +939,8 @@ Earlier releases [@termoshtt]: https://github.com/termoshtt [@rth]: https://github.com/rth [@nitsky]: https://github.com/nitsky +[@d-dorazio]: https://github.com/d-dorazio +[@nilgoyette]: https://github.com/nilgoyette +[@TheLortex]: https://github.com/TheLortex +[@mockersf]: https://github.com/mockersf +[@viniciusd]: https://github.com/viniciusd From 869f3245240bba51f1af9456b3114d823717b7d0 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 12 Apr 2020 12:53:05 +0200 Subject: [PATCH 015/651] FIX: Temporarily remove rustfmt from CI check, because rustfmt crashes --- scripts/all-tests.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index 9b41b41d8..091c38f89 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -6,7 +6,6 @@ set -e FEATURES=$1 CHANNEL=$2 -([ "$CHANNEL" != "beta" ] || (rustup component add rustfmt && cargo fmt --all -- --check)) cargo build --verbose --no-default-features # Testing both dev and release profiles helps find bugs, especially in low level code cargo test --verbose --no-default-features From c913363a156dd0e5efffae4044021351b0dcf79a Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 12 Apr 2020 13:07:17 +0200 Subject: [PATCH 016/651] FIX: Rewrite size_hint to fix clippy lint Fix warning 'this match could be written as a `let` statement' --- src/indexes.rs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/indexes.rs b/src/indexes.rs index 0218da51e..9eb7a8b5c 100644 --- a/src/indexes.rs +++ b/src/indexes.rs @@ -273,18 +273,14 @@ where if !self.has_remaining { return (0, Some(0)); } - let l = match self.index { - ref ix => { - let gone = self - .dim - .fortran_strides() - .slice() - .iter() - .zip(ix.slice().iter()) - .fold(0, |s, (&a, &b)| s + a as usize * b as usize); - self.dim.size() - gone - } - }; + let gone = self + .dim + .fortran_strides() + .slice() + .iter() + .zip(self.index.slice().iter()) + .fold(0, |s, (&a, &b)| s + a as usize * b as usize); + let l = self.dim.size() - gone; (l, Some(l)) } } From 66ab786656db6d25b955c97b0744818b1306f44b Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 13 Apr 2020 16:40:48 +0200 Subject: [PATCH 017/651] FEAT: OwnedRepr as an independent Vec-like struct Avoid the unique ownership meaning of Vec by storing the fields raw in our own representation. Supply conversion to/from Vec. --- src/data_repr.rs | 97 +++++++++++++++++++++++++++++++++++++++++ src/data_traits.rs | 18 ++++---- src/impl_owned_array.rs | 10 ++--- src/lib.rs | 9 ++-- 4 files changed, 114 insertions(+), 20 deletions(-) create mode 100644 src/data_repr.rs diff --git a/src/data_repr.rs b/src/data_repr.rs new file mode 100644 index 000000000..c7a8b96fa --- /dev/null +++ b/src/data_repr.rs @@ -0,0 +1,97 @@ + +use std::mem; +use std::ptr::NonNull; +use std::slice; +use crate::extension::nonnull; + +/// Array's representation. +/// +/// *Don’t use this type directly—use the type alias +/// [`Array`](type.Array.html) for the array type!* +// Like a Vec, but with non-unique ownership semantics +#[derive(Debug)] +pub struct OwnedRepr { + ptr: NonNull, + len: usize, + capacity: usize, +} + +impl OwnedRepr { + pub(crate) fn from(mut v: Vec) -> Self { + let len = v.len(); + let capacity = v.capacity(); + let ptr = nonnull::nonnull_from_vec_data(&mut v); + mem::forget(v); + Self { + ptr, + len, + capacity, + } + } + + pub(crate) fn into_vec(mut self) -> Vec { + let v = self.take_as_vec(); + mem::forget(self); + v + } + + pub(crate) fn as_slice(&self) -> &[A] { + unsafe { + slice::from_raw_parts(self.ptr.as_ptr(), self.len) + } + } + + pub(crate) fn len(&self) -> usize { self.len } + + pub(crate) fn as_ptr(&self) -> *const A { + self.ptr.as_ptr() + } + + pub(crate) fn as_nonnull_mut(&mut self) -> NonNull { + self.ptr + } + + fn take_as_vec(&mut self) -> Vec { + let capacity = self.capacity; + let len = self.len; + self.len = 0; + self.capacity = 0; + unsafe { + Vec::from_raw_parts(self.ptr.as_ptr(), len, capacity) + } + } +} + +impl Clone for OwnedRepr + where A: Clone +{ + fn clone(&self) -> Self { + Self::from(self.as_slice().to_owned()) + } + + fn clone_from(&mut self, other: &Self) { + let mut v = self.take_as_vec(); + let other = other.as_slice(); + + if v.len() > other.len() { + v.truncate(other.len()); + } + let (front, back) = other.split_at(v.len()); + v.clone_from_slice(front); + v.extend_from_slice(back); + *self = Self::from(v); + } +} + +impl Drop for OwnedRepr { + fn drop(&mut self) { + if self.capacity > 0 { + // drop as a Vec. + self.take_as_vec(); + } + } +} + +unsafe impl Sync for OwnedRepr where A: Sync { } +unsafe impl Send for OwnedRepr where A: Send { } + diff --git a/src/data_traits.rs b/src/data_traits.rs index b3f07959e..a2e8858ab 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -242,7 +242,7 @@ unsafe impl Data for OwnedArcRepr { D: Dimension, { Self::ensure_unique(&mut self_); - let data = OwnedRepr(Arc::try_unwrap(self_.data.0).ok().unwrap()); + let data = OwnedRepr::from(Arc::try_unwrap(self_.data.0).ok().unwrap()); ArrayBase { data, ptr: self_.ptr, @@ -264,7 +264,7 @@ unsafe impl RawDataClone for OwnedArcRepr { unsafe impl RawData for OwnedRepr { type Elem = A; fn _data_slice(&self) -> Option<&[A]> { - Some(&self.0) + Some(self.as_slice()) } private_impl! {} } @@ -303,10 +303,10 @@ where { unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) { let mut u = self.clone(); - let mut new_ptr = nonnull_from_vec_data(&mut u.0); + let mut new_ptr = u.as_nonnull_mut(); if size_of::() != 0 { let our_off = - (ptr.as_ptr() as isize - self.0.as_ptr() as isize) / mem::size_of::() as isize; + (ptr.as_ptr() as isize - self.as_ptr() as isize) / mem::size_of::() as isize; new_ptr = new_ptr.offset(our_off); } (u, new_ptr) @@ -318,12 +318,12 @@ where ptr: NonNull, ) -> NonNull { let our_off = if size_of::() != 0 { - (ptr.as_ptr() as isize - other.0.as_ptr() as isize) / mem::size_of::() as isize + (ptr.as_ptr() as isize - other.as_ptr() as isize) / mem::size_of::() as isize } else { 0 }; - self.0.clone_from(&other.0); - nonnull_from_vec_data(&mut self.0).offset(our_off) + self.clone_from(&other); + self.as_nonnull_mut().offset(our_off) } } @@ -413,10 +413,10 @@ unsafe impl<'a, A> DataShared for ViewRepr<&'a A> {} unsafe impl DataOwned for OwnedRepr { fn new(elements: Vec) -> Self { - OwnedRepr(elements) + OwnedRepr::from(elements) } fn into_shared(self) -> OwnedRcRepr { - OwnedArcRepr(Arc::new(self.0)) + OwnedArcRepr(Arc::new(self.into_vec())) } } diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 96075593f..687452f9b 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -19,22 +19,22 @@ impl Array { /// let scalar: Foo = array.into_scalar(); /// assert_eq!(scalar, Foo); /// ``` - pub fn into_scalar(mut self) -> A { + pub fn into_scalar(self) -> A { let size = ::std::mem::size_of::(); if size == 0 { // Any index in the `Vec` is fine since all elements are identical. - self.data.0.remove(0) + self.data.into_vec().remove(0) } else { // Find the index in the `Vec` corresponding to `self.ptr`. // (This is necessary because the element in the array might not be // the first element in the `Vec`, such as if the array was created // by `array![1, 2, 3, 4].slice_move(s![2])`.) let first = self.ptr.as_ptr() as usize; - let base = self.data.0.as_ptr() as usize; + let base = self.data.as_ptr() as usize; let index = (first - base) / size; debug_assert_eq!((first - base) % size, 0); // Remove the element at the index and return it. - self.data.0.remove(index) + self.data.into_vec().remove(index) } } } @@ -54,6 +54,6 @@ where /// If the array is in standard memory layout, the logical element order /// of the array (`.iter()` order) and of the returned vector will be the same. pub fn into_raw_vec(self) -> Vec { - self.data.0 + self.data.into_vec() } } diff --git a/src/lib.rs b/src/lib.rs index f9a208df1..0e8542d1b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -150,6 +150,7 @@ mod array_serde; mod arrayformat; mod arraytraits; mod data_traits; +mod data_repr; pub use crate::aliases::*; @@ -1386,12 +1387,8 @@ pub type RawArrayView = ArrayBase, D>; /// [`from_shape_ptr`](#method.from_shape_ptr) for details. pub type RawArrayViewMut = ArrayBase, D>; -/// Array's representation. -/// -/// *Don’t use this type directly—use the type alias -/// [`Array`](type.Array.html) for the array type!* -#[derive(Clone, Debug)] -pub struct OwnedRepr(Vec); +pub use data_repr::OwnedRepr; + /// RcArray's representation. /// From c4b562d6688f37992fc4e6f8c6d9038b7fc206e3 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 15 Apr 2020 19:01:57 +0200 Subject: [PATCH 018/651] FEAT: Also used OwnedRepr for ArcArray --- src/data_traits.rs | 11 +++++------ src/lib.rs | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index a2e8858ab..7e04561bc 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -8,7 +8,6 @@ //! The data (inner representation) traits for ndarray -use crate::extension::nonnull::nonnull_from_vec_data; use rawpointer::PointerExt; use std::mem::{self, size_of}; use std::ptr::NonNull; @@ -188,7 +187,7 @@ unsafe impl RawDataClone for RawViewRepr<*mut A> { unsafe impl RawData for OwnedArcRepr { type Elem = A; fn _data_slice(&self) -> Option<&[A]> { - Some(&self.0) + Some(self.0.as_slice()) } private_impl! {} } @@ -226,7 +225,7 @@ where }; let rvec = Arc::make_mut(rcvec); unsafe { - self_.ptr = nonnull_from_vec_data(rvec).offset(our_off); + self_.ptr = rvec.as_nonnull_mut().offset(our_off); } } @@ -242,7 +241,7 @@ unsafe impl Data for OwnedArcRepr { D: Dimension, { Self::ensure_unique(&mut self_); - let data = OwnedRepr::from(Arc::try_unwrap(self_.data.0).ok().unwrap()); + let data = Arc::try_unwrap(self_.data.0).ok().unwrap(); ArrayBase { data, ptr: self_.ptr, @@ -416,13 +415,13 @@ unsafe impl DataOwned for OwnedRepr { OwnedRepr::from(elements) } fn into_shared(self) -> OwnedRcRepr { - OwnedArcRepr(Arc::new(self.into_vec())) + OwnedArcRepr(Arc::new(self)) } } unsafe impl DataOwned for OwnedArcRepr { fn new(elements: Vec) -> Self { - OwnedArcRepr(Arc::new(elements)) + OwnedArcRepr(Arc::new(OwnedRepr::from(elements))) } fn into_shared(self) -> OwnedRcRepr { diff --git a/src/lib.rs b/src/lib.rs index 0e8542d1b..32830d3b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1402,7 +1402,7 @@ pub use self::OwnedArcRepr as OwnedRcRepr; /// *Don’t use this type directly—use the type alias /// [`ArcArray`](type.ArcArray.html) for the array type!* #[derive(Debug)] -pub struct OwnedArcRepr(Arc>); +pub struct OwnedArcRepr(Arc>); impl Clone for OwnedArcRepr { fn clone(&self) -> Self { From 876c5cb80d2e400056428f3e108d31b14bacad79 Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 14 Apr 2020 22:36:55 +0200 Subject: [PATCH 019/651] FEAT: Implement internal Array::maybe_uninit and assume_init Use MaybeUninit to handle uninitialized data safely. --- src/impl_constructors.rs | 20 ++++++++++++++++++++ src/impl_owned_array.rs | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 024a1e3b4..54b3a8f14 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -13,6 +13,7 @@ #![allow(clippy::match_wild_err_arm)] use num_traits::{Float, One, Zero}; +use std::mem::MaybeUninit; use crate::dimension; use crate::error::{self, ShapeError}; @@ -517,3 +518,22 @@ where Self::from_shape_vec_unchecked(shape, v) } } + +impl ArrayBase +where + S: DataOwned>, + D: Dimension, +{ + pub(crate) fn maybe_uninit(shape: Sh) -> Self + where + Sh: ShapeBuilder, + { + unsafe { + let shape = shape.into_shape(); + let size = size_of_shape_checked_unwrap!(&shape.dim); + let mut v = Vec::with_capacity(size); + v.set_len(size); + Self::from_shape_vec_unchecked(shape, v) + } + } +} diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 96075593f..174a67031 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -1,4 +1,8 @@ +use std::mem::MaybeUninit; +use std::mem::transmute; + use crate::imp_prelude::*; +use crate::OwnedRepr; /// Methods specific to `Array0`. /// @@ -57,3 +61,33 @@ where self.data.0 } } + +/// Methods specific to `Array` of `MaybeUninit`. +/// +/// ***See also all methods for [`ArrayBase`]*** +/// +/// [`ArrayBase`]: struct.ArrayBase.html +impl Array, D> +where + D: Dimension, +{ + /// Assert that the array's storage's elements are all fully initialized, and conver + /// the array from element type `MaybeUninit` to `A`. + pub(crate) unsafe fn assume_init(self) -> Array { + // NOTE: Fully initialized includes elements not reachable in current slicing/view. + // + // Should this method be generalized to all array types? + // (Will need a way to map the RawData to RawData of same kind) + + let Array { data, ptr, dim, strides } = self; + let data = transmute::>, OwnedRepr>(data); + let ptr = ptr.cast::(); + + Array { + data, + ptr, + dim, + strides, + } + } +} From 5d0159e40db141aec14574201209f11abe907392 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 13 Apr 2020 17:20:37 +0200 Subject: [PATCH 020/651] FEAT: Add method Zip::apply_collect to collect results into Array Use Array::maybe_uniit to not need to zero result elements; Restrict to Copy, because we don't implement correct drop on panic yet. --- src/zip/mod.rs | 23 +++++++++++++++++++++++ tests/azip.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 33317253e..f140a12d9 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -982,6 +982,29 @@ macro_rules! map_impl { dimension: self.dimension, } } + + /// Apply and collect the results into a new array, which has the same size as the + /// inputs. + /// + /// If all inputs are c- or f-order respectively, that is preserved in the output. + /// + /// Restricted to functions that produce copyable results for technical reasons; other + /// cases are not yet implemented. + pub fn apply_collect(self, mut f: impl FnMut($($p::Item,)* ) -> R) -> Array + where R: Copy, + { + unsafe { + let is_c = self.layout.is(CORDER); + let is_f = !is_c && self.layout.is(FORDER); + let mut output = Array::maybe_uninit(self.dimension.clone().set_f(is_f)); + self.and(&mut output) + .apply(move |$($p, )* output_| { + std::ptr::write(output_.as_mut_ptr(), f($($p ),*)); + }); + output.assume_init() + } + } + ); /// Split the `Zip` evenly in two. diff --git a/tests/azip.rs b/tests/azip.rs index 885db85b7..b969d5365 100644 --- a/tests/azip.rs +++ b/tests/azip.rs @@ -49,6 +49,34 @@ fn test_azip2_3() { assert!(a != b); } +#[test] +#[cfg(feature = "approx")] +fn test_zip_collect() { + use approx::assert_abs_diff_eq; + + // test Zip::apply_collect and that it preserves c/f layout. + + let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j + 1) as f32); + let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); + + { + let a = Zip::from(&b).and(&c).apply_collect(|x, y| x + y); + + assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); + assert_eq!(a.strides(), b.strides()); + } + + { + let b = b.t(); + let c = c.t(); + + let a = Zip::from(&b).and(&c).apply_collect(|x, y| x + y); + + assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); + assert_eq!(a.strides(), b.strides()); + } +} + #[test] fn test_azip_syntax_trailing_comma() { let mut b = Array::::zeros((5, 5)); From 07cf42dc02407b1c92bcae0306a55960f60d6499 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 15 Apr 2020 19:43:03 +0200 Subject: [PATCH 021/651] TEST: Update benchmarks for addition to also test Zip::apply_collect() --- benches/bench1.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/benches/bench1.rs b/benches/bench1.rs index 190ab5065..d7fa9f163 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -258,23 +258,35 @@ fn add_2d_zip(bench: &mut test::Bencher) { } #[bench] -fn add_2d_alloc(bench: &mut test::Bencher) { +fn add_2d_alloc_plus(bench: &mut test::Bencher) { let a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| &a + &b); } #[bench] -fn add_2d_zip_alloc(bench: &mut test::Bencher) { +fn add_2d_alloc_zip_uninit(bench: &mut test::Bencher) { let a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| unsafe { let mut c = Array::uninitialized(a.dim()); - azip!((&a in &a, &b in &b, c in &mut c) *c = a + b); + azip!((&a in &a, &b in &b, c in c.raw_view_mut()) + std::ptr::write(c, a + b) + ); c }); } +#[bench] +fn add_2d_alloc_zip_collect(bench: &mut test::Bencher) { + let a = Array::::zeros((ADD2DSZ, ADD2DSZ)); + let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); + bench.iter(|| { + Zip::from(&a).and(&b).apply_collect(|&x, &y| x + y) + }); +} + + #[bench] fn add_2d_assign_ops(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); From 5c3bc3df2880c26beea6485349a01ef6032c4e55 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 15 Apr 2020 21:38:05 +0200 Subject: [PATCH 022/651] FEAT: Add Zip::apply_assign_into --- src/argument_traits.rs | 31 +++++++++++++++++++++++++++++++ src/lib.rs | 2 ++ src/zip/mod.rs | 33 +++++++++++++++++++++++++-------- src/zip/zipmacro.rs | 6 ++++++ tests/azip.rs | 31 +++++++++++++++++++++++++++++++ 5 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 src/argument_traits.rs diff --git a/src/argument_traits.rs b/src/argument_traits.rs new file mode 100644 index 000000000..a93e33a12 --- /dev/null +++ b/src/argument_traits.rs @@ -0,0 +1,31 @@ +use std::cell::Cell; +use std::mem::MaybeUninit; + + +/// A producer element that can be assigned to once +pub trait AssignElem { + /// Assign the value `input` to the element that self represents. + fn assign_elem(self, input: T); +} + +/// Assignable element, simply `*self = input`. +impl<'a, T> AssignElem for &'a mut T { + fn assign_elem(self, input: T) { + *self = input; + } +} + +/// Assignable element, simply `self.set(input)`. +impl<'a, T> AssignElem for &'a Cell { + fn assign_elem(self, input: T) { + self.set(input); + } +} + +/// Assignable element, the item in the MaybeUninit is overwritten (prior value, if any, is not +/// read or dropped). +impl<'a, T> AssignElem for &'a mut MaybeUninit { + fn assign_elem(self, input: T) { + *self = MaybeUninit::new(input); + } +} diff --git a/src/lib.rs b/src/lib.rs index f9a208df1..c8d750d7a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -149,6 +149,8 @@ mod array_approx; mod array_serde; mod arrayformat; mod arraytraits; +mod argument_traits; +pub use crate::argument_traits::AssignElem; mod data_traits; pub use crate::aliases::*; diff --git a/src/zip/mod.rs b/src/zip/mod.rs index f140a12d9..a2fd08502 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -10,6 +10,7 @@ mod zipmacro; use crate::imp_prelude::*; +use crate::AssignElem; use crate::IntoDimension; use crate::Layout; use crate::NdIndex; @@ -579,6 +580,7 @@ pub struct Zip { layout: Layout, } + impl Zip<(P,), D> where D: Dimension, @@ -990,21 +992,36 @@ macro_rules! map_impl { /// /// Restricted to functions that produce copyable results for technical reasons; other /// cases are not yet implemented. - pub fn apply_collect(self, mut f: impl FnMut($($p::Item,)* ) -> R) -> Array + pub fn apply_collect(self, f: impl FnMut($($p::Item,)* ) -> R) -> Array where R: Copy, { + // To support non-Copy elements, implementation of dropping partial array (on + // panic) is needed + let is_c = self.layout.is(CORDER); + let is_f = !is_c && self.layout.is(FORDER); + let mut output = Array::maybe_uninit(self.dimension.clone().set_f(is_f)); + self.apply_assign_into(&mut output, f); unsafe { - let is_c = self.layout.is(CORDER); - let is_f = !is_c && self.layout.is(FORDER); - let mut output = Array::maybe_uninit(self.dimension.clone().set_f(is_f)); - self.and(&mut output) - .apply(move |$($p, )* output_| { - std::ptr::write(output_.as_mut_ptr(), f($($p ),*)); - }); output.assume_init() } } + /// Apply and assign the results into the producer `into`, which should have the same + /// size as the other inputs. + /// + /// The producer should have assignable items as dictated by the `AssignElem` trait, + /// for example `&mut R`. + pub fn apply_assign_into(self, into: Q, mut f: impl FnMut($($p::Item,)* ) -> R) + where Q: IntoNdProducer, + Q::Item: AssignElem + { + self.and(into) + .apply(move |$($p, )* output_| { + output_.assign_elem(f($($p ),*)); + }); + } + + ); /// Split the `Zip` evenly in two. diff --git a/src/zip/zipmacro.rs b/src/zip/zipmacro.rs index ea616a05e..3ac73b8fe 100644 --- a/src/zip/zipmacro.rs +++ b/src/zip/zipmacro.rs @@ -122,6 +122,12 @@ macro_rules! azip { $(.and($prod))* .$apply(|$first_pat, $($pat),*| $body) }; + + // Unindexed with one or more producer, no loop body + (@build $apply:ident $first_prod:expr $(, $prod:expr)* $(,)?) => { + $crate::Zip::from($first_prod) + $(.and($prod))* + }; // catch-all rule (@build $($t:tt)*) => { compile_error!("Invalid syntax in azip!()") }; ($($t:tt)*) => { diff --git a/tests/azip.rs b/tests/azip.rs index b969d5365..90da41853 100644 --- a/tests/azip.rs +++ b/tests/azip.rs @@ -77,6 +77,37 @@ fn test_zip_collect() { } } +#[test] +#[cfg(feature = "approx")] +fn test_zip_assign_into() { + use approx::assert_abs_diff_eq; + + let mut a = Array::::zeros((5, 10)); + let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j + 1) as f32); + let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); + + Zip::from(&b).and(&c).apply_assign_into(&mut a, |x, y| x + y); + + assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); +} + +#[test] +#[cfg(feature = "approx")] +fn test_zip_assign_into_cell() { + use approx::assert_abs_diff_eq; + use std::cell::Cell; + + let a = Array::, _>::default((5, 10)); + let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j + 1) as f32); + let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); + + Zip::from(&b).and(&c).apply_assign_into(&a, |x, y| x + y); + let a2 = a.mapv(|elt| elt.get()); + + assert_abs_diff_eq!(a2, &b + &c, epsilon = 1e-6); +} + + #[test] fn test_azip_syntax_trailing_comma() { let mut b = Array::::zeros((5, 5)); From f6d6f01897807529bdeb72b036a3f05ec506372c Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 16 Apr 2020 11:58:13 +0200 Subject: [PATCH 023/651] FIX: Factor out making the uninit array in Zip --- src/zip/mod.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/zip/mod.rs b/src/zip/mod.rs index a2fd08502..52f98699f 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -9,6 +9,8 @@ #[macro_use] mod zipmacro; +use std::mem::MaybeUninit; + use crate::imp_prelude::*; use crate::AssignElem; use crate::IntoDimension; @@ -737,6 +739,12 @@ where self.dimension[unroll_axis] = inner_len; FoldWhile::Continue(acc) } + + pub(crate) fn uninitalized_for_current_layout(&self) -> Array, D> + { + let is_f = !self.layout.is(CORDER) && self.layout.is(FORDER); + Array::maybe_uninit(self.dimension.clone().set_f(is_f)) + } } /* @@ -997,9 +1005,7 @@ macro_rules! map_impl { { // To support non-Copy elements, implementation of dropping partial array (on // panic) is needed - let is_c = self.layout.is(CORDER); - let is_f = !is_c && self.layout.is(FORDER); - let mut output = Array::maybe_uninit(self.dimension.clone().set_f(is_f)); + let mut output = self.uninitalized_for_current_layout::(); self.apply_assign_into(&mut output, f); unsafe { output.assume_init() From 739dd6aa541c89ac9b5d5ebbf0a3f807ce664d4b Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 16 Apr 2020 12:56:36 +0200 Subject: [PATCH 024/651] FEAT: Add Zip::par_apply_collect, par_apply_assign_into --- src/parallel/impl_par_methods.rs | 53 +++++++++++++++++++++++++++----- tests/par_zip.rs | 42 +++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 8 deletions(-) diff --git a/src/parallel/impl_par_methods.rs b/src/parallel/impl_par_methods.rs index 88fe769bf..867cd07ad 100644 --- a/src/parallel/impl_par_methods.rs +++ b/src/parallel/impl_par_methods.rs @@ -1,4 +1,5 @@ -use crate::{ArrayBase, DataMut, Dimension, NdProducer, Zip}; +use crate::{Array, ArrayBase, DataMut, Dimension, IntoNdProducer, NdProducer, Zip}; +use crate::AssignElem; use crate::parallel::prelude::*; @@ -43,7 +44,7 @@ where // Zip macro_rules! zip_impl { - ($([$($p:ident)*],)+) => { + ($([$notlast:ident $($p:ident)*],)+) => { $( #[allow(non_snake_case)] impl Zip<($($p,)*), D> @@ -63,16 +64,52 @@ macro_rules! zip_impl { { self.into_par_iter().for_each(move |($($p,)*)| function($($p),*)) } + + expand_if!(@bool [$notlast] + + /// Apply and collect the results into a new array, which has the same size as the + /// inputs. + /// + /// If all inputs are c- or f-order respectively, that is preserved in the output. + /// + /// Restricted to functions that produce copyable results for technical reasons; other + /// cases are not yet implemented. + pub fn par_apply_collect(self, f: impl Fn($($p::Item,)* ) -> R + Sync + Send) -> Array + where R: Copy + Send + { + let mut output = self.uninitalized_for_current_layout::(); + self.par_apply_assign_into(&mut output, f); + unsafe { + output.assume_init() + } + } + + /// Apply and assign the results into the producer `into`, which should have the same + /// size as the other inputs. + /// + /// The producer should have assignable items as dictated by the `AssignElem` trait, + /// for example `&mut R`. + pub fn par_apply_assign_into(self, into: Q, f: impl Fn($($p::Item,)* ) -> R + Sync + Send) + where Q: IntoNdProducer, + Q::Item: AssignElem + Send, + Q::Output: Send, + { + self.and(into) + .par_apply(move |$($p, )* output_| { + output_.assign_elem(f($($p ),*)); + }); + } + ); } )+ } } zip_impl! { - [P1], - [P1 P2], - [P1 P2 P3], - [P1 P2 P3 P4], - [P1 P2 P3 P4 P5], - [P1 P2 P3 P4 P5 P6], + [true P1], + [true P1 P2], + [true P1 P2 P3], + [true P1 P2 P3 P4], + [true P1 P2 P3 P4 P5], + [false P1 P2 P3 P4 P5 P6], } diff --git a/tests/par_zip.rs b/tests/par_zip.rs index f10f3acde..a24dc8e80 100644 --- a/tests/par_zip.rs +++ b/tests/par_zip.rs @@ -70,3 +70,45 @@ fn test_zip_index_4() { assert_eq!(*elt, j); } } + +#[test] +#[cfg(feature = "approx")] +fn test_zip_collect() { + use approx::assert_abs_diff_eq; + + // test Zip::apply_collect and that it preserves c/f layout. + + let b = Array::from_shape_fn((M, N), |(i, j)| 1. / (i + 2 * j + 1) as f32); + let c = Array::from_shape_fn((M, N), |(i, j)| f32::ln((1 + i + j) as f32)); + + { + let a = Zip::from(&b).and(&c).par_apply_collect(|x, y| x + y); + + assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); + assert_eq!(a.strides(), b.strides()); + } + + { + let b = b.t(); + let c = c.t(); + + let a = Zip::from(&b).and(&c).par_apply_collect(|x, y| x + y); + + assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); + assert_eq!(a.strides(), b.strides()); + } +} + +#[test] +#[cfg(feature = "approx")] +fn test_zip_assign_into() { + use approx::assert_abs_diff_eq; + + let mut a = Array::::zeros((M, N)); + let b = Array::from_shape_fn((M, N), |(i, j)| 1. / (i + 2 * j + 1) as f32); + let c = Array::from_shape_fn((M, N), |(i, j)| f32::ln((1 + i + j) as f32)); + + Zip::from(&b).and(&c).par_apply_assign_into(&mut a, |x, y| x + y); + + assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); +} From f7319b31ce7c84cb2a34532fe9d98f24a2d2115f Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 18 Apr 2020 13:11:30 +0200 Subject: [PATCH 025/651] FIX: Use pub(crate) methods instead of internal trait for Layout (This is just a remainder from before pub(crate) was a feature we could use) --- src/layout/layoutfmt.rs | 1 - src/layout/mod.rs | 19 ++++++------------- src/zip/mod.rs | 1 - 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/layout/layoutfmt.rs b/src/layout/layoutfmt.rs index cb799ca81..d5049512e 100644 --- a/src/layout/layoutfmt.rs +++ b/src/layout/layoutfmt.rs @@ -7,7 +7,6 @@ // except according to those terms. use super::Layout; -use super::LayoutPriv; const LAYOUT_NAMES: &[&str] = &["C", "F"]; diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 741a0e054..57983cc3b 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -1,35 +1,28 @@ mod layoutfmt; -// public but users don't interact with it +// public struct but users don't interact with it #[doc(hidden)] /// Memory layout description #[derive(Copy, Clone)] pub struct Layout(u32); -pub trait LayoutPriv: Sized { - fn new(x: u32) -> Self; - fn and(self, flag: Self) -> Self; - fn is(self, flag: u32) -> bool; - fn flag(self) -> u32; -} - -impl LayoutPriv for Layout { +impl Layout { #[inline(always)] - fn new(x: u32) -> Self { + pub(crate) fn new(x: u32) -> Self { Layout(x) } #[inline(always)] - fn is(self, flag: u32) -> bool { + pub(crate) fn is(self, flag: u32) -> bool { self.0 & flag != 0 } #[inline(always)] - fn and(self, flag: Layout) -> Layout { + pub(crate) fn and(self, flag: Layout) -> Layout { Layout(self.0 & flag.0) } #[inline(always)] - fn flag(self) -> u32 { + pub(crate) fn flag(self) -> u32 { self.0 } } diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 52f98699f..0968dff3d 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -18,7 +18,6 @@ use crate::Layout; use crate::NdIndex; use crate::indexes::{indices, Indices}; -use crate::layout::LayoutPriv; use crate::layout::{CORDER, FORDER}; /// Return if the expression is a break value. From 0ceaf429f88a2e9fd2c7c88d1c07d72ace89e5d4 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 18 Apr 2020 14:12:41 +0200 Subject: [PATCH 026/651] FIX: Skip dropping array elements that don't need drop While this sounds tautological; to be careful, set the vector length to zero before dropping the vector of the internal storage of an array. See the comment in the code for why tihs makes a difference. --- src/data_repr.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/data_repr.rs b/src/data_repr.rs index c7a8b96fa..ccaed85c0 100644 --- a/src/data_repr.rs +++ b/src/data_repr.rs @@ -86,6 +86,18 @@ impl Clone for OwnedRepr impl Drop for OwnedRepr { fn drop(&mut self) { if self.capacity > 0 { + // correct because: If the elements don't need dropping, an + // empty Vec is ok. Only the Vec's allocation needs dropping. + // + // implemented because: in some places in ndarray + // where A: Copy (hence does not need drop) we use uninitialized elements in + // vectors. Setting the length to 0 avoids that the vector tries to + // drop, slice or otherwise produce values of these elements. + // (The details of the validity letting this happen with nonzero len, are + // under discussion as of this writing.) + if !mem::needs_drop::() { + self.len = 0; + } // drop as a Vec. self.take_as_vec(); } From 1fed7c28ccd80298ee1313336085a4a298d6fe8c Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 18 Apr 2020 15:25:33 +0200 Subject: [PATCH 027/651] DOC: Update examples and doc for Zip --- src/parallel/mod.rs | 2 ++ src/zip/mod.rs | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/parallel/mod.rs b/src/parallel/mod.rs index 60dbe4662..3583d342f 100644 --- a/src/parallel/mod.rs +++ b/src/parallel/mod.rs @@ -22,6 +22,8 @@ //! - [`ArrayBase::par_map_inplace()`] //! - [`ArrayBase::par_mapv_inplace()`] //! - [`Zip::par_apply()`] (all arities) +//! - [`Zip::par_apply_collect()`] (all arities) +//! - [`Zip::par_apply_assign_into()`] (all arities) //! //! Note that you can use the parallel iterator for [Zip] to access all other //! rayon parallel iterator methods. diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 0968dff3d..fc2812a49 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -573,6 +573,14 @@ impl NdProducer for RawArrayViewMut { /// /// // Check the result against the built in `.sum_axis()` along axis 1. /// assert_eq!(totals, a.sum_axis(Axis(1))); +/// +/// +/// // Example 3: Recreate Example 2 using apply_collect to make a new array +/// +/// let mut totals2 = Zip::from(a.genrows()).apply_collect(|row| row.sum()); +/// +/// // Check the result against the previous example. +/// assert_eq!(totals, totals2); /// ``` #[derive(Debug, Clone)] pub struct Zip { From e7e164ead0465ea551175c7d2dd6c1cd1d98a154 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 18 Apr 2020 20:26:13 +0200 Subject: [PATCH 028/651] FIX: Use ManuallyDrop instead of forget in data_repr ManuallyDrop has the benefit that we use it up front, instead of after any "critical" operations. --- src/data_repr.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/data_repr.rs b/src/data_repr.rs index ccaed85c0..4839a8439 100644 --- a/src/data_repr.rs +++ b/src/data_repr.rs @@ -1,5 +1,6 @@ use std::mem; +use std::mem::ManuallyDrop; use std::ptr::NonNull; use std::slice; use crate::extension::nonnull; @@ -17,11 +18,11 @@ pub struct OwnedRepr { } impl OwnedRepr { - pub(crate) fn from(mut v: Vec) -> Self { + pub(crate) fn from(v: Vec) -> Self { + let mut v = ManuallyDrop::new(v); let len = v.len(); let capacity = v.capacity(); let ptr = nonnull::nonnull_from_vec_data(&mut v); - mem::forget(v); Self { ptr, len, @@ -29,10 +30,8 @@ impl OwnedRepr { } } - pub(crate) fn into_vec(mut self) -> Vec { - let v = self.take_as_vec(); - mem::forget(self); - v + pub(crate) fn into_vec(self) -> Vec { + ManuallyDrop::new(self).take_as_vec() } pub(crate) fn as_slice(&self) -> &[A] { From abc1813c3c23f4616851fc9a74b771a7b90fce76 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 18 Apr 2020 20:36:07 +0200 Subject: [PATCH 029/651] FEAT: Add constructor Array::maybe_uninit for an uninitialized array --- src/impl_constructors.rs | 59 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 54b3a8f14..804ad417e 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -524,7 +524,64 @@ where S: DataOwned>, D: Dimension, { - pub(crate) fn maybe_uninit(shape: Sh) -> Self + /// Create an array with uninitalized elements, shape `shape`. + /// + /// The uninitialized elements of type `A` are represented by the type `MaybeUninit`, + /// an easier way to handle uninit values correctly. + /// + /// Only *when* the array is completely initialized with valid elements, can it be + /// converted to an array of `A` elements using [`assume_init()`]. + /// + /// **Panics** if the number of elements in `shape` would overflow isize. + /// + /// ### Safety + /// + /// The whole of the array must be initialized before it is converted + /// using [`assume_init()`] or otherwise traversed. + /// + /// ### Examples + /// + /// It is possible to assign individual values through `*elt = MaybeUninit::new(value)` + /// and so on. + /// + /// ``` + /// use ndarray::{s, Array2}; + /// use ndarray::Zip; + /// use ndarray::Axis; + /// + /// // Example Task: Let's create a transposed copy of the input + /// + /// fn shift_by_two(a: &Array2) -> Array2 { + /// // create an uninitialized array + /// let mut b = Array2::maybe_uninit(a.dim()); + /// + /// // two first columns in b are two last in a + /// // rest of columns in b are the initial columns in a + /// + /// assign_to(a.slice(s![.., -2..]), b.slice_mut(s![.., ..2])); + /// assign_to(a.slice(s![.., 2..]), b.slice_mut(s![.., ..-2])); + /// + /// // Now we can promise that `b` is safe to use with all operations + /// unsafe { + /// b.assume_init() + /// } + /// } + /// + /// use ndarray::{IntoNdProducer, AssignElem}; + /// + /// fn assign_to<'a, P1, P2, A>(from: P1, to: P2) + /// where P1: IntoNdProducer, + /// P2: IntoNdProducer, + /// P2::Item: AssignElem, + /// A: Clone + 'a + /// { + /// Zip::from(from) + /// .apply_assign_into(to, A::clone); + /// } + /// + /// # shift_by_two(&Array2::zeros((8, 8))); + /// ``` + pub fn maybe_uninit(shape: Sh) -> Self where Sh: ShapeBuilder, { From 46b294608ec97a52ed22d089ce2a278f245a6434 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 18 Apr 2020 20:40:13 +0200 Subject: [PATCH 030/651] FEAT: New method .assume_init() for all array types Add representation trait RawDataSubst that array storage types correctly (emulation of higher kinded types). Implement it as a transmute-like operation, using the representation guarantees that MaybeUninit documents. Make it a public method - avaialable on all array types - owned arrays, arcarray, array views and so on. The safety percussions of exposing this transformation are only explored to an extent, but it's a type change without value conversion so there should not be much that can go wrong. Interesting aspects include being able to have one ArcArray handle with the old type and one with the new type, or one owned array with old and a view with new. --- src/data_traits.rs | 35 ++++++++++++++++++ src/impl_owned_array.rs | 33 ----------------- src/impl_special_element_types.rs | 59 +++++++++++++++++++++++++++++++ src/lib.rs | 2 ++ 4 files changed, 96 insertions(+), 33 deletions(-) create mode 100644 src/impl_special_element_types.rs diff --git a/src/data_traits.rs b/src/data_traits.rs index 7e04561bc..8c22a225c 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -528,3 +528,38 @@ unsafe impl<'a, A> Data for CowRepr<'a, A> { } unsafe impl<'a, A> DataMut for CowRepr<'a, A> where A: Clone {} + +/// Array representation trait. +/// +/// The RawDataSubst trait maps the element type of array storage, while +/// keeping the same kind of storage. +/// +/// For example, `RawDataSubst` can map the type `OwnedRepr` to `OwnedRepr`. +pub trait RawDataSubst: RawData { + /// The resulting array storage of the same kind but substituted element type + type Output: RawData; +} + +impl RawDataSubst for OwnedRepr { + type Output = OwnedRepr; +} + +impl RawDataSubst for OwnedArcRepr { + type Output = OwnedArcRepr; +} + +impl RawDataSubst for RawViewRepr<*const A> { + type Output = RawViewRepr<*const B>; +} + +impl RawDataSubst for RawViewRepr<*mut A> { + type Output = RawViewRepr<*mut B>; +} + +impl<'a, A: 'a, B: 'a> RawDataSubst for ViewRepr<&'a A> { + type Output = ViewRepr<&'a B>; +} + +impl<'a, A: 'a, B: 'a> RawDataSubst for ViewRepr<&'a mut A> { + type Output = ViewRepr<&'a mut B>; +} diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 8ea10501d..f219b7b69 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -1,8 +1,5 @@ -use std::mem::MaybeUninit; -use std::mem::transmute; use crate::imp_prelude::*; -use crate::OwnedRepr; /// Methods specific to `Array0`. /// @@ -61,33 +58,3 @@ where self.data.into_vec() } } - -/// Methods specific to `Array` of `MaybeUninit`. -/// -/// ***See also all methods for [`ArrayBase`]*** -/// -/// [`ArrayBase`]: struct.ArrayBase.html -impl Array, D> -where - D: Dimension, -{ - /// Assert that the array's storage's elements are all fully initialized, and conver - /// the array from element type `MaybeUninit` to `A`. - pub(crate) unsafe fn assume_init(self) -> Array { - // NOTE: Fully initialized includes elements not reachable in current slicing/view. - // - // Should this method be generalized to all array types? - // (Will need a way to map the RawData to RawData of same kind) - - let Array { data, ptr, dim, strides } = self; - let data = transmute::>, OwnedRepr>(data); - let ptr = ptr.cast::(); - - Array { - data, - ptr, - dim, - strides, - } - } -} diff --git a/src/impl_special_element_types.rs b/src/impl_special_element_types.rs new file mode 100644 index 000000000..9b86625a1 --- /dev/null +++ b/src/impl_special_element_types.rs @@ -0,0 +1,59 @@ +// Copyright 2020 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::mem::size_of; +use std::mem::ManuallyDrop; +use std::mem::MaybeUninit; + +use crate::imp_prelude::*; +use crate::RawDataSubst; + + +/// Methods specific to arrays with `MaybeUninit` elements. +/// +/// ***See also all methods for [`ArrayBase`]*** +/// +/// [`ArrayBase`]: struct.ArrayBase.html +impl ArrayBase +where + S: RawDataSubst>, + D: Dimension, +{ + /// **Promise** that the array's elements are all fully initialized, and convert + /// the array from element type `MaybeUninit` to `A`. + /// + /// For owned arrays, the promise must include all of the Array's storage + pub unsafe fn assume_init(self) -> ArrayBase<>::Output, D> { + // NOTE: Fully initialized includes elements not reachable in current slicing/view. + + let ArrayBase { data, ptr, dim, strides } = self; + + // transmute from storage of MaybeUninit to storage of A + let data = unlimited_transmute::(data); + let ptr = ptr.cast::(); + + ArrayBase { + data, + ptr, + dim, + strides, + } + } +} + +/// Transmute from A to B. +/// +/// Like transmute, but does not have the compile-time size check which blocks +/// using regular transmute for "S to S::Output". +/// +/// **Panics** if the size of A and B are different. +unsafe fn unlimited_transmute(data: A) -> B { + assert_eq!(size_of::(), size_of::()); + let old_data = ManuallyDrop::new(data); + (&*old_data as *const A as *const B).read() +} diff --git a/src/lib.rs b/src/lib.rs index 0df209cbb..eb09d31ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -159,6 +159,7 @@ pub use crate::aliases::*; #[allow(deprecated)] pub use crate::data_traits::{ Data, DataClone, DataMut, DataOwned, DataShared, RawData, RawDataClone, RawDataMut, + RawDataSubst, }; mod free_functions; @@ -1483,6 +1484,7 @@ mod impl_constructors; mod impl_methods; mod impl_owned_array; +mod impl_special_element_types; /// Private Methods impl ArrayBase From c4f153fcba1579426959570aff73ecdad80bd725 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 18 Apr 2020 20:43:32 +0200 Subject: [PATCH 031/651] TEST: Add test for Array::maybe_uninit and .assume_init() --- tests/array-construct.rs | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/array-construct.rs b/tests/array-construct.rs index 6c1caf1ef..97e4ef491 100644 --- a/tests/array-construct.rs +++ b/tests/array-construct.rs @@ -7,6 +7,7 @@ use defmac::defmac; use ndarray::prelude::*; +use ndarray::Zip; #[test] fn test_from_shape_fn() { @@ -194,3 +195,46 @@ fn deny_wraparound_uninit() { let _five_large = Array::::uninitialized((3, 7, 29, 36760123, 823996703)); } } + + +#[test] +fn maybe_uninit_1() { + use std::mem::MaybeUninit; + + unsafe { + // Array + type Mat = Array, D>; + + let mut a = Mat::maybe_uninit((10, 10)); + a.mapv_inplace(|_| MaybeUninit::new(1.)); + + let a_init = a.assume_init(); + assert_eq!(a_init, Array2::from_elem(a_init.dim(), 1.)); + + // ArcArray + type ArcMat = ArcArray, D>; + + let mut a = ArcMat::maybe_uninit((10, 10)); + a.mapv_inplace(|_| MaybeUninit::new(1.)); + let a2 = a.clone(); + + let a_init = a.assume_init(); + assert_eq!(a_init, Array2::from_elem(a_init.dim(), 1.)); + + // ArrayView + let av_init = a2.view().assume_init(); + assert_eq!(av_init, Array2::from_elem(a_init.dim(), 1.)); + + // RawArrayViewMut + let mut a = Mat::maybe_uninit((10, 10)); + let v = a.raw_view_mut(); + Zip::from(v) + .apply(|ptr| *(*ptr).as_mut_ptr() = 1.); + + let u = a.raw_view_mut().assume_init(); + + Zip::from(u) + .apply(|ptr| assert_eq!(*ptr, 1.)); + + } +} From ba55e4bf3f20ea205842e7b235ed126b79359109 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 18 Apr 2020 20:49:07 +0200 Subject: [PATCH 032/651] DOC: Array::uninitialized doc update --- src/impl_constructors.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 804ad417e..a4e236e28 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -467,6 +467,9 @@ where /// Create an array with uninitalized elements, shape `shape`. /// + /// Prefer to use [`maybe_uninit()`](ArrayBase::maybe_uninit) if possible, because it is + /// easier to use correctly. + /// /// **Panics** if the number of elements in `shape` would overflow isize. /// /// ### Safety From a172258080250f8432b5da7353556540985bb849 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 18 Apr 2020 20:51:50 +0200 Subject: [PATCH 033/651] DOC: Update doc for maybe_uninit and .assume_init() --- src/impl_constructors.rs | 6 ++++-- src/impl_special_element_types.rs | 10 +++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index a4e236e28..964003dd8 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -533,20 +533,22 @@ where /// an easier way to handle uninit values correctly. /// /// Only *when* the array is completely initialized with valid elements, can it be - /// converted to an array of `A` elements using [`assume_init()`]. + /// converted to an array of `A` elements using [`.assume_init()`]. /// /// **Panics** if the number of elements in `shape` would overflow isize. /// /// ### Safety /// /// The whole of the array must be initialized before it is converted - /// using [`assume_init()`] or otherwise traversed. + /// using [`.assume_init()`] or otherwise traversed. /// /// ### Examples /// /// It is possible to assign individual values through `*elt = MaybeUninit::new(value)` /// and so on. /// + /// [`.assume_init()`]: ArrayBase::assume_init + /// /// ``` /// use ndarray::{s, Array2}; /// use ndarray::Zip; diff --git a/src/impl_special_element_types.rs b/src/impl_special_element_types.rs index 9b86625a1..40cba5822 100644 --- a/src/impl_special_element_types.rs +++ b/src/impl_special_element_types.rs @@ -27,7 +27,15 @@ where /// **Promise** that the array's elements are all fully initialized, and convert /// the array from element type `MaybeUninit` to `A`. /// - /// For owned arrays, the promise must include all of the Array's storage + /// For example, it can convert an `Array, D>` to `Array`. + /// + /// ## Safety + /// + /// Safe to use if all the array's elements have been initialized. + /// + /// Note that for owned and shared ownership arrays, the promise must include all of the + /// array's storage; it is for example possible to slice these in place, but that must + /// only be done after all elements have been initialized. pub unsafe fn assume_init(self) -> ArrayBase<>::Output, D> { // NOTE: Fully initialized includes elements not reachable in current slicing/view. From 684b9c4c40778785a39bd26df8229c610c881c53 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 18 Apr 2020 14:38:11 +0200 Subject: [PATCH 034/651] TEST: Update example sort-axis to use MaybeUninit --- examples/sort-axis.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/examples/sort-axis.rs b/examples/sort-axis.rs index 4465464b8..55d20950c 100644 --- a/examples/sort-axis.rs +++ b/examples/sort-axis.rs @@ -102,26 +102,31 @@ where assert_eq!(axis_len, perm.indices.len()); debug_assert!(perm.correct()); - let mut v = Vec::with_capacity(self.len()); - let mut result; + let mut result = Array::maybe_uninit(self.dim()); // panic-critical begin: we must not panic unsafe { - v.set_len(self.len()); - result = Array::from_shape_vec_unchecked(self.dim(), v); + // logically move ownership of all elements from self into result + // the result realizes this ownership at .assume_init() further down + let mut moved_elements = 0; for i in 0..axis_len { let perm_i = perm.indices[i]; Zip::from(result.index_axis_mut(axis, perm_i)) .and(self.index_axis(axis, i)) - .apply(|to, from| copy_nonoverlapping(from, to, 1)); + .apply(|to, from| { + copy_nonoverlapping(from, to.as_mut_ptr(), 1); + moved_elements += 1; + }); } // forget moved array elements but not its vec + // old_storage drops empty let mut old_storage = self.into_raw_vec(); old_storage.set_len(0); - // old_storage drops empty + + debug_assert_eq!(result.len(), moved_elements); + result.assume_init() } // panic-critical end - result } } From bdc07248a6e5075a219e24584222b79ee97fba1d Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 19 Apr 2020 09:42:24 +0200 Subject: [PATCH 035/651] TEST: To show the improvement of maybe_uninit, fix the comments in sort-axis.rs See comments. There is no real panic critical section, just the small section that hands over ownership. --- examples/sort-axis.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/sort-axis.rs b/examples/sort-axis.rs index 55d20950c..eabf9bb50 100644 --- a/examples/sort-axis.rs +++ b/examples/sort-axis.rs @@ -104,7 +104,6 @@ where let mut result = Array::maybe_uninit(self.dim()); - // panic-critical begin: we must not panic unsafe { // logically move ownership of all elements from self into result // the result realizes this ownership at .assume_init() further down @@ -118,15 +117,16 @@ where moved_elements += 1; }); } + debug_assert_eq!(result.len(), moved_elements); + // panic-critical begin: we must not panic // forget moved array elements but not its vec // old_storage drops empty let mut old_storage = self.into_raw_vec(); old_storage.set_len(0); - debug_assert_eq!(result.len(), moved_elements); result.assume_init() + // panic-critical end } - // panic-critical end } } From 48606c63f0fc903cfb48dc188839c16b3ddc5501 Mon Sep 17 00:00:00 2001 From: andrei-papou Date: Tue, 1 Oct 2019 16:03:05 +0300 Subject: [PATCH 036/651] Renamed `stack` to `concatenate` and added numpy-compliant `stack` function --- src/doc/ndarray_for_numpy_users/mod.rs | 5 +- src/impl_methods.rs | 4 +- src/lib.rs | 2 +- src/stacking.rs | 75 ++++++++++++++++++++++---- tests/stacking.rs | 33 +++++++++--- 5 files changed, 96 insertions(+), 23 deletions(-) diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index b061d8e67..79d1314a8 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -532,7 +532,8 @@ //! ------|-----------|------ //! `a[:] = 3.` | [`a.fill(3.)`][.fill()] | set all array elements to the same scalar value //! `a[:] = b` | [`a.assign(&b)`][.assign()] | copy the data from array `b` into array `a` -//! `np.concatenate((a,b), axis=1)` | [`stack![Axis(1), a, b]`][stack!] or [`stack(Axis(1), &[a.view(), b.view()])`][stack()] | concatenate arrays `a` and `b` along axis 1 +//! `np.concatenate((a,b), axis=1)` | [`concatenate![Axis(1), a, b]`][concatenate!] or [`concatenate(Axis(1), &[a.view(), b.view()])`][concatenate()] | concatenate arrays `a` and `b` along axis 1 +//! `np.stack((a,b), axis=1)` | [`stack![Axis(1), a, b]`][stack!] or [`stack(Axis(1), vec![a.view(), b.view()])`][stack()] | stack arrays `a` and `b` along axis 1 //! `a[:,np.newaxis]` or `np.expand_dims(a, axis=1)` | [`a.insert_axis(Axis(1))`][.insert_axis()] | create an array from `a`, inserting a new axis 1 //! `a.transpose()` or `a.T` | [`a.t()`][.t()] or [`a.reversed_axes()`][.reversed_axes()] | transpose of array `a` (view for `.t()` or by-move for `.reversed_axes()`) //! `np.diag(a)` | [`a.diag()`][.diag()] | view the diagonal of `a` @@ -640,6 +641,8 @@ //! [.slice_move()]: ../../struct.ArrayBase.html#method.slice_move //! [.slice_mut()]: ../../struct.ArrayBase.html#method.slice_mut //! [.shape()]: ../../struct.ArrayBase.html#method.shape +//! [concatenate!]: ../../macro.concatenate.html +//! [concatenate()]: ../../fn.concatenate.html //! [stack!]: ../../macro.stack.html //! [stack()]: ../../fn.stack.html //! [.strides()]: ../../struct.ArrayBase.html#method.strides diff --git a/src/impl_methods.rs b/src/impl_methods.rs index db502663f..9853a1c47 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -28,7 +28,7 @@ use crate::iter::{ IndexedIter, IndexedIterMut, Iter, IterMut, Lanes, LanesMut, Windows, }; use crate::slice::MultiSlice; -use crate::stacking::stack; +use crate::stacking::concatenate; use crate::{NdIndex, Slice, SliceInfo, SliceOrIndex}; /// # Methods For All Array Types @@ -840,7 +840,7 @@ where dim.set_axis(axis, 0); unsafe { Array::from_shape_vec_unchecked(dim, vec![]) } } else { - stack(axis, &subs).unwrap() + concatenate(axis, &subs).unwrap() } } diff --git a/src/lib.rs b/src/lib.rs index eb09d31ea..191687c02 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,7 +131,7 @@ use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes, Lane pub use crate::arraytraits::AsArray; pub use crate::linalg_traits::{LinalgScalar, NdFloat}; -pub use crate::stacking::stack; +pub use crate::stacking::{concatenate, stack}; pub use crate::impl_views::IndexLonger; pub use crate::shape_builder::ShapeBuilder; diff --git a/src/stacking.rs b/src/stacking.rs index e998b6d15..975a840d6 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -17,21 +17,21 @@ use crate::imp_prelude::*; /// if the result is larger than is possible to represent. /// /// ``` -/// use ndarray::{arr2, Axis, stack}; +/// use ndarray::{arr2, Axis, concatenate}; /// /// let a = arr2(&[[2., 2.], /// [3., 3.]]); /// assert!( -/// stack(Axis(0), &[a.view(), a.view()]) +/// concatenate(Axis(0), &[a.view(), a.view()]) /// == Ok(arr2(&[[2., 2.], /// [3., 3.], /// [2., 2.], /// [3., 3.]])) /// ); /// ``` -pub fn stack<'a, A, D>( +pub fn concatenate( axis: Axis, - arrays: &[ArrayView<'a, A, D>], + arrays: &[ArrayView], ) -> Result, ShapeError> where A: Copy, @@ -76,26 +76,45 @@ where Ok(res) } -/// Stack arrays along the given axis. +pub fn stack( + axis: Axis, + arrays: Vec>, +) -> Result, ShapeError> +where + A: Copy, + D: Dimension, + D::Larger: RemoveAxis, +{ + if let Some(ndim) = D::NDIM { + if axis.index() > ndim { + return Err(from_kind(ErrorKind::OutOfBounds)); + } + } + let arrays: Vec> = arrays.into_iter() + .map(|a| a.insert_axis(axis)).collect(); + concatenate(axis, &arrays) +} + +/// Concatenate arrays along the given axis. /// -/// Uses the [`stack`][1] function, calling `ArrayView::from(&a)` on each +/// Uses the [`concatenate`][1] function, calling `ArrayView::from(&a)` on each /// argument `a`. /// -/// [1]: fn.stack.html +/// [1]: fn.concatenate.html /// -/// ***Panics*** if the `stack` function would return an error. +/// ***Panics*** if the `concatenate` function would return an error. /// /// ``` /// extern crate ndarray; /// -/// use ndarray::{arr2, stack, Axis}; +/// use ndarray::{arr2, concatenate, Axis}; /// /// # fn main() { /// /// let a = arr2(&[[2., 2.], /// [3., 3.]]); /// assert!( -/// stack![Axis(0), a, a] +/// concatenate![Axis(0), a, a] /// == arr2(&[[2., 2.], /// [3., 3.], /// [2., 2.], @@ -104,8 +123,42 @@ where /// # } /// ``` #[macro_export] +macro_rules! concatenate { + ($axis:expr, $( $array:expr ),+ ) => { + $crate::concatenate($axis, &[ $($crate::ArrayView::from(&$array) ),* ]).unwrap() + } +} + +/// Stack arrays along the new axis. +/// +/// Uses the [`stack`][1] function, calling `ArrayView::from(&a)` on each +/// argument `a`. +/// +/// [1]: fn.concatenate.html +/// +/// ***Panics*** if the `stack` function would return an error. +/// +/// ``` +/// extern crate ndarray; +/// +/// use ndarray::{arr2, arr3, stack, Axis}; +/// +/// # fn main() { +/// +/// let a = arr2(&[[2., 2.], +/// [3., 3.]]); +/// assert!( +/// stack![Axis(0), a, a] +/// == arr3(&[[[2., 2.], +/// [3., 3.]], +/// [[2., 2.], +/// [3., 3.]]]) +/// ); +/// # } +/// ``` +#[macro_export] macro_rules! stack { ($axis:expr, $( $array:expr ),+ ) => { - $crate::stack($axis, &[ $($crate::ArrayView::from(&$array) ),* ]).unwrap() + $crate::stack($axis, vec![ $($crate::ArrayView::from(&$array) ),* ]).unwrap() } } diff --git a/tests/stacking.rs b/tests/stacking.rs index a9a031711..2baaeed04 100644 --- a/tests/stacking.rs +++ b/tests/stacking.rs @@ -1,26 +1,43 @@ -use ndarray::{arr2, aview1, stack, Array2, Axis, ErrorKind}; +use ndarray::{arr2, arr3, aview1, concatenate, Array2, Axis, ErrorKind, Ix1}; #[test] -fn stacking() { +fn concatenating() { let a = arr2(&[[2., 2.], [3., 3.]]); - let b = ndarray::stack(Axis(0), &[a.view(), a.view()]).unwrap(); + let b = ndarray::concatenate(Axis(0), &[a.view(), a.view()]).unwrap(); assert_eq!(b, arr2(&[[2., 2.], [3., 3.], [2., 2.], [3., 3.]])); - let c = stack![Axis(0), a, b]; + let c = concatenate![Axis(0), a, b]; assert_eq!( c, arr2(&[[2., 2.], [3., 3.], [2., 2.], [3., 3.], [2., 2.], [3., 3.]]) ); - let d = stack![Axis(0), a.row(0), &[9., 9.]]; + let d = concatenate![Axis(0), a.row(0), &[9., 9.]]; assert_eq!(d, aview1(&[2., 2., 9., 9.])); - let res = ndarray::stack(Axis(1), &[a.view(), c.view()]); + let res = ndarray::concatenate(Axis(1), &[a.view(), c.view()]); + assert_eq!(res.unwrap_err().kind(), ErrorKind::IncompatibleShape); + + let res = ndarray::concatenate(Axis(2), &[a.view(), c.view()]); + assert_eq!(res.unwrap_err().kind(), ErrorKind::OutOfBounds); + + let res: Result, _> = ndarray::concatenate(Axis(0), &[]); + assert_eq!(res.unwrap_err().kind(), ErrorKind::Unsupported); +} + +#[test] +fn stacking() { + let a = arr2(&[[2., 2.], [3., 3.]]); + let b = ndarray::stack(Axis(0), vec![a.view(), a.view()]).unwrap(); + assert_eq!(b, arr3(&[[[2., 2.], [3., 3.]], [[2., 2.], [3., 3.]]])); + + let c = arr2(&[[3., 2., 3.], [2., 3., 2.]]); + let res = ndarray::stack(Axis(1), vec![a.view(), c.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::IncompatibleShape); - let res = ndarray::stack(Axis(2), &[a.view(), c.view()]); + let res = ndarray::stack(Axis(3), vec![a.view(), a.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::OutOfBounds); - let res: Result, _> = ndarray::stack(Axis(0), &[]); + let res: Result, _> = ndarray::stack::<_, Ix1>(Axis(0), vec![]); assert_eq!(res.unwrap_err().kind(), ErrorKind::Unsupported); } From 9f1a3d08a1d7a5b1cebc6801e0dc6688fafee754 Mon Sep 17 00:00:00 2001 From: andrei-papou Date: Tue, 1 Oct 2019 16:23:32 +0300 Subject: [PATCH 037/651] Rustfmt fixes --- src/stacking.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/stacking.rs b/src/stacking.rs index 975a840d6..d0d4812b3 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -29,10 +29,7 @@ use crate::imp_prelude::*; /// [3., 3.]])) /// ); /// ``` -pub fn concatenate( - axis: Axis, - arrays: &[ArrayView], -) -> Result, ShapeError> +pub fn concatenate(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> where A: Copy, D: RemoveAxis, @@ -90,8 +87,8 @@ where return Err(from_kind(ErrorKind::OutOfBounds)); } } - let arrays: Vec> = arrays.into_iter() - .map(|a| a.insert_axis(axis)).collect(); + let arrays: Vec> = + arrays.into_iter().map(|a| a.insert_axis(axis)).collect(); concatenate(axis, &arrays) } From 2113b7a578f1d20367060d7dcec5992ac02f75a4 Mon Sep 17 00:00:00 2001 From: Fuyang Liu Date: Tue, 21 Apr 2020 17:16:10 +0200 Subject: [PATCH 038/651] Add quick start readme (#785) Co-authored-by: Luca Palmieri --- README-quick-start.md | 606 ++++++++++++++++++++++++++++++++++++++++++ README.rst | 1 + 2 files changed, 607 insertions(+) create mode 100644 README-quick-start.md diff --git a/README-quick-start.md b/README-quick-start.md new file mode 100644 index 000000000..2adc04d70 --- /dev/null +++ b/README-quick-start.md @@ -0,0 +1,606 @@ +# Quickstart tutorial + +If you are familiar with Python Numpy, do check out this [For Numpy User Doc](https://docs.rs/ndarray/0.13.0/ndarray/doc/ndarray_for_numpy_users/index.html) +after you go through this tutorial. + +You can use [play.integer32.com](https://play.integer32.com/) to immediately try out the examples. + +## The Basics + +Just create your first 2x3 floating-point ndarray +```rust +use ndarray::prelude::*; + +fn main() { + let a = array![ + [1.,2.,3.], + [4.,5.,6.], + ]; + assert_eq!(a.ndim(), 2); // get the number of dimensions of array a + assert_eq!(a.len(), 6); // get the number of elements in array a + assert_eq!(a.shape(), [2, 3]); // get the shape of array a + assert_eq!(a.is_empty(), false); // check if the array has zero elements + + println!("{:?}", a); +} +``` +This code will create a simple array and output to stdout: +``` +[[1.0, 2.0, 3.0], + [4.0, 5.0, 6.0]], shape=[2, 3], strides=[3, 1], layout=C (0x1), const ndim=2 +``` + +## Array Creation + +### Element type and dimensionality + +Now let's create more arrays. How about try make a zero array with dimension of (3, 2, 4)? + +```rust +use ndarray::prelude::*; +use ndarray::Array; +fn main() { + let a = Array::zeros((3, 2, 4).f()); + println!("{:?}", a); +} +``` +gives +``` +| let a = Array::zeros((3, 2, 4).f()); +| - ^^^^^^^^^^^^ cannot infer type for type parameter `A` +``` +Note that the compiler needs to infer the element type and dimensionality from context. In this +case the compiler failed to do that. Now we give it the type and let it infer dimensionality + +```rust +use ndarray::prelude::*; +use ndarray::Array; +fn main() { + let a = Array::::zeros((3, 2, 4).f()); + println!("{:?}", a); +} +``` +and now it works: +``` +[[[0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0]], + + [[0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0]], + + [[0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0]]], shape=[3, 2, 4], strides=[1, 3, 6], layout=F (0x2), const ndim=3 +``` + +We can also specify its dimensionality + +```rust +use ndarray::prelude::*; +use ndarray::{Array, Ix3}; +fn main() { + let a = Array::::zeros((3, 2, 4).f()); + println!("{:?}", a); +} +``` +`Ix3` stands for 3D array. + +And now we are type checked. Try change the code above to `Array::::zeros((3, 2, 4, 5).f());` +and compile, see what happens. + +### How about create array of different type and having different initial values? + +The [`from_elm`](http://docs.rs/ndarray/latest/ndarray/struct.ArrayBase.html#method.from_elem) method can be handy here: + +```rust +use ndarray::{Array, Ix3}; +fn main() { + let a = Array::::from_elem((3, 2, 4), false); + println!("{:?}", a); +} +``` + +### Some common create helper functions +`linspace` - Create a 1-D array with 21 elements with values 0., …, 5. +```rust +use ndarray::prelude::*; +use ndarray::{Array, Ix3}; +fn main() { + let a = Array::::linspace(0., 5., 11); + println!("{:?}", a); +} +``` +The output is: +``` +[0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0], shape=[11], strides=[1], layout=C | F (0x3), const ndim=1 +``` + +And there are also `range`, `logspace`, `ones`, `eye` and so on you can choose to use. + +## Basic operations + +```rust +use ndarray::prelude::*; +use ndarray::Array; +use std::f64::INFINITY as inf; + +fn main() { + let a = array![ + [10.,20.,30., 40.,], + ]; + let b = Array::range(0., 4., 1.); // [0., 1., 2., 3, ] + + assert_eq!(&a + &b, array![[10., 21., 32., 43.,]]); // Allocates a new array. Note the explicit `&`. + assert_eq!(&a - &b, array![[10., 19., 28., 37.,]]); + assert_eq!(&a * &b, array![[0., 20., 60., 120.,]]); + assert_eq!(&a / &b, array![[inf, 20., 15., 13.333333333333334,]]); +} +``` + +Try remove all the `&` sign in front of `a` and `b`, does it still compile? Why? + +Note that +* `&A @ &A` produces a new `Array` +* `B @ A` consumes `B`, updates it with the result, and returns it +* `B @ &A` consumes `B`, updates it with the result, and returns it +* `C @= &A` performs an arithmetic operation in place + +For more info checkout https://docs.rs/ndarray/latest/ndarray/struct.ArrayBase.html#arithmetic-operations + +Some operations have `_axis` appended to the function name: they generally take in a parameter of type `Axis` as one of their inputs, +such as `sum_axis`: + +```rust +use ndarray::{aview0, aview1, arr2, Axis}; + +fn main() { + let a = arr2(&[[1., 2., 3.], + [4., 5., 6.]]); + assert!( + a.sum_axis(Axis(0)) == aview1(&[5., 7., 9.]) && + a.sum_axis(Axis(1)) == aview1(&[6., 15.]) && + + a.sum_axis(Axis(0)).sum_axis(Axis(0)) == aview0(&21.) && + a.sum_axis(Axis(0)).sum_axis(Axis(0)) == aview0(&a.sum()) + ); +} +``` + +### Matrix product + +```rust +use ndarray::prelude::*; +use ndarray::Array; + +fn main() { + let a = array![ + [10.,20.,30., 40.,], + ]; + let b = Array::range(0., 4., 1.); // b = [0., 1., 2., 3, ] + println!("a shape {:?}", &a.shape()); + println!("b shape {:?}", &b.shape()); + + let b = b.into_shape((4,1)).unwrap(); // reshape b to shape [4, 1] + println!("b shape {:?}", &b.shape()); + + println!("{}", a.dot(&b)); // [1, 4] x [4, 1] -> [1, 1] + println!("{}", a.t().dot(&b.t())); // [4, 1] x [1, 4] -> [4, 4] +} +``` +The output is: +``` +a shape [1, 4] +b shape [4] +b shape after reshape [4, 1] +[[200]] +[[0, 10, 20, 30], + [0, 20, 40, 60], + [0, 30, 60, 90], + [0, 40, 80, 120]] +``` + +## Indexing, Slicing and Iterating +One-dimensional arrays can be indexed, sliced and iterated over, much like `numpy` arrays + +```rust +use ndarray::prelude::*; +use ndarray::Array; + +fn main() { + let a = Array::range(0., 10., 1.); + + let mut a = a.mapv(|a: f64| a.powi(3)); // numpy equivlant of `a ** 3`; https://doc.rust-lang.org/nightly/std/primitive.f64.html#method.powi + + println!("{}", a); + + println!("{}", a[[2]]); + println!("{}", a.slice(s![2])); + + println!("{}", a.slice(s![2..5])); + + a.slice_mut(s![..6;2]).fill(1000.); // numpy equivlant of `a[:6:2] = 1000` + println!("{}", a); + + for i in a.iter() { + print!("{}, ", i.powf(1./3.)) + } +} +``` +The output is: +``` +[0, 1, 8, 27, 64, 125, 216, 343, 512, 729] +8 +8 +[8, 27, 64] +[1000, 1, 1000, 27, 1000, 125, 216, 343, 512, 729] +9.999999999999998, 1, 9.999999999999998, 3, 9.999999999999998, 4.999999999999999, 5.999999999999999, 6.999999999999999, 7.999999999999999, 8.999999999999998, +``` + +For more info about iteration see [Loops, Producers, and Iterators](https://docs.rs/ndarray/0.13.0/ndarray/struct.ArrayBase.html#loops-producers-and-iterators) + +Let's try a 3D array with elements of type `isize`. This is how you index it: +```rust +use ndarray::prelude::*; + +fn main() { + let a = array![ + [[ 0, 1, 2], // a 3D array 2 x 2 x 3 + [ 10, 12, 13]], + + [[100,101,102], + [110,112,113]] + ]; + + let a = a.mapv(|a: isize| a.pow(1)); // numpy equivlant of `a ** 1`; + // This line does nothing but illustrate mapv with isize type + println!("a -> \n{}\n", a); + + println!("`a.slice(s![1, .., ..])` -> \n{}\n", a.slice(s![1, .., ..])); + + println!("`a.slice(s![.., .., 2])` -> \n{}\n", a.slice(s![.., .., 2])); + + println!("`a.slice(s![.., 1, 0..2])` -> \n{}\n", a.slice(s![.., 1, 0..2])); + + println!("`a.iter()` ->"); + for i in a.iter() { + print!("{}, ", i) // flat out to every element + } + + println!("\n\n`a.outer_iter()` ->"); + for i in a.outer_iter() { + print!("row: {}, \n", i) // iterate through first dimension + } +} +``` +The output is: +``` +a -> +[[[0, 1, 2], + [10, 12, 13]], + + [[100, 101, 102], + [110, 112, 113]]] + +`a.slice(s![1, .., ..])` -> +[[100, 101, 102], + [110, 112, 113]] + +`a.slice(s![.., .., 2])` -> +[[2, 13], + [102, 113]] + +`a.slice(s![.., 1, 0..2])` -> +[[10, 12], + [110, 112]] + +`a.iter()` -> +0, 1, 2, 10, 12, 13, 100, 101, 102, 110, 112, 113, + +`a.outer_iter()` -> +row: [[0, 1, 2], + [10, 12, 13]], +row: [[100, 101, 102], + [110, 112, 113]], +``` + +## Shape Manipulation + +### Changing the shape of an array +The shape of an array can be changed with `into_shape` method. + +````rust +use ndarray::prelude::*; +use ndarray::Array; +use std::iter::FromIterator; +// use ndarray_rand::RandomExt; +// use ndarray_rand::rand_distr::Uniform; + +fn main() { + // Or you may use ndarray_rand crate to generate random arrays + // let a = Array::random((2, 5), Uniform::new(0., 10.)); + + let a = array![ + [3., 7., 3., 4.], + [1., 4., 2., 2.], + [7., 2., 4., 9.]]; + + println!("a = \n{:?}\n", a); + + // use trait FromIterator to flatten a matrix to a vector + let b = Array::from_iter(a.iter()); + println!("b = \n{:?}\n", b); + + let c = b.into_shape([6, 2]).unwrap(); // consume b and generate c with new shape + println!("c = \n{:?}", c); +} +```` +The output is: +``` +a = +[[3.0, 7.0, 3.0, 4.0], + [1.0, 4.0, 2.0, 2.0], + [7.0, 2.0, 4.0, 9.0]], shape=[3, 4], strides=[4, 1], layout=C (0x1), const ndim=2 + +b = +[3.0, 7.0, 3.0, 4.0, 1.0, 4.0, 2.0, 2.0, 7.0, 2.0, 4.0, 9.0], shape=[12], strides=[1], layout=C | F (0x3), const ndim=1 + +c = +[[3.0, 7.0], + [3.0, 4.0], + [1.0, 4.0], + [2.0, 2.0], + [7.0, 2.0], + [4.0, 9.0]], shape=[6, 2], strides=[2, 1], layout=C (0x1), const ndim=2 +``` + +### Stacking together different arrays + +Macro `stack!` is helpful for stacking arrays: +```rust +use ndarray::prelude::*; +use ndarray::{Array, Axis, stack}; + +fn main() { + + let a = array![ + [9., 7.], + [5., 2.]]; + + let b = array![ + [1., 9.], + [5., 1.]]; + + println!("a vstack b = \n{:?}\n", stack![Axis(0), a, b]); + + println!("a hstack b = \n{:?}\n", stack![Axis(1), a, b]); +} +``` +The output is: +``` +a vstack b = +[[9.0, 7.0], + [5.0, 2.0], + [1.0, 9.0], + [5.0, 1.0]], shape=[4, 2], strides=[2, 1], layout=C (0x1), const ndim=2 + +a hstack b = +[[9.0, 7.0, 1.0, 9.0], + [5.0, 2.0, 5.0, 1.0]], shape=[2, 4], strides=[4, 1], layout=C (0x1), const ndim=2 +``` + +### Splitting one array into several smaller ones + +More to see here [ArrayView::split_at](https://docs.rs/ndarray/latest/ndarray/type.ArrayView.html#method.split_at) +```rust +use ndarray::prelude::*; +use ndarray::Axis; + +fn main() { + + let a = array![ + [6., 7., 6., 9., 0., 5., 4., 0., 6., 8., 5., 2.], + [8., 5., 5., 7., 1., 8., 6., 7., 1., 8., 1., 0.]]; + + let (s1, s2) = a.view().split_at(Axis(0), 1); + println!("Split a from Axis(0), at index 1:"); + println!("s1 = \n{}", s1); + println!("s2 = \n{}\n", s2); + + + let (s1, s2) = a.view().split_at(Axis(1), 4); + println!("Split a from Axis(1), at index 4:"); + println!("s1 = \n{}", s1); + println!("s2 = \n{}\n", s2); +} +``` +The output is: +``` +Split a from Axis(0), at index 1: +s1 = +[[6, 7, 6, 9, 0, 5, 4, 0, 6, 8, 5, 2]] +s2 = +[[8, 5, 5, 7, 1, 8, 6, 7, 1, 8, 1, 0]] + +Split a from Axis(1), at index 4: +s1 = +[[6, 7, 6, 9], + [8, 5, 5, 7]] +s2 = +[[0, 5, 4, 0, 6, 8, 5, 2], + [1, 8, 6, 7, 1, 8, 1, 0]] + +``` + +## Copies and Views +### View, Ref or Shallow Copy +As in Rust we have owner ship, so we cannot simply +update an element of an array while we have a +shared view of it. This will help us write more +robust code. + +```rust +use ndarray::prelude::*; +use ndarray::{Array, Axis}; + +fn main() { + + let mut a = Array::range(0., 12., 1.).into_shape([3 ,4]).unwrap(); + println!("a = \n{}\n", a); + + { + let (s1, s2) = a.view().split_at(Axis(1), 2); + + // with s as a view sharing the ref of a, we cannot update a here + // a.slice_mut(s![1, 1]).fill(1234.); + + println!("Split a from Axis(0), at index 1:"); + println!("s1 = \n{}", s1); + println!("s2 = \n{}\n", s2); + } + + // now we can update a again here, as views of s1, s2 are dropped already + a.slice_mut(s![1, 1]).fill(1234.); + + let (s1, s2) = a.view().split_at(Axis(1), 2); + println!("Split a from Axis(0), at index 1:"); + println!("s1 = \n{}", s1); + println!("s2 = \n{}\n", s2); +} +``` +The output is: +``` +a = +[[0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11]] + +Split a from Axis(0), at index 1: +s1 = +[[0, 1], + [4, 5], + [8, 9]] +s2 = +[[2, 3], + [6, 7], + [10, 11]] + +Split a from Axis(0), at index 1: +s1 = +[[0, 1], + [4, 1234], + [8, 9]] +s2 = +[[2, 3], + [6, 7], + [10, 11]] +``` + +### Deep Copy +As the usual way in Rust, a `clone()` call will +make a copy of your array: +```rust +use ndarray::prelude::*; +use ndarray::Array; + +fn main() { + + let mut a = Array::range(0., 4., 1.).into_shape([2 ,2]).unwrap(); + let b = a.clone(); + + println!("a = \n{}\n", a); + println!("b clone of a = \n{}\n", a); + + a.slice_mut(s![1, 1]).fill(1234.); + + println!("a updated..."); + println!("a = \n{}\n", a); + println!("b clone of a = \n{}\n", b); +} +``` + +The output is: +``` +a = +[[0, 1], + [2, 3]] + +b clone of a = +[[0, 1], + [2, 3]] + +a updated... +a = +[[0, 1], + [2, 1234]] + +b clone of a = +[[0, 1], + [2, 3]] +``` + +Noticing that using `clone()` (or cloning) an `Array` type also copies the array's elements. It creates an independently owned array of the same type. + +Cloning an `ArrayView` does not clone or copy the underlying elements - it just clones the view reference (as it happens in Rust when cloning a `&` reference). + +## Broadcasting + +Arrays support limited broadcasting, where arithmetic operations with array operands of different sizes can be carried out by repeating the elements of the smaller dimension array. + +```rust +use ndarray::prelude::*; + +fn main() { + let a = array![ + [1., 1.], + [1., 2.], + [0., 3.], + [0., 4.]]; + + let b = array![[0., 1.]]; + + let c = array![ + [1., 2.], + [1., 3.], + [0., 4.], + [0., 5.]]; + + // We can add because the shapes are compatible even if not equal. + // The `b` array is shape 1 × 2 but acts like a 4 × 2 array. + assert!(c == a + b); +} +``` + +See [.broadcast()](https://docs.rs/ndarray/latest/ndarray/struct.ArrayBase.html#method.broadcast) for a more detailed description. + +And there is a short example of it: +```rust +use ndarray::prelude::*; + +fn main() { + let a = array![ + [1., 2.], + [3., 4.], + ]; + + let b = a.broadcast((3, 2, 2)).unwrap(); + println!("shape of a is {:?}", a.shape()); + println!("a is broadcased to 3x2x2 = \n{}", b); +} +``` +The output is: +``` +shape of a is [2, 2] +a is broadcased to 3x2x2 = +[[[1, 2], + [3, 4]], + + [[1, 2], + [3, 4]], + + [[1, 2], + [3, 4]]] +``` + +## Want to learn more? +Please checkout these docs for more information +* [`ArrayBase` doc page](https://docs.rs/ndarray/latest/ndarray/struct.ArrayBase.html) +* [`ndarray` for `numpy` user doc page](https://docs.rs/ndarray/latest/ndarray/doc/ndarray_for_numpy_users/index.html) diff --git a/README.rst b/README.rst index f1a3524ad..9e2580a77 100644 --- a/README.rst +++ b/README.rst @@ -5,6 +5,7 @@ The ``ndarray`` crate provides an *n*-dimensional container for general elements and for numerics. Please read the `API documentation on docs.rs`__ +or take a look at the `quickstart tutorial <./README-quick-start.md>`_. __ https://docs.rs/ndarray/ From ca102008dee19efd9b1c63788ebf8f3088ad0525 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 19 Apr 2020 08:46:19 +0200 Subject: [PATCH 039/651] DOC: Add to 0.13.1 changelog --- RELEASES.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 071d12464..56741a7e0 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -14,7 +14,10 @@ New features https://github.com/rust-ndarray/ndarray/pull/728 - `Dimension::Larger` now requires `RemoveAxis` by [@TheLortex] https://github.com/rust-ndarray/ndarray/pull/792 - +- New methods for collecting Zip into an array by [@bluss] + https://github.com/rust-ndarray/ndarray/pull/797 +- New `Array::maybe_uninit` and `.assume_init()` by [@bluss] + https://github.com/rust-ndarray/ndarray/pull/803 Enhancements ------------ @@ -25,6 +28,8 @@ Enhancements https://github.com/rust-ndarray/ndarray/pull/754 - Implement `fold` for `IndicesIter` by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/733 +- New Quick Start readme by [@lifuyang] + https://github.com/rust-ndarray/ndarray/pull/785 API changes ----------- @@ -39,6 +44,9 @@ Other changes - Improve blas version documentation by [@jturner314] - Doc improvements by [@mockersf] https://github.com/rust-ndarray/ndarray/pull/751 - Doc and lint related improvements by [@viniciusd] https://github.com/rust-ndarray/ndarray/pull/750 +- Minor fixes related to best practices for unsafe code by [@bluss] + https://github.com/rust-ndarray/ndarray/pull/799 + https://github.com/rust-ndarray/ndarray/pull/802 - Release management by [@bluss] @@ -944,3 +952,4 @@ Earlier releases [@TheLortex]: https://github.com/TheLortex [@mockersf]: https://github.com/mockersf [@viniciusd]: https://github.com/viniciusd +[@lifuyang]: https://github.com/liufuyang From 3ddfbeeb672b3e6380c97b7dc9837ef93af0fbcb Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 20 Apr 2020 21:08:13 +0200 Subject: [PATCH 040/651] DOC: Update Array::uninitialized's example and doc See the bug reports; these examples can not really be recommended; instead we recommend using Array::maybe_uninit. --- src/impl_constructors.rs | 37 ++++++++----------------------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 964003dd8..62c1f2aa2 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -474,41 +474,20 @@ where /// /// ### Safety /// - /// Accessing uninitalized values is undefined behaviour. You must - /// overwrite *all* the elements in the array after it is created; for - /// example using the methods `.fill()` or `.assign()`. + /// Accessing uninitalized values is undefined behaviour. You must overwrite *all* the elements + /// in the array after it is created; for example using + /// [`raw_view_mut`](ArrayBase::raw_view_mut) or other low-level element access. /// /// The contents of the array is indeterminate before initialization and it /// is an error to perform operations that use the previous values. For /// example it would not be legal to use `a += 1.;` on such an array. /// /// This constructor is limited to elements where `A: Copy` (no destructors) - /// to avoid users shooting themselves too hard in the foot; it is not - /// a problem to drop an array created with this method even before elements - /// are initialized. (Note that constructors `from_shape_vec` and - /// `from_shape_vec_unchecked` allow the user yet more control). - /// - /// ### Examples - /// - /// ``` - /// use ndarray::{s, Array2}; - /// - /// // Example Task: Let's create a column shifted copy of a in b - /// - /// fn shift_by_two(a: &Array2) -> Array2 { - /// let mut b = unsafe { Array2::uninitialized(a.dim()) }; - /// - /// // two first columns in b are two last in a - /// // rest of columns in b are the initial columns in a - /// b.slice_mut(s![.., ..2]).assign(&a.slice(s![.., -2..])); - /// b.slice_mut(s![.., 2..]).assign(&a.slice(s![.., ..-2])); - /// - /// // `b` is safe to use with all operations at this point - /// b - /// } - /// - /// # shift_by_two(&Array2::zeros((8, 8))); - /// ``` + /// to avoid users shooting themselves too hard in the foot. + /// + /// (Also note that the constructors `from_shape_vec` and + /// `from_shape_vec_unchecked` allow the user yet more control, in the sense + /// that Arrays can be created from arbitrary vectors.) pub unsafe fn uninitialized(shape: Sh) -> Self where A: Copy, From 80dd490ec7b382d8910f6aaf380360bf82e8e1a6 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 20 Apr 2020 21:09:52 +0200 Subject: [PATCH 041/651] DOC: Correct typo and improve maybe_uninit's example The example is very far from perfect, but it works ;) In the long run maybe the example's assign_to function can have a place in ndarray --- src/impl_constructors.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 62c1f2aa2..8d1471e78 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -533,7 +533,7 @@ where /// use ndarray::Zip; /// use ndarray::Axis; /// - /// // Example Task: Let's create a transposed copy of the input + /// // Example Task: Let's create a column shifted copy of the input /// /// fn shift_by_two(a: &Array2) -> Array2 { /// // create an uninitialized array @@ -553,6 +553,8 @@ where /// /// use ndarray::{IntoNdProducer, AssignElem}; /// + /// // This function clones elements from the first input to the second; + /// // the two producers must have the same shape /// fn assign_to<'a, P1, P2, A>(from: P1, to: P2) /// where P1: IntoNdProducer, /// P2: IntoNdProducer, From 1f6d249cca221ded3bb8d7498aa61f34e6dc0c33 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 20 Apr 2020 21:24:38 +0200 Subject: [PATCH 042/651] DOC: Put release date on 0.13.1 --- RELEASES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 56741a7e0..b5e351e92 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,4 +1,4 @@ -Version 0.13.1 (2020-04) +Version 0.13.1 (2020-04-21) =========================== New features From 9cba023a988954a984969bb2eb5b68689f38d72f Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 21 Apr 2020 17:24:16 +0200 Subject: [PATCH 043/651] 0.13.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3da63d1d7..86eabb48a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ndarray" -version = "0.13.0" +version = "0.13.1" edition = "2018" authors = [ "bluss", From 85e181326a18cb2e8a18c5c6b51d2f88b6ea5f5b Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 22 Apr 2020 15:14:45 +0200 Subject: [PATCH 044/651] DOC: Add comments to Indices' NdProducer implementation It's quite counterintuitive what's happening, so add some comments. --- src/indexes.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/indexes.rs b/src/indexes.rs index 9eb7a8b5c..a0831b0e7 100644 --- a/src/indexes.rs +++ b/src/indexes.rs @@ -157,6 +157,20 @@ where private_impl! {} } +// How the NdProducer for Indices works. +// +// NdProducer allows for raw pointers (Ptr), strides (Stride) and the produced +// item (Item). +// +// Instead of Ptr, there is `IndexPtr` which is an index value, like [0, 0, 0] +// for the three dimensional case. +// +// The stride is simply which axis is currently being incremented. The stride for axis 1, is 1. +// +// .stride_offset(stride, index) simply computes the new index along that axis, for example: +// [0, 0, 0].stride_offset(1, 10) => [0, 10, 0] axis 1 is incremented by 10. +// +// .as_ref() converts the Ptr value to an Item. For example [0, 10, 0] => (0, 10, 0) impl NdProducer for Indices { type Item = D::Pattern; type Dim = D; From 5481e25248d2e0e72a66df86d05cf6bd978f6e96 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 22 Apr 2020 15:21:19 +0200 Subject: [PATCH 045/651] FIX: Factor out duplicate code in Zip::and and Zip::and_broadcast Since these methods are duplicated fivefold, it might actually be a good saving, even if it's a small method. And we don't have to write the layout combination code twice. --- src/zip/mod.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/zip/mod.rs b/src/zip/mod.rs index fc2812a49..b2e24a62e 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -969,15 +969,9 @@ macro_rules! map_impl { pub fn and

(self, p: P) -> Zip<($($p,)* P::Output, ), D> where P: IntoNdProducer, { - let array = p.into_producer(); - self.check(&array); - let part_layout = array.layout(); - let ($($p,)*) = self.parts; - Zip { - parts: ($($p,)* array, ), - layout: self.layout.and(part_layout), - dimension: self.dimension, - } + let part = p.into_producer(); + self.check(&part); + self.build_and(part) } /// Include the producer `p` in the Zip, broadcasting if needed. @@ -990,11 +984,17 @@ macro_rules! map_impl { where P: IntoNdProducer, Item=&'a Elem>, D2: Dimension, { - let array = p.into_producer().broadcast_unwrap(self.dimension.clone()); - let part_layout = array.layout(); + let part = p.into_producer().broadcast_unwrap(self.dimension.clone()); + self.build_and(part) + } + + fn build_and

{ + pub(crate) iter: P, + pub(crate) min_size: usize, +} + +impl

ParallelIterator for ParallelSplits

+ where P: SplitPreference + Send, +{ + type Item = P; + + fn drive_unindexed(self, consumer: C) -> C::Result + where C: UnindexedConsumer + { + bridge_unindexed(self, consumer) + } + + fn opt_len(&self) -> Option { + None + } +} + +impl

UnindexedProducer for ParallelSplits

+ where P: SplitPreference + Send, +{ + type Item = P; + + fn split(self) -> (Self, Option) { + if self.iter.size() <= self.min_size || !self.iter.can_split() { + return (self, None) + } + let (a, b) = self.iter.split(); + (ParallelSplits { + iter: a, + min_size: self.min_size, + }, + Some(ParallelSplits { + iter: b, + min_size: self.min_size, + })) + } + + fn fold_with(self, folder: Fold) -> Fold + where Fold: Folder, + { + folder.consume(self.iter) + } +} From ee97dbfbaf42b3a21d92384f414ec20fce0617dd Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 11 May 2020 23:31:18 +0200 Subject: [PATCH 073/651] TEST: Add parallel collect test for small arrays --- tests/par_zip.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/par_zip.rs b/tests/par_zip.rs index a24dc8e80..36d7f43c2 100644 --- a/tests/par_zip.rs +++ b/tests/par_zip.rs @@ -97,6 +97,28 @@ fn test_zip_collect() { assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); assert_eq!(a.strides(), b.strides()); } + +} + +#[test] +#[cfg(feature = "approx")] +fn test_zip_small_collect() { + use approx::assert_abs_diff_eq; + + for m in 0..32 { + for n in 0..16 { + let dim = (m, n); + let b = Array::from_shape_fn(dim, |(i, j)| 1. / (i + 2 * j + 1) as f32); + let c = Array::from_shape_fn(dim, |(i, j)| f32::ln((1 + i + j) as f32)); + + { + let a = Zip::from(&b).and(&c).par_apply_collect(|x, y| x + y); + + assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); + assert_eq!(a.strides(), b.strides()); + } + } + } } #[test] From fe2ebf6eb96a191231751ebae65e76ab94476e76 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 11 May 2020 23:31:18 +0200 Subject: [PATCH 074/651] FEAT: Add internal Zip::last_producer This method is useful for parallel Zip. --- src/zip/mod.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/zip/mod.rs b/src/zip/mod.rs index f319afd4b..af251c235 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -916,6 +916,11 @@ zipt_impl! { [A B C D E F][ a b c d e f], } +macro_rules! last_of { + ($q:ty) => { $q }; + ($p:ty, $($q:ty),+) => { last_of!($($q),+) }; +} + macro_rules! map_impl { ($([$notlast:ident $($p:ident)*],)+) => { $( @@ -1012,6 +1017,14 @@ macro_rules! map_impl { }).is_done() } + #[cfg(feature = "rayon")] + #[allow(dead_code)] // unused for the first of the Zip arities + /// Return a reference to the last producer + pub(crate) fn last_producer(&self) -> &last_of!($($p),*) { + let (.., ref last) = &self.parts; + last + } + expand_if!(@bool [$notlast] /// Include the producer `p` in the Zip. From 42772962313e550904b027fa233ceda45f1a3214 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 11 May 2020 23:31:18 +0200 Subject: [PATCH 075/651] FEAT: Implement generic parallel collect Allow non-copy elements by implementing dropping partial results from collect (needed if there is a panic with unwinding during the apply-collect process). It is implemented by: 1. allocate an uninit output array of the right size and layout 2. use parallelsplits to split the Zip into chunks processed in parallel 3. for each chunk keep track of the slice of written elements 4. each output chunk is contiguous due to the layout being picked to match the Zip's preferred layout 5. Use reduce to merge adjacent partial results; this ensures we drop all the rests correctly, if there is a panic in any thread --- src/parallel/impl_par_methods.rs | 126 +++++++++++++++++++++++++++++-- src/parallel/mod.rs | 1 + src/parallel/send_producer.rs | 83 ++++++++++++++++++++ 3 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 src/parallel/send_producer.rs diff --git a/src/parallel/impl_par_methods.rs b/src/parallel/impl_par_methods.rs index 867cd07ad..c3292876f 100644 --- a/src/parallel/impl_par_methods.rs +++ b/src/parallel/impl_par_methods.rs @@ -2,6 +2,8 @@ use crate::{Array, ArrayBase, DataMut, Dimension, IntoNdProducer, NdProducer, Zi use crate::AssignElem; use crate::parallel::prelude::*; +use crate::parallel::par::ParallelSplits; +use super::send_producer::SendProducer; /// # Parallel methods /// @@ -43,6 +45,8 @@ where // Zip +const COLLECT_MAX_PARTS: usize = 256; + macro_rules! zip_impl { ($([$notlast:ident $($p:ident)*],)+) => { $( @@ -71,14 +75,56 @@ macro_rules! zip_impl { /// inputs. /// /// If all inputs are c- or f-order respectively, that is preserved in the output. - /// - /// Restricted to functions that produce copyable results for technical reasons; other - /// cases are not yet implemented. - pub fn par_apply_collect(self, f: impl Fn($($p::Item,)* ) -> R + Sync + Send) -> Array - where R: Copy + Send + pub fn par_apply_collect(self, f: impl Fn($($p::Item,)* ) -> R + Sync + Send) + -> Array + where R: Send { let mut output = self.uninitalized_for_current_layout::(); - self.par_apply_assign_into(&mut output, f); + let total_len = output.len(); + + // Create a parallel iterator that produces chunks of the zip with the output + // array. It's crucial that both parts split in the same way, and in a way + // so that the chunks of the output are still contig. + // + // Use a raw view so that we can alias the output data here and in the partial + // result. + let splits = unsafe { + ParallelSplits { + iter: self.and(SendProducer::new(output.raw_view_mut().cast::())), + // Keep it from splitting the Zip down too small + min_size: total_len / COLLECT_MAX_PARTS, + } + }; + + let collect_result = splits.map(move |zip| { + // Create a partial result for the contiguous slice of data being written to + let output = zip.last_producer(); + debug_assert!(output.is_contiguous()); + + let mut partial = Partial::new(output.as_ptr()); + + // Apply the mapping function on this chunk of the zip + let partial_len = &mut partial.len; + let f = &f; + zip.apply(move |$($p,)* output_elem: *mut R| unsafe { + output_elem.write(f($($p),*)); + if std::mem::needs_drop::() { + *partial_len += 1; + } + }); + + partial + }) + .reduce(Partial::stub, Partial::try_merge); + + if std::mem::needs_drop::() { + debug_assert_eq!(total_len, collect_result.len, "collect len is not correct, expected {}", total_len); + assert!(collect_result.len == total_len, "Collect: Expected number of writes not completed"); + } + + // Here the collect result is complete, and we release its ownership and transfer + // it to the output array. + collect_result.release_ownership(); unsafe { output.assume_init() } @@ -113,3 +159,71 @@ zip_impl! { [true P1 P2 P3 P4 P5], [false P1 P2 P3 P4 P5 P6], } + +/// Partial is a partially written contiguous slice of data; +/// it is the owner of the elements, but not the allocation, +/// and will drop the elements on drop. +#[must_use] +pub(crate) struct Partial { + /// Data pointer + ptr: *mut T, + /// Current length + len: usize, +} + +impl Partial { + /// Create an empty partial for this data pointer + pub(crate) fn new(ptr: *mut T) -> Self { + Self { + ptr, + len: 0, + } + } + + pub(crate) fn stub() -> Self { + Self { len: 0, ptr: 0 as *mut _ } + } + + pub(crate) fn is_stub(&self) -> bool { + self.ptr.is_null() + } + + /// Release Partial's ownership of the written elements, and return the current length + pub(crate) fn release_ownership(mut self) -> usize { + let ret = self.len; + self.len = 0; + ret + } + + /// Merge if they are in order (left to right) and contiguous. + /// Skips merge if T does not need drop. + pub(crate) fn try_merge(mut left: Self, right: Self) -> Self { + if !std::mem::needs_drop::() { + return left; + } + // Merge the partial collect results; the final result will be a slice that + // covers the whole output. + if left.is_stub() { + right + } else if left.ptr.wrapping_add(left.len) == right.ptr { + left.len += right.release_ownership(); + left + } else { + // failure to merge; this is a bug in collect, so we will never reach this + debug_assert!(false, "Partial: failure to merge left and right parts"); + left + } + } +} + +unsafe impl Send for Partial where T: Send { } + +impl Drop for Partial { + fn drop(&mut self) { + if !self.ptr.is_null() { + unsafe { + std::ptr::drop_in_place(std::slice::from_raw_parts_mut(self.ptr, self.len)); + } + } + } +} diff --git a/src/parallel/mod.rs b/src/parallel/mod.rs index 82cd1cba6..12059cee4 100644 --- a/src/parallel/mod.rs +++ b/src/parallel/mod.rs @@ -155,4 +155,5 @@ pub use crate::par_azip; mod impl_par_methods; mod into_impls; mod par; +mod send_producer; mod zipmacro; diff --git a/src/parallel/send_producer.rs b/src/parallel/send_producer.rs new file mode 100644 index 000000000..5324b3490 --- /dev/null +++ b/src/parallel/send_producer.rs @@ -0,0 +1,83 @@ + +use crate::imp_prelude::*; +use crate::{Layout, NdProducer}; +use std::ops::{Deref, DerefMut}; + +/// An NdProducer that is unconditionally `Send`. +#[repr(transparent)] +pub(crate) struct SendProducer { + inner: T +} + +impl SendProducer { + /// Create an unconditionally `Send` ndproducer from the producer + pub(crate) unsafe fn new(producer: T) -> Self { Self { inner: producer } } +} + +unsafe impl

Send for SendProducer

{ } + +impl

Deref for SendProducer

{ + type Target = P; + fn deref(&self) -> &P { &self.inner } +} + +impl

DerefMut for SendProducer

{ + fn deref_mut(&mut self) -> &mut P { &mut self.inner } +} + +impl NdProducer for SendProducer

+ where P: NdProducer, +{ + type Item = P::Item; + type Dim = P::Dim; + type Ptr = P::Ptr; + type Stride = P::Stride; + + private_impl! {} + + #[inline(always)] + fn raw_dim(&self) -> Self::Dim { + self.inner.raw_dim() + } + + #[inline(always)] + fn equal_dim(&self, dim: &Self::Dim) -> bool { + self.inner.equal_dim(dim) + } + + #[inline(always)] + fn as_ptr(&self) -> Self::Ptr { + self.inner.as_ptr() + } + + #[inline(always)] + fn layout(&self) -> Layout { + self.inner.layout() + } + + #[inline(always)] + unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { + self.inner.as_ref(ptr) + } + + #[inline(always)] + unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { + self.inner.uget_ptr(i) + } + + #[inline(always)] + fn stride_of(&self, axis: Axis) -> Self::Stride { + self.inner.stride_of(axis) + } + + #[inline(always)] + fn contiguous_stride(&self) -> Self::Stride { + self.inner.contiguous_stride() + } + + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { + let (a, b) = self.inner.split_at(axis, index); + (Self { inner: a }, Self { inner: b }) + } +} + From 6d43c133db81f7c32bfd2b20a9ea9d92513d07cf Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 18 May 2020 20:18:00 +0200 Subject: [PATCH 076/651] FIX: In ParallelSplits, count maximum number of splits Instead of requiring to use the size in elements of the thing-to-split, simply use a counter for the number of splits. --- src/parallel/impl_par_methods.rs | 4 ++-- src/parallel/par.rs | 8 ++++---- src/split_at.rs | 1 - src/zip/mod.rs | 2 -- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/parallel/impl_par_methods.rs b/src/parallel/impl_par_methods.rs index c3292876f..f73b952d5 100644 --- a/src/parallel/impl_par_methods.rs +++ b/src/parallel/impl_par_methods.rs @@ -45,7 +45,7 @@ where // Zip -const COLLECT_MAX_PARTS: usize = 256; +const COLLECT_MAX_SPLITS: usize = 10; macro_rules! zip_impl { ($([$notlast:ident $($p:ident)*],)+) => { @@ -92,7 +92,7 @@ macro_rules! zip_impl { ParallelSplits { iter: self.and(SendProducer::new(output.raw_view_mut().cast::())), // Keep it from splitting the Zip down too small - min_size: total_len / COLLECT_MAX_PARTS, + max_splits: COLLECT_MAX_SPLITS, } }; diff --git a/src/parallel/par.rs b/src/parallel/par.rs index c7a0fcb3a..d9d592af6 100644 --- a/src/parallel/par.rs +++ b/src/parallel/par.rs @@ -288,7 +288,7 @@ zip_impl! { /// or producer `P`. pub(crate) struct ParallelSplits

{ pub(crate) iter: P, - pub(crate) min_size: usize, + pub(crate) max_splits: usize, } impl

ParallelIterator for ParallelSplits

@@ -313,17 +313,17 @@ impl

UnindexedProducer for ParallelSplits

type Item = P; fn split(self) -> (Self, Option) { - if self.iter.size() <= self.min_size || !self.iter.can_split() { + if self.max_splits == 0 || !self.iter.can_split() { return (self, None) } let (a, b) = self.iter.split(); (ParallelSplits { iter: a, - min_size: self.min_size, + max_splits: self.max_splits - 1, }, Some(ParallelSplits { iter: b, - min_size: self.min_size, + max_splits: self.max_splits - 1, })) } diff --git a/src/split_at.rs b/src/split_at.rs index da15dfcf3..b05e58346 100644 --- a/src/split_at.rs +++ b/src/split_at.rs @@ -8,7 +8,6 @@ pub(crate) trait SplitAt { pub(crate) trait SplitPreference : SplitAt { fn can_split(&self) -> bool; - fn size(&self) -> usize; fn split_preference(&self) -> (Axis, usize); fn split(self) -> (Self, Self) where Self: Sized { let (axis, index) = self.split_preference(); diff --git a/src/zip/mod.rs b/src/zip/mod.rs index af251c235..9196a2663 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -1126,8 +1126,6 @@ macro_rules! map_impl { { fn can_split(&self) -> bool { self.size() > 1 } - fn size(&self) -> usize { self.size() } - fn split_preference(&self) -> (Axis, usize) { // Always split in a way that preserves layout (if any) let axis = self.max_stride_axis(); From e3ebf8c545ba7e5d59ef0da04ae8c909a90b1166 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 18 May 2020 20:19:06 +0200 Subject: [PATCH 077/651] FIX: In Zip, fix unused code warning for `last_of` macro --- src/zip/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 9196a2663..57c0e2dc5 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -916,6 +916,7 @@ zipt_impl! { [A B C D E F][ a b c d e f], } +#[cfg(feature = "rayon")] macro_rules! last_of { ($q:ty) => { $q }; ($p:ty, $($q:ty),+) => { last_of!($($q),+) }; From efcd6074324c18ef21cd2a0ce10c89b2732aef13 Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 19 May 2020 21:21:15 +0200 Subject: [PATCH 078/651] FIX: Make Partial::new an unsafe method --- src/parallel/impl_par_methods.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/parallel/impl_par_methods.rs b/src/parallel/impl_par_methods.rs index f73b952d5..0ed48cad1 100644 --- a/src/parallel/impl_par_methods.rs +++ b/src/parallel/impl_par_methods.rs @@ -100,8 +100,10 @@ macro_rules! zip_impl { // Create a partial result for the contiguous slice of data being written to let output = zip.last_producer(); debug_assert!(output.is_contiguous()); - - let mut partial = Partial::new(output.as_ptr()); + let mut partial; + unsafe { + partial = Partial::new(output.as_ptr()); + } // Apply the mapping function on this chunk of the zip let partial_len = &mut partial.len; @@ -173,7 +175,12 @@ pub(crate) struct Partial { impl Partial { /// Create an empty partial for this data pointer - pub(crate) fn new(ptr: *mut T) -> Self { + /// + /// Safety: Unless ownership is released, the + /// Partial acts as an owner of the slice of data (not the allocation); + /// and will free the elements on drop; the pointer must be dereferenceable + /// and the `len` elements following it valid. + pub(crate) unsafe fn new(ptr: *mut T) -> Self { Self { ptr, len: 0, From 8ed9ac331ebf11f85d18ed963b297ff033bf35c8 Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 19 May 2020 21:21:32 +0200 Subject: [PATCH 079/651] FIX: Wrap long lines in impl_par_methods.rs --- src/parallel/impl_par_methods.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/parallel/impl_par_methods.rs b/src/parallel/impl_par_methods.rs index 0ed48cad1..0aedee9e8 100644 --- a/src/parallel/impl_par_methods.rs +++ b/src/parallel/impl_par_methods.rs @@ -120,8 +120,10 @@ macro_rules! zip_impl { .reduce(Partial::stub, Partial::try_merge); if std::mem::needs_drop::() { - debug_assert_eq!(total_len, collect_result.len, "collect len is not correct, expected {}", total_len); - assert!(collect_result.len == total_len, "Collect: Expected number of writes not completed"); + debug_assert_eq!(total_len, collect_result.len, + "collect len is not correct, expected {}", total_len); + assert!(collect_result.len == total_len, + "Collect: Expected number of writes not completed"); } // Here the collect result is complete, and we release its ownership and transfer From d02b757aca316f96902ebd63ed4d0a71a51acfb3 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 23 May 2020 20:04:55 +0200 Subject: [PATCH 080/651] FIX: Use Partial instead of PartialArray Partial is just a contiguous slice, and much simpler than PartialArray; Partial is all that's needed, because the area written will always be contiguous. --- src/lib.rs | 1 + src/parallel/impl_par_methods.rs | 75 +--------------- src/partial.rs | 88 +++++++++++++++++++ src/zip/mod.rs | 21 +++-- src/zip/partial_array.rs | 144 ------------------------------- 5 files changed, 104 insertions(+), 225 deletions(-) create mode 100644 src/partial.rs delete mode 100644 src/zip/partial_array.rs diff --git a/src/lib.rs b/src/lib.rs index b40eee01a..f65e82ec9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -176,6 +176,7 @@ mod linalg_traits; mod linspace; mod logspace; mod numeric_util; +mod partial; mod shape_builder; #[macro_use] mod slice; diff --git a/src/parallel/impl_par_methods.rs b/src/parallel/impl_par_methods.rs index 0aedee9e8..e5470e170 100644 --- a/src/parallel/impl_par_methods.rs +++ b/src/parallel/impl_par_methods.rs @@ -5,6 +5,8 @@ use crate::parallel::prelude::*; use crate::parallel::par::ParallelSplits; use super::send_producer::SendProducer; +use crate::partial::Partial; + /// # Parallel methods /// /// These methods require crate feature `rayon`. @@ -163,76 +165,3 @@ zip_impl! { [true P1 P2 P3 P4 P5], [false P1 P2 P3 P4 P5 P6], } - -/// Partial is a partially written contiguous slice of data; -/// it is the owner of the elements, but not the allocation, -/// and will drop the elements on drop. -#[must_use] -pub(crate) struct Partial { - /// Data pointer - ptr: *mut T, - /// Current length - len: usize, -} - -impl Partial { - /// Create an empty partial for this data pointer - /// - /// Safety: Unless ownership is released, the - /// Partial acts as an owner of the slice of data (not the allocation); - /// and will free the elements on drop; the pointer must be dereferenceable - /// and the `len` elements following it valid. - pub(crate) unsafe fn new(ptr: *mut T) -> Self { - Self { - ptr, - len: 0, - } - } - - pub(crate) fn stub() -> Self { - Self { len: 0, ptr: 0 as *mut _ } - } - - pub(crate) fn is_stub(&self) -> bool { - self.ptr.is_null() - } - - /// Release Partial's ownership of the written elements, and return the current length - pub(crate) fn release_ownership(mut self) -> usize { - let ret = self.len; - self.len = 0; - ret - } - - /// Merge if they are in order (left to right) and contiguous. - /// Skips merge if T does not need drop. - pub(crate) fn try_merge(mut left: Self, right: Self) -> Self { - if !std::mem::needs_drop::() { - return left; - } - // Merge the partial collect results; the final result will be a slice that - // covers the whole output. - if left.is_stub() { - right - } else if left.ptr.wrapping_add(left.len) == right.ptr { - left.len += right.release_ownership(); - left - } else { - // failure to merge; this is a bug in collect, so we will never reach this - debug_assert!(false, "Partial: failure to merge left and right parts"); - left - } - } -} - -unsafe impl Send for Partial where T: Send { } - -impl Drop for Partial { - fn drop(&mut self) { - if !self.ptr.is_null() { - unsafe { - std::ptr::drop_in_place(std::slice::from_raw_parts_mut(self.ptr, self.len)); - } - } - } -} diff --git a/src/partial.rs b/src/partial.rs new file mode 100644 index 000000000..887e93824 --- /dev/null +++ b/src/partial.rs @@ -0,0 +1,88 @@ +// Copyright 2020 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ptr; + +/// Partial is a partially written contiguous slice of data; +/// it is the owner of the elements, but not the allocation, +/// and will drop the elements on drop. +#[must_use] +pub(crate) struct Partial { + /// Data pointer + ptr: *mut T, + /// Current length + pub(crate) len: usize, +} + +impl Partial { + /// Create an empty partial for this data pointer + /// + /// ## Safety + /// + /// Unless ownership is released, the Partial acts as an owner of the slice of data (not the + /// allocation); and will free the elements on drop; the pointer must be dereferenceable and + /// the `len` elements following it valid. + /// + /// The Partial has an accessible length field which must only be modified in trusted code. + pub(crate) unsafe fn new(ptr: *mut T) -> Self { + Self { + ptr, + len: 0, + } + } + + #[cfg(feature = "rayon")] + pub(crate) fn stub() -> Self { + Self { len: 0, ptr: 0 as *mut _ } + } + + #[cfg(feature = "rayon")] + pub(crate) fn is_stub(&self) -> bool { + self.ptr.is_null() + } + + /// Release Partial's ownership of the written elements, and return the current length + pub(crate) fn release_ownership(mut self) -> usize { + let ret = self.len; + self.len = 0; + ret + } + + #[cfg(feature = "rayon")] + /// Merge if they are in order (left to right) and contiguous. + /// Skips merge if T does not need drop. + pub(crate) fn try_merge(mut left: Self, right: Self) -> Self { + if !std::mem::needs_drop::() { + return left; + } + // Merge the partial collect results; the final result will be a slice that + // covers the whole output. + if left.is_stub() { + right + } else if left.ptr.wrapping_add(left.len) == right.ptr { + left.len += right.release_ownership(); + left + } else { + // failure to merge; this is a bug in collect, so we will never reach this + debug_assert!(false, "Partial: failure to merge left and right parts"); + left + } + } +} + +unsafe impl Send for Partial where T: Send { } + +impl Drop for Partial { + fn drop(&mut self) { + if !self.ptr.is_null() { + unsafe { + ptr::drop_in_place(std::slice::from_raw_parts_mut(self.ptr, self.len)); + } + } + } +} diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 57c0e2dc5..f894390e6 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -8,7 +8,6 @@ #[macro_use] mod zipmacro; -mod partial_array; use std::mem::MaybeUninit; @@ -17,13 +16,12 @@ use crate::AssignElem; use crate::IntoDimension; use crate::Layout; use crate::NdIndex; +use crate::partial::Partial; use crate::indexes::{indices, Indices}; use crate::layout::{CORDER, FORDER}; use crate::split_at::{SplitPreference, SplitAt}; -use partial_array::PartialArray; - /// Return if the expression is a break value. macro_rules! fold_while { ($e:expr) => { @@ -1070,7 +1068,7 @@ macro_rules! map_impl { /// inputs. /// /// If all inputs are c- or f-order respectively, that is preserved in the output. - pub fn apply_collect(self, f: impl FnMut($($p::Item,)* ) -> R) -> Array + pub fn apply_collect(self, mut f: impl FnMut($($p::Item,)* ) -> R) -> Array { // Make uninit result let mut output = self.uninitalized_for_current_layout::(); @@ -1078,13 +1076,20 @@ macro_rules! map_impl { // For elements with no drop glue, just overwrite into the array self.apply_assign_into(&mut output, f); } else { - // For generic elements, use a proxy that counts the number of filled elements, + // For generic elements, use a Partial to counts the number of filled elements, // and can drop the right number of elements on unwinding unsafe { - PartialArray::scope(output.view_mut(), move |partial| { - debug_assert_eq!(partial.layout().tendency() >= 0, self.layout_tendency >= 0); - self.apply_assign_into(partial, f); + let mut output = output.raw_view_mut().cast::(); + let mut partial = Partial::new(output.as_mut_ptr()); + let partial_ref = &mut partial; + debug_assert!(output.is_contiguous()); + debug_assert_eq!(output.layout().tendency() >= 0, self.layout_tendency >= 0); + self.and(output) + .apply(move |$($p, )* output_: *mut R| { + output_.write(f($($p ),*)); + partial_ref.len += 1; }); + partial.release_ownership(); } } diff --git a/src/zip/partial_array.rs b/src/zip/partial_array.rs deleted file mode 100644 index d6a2bb9cd..000000000 --- a/src/zip/partial_array.rs +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2020 bluss and ndarray developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use crate::imp_prelude::*; -use crate::{ - AssignElem, - Layout, - NdProducer, - Zip, - FoldWhile, -}; - -use std::cell::Cell; -use std::mem; -use std::mem::MaybeUninit; -use std::ptr; - -/// An assignable element reference that increments a counter when assigned -pub(crate) struct ProxyElem<'a, 'b, A> { - item: &'a mut MaybeUninit, - filled: &'b Cell -} - -impl<'a, 'b, A> AssignElem for ProxyElem<'a, 'b, A> { - fn assign_elem(self, item: A) { - self.filled.set(self.filled.get() + 1); - *self.item = MaybeUninit::new(item); - } -} - -/// Handles progress of assigning to a part of an array, for elements that need -/// to be dropped on unwinding. See Self::scope. -pub(crate) struct PartialArray<'a, 'b, A, D> - where D: Dimension -{ - data: ArrayViewMut<'a, MaybeUninit, D>, - filled: &'b Cell, -} - -impl<'a, 'b, A, D> PartialArray<'a, 'b, A, D> - where D: Dimension -{ - /// Create a temporary PartialArray that wraps the array view `data`; - /// if the end of the scope is reached, the partial array is marked complete; - /// if execution unwinds at any time before them, the elements written until then - /// are dropped. - /// - /// Safety: the caller *must* ensure that elements will be written in `data`'s preferred order. - /// PartialArray can not handle arbitrary writes, only in the memory order. - pub(crate) unsafe fn scope(data: ArrayViewMut<'a, MaybeUninit, D>, - scope_fn: impl FnOnce(&mut PartialArray)) - { - let filled = Cell::new(0); - let mut partial = PartialArray::new(data, &filled); - scope_fn(&mut partial); - filled.set(0); // mark complete - } - - unsafe fn new(data: ArrayViewMut<'a, MaybeUninit, D>, - filled: &'b Cell) -> Self - { - debug_assert_eq!(filled.get(), 0); - Self { data, filled } - } -} - -impl<'a, 'b, A, D> Drop for PartialArray<'a, 'b, A, D> - where D: Dimension -{ - fn drop(&mut self) { - if !mem::needs_drop::() { - return; - } - - let mut count = self.filled.get(); - if count == 0 { - return; - } - - Zip::from(self).fold_while((), move |(), elt| { - if count > 0 { - count -= 1; - unsafe { - ptr::drop_in_place::(elt.item.as_mut_ptr()); - } - FoldWhile::Continue(()) - } else { - FoldWhile::Done(()) - } - }); - } -} - -impl<'a: 'c, 'b: 'c, 'c, A, D: Dimension> NdProducer for &'c mut PartialArray<'a, 'b, A, D> { - // This just wraps ArrayViewMut as NdProducer and maps the item - type Item = ProxyElem<'a, 'b, A>; - type Dim = D; - type Ptr = *mut MaybeUninit; - type Stride = isize; - - private_impl! {} - fn raw_dim(&self) -> Self::Dim { - self.data.raw_dim() - } - - fn equal_dim(&self, dim: &Self::Dim) -> bool { - self.data.equal_dim(dim) - } - - fn as_ptr(&self) -> Self::Ptr { - NdProducer::as_ptr(&self.data) - } - - fn layout(&self) -> Layout { - self.data.layout() - } - - unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { - ProxyElem { filled: self.filled, item: &mut *ptr } - } - - unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { - self.data.uget_ptr(i) - } - - fn stride_of(&self, axis: Axis) -> Self::Stride { - self.data.stride_of(axis) - } - - #[inline(always)] - fn contiguous_stride(&self) -> Self::Stride { - self.data.contiguous_stride() - } - - fn split_at(self, _axis: Axis, _index: usize) -> (Self, Self) { - unimplemented!(); - } -} - From e47261294d79d40b21c4690a457e5b2c7dc4049b Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 23 May 2020 22:15:56 +0200 Subject: [PATCH 081/651] FEAT: Combine common parts of apply_collect and par_apply_collect Factor out the common part of the parallel and and regular apply_collect implementation; the non-parallel part came first and ended up more complicated originally. With the parallel version in place, both can use the same main operation which is implemented in the methods Zip::collect_with_partial. --- src/parallel/impl_par_methods.rs | 18 +------ src/zip/mod.rs | 91 ++++++++++++++++++++------------ 2 files changed, 58 insertions(+), 51 deletions(-) diff --git a/src/parallel/impl_par_methods.rs b/src/parallel/impl_par_methods.rs index e5470e170..a4efa560f 100644 --- a/src/parallel/impl_par_methods.rs +++ b/src/parallel/impl_par_methods.rs @@ -99,25 +99,11 @@ macro_rules! zip_impl { }; let collect_result = splits.map(move |zip| { + // Apply the mapping function on this chunk of the zip // Create a partial result for the contiguous slice of data being written to - let output = zip.last_producer(); - debug_assert!(output.is_contiguous()); - let mut partial; unsafe { - partial = Partial::new(output.as_ptr()); + zip.collect_with_partial(&f) } - - // Apply the mapping function on this chunk of the zip - let partial_len = &mut partial.len; - let f = &f; - zip.apply(move |$($p,)* output_elem: *mut R| unsafe { - output_elem.write(f($($p),*)); - if std::mem::needs_drop::() { - *partial_len += 1; - } - }); - - partial }) .reduce(Partial::stub, Partial::try_merge); diff --git a/src/zip/mod.rs b/src/zip/mod.rs index f894390e6..8ce5e9869 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -914,12 +914,6 @@ zipt_impl! { [A B C D E F][ a b c d e f], } -#[cfg(feature = "rayon")] -macro_rules! last_of { - ($q:ty) => { $q }; - ($p:ty, $($q:ty),+) => { last_of!($($q),+) }; -} - macro_rules! map_impl { ($([$notlast:ident $($p:ident)*],)+) => { $( @@ -1016,14 +1010,6 @@ macro_rules! map_impl { }).is_done() } - #[cfg(feature = "rayon")] - #[allow(dead_code)] // unused for the first of the Zip arities - /// Return a reference to the last producer - pub(crate) fn last_producer(&self) -> &last_of!($($p),*) { - let (.., ref last) = &self.parts; - last - } - expand_if!(@bool [$notlast] /// Include the producer `p` in the Zip. @@ -1068,32 +1054,19 @@ macro_rules! map_impl { /// inputs. /// /// If all inputs are c- or f-order respectively, that is preserved in the output. - pub fn apply_collect(self, mut f: impl FnMut($($p::Item,)* ) -> R) -> Array + pub fn apply_collect(self, f: impl FnMut($($p::Item,)* ) -> R) -> Array { // Make uninit result let mut output = self.uninitalized_for_current_layout::(); - if !std::mem::needs_drop::() { - // For elements with no drop glue, just overwrite into the array - self.apply_assign_into(&mut output, f); - } else { - // For generic elements, use a Partial to counts the number of filled elements, - // and can drop the right number of elements on unwinding - unsafe { - let mut output = output.raw_view_mut().cast::(); - let mut partial = Partial::new(output.as_mut_ptr()); - let partial_ref = &mut partial; - debug_assert!(output.is_contiguous()); - debug_assert_eq!(output.layout().tendency() >= 0, self.layout_tendency >= 0); - self.and(output) - .apply(move |$($p, )* output_: *mut R| { - output_.write(f($($p ),*)); - partial_ref.len += 1; - }); - partial.release_ownership(); - } - } + // Use partial to counts the number of filled elements, and can drop the right + // number of elements on unwinding (if it happens during apply/collect). unsafe { + let output_view = output.raw_view_mut().cast::(); + self.and(output_view) + .collect_with_partial(f) + .release_ownership(); + output.assume_init() } } @@ -1126,6 +1099,54 @@ macro_rules! map_impl { } } + expand_if!(@bool [$notlast] + // For collect; Last producer is a RawViewMut + #[allow(non_snake_case)] + impl Zip<($($p,)* PLast), D> + where D: Dimension, + $($p: NdProducer ,)* + PLast: NdProducer, + { + /// The inner workings of apply_collect and par_apply_collect + /// + /// Apply the function and collect the results into the output (last producer) + /// which should be a raw array view; a Partial that owns the written + /// elements is returned. + /// + /// Elements will be overwritten in place (in the sense of std::ptr::write). + /// + /// ## Safety + /// + /// The last producer is a RawArrayViewMut and must be safe to write into. + /// The producer must be c- or f-contig and have the same layout tendency + /// as the whole Zip. + /// + /// The returned Partial's proxy ownership of the elements must be handled, + /// before the array the raw view points to realizes its ownership. + pub(crate) unsafe fn collect_with_partial(self, mut f: F) -> Partial + where F: FnMut($($p::Item,)* ) -> R + { + // Get the last producer; and make a Partial that aliases its data pointer + let (.., ref output) = &self.parts; + debug_assert!(output.layout().is(CORDER | FORDER)); + debug_assert_eq!(output.layout().tendency() >= 0, self.layout_tendency >= 0); + let mut partial = Partial::new(output.as_ptr()); + + // Apply the mapping function on this zip + // if we panic with unwinding; Partial will drop the written elements. + let partial_len = &mut partial.len; + self.apply(move |$($p,)* output_elem: *mut R| { + output_elem.write(f($($p),*)); + if std::mem::needs_drop::() { + *partial_len += 1; + } + }); + + partial + } + } + ); + impl SplitPreference for Zip<($($p,)*), D> where D: Dimension, $($p: NdProducer ,)* From cc05276596a783e828425f638b2ef4ea7d973600 Mon Sep 17 00:00:00 2001 From: Adam Jensen Date: Sat, 18 Jul 2020 21:20:09 -0400 Subject: [PATCH 082/651] Fix function name typo in quickstart guide --- README-quick-start.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README-quick-start.md b/README-quick-start.md index 2adc04d70..a6f07310d 100644 --- a/README-quick-start.md +++ b/README-quick-start.md @@ -89,7 +89,7 @@ and compile, see what happens. ### How about create array of different type and having different initial values? -The [`from_elm`](http://docs.rs/ndarray/latest/ndarray/struct.ArrayBase.html#method.from_elem) method can be handy here: +The [`from_elem`](http://docs.rs/ndarray/latest/ndarray/struct.ArrayBase.html#method.from_elem) method can be handy here: ```rust use ndarray::{Array, Ix3}; From dee84ae025631aa1c38fb1b92bd7ad7652806b85 Mon Sep 17 00:00:00 2001 From: "andrei.papou" Date: Thu, 17 Oct 2019 01:51:49 +0300 Subject: [PATCH 083/651] Removed allocation in `stack_new_axis`, renamed `concatenate` -> `stack`, `stack` -> `stack_new_axis` --- src/doc/ndarray_for_numpy_users/mod.rs | 8 +-- src/impl_methods.rs | 4 +- src/lib.rs | 2 +- src/stacking.rs | 69 +++++++++++++++++--------- tests/stacking.rs | 22 ++++---- 5 files changed, 64 insertions(+), 41 deletions(-) diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index 79d1314a8..d3125e712 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -532,8 +532,8 @@ //! ------|-----------|------ //! `a[:] = 3.` | [`a.fill(3.)`][.fill()] | set all array elements to the same scalar value //! `a[:] = b` | [`a.assign(&b)`][.assign()] | copy the data from array `b` into array `a` -//! `np.concatenate((a,b), axis=1)` | [`concatenate![Axis(1), a, b]`][concatenate!] or [`concatenate(Axis(1), &[a.view(), b.view()])`][concatenate()] | concatenate arrays `a` and `b` along axis 1 -//! `np.stack((a,b), axis=1)` | [`stack![Axis(1), a, b]`][stack!] or [`stack(Axis(1), vec![a.view(), b.view()])`][stack()] | stack arrays `a` and `b` along axis 1 +//! `np.concatenate((a,b), axis=1)` | [`stack![Axis(1), a, b]`][stack!] or [`stack(Axis(1), &[a.view(), b.view()])`][stack()] | concatenate arrays `a` and `b` along axis 1 +//! `np.stack((a,b), axis=1)` | [`stack_new_axis![Axis(1), a, b]`][stack_new_axis!] or [`stack_new_axis(Axis(1), vec![a.view(), b.view()])`][stack_new_axis()] | stack arrays `a` and `b` along axis 1 //! `a[:,np.newaxis]` or `np.expand_dims(a, axis=1)` | [`a.insert_axis(Axis(1))`][.insert_axis()] | create an array from `a`, inserting a new axis 1 //! `a.transpose()` or `a.T` | [`a.t()`][.t()] or [`a.reversed_axes()`][.reversed_axes()] | transpose of array `a` (view for `.t()` or by-move for `.reversed_axes()`) //! `np.diag(a)` | [`a.diag()`][.diag()] | view the diagonal of `a` @@ -641,10 +641,10 @@ //! [.slice_move()]: ../../struct.ArrayBase.html#method.slice_move //! [.slice_mut()]: ../../struct.ArrayBase.html#method.slice_mut //! [.shape()]: ../../struct.ArrayBase.html#method.shape -//! [concatenate!]: ../../macro.concatenate.html -//! [concatenate()]: ../../fn.concatenate.html //! [stack!]: ../../macro.stack.html //! [stack()]: ../../fn.stack.html +//! [stack_new_axis!]: ../../macro.stack_new_axis.html +//! [stack_new_axis()]: ../../fn.stack_new_axis.html //! [.strides()]: ../../struct.ArrayBase.html#method.strides //! [.index_axis()]: ../../struct.ArrayBase.html#method.index_axis //! [.sum_axis()]: ../../struct.ArrayBase.html#method.sum_axis diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 9853a1c47..db502663f 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -28,7 +28,7 @@ use crate::iter::{ IndexedIter, IndexedIterMut, Iter, IterMut, Lanes, LanesMut, Windows, }; use crate::slice::MultiSlice; -use crate::stacking::concatenate; +use crate::stacking::stack; use crate::{NdIndex, Slice, SliceInfo, SliceOrIndex}; /// # Methods For All Array Types @@ -840,7 +840,7 @@ where dim.set_axis(axis, 0); unsafe { Array::from_shape_vec_unchecked(dim, vec![]) } } else { - concatenate(axis, &subs).unwrap() + stack(axis, &subs).unwrap() } } diff --git a/src/lib.rs b/src/lib.rs index 191687c02..c12ea7a2e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,7 +131,7 @@ use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes, Lane pub use crate::arraytraits::AsArray; pub use crate::linalg_traits::{LinalgScalar, NdFloat}; -pub use crate::stacking::{concatenate, stack}; +pub use crate::stacking::{stack, stack_new_axis}; pub use crate::impl_views::IndexLonger; pub use crate::shape_builder::ShapeBuilder; diff --git a/src/stacking.rs b/src/stacking.rs index d0d4812b3..24b98108a 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -17,19 +17,19 @@ use crate::imp_prelude::*; /// if the result is larger than is possible to represent. /// /// ``` -/// use ndarray::{arr2, Axis, concatenate}; +/// use ndarray::{arr2, Axis, stack}; /// /// let a = arr2(&[[2., 2.], /// [3., 3.]]); /// assert!( -/// concatenate(Axis(0), &[a.view(), a.view()]) +/// stack(Axis(0), &[a.view(), a.view()]) /// == Ok(arr2(&[[2., 2.], /// [3., 3.], /// [2., 2.], /// [3., 3.]])) /// ); /// ``` -pub fn concatenate(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> +pub fn stack(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> where A: Copy, D: RemoveAxis, @@ -73,7 +73,7 @@ where Ok(res) } -pub fn stack( +pub fn stack_new_axis( axis: Axis, arrays: Vec>, ) -> Result, ShapeError> @@ -82,36 +82,59 @@ where D: Dimension, D::Larger: RemoveAxis, { - if let Some(ndim) = D::NDIM { - if axis.index() > ndim { - return Err(from_kind(ErrorKind::OutOfBounds)); - } + if arrays.is_empty() { + return Err(from_kind(ErrorKind::Unsupported)); + } + let common_dim = arrays[0].raw_dim(); + // Avoid panic on `insert_axis` call, return an Err instead of it. + if axis.index() > common_dim.ndim() { + return Err(from_kind(ErrorKind::OutOfBounds)); + } + let mut res_dim = common_dim.insert_axis(axis); + + if arrays.iter().any(|a| a.raw_dim() != common_dim) { + return Err(from_kind(ErrorKind::IncompatibleShape)); } - let arrays: Vec> = - arrays.into_iter().map(|a| a.insert_axis(axis)).collect(); - concatenate(axis, &arrays) + + res_dim.set_axis(axis, arrays.len()); + + // we can safely use uninitialized values here because they are Copy + // and we will only ever write to them + let size = res_dim.size(); + let mut v = Vec::with_capacity(size); + unsafe { + v.set_len(size); + } + let mut res = Array::from_shape_vec(res_dim, v)?; + + res.axis_iter_mut(axis).zip(arrays.into_iter()) + .for_each(|(mut assign_view, array)| { + assign_view.assign(&array); + }); + + Ok(res) } /// Concatenate arrays along the given axis. /// -/// Uses the [`concatenate`][1] function, calling `ArrayView::from(&a)` on each +/// Uses the [`stack`][1] function, calling `ArrayView::from(&a)` on each /// argument `a`. /// -/// [1]: fn.concatenate.html +/// [1]: fn.stack.html /// /// ***Panics*** if the `concatenate` function would return an error. /// /// ``` /// extern crate ndarray; /// -/// use ndarray::{arr2, concatenate, Axis}; +/// use ndarray::{arr2, stack, Axis}; /// /// # fn main() { /// /// let a = arr2(&[[2., 2.], /// [3., 3.]]); /// assert!( -/// concatenate![Axis(0), a, a] +/// stack![Axis(0), a, a] /// == arr2(&[[2., 2.], /// [3., 3.], /// [2., 2.], @@ -120,32 +143,32 @@ where /// # } /// ``` #[macro_export] -macro_rules! concatenate { +macro_rules! stack { ($axis:expr, $( $array:expr ),+ ) => { - $crate::concatenate($axis, &[ $($crate::ArrayView::from(&$array) ),* ]).unwrap() + $crate::stack($axis, &[ $($crate::ArrayView::from(&$array) ),* ]).unwrap() } } /// Stack arrays along the new axis. /// -/// Uses the [`stack`][1] function, calling `ArrayView::from(&a)` on each +/// Uses the [`stack_new_axis`][1] function, calling `ArrayView::from(&a)` on each /// argument `a`. /// -/// [1]: fn.concatenate.html +/// [1]: fn.stack_new_axis.html /// /// ***Panics*** if the `stack` function would return an error. /// /// ``` /// extern crate ndarray; /// -/// use ndarray::{arr2, arr3, stack, Axis}; +/// use ndarray::{arr2, arr3, stack_new_axis, Axis}; /// /// # fn main() { /// /// let a = arr2(&[[2., 2.], /// [3., 3.]]); /// assert!( -/// stack![Axis(0), a, a] +/// stack_new_axis![Axis(0), a, a] /// == arr3(&[[[2., 2.], /// [3., 3.]], /// [[2., 2.], @@ -154,8 +177,8 @@ macro_rules! concatenate { /// # } /// ``` #[macro_export] -macro_rules! stack { +macro_rules! stack_new_axis { ($axis:expr, $( $array:expr ),+ ) => { - $crate::stack($axis, vec![ $($crate::ArrayView::from(&$array) ),* ]).unwrap() + $crate::stack_new_axis($axis, vec![ $($crate::ArrayView::from(&$array) ),* ]).unwrap() } } diff --git a/tests/stacking.rs b/tests/stacking.rs index 2baaeed04..c07c5bb24 100644 --- a/tests/stacking.rs +++ b/tests/stacking.rs @@ -1,43 +1,43 @@ -use ndarray::{arr2, arr3, aview1, concatenate, Array2, Axis, ErrorKind, Ix1}; +use ndarray::{arr2, arr3, aview1, stack, Array2, Axis, ErrorKind, Ix1}; #[test] fn concatenating() { let a = arr2(&[[2., 2.], [3., 3.]]); - let b = ndarray::concatenate(Axis(0), &[a.view(), a.view()]).unwrap(); + let b = ndarray::stack(Axis(0), &[a.view(), a.view()]).unwrap(); assert_eq!(b, arr2(&[[2., 2.], [3., 3.], [2., 2.], [3., 3.]])); - let c = concatenate![Axis(0), a, b]; + let c = stack![Axis(0), a, b]; assert_eq!( c, arr2(&[[2., 2.], [3., 3.], [2., 2.], [3., 3.], [2., 2.], [3., 3.]]) ); - let d = concatenate![Axis(0), a.row(0), &[9., 9.]]; + let d = stack![Axis(0), a.row(0), &[9., 9.]]; assert_eq!(d, aview1(&[2., 2., 9., 9.])); - let res = ndarray::concatenate(Axis(1), &[a.view(), c.view()]); + let res = ndarray::stack(Axis(1), &[a.view(), c.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::IncompatibleShape); - let res = ndarray::concatenate(Axis(2), &[a.view(), c.view()]); + let res = ndarray::stack(Axis(2), &[a.view(), c.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::OutOfBounds); - let res: Result, _> = ndarray::concatenate(Axis(0), &[]); + let res: Result, _> = ndarray::stack(Axis(0), &[]); assert_eq!(res.unwrap_err().kind(), ErrorKind::Unsupported); } #[test] fn stacking() { let a = arr2(&[[2., 2.], [3., 3.]]); - let b = ndarray::stack(Axis(0), vec![a.view(), a.view()]).unwrap(); + let b = ndarray::stack_new_axis(Axis(0), vec![a.view(), a.view()]).unwrap(); assert_eq!(b, arr3(&[[[2., 2.], [3., 3.]], [[2., 2.], [3., 3.]]])); let c = arr2(&[[3., 2., 3.], [2., 3., 2.]]); - let res = ndarray::stack(Axis(1), vec![a.view(), c.view()]); + let res = ndarray::stack_new_axis(Axis(1), vec![a.view(), c.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::IncompatibleShape); - let res = ndarray::stack(Axis(3), vec![a.view(), a.view()]); + let res = ndarray::stack_new_axis(Axis(3), vec![a.view(), a.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::OutOfBounds); - let res: Result, _> = ndarray::stack::<_, Ix1>(Axis(0), vec![]); + let res: Result, _> = ndarray::stack_new_axis::<_, Ix1>(Axis(0), vec![]); assert_eq!(res.unwrap_err().kind(), ErrorKind::Unsupported); } From 1bb7104e43740116d00afc4471af48a99e52f325 Mon Sep 17 00:00:00 2001 From: "andrei.papou" Date: Thu, 17 Oct 2019 03:10:59 +0300 Subject: [PATCH 084/651] Rustfmt fixes --- src/stacking.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stacking.rs b/src/stacking.rs index 24b98108a..3847c6dc6 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -107,7 +107,8 @@ where } let mut res = Array::from_shape_vec(res_dim, v)?; - res.axis_iter_mut(axis).zip(arrays.into_iter()) + res.axis_iter_mut(axis) + .zip(arrays.into_iter()) .for_each(|(mut assign_view, array)| { assign_view.assign(&array); }); From 14f2f576f4dab52f97cb87e70f45eea69f7d935e Mon Sep 17 00:00:00 2001 From: "andrei.papou" Date: Wed, 18 Dec 2019 21:54:09 +0300 Subject: [PATCH 085/651] Introduced concatenate function, deprecated stack function since 0.13.0 --- src/lib.rs | 2 +- src/stacking.rs | 70 +++++++++++++++++++++++++++++++++++++++++++++-- tests/stacking.rs | 24 +++++++++++++++- 3 files changed, 92 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c12ea7a2e..f9b90a0f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,7 +131,7 @@ use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes, Lane pub use crate::arraytraits::AsArray; pub use crate::linalg_traits::{LinalgScalar, NdFloat}; -pub use crate::stacking::{stack, stack_new_axis}; +pub use crate::stacking::{concatenate, stack, stack_new_axis}; pub use crate::impl_views::IndexLonger; pub use crate::shape_builder::ShapeBuilder; diff --git a/src/stacking.rs b/src/stacking.rs index 3847c6dc6..d5d0fb94c 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -9,7 +9,7 @@ use crate::error::{from_kind, ErrorKind, ShapeError}; use crate::imp_prelude::*; -/// Stack arrays along the given axis. +/// Concatenate arrays along the given axis. /// /// ***Errors*** if the arrays have mismatching shapes, apart from along `axis`. /// (may be made more flexible in the future).
@@ -29,6 +29,10 @@ use crate::imp_prelude::*; /// [3., 3.]])) /// ); /// ``` +#[deprecated( + since = "0.13.0", + note = "Please use the `concatenate` function instead" +)] pub fn stack(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> where A: Copy, @@ -73,6 +77,34 @@ where Ok(res) } +/// Concatenate arrays along the given axis. +/// +/// ***Errors*** if the arrays have mismatching shapes, apart from along `axis`. +/// (may be made more flexible in the future).
+/// ***Errors*** if `arrays` is empty, if `axis` is out of bounds, +/// if the result is larger than is possible to represent. +/// +/// ``` +/// use ndarray::{arr2, Axis, concatenate}; +/// +/// let a = arr2(&[[2., 2.], +/// [3., 3.]]); +/// assert!( +/// concatenate(Axis(0), &[a.view(), a.view()]) +/// == Ok(arr2(&[[2., 2.], +/// [3., 3.], +/// [2., 2.], +/// [3., 3.]])) +/// ); +/// ``` +pub fn concatenate(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> +where + A: Copy, + D: RemoveAxis, +{ + stack(axis, arrays) +} + pub fn stack_new_axis( axis: Axis, arrays: Vec>, @@ -123,7 +155,7 @@ where /// /// [1]: fn.stack.html /// -/// ***Panics*** if the `concatenate` function would return an error. +/// ***Panics*** if the `stack` function would return an error. /// /// ``` /// extern crate ndarray; @@ -150,6 +182,40 @@ macro_rules! stack { } } +/// Concatenate arrays along the given axis. +/// +/// Uses the [`concatenate`][1] function, calling `ArrayView::from(&a)` on each +/// argument `a`. +/// +/// [1]: fn.concatenate.html +/// +/// ***Panics*** if the `concatenate` function would return an error. +/// +/// ``` +/// extern crate ndarray; +/// +/// use ndarray::{arr2, concatenate, Axis}; +/// +/// # fn main() { +/// +/// let a = arr2(&[[2., 2.], +/// [3., 3.]]); +/// assert!( +/// concatenate![Axis(0), a, a] +/// == arr2(&[[2., 2.], +/// [3., 3.], +/// [2., 2.], +/// [3., 3.]]) +/// ); +/// # } +/// ``` +#[macro_export] +macro_rules! concatenate { + ($axis:expr, $( $array:expr ),+ ) => { + $crate::concatenate($axis, &[ $($crate::ArrayView::from(&$array) ),* ]).unwrap() + } +} + /// Stack arrays along the new axis. /// /// Uses the [`stack_new_axis`][1] function, calling `ArrayView::from(&a)` on each diff --git a/tests/stacking.rs b/tests/stacking.rs index c07c5bb24..f723adc57 100644 --- a/tests/stacking.rs +++ b/tests/stacking.rs @@ -1,4 +1,4 @@ -use ndarray::{arr2, arr3, aview1, stack, Array2, Axis, ErrorKind, Ix1}; +use ndarray::{arr2, arr3, aview1, concatenate, stack, Array2, Axis, ErrorKind, Ix1}; #[test] fn concatenating() { @@ -23,6 +23,28 @@ fn concatenating() { let res: Result, _> = ndarray::stack(Axis(0), &[]); assert_eq!(res.unwrap_err().kind(), ErrorKind::Unsupported); + + let a = arr2(&[[2., 2.], [3., 3.]]); + let b = ndarray::concatenate(Axis(0), &[a.view(), a.view()]).unwrap(); + assert_eq!(b, arr2(&[[2., 2.], [3., 3.], [2., 2.], [3., 3.]])); + + let c = concatenate![Axis(0), a, b]; + assert_eq!( + c, + arr2(&[[2., 2.], [3., 3.], [2., 2.], [3., 3.], [2., 2.], [3., 3.]]) + ); + + let d = concatenate![Axis(0), a.row(0), &[9., 9.]]; + assert_eq!(d, aview1(&[2., 2., 9., 9.])); + + let res = ndarray::concatenate(Axis(1), &[a.view(), c.view()]); + assert_eq!(res.unwrap_err().kind(), ErrorKind::IncompatibleShape); + + let res = ndarray::concatenate(Axis(2), &[a.view(), c.view()]); + assert_eq!(res.unwrap_err().kind(), ErrorKind::OutOfBounds); + + let res: Result, _> = ndarray::concatenate(Axis(0), &[]); + assert_eq!(res.unwrap_err().kind(), ErrorKind::Unsupported); } #[test] From ef74ac900b24f455ff0db8a614f58097d469e5bd Mon Sep 17 00:00:00 2001 From: "andrei.papou" Date: Wed, 18 Dec 2019 22:22:21 +0300 Subject: [PATCH 086/651] Updated deprecation version for stack function, suppressed deprecation warnings --- src/impl_methods.rs | 4 ++-- src/lib.rs | 2 ++ src/stacking.rs | 3 ++- tests/stacking.rs | 2 ++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index db502663f..9853a1c47 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -28,7 +28,7 @@ use crate::iter::{ IndexedIter, IndexedIterMut, Iter, IterMut, Lanes, LanesMut, Windows, }; use crate::slice::MultiSlice; -use crate::stacking::stack; +use crate::stacking::concatenate; use crate::{NdIndex, Slice, SliceInfo, SliceOrIndex}; /// # Methods For All Array Types @@ -840,7 +840,7 @@ where dim.set_axis(axis, 0); unsafe { Array::from_shape_vec_unchecked(dim, vec![]) } } else { - stack(axis, &subs).unwrap() + concatenate(axis, &subs).unwrap() } } diff --git a/src/lib.rs b/src/lib.rs index f9b90a0f8..ad7ffec7d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,6 +131,8 @@ use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes, Lane pub use crate::arraytraits::AsArray; pub use crate::linalg_traits::{LinalgScalar, NdFloat}; + +#[allow(deprecated)] pub use crate::stacking::{concatenate, stack, stack_new_axis}; pub use crate::impl_views::IndexLonger; diff --git a/src/stacking.rs b/src/stacking.rs index d5d0fb94c..822b4b56d 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -30,7 +30,7 @@ use crate::imp_prelude::*; /// ); /// ``` #[deprecated( - since = "0.13.0", + since = "0.13.1", note = "Please use the `concatenate` function instead" )] pub fn stack(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> @@ -97,6 +97,7 @@ where /// [3., 3.]])) /// ); /// ``` +#[allow(deprecated)] pub fn concatenate(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> where A: Copy, diff --git a/tests/stacking.rs b/tests/stacking.rs index f723adc57..27d36d0f3 100644 --- a/tests/stacking.rs +++ b/tests/stacking.rs @@ -1,3 +1,5 @@ +#![allow(deprecated)] + use ndarray::{arr2, arr3, aview1, concatenate, stack, Array2, Axis, ErrorKind, Ix1}; #[test] From 06e61458831fd41db61ee614df6102583b04d1cd Mon Sep 17 00:00:00 2001 From: andrei-papou Date: Fri, 25 Sep 2020 11:46:39 +0300 Subject: [PATCH 087/651] Updated `stack_new_axis`, deprecated `stack!` macro. --- src/stacking.rs | 34 +++++++++++++++++++++++++++++++--- tests/stacking.rs | 8 ++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/stacking.rs b/src/stacking.rs index 822b4b56d..e31581679 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -30,7 +30,7 @@ use crate::imp_prelude::*; /// ); /// ``` #[deprecated( - since = "0.13.1", + since = "0.13.2", note = "Please use the `concatenate` function instead" )] pub fn stack(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> @@ -106,9 +106,33 @@ where stack(axis, arrays) } +/// Stack arrays along the new axis. +/// +/// ***Errors*** if the arrays have mismatching shapes. +/// ***Errors*** if `arrays` is empty, if `axis` is out of bounds, +/// if the result is larger than is possible to represent. +/// +/// ``` +/// extern crate ndarray; +/// +/// use ndarray::{arr2, arr3, stack_new_axis, Axis}; +/// +/// # fn main() { +/// +/// let a = arr2(&[[2., 2.], +/// [3., 3.]]); +/// assert!( +/// stack_new_axis(Axis(0), &[a.view(), a.view()]) +/// == Ok(arr3(&[[[2., 2.], +/// [3., 3.]], +/// [[2., 2.], +/// [3., 3.]]])) +/// ); +/// # } +/// ``` pub fn stack_new_axis( axis: Axis, - arrays: Vec>, + arrays: &[ArrayView], ) -> Result, ShapeError> where A: Copy, @@ -176,6 +200,10 @@ where /// ); /// # } /// ``` +#[deprecated( + since = "0.13.2", + note = "Please use the `concatenate!` macro instead" +)] #[macro_export] macro_rules! stack { ($axis:expr, $( $array:expr ),+ ) => { @@ -247,6 +275,6 @@ macro_rules! concatenate { #[macro_export] macro_rules! stack_new_axis { ($axis:expr, $( $array:expr ),+ ) => { - $crate::stack_new_axis($axis, vec![ $($crate::ArrayView::from(&$array) ),* ]).unwrap() + $crate::stack_new_axis($axis, &[ $($crate::ArrayView::from(&$array) ),* ]).unwrap() } } diff --git a/tests/stacking.rs b/tests/stacking.rs index 27d36d0f3..94077def2 100644 --- a/tests/stacking.rs +++ b/tests/stacking.rs @@ -52,16 +52,16 @@ fn concatenating() { #[test] fn stacking() { let a = arr2(&[[2., 2.], [3., 3.]]); - let b = ndarray::stack_new_axis(Axis(0), vec![a.view(), a.view()]).unwrap(); + let b = ndarray::stack_new_axis(Axis(0), &[a.view(), a.view()]).unwrap(); assert_eq!(b, arr3(&[[[2., 2.], [3., 3.]], [[2., 2.], [3., 3.]]])); let c = arr2(&[[3., 2., 3.], [2., 3., 2.]]); - let res = ndarray::stack_new_axis(Axis(1), vec![a.view(), c.view()]); + let res = ndarray::stack_new_axis(Axis(1), &[a.view(), c.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::IncompatibleShape); - let res = ndarray::stack_new_axis(Axis(3), vec![a.view(), a.view()]); + let res = ndarray::stack_new_axis(Axis(3), &[a.view(), a.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::OutOfBounds); - let res: Result, _> = ndarray::stack_new_axis::<_, Ix1>(Axis(0), vec![]); + let res: Result, _> = ndarray::stack_new_axis::<_, Ix1>(Axis(0), &[]); assert_eq!(res.unwrap_err().kind(), ErrorKind::Unsupported); } From 355449fc6d5044f60496bd72871f0f4cadd92002 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Wed, 11 Nov 2020 20:38:32 +0100 Subject: [PATCH 088/651] bump MSRV to 1.40 --- .travis.yml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c46a2c790..ede7d1f3b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ sudo: required dist: trusty matrix: include: - - rust: 1.37.0 + - rust: 1.40.0 env: - FEATURES='test docs' - RUSTFLAGS='-D warnings' diff --git a/src/lib.rs b/src/lib.rs index 1b7590da1..5ea7e375b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,7 +61,7 @@ //! needs matching memory layout to be efficient (with some exceptions). //! + Efficient floating point matrix multiplication even for very large //! matrices; can optionally use BLAS to improve it further. -//! - **Requires Rust 1.37 or later** +//! - **Requires Rust 1.40 or later** //! //! ## Crate Feature Flags //! From f9b8286c3472c01608e81dee7d100696cfaf48f2 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Wed, 11 Nov 2020 20:40:01 +0100 Subject: [PATCH 089/651] Marking ErrorKind enum as #[non_exhaustive] --- src/error.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/error.rs b/src/error.rs index 79acdd8b3..be7b1f9a5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -33,6 +33,7 @@ impl ShapeError { /// /// This enumeration is not exhaustive. The representation of the enum /// is not guaranteed. +#[non_exhaustive] #[derive(Copy, Clone, Debug)] pub enum ErrorKind { /// incompatible shape From 7d370733af023ba3d6bdd451bb0497c3cd28e19c Mon Sep 17 00:00:00 2001 From: xd009642 Date: Wed, 11 Nov 2020 19:42:29 +0000 Subject: [PATCH 090/651] Update docs --- src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 1b7590da1..ea07521f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,6 +105,9 @@ //! but more advanced routines can be found in [`ndarray-stats`](https://crates.io/crates/ndarray-stats). //! //! If you are looking to generate random arrays instead, check out [`ndarray-rand`](https://crates.io/crates/ndarray-rand). +//! +//! For conversion between `ndarray`, [`nalgebra`](https://crates.io/crates/nalgebra) and +//! [`image`](https://crates.io/crates/image) check out [`nshare`](https://crates.io/crates/nshare). #[cfg(feature = "blas")] extern crate blas_src; From 3e5d02d04ea6192833e1bc6df83371f0de73626c Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Wed, 11 Nov 2020 23:12:08 +0100 Subject: [PATCH 091/651] remove __Incomplete ErrorKind due to using #[non_exhaustive] --- src/error.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/error.rs b/src/error.rs index be7b1f9a5..090937561 100644 --- a/src/error.rs +++ b/src/error.rs @@ -48,8 +48,6 @@ pub enum ErrorKind { Unsupported, /// overflow when computing offset, length, etc. Overflow, - #[doc(hidden)] - __Incomplete, } #[inline(always)] @@ -82,7 +80,6 @@ impl fmt::Display for ShapeError { ErrorKind::OutOfBounds => "out of bounds indexing", ErrorKind::Unsupported => "unsupported operation", ErrorKind::Overflow => "arithmetic overflow", - ErrorKind::__Incomplete => "this error variant is not in use", }; write!(f, "ShapeError/{:?}: {}", self.kind(), description) } From 9a6f8b6693ad90520c7ce94a638d9f09160b36f9 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Thu, 12 Nov 2020 00:27:50 +0100 Subject: [PATCH 092/651] Following clippy lints --- src/slice.rs | 10 ++-------- src/stacking.rs | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/slice.rs b/src/slice.rs index 15765c61d..86a2b0b8f 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -110,18 +110,12 @@ copy_and_clone! {SliceOrIndex} impl SliceOrIndex { /// Returns `true` if `self` is a `Slice` value. pub fn is_slice(&self) -> bool { - match self { - SliceOrIndex::Slice { .. } => true, - _ => false, - } + matches!(self, SliceOrIndex::Slice { .. }) } /// Returns `true` if `self` is an `Index` value. pub fn is_index(&self) -> bool { - match self { - SliceOrIndex::Index(_) => true, - _ => false, - } + matches!(self, SliceOrIndex::Index(_)) } /// Returns a new `SliceOrIndex` with the given step size (multiplied with diff --git a/src/stacking.rs b/src/stacking.rs index 3e3b1afd8..3774aa083 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -165,7 +165,7 @@ where let mut res = Array::from_shape_vec(res_dim, v)?; res.axis_iter_mut(axis) - .zip(arrays.into_iter()) + .zip(arrays.iter()) .for_each(|(mut assign_view, array)| { assign_view.assign(&array); }); From 929e6997ef2efab208551462f2ff9f024cf74102 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Thu, 12 Nov 2020 02:08:50 +0100 Subject: [PATCH 093/651] Move MSRV to 1.42 --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 5ea7e375b..55b5a5080 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,7 +61,7 @@ //! needs matching memory layout to be efficient (with some exceptions). //! + Efficient floating point matrix multiplication even for very large //! matrices; can optionally use BLAS to improve it further. -//! - **Requires Rust 1.40 or later** +//! - **Requires Rust 1.42 or later** //! //! ## Crate Feature Flags //! From 4f0cd9bbeea3077ba78f63d9fb2506e14c855c8b Mon Sep 17 00:00:00 2001 From: ZuseZ4 Date: Thu, 12 Nov 2020 06:16:23 +0100 Subject: [PATCH 094/651] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ede7d1f3b..f1dcd036a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ sudo: required dist: trusty matrix: include: - - rust: 1.40.0 + - rust: 1.42.0 env: - FEATURES='test docs' - RUSTFLAGS='-D warnings' From a27855c3384436e5045b19e25bdb24d0381a6794 Mon Sep 17 00:00:00 2001 From: andrei Date: Tue, 24 Nov 2020 14:58:09 +0300 Subject: [PATCH 095/651] Renamed stack and concatenate to be compliant with numpy naming. --- src/lib.rs | 1 - src/stacking.rs | 94 ++++++++++++++++++++++++----------------------- tests/stacking.rs | 35 ++++-------------- 3 files changed, 55 insertions(+), 75 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1b7590da1..1775d07a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -132,7 +132,6 @@ use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes, Lane pub use crate::arraytraits::AsArray; pub use crate::linalg_traits::{LinalgScalar, NdFloat}; -#[allow(deprecated)] pub use crate::stacking::{concatenate, stack, stack_new_axis}; pub use crate::impl_views::IndexLonger; diff --git a/src/stacking.rs b/src/stacking.rs index 3e3b1afd8..864c40998 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -9,6 +9,42 @@ use crate::error::{from_kind, ErrorKind, ShapeError}; use crate::imp_prelude::*; +/// Stack arrays along the new axis. +/// +/// ***Errors*** if the arrays have mismatching shapes. +/// ***Errors*** if `arrays` is empty, if `axis` is out of bounds, +/// if the result is larger than is possible to represent. +/// +/// ``` +/// extern crate ndarray; +/// +/// use ndarray::{arr2, arr3, stack, Axis}; +/// +/// # fn main() { +/// +/// let a = arr2(&[[2., 2.], +/// [3., 3.]]); +/// assert!( +/// stack(Axis(0), &[a.view(), a.view()]) +/// == Ok(arr3(&[[[2., 2.], +/// [3., 3.]], +/// [[2., 2.], +/// [3., 3.]]])) +/// ); +/// # } +/// ``` +pub fn stack( + axis: Axis, + arrays: &[ArrayView], +) -> Result, ShapeError> +where + A: Copy, + D: Dimension, + D::Larger: RemoveAxis, +{ + stack_new_axis(axis, arrays) +} + /// Concatenate arrays along the given axis. /// /// ***Errors*** if the arrays have mismatching shapes, apart from along `axis`. @@ -17,23 +53,20 @@ use crate::imp_prelude::*; /// if the result is larger than is possible to represent. /// /// ``` -/// use ndarray::{arr2, Axis, stack}; +/// use ndarray::{arr2, Axis, concatenate}; /// /// let a = arr2(&[[2., 2.], /// [3., 3.]]); /// assert!( -/// stack(Axis(0), &[a.view(), a.view()]) +/// concatenate(Axis(0), &[a.view(), a.view()]) /// == Ok(arr2(&[[2., 2.], /// [3., 3.], /// [2., 2.], /// [3., 3.]])) /// ); /// ``` -#[deprecated( - since = "0.13.2", - note = "Please use the `concatenate` function instead" -)] -pub fn stack(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> +#[allow(deprecated)] +pub fn concatenate(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> where A: Copy, D: RemoveAxis, @@ -77,35 +110,6 @@ where Ok(res) } -/// Concatenate arrays along the given axis. -/// -/// ***Errors*** if the arrays have mismatching shapes, apart from along `axis`. -/// (may be made more flexible in the future).
-/// ***Errors*** if `arrays` is empty, if `axis` is out of bounds, -/// if the result is larger than is possible to represent. -/// -/// ``` -/// use ndarray::{arr2, Axis, concatenate}; -/// -/// let a = arr2(&[[2., 2.], -/// [3., 3.]]); -/// assert!( -/// concatenate(Axis(0), &[a.view(), a.view()]) -/// == Ok(arr2(&[[2., 2.], -/// [3., 3.], -/// [2., 2.], -/// [3., 3.]])) -/// ); -/// ``` -#[allow(deprecated)] -pub fn concatenate(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> -where - A: Copy, - D: RemoveAxis, -{ - stack(axis, arrays) -} - /// Stack arrays along the new axis. /// /// ***Errors*** if the arrays have mismatching shapes. @@ -173,7 +177,7 @@ where Ok(res) } -/// Concatenate arrays along the given axis. +/// Stack arrays along the new axis. /// /// Uses the [`stack`][1] function, calling `ArrayView::from(&a)` on each /// argument `a`. @@ -183,7 +187,9 @@ where /// ***Panics*** if the `stack` function would return an error. /// /// ``` -/// use ndarray::{arr2, stack, Axis}; +/// extern crate ndarray; +/// +/// use ndarray::{arr2, arr3, stack, Axis}; /// /// # fn main() { /// @@ -191,17 +197,13 @@ where /// [3., 3.]]); /// assert!( /// stack![Axis(0), a, a] -/// == arr2(&[[2., 2.], -/// [3., 3.], -/// [2., 2.], -/// [3., 3.]]) +/// == arr3(&[[[2., 2.], +/// [3., 3.]], +/// [[2., 2.], +/// [3., 3.]]]) /// ); /// # } /// ``` -#[deprecated( - since = "0.13.2", - note = "Please use the `concatenate!` macro instead" -)] #[macro_export] macro_rules! stack { ($axis:expr, $( $array:expr ),+ ) => { diff --git a/tests/stacking.rs b/tests/stacking.rs index 94077def2..032525ffa 100644 --- a/tests/stacking.rs +++ b/tests/stacking.rs @@ -1,31 +1,7 @@ -#![allow(deprecated)] - use ndarray::{arr2, arr3, aview1, concatenate, stack, Array2, Axis, ErrorKind, Ix1}; #[test] fn concatenating() { - let a = arr2(&[[2., 2.], [3., 3.]]); - let b = ndarray::stack(Axis(0), &[a.view(), a.view()]).unwrap(); - assert_eq!(b, arr2(&[[2., 2.], [3., 3.], [2., 2.], [3., 3.]])); - - let c = stack![Axis(0), a, b]; - assert_eq!( - c, - arr2(&[[2., 2.], [3., 3.], [2., 2.], [3., 3.], [2., 2.], [3., 3.]]) - ); - - let d = stack![Axis(0), a.row(0), &[9., 9.]]; - assert_eq!(d, aview1(&[2., 2., 9., 9.])); - - let res = ndarray::stack(Axis(1), &[a.view(), c.view()]); - assert_eq!(res.unwrap_err().kind(), ErrorKind::IncompatibleShape); - - let res = ndarray::stack(Axis(2), &[a.view(), c.view()]); - assert_eq!(res.unwrap_err().kind(), ErrorKind::OutOfBounds); - - let res: Result, _> = ndarray::stack(Axis(0), &[]); - assert_eq!(res.unwrap_err().kind(), ErrorKind::Unsupported); - let a = arr2(&[[2., 2.], [3., 3.]]); let b = ndarray::concatenate(Axis(0), &[a.view(), a.view()]).unwrap(); assert_eq!(b, arr2(&[[2., 2.], [3., 3.], [2., 2.], [3., 3.]])); @@ -52,16 +28,19 @@ fn concatenating() { #[test] fn stacking() { let a = arr2(&[[2., 2.], [3., 3.]]); - let b = ndarray::stack_new_axis(Axis(0), &[a.view(), a.view()]).unwrap(); + let b = ndarray::stack(Axis(0), &[a.view(), a.view()]).unwrap(); assert_eq!(b, arr3(&[[[2., 2.], [3., 3.]], [[2., 2.], [3., 3.]]])); + let c = stack![Axis(0), a, a]; + assert_eq!(c, arr3(&[[[2., 2.], [3., 3.]], [[2., 2.], [3., 3.]]])); + let c = arr2(&[[3., 2., 3.], [2., 3., 2.]]); - let res = ndarray::stack_new_axis(Axis(1), &[a.view(), c.view()]); + let res = ndarray::stack(Axis(1), &[a.view(), c.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::IncompatibleShape); - let res = ndarray::stack_new_axis(Axis(3), &[a.view(), a.view()]); + let res = ndarray::stack(Axis(3), &[a.view(), a.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::OutOfBounds); - let res: Result, _> = ndarray::stack_new_axis::<_, Ix1>(Axis(0), &[]); + let res: Result, _> = ndarray::stack::<_, Ix1>(Axis(0), &[]); assert_eq!(res.unwrap_err().kind(), ErrorKind::Unsupported); } From 13dbaf1ab271403ebebf2fc12bc68d3548866fab Mon Sep 17 00:00:00 2001 From: andrei-papou Date: Tue, 24 Nov 2020 15:05:03 +0300 Subject: [PATCH 096/651] Removed allow deprecated macro from concatenate implementation. --- src/stacking.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/stacking.rs b/src/stacking.rs index 864c40998..981f37ef3 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -65,7 +65,6 @@ where /// [3., 3.]])) /// ); /// ``` -#[allow(deprecated)] pub fn concatenate(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> where A: Copy, From 5cf75720e166824e444c0cdea389ea5cbf29fe10 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 28 Nov 2020 16:25:54 +0100 Subject: [PATCH 097/651] API: Bump num-complex to version 0.3 Use num-complex without activating std (because ndarray does not need it by itself) - this means we can't use norm by default in the test. --- Cargo.toml | 2 +- tests/complex.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 48fad0822..22fe19e76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ test = true [dependencies] num-integer = "0.1.39" num-traits = "0.2" -num-complex = "0.2" +num-complex = { version = "0.3", default-features = false } rayon = { version = "1.0.3", optional = true } diff --git a/tests/complex.rs b/tests/complex.rs index 543889dd7..1b52b2671 100644 --- a/tests/complex.rs +++ b/tests/complex.rs @@ -10,7 +10,7 @@ fn c(re: T, im: T) -> Complex { #[test] fn complex_mat_mul() { let a = arr2(&[[c(3., 4.), c(2., 0.)], [c(0., -2.), c(3., 0.)]]); - let b = (&a * c(3., 0.)).map(|c| 5. * c / c.norm()); + let b = (&a * c(3., 0.)).map(|c| 5. * c / c.norm_sqr()); println!("{:>8.2}", b); let e = Array::eye(2); let r = a.dot(&e); From a9b4ab86f90ad23a544a80fe1e07933d62a8555d Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 28 Nov 2020 16:26:43 +0100 Subject: [PATCH 098/651] API: Bump approx to 0.4 --- Cargo.toml | 4 ++-- blas-tests/Cargo.toml | 2 +- numeric-tests/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 22fe19e76..30c9fada6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ num-complex = { version = "0.3", default-features = false } rayon = { version = "1.0.3", optional = true } -approx = { version = "0.3.2", optional = true } +approx = { version = "0.4", optional = true } # Use via the `blas` crate feature! cblas-sys = { version = "0.1.4", optional = true, default-features = false } @@ -47,7 +47,7 @@ rawpointer = { version = "0.2" } [dev-dependencies] defmac = "0.2" quickcheck = { version = "0.9", default-features = false } -approx = "0.3.2" +approx = "0.4" itertools = { version = "0.9.0", default-features = false, features = ["use_std"] } [features] diff --git a/blas-tests/Cargo.toml b/blas-tests/Cargo.toml index d7b701084..da9aad98f 100644 --- a/blas-tests/Cargo.toml +++ b/blas-tests/Cargo.toml @@ -8,7 +8,7 @@ publish = false test = false [dev-dependencies] -approx = "0.3.2" +approx = "0.4" ndarray = { path = "../", features = ["approx", "blas"] } blas-src = { version = "0.6.1", default-features = false, features = ["openblas"] } openblas-src = { version = "0.9.0", default-features = false, features = ["cblas", "system"] } diff --git a/numeric-tests/Cargo.toml b/numeric-tests/Cargo.toml index dc1261512..d6549eed4 100644 --- a/numeric-tests/Cargo.toml +++ b/numeric-tests/Cargo.toml @@ -5,7 +5,7 @@ authors = ["bluss"] publish = false [dependencies] -approx = "0.3.2" +approx = "0.4" ndarray = { path = "..", features = ["approx"] } ndarray-rand = { path = "../ndarray-rand/" } rand_distr = "0.2.1" From 84eeb11d1d477d54e52ef5f7dc9df784834a0c8d Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 28 Nov 2020 16:26:54 +0100 Subject: [PATCH 099/651] MAINT: Write license spec in Cargo.toml in the more spec-correct way --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 30c9fada6..362d62afa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ authors = [ "bluss", "Jim Turner" ] -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" readme = "README-crates.io.md" repository = "https://github.com/rust-ndarray/ndarray" From 8fec0ebf99abb4016c23cbceae0c7029e2311bb2 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 28 Nov 2020 21:02:17 +0100 Subject: [PATCH 100/651] API: Remove RcArray (deprecated alias of ArcArray) --- src/aliases.rs | 11 +---------- src/data_traits.rs | 10 +++++----- src/impl_methods.rs | 4 ++-- src/lib.rs | 16 ---------------- src/prelude.rs | 1 - 5 files changed, 8 insertions(+), 34 deletions(-) diff --git a/src/aliases.rs b/src/aliases.rs index f7c71b3d4..911e745bd 100644 --- a/src/aliases.rs +++ b/src/aliases.rs @@ -3,7 +3,7 @@ use crate::dimension::Dim; #[allow(deprecated)] -use crate::{ArcArray, Array, ArrayView, ArrayViewMut, Ix, IxDynImpl, RcArray}; +use crate::{ArcArray, Array, ArrayView, ArrayViewMut, Ix, IxDynImpl}; /// Create a zero-dimensional index #[allow(non_snake_case)] @@ -150,15 +150,6 @@ pub type ArrayViewMut6<'a, A> = ArrayViewMut<'a, A, Ix6>; /// dynamic-dimensional read-write array view pub type ArrayViewMutD<'a, A> = ArrayViewMut<'a, A, IxDyn>; -/// one-dimensional shared ownership array -#[allow(deprecated)] -#[deprecated(note = "`RcArray` has been renamed to `ArcArray`")] -pub type RcArray1
= RcArray; -/// two-dimensional shared ownership array -#[allow(deprecated)] -#[deprecated(note = "`RcArray` has been renamed to `ArcArray`")] -pub type RcArray2 = RcArray; - /// one-dimensional shared ownership array pub type ArcArray1 = ArcArray; /// two-dimensional shared ownership array diff --git a/src/data_traits.rs b/src/data_traits.rs index 8c22a225c..a3e00c1ed 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -14,7 +14,7 @@ use std::ptr::NonNull; use std::sync::Arc; use crate::{ - ArrayBase, CowRepr, Dimension, OwnedArcRepr, OwnedRcRepr, OwnedRepr, RawViewRepr, ViewRepr, + ArrayBase, CowRepr, Dimension, OwnedArcRepr, OwnedRepr, RawViewRepr, ViewRepr, }; /// Array representation trait. @@ -397,7 +397,7 @@ pub unsafe trait DataOwned: Data { /// Converts the data representation to a shared (copy on write) /// representation, without any copying. #[doc(hidden)] - fn into_shared(self) -> OwnedRcRepr; + fn into_shared(self) -> OwnedArcRepr; } /// Array representation trait. @@ -407,14 +407,14 @@ pub unsafe trait DataOwned: Data { /// ***Internal trait, see `Data`.*** pub unsafe trait DataShared: Clone + Data + RawDataClone {} -unsafe impl DataShared for OwnedRcRepr {} +unsafe impl DataShared for OwnedArcRepr {} unsafe impl<'a, A> DataShared for ViewRepr<&'a A> {} unsafe impl DataOwned for OwnedRepr { fn new(elements: Vec) -> Self { OwnedRepr::from(elements) } - fn into_shared(self) -> OwnedRcRepr { + fn into_shared(self) -> OwnedArcRepr { OwnedArcRepr(Arc::new(self)) } } @@ -424,7 +424,7 @@ unsafe impl DataOwned for OwnedArcRepr { OwnedArcRepr(Arc::new(OwnedRepr::from(elements))) } - fn into_shared(self) -> OwnedRcRepr { + fn into_shared(self) -> OwnedArcRepr { self } } diff --git a/src/impl_methods.rs b/src/impl_methods.rs index ca5b5499a..0fcfee33d 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1363,7 +1363,7 @@ where where S: RawDataMut, { - self.try_ensure_unique(); // for RcArray + self.try_ensure_unique(); // for ArcArray self.ptr.as_ptr() } @@ -1379,7 +1379,7 @@ where where S: RawDataMut, { - self.try_ensure_unique(); // for RcArray + self.try_ensure_unique(); // for ArcArray unsafe { RawArrayViewMut::new(self.ptr, self.dim.clone(), self.strides.clone()) } } diff --git a/src/lib.rs b/src/lib.rs index a30e98dbf..8d5f0e034 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1241,14 +1241,6 @@ where strides: D, } -/// An array where the data has shared ownership and is copy on write. -/// -/// It can act as both an owner as the data as well as a shared reference (view like). -/// -/// **Note: this type alias is obsolete.** See the equivalent [`ArcArray`] instead. -#[deprecated(note = "`RcArray` has been renamed to `ArcArray`")] -pub type RcArray = ArrayBase, D>; - /// An array where the data has shared ownership and is copy on write. /// /// The `ArcArray` is parameterized by `A` for the element type and `D` for @@ -1399,14 +1391,6 @@ pub type RawArrayViewMut = ArrayBase, D>; pub use data_repr::OwnedRepr; - -/// RcArray's representation. -/// -/// *Don’t use this type directly—use the type alias -/// [`RcArray`](type.RcArray.html) for the array type!* -#[deprecated(note = "RcArray is replaced by ArcArray")] -pub use self::OwnedArcRepr as OwnedRcRepr; - /// ArcArray's representation. /// /// *Don’t use this type directly—use the type alias diff --git a/src/prelude.rs b/src/prelude.rs index 8662a4d34..190f679a4 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -20,7 +20,6 @@ #[allow(deprecated)] pub use crate::{ ArcArray, Array, ArrayBase, ArrayView, ArrayViewMut, CowArray, RawArrayView, RawArrayViewMut, - RcArray, }; #[doc(no_inline)] From f51f3e42a7e6aaa06535dbc11730c52c8f70ad82 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 28 Nov 2020 21:03:05 +0100 Subject: [PATCH 101/651] API: Remove deprecated subview and indexing methods We had renamed subview_inplace to collapse_axis (deprecating the old name). We had renamed subview_mut toindex_axis_mut (deprecating the old name). We had renamed into_subview to index_axis_move (deprecating the old name). We had renamed subview to index_axis (deprecating the old name). We had renamed slice_inplace to slice_collapse (deprecating the old name). UNdeprecate remove_axis because its replacement is quite cryptic. --- src/impl_methods.rs | 58 +++------------------------------------------ 1 file changed, 3 insertions(+), 55 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 0fcfee33d..061be1fe2 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -456,15 +456,6 @@ where }); } - /// Slice the array in place without changing the number of dimensions. - /// - /// **Panics** if an index is out of bounds or step size is zero.
- /// (**Panics** if `D` is `IxDyn` and `indices` does not match the number of array axes.) - #[deprecated(note = "renamed to `slice_collapse`", since = "0.12.1")] - pub fn slice_inplace(&mut self, indices: &D::SliceArg) { - self.slice_collapse(indices) - } - /// Return a view of the array, sliced along the specified axis. /// /// **Panics** if an index is out of bounds or step size is zero.
@@ -759,51 +750,6 @@ where debug_assert!(self.pointer_is_inbounds()); } - /// Along `axis`, select the subview `index` and return a - /// view with that axis removed. - /// - /// **Panics** if `axis` or `index` is out of bounds. - #[deprecated(note = "renamed to `index_axis`", since = "0.12.1")] - pub fn subview(&self, axis: Axis, index: Ix) -> ArrayView<'_, A, D::Smaller> - where - S: Data, - D: RemoveAxis, - { - self.index_axis(axis, index) - } - - /// Along `axis`, select the subview `index` and return a read-write view - /// with the axis removed. - /// - /// **Panics** if `axis` or `index` is out of bounds. - #[deprecated(note = "renamed to `index_axis_mut`", since = "0.12.1")] - pub fn subview_mut(&mut self, axis: Axis, index: Ix) -> ArrayViewMut<'_, A, D::Smaller> - where - S: DataMut, - D: RemoveAxis, - { - self.index_axis_mut(axis, index) - } - - /// Collapse dimension `axis` into length one, - /// and select the subview of `index` along that axis. - /// - /// **Panics** if `index` is past the length of the axis. - #[deprecated(note = "renamed to `collapse_axis`", since = "0.12.1")] - pub fn subview_inplace(&mut self, axis: Axis, index: Ix) { - self.collapse_axis(axis, index) - } - - /// Along `axis`, select the subview `index` and return `self` - /// with that axis removed. - #[deprecated(note = "renamed to `index_axis_move`", since = "0.12.1")] - pub fn into_subview(self, axis: Axis, index: Ix) -> ArrayBase - where - D: RemoveAxis, - { - self.index_axis_move(axis, index) - } - /// Along `axis`, select arbitrary subviews corresponding to `indices` /// and and copy them into a new array. /// @@ -1886,8 +1832,10 @@ where /// Remove array axis `axis` and return the result. /// + /// This is equivalent to `.index-axis_move(axis, 0)` and makes most sense to use if the + /// axis to remove is of length 1. + /// /// **Panics** if the axis is out of bounds or its length is zero. - #[deprecated(note = "use `.index_axis_move(Axis(_), 0)` instead", since = "0.12.1")] pub fn remove_axis(self, axis: Axis) -> ArrayBase where D: RemoveAxis, From fec35f7d10fc384e1821cb6ee2dd5874f7314132 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 28 Nov 2020 20:53:31 +0100 Subject: [PATCH 102/651] 0.14.0 --- Cargo.toml | 2 +- README.rst | 9 +++--- RELEASES.md | 69 ++++++++++++++++++++++++++++++++++++++++- ndarray-rand/Cargo.toml | 2 +- src/lib.rs | 2 +- 5 files changed, 76 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 362d62afa..4dcc8831d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ndarray" -version = "0.13.1" +version = "0.14.0" edition = "2018" authors = [ "bluss", diff --git a/README.rst b/README.rst index 9e2580a77..ded336b61 100644 --- a/README.rst +++ b/README.rst @@ -71,7 +71,7 @@ How to use with cargo :: [dependencies] - ndarray = "0.13.0" + ndarray = "0.14.0" How to enable blas integration. Depend on ``blas-src`` directly to pick a blas provider. Depend on the same ``blas-src`` version as ``ndarray`` does, for the @@ -81,15 +81,16 @@ provider:: [dependencies] - ndarray = { version = "0.13.0", features = ["blas"] } - blas-src = { version = "0.2.0", default-features = false, features = ["openblas"] } - openblas-src = { version = "0.6.0", default-features = false, features = ["cblas", "system"] } + ndarray = { version = "0.14.0", features = ["blas"] } + blas-src = { version = "0.6.1", default-features = false, features = ["openblas"] } + openblas-src = { version = "0.9", default-features = false, features = ["cblas", "system"] } For official releases of ``ndarray``, the versions are: =========== ============ ================ ``ndarray`` ``blas-src`` ``openblas-src`` =========== ============ ================ +0.14.0 0.6.1 0.9.0 0.13.0 0.2.0 0.6.0 0.12.\* 0.2.0 0.6.0 0.11.\* 0.1.2 0.5.0 diff --git a/RELEASES.md b/RELEASES.md index b5e351e92..06a20f037 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,67 @@ +Version 0.14.0 (2020-11-28) +=========================== + +New features +------------ + +- `Zip::apply_collect` and `Zip::par_apply_collect` now supports general + elements (not just `Copy`) + https://github.com/rust-ndarray/ndarray/pull/817 +- New function `stack` (new behaviour!) by [@andrei-papou] + +Enhancements +------------ + +- Handle inhomogenous shape inputs better in Zip, in practice, guess better whether + to prefer c- or f-order for the inner loop by [@bluss] + https://github.com/rust-ndarray/ndarray/pull/809 + +API changes +----------- + +- The **old function** `stack` has been renamed to `concatenate`. + A new function `stack` with numpy-like semantics have taken its place. + Old usages of `stack` should change to use `concatenate`. + + `concatenate` produces an array with the same number of axes as the inputs. + `stack` produces an array that has one more axis than the inputs. + + This change was unfortunately done without a deprecation period, due to the long period between releases. + +- Enum ErrorKind is now properly non-exhaustive and has lost its old placeholder invalid variant. By [@Zuse64] + https://github.com/rust-ndarray/ndarray/pull/848 + +- Remove deprecated items: + + - RcArray (deprecated alias for ArcArray) + - Removed `subview_inplace` use `collapse_axis` + - Removed `subview_mut` use `index_axis_mut` + - Removed `into_subview` use `index_axis_move` + - Removed `subview` use `index_axis` + - Removed `slice_inplace` use `slice_collapse` + - Undeprecate `remove_axis` because its replacement is hard to find out on your own. + +- Update public external dependencies to new versions by [@Eijebong] and [@bluss] + + - num-complex 0.3 + - approx 0.4 (optional) + - blas-src 0.6.1 and openblas-src 0.9.0 (optional) + + https://github.com/rust-ndarray/ndarray/pull/810 + https://github.com/rust-ndarray/ndarray/pull/851 + + +Other changes +------------- + +- Minor doc fixes by [@acj] + https://github.com/rust-ndarray/ndarray/pull/834 + +- Minor doc fixes by [@xd009642] + https://github.com/rust-ndarray/ndarray/pull/847 + +- The minimum required rust version is Rust 1.42. + Version 0.13.1 (2020-04-21) =========================== @@ -942,7 +1006,6 @@ Earlier releases [@max-sixty]: https://github.com/max-sixty [@JP-Ellis]: https://github.com/JP-Ellis [@sebasv]: https://github.com/sebasv -[@andrei-papou]: https://github.com/sebasv [@mneumann]: https://github.com/mneumann [@termoshtt]: https://github.com/termoshtt [@rth]: https://github.com/rth @@ -953,3 +1016,7 @@ Earlier releases [@mockersf]: https://github.com/mockersf [@viniciusd]: https://github.com/viniciusd [@lifuyang]: https://github.com/liufuyang +[@acj]: https://github.com/acj +[@Eijebong]: https://github.com/Eijebong +[@andrei-papou]: https://github.com/andrei-papou +[@xd009642]: https://github.com/xd009642 diff --git a/ndarray-rand/Cargo.toml b/ndarray-rand/Cargo.toml index aa4715fc4..755ab9cca 100644 --- a/ndarray-rand/Cargo.toml +++ b/ndarray-rand/Cargo.toml @@ -14,7 +14,7 @@ description = "Constructors for randomized arrays. `rand` integration for `ndarr keywords = ["multidimensional", "matrix", "rand", "ndarray"] [dependencies] -ndarray = { version = "0.13", path = ".." } +ndarray = { version = "0.14", path = ".." } rand_distr = "0.2.1" quickcheck = { version = "0.9", default-features = false, optional = true } diff --git a/src/lib.rs b/src/lib.rs index 8d5f0e034..7e79b4cbf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. #![crate_name = "ndarray"] -#![doc(html_root_url = "https://docs.rs/ndarray/0.13/")] +#![doc(html_root_url = "https://docs.rs/ndarray/0.14/")] #![allow( clippy::many_single_char_names, clippy::deref_addrof, From 26d1bc0f573e261350536db0f4707487fe4c1e71 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 29 Nov 2020 16:04:13 +0100 Subject: [PATCH 103/651] ndarray-rand 0.12.0 --- ndarray-rand/Cargo.toml | 4 ++-- ndarray-rand/RELEASES.md | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ndarray-rand/Cargo.toml b/ndarray-rand/Cargo.toml index 755ab9cca..5c0352758 100644 --- a/ndarray-rand/Cargo.toml +++ b/ndarray-rand/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ndarray-rand" -version = "0.11.0" +version = "0.12.0" edition = "2018" authors = ["bluss"] license = "MIT/Apache-2.0" @@ -15,7 +15,7 @@ keywords = ["multidimensional", "matrix", "rand", "ndarray"] [dependencies] ndarray = { version = "0.14", path = ".." } -rand_distr = "0.2.1" +rand_distr = "0.3.0" quickcheck = { version = "0.9", default-features = false, optional = true } [dependencies.rand] diff --git a/ndarray-rand/RELEASES.md b/ndarray-rand/RELEASES.md index feea2dbce..ad0730608 100644 --- a/ndarray-rand/RELEASES.md +++ b/ndarray-rand/RELEASES.md @@ -1,6 +1,12 @@ Recent Changes -------------- +- 0.12.0 + + - Require ndarray 0.14 + - Require rand 0.7 (unchanged from previous version) + - Require rand_distr 0.3 + - 0.11.0 - Require ndarray 0.13 From 282475d7790daafb859a0da29eecb976b9f78a0e Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 29 Nov 2020 16:10:13 +0100 Subject: [PATCH 104/651] FIX version of rand_distr in numeric-tests --- numeric-tests/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/numeric-tests/Cargo.toml b/numeric-tests/Cargo.toml index d6549eed4..bcf98df9f 100644 --- a/numeric-tests/Cargo.toml +++ b/numeric-tests/Cargo.toml @@ -8,7 +8,7 @@ publish = false approx = "0.4" ndarray = { path = "..", features = ["approx"] } ndarray-rand = { path = "../ndarray-rand/" } -rand_distr = "0.2.1" +rand_distr = "0.3" [dependencies.rand] version = "0.7.0" From f40e71240e3cf6f8b30b4e684002dd942297c89e Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 29 Nov 2020 16:10:33 +0100 Subject: [PATCH 105/651] DOC: Fix changelog link in ndarray-rand readme --- ndarray-rand/README.md | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/ndarray-rand/README.md b/ndarray-rand/README.md index e993440df..3e32d318c 100644 --- a/ndarray-rand/README.md +++ b/ndarray-rand/README.md @@ -28,7 +28,7 @@ Dependencies ``ndarray-rand`` depends on ``rand`` 0.7. -[`rand`](https://docs.rs/rand/0.7.0/rand/) and [`rand-distr`](https://docs.rs/rand_distr/0.2.1/rand_distr/) are +[`rand`](https://docs.rs/rand/0.7.0/rand/) and [`rand-distr`](https://docs.rs/rand_distr/0.3/) are re-exported as sub-modules, `ndarray_rand::rand` and `ndarray_rand::rand_distr` respectively. Please rely on these submodules for guaranteed version compatibility. @@ -41,15 +41,7 @@ necessary trait). Recent changes ============== -0.10.0 ------- - - - Require `rand` 0.7 - - Require Rust 1.32 or later - - Re-export `rand` as a submodule, `ndarray_rand::rand` - - Re-export `rand-distr` as a submodule, `ndarray_rand::rand_distr` - -Check _[Changelogs](https://github.com/rust-ndarray/ndarray/ndarray-rand/RELEASES.md)_ to see +Check _[RELEASES.md](https://github.com/rust-ndarray/ndarray/blob/master/ndarray-rand/RELEASES.md)_ to see the changes introduced in previous releases. From 03b051775bc3d8031901ffe8e09cb891e8dac8a7 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 29 Nov 2020 16:11:44 +0100 Subject: [PATCH 106/651] DOC: Fix rand_distr link version in ndarray-rand 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 63cf1c397..5e0a01fd3 100644 --- a/ndarray-rand/src/lib.rs +++ b/ndarray-rand/src/lib.rs @@ -21,7 +21,7 @@ //! convenience. //! //! [rand]: https://docs.rs/rand/0.7 -//! [rand_distr]: https://docs.rs/rand_distr/0.2 +//! [rand_distr]: https://docs.rs/rand_distr/0.3 //! //! If you want to use a random number generator or distribution from another crate //! with `ndarray-rand`, you need to make sure that the other crate also depends on the @@ -44,7 +44,7 @@ pub mod rand { pub use rand::*; } -/// [`rand-distr`](https://docs.rs/rand_distr/0.2), re-exported for convenience and version-compatibility. +/// [`rand-distr`](https://docs.rs/rand_distr/0.3), re-exported for convenience and version-compatibility. pub mod rand_distr { pub use rand_distr::*; } From a5448f29e399b972518354d6f703981b5d19578c Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 30 Nov 2020 17:08:54 +0100 Subject: [PATCH 107/651] DOC: Fix typo in remove_axis doc comment --- src/impl_methods.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 061be1fe2..dd0034b26 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1832,7 +1832,7 @@ where /// Remove array axis `axis` and return the result. /// - /// This is equivalent to `.index-axis_move(axis, 0)` and makes most sense to use if the + /// This is equivalent to `.index_axis_move(axis, 0)` and makes most sense to use if the /// axis to remove is of length 1. /// /// **Panics** if the axis is out of bounds or its length is zero. From 224358e569069f2aa11b3f75371cf178073b1c21 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 30 Nov 2020 17:08:54 +0100 Subject: [PATCH 108/651] DOC: Fix some links, language issues and add details in release note for 0.14 --- RELEASES.md | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 06a20f037..95d821715 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4,18 +4,25 @@ Version 0.14.0 (2020-11-28) New features ------------ -- `Zip::apply_collect` and `Zip::par_apply_collect` now supports general - elements (not just `Copy`) +- `Zip::apply_collect` and `Zip::par_apply_collect` now support all + elements (not just `Copy` elements) by [@bluss] + https://github.com/rust-ndarray/ndarray/pull/814 https://github.com/rust-ndarray/ndarray/pull/817 -- New function `stack` (new behaviour!) by [@andrei-papou] + +- New function `stack` by [@andrei-papou] + https://github.com/rust-ndarray/ndarray/pull/844 + https://github.com/rust-ndarray/ndarray/pull/850 Enhancements ------------ -- Handle inhomogenous shape inputs better in Zip, in practice, guess better whether +- Handle inhomogenous shape inputs better in Zip, in practice: guess better whether to prefer c- or f-order for the inner loop by [@bluss] https://github.com/rust-ndarray/ndarray/pull/809 +- Improve code sharing in some commonly used code by [@bluss] + https://github.com/rust-ndarray/ndarray/pull/819 + API changes ----------- @@ -28,6 +35,9 @@ API changes This change was unfortunately done without a deprecation period, due to the long period between releases. + https://github.com/rust-ndarray/ndarray/pull/844 + https://github.com/rust-ndarray/ndarray/pull/850 + - Enum ErrorKind is now properly non-exhaustive and has lost its old placeholder invalid variant. By [@Zuse64] https://github.com/rust-ndarray/ndarray/pull/848 @@ -39,7 +49,8 @@ API changes - Removed `into_subview` use `index_axis_move` - Removed `subview` use `index_axis` - Removed `slice_inplace` use `slice_collapse` - - Undeprecate `remove_axis` because its replacement is hard to find out on your own. + +- Undeprecated `remove_axis` because its replacement is hard to find out on your own. - Update public external dependencies to new versions by [@Eijebong] and [@bluss] @@ -62,6 +73,8 @@ Other changes - The minimum required rust version is Rust 1.42. +- Release management by [@bluss] + Version 0.13.1 (2020-04-21) =========================== @@ -1020,3 +1033,4 @@ Earlier releases [@Eijebong]: https://github.com/Eijebong [@andrei-papou]: https://github.com/andrei-papou [@xd009642]: https://github.com/xd009642 +[@Zuse64]: https://github.com/Zuse64 From 7f7eb249c56c5babeb4745f4eaa381ba33c3015f Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 30 Nov 2020 23:14:26 +0100 Subject: [PATCH 109/651] MAINT: Try adding github action for CI (draft) --- .github/workflows/ci.yml | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..36ee2d3c2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,38 @@ +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + + +name: Continuous integration + + +env: + CARGO_TERM_COLOR: always + HOST: x86_64-unknown-linux-gnu + FEATURES: "test docs" + +jobs: + ci: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - stable + - beta + - nightly + - 1.42.0 # MSRV + + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + override: true + components: rustfmt, clippy + - name: Install openblas + run: sudo apt-get install libopenblas-dev gfortran + - run: ./scripts/all-tests.sh "$FEATURES" ${{ matrix.rust }} + From 5e83e34899556100489b31cb5e93fbac9acf161f Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 30 Nov 2020 23:53:23 +0100 Subject: [PATCH 110/651] MAINT: Make clippy its own gh actions job --- .github/workflows/ci.yml | 21 +++++++++++++++++---- scripts/all-tests.sh | 1 - 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36ee2d3c2..91f03b705 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,17 +4,15 @@ on: pull_request: branches: [ master ] - name: Continuous integration - env: CARGO_TERM_COLOR: always HOST: x86_64-unknown-linux-gnu FEATURES: "test docs" jobs: - ci: + tests: runs-on: ubuntu-latest strategy: matrix: @@ -31,8 +29,23 @@ jobs: profile: minimal toolchain: ${{ matrix.rust }} override: true - components: rustfmt, clippy - name: Install openblas run: sudo apt-get install libopenblas-dev gfortran - run: ./scripts/all-tests.sh "$FEATURES" ${{ matrix.rust }} + clippy: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - beta + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + override: true + components: clippy + - run: cargo clippy + diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index 091c38f89..22f1d1f94 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -17,5 +17,4 @@ cargo test --manifest-path=ndarray-rand/Cargo.toml --features quickcheck --verbo cargo test --manifest-path=serialization-tests/Cargo.toml --verbose cargo test --manifest-path=blas-tests/Cargo.toml --verbose CARGO_TARGET_DIR=target/ cargo test --manifest-path=numeric-tests/Cargo.toml --verbose -([ "$CHANNEL" != "beta" ] || (rustup component add clippy && cargo clippy)) ([ "$CHANNEL" != "nightly" ] || cargo bench --no-run --verbose --features "$FEATURES") From 986c28761124ba304e7e4060a916b76abac5c11c Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 1 Dec 2020 00:00:21 +0100 Subject: [PATCH 111/651] MAINT: remove .travis.yml because of migration to gh actions --- .travis.yml | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f1dcd036a..000000000 --- a/.travis.yml +++ /dev/null @@ -1,34 +0,0 @@ -language: rust -# use trusty for newer openblas -sudo: required -dist: trusty -matrix: - include: - - rust: 1.42.0 - env: - - FEATURES='test docs' - - RUSTFLAGS='-D warnings' - - rust: stable - env: - - FEATURES='test docs' - - RUSTFLAGS='-D warnings' - - rust: beta - env: - - FEATURES='test docs' - - CHANNEL='beta' - - RUSTFLAGS='-D warnings' - - rust: nightly - env: - - FEATURES='test docs' - - CHANNEL='nightly' -env: - global: - - HOST=x86_64-unknown-linux-gnu - - CARGO_INCREMENTAL=0 -addons: - apt: - packages: - - libopenblas-dev - - gfortran -script: - - ./scripts/all-tests.sh "$FEATURES" "$CHANNEL" From 8df954a5f8cdb09f80a69db1ca7e0de581cb8c59 Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 1 Dec 2020 00:06:03 +0100 Subject: [PATCH 112/651] DOC: Update readme with gh actions badge --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index ded336b61..df416919e 100644 --- a/README.rst +++ b/README.rst @@ -11,8 +11,8 @@ __ https://docs.rs/ndarray/ |build_status|_ |crates|_ -.. |build_status| image:: https://api.travis-ci.org/rust-ndarray/ndarray.svg?branch=master -.. _build_status: https://travis-ci.org/rust-ndarray/ndarray +.. |build_status| image:: https://github.com/rust-ndarray/ndarray/workflows/Continuous%20integration/badge.svg?branch=master +.. _build_status: https://github.com/rust-ndarray/ndarray/actions .. |crates| image:: http://meritbadge.herokuapp.com/ndarray .. _crates: https://crates.io/crates/ndarray From ddfc6fd5e37be02faccf93140b1b93b04410fef0 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 3 Dec 2020 18:52:56 +0100 Subject: [PATCH 113/651] MAINT: Add cross testing of 32-bit architectures in CI --- .github/workflows/ci.yml | 27 +++++++++++++++++++++++++++ scripts/cross-tests.sh | 14 ++++++++++++++ 2 files changed, 41 insertions(+) create mode 100755 scripts/cross-tests.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91f03b705..42956f550 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,33 @@ jobs: run: sudo apt-get install libopenblas-dev gfortran - run: ./scripts/all-tests.sh "$FEATURES" ${{ matrix.rust }} + cross_test: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - rust: stable + target: mips-unknown-linux-gnu + - rust: stable + target: i686-unknown-linux-gnu + + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + taret: ${{ matrix.target }} + override: true + - name: Cache cargo plugins + uses: actions/cache@v1 + with: + path: ~/.cargo/bin/ + key: ${{ runner.os }}-cargo-plugins + - name: Install cross + run: cargo install cross || true + - run: ./scripts/cross-tests.sh "docs" ${{ matrix.rust }} ${{ matrix.target }} + clippy: runs-on: ubuntu-latest strategy: diff --git a/scripts/cross-tests.sh b/scripts/cross-tests.sh new file mode 100755 index 000000000..d9bc8347e --- /dev/null +++ b/scripts/cross-tests.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +set -x +set -e + +FEATURES=$1 +CHANNEL=$2 +TARGET=$3 + +cross build -v --features="$FEATURES" --target=$TARGET +cross test -v --no-fail-fast --features="$FEATURES" --target=$TARGET +cross test -v --no-fail-fast --target=$TARGET --manifest-path=ndarray-rand/Cargo.toml --features quickcheck +cross test -v --no-fail-fast --target=$TARGET --manifest-path=serialization-tests/Cargo.toml --verbose +CARGO_TARGET_DIR=target/ cross test -v --no-fail-fast --target=$TARGET --manifest-path=numeric-tests/Cargo.toml From 4549b00e8d1efc0fbf9286cd03fb8e657b53580b Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 3 Dec 2020 20:06:48 +0100 Subject: [PATCH 114/651] MAINT: Fix misspelling in workflow file --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 42956f550..86aa8b727 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: with: profile: minimal toolchain: ${{ matrix.rust }} - taret: ${{ matrix.target }} + target: ${{ matrix.target }} override: true - name: Cache cargo plugins uses: actions/cache@v1 From c195930da36611be12a51f59eb75575f279f17b9 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 2 Dec 2020 22:24:38 +0100 Subject: [PATCH 115/651] FIX: Error-check array shape before computing strides Update StrideShape's representation to use enum { C, F, Custom(D) } so that we don't try to compute a C or F stride set until after we have checked the actual array shape. This avoids overflow errors (that panic) in places where we expected to return an error. It was visible as a test failure on 32-bit that long went undetected because tests didn't run on such platforms before (but the bug affected all platforms, given sufficiently large inputs). Also move the Shape, StrideShape types into the shape builder module. It all calls out for a nicer organization of types that makes constructors easier to understand for users, but that's an issue for another time - and it's a breaking change. --- src/dimension/mod.rs | 23 ++++++-- src/impl_constructors.rs | 19 ++++--- src/impl_raw_views.rs | 19 +++++-- src/impl_views/constructors.rs | 16 ++---- src/lib.rs | 20 +------ src/shape_builder.rs | 99 ++++++++++++++++++++++++---------- tests/array-construct.rs | 1 + 7 files changed, 121 insertions(+), 76 deletions(-) diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 212197fed..9ee1f44d6 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -19,6 +19,8 @@ pub use self::dynindeximpl::IxDynImpl; pub use self::ndindex::NdIndex; pub use self::remove_axis::RemoveAxis; +use crate::shape_builder::Strides; + use std::isize; use std::mem; @@ -114,11 +116,24 @@ pub fn size_of_shape_checked(dim: &D) -> Result /// conditions 1 and 2 are sufficient to guarantee that the offset in units of /// `A` and in units of bytes between the least address and greatest address /// accessible by moving along all axes does not exceed `isize::MAX`. -pub fn can_index_slice_not_custom(data: &[A], dim: &D) -> Result<(), ShapeError> { +pub(crate) fn can_index_slice_with_strides(data: &[A], dim: &D, + strides: &Strides) + -> Result<(), ShapeError> +{ + if let Strides::Custom(strides) = strides { + can_index_slice(data, dim, strides) + } else { + can_index_slice_not_custom(data.len(), dim) + } +} + +pub(crate) fn can_index_slice_not_custom(data_len: usize, dim: &D) + -> Result<(), ShapeError> +{ // Condition 1. let len = size_of_shape_checked(dim)?; // Condition 2. - if len > data.len() { + if len > data_len { return Err(from_kind(ErrorKind::OutOfBounds)); } Ok(()) @@ -217,7 +232,7 @@ where /// condition 4 is sufficient to guarantee that the absolute difference in /// units of `A` and in units of bytes between the least address and greatest /// address accessible by moving along all axes does not exceed `isize::MAX`. -pub fn can_index_slice( +pub(crate) fn can_index_slice( data: &[A], dim: &D, strides: &D, @@ -771,7 +786,7 @@ mod test { quickcheck! { fn can_index_slice_not_custom_same_as_can_index_slice(data: Vec, dim: Vec) -> bool { let dim = IxDyn(&dim); - let result = can_index_slice_not_custom(&data, &dim); + let result = can_index_slice_not_custom(data.len(), &dim); if dim.size_checked().is_none() { // Avoid overflow `dim.default_strides()` or `dim.fortran_strides()`. result.is_err() diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 8d1471e78..4353a827e 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -358,7 +358,7 @@ where { let shape = shape.into_shape(); let _ = size_of_shape_checked_unwrap!(&shape.dim); - if shape.is_c { + if shape.is_c() { let v = to_vec_mapped(indices(shape.dim.clone()).into_iter(), f); unsafe { Self::from_shape_vec_unchecked(shape, v) } } else { @@ -411,15 +411,12 @@ where fn from_shape_vec_impl(shape: StrideShape, v: Vec
) -> Result { let dim = shape.dim; - let strides = shape.strides; - if shape.custom { - dimension::can_index_slice(&v, &dim, &strides)?; - } else { - dimension::can_index_slice_not_custom::(&v, &dim)?; - if dim.size() != v.len() { - return Err(error::incompatible_shapes(&Ix1(v.len()), &dim)); - } + let is_custom = shape.strides.is_custom(); + dimension::can_index_slice_with_strides(&v, &dim, &shape.strides)?; + if !is_custom && dim.size() != v.len() { + return Err(error::incompatible_shapes(&Ix1(v.len()), &dim)); } + let strides = shape.strides.strides_for_dim(&dim); unsafe { Ok(Self::from_vec_dim_stride_unchecked(dim, strides, v)) } } @@ -451,7 +448,9 @@ where Sh: Into>, { let shape = shape.into(); - Self::from_vec_dim_stride_unchecked(shape.dim, shape.strides, v) + let dim = shape.dim; + let strides = shape.strides.strides_for_dim(&dim); + Self::from_vec_dim_stride_unchecked(dim, strides, v) } unsafe fn from_vec_dim_stride_unchecked(dim: D, strides: D, mut v: Vec) -> Self { diff --git a/src/impl_raw_views.rs b/src/impl_raw_views.rs index 22e76277a..8377dc93c 100644 --- a/src/impl_raw_views.rs +++ b/src/impl_raw_views.rs @@ -4,7 +4,8 @@ use std::ptr::NonNull; use crate::dimension::{self, stride_offset}; use crate::extension::nonnull::nonnull_debug_checked_from_ptr; use crate::imp_prelude::*; -use crate::{is_aligned, StrideShape}; +use crate::is_aligned; +use crate::shape_builder::{Strides, StrideShape}; impl RawArrayView where @@ -69,11 +70,15 @@ where { let shape = shape.into(); let dim = shape.dim; - let strides = shape.strides; if cfg!(debug_assertions) { assert!(!ptr.is_null(), "The pointer must be non-null."); - dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); + if let Strides::Custom(strides) = &shape.strides { + dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); + } else { + dimension::size_of_shape_checked(&dim).unwrap(); + } } + let strides = shape.strides.strides_for_dim(&dim); RawArrayView::new_(ptr, dim, strides) } @@ -205,11 +210,15 @@ where { let shape = shape.into(); let dim = shape.dim; - let strides = shape.strides; if cfg!(debug_assertions) { assert!(!ptr.is_null(), "The pointer must be non-null."); - dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); + if let Strides::Custom(strides) = &shape.strides { + dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); + } else { + dimension::size_of_shape_checked(&dim).unwrap(); + } } + let strides = shape.strides.strides_for_dim(&dim); RawArrayViewMut::new_(ptr, dim, strides) } diff --git a/src/impl_views/constructors.rs b/src/impl_views/constructors.rs index e2244dd09..bc1602af4 100644 --- a/src/impl_views/constructors.rs +++ b/src/impl_views/constructors.rs @@ -53,12 +53,8 @@ where fn from_shape_impl(shape: StrideShape, xs: &'a [A]) -> Result { let dim = shape.dim; - let strides = shape.strides; - if shape.custom { - dimension::can_index_slice(xs, &dim, &strides)?; - } else { - dimension::can_index_slice_not_custom::(xs, &dim)?; - } + dimension::can_index_slice_with_strides(xs, &dim, &shape.strides)?; + let strides = shape.strides.strides_for_dim(&dim); unsafe { Ok(Self::new_(xs.as_ptr(), dim, strides)) } } @@ -149,12 +145,8 @@ where fn from_shape_impl(shape: StrideShape, xs: &'a mut [A]) -> Result { let dim = shape.dim; - let strides = shape.strides; - if shape.custom { - dimension::can_index_slice(xs, &dim, &strides)?; - } else { - dimension::can_index_slice_not_custom::(xs, &dim)?; - } + dimension::can_index_slice_with_strides(xs, &dim, &shape.strides)?; + let strides = shape.strides.strides_for_dim(&dim); unsafe { Ok(Self::new_(xs.as_mut_ptr(), dim, strides)) } } diff --git a/src/lib.rs b/src/lib.rs index 7e79b4cbf..f394413db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,7 +138,7 @@ pub use crate::linalg_traits::{LinalgScalar, NdFloat}; pub use crate::stacking::{concatenate, stack, stack_new_axis}; pub use crate::impl_views::IndexLonger; -pub use crate::shape_builder::ShapeBuilder; +pub use crate::shape_builder::{Shape, StrideShape, ShapeBuilder}; #[macro_use] mod macro_utils; @@ -1595,24 +1595,8 @@ mod impl_raw_views; // Copy-on-write array methods mod impl_cow; -/// A contiguous array shape of n dimensions. -/// -/// Either c- or f- memory ordered (*c* a.k.a *row major* is the default). -#[derive(Copy, Clone, Debug)] -pub struct Shape { - dim: D, - is_c: bool, -} - -/// An array shape of n dimensions in c-order, f-order or custom strides. -#[derive(Copy, Clone, Debug)] -pub struct StrideShape { - dim: D, - strides: D, - custom: bool, -} - /// Returns `true` if the pointer is aligned. pub(crate) fn is_aligned(ptr: *const T) -> bool { (ptr as usize) % ::std::mem::align_of::() == 0 } + diff --git a/src/shape_builder.rs b/src/shape_builder.rs index bb5a949ab..6fc99d0b2 100644 --- a/src/shape_builder.rs +++ b/src/shape_builder.rs @@ -1,6 +1,66 @@ use crate::dimension::IntoDimension; use crate::Dimension; -use crate::{Shape, StrideShape}; + +/// A contiguous array shape of n dimensions. +/// +/// Either c- or f- memory ordered (*c* a.k.a *row major* is the default). +#[derive(Copy, Clone, Debug)] +pub struct Shape { + /// Shape (axis lengths) + pub(crate) dim: D, + /// Strides can only be C or F here + pub(crate) strides: Strides, +} + +#[derive(Copy, Clone, Debug)] +pub(crate) enum Contiguous { } + +impl Shape { + pub(crate) fn is_c(&self) -> bool { + matches!(self.strides, Strides::C) + } +} + + +/// An array shape of n dimensions in c-order, f-order or custom strides. +#[derive(Copy, Clone, Debug)] +pub struct StrideShape { + pub(crate) dim: D, + pub(crate) strides: Strides, +} + +/// Stride description +#[derive(Copy, Clone, Debug)] +pub(crate) enum Strides { + /// Row-major ("C"-order) + C, + /// Column-major ("F"-order) + F, + /// Custom strides + Custom(D) +} + +impl Strides { + /// Return strides for `dim` (computed from dimension if c/f, else return the custom stride) + pub(crate) fn strides_for_dim(self, dim: &D) -> D + where D: Dimension + { + match self { + Strides::C => dim.default_strides(), + Strides::F => dim.fortran_strides(), + Strides::Custom(c) => { + debug_assert_eq!(c.ndim(), dim.ndim(), + "Custom strides given with {} dimensions, expected {}", + c.ndim(), dim.ndim()); + c + } + } + } + + pub(crate) fn is_custom(&self) -> bool { + matches!(*self, Strides::Custom(_)) + } +} /// A trait for `Shape` and `D where D: Dimension` that allows /// customizing the memory layout (strides) of an array shape. @@ -34,36 +94,18 @@ where { fn from(value: T) -> Self { let shape = value.into_shape(); - let d = shape.dim; - let st = if shape.is_c { - d.default_strides() + let st = if shape.is_c() { + Strides::C } else { - d.fortran_strides() + Strides::F }; StrideShape { strides: st, - dim: d, - custom: false, + dim: shape.dim, } } } -/* -impl From> for StrideShape - where D: Dimension -{ - fn from(shape: Shape) -> Self { - let d = shape.dim; - let st = if shape.is_c { d.default_strides() } else { d.fortran_strides() }; - StrideShape { - strides: st, - dim: d, - custom: false, - } - } -} -*/ - impl ShapeBuilder for T where T: IntoDimension, @@ -73,7 +115,7 @@ where fn into_shape(self) -> Shape { Shape { dim: self.into_dimension(), - is_c: true, + strides: Strides::C, } } fn f(self) -> Shape { @@ -93,21 +135,24 @@ where { type Dim = D; type Strides = D; + fn into_shape(self) -> Shape { self } + fn f(self) -> Self { self.set_f(true) } + fn set_f(mut self, is_f: bool) -> Self { - self.is_c = !is_f; + self.strides = if !is_f { Strides::C } else { Strides::F }; self } + fn strides(self, st: D) -> StrideShape { StrideShape { dim: self.dim, - strides: st, - custom: true, + strides: Strides::Custom(st), } } } diff --git a/tests/array-construct.rs b/tests/array-construct.rs index 97e4ef491..738e3b1fc 100644 --- a/tests/array-construct.rs +++ b/tests/array-construct.rs @@ -148,6 +148,7 @@ fn test_from_fn_f3() { fn deny_wraparound_from_vec() { let five = vec![0; 5]; let five_large = Array::from_shape_vec((3, 7, 29, 36760123, 823996703), five.clone()); + println!("{:?}", five_large); assert!(five_large.is_err()); let six = Array::from_shape_vec(6, five.clone()); assert!(six.is_err()); From e12d11d4a410319c23c10dfedea2b91b61b80c1c Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 8 Dec 2020 19:36:34 -0500 Subject: [PATCH 116/651] Fix Zip::indexed for the 0-dimensional case This commit fixes a panic for 0-dimensional, indexed `Zip` instances which results from an out-of-bounds index in a call to `IndexPtr::stride_offset` in `Zip::inner`. Basically, the "stride" for `IndexPtr` is the axis to update, but for the 0-dimensional case, there are no axes, so `IndexPtr::stride_offset` cannot be called without panicking due to the `self.index[stride]` access. The chosen solution is to add a special check to `Zip::apply_core` for the 0-dimensional case. Another possible solution would be to modify the loop of `Zip::inner` such that an offset would not be performed for an index of zero. --- src/zip/mod.rs | 6 ++++-- tests/azip.rs | 13 +++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 333911c6b..ed92e2509 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -691,12 +691,14 @@ impl Zip where D: Dimension, { - fn apply_core(&mut self, acc: Acc, function: F) -> FoldWhile + fn apply_core(&mut self, acc: Acc, mut function: F) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, P: ZippableTuple, { - if self.layout.is(CORDER | FORDER) { + if self.dimension.ndim() == 0 { + function(acc, unsafe { self.parts.as_ref(self.parts.as_ptr()) }) + } else if self.layout.is(CORDER | FORDER) { self.apply_core_contiguous(acc, function) } else { self.apply_core_strided(acc, function) diff --git a/tests/azip.rs b/tests/azip.rs index 9027927ff..b553d7fb5 100644 --- a/tests/azip.rs +++ b/tests/azip.rs @@ -303,6 +303,19 @@ fn test_clone() { }); } +#[test] +fn test_indices_0() { + let a1 = arr0(3); + + let mut count = 0; + Zip::indexed(&a1).apply(|i, elt| { + count += 1; + assert_eq!(i, ()); + assert_eq!(*elt, 3); + }); + assert_eq!(count, 1); +} + #[test] fn test_indices_1() { let mut a1 = Array::default(12); From 1b4a1ec3835d68a91def8158eafb60a3e1a7664f Mon Sep 17 00:00:00 2001 From: xd009642 Date: Tue, 15 Dec 2020 09:50:01 +0400 Subject: [PATCH 117/651] Put NdFloat and num-traits/std behind std feature --- Cargo.toml | 5 ++++- src/lib.rs | 15 ++++++++------- src/linalg_traits.rs | 8 +++++++- src/prelude.rs | 6 +++++- tests/oper.rs | 15 +++++++-------- 5 files changed, 31 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4dcc8831d..b312fd4a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ test = true [dependencies] num-integer = "0.1.39" -num-traits = "0.2" +num-traits = { version = "0.2", default-features = false } num-complex = { version = "0.3", default-features = false } rayon = { version = "1.0.3", optional = true } @@ -51,6 +51,7 @@ approx = "0.4" itertools = { version = "0.9.0", default-features = false, features = ["use_std"] } [features] +default = ["std"] # Enable blas usage # See README for more instructions blas = ["cblas-sys", "blas-src"] @@ -65,6 +66,8 @@ test = ["test-blas-openblas-sys"] # This feature is used for docs docs = ["approx", "serde", "rayon"] +std = ["num-traits/std"] + [profile.release] [profile.bench] debug = true diff --git a/src/lib.rs b/src/lib.rs index f394413db..a99d6d52f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ #![allow( clippy::many_single_char_names, clippy::deref_addrof, - clippy::unreadable_literal, + clippy::unreadable_literal )] //! The `ndarray` crate provides an *n*-dimensional container for general elements @@ -106,7 +106,7 @@ //! //! If you are looking to generate random arrays instead, check out [`ndarray-rand`](https://crates.io/crates/ndarray-rand). //! -//! For conversion between `ndarray`, [`nalgebra`](https://crates.io/crates/nalgebra) and +//! For conversion between `ndarray`, [`nalgebra`](https://crates.io/crates/nalgebra) and //! [`image`](https://crates.io/crates/image) check out [`nshare`](https://crates.io/crates/nshare). #[cfg(feature = "blas")] @@ -133,12 +133,14 @@ use crate::iterators::Baseiter; use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes, LanesMut}; pub use crate::arraytraits::AsArray; -pub use crate::linalg_traits::{LinalgScalar, NdFloat}; +#[cfg(feature = "std")] +pub use crate::linalg_traits::NdFloat; +pub use crate::linalg_traits::LinalgScalar; pub use crate::stacking::{concatenate, stack, stack_new_axis}; pub use crate::impl_views::IndexLonger; -pub use crate::shape_builder::{Shape, StrideShape, ShapeBuilder}; +pub use crate::shape_builder::{Shape, ShapeBuilder, StrideShape}; #[macro_use] mod macro_utils; @@ -147,16 +149,16 @@ mod private; mod aliases; #[macro_use] mod itertools; +mod argument_traits; #[cfg(feature = "approx")] mod array_approx; #[cfg(feature = "serde")] mod array_serde; mod arrayformat; mod arraytraits; -mod argument_traits; pub use crate::argument_traits::AssignElem; -mod data_traits; mod data_repr; +mod data_traits; pub use crate::aliases::*; @@ -1599,4 +1601,3 @@ mod impl_cow; pub(crate) fn is_aligned(ptr: *const T) -> bool { (ptr as usize) % ::std::mem::align_of::() == 0 } - diff --git a/src/linalg_traits.rs b/src/linalg_traits.rs index a7f5a1a3e..43fe5f8d3 100644 --- a/src/linalg_traits.rs +++ b/src/linalg_traits.rs @@ -6,7 +6,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::ScalarOperand; -use num_traits::{Float, One, Zero}; +#[cfg(feature = "std")] +use num_traits::Float; +use num_traits::{One, Zero}; use std::fmt; use std::ops::{Add, Div, Mul, Sub}; use std::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign}; @@ -47,6 +49,7 @@ impl LinalgScalar for T where /// operations (`ScalarOperand`). /// /// This trait can only be implemented by `f32` and `f64`. +#[cfg(feature = "std")] pub trait NdFloat: Float + AddAssign @@ -65,5 +68,8 @@ pub trait NdFloat: { } +#[cfg(feature = "std")] impl NdFloat for f32 {} +#[cfg(feature = "std")] impl NdFloat for f64 {} + diff --git a/src/prelude.rs b/src/prelude.rs index 190f679a4..e25e52175 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -51,4 +51,8 @@ pub use crate::{array, azip, s}; pub use crate::ShapeBuilder; #[doc(no_inline)] -pub use crate::{AsArray, NdFloat}; +pub use crate::AsArray; + +#[doc(no_inline)] +#[cfg(feature = "std")] +pub use crate::NdFloat; diff --git a/tests/oper.rs b/tests/oper.rs index 89c45888c..7193a49da 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -10,6 +10,7 @@ use ndarray::prelude::*; use ndarray::{rcarr1, rcarr2}; use ndarray::{Data, LinalgScalar}; use ndarray::{Ix, Ixs}; +use num_traits::Zero; use std::iter::FromIterator; use approx::assert_abs_diff_eq; @@ -33,10 +34,9 @@ fn test_oper(op: &str, a: &[f32], b: &[f32], c: &[f32]) { test_oper_arr::(op, aa.clone(), bb.clone(), cc.clone()); } -fn test_oper_arr(op: &str, mut aa: ArcArray, bb: ArcArray, cc: ArcArray) + +fn test_oper_arr(op: &str, mut aa: ArcArray, bb: ArcArray, cc: ArcArray) where - A: NdFloat, - for<'a> &'a A: Neg, D: Dimension, { match op { @@ -147,17 +147,16 @@ fn scalar_operations() { } } -fn reference_dot<'a, A, V1, V2>(a: V1, b: V2) -> A +fn reference_dot<'a, V1, V2>(a: V1, b: V2) -> f32 where - A: NdFloat, - V1: AsArray<'a, A>, - V2: AsArray<'a, A>, + V1: AsArray<'a, f32>, + V2: AsArray<'a, f32>, { let a = a.into(); let b = b.into(); a.iter() .zip(b.iter()) - .fold(A::zero(), |acc, (&x, &y)| acc + x * y) + .fold(f32::zero(), |acc, (&x, &y)| acc + x * y) } #[test] From 37e4070cc399274f9b209c864ceeb97e27830ba5 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 21 Dec 2020 18:22:29 +0100 Subject: [PATCH 118/651] rand: Update ndarray-rand to rand 0.8 Also edit docs and links in docs. Remove some instances of versioned links (we can't have too many places where we need to update the version for each new version of rand). Update ndarray-rand quickcheck after rand update (quickcheck is still on rand 0.7, but the rest of the crate uses rand 0.8) Update numeric-tests to rand 0.8 --- ndarray-rand/Cargo.toml | 6 +++--- ndarray-rand/src/lib.rs | 15 +++++++------- numeric-tests/Cargo.toml | 4 ++-- numeric-tests/tests/accuracy.rs | 36 ++++++++++++++++----------------- 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/ndarray-rand/Cargo.toml b/ndarray-rand/Cargo.toml index 5c0352758..ee6fdaaec 100644 --- a/ndarray-rand/Cargo.toml +++ b/ndarray-rand/Cargo.toml @@ -15,15 +15,15 @@ keywords = ["multidimensional", "matrix", "rand", "ndarray"] [dependencies] ndarray = { version = "0.14", path = ".." } -rand_distr = "0.3.0" +rand_distr = "0.4.0" quickcheck = { version = "0.9", default-features = false, optional = true } [dependencies.rand] -version = "0.7.0" +version = "0.8.0" features = ["small_rng"] [dev-dependencies] -rand_isaac = "0.2.0" +rand_isaac = "0.3.0" quickcheck = { version = "0.9", default-features = false } [package.metadata.release] diff --git a/ndarray-rand/src/lib.rs b/ndarray-rand/src/lib.rs index 5e0a01fd3..ab32ae9f7 100644 --- a/ndarray-rand/src/lib.rs +++ b/ndarray-rand/src/lib.rs @@ -12,7 +12,7 @@ //! //! ## Note //! -//! `ndarray-rand` depends on [`rand` 0.7][rand]. +//! `ndarray-rand` depends on [`rand` 0.8][rand]. //! //! [`rand`][rand] and [`rand_distr`][rand_distr] //! are re-exported as sub-modules, [`ndarray_rand::rand`](rand/index.html) @@ -20,8 +20,8 @@ //! You can use these submodules for guaranteed version compatibility or //! convenience. //! -//! [rand]: https://docs.rs/rand/0.7 -//! [rand_distr]: https://docs.rs/rand_distr/0.3 +//! [rand]: https://docs.rs/rand/0.8 +//! [rand_distr]: https://docs.rs/rand_distr/0.4 //! //! If you want to use a random number generator or distribution from another crate //! with `ndarray-rand`, you need to make sure that the other crate also depends on the @@ -39,12 +39,12 @@ use ndarray::{ArrayBase, DataOwned, Dimension}; #[cfg(feature = "quickcheck")] use quickcheck::{Arbitrary, Gen}; -/// [`rand`](https://docs.rs/rand/0.7), re-exported for convenience and version-compatibility. +/// `rand`, re-exported for convenience and version-compatibility. pub mod rand { pub use rand::*; } -/// [`rand-distr`](https://docs.rs/rand_distr/0.3), re-exported for convenience and version-compatibility. +/// `rand-distr`, re-exported for convenience and version-compatibility. pub mod rand_distr { pub use rand_distr::*; } @@ -55,8 +55,7 @@ pub mod rand_distr { /// for other types. /// /// The default RNG is a fast automatically seeded rng (currently -/// [`rand::rngs::SmallRng`](https://docs.rs/rand/0.7/rand/rngs/struct.SmallRng.html) -/// seeded from [`rand::thread_rng`](https://docs.rs/rand/0.7/rand/fn.thread_rng.html)). +/// [`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 @@ -298,7 +297,7 @@ pub enum SamplingStrategy { #[cfg(feature = "quickcheck")] impl Arbitrary for SamplingStrategy { fn arbitrary(g: &mut G) -> Self { - if g.gen_bool(0.5) { + if bool::arbitrary(g) { SamplingStrategy::WithReplacement } else { SamplingStrategy::WithoutReplacement diff --git a/numeric-tests/Cargo.toml b/numeric-tests/Cargo.toml index bcf98df9f..fd017bd39 100644 --- a/numeric-tests/Cargo.toml +++ b/numeric-tests/Cargo.toml @@ -8,10 +8,10 @@ publish = false approx = "0.4" ndarray = { path = "..", features = ["approx"] } ndarray-rand = { path = "../ndarray-rand/" } -rand_distr = "0.3" +rand_distr = "0.4" [dependencies.rand] -version = "0.7.0" +version = "0.8.0" features = ["small_rng"] [lib] diff --git a/numeric-tests/tests/accuracy.rs b/numeric-tests/tests/accuracy.rs index 4f61248df..044ba17cc 100644 --- a/numeric-tests/tests/accuracy.rs +++ b/numeric-tests/tests/accuracy.rs @@ -76,8 +76,8 @@ fn accurate_eye_f32() { // pick a few random sizes let mut rng = SmallRng::from_entropy(); for _ in 0..10 { - let i = rng.gen_range(15, 512); - let j = rng.gen_range(15, 512); + let i = rng.gen_range(15..512); + let j = rng.gen_range(15..512); println!("Testing size {} by {}", i, j); let a = gen(Ix2(i, j)); let eye = Array::eye(i); @@ -104,8 +104,8 @@ fn accurate_eye_f64() { // pick a few random sizes let mut rng = SmallRng::from_entropy(); for _ in 0..10 { - let i = rng.gen_range(15, 512); - let j = rng.gen_range(15, 512); + let i = rng.gen_range(15..512); + let j = rng.gen_range(15..512); println!("Testing size {} by {}", i, j); let a = gen_f64(Ix2(i, j)); let eye = Array::eye(i); @@ -121,9 +121,9 @@ fn accurate_mul_f32() { // pick a few random sizes let mut rng = SmallRng::from_entropy(); for i in 0..20 { - let m = rng.gen_range(15, 512); - let k = rng.gen_range(15, 512); - let n = rng.gen_range(15, 1560); + let m = rng.gen_range(15..512); + let k = rng.gen_range(15..512); + let n = rng.gen_range(15..1560); let a = gen(Ix2(m, k)); let b = gen(Ix2(n, k)); let b = b.t(); @@ -145,9 +145,9 @@ fn accurate_mul_f32_general() { // pick a few random sizes let mut rng = SmallRng::from_entropy(); for i in 0..20 { - let m = rng.gen_range(15, 512); - let k = rng.gen_range(15, 512); - let n = rng.gen_range(15, 1560); + let m = rng.gen_range(15..512); + let k = rng.gen_range(15..512); + let n = rng.gen_range(15..1560); let a = gen(Ix2(m, k)); let b = gen(Ix2(n, k)); let mut c = gen(Ix2(m, n)); @@ -171,9 +171,9 @@ fn accurate_mul_f64() { // pick a few random sizes let mut rng = SmallRng::from_entropy(); for i in 0..20 { - let m = rng.gen_range(15, 512); - let k = rng.gen_range(15, 512); - let n = rng.gen_range(15, 1560); + let m = rng.gen_range(15..512); + let k = rng.gen_range(15..512); + let n = rng.gen_range(15..1560); let a = gen_f64(Ix2(m, k)); let b = gen_f64(Ix2(n, k)); let b = b.t(); @@ -195,9 +195,9 @@ fn accurate_mul_f64_general() { // pick a few random sizes let mut rng = SmallRng::from_entropy(); for i in 0..20 { - let m = rng.gen_range(15, 512); - let k = rng.gen_range(15, 512); - let n = rng.gen_range(15, 1560); + let m = rng.gen_range(15..512); + let k = rng.gen_range(15..512); + let n = rng.gen_range(15..1560); let a = gen_f64(Ix2(m, k)); let b = gen_f64(Ix2(n, k)); let mut c = gen_f64(Ix2(m, n)); @@ -221,8 +221,8 @@ fn accurate_mul_with_column_f64() { // pick a few random sizes let mut rng = SmallRng::from_entropy(); for i in 0..10 { - let m = rng.gen_range(1, 350); - let k = rng.gen_range(1, 350); + let m = rng.gen_range(1..350); + let k = rng.gen_range(1..350); let a = gen_f64(Ix2(m, k)); let b_owner = gen_f64(Ix2(k, k)); let b_row_col; From 2dfa40f6a6c499c68b10e7a00a042a614fac03ef Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 21 Dec 2020 19:47:49 +0100 Subject: [PATCH 119/651] FEAT: Let RandomExt methods apply to array views if possible (sample) The RandomExt methods for sampling were unintentionally restricted to owned arrays only (like the original random constructors). Now the methods which can also apply to array views. --- ndarray-rand/src/lib.rs | 14 +++++++++++--- ndarray-rand/tests/tests.rs | 7 +++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/ndarray-rand/src/lib.rs b/ndarray-rand/src/lib.rs index ab32ae9f7..7dc95ec5d 100644 --- a/ndarray-rand/src/lib.rs +++ b/ndarray-rand/src/lib.rs @@ -35,7 +35,7 @@ use crate::rand::seq::index; use crate::rand::{thread_rng, Rng, SeedableRng}; use ndarray::{Array, Axis, RemoveAxis, ShapeBuilder}; -use ndarray::{ArrayBase, DataOwned, Dimension}; +use ndarray::{ArrayBase, DataOwned, RawData, Data, Dimension}; #[cfg(feature = "quickcheck")] use quickcheck::{Arbitrary, Gen}; @@ -63,7 +63,7 @@ pub mod rand_distr { /// [`.random_using()`](#tymethod.random_using). pub trait RandomExt where - S: DataOwned, + S: RawData, D: Dimension, { /// Create an array with shape `dim` with elements drawn from @@ -87,6 +87,7 @@ where fn random(shape: Sh, distribution: IdS) -> ArrayBase where IdS: Distribution, + S: DataOwned, Sh: ShapeBuilder; /// Create an array with shape `dim` with elements drawn from @@ -117,6 +118,7 @@ where where IdS: Distribution, R: Rng + ?Sized, + S: DataOwned, Sh: ShapeBuilder; /// Sample `n_samples` lanes slicing along `axis` using the default RNG. @@ -163,6 +165,7 @@ 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`. @@ -223,17 +226,19 @@ where where R: Rng + ?Sized, A: Copy, + S: Data, D: RemoveAxis; } impl RandomExt for ArrayBase where - S: DataOwned, + S: RawData, D: Dimension, { fn random(shape: Sh, dist: IdS) -> ArrayBase where IdS: Distribution, + S: DataOwned, Sh: ShapeBuilder, { Self::random_using(shape, dist, &mut get_rng()) @@ -243,6 +248,7 @@ where where IdS: Distribution, R: Rng + ?Sized, + S: DataOwned, Sh: ShapeBuilder, { Self::from_shape_simple_fn(shape, move || dist.sample(rng)) @@ -251,6 +257,7 @@ where fn sample_axis(&self, axis: Axis, n_samples: usize, strategy: SamplingStrategy) -> Array where A: Copy, + S: Data, D: RemoveAxis, { self.sample_axis_using(axis, n_samples, strategy, &mut get_rng()) @@ -266,6 +273,7 @@ where where R: Rng + ?Sized, A: Copy, + S: Data, D: RemoveAxis, { let indices: Vec<_> = match strategy { diff --git a/ndarray-rand/tests/tests.rs b/ndarray-rand/tests/tests.rs index f7860ac12..d8b18fe56 100644 --- a/ndarray-rand/tests/tests.rs +++ b/ndarray-rand/tests/tests.rs @@ -35,6 +35,13 @@ fn test_dim_f() { } } +#[test] +fn sample_axis_on_view() { + let m = 5; + let a = Array::random((m, 4), Uniform::new(0., 2.)); + let _samples = a.view().sample_axis(Axis(0), m, SamplingStrategy::WithoutReplacement); +} + #[test] #[should_panic] fn oversampling_without_replacement_should_panic() { From 72eb592895c0b9688ef7d6af32dd3677bed15900 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 21 Dec 2020 19:51:10 +0100 Subject: [PATCH 120/651] ndarray-rand 0.13.0 --- ndarray-rand/Cargo.toml | 4 ++-- ndarray-rand/README.md | 4 ++-- ndarray-rand/RELEASES.md | 7 +++++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ndarray-rand/Cargo.toml b/ndarray-rand/Cargo.toml index ee6fdaaec..a02b346aa 100644 --- a/ndarray-rand/Cargo.toml +++ b/ndarray-rand/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "ndarray-rand" -version = "0.12.0" +version = "0.13.0" edition = "2018" authors = ["bluss"] -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" repository = "https://github.com/rust-ndarray/ndarray" documentation = "https://docs.rs/ndarray-rand/" diff --git a/ndarray-rand/README.md b/ndarray-rand/README.md index 3e32d318c..0109e9732 100644 --- a/ndarray-rand/README.md +++ b/ndarray-rand/README.md @@ -26,9 +26,9 @@ fn main() { Dependencies ============ -``ndarray-rand`` depends on ``rand`` 0.7. +``ndarray-rand`` depends on ``rand``. -[`rand`](https://docs.rs/rand/0.7.0/rand/) and [`rand-distr`](https://docs.rs/rand_distr/0.3/) are +[`rand`](https://docs.rs/rand/) and [`rand-distr`](https://docs.rs/rand_distr/) are re-exported as sub-modules, `ndarray_rand::rand` and `ndarray_rand::rand_distr` respectively. Please rely on these submodules for guaranteed version compatibility. diff --git a/ndarray-rand/RELEASES.md b/ndarray-rand/RELEASES.md index ad0730608..0142831af 100644 --- a/ndarray-rand/RELEASES.md +++ b/ndarray-rand/RELEASES.md @@ -1,6 +1,13 @@ Recent Changes -------------- +- 0.13.0 + +- Require ndarray 0.14 (unchanged from previous version) +- Require rand 0.8 +- Require rand_distr 0.4 +- Fix methods `sample_axis` and `sample_axis_using` so that they can be used on array views too. + - 0.12.0 - Require ndarray 0.14 From 1912bd95fb9a534876048a77fa146364f249e7c4 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 23 Dec 2020 23:57:36 +0100 Subject: [PATCH 121/651] API: Remove deprecated .rows() and .cols() Use replacements .nrows() and .ncols() respectively --- src/impl_2d.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/impl_2d.rs b/src/impl_2d.rs index 7b919eb3f..64d4860cf 100644 --- a/src/impl_2d.rs +++ b/src/impl_2d.rs @@ -58,12 +58,6 @@ where self.len_of(Axis(0)) } - /// Return the number of rows (length of `Axis(0)`) in the two-dimensional array. - #[deprecated(note = "Renamed to .nrows(), please use the new name")] - pub fn rows(&self) -> usize { - self.nrows() - } - /// Return an array view of column `index`. /// /// **Panics** if `index` is out of bounds. @@ -108,12 +102,6 @@ where self.len_of(Axis(1)) } - /// Return the number of columns (length of `Axis(1)`) in the two-dimensional array. - #[deprecated(note = "Renamed to .ncols(), please use the new name")] - pub fn cols(&self) -> usize { - self.ncols() - } - /// Return true if the array is square, false otherwise. /// /// # Examples From 0cc42b572b1ddf4ae8eb453b3273b75d34c78fd1 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 24 Dec 2020 00:18:26 +0100 Subject: [PATCH 122/651] API: Remove deprecated trait DataClone Use Data + RawDataClone as replacement --- src/data_traits.rs | 12 ------------ src/lib.rs | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index a3e00c1ed..30133d70f 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -129,18 +129,6 @@ pub unsafe trait DataMut: Data + RawDataMut { } } -/// Array representation trait. -/// -/// An array representation that can be cloned and allows elements to be -/// accessed with safe code. -/// -/// ***Internal trait, see `Data`.*** -#[deprecated(note = "use `Data + RawDataClone` instead", since = "0.13.0")] -pub trait DataClone: Data + RawDataClone {} - -#[allow(deprecated)] -impl DataClone for T where T: Data + RawDataClone {} - unsafe impl RawData for RawViewRepr<*const A> { type Elem = A; fn _data_slice(&self) -> Option<&[A]> { diff --git a/src/lib.rs b/src/lib.rs index a99d6d52f..3268d8db5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -164,7 +164,7 @@ pub use crate::aliases::*; #[allow(deprecated)] pub use crate::data_traits::{ - Data, DataClone, DataMut, DataOwned, DataShared, RawData, RawDataClone, RawDataMut, + Data, DataMut, DataOwned, DataShared, RawData, RawDataClone, RawDataMut, RawDataSubst, }; From 078c3d8c76f36d13da5f8392b5121b6e8d36c1fa Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 23 Dec 2020 23:58:19 +0100 Subject: [PATCH 123/651] API: Rename .genrows/gencolumns/_mut into .rows/columns/_mut Rename these to give them clearer names. Rows is for rows (and generalized rows, which is a bit strange). Columns is for columns (and generalized ones). Generalized just means that we take the concept of rows/columns and somehow extend it to n-dimension arrays. The old names .genrows/gencolums/_mut are deprecated --- src/impl_methods.rs | 56 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index dd0034b26..09edbb15e 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -811,12 +811,12 @@ where /// [[ 6, 7, 8], // -- row 1, 0 /// [ 9, 10, 11]]]); // -- row 1, 1 /// - /// // `genrows` will yield the four generalized rows of the array. - /// for row in a.genrows() { + /// // `rows` will yield the four generalized rows of the array. + /// for row in a.rows() { /// /* loop body */ /// } /// ``` - pub fn genrows(&self) -> Lanes<'_, A, D::Smaller> + pub fn rows(&self) -> Lanes<'_, A, D::Smaller> where S: Data, { @@ -827,11 +827,19 @@ where Lanes::new(self.view(), Axis(n - 1)) } + #[deprecated(note="Renamed to .rows()", since="0.15.0")] + pub fn genrows(&self) -> Lanes<'_, A, D::Smaller> + where + S: Data, + { + self.rows() + } + /// Return a producer and iterable that traverses over the *generalized* /// rows of the array and yields mutable array views. /// /// Iterator element is `ArrayView1` (1D read-write array view). - pub fn genrows_mut(&mut self) -> LanesMut<'_, A, D::Smaller> + pub fn rows_mut(&mut self) -> LanesMut<'_, A, D::Smaller> where S: DataMut, { @@ -842,6 +850,14 @@ where LanesMut::new(self.view_mut(), Axis(n - 1)) } + #[deprecated(note="Renamed to .rows_mut()", since="0.15.0")] + pub fn genrows_mut(&mut self) -> LanesMut<'_, A, D::Smaller> + where + S: DataMut, + { + self.rows_mut() + } + /// Return a producer and iterable that traverses over the *generalized* /// columns of the array. For a 2D array these are the regular columns. /// @@ -863,29 +879,53 @@ where /// let a = arr3(&[[[ 0, 1, 2], [ 3, 4, 5]], /// [[ 6, 7, 8], [ 9, 10, 11]]]); /// - /// // Here `gencolumns` will yield the six generalized columns of the array. - /// for row in a.gencolumns() { + /// // Here `columns` will yield the six generalized columns of the array. + /// for row in a.columns() { /// /* loop body */ /// } /// ``` - pub fn gencolumns(&self) -> Lanes<'_, A, D::Smaller> + pub fn columns(&self) -> Lanes<'_, A, D::Smaller> where S: Data, { Lanes::new(self.view(), Axis(0)) } + /// Return a producer and iterable that traverses over the *generalized* + /// columns of the array. For a 2D array these are the regular columns. + /// + /// Renamed to `.columns()` + #[deprecated(note="Renamed to .columns()", since="0.15.0")] + pub fn gencolumns(&self) -> Lanes<'_, A, D::Smaller> + where + S: Data, + { + self.columns() + } + /// Return a producer and iterable that traverses over the *generalized* /// columns of the array and yields mutable array views. /// /// Iterator element is `ArrayView1` (1D read-write array view). - pub fn gencolumns_mut(&mut self) -> LanesMut<'_, A, D::Smaller> + pub fn columns_mut(&mut self) -> LanesMut<'_, A, D::Smaller> where S: DataMut, { LanesMut::new(self.view_mut(), Axis(0)) } + /// Return a producer and iterable that traverses over the *generalized* + /// columns of the array and yields mutable array views. + /// + /// Renamed to `.columns_mut()` + #[deprecated(note="Renamed to .columns_mut()", since="0.15.0")] + pub fn gencolumns_mut(&mut self) -> LanesMut<'_, A, D::Smaller> + where + S: DataMut, + { + self.columns_mut() + } + /// Return a producer and iterable that traverses over all 1D lanes /// pointing in the direction of `axis`. /// From a4784936e1a22ae568129cc3fed4fd8b237c18a7 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 24 Dec 2020 00:10:02 +0100 Subject: [PATCH 124/651] API: Update all internal uses of genrows/gencolumns to rows/columns --- benches/bench1.rs | 4 ++-- examples/life.rs | 2 +- src/lib.rs | 24 ++++++++++++------------ src/zip/mod.rs | 4 ++-- src/zip/zipmacro.rs | 4 ++-- tests/array.rs | 2 +- tests/iterators.rs | 22 +++++++++++----------- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/benches/bench1.rs b/benches/bench1.rs index 8cd04c458..35a1d6e7e 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -61,7 +61,7 @@ fn iter_sum_2d_by_row(bench: &mut test::Bencher) { let a = black_box(a); bench.iter(|| { let mut sum = 0; - for row in a.genrows() { + for row in a.rows() { for &elt in row { sum += elt; } @@ -121,7 +121,7 @@ fn iter_sum_2d_cutout_outer_iter(bench: &mut test::Bencher) { let a = black_box(av); bench.iter(|| { let mut sum = 0; - for row in a.genrows() { + for row in a.rows() { for &elt in row { sum += elt; } diff --git a/examples/life.rs b/examples/life.rs index 1c2789389..748f16053 100644 --- a/examples/life.rs +++ b/examples/life.rs @@ -67,7 +67,7 @@ fn turn_on_corners(z: &mut Board) { } fn render(a: &Board) { - for row in a.genrows() { + for row in a.rows() { for &x in row { if x > 0 { print!("#"); diff --git a/src/lib.rs b/src/lib.rs index a99d6d52f..b94eee228 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -413,16 +413,16 @@ pub type Ixs = isize; /// /// The `outer_iter` and `axis_iter` are one dimensional producers. /// -/// ## `.genrows()`, `.gencolumns()` and `.lanes()` +/// ## `.rows()`, `.columns()` and `.lanes()` /// -/// [`.genrows()`][gr] is a producer (and iterable) of all rows in an array. +/// [`.rows()`][gr] is a producer (and iterable) of all rows in an array. /// /// ``` /// use ndarray::Array; /// /// // 1. Loop over the rows of a 2D array /// let mut a = Array::zeros((10, 10)); -/// for mut row in a.genrows_mut() { +/// for mut row in a.rows_mut() { /// row.fill(1.); /// } /// @@ -430,7 +430,7 @@ pub type Ixs = isize; /// use ndarray::Zip; /// let mut b = Array::zeros(a.nrows()); /// -/// Zip::from(a.genrows()) +/// Zip::from(a.rows()) /// .and(&mut b) /// .apply(|a_row, b_elt| { /// *b_elt = a_row[a.ncols() - 1] - a_row[0]; @@ -448,21 +448,21 @@ pub type Ixs = isize; /// has *a m* rows. It's composed of *a* times the previous array, so it /// has *a* times as many rows. /// -/// All methods: [`.genrows()`][gr], [`.genrows_mut()`][grm], -/// [`.gencolumns()`][gc], [`.gencolumns_mut()`][gcm], +/// All methods: [`.rows()`][gr], [`.rows_mut()`][grm], +/// [`.columns()`][gc], [`.columns_mut()`][gcm], /// [`.lanes(axis)`][l], [`.lanes_mut(axis)`][lm]. /// -/// [gr]: #method.genrows -/// [grm]: #method.genrows_mut -/// [gc]: #method.gencolumns -/// [gcm]: #method.gencolumns_mut +/// [gr]: #method.rows +/// [grm]: #method.rows_mut +/// [gc]: #method.columns +/// [gcm]: #method.columns_mut /// [l]: #method.lanes /// [lm]: #method.lanes_mut /// -/// Yes, for 2D arrays `.genrows()` and `.outer_iter()` have about the same +/// Yes, for 2D arrays `.rows()` and `.outer_iter()` have about the same /// effect: /// -/// + `genrows()` is a producer with *n* - 1 dimensions of 1 dimensional items +/// + `rows()` is a producer with *n* - 1 dimensions of 1 dimensional items /// + `outer_iter()` is a producer with 1 dimension of *n* - 1 dimensional items /// /// ## Slicing diff --git a/src/zip/mod.rs b/src/zip/mod.rs index ed92e2509..835ae5d31 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -561,7 +561,7 @@ impl NdProducer for RawArrayViewMut { /// let mut totals = Array1::zeros(a.nrows()); /// /// Zip::from(&mut totals) -/// .and(a.genrows()) +/// .and(a.rows()) /// .apply(|totals, row| *totals = row.sum()); /// /// // Check the result against the built in `.sum_axis()` along axis 1. @@ -570,7 +570,7 @@ impl NdProducer for RawArrayViewMut { /// /// // Example 3: Recreate Example 2 using apply_collect to make a new array /// -/// let mut totals2 = Zip::from(a.genrows()).apply_collect(|row| row.sum()); +/// let mut totals2 = Zip::from(a.rows()).apply_collect(|row| row.sum()); /// /// // Check the result against the previous example. /// assert_eq!(totals, totals2); diff --git a/src/zip/zipmacro.rs b/src/zip/zipmacro.rs index ba2b3da22..5353a6378 100644 --- a/src/zip/zipmacro.rs +++ b/src/zip/zipmacro.rs @@ -89,8 +89,8 @@ /// // entry in `totals` with the sum across each row. /// // /// // The row is an array view; it doesn't need to be dereferenced. -/// let mut totals = Array1::zeros(a.rows()); -/// azip!((totals in &mut totals, row in a.genrows()) *totals = row.sum()); +/// let mut totals = Array1::zeros(a.nrows()); +/// azip!((totals in &mut totals, row in a.rows()) *totals = row.sum()); /// /// // Check the result against the built in `.sum_axis()` along axis 1. /// assert_eq!(totals, a.sum_axis(Axis(1))); diff --git a/tests/array.rs b/tests/array.rs index db54b7e5f..7a0e2d513 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1701,7 +1701,7 @@ fn test_f_order() { assert_eq!(c.strides(), &[3, 1]); assert_eq!(f.strides(), &[1, 2]); itertools::assert_equal(f.iter(), c.iter()); - itertools::assert_equal(f.genrows(), c.genrows()); + itertools::assert_equal(f.rows(), c.rows()); itertools::assert_equal(f.outer_iter(), c.outer_iter()); itertools::assert_equal(f.axis_iter(Axis(0)), c.axis_iter(Axis(0))); itertools::assert_equal(f.axis_iter(Axis(1)), c.axis_iter(Axis(1))); diff --git a/tests/iterators.rs b/tests/iterators.rs index 371339b96..7a30003c4 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -142,7 +142,7 @@ fn inner_iter() { // [8, 9], // ... assert_equal( - a.genrows(), + a.rows(), vec![ aview1(&[0, 1]), aview1(&[2, 3]), @@ -156,7 +156,7 @@ fn inner_iter() { b.swap_axes(0, 2); b.assign(&a); assert_equal( - b.genrows(), + b.rows(), vec![ aview1(&[0, 1]), aview1(&[2, 3]), @@ -171,13 +171,13 @@ fn inner_iter() { #[test] fn inner_iter_corner_cases() { let a0 = ArcArray::::zeros(()); - assert_equal(a0.genrows(), vec![aview1(&[0])]); + assert_equal(a0.rows(), vec![aview1(&[0])]); let a2 = ArcArray::::zeros((0, 3)); - assert_equal(a2.genrows(), vec![aview1(&[]); 0]); + assert_equal(a2.rows(), vec![aview1(&[]); 0]); let a2 = ArcArray::::zeros((3, 0)); - assert_equal(a2.genrows(), vec![aview1(&[]); 3]); + assert_equal(a2.rows(), vec![aview1(&[]); 3]); } #[test] @@ -185,7 +185,7 @@ fn inner_iter_size_hint() { // Check that the size hint is correctly computed let a = ArcArray::from_iter(0..24).reshape((2, 3, 4)); let mut len = 6; - let mut it = a.genrows().into_iter(); + let mut it = a.rows().into_iter(); assert_eq!(it.len(), len); while len > 0 { it.next(); @@ -223,7 +223,7 @@ fn outer_iter() { found_rows.push(row); } } - assert_equal(a.genrows(), found_rows.clone()); + assert_equal(a.rows(), found_rows.clone()); let mut found_rows_rev = Vec::new(); for sub in b.outer_iter().rev() { @@ -251,7 +251,7 @@ fn outer_iter() { } } println!("{:#?}", found_rows); - assert_equal(a.genrows(), found_rows); + assert_equal(a.rows(), found_rows); } #[test] @@ -370,7 +370,7 @@ fn outer_iter_mut() { found_rows.push(row); } } - assert_equal(a.genrows(), found_rows); + assert_equal(a.rows(), found_rows); } #[test] @@ -747,8 +747,8 @@ fn iterators_are_send_sync() { _send_sync(&a.iter_mut()); _send_sync(&a.indexed_iter()); _send_sync(&a.indexed_iter_mut()); - _send_sync(&a.genrows()); - _send_sync(&a.genrows_mut()); + _send_sync(&a.rows()); + _send_sync(&a.rows_mut()); _send_sync(&a.outer_iter()); _send_sync(&a.outer_iter_mut()); _send_sync(&a.axis_iter(Axis(1))); From 49b315a0572fafd22a92e47fe84a25bd8f316c1f Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 24 Dec 2020 00:21:54 +0100 Subject: [PATCH 125/651] API: Mark .scalar_sum() as deprecated (Renamed to .sum()) --- src/numeric/impl_numeric.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 85f69444d..4850bd805 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -48,6 +48,17 @@ where sum } + /// Return the sum of all elements in the array. + /// + /// *This method has been renamed to `.sum()`* + #[deprecated(note="renamed to `sum`", since="0.15.0")] + pub fn scalar_sum(&self) -> A + where + A: Clone + Add + num_traits::Zero, + { + self.sum() + } + /// Returns the [arithmetic mean] x̅ of all elements in the array: /// /// ```text @@ -75,18 +86,6 @@ where } } - /// Return the sum of all elements in the array. - /// - /// *This method has been renamed to `.sum()` and will be deprecated in the - /// next version.* - // #[deprecated(note="renamed to `sum`", since="0.13")] - pub fn scalar_sum(&self) -> A - where - A: Clone + Add + num_traits::Zero, - { - self.sum() - } - /// Return the product of all elements in the array. /// /// ``` From e585262a7145ad54f356280c71bf295b399dd309 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 24 Dec 2020 00:51:15 +0100 Subject: [PATCH 126/651] API: Remove deprecated .all_close() Replaced by .abs_diff_eq (using the approx feature) --- src/numeric/impl_numeric.rs | 30 ------------------------------ tests/array.rs | 11 ----------- 2 files changed, 41 deletions(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 4850bd805..6fc4f6cf4 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -13,8 +13,6 @@ use crate::imp_prelude::*; use crate::itertools::enumerate; use crate::numeric_util; -use crate::{FoldWhile, Zip}; - /// # Numerical Methods for Arrays impl ArrayBase where @@ -304,32 +302,4 @@ where { self.var_axis(axis, ddof).mapv_into(|x| x.sqrt()) } - - /// Return `true` if the arrays' elementwise differences are all within - /// the given absolute tolerance, `false` otherwise. - /// - /// If their shapes disagree, `rhs` is broadcast to the shape of `self`. - /// - /// **Panics** if broadcasting to the same shape isn’t possible. - #[deprecated( - note = "Use `abs_diff_eq` - it requires the `approx` crate feature", - since = "0.13.0" - )] - pub fn all_close(&self, rhs: &ArrayBase, tol: A) -> bool - where - A: Float, - S2: Data, - E: Dimension, - { - !Zip::from(self) - .and(rhs.broadcast_unwrap(self.raw_dim())) - .fold_while((), |_, x, y| { - if (*x - *y).abs() <= tol { - FoldWhile::Continue(()) - } else { - FoldWhile::Done(()) - } - }) - .is_done() - } } diff --git a/tests/array.rs b/tests/array.rs index db54b7e5f..436a3e972 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1786,17 +1786,6 @@ fn test_contiguous() { assert!(b.as_slice_memory_order().is_some()); } -#[test] -#[allow(deprecated)] -fn test_all_close() { - let c = arr3(&[ - [[1., 2., 3.], [1.5, 1.5, 3.]], - [[1., 2., 3.], [1., 2.5, 3.]], - ]); - assert!(c.all_close(&aview1(&[1., 2., 3.]), 1.)); - assert!(!c.all_close(&aview1(&[1., 2., 3.]), 0.1)); -} - #[test] fn test_swap() { let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9]]); From dd5f840c022c948028342cda4b03a25f0f2f3086 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 24 Dec 2020 00:23:43 +0100 Subject: [PATCH 127/651] MAINT: Remove unused allow(deprecated) attr --- src/aliases.rs | 1 - src/lib.rs | 1 - src/prelude.rs | 1 - 3 files changed, 3 deletions(-) diff --git a/src/aliases.rs b/src/aliases.rs index 911e745bd..d41c888a6 100644 --- a/src/aliases.rs +++ b/src/aliases.rs @@ -2,7 +2,6 @@ //! use crate::dimension::Dim; -#[allow(deprecated)] use crate::{ArcArray, Array, ArrayView, ArrayViewMut, Ix, IxDynImpl}; /// Create a zero-dimensional index diff --git a/src/lib.rs b/src/lib.rs index 3268d8db5..52265ccb8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -162,7 +162,6 @@ mod data_traits; pub use crate::aliases::*; -#[allow(deprecated)] pub use crate::data_traits::{ Data, DataMut, DataOwned, DataShared, RawData, RawDataClone, RawDataMut, RawDataSubst, diff --git a/src/prelude.rs b/src/prelude.rs index e25e52175..def236841 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -17,7 +17,6 @@ //! ``` #[doc(no_inline)] -#[allow(deprecated)] pub use crate::{ ArcArray, Array, ArrayBase, ArrayView, ArrayViewMut, CowArray, RawArrayView, RawArrayViewMut, }; From e071bdbe363ac8fed6a573e3eafcc34e22ab2d07 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 24 Dec 2020 01:31:52 +0100 Subject: [PATCH 128/651] API: Remove deprecated ArrayView::into_slice (use to_slice) This could really use either name - `into_slice` for the "into" semantic of preserving the lifetime parameter, or the `to_slice` name which is customary for `&self` methods. Both names have the problem that they compete with `ArrayBase::as_slice()`, but the doc has been improved to explain some of that. --- src/impl_views/conversions.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs index 303541b8b..3ca44d75c 100644 --- a/src/impl_views/conversions.rs +++ b/src/impl_views/conversions.rs @@ -31,18 +31,9 @@ where /// Return the array’s data as a slice, if it is contiguous and in standard order. /// Return `None` otherwise. - #[deprecated(note = "`into_slice` has been renamed to `to_slice`", since = "0.13.0")] - #[allow(clippy::wrong_self_convention)] - pub fn into_slice(&self) -> Option<&'a [A]> { - if self.is_standard_layout() { - unsafe { Some(slice::from_raw_parts(self.ptr.as_ptr(), self.len())) } - } else { - None - } - } - - /// Return the array’s data as a slice, if it is contiguous and in standard order. - /// Return `None` otherwise. + /// + /// Note that while the method is similar to [`ArrayBase::as_slice()`], this method tranfers + /// the view's lifetime to the slice, so it is a bit more powerful. pub fn to_slice(&self) -> Option<&'a [A]> { if self.is_standard_layout() { unsafe { Some(slice::from_raw_parts(self.ptr.as_ptr(), self.len())) } @@ -120,6 +111,9 @@ where { /// Return the array’s data as a slice, if it is contiguous and in standard order. /// Return `None` otherwise. + /// + /// Note that while this is similar to [`ArrayBase::as_slice_mut()`], this method tranfers the + /// view's lifetime to the slice. pub fn into_slice(self) -> Option<&'a mut [A]> { self.into_slice_().ok() } From 4e2e60c60e164f352a9e6afae654ef08ac8ba08f Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 29 Dec 2020 02:29:22 +0100 Subject: [PATCH 129/651] FIX: Internal cleanup, rename into_slice_ -> try_into_slice --- src/impl_views/conversions.rs | 6 ++++-- src/iterators/mod.rs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs index 3ca44d75c..863d26ddb 100644 --- a/src/impl_views/conversions.rs +++ b/src/impl_views/conversions.rs @@ -115,7 +115,7 @@ where /// Note that while this is similar to [`ArrayBase::as_slice_mut()`], this method tranfers the /// view's lifetime to the slice. pub fn into_slice(self) -> Option<&'a mut [A]> { - self.into_slice_().ok() + self.try_into_slice().ok() } } @@ -173,7 +173,9 @@ where ElementsBaseMut::new(self) } - pub(crate) fn into_slice_(self) -> Result<&'a mut [A], Self> { + /// Return the array’s data as a slice, if it is contiguous and in standard order. + /// Otherwise return self in the Err branch of the result. + pub(crate) fn try_into_slice(self) -> Result<&'a mut [A], Self> { if self.is_standard_layout() { unsafe { Ok(slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len())) } } else { diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 621c141ff..8cb7a9088 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -294,7 +294,7 @@ where { pub(crate) fn new(self_: ArrayViewMut<'a, A, D>) -> Self { IterMut { - inner: match self_.into_slice_() { + inner: match self_.try_into_slice() { Ok(x) => ElementsRepr::Slice(x.iter_mut()), Err(self_) => ElementsRepr::Counted(self_.into_elements_base()), }, From 52ca23424c28506b01d9b47ad6a42f95d553cedc Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 15 Feb 2020 13:04:30 -0500 Subject: [PATCH 130/651] Add benches for op with scalar and strided array --- benches/bench1.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/benches/bench1.rs b/benches/bench1.rs index 35a1d6e7e..291a25e97 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -431,6 +431,22 @@ fn scalar_add_2(bench: &mut test::Bencher) { bench.iter(|| n + &a); } +#[bench] +fn scalar_add_strided_1(bench: &mut test::Bencher) { + let a = + Array::from_shape_fn((64, 64 * 2), |(i, j)| (i * 64 + j) as f32).slice_move(s![.., ..;2]); + let n = 1.; + bench.iter(|| &a + n); +} + +#[bench] +fn scalar_add_strided_2(bench: &mut test::Bencher) { + let a = + Array::from_shape_fn((64, 64 * 2), |(i, j)| (i * 64 + j) as f32).slice_move(s![.., ..;2]); + let n = 1.; + bench.iter(|| n + &a); +} + #[bench] fn scalar_sub_1(bench: &mut test::Bencher) { let a = Array::::zeros((64, 64)); From de1b903fd0a0fff86d40492ab6b347598ff4628f Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 29 Dec 2020 22:19:42 +0100 Subject: [PATCH 131/651] FIX: Put .assume_init() on sound ground Fix up the way we implement .assume_init() - use a mapping of the data storage type instead of transmute. The end result is similar anyway but easier to verify that it is correct. See docs for Arc::from_raw for its requirements - which we should now adhere to. --- src/data_repr.rs | 21 ++++++++++++++++++++ src/data_traits.rs | 32 +++++++++++++++++++++++++++++++ src/impl_special_element_types.rs | 20 ++----------------- 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/src/data_repr.rs b/src/data_repr.rs index 4839a8439..56682d9ba 100644 --- a/src/data_repr.rs +++ b/src/data_repr.rs @@ -10,7 +10,11 @@ use crate::extension::nonnull; /// *Don’t use this type directly—use the type alias /// [`Array`](type.Array.html) for the array type!* // Like a Vec, but with non-unique ownership semantics +// +// repr(C) to make it transmutable OwnedRepr -> OwnedRepr if +// transmutable A -> B. #[derive(Debug)] +#[repr(C)] pub struct OwnedRepr { ptr: NonNull, len: usize, @@ -50,6 +54,23 @@ impl OwnedRepr { self.ptr } + /// Cast self into equivalent repr of other element type + /// + /// ## Safety + /// + /// Caller must ensure the two types have the same representation. + /// **Panics** if sizes don't match (which is not a sufficient check). + pub(crate) unsafe fn data_subst(self) -> OwnedRepr { + // necessary but not sufficient check + assert_eq!(mem::size_of::(), mem::size_of::()); + let self_ = ManuallyDrop::new(self); + OwnedRepr { + ptr: self_.ptr.cast::(), + len: self_.len, + capacity: self_.capacity, + } + } + fn take_as_vec(&mut self) -> Vec { let capacity = self.capacity; let len = self.len; diff --git a/src/data_traits.rs b/src/data_traits.rs index 30133d70f..057ef9249 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -526,28 +526,60 @@ unsafe impl<'a, A> DataMut for CowRepr<'a, A> where A: Clone {} pub trait RawDataSubst: RawData { /// The resulting array storage of the same kind but substituted element type type Output: RawData; + + /// Unsafely translate the data representation from one element + /// representation to another. + /// + /// ## Safety + /// + /// Caller must ensure the two types have the same representation. + unsafe fn data_subst(self) -> Self::Output; } impl RawDataSubst for OwnedRepr { type Output = OwnedRepr; + + unsafe fn data_subst(self) -> Self::Output { + self.data_subst() + } } impl RawDataSubst for OwnedArcRepr { type Output = OwnedArcRepr; + + unsafe fn data_subst(self) -> Self::Output { + OwnedArcRepr(Arc::from_raw(Arc::into_raw(self.0) as *const OwnedRepr)) + } } impl RawDataSubst for RawViewRepr<*const A> { type Output = RawViewRepr<*const B>; + + unsafe fn data_subst(self) -> Self::Output { + RawViewRepr::new() + } } impl RawDataSubst for RawViewRepr<*mut A> { type Output = RawViewRepr<*mut B>; + + unsafe fn data_subst(self) -> Self::Output { + RawViewRepr::new() + } } impl<'a, A: 'a, B: 'a> RawDataSubst for ViewRepr<&'a A> { type Output = ViewRepr<&'a B>; + + unsafe fn data_subst(self) -> Self::Output { + ViewRepr::new() + } } impl<'a, A: 'a, B: 'a> RawDataSubst for ViewRepr<&'a mut A> { type Output = ViewRepr<&'a mut B>; + + unsafe fn data_subst(self) -> Self::Output { + ViewRepr::new() + } } diff --git a/src/impl_special_element_types.rs b/src/impl_special_element_types.rs index 40cba5822..e41177dbf 100644 --- a/src/impl_special_element_types.rs +++ b/src/impl_special_element_types.rs @@ -6,8 +6,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::mem::size_of; -use std::mem::ManuallyDrop; use std::mem::MaybeUninit; use crate::imp_prelude::*; @@ -37,12 +35,10 @@ where /// array's storage; it is for example possible to slice these in place, but that must /// only be done after all elements have been initialized. pub unsafe fn assume_init(self) -> ArrayBase<>::Output, D> { - // NOTE: Fully initialized includes elements not reachable in current slicing/view. - let ArrayBase { data, ptr, dim, strides } = self; - // transmute from storage of MaybeUninit to storage of A - let data = unlimited_transmute::(data); + // "transmute" from storage of MaybeUninit to storage of A + let data = S::data_subst(data); let ptr = ptr.cast::(); ArrayBase { @@ -53,15 +49,3 @@ where } } } - -/// Transmute from A to B. -/// -/// Like transmute, but does not have the compile-time size check which blocks -/// using regular transmute for "S to S::Output". -/// -/// **Panics** if the size of A and B are different. -unsafe fn unlimited_transmute(data: A) -> B { - assert_eq!(size_of::(), size_of::()); - let old_data = ManuallyDrop::new(data); - (&*old_data as *const A as *const B).read() -} From 1b45140f94622e89382effae4e8fc782594ef04f Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 29 Dec 2020 23:10:29 +0100 Subject: [PATCH 132/651] FIX: Update stack/concatenate to use maybe_uninit --- src/lib.rs | 3 ++- src/stacking.rs | 47 +++++++++++++++++++++--------------------- src/traversal_utils.rs | 26 +++++++++++++++++++++++ 3 files changed, 52 insertions(+), 24 deletions(-) create mode 100644 src/traversal_utils.rs diff --git a/src/lib.rs b/src/lib.rs index 90eb7c1bf..14b4c0d6f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2014-2016 bluss and ndarray developers. +// Copyright 2014-2020 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license @@ -187,6 +187,7 @@ mod shape_builder; mod slice; mod split_at; mod stacking; +mod traversal_utils; #[macro_use] mod zip; diff --git a/src/stacking.rs b/src/stacking.rs index 2c2db7b59..604a7cc5b 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -1,4 +1,4 @@ -// Copyright 2014-2016 bluss and ndarray developers. +// Copyright 2014-2020 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license @@ -8,6 +8,7 @@ use crate::error::{from_kind, ErrorKind, ShapeError}; use crate::imp_prelude::*; +use crate::traversal_utils::assign_to; /// Stack arrays along the new axis. /// @@ -88,25 +89,23 @@ where let stacked_dim = arrays.iter().fold(0, |acc, a| acc + a.len_of(axis)); res_dim.set_axis(axis, stacked_dim); - // we can safely use uninitialized values here because they are Copy - // and we will only ever write to them - let size = res_dim.size(); - let mut v = Vec::with_capacity(size); - unsafe { - v.set_len(size); - } - let mut res = Array::from_shape_vec(res_dim, v)?; + // we can safely use uninitialized values here because we will + // overwrite every one of them. + let mut res = Array::maybe_uninit(res_dim); { let mut assign_view = res.view_mut(); for array in arrays { let len = array.len_of(axis); - let (mut front, rest) = assign_view.split_at(axis, len); - front.assign(array); + let (front, rest) = assign_view.split_at(axis, len); + assign_to(array, front); assign_view = rest; } + debug_assert_eq!(assign_view.len(), 0); + } + unsafe { + Ok(res.assume_init()) } - Ok(res) } /// Stack arrays along the new axis. @@ -158,22 +157,24 @@ where res_dim.set_axis(axis, arrays.len()); - // we can safely use uninitialized values here because they are Copy - // and we will only ever write to them - let size = res_dim.size(); - let mut v = Vec::with_capacity(size); - unsafe { - v.set_len(size); - } - let mut res = Array::from_shape_vec(res_dim, v)?; + // we can safely use uninitialized values here because we will + // overwrite every one of them. + let mut res = Array::maybe_uninit(res_dim); res.axis_iter_mut(axis) .zip(arrays.iter()) - .for_each(|(mut assign_view, array)| { - assign_view.assign(&array); + .for_each(|(assign_view, array)| { + // assign_view is D::Larger::Smaller which is usually == D + // (but if D is Ix6, we have IxD != Ix6 here; differing types + // but same number of axes). + let assign_view = assign_view.into_dimensionality::() + .expect("same-dimensionality cast"); + assign_to(array, assign_view); }); - Ok(res) + unsafe { + Ok(res.assume_init()) + } } /// Stack arrays along the new axis. diff --git a/src/traversal_utils.rs b/src/traversal_utils.rs new file mode 100644 index 000000000..b77d410a2 --- /dev/null +++ b/src/traversal_utils.rs @@ -0,0 +1,26 @@ +// Copyright 2020 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::{ + IntoNdProducer, + AssignElem, + Zip, +}; + +/// Assign values from producer P1 to producer P2 +/// P1 and P2 must be of the same shape and dimension +pub(crate) fn assign_to<'a, P1, P2, A>(from: P1, to: P2) + where P1: IntoNdProducer, + P2: IntoNdProducer, + P2::Item: AssignElem, + A: Clone + 'a +{ + Zip::from(from) + .apply_assign_into(to, A::clone); +} + From a2162355c9ff67479eb21e5ed9c4709c9b95ec9c Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 29 Dec 2020 23:12:40 +0100 Subject: [PATCH 133/651] FIX: Update .dot() matrix-vector product to use maybe_uninit Avoid Array::uninitialized and use maybe_uninit. --- src/linalg/impl_linalg.rs | 67 ++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index fd8d77d85..f54f14431 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -1,4 +1,4 @@ -// Copyright 2014-2016 bluss and ndarray developers. +// Copyright 2014-2020 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license @@ -325,9 +325,9 @@ where // Avoid initializing the memory in vec -- set it during iteration unsafe { - let mut c = Array::uninitialized(m); - general_mat_vec_mul(A::one(), self, rhs, A::zero(), &mut c); - c + let mut c = Array1::maybe_uninit(m); + general_mat_vec_mul_impl(A::one(), self, rhs, A::zero(), c.raw_view_mut().cast::()); + c.assume_init() } } } @@ -598,6 +598,30 @@ pub fn general_mat_vec_mul( S2: Data, S3: DataMut, A: LinalgScalar, +{ + unsafe { + general_mat_vec_mul_impl(alpha, a, x, beta, y.raw_view_mut()) + } +} + +/// General matrix-vector multiplication +/// +/// Use a raw view for the destination vector, so that it can be uninitalized. +/// +/// ## Safety +/// +/// The caller must ensure that the raw view is valid for writing. +/// the destination may be uninitialized iff beta is zero. +unsafe fn general_mat_vec_mul_impl( + alpha: A, + a: &ArrayBase, + x: &ArrayBase, + beta: A, + y: RawArrayViewMut, +) where + S1: Data, + S2: Data, + A: LinalgScalar, { let ((m, k), k2) = (a.dim(), x.dim()); let m2 = y.dim(); @@ -626,22 +650,20 @@ pub fn general_mat_vec_mul( let x_stride = x.strides()[0] as blas_index; let y_stride = y.strides()[0] as blas_index; - unsafe { - blas_sys::$gemv( - layout, - a_trans, - m as blas_index, // m, rows of Op(a) - k as blas_index, // n, cols of Op(a) - cast_as(&alpha), // alpha - a.ptr.as_ptr() as *const _, // a - a_stride, // lda - x.ptr.as_ptr() as *const _, // x - x_stride, - cast_as(&beta), // beta - y.ptr.as_ptr() as *mut _, // x - y_stride, - ); - } + blas_sys::$gemv( + layout, + a_trans, + m as blas_index, // m, rows of Op(a) + k as blas_index, // n, cols of Op(a) + cast_as(&alpha), // alpha + a.ptr.as_ptr() as *const _, // a + a_stride, // lda + x.ptr.as_ptr() as *const _, // x + x_stride, + cast_as(&beta), // beta + y.ptr.as_ptr() as *mut _, // x + y_stride, + ); return; } } @@ -655,8 +677,9 @@ pub fn general_mat_vec_mul( /* general */ if beta.is_zero() { + // when beta is zero, c may be uninitialized Zip::from(a.outer_iter()).and(y).apply(|row, elt| { - *elt = row.dot(x) * alpha; + elt.write(row.dot(x) * alpha); }); } else { Zip::from(a.outer_iter()).and(y).apply(|row, elt| { @@ -683,7 +706,7 @@ fn cast_as(a: &A) -> B { #[cfg(feature = "blas")] fn blas_compat_1d(a: &ArrayBase) -> bool where - S: Data, + S: RawData, A: 'static, S::Elem: 'static, { From 5e9a01f310f3a32c52277025850b0e28fffad9b6 Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 29 Dec 2020 23:14:51 +0100 Subject: [PATCH 134/651] DOC: Remove unused link to Array::uninitialized in numpy docs --- src/doc/ndarray_for_numpy_users/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index a9e864a55..fff72ce25 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -647,7 +647,6 @@ //! [.index_axis()]: ../../struct.ArrayBase.html#method.index_axis //! [.sum_axis()]: ../../struct.ArrayBase.html#method.sum_axis //! [.t()]: ../../struct.ArrayBase.html#method.t -//! [::uninitialized()]: ../../struct.ArrayBase.html#method.uninitialized //! [vec-* dot]: ../../struct.ArrayBase.html#method.dot //! [.visit()]: ../../struct.ArrayBase.html#method.visit //! [::zeros()]: ../../struct.ArrayBase.html#method.zeros From ba07345a1c58d681e65913f0e1ffdd6cca00f377 Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 29 Dec 2020 23:31:04 +0100 Subject: [PATCH 135/651] FIX: Use maybe_uninit in benchmarks --- benches/bench1.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/benches/bench1.rs b/benches/bench1.rs index 35a1d6e7e..de865aa8b 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -9,6 +9,8 @@ extern crate test; +use std::mem::MaybeUninit; + use ndarray::ShapeBuilder; use ndarray::{arr0, arr1, arr2, azip, s}; use ndarray::{Array, Array1, Array2, Axis, Ix, Zip}; @@ -269,9 +271,9 @@ fn add_2d_alloc_zip_uninit(bench: &mut test::Bencher) { let a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| unsafe { - let mut c = Array::uninitialized(a.dim()); - azip!((&a in &a, &b in &b, c in c.raw_view_mut()) - std::ptr::write(c, a + b) + let mut c = Array::, _>::maybe_uninit(a.dim()); + azip!((&a in &a, &b in &b, c in c.raw_view_mut().cast::()) + c.write(a + b) ); c }); From 4d9641db085e64729d52050ea0c24e4a541629a7 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 1 Jan 2021 14:51:47 +0100 Subject: [PATCH 136/651] DOC: Mini edits for doc comments - typos and language fixes --- src/impl_methods.rs | 4 ++-- src/lib.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 09edbb15e..cccc195a1 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1151,8 +1151,8 @@ where /// The windows are all distinct overlapping views of size `window_size` /// that fit into the array's shape. /// - /// Will yield over no elements if window size is larger - /// than the actual array size of any dimension. + /// This produces no elements if the window size is larger than the actual array size along any + /// axis. /// /// The produced element is an `ArrayView` with exactly the dimension /// `window_size`. diff --git a/src/lib.rs b/src/lib.rs index 14b4c0d6f..c0a3b2e85 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1362,7 +1362,7 @@ pub type ArrayViewMut<'a, A, D> = ArrayBase, D>; /// /// # Warning /// -/// You can't use this type wih an arbitrary raw pointer; see +/// You can't use this type with an arbitrary raw pointer; see /// [`from_shape_ptr`](#method.from_shape_ptr) for details. pub type RawArrayView = ArrayBase, D>; @@ -1387,7 +1387,7 @@ pub type RawArrayView = ArrayBase, D>; /// /// # Warning /// -/// You can't use this type wih an arbitrary raw pointer; see +/// You can't use this type with an arbitrary raw pointer; see /// [`from_shape_ptr`](#method.from_shape_ptr) for details. pub type RawArrayViewMut = ArrayBase, D>; From 929729554285a7f4111c195924066759bc46c365 Mon Sep 17 00:00:00 2001 From: David Sanders Date: Fri, 8 Jan 2021 10:59:28 -0800 Subject: [PATCH 137/651] Fixing typographical errors. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index c0a3b2e85..beea4fd04 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ pub type Ixs = isize; /// /// [`ArrayView`] and [`ArrayViewMut`] are read-only and read-write array views /// respectively. They use dimensionality, indexing, and almost all other -/// methods the same was as the other array types. +/// methods the same way as the other array types. /// /// Methods for `ArrayBase` apply to array views too, when the trait bounds /// allow. From 551ee284d5ea64249245d7e2215535cb1aa3710f Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 29 Dec 2020 23:55:31 +0100 Subject: [PATCH 138/651] FEAT: Add methods .cell_view() and .into_cell_view() --- src/impl_methods.rs | 15 +++++++++++++++ src/impl_views/conversions.rs | 16 ++++++++++++++++ tests/views.rs | 16 ++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 tests/views.rs diff --git a/src/impl_methods.rs b/src/impl_methods.rs index cccc195a1..59f27d55c 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::cell::Cell; use std::ptr as std_ptr; use std::slice; @@ -151,6 +152,20 @@ where unsafe { ArrayViewMut::new(self.ptr, self.dim.clone(), self.strides.clone()) } } + /// Return a shared view of the array with elements as if they were embedded in cells. + /// + /// The cell view requires a mutable borrow of the array. Once borrowed the + /// cell view itself can be copied and accessed without exclusivity. + /// + /// The view acts "as if" the elements are temporarily in cells, and elements + /// can be changed through shared references using the regular cell methods. + pub fn cell_view(&mut self) -> ArrayView<'_, Cell, D> + where + S: DataMut, + { + self.view_mut().into_cell_view() + } + /// Return an uniquely owned copy of the array. /// /// If the input array is contiguous and its strides are positive, then the diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs index 863d26ddb..4265f7616 100644 --- a/src/impl_views/conversions.rs +++ b/src/impl_views/conversions.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::cell::Cell; use std::slice; use crate::imp_prelude::*; @@ -117,6 +118,21 @@ where pub fn into_slice(self) -> Option<&'a mut [A]> { self.try_into_slice().ok() } + + /// Return a shared view of the array with elements as if they were embedded in cells. + /// + /// The cell view itself can be copied and accessed without exclusivity. + /// + /// The view acts "as if" the elements are temporarily in cells, and elements + /// can be changed through shared references using the regular cell methods. + pub fn into_cell_view(self) -> ArrayView<'a, Cell, D> { + // safety: valid because + // A and Cell have the same representation + // &'a mut T is interchangeable with &'a Cell -- see method Cell::from_mut + unsafe { + self.into_raw_view_mut().cast::>().deref_into_view() + } + } } /// Private array view methods diff --git a/tests/views.rs b/tests/views.rs new file mode 100644 index 000000000..216e55402 --- /dev/null +++ b/tests/views.rs @@ -0,0 +1,16 @@ +use ndarray::prelude::*; +use ndarray::Zip; + +#[test] +fn cell_view() { + let mut a = Array::from_shape_fn((10, 5), |(i, j)| (i * j) as f32); + let answer = &a + 1.; + + { + let cv1 = a.cell_view(); + let cv2 = cv1; + + Zip::from(cv1).and(cv2).apply(|a, b| a.set(b.get() + 1.)); + } + assert_eq!(a, answer); +} From 911d71e2cb27abe7d7583ba650665110901315fb Mon Sep 17 00:00:00 2001 From: David Sanders Date: Fri, 8 Jan 2021 11:21:42 -0800 Subject: [PATCH 139/651] Fixing typographical errors. --- src/impl_methods.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index cccc195a1..06bd9189b 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -69,7 +69,7 @@ where self.dim.clone().into_pattern() } - /// Return the shape of the array as it stored in the array. + /// Return the shape of the array as it's stored in the array. /// /// This is primarily useful for passing to other `ArrayBase` /// functions, such as when creating another array of the same @@ -630,7 +630,7 @@ where /// /// The caller must ensure that: /// - /// 1. both `index1 and `index2` are in-bounds and + /// 1. both `index1` and `index2` are in-bounds and /// /// 2. the data is uniquely held by the array. (This property is guaranteed /// for `Array` and `ArrayViewMut`, but not for `ArcArray` or `CowArray`.) @@ -929,7 +929,7 @@ where /// Return a producer and iterable that traverses over all 1D lanes /// pointing in the direction of `axis`. /// - /// When the pointing in the direction of the first axis, they are *columns*, + /// When pointing in the direction of the first axis, they are *columns*, /// in the direction of the last axis *rows*; in general they are all /// *lanes* and are one dimensional. /// @@ -1198,7 +1198,7 @@ where (len, stride) } - /// Return an view of the diagonal elements of the array. + /// Return a view of the diagonal elements of the array. /// /// The diagonal is simply the sequence indexed by *(0, 0, .., 0)*, /// *(1, 1, ..., 1)* etc as long as all axes have elements. @@ -1257,7 +1257,7 @@ where /// Return `true` if the array data is laid out in contiguous “C order” in /// memory (where the last index is the most rapidly varying). /// - /// Return `false` otherwise, i.e the array is possibly not + /// Return `false` otherwise, i.e. the array is possibly not /// contiguous in memory, it has custom strides, etc. pub fn is_standard_layout(&self) -> bool { fn is_standard_layout(dim: &D, strides: &D) -> bool { From 894d981f4c151a28486bb1cb008b33d76aa8fe08 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 8 Jan 2021 20:26:40 +0100 Subject: [PATCH 140/651] FEAT: Add MathCell, a wrapper for std Cell This will be used so that we can implement arithmetic ops for the views with cells in them and for cells. --- src/argument_traits.rs | 8 +++ src/impl_methods.rs | 4 +- src/impl_views/conversions.rs | 10 ++-- src/lib.rs | 2 + src/math_cell.rs | 102 ++++++++++++++++++++++++++++++++++ 5 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 src/math_cell.rs diff --git a/src/argument_traits.rs b/src/argument_traits.rs index a93e33a12..82d4869a9 100644 --- a/src/argument_traits.rs +++ b/src/argument_traits.rs @@ -1,6 +1,7 @@ use std::cell::Cell; use std::mem::MaybeUninit; +use crate::math_cell::MathCell; /// A producer element that can be assigned to once pub trait AssignElem { @@ -22,6 +23,13 @@ impl<'a, T> AssignElem for &'a Cell { } } +/// Assignable element, simply `self.set(input)`. +impl<'a, T> AssignElem for &'a MathCell { + fn assign_elem(self, input: T) { + self.set(input); + } +} + /// Assignable element, the item in the MaybeUninit is overwritten (prior value, if any, is not /// read or dropped). impl<'a, T> AssignElem for &'a mut MaybeUninit { diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 59f27d55c..df48ea1cf 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -6,7 +6,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::cell::Cell; use std::ptr as std_ptr; use std::slice; @@ -21,6 +20,7 @@ use crate::dimension::{ abs_index, axes_of, do_slice, merge_axes, size_of_shape_checked, stride_offset, Axes, }; use crate::error::{self, ErrorKind, ShapeError}; +use crate::math_cell::MathCell; use crate::itertools::zip; use crate::zip::Zip; @@ -159,7 +159,7 @@ where /// /// The view acts "as if" the elements are temporarily in cells, and elements /// can be changed through shared references using the regular cell methods. - pub fn cell_view(&mut self) -> ArrayView<'_, Cell, D> + pub fn cell_view(&mut self) -> ArrayView<'_, MathCell, D> where S: DataMut, { diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs index 4265f7616..8c4230108 100644 --- a/src/impl_views/conversions.rs +++ b/src/impl_views/conversions.rs @@ -6,7 +6,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::cell::Cell; use std::slice; use crate::imp_prelude::*; @@ -14,6 +13,7 @@ use crate::imp_prelude::*; use crate::{Baseiter, ElementsBase, ElementsBaseMut, Iter, IterMut}; use crate::iter::{self, AxisIter, AxisIterMut}; +use crate::math_cell::MathCell; use crate::IndexLonger; /// Methods for read-only array views. @@ -125,12 +125,12 @@ where /// /// The view acts "as if" the elements are temporarily in cells, and elements /// can be changed through shared references using the regular cell methods. - pub fn into_cell_view(self) -> ArrayView<'a, Cell, D> { + pub fn into_cell_view(self) -> ArrayView<'a, MathCell, D> { // safety: valid because - // A and Cell have the same representation - // &'a mut T is interchangeable with &'a Cell -- see method Cell::from_mut + // A and MathCell have the same representation + // &'a mut T is interchangeable with &'a Cell -- see method Cell::from_mut in std unsafe { - self.into_raw_view_mut().cast::>().deref_into_view() + self.into_raw_view_mut().cast::>().deref_into_view() } } } diff --git a/src/lib.rs b/src/lib.rs index c0a3b2e85..89862c78c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -139,6 +139,7 @@ pub use crate::linalg_traits::LinalgScalar; pub use crate::stacking::{concatenate, stack, stack_new_axis}; +pub use crate::math_cell::MathCell; pub use crate::impl_views::IndexLonger; pub use crate::shape_builder::{Shape, ShapeBuilder, StrideShape}; @@ -180,6 +181,7 @@ mod layout; mod linalg_traits; mod linspace; mod logspace; +mod math_cell; mod numeric_util; mod partial; mod shape_builder; diff --git a/src/math_cell.rs b/src/math_cell.rs new file mode 100644 index 000000000..f0f8da40b --- /dev/null +++ b/src/math_cell.rs @@ -0,0 +1,102 @@ + +use std::cell::Cell; +use std::cmp::Ordering; +use std::fmt; + +use std::ops::{Deref, DerefMut}; + +/// A transparent wrapper of [`Cell`](std::cell::Cell) which is identical in every way, except +/// it will implement arithmetic operators as well. +/// +/// The purpose of `MathCell` is to be used from [.cell_view()](crate::ArrayBase::cell_view). +/// The `MathCell` derefs to `Cell`, so all the cell's methods are available. +#[repr(transparent)] +#[derive(Default)] +pub struct MathCell(Cell); + +impl MathCell { + /// Create a new cell with the given value + #[inline(always)] + pub const fn new(value: T) -> Self { MathCell(Cell::new(value)) } + + /// Return the inner value + pub fn into_inner(self) -> T { Cell::into_inner(self.0) } + + /// Swap value with another cell + pub fn swap(&self, other: &Self) { + Cell::swap(&self.0, &other.0) + } +} + +impl Deref for MathCell { + type Target = Cell; + #[inline(always)] + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl DerefMut for MathCell { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } +} + +impl Clone for MathCell + where T: Copy +{ + fn clone(&self) -> Self { + MathCell::new(self.get()) + } +} + +impl PartialEq for MathCell + where T: Copy + PartialEq +{ + fn eq(&self, rhs: &Self) -> bool { + self.get() == rhs.get() + } +} + +impl Eq for MathCell + where T: Copy + Eq +{ } + +impl PartialOrd for MathCell + where T: Copy + PartialOrd +{ + fn partial_cmp(&self, rhs: &Self) -> Option { + self.get().partial_cmp(&rhs.get()) + } + + fn lt(&self, rhs: &Self) -> bool { self.get().lt(&rhs.get()) } + fn le(&self, rhs: &Self) -> bool { self.get().le(&rhs.get()) } + fn gt(&self, rhs: &Self) -> bool { self.get().gt(&rhs.get()) } + fn ge(&self, rhs: &Self) -> bool { self.get().ge(&rhs.get()) } +} + +impl Ord for MathCell + where T: Copy + Ord +{ + fn cmp(&self, rhs: &Self) -> Ordering { + self.get().cmp(&rhs.get()) + } +} + +impl fmt::Debug for MathCell + where T: Copy + fmt::Debug +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.get().fmt(f) + } +} + + +#[cfg(test)] +mod tests { + use super::MathCell; + + #[test] + fn test_basic() { + let c = &MathCell::new(0); + c.set(1); + assert_eq!(c.get(), 1); + } +} From df97061f4703b6ec7832428381e4a1da74a246e5 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 8 Jan 2021 20:57:55 +0100 Subject: [PATCH 141/651] DOC: Add draft of release notes for next release --- RELEASES.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 95d821715..2f4104730 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,56 @@ +Version 0.15.0 (Not released yet) +================================= + +New features +------------ + +- Support for compiling ndarray as `no_std` (using core and alloc) by [@xd009642] + + https://github.com/rust-ndarray/ndarray/pull/861 + +- New methods `.cell_view()` and `ArrayViewMut::into_cell_view` that enable + new ways of working with array elements as if they were in Cells - setting + elements through shared views and broadcast views. + + https://github.com/rust-ndarray/ndarray/pull/877 + + +Enhancements +------------ + +- Fix `Zip` for the 0-dimensional case by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/862 + +API changes +----------- + +- Removed deprecated methods by [@bluss]: + + - Remove deprecated `.all_close()` - use approx feature and methods like `.abs_diff_eq` instead + - Mark `.scalar_sum()` as deprecated - use `.sum()` instead + - Remove deprecated `DataClone` - use `Data + RawDataClone` instead + - Remove deprecated `ArrayView::into_slice` - use `to_slice()` instead. + + https://github.com/rust-ndarray/ndarray/pull/874 + +- Remove deprecated methods: rows, cols (for row and column count; the new + names are nrows and ncols) by [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/872 + +- Renamed methods (old names are now deprecated) by [@bluss] + + - `genrows/_mut` -> `rows/_mut` + - `gencolumns/_mut` -> `columns/_mut` + + https://github.com/rust-ndarray/ndarray/pull/872 + +Other changes +------------- + + + Version 0.14.0 (2020-11-28) =========================== From 9758af7ea192857741e2f7e024316f348b506afa Mon Sep 17 00:00:00 2001 From: Kirill Dubovikov Date: Fri, 8 Jan 2021 23:25:54 +0300 Subject: [PATCH 142/651] Added implementation of `var` and `std` methods for ArrayBase (#790) Add implementation of `var` and `std` methods that are a single-dimensional versions of `var_axis` and `std_axis` methods Co-authored-by: Kirill Dubovikov --- src/numeric/impl_numeric.rs | 110 ++++++++++++++++++++++++++++++++++++ src/private.rs | 2 +- tests/numeric.rs | 66 ++++++++++++++++++++++ 3 files changed, 177 insertions(+), 1 deletion(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 6fc4f6cf4..a3858f829 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -13,6 +13,8 @@ use crate::imp_prelude::*; use crate::itertools::enumerate; use crate::numeric_util; +use crate::{FoldWhile, Zip}; + /// # Numerical Methods for Arrays impl ArrayBase where @@ -111,6 +113,114 @@ where sum } + /// Return variance of elements in the array. + /// + /// The variance is computed using the [Welford one-pass + /// algorithm](https://www.jstor.org/stable/1266577). + /// + /// The parameter `ddof` specifies the "delta degrees of freedom". For + /// example, to calculate the population variance, use `ddof = 0`, or to + /// calculate the sample variance, use `ddof = 1`. + /// + /// The variance is defined as: + /// + /// ```text + /// 1 n + /// variance = ―――――――― ∑ (xᵢ - x̅)² + /// n - ddof i=1 + /// ``` + /// + /// where + /// + /// ```text + /// 1 n + /// x̅ = ― ∑ xᵢ + /// n i=1 + /// ``` + /// + /// and `n` is the length of the array. + /// + /// **Panics** if `ddof` is less than zero or greater than `n` + /// + /// # Example + /// + /// ``` + /// use ndarray::array; + /// use approx::assert_abs_diff_eq; + /// + /// let a = array![1., -4.32, 1.14, 0.32]; + /// let var = a.var(1.); + /// assert_abs_diff_eq!(var, 6.7331, epsilon = 1e-4); + /// ``` + pub fn var(&self, ddof: A) -> A + where + A: Float + FromPrimitive, + { + let zero = A::from_usize(0).expect("Converting 0 to `A` must not fail."); + let n = A::from_usize(self.len()).expect("Converting length to `A` must not fail."); + assert!( + !(ddof < zero || ddof > n), + "`ddof` must not be less than zero or greater than the length of \ + the axis", + ); + let dof = n - ddof; + let mut mean = A::zero(); + let mut sum_sq = A::zero(); + for (i, &x) in self.into_iter().enumerate() { + let count = A::from_usize(i + 1).expect("Converting index to `A` must not fail."); + let delta = x - mean; + mean = mean + delta / count; + sum_sq = (x - mean).mul_add(delta, sum_sq); + } + sum_sq / dof + } + + /// Return standard deviation of elements in the array. + /// + /// The standard deviation is computed from the variance using + /// the [Welford one-pass algorithm](https://www.jstor.org/stable/1266577). + /// + /// The parameter `ddof` specifies the "delta degrees of freedom". For + /// example, to calculate the population standard deviation, use `ddof = 0`, + /// or to calculate the sample standard deviation, use `ddof = 1`. + /// + /// The standard deviation is defined as: + /// + /// ```text + /// ⎛ 1 n ⎞ + /// stddev = sqrt ⎜ ―――――――― ∑ (xᵢ - x̅)²⎟ + /// ⎝ n - ddof i=1 ⎠ + /// ``` + /// + /// where + /// + /// ```text + /// 1 n + /// x̅ = ― ∑ xᵢ + /// n i=1 + /// ``` + /// + /// and `n` is the length of the array. + /// + /// **Panics** if `ddof` is less than zero or greater than `n` + /// + /// # Example + /// + /// ``` + /// use ndarray::array; + /// use approx::assert_abs_diff_eq; + /// + /// let a = array![1., -4.32, 1.14, 0.32]; + /// let stddev = a.std(1.); + /// assert_abs_diff_eq!(stddev, 2.59483, epsilon = 1e-4); + /// ``` + pub fn std(&self, ddof: A) -> A + where + A: Float + FromPrimitive, + { + self.var(ddof).sqrt() + } + /// Return sum along `axis`. /// /// ``` diff --git a/src/private.rs b/src/private.rs index ea13164e4..552b31497 100644 --- a/src/private.rs +++ b/src/private.rs @@ -21,5 +21,5 @@ macro_rules! private_impl { fn __private__(&self) -> crate::private::PrivateMarker { crate::private::PrivateMarker } - } + }; } diff --git a/tests/numeric.rs b/tests/numeric.rs index 7c6f1441e..13f8433c5 100644 --- a/tests/numeric.rs +++ b/tests/numeric.rs @@ -64,6 +64,72 @@ fn sum_mean_empty() { assert_eq!(a, None); } +#[test] +fn var() { + let a = array![1., -4.32, 1.14, 0.32]; + assert_abs_diff_eq!(a.var(0.), 5.049875, epsilon = 1e-8); +} + +#[test] +#[should_panic] +fn var_negative_ddof() { + let a = array![1., 2., 3.]; + a.var(-1.); +} + +#[test] +#[should_panic] +fn var_too_large_ddof() { + let a = array![1., 2., 3.]; + a.var(4.); +} + +#[test] +fn var_nan_ddof() { + let a = Array2::::zeros((2, 3)); + let v = a.var(::std::f64::NAN); + assert!(v.is_nan()); +} + +#[test] +fn var_empty_arr() { + let a: Array1 = array![]; + assert!(a.var(0.0).is_nan()); +} + +#[test] +fn std() { + let a = array![1., -4.32, 1.14, 0.32]; + assert_abs_diff_eq!(a.std(0.), 2.24719, epsilon = 1e-5); +} + +#[test] +#[should_panic] +fn std_negative_ddof() { + let a = array![1., 2., 3.]; + a.std(-1.); +} + +#[test] +#[should_panic] +fn std_too_large_ddof() { + let a = array![1., 2., 3.]; + a.std(4.); +} + +#[test] +fn std_nan_ddof() { + let a = Array2::::zeros((2, 3)); + let v = a.std(::std::f64::NAN); + assert!(v.is_nan()); +} + +#[test] +fn std_empty_arr() { + let a: Array1 = array![]; + assert!(a.std(0.0).is_nan()); +} + #[test] #[cfg(feature = "approx")] fn var_axis() { From d27b1a9fe45053f90606bddc37f24bc5b66b2059 Mon Sep 17 00:00:00 2001 From: Sparrow Li <68270294+SparrowLii@users.noreply.github.com> Date: Sun, 10 Jan 2021 21:58:24 +0800 Subject: [PATCH 143/651] No_std support for ndarray (#864) This pr enables ndarray to be used in the no_std environment, and maintains most of the functions. Fixes #708. The `geomspace` `linspace` `logspace` `range` `var_axis` and `std_axis` methods are only available when `std` is enabled at current. And 'blas''rayon''serde' features are not available without std too. --- Cargo.toml | 9 +++++---- README.rst | 14 +++++++++++++- examples/convo.rs | 6 +++++- examples/sort-axis.rs | 4 +++- src/array_approx.rs | 1 + src/array_serde.rs | 2 ++ src/arrayformat.rs | 3 +++ src/arraytraits.rs | 1 + src/data_repr.rs | 5 +++-- src/data_traits.rs | 3 ++- src/dimension/conversion.rs | 1 + src/dimension/dim.rs | 2 +- src/dimension/dimension_trait.rs | 1 + src/dimension/dynindeximpl.rs | 4 +++- src/dimension/mod.rs | 6 +++--- src/error.rs | 2 ++ src/extension/nonnull.rs | 1 + src/free_functions.rs | 4 +++- src/geomspace.rs | 1 + src/impl_1d.rs | 1 + src/impl_constructors.rs | 13 +++++++++++-- src/impl_methods.rs | 5 +++-- src/impl_owned_array.rs | 1 + src/impl_views/conversions.rs | 2 +- src/iterators/mod.rs | 8 ++++++-- src/lib.rs | 21 +++++++++++++++++++-- src/linalg/impl_linalg.rs | 1 + src/linspace.rs | 1 + src/logspace.rs | 1 + src/numeric/impl_numeric.rs | 8 +++++++- src/partial.rs | 2 +- src/stacking.rs | 2 +- src/zip/mod.rs | 1 + tests/array-construct.rs | 1 + tests/array.rs | 5 +++++ tests/broadcast.rs | 3 +++ tests/dimension.rs | 1 + tests/iterator_chunks.rs | 1 + tests/iterators.rs | 4 ++++ tests/ixdyn.rs | 1 + tests/numeric.rs | 16 ++++++++++++++++ tests/oper.rs | 2 +- 42 files changed, 142 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b312fd4a6..5acdd7589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,19 +28,19 @@ bench = false test = true [dependencies] -num-integer = "0.1.39" +num-integer = { version = "0.1.39", default-features = false } num-traits = { version = "0.2", default-features = false } num-complex = { version = "0.3", default-features = false } rayon = { version = "1.0.3", optional = true } -approx = { version = "0.4", optional = true } +approx = { version = "0.4", optional = true , default-features = false } # Use via the `blas` crate feature! cblas-sys = { version = "0.1.4", optional = true, default-features = false } blas-src = { version = "0.6.1", optional = true, default-features = false } -matrixmultiply = { version = "0.2.0" } +matrixmultiply = { version = "0.2.0", default-features = false} serde = { version = "1.0", optional = true } rawpointer = { version = "0.2" } @@ -52,6 +52,7 @@ itertools = { version = "0.9.0", default-features = false, features = ["use_std" [features] default = ["std"] + # Enable blas usage # See README for more instructions blas = ["cblas-sys", "blas-src"] @@ -66,7 +67,7 @@ test = ["test-blas-openblas-sys"] # This feature is used for docs docs = ["approx", "serde", "rayon"] -std = ["num-traits/std"] +std = ["num-traits/std", "matrixmultiply/std"] [profile.release] [profile.bench] diff --git a/README.rst b/README.rst index df416919e..0dc32b19b 100644 --- a/README.rst +++ b/README.rst @@ -48,6 +48,19 @@ Crate Feature Flags The following crate feature flags are available. They are configured in your `Cargo.toml`. +- ``std`` + + - Rust Standard Library + + - This crate can be used without the standard library by disabling the + default `std` feature. To do so, use this in your `Cargo.toml`: + + [dependencies] + ndarray = { version = "0.x.y", default-features = false } + + - The `geomspace` `linspace` `logspace` `range` `std` `var` `var_axis` and `std_axis` + methods are only available when `std` is enabled. + - ``serde`` - Optional, compatible with Rust stable @@ -112,4 +125,3 @@ http://opensource.org/licenses/MIT, at your option. This file may not be copied, modified, or distributed except according to those terms. - diff --git a/examples/convo.rs b/examples/convo.rs index a9f073bd5..b50ab5247 100644 --- a/examples/convo.rs +++ b/examples/convo.rs @@ -1,7 +1,7 @@ #![allow(unused)] extern crate ndarray; -extern crate num_traits; +#[cfg(feature = "std")] use num_traits::Float; use ndarray::prelude::*; @@ -13,6 +13,7 @@ const SHARPEN: [[f32; 3]; 3] = [[0., -1., 0.], [-1., 5., -1.], [0., -1., 0.]]; type Kernel3x3 = [[A; 3]; 3]; #[inline(never)] +#[cfg(feature = "std")] fn conv_3x3(a: &ArrayView2<'_, F>, out: &mut ArrayViewMut2<'_, F>, kernel: &Kernel3x3) where F: Float, @@ -41,6 +42,7 @@ where } } +#[cfg(feature = "std")] fn main() { let n = 16; let mut a = Array::zeros((n, n)); @@ -61,3 +63,5 @@ fn main() { } println!("{:2}", res); } +#[cfg(not(feature = "std"))] +fn main() {} diff --git a/examples/sort-axis.rs b/examples/sort-axis.rs index eabf9bb50..ab5dfdab0 100644 --- a/examples/sort-axis.rs +++ b/examples/sort-axis.rs @@ -129,7 +129,7 @@ where } } } - +#[cfg(feature = "std")] fn main() { let a = Array::linspace(0., 63., 64).into_shape((8, 8)).unwrap(); let strings = a.map(|x| x.to_string()); @@ -143,3 +143,5 @@ fn main() { let c = strings.permute_axis(Axis(1), &perm); println!("{:?}", c); } +#[cfg(not(feature = "std"))] +fn main() {} diff --git a/src/array_approx.rs b/src/array_approx.rs index 82c95a224..a2e9c2327 100644 --- a/src/array_approx.rs +++ b/src/array_approx.rs @@ -81,6 +81,7 @@ where #[cfg(test)] mod tests { use crate::prelude::*; + use alloc::vec; use approx::{ assert_abs_diff_eq, assert_abs_diff_ne, assert_relative_eq, assert_relative_ne, assert_ulps_eq, assert_ulps_ne, diff --git a/src/array_serde.rs b/src/array_serde.rs index c8b485d26..41f7d60c1 100644 --- a/src/array_serde.rs +++ b/src/array_serde.rs @@ -11,6 +11,8 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use std::marker::PhantomData; +use alloc::format; +use alloc::vec::Vec; use crate::imp_prelude::*; diff --git a/src/arrayformat.rs b/src/arrayformat.rs index a7203b38a..de6caf12c 100644 --- a/src/arrayformat.rs +++ b/src/arrayformat.rs @@ -8,6 +8,9 @@ use super::{ArrayBase, ArrayView, Axis, Data, Dimension, NdProducer}; use crate::aliases::{Ix1, IxDyn}; use std::fmt; +use alloc::format; +use alloc::string::String; +use alloc::vec::Vec; /// Default threshold, below this element count, we don't ellipsize const ARRAY_MANY_ELEMENT_LIMIT: usize = 500; diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 143bb5faf..449d57781 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -12,6 +12,7 @@ use std::iter::FromIterator; use std::iter::IntoIterator; use std::mem; use std::ops::{Index, IndexMut}; +use alloc::vec::Vec; use crate::imp_prelude::*; use crate::iter::{Iter, IterMut}; diff --git a/src/data_repr.rs b/src/data_repr.rs index 56682d9ba..b34f0a4ca 100644 --- a/src/data_repr.rs +++ b/src/data_repr.rs @@ -1,8 +1,9 @@ - use std::mem; use std::mem::ManuallyDrop; use std::ptr::NonNull; -use std::slice; +use alloc::slice; +use alloc::borrow::ToOwned; +use alloc::vec::Vec; use crate::extension::nonnull; /// Array's representation. diff --git a/src/data_traits.rs b/src/data_traits.rs index 057ef9249..3d6ce88b6 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -11,7 +11,8 @@ use rawpointer::PointerExt; use std::mem::{self, size_of}; use std::ptr::NonNull; -use std::sync::Arc; +use alloc::sync::Arc; +use alloc::vec::Vec; use crate::{ ArrayBase, CowRepr, Dimension, OwnedArcRepr, OwnedRepr, RawViewRepr, ViewRepr, diff --git a/src/dimension/conversion.rs b/src/dimension/conversion.rs index bf48dae2f..6b53a4eef 100644 --- a/src/dimension/conversion.rs +++ b/src/dimension/conversion.rs @@ -10,6 +10,7 @@ use num_traits::Zero; use std::ops::{Index, IndexMut}; +use alloc::vec::Vec; use crate::{Dim, Dimension, Ix, Ix1, IxDyn, IxDynImpl}; diff --git a/src/dimension/dim.rs b/src/dimension/dim.rs index 3f47e15ae..97de6d842 100644 --- a/src/dimension/dim.rs +++ b/src/dimension/dim.rs @@ -6,8 +6,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::fmt; +use std::fmt; use super::Dimension; use super::IntoDimension; use crate::itertools::zip; diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index b2ef92e43..d98eedd65 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -9,6 +9,7 @@ use std::fmt::Debug; use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; use std::ops::{Index, IndexMut}; +use alloc::vec::Vec; use super::axes_of; use super::conversion::Convert; diff --git a/src/dimension/dynindeximpl.rs b/src/dimension/dynindeximpl.rs index 76087aa52..5c9cd0d61 100644 --- a/src/dimension/dynindeximpl.rs +++ b/src/dimension/dynindeximpl.rs @@ -1,7 +1,9 @@ use crate::imp_prelude::*; use std::hash::{Hash, Hasher}; use std::ops::{Deref, DerefMut, Index, IndexMut}; - +use alloc::vec; +use alloc::boxed::Box; +use alloc::vec::Vec; const CAP: usize = 4; /// T is usize or isize diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 9ee1f44d6..b4610f0dc 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -676,7 +676,7 @@ mod test { #[test] fn slice_indexing_uncommon_strides() { - let v: Vec<_> = (0..12).collect(); + let v: alloc::vec::Vec<_> = (0..12).collect(); let dim = (2, 3, 2).into_dimension(); let strides = (1, 2, 6).into_dimension(); assert!(super::can_index_slice(&v, &dim, &strides).is_ok()); @@ -784,7 +784,7 @@ mod test { } quickcheck! { - fn can_index_slice_not_custom_same_as_can_index_slice(data: Vec, dim: Vec) -> bool { + fn can_index_slice_not_custom_same_as_can_index_slice(data: alloc::vec::Vec, dim: alloc::vec::Vec) -> bool { let dim = IxDyn(&dim); let result = can_index_slice_not_custom(data.len(), &dim); if dim.size_checked().is_none() { @@ -871,7 +871,7 @@ mod test { let (min2, max2) = (cmp::min(first2, last2), cmp::max(first2, last2)); // Naively determine if the sequences intersect. - let seq1: Vec<_> = (0..len1) + let seq1: alloc::vec::Vec<_> = (0..len1) .map(|n| first1 + step1 * n) .collect(); let intersects = (0..len2) diff --git a/src/error.rs b/src/error.rs index 090937561..c45496142 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. use super::Dimension; +#[cfg(feature = "std")] use std::error::Error; use std::fmt; @@ -69,6 +70,7 @@ impl PartialEq for ShapeError { } } +#[cfg(feature = "std")] impl Error for ShapeError {} impl fmt::Display for ShapeError { diff --git a/src/extension/nonnull.rs b/src/extension/nonnull.rs index 32fbb07c4..5aa50fdc2 100644 --- a/src/extension/nonnull.rs +++ b/src/extension/nonnull.rs @@ -1,4 +1,5 @@ use std::ptr::NonNull; +use alloc::vec::Vec; /// Return a NonNull pointer to the vector's data pub(crate) fn nonnull_from_vec_data(v: &mut Vec) -> NonNull { diff --git a/src/free_functions.rs b/src/free_functions.rs index ff7984ee6..95eb7dc81 100644 --- a/src/free_functions.rs +++ b/src/free_functions.rs @@ -7,7 +7,9 @@ // except according to those terms. use std::mem::{forget, size_of}; -use std::slice; +use alloc::slice; +use alloc::vec; +use alloc::vec::Vec; use crate::imp_prelude::*; use crate::{dimension, ArcArray1, ArcArray2}; diff --git a/src/geomspace.rs b/src/geomspace.rs index 06242f68e..c1935c71e 100644 --- a/src/geomspace.rs +++ b/src/geomspace.rs @@ -5,6 +5,7 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. +#![cfg(feature = "std")] use num_traits::Float; /// An iterator of a sequence of geometrically spaced floats. diff --git a/src/impl_1d.rs b/src/impl_1d.rs index fa877eff0..704d8643d 100644 --- a/src/impl_1d.rs +++ b/src/impl_1d.rs @@ -7,6 +7,7 @@ // except according to those terms. //! Methods for one-dimensional arrays. +use alloc::vec::Vec; use crate::imp_prelude::*; /// # Methods For 1-D Arrays diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 4353a827e..9b84f3105 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -11,9 +11,12 @@ //! #![allow(clippy::match_wild_err_arm)] - -use num_traits::{Float, One, Zero}; +#[cfg(feature = "std")] +use num_traits::Float; +use num_traits::{One, Zero}; use std::mem::MaybeUninit; +use alloc::vec; +use alloc::vec::Vec; use crate::dimension; use crate::error::{self, ShapeError}; @@ -23,8 +26,10 @@ use crate::indexes; use crate::indices; use crate::iterators::{to_vec, to_vec_mapped}; use crate::StrideShape; +#[cfg(feature = "std")] use crate::{geomspace, linspace, logspace}; + /// # Constructor Methods for Owned Arrays /// /// Note that the constructor methods apply to `Array` and `ArcArray`, @@ -66,6 +71,7 @@ where /// let array = Array::linspace(0., 1., 5); /// 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, @@ -84,6 +90,7 @@ where /// let array = Array::range(0., 5., 1.); /// assert!(array == arr1(&[0., 1., 2., 3., 4.])) /// ``` + #[cfg(feature = "std")] pub fn range(start: A, end: A, step: A) -> Self where A: Float, @@ -112,6 +119,7 @@ where /// assert_abs_diff_eq!(array, arr1(&[-1e3, -1e2, -1e1, -1e0])); /// # } /// ``` + #[cfg(feature = "std")] pub fn logspace(base: A, start: A, end: A, n: usize) -> Self where A: Float, @@ -146,6 +154,7 @@ where /// # /// # example().unwrap(); /// ``` + #[cfg(feature = "std")] pub fn geomspace(start: A, end: A, n: usize) -> Option where A: Float, diff --git a/src/impl_methods.rs b/src/impl_methods.rs index fd89df211..3afb68147 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -7,8 +7,9 @@ // except according to those terms. use std::ptr as std_ptr; -use std::slice; - +use alloc::slice; +use alloc::vec; +use alloc::vec::Vec; use rawpointer::PointerExt; use crate::imp_prelude::*; diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index f219b7b69..41eff2b11 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -1,4 +1,5 @@ +use alloc::vec::Vec; use crate::imp_prelude::*; /// Methods specific to `Array0`. diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs index 8c4230108..cfd7f9aa0 100644 --- a/src/impl_views/conversions.rs +++ b/src/impl_views/conversions.rs @@ -6,7 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::slice; +use alloc::slice; use crate::imp_prelude::*; diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 8cb7a9088..595f0897d 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -16,6 +16,7 @@ mod windows; use std::iter::FromIterator; use std::marker::PhantomData; use std::ptr; +use alloc::vec::Vec; use crate::Ix1; @@ -1446,10 +1447,13 @@ pub unsafe trait TrustedIterator {} use crate::indexes::IndicesIterF; use crate::iter::IndicesIter; +#[cfg(feature = "std")] use crate::{geomspace::Geomspace, linspace::Linspace, logspace::Logspace}; - -unsafe impl TrustedIterator for Geomspace {} +#[cfg(feature = "std")] unsafe impl TrustedIterator for Linspace {} +#[cfg(feature = "std")] +unsafe impl TrustedIterator for Geomspace {} +#[cfg(feature = "std")] unsafe impl TrustedIterator for Logspace {} unsafe impl<'a, A, D> TrustedIterator for Iter<'a, A, D> {} unsafe impl<'a, A, D> TrustedIterator for IterMut<'a, A, D> {} diff --git a/src/lib.rs b/src/lib.rs index e49445a82..6ca3c0be0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ clippy::deref_addrof, clippy::unreadable_literal )] +#![cfg_attr(not(feature = "std"), no_std)] //! The `ndarray` crate provides an *n*-dimensional container for general elements //! and for numerics. @@ -68,6 +69,14 @@ //! The following crate feature flags are available. They are configured in your //! `Cargo.toml`. //! +//! - `std` +//! - Rust Standard Library +//! - This crate can be used without the standard library by disabling the +//! default `std` feature. To do so, use `default-features = false` in +//! your `Cargo.toml`. +//! - The `geomspace` `linspace` `logspace` `range` `std` `var` `var_axis` +//! and `std_axis` methods are only available when `std` is +//! enabled. //! - `serde` //! - Optional, compatible with Rust stable //! - Enables serialization support for serde 1.x @@ -109,6 +118,14 @@ //! For conversion between `ndarray`, [`nalgebra`](https://crates.io/crates/nalgebra) and //! [`image`](https://crates.io/crates/image) check out [`nshare`](https://crates.io/crates/nshare). + +extern crate alloc; + +#[cfg(feature = "std")] +extern crate std; +#[cfg(not(feature = "std"))] +extern crate core as std; + #[cfg(feature = "blas")] extern crate blas_src; #[cfg(feature = "blas")] @@ -118,7 +135,7 @@ extern crate cblas_sys; pub mod doc; use std::marker::PhantomData; -use std::sync::Arc; +use alloc::sync::Arc; pub use crate::dimension::dim::*; pub use crate::dimension::{Axis, AxisDescription, Dimension, IntoDimension, RemoveAxis}; @@ -135,7 +152,7 @@ use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes, Lane pub use crate::arraytraits::AsArray; #[cfg(feature = "std")] pub use crate::linalg_traits::NdFloat; -pub use crate::linalg_traits::LinalgScalar; +pub use crate::linalg_traits::LinalgScalar; pub use crate::stacking::{concatenate, stack, stack_new_axis}; diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index f54f14431..5201d55e2 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -12,6 +12,7 @@ use crate::numeric_util; use crate::{LinalgScalar, Zip}; use std::any::TypeId; +use alloc::vec::Vec; #[cfg(feature = "blas")] use std::cmp; diff --git a/src/linspace.rs b/src/linspace.rs index ca0eae470..6e9b1203c 100644 --- a/src/linspace.rs +++ b/src/linspace.rs @@ -5,6 +5,7 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. +#![cfg(feature = "std")] use num_traits::Float; /// An iterator of a sequence of evenly spaced floats. diff --git a/src/logspace.rs b/src/logspace.rs index 55b5397c8..4dc6e1f32 100644 --- a/src/logspace.rs +++ b/src/logspace.rs @@ -5,6 +5,7 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. +#![cfg(feature = "std")] use num_traits::Float; /// An iterator of a sequence of logarithmically spaced number. diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index a3858f829..3d7e15d0d 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -6,7 +6,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use num_traits::{self, Float, FromPrimitive, Zero}; +#[cfg(feature = "std")] +use num_traits::Float; +use num_traits::{self, FromPrimitive, Zero}; use std::ops::{Add, Div, Mul}; use crate::imp_prelude::*; @@ -152,6 +154,7 @@ where /// let var = a.var(1.); /// assert_abs_diff_eq!(var, 6.7331, epsilon = 1e-4); /// ``` + #[cfg(feature = "std")] pub fn var(&self, ddof: A) -> A where A: Float + FromPrimitive, @@ -214,6 +217,7 @@ where /// let stddev = a.std(1.); /// assert_abs_diff_eq!(stddev, 2.59483, epsilon = 1e-4); /// ``` + #[cfg(feature = "std")] pub fn std(&self, ddof: A) -> A where A: Float + FromPrimitive, @@ -337,6 +341,7 @@ where /// let var = a.var_axis(Axis(0), 1.); /// assert_eq!(var, aview1(&[4., 4.])); /// ``` + #[cfg(feature = "std")] pub fn var_axis(&self, axis: Axis, ddof: A) -> Array where A: Float + FromPrimitive, @@ -405,6 +410,7 @@ where /// let stddev = a.std_axis(Axis(0), 1.); /// assert_eq!(stddev, aview1(&[2., 2.])); /// ``` + #[cfg(feature = "std")] pub fn std_axis(&self, axis: Axis, ddof: A) -> Array where A: Float + FromPrimitive, diff --git a/src/partial.rs b/src/partial.rs index 887e93824..a8146aff0 100644 --- a/src/partial.rs +++ b/src/partial.rs @@ -81,7 +81,7 @@ impl Drop for Partial { fn drop(&mut self) { if !self.ptr.is_null() { unsafe { - ptr::drop_in_place(std::slice::from_raw_parts_mut(self.ptr, self.len)); + ptr::drop_in_place(alloc::slice::from_raw_parts_mut(self.ptr, self.len)); } } } diff --git a/src/stacking.rs b/src/stacking.rs index 604a7cc5b..39126a2dc 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -5,7 +5,7 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. - +use alloc::vec::Vec; use crate::error::{from_kind, ErrorKind, ShapeError}; use crate::imp_prelude::*; use crate::traversal_utils::assign_to; diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 835ae5d31..3c61d8110 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -10,6 +10,7 @@ mod zipmacro; use std::mem::MaybeUninit; +use alloc::vec::Vec; use crate::imp_prelude::*; use crate::AssignElem; diff --git a/tests/array-construct.rs b/tests/array-construct.rs index 738e3b1fc..4513dd453 100644 --- a/tests/array-construct.rs +++ b/tests/array-construct.rs @@ -51,6 +51,7 @@ fn test_arcarray_thread_safe() { } #[test] +#[cfg(feature = "std")] fn test_uninit() { unsafe { let mut a = Array::::uninitialized((3, 4).f()); diff --git a/tests/array.rs b/tests/array.rs index 3d917f72b..c1976511c 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -67,6 +67,7 @@ fn arrayviewmut_shrink_lifetime<'a, 'b: 'a>( } #[test] +#[cfg(feature = "std")] fn test_mat_mul() { // smoke test, a big matrix multiplication of uneven size let (n, m) = (45, 33); @@ -609,6 +610,7 @@ fn test_cow_shrink() { } #[test] +#[cfg(feature = "std")] fn test_sub() { let mat = ArcArray::linspace(0., 15., 16).reshape((2, 4, 2)); let s1 = mat.index_axis(Axis(0), 0); @@ -623,6 +625,7 @@ fn test_sub() { #[should_panic] #[test] +#[cfg(feature = "std")] fn test_sub_oob_1() { let mat = ArcArray::linspace(0., 15., 16).reshape((2, 4, 2)); mat.index_axis(Axis(0), 2); @@ -1610,6 +1613,7 @@ fn scalar_ops() { } #[test] +#[cfg(feature = "std")] fn split_at() { let mut a = arr2(&[[1., 2.], [3., 4.]]); @@ -1661,6 +1665,7 @@ fn deny_split_at_index_out_of_bounds() { } #[test] +#[cfg(feature = "std")] fn test_range() { let a = Array::range(0., 5., 1.); assert_eq!(a.len(), 5); diff --git a/tests/broadcast.rs b/tests/broadcast.rs index 6840947bb..5416e9017 100644 --- a/tests/broadcast.rs +++ b/tests/broadcast.rs @@ -1,6 +1,7 @@ use ndarray::prelude::*; #[test] +#[cfg(feature = "std")] fn broadcast_1() { let a_dim = Dim([2, 4, 2, 2]); let b_dim = Dim([2, 1, 2, 1]); @@ -26,6 +27,7 @@ fn broadcast_1() { } #[test] +#[cfg(feature = "std")] fn test_add() { let a_dim = Dim([2, 4, 2, 2]); let b_dim = Dim([2, 1, 2, 1]); @@ -38,6 +40,7 @@ fn test_add() { #[test] #[should_panic] +#[cfg(feature = "std")] fn test_add_incompat() { let a_dim = Dim([2, 4, 2, 2]); let mut a = ArcArray::linspace(0.0, 1., a_dim.size()).reshape(a_dim); diff --git a/tests/dimension.rs b/tests/dimension.rs index 7e76132aa..cfc122e40 100644 --- a/tests/dimension.rs +++ b/tests/dimension.rs @@ -287,6 +287,7 @@ fn test_array_view() { } #[test] +#[cfg(feature = "std")] #[allow(clippy::cognitive_complexity)] fn test_all_ndindex() { macro_rules! ndindex { diff --git a/tests/iterator_chunks.rs b/tests/iterator_chunks.rs index 8ac885022..995557173 100644 --- a/tests/iterator_chunks.rs +++ b/tests/iterator_chunks.rs @@ -10,6 +10,7 @@ use ndarray::prelude::*; use ndarray::NdProducer; #[test] +#[cfg(feature = "std")] fn chunks() { let a = >::linspace(1., 100., 10 * 10) .into_shape((10, 10)) diff --git a/tests/iterators.rs b/tests/iterators.rs index 7a30003c4..7bb856adf 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -28,6 +28,7 @@ macro_rules! assert_panics { } #[test] +#[cfg(feature = "std")] fn double_ended() { let a = ArcArray::linspace(0., 7., 8); let mut it = a.iter().cloned(); @@ -58,6 +59,7 @@ fn iter_size_hint() { } #[test] +#[cfg(feature = "std")] fn indexed() { let a = ArcArray::linspace(0., 7., 8); for (i, elt) in a.indexed_iter() { @@ -90,6 +92,7 @@ where } #[test] +#[cfg(feature = "std")] fn as_slice() { let a = ArcArray::linspace(0., 7., 8); let a = a.reshape((2, 4, 1)); @@ -524,6 +527,7 @@ fn axis_iter_mut_zip_partially_consumed_discontiguous() { } #[test] +#[cfg(feature = "std")] fn axis_chunks_iter_corner_cases() { // examples provided by @bluss in PR #65 // these tests highlight corner cases of the axis_chunks_iter implementation diff --git a/tests/ixdyn.rs b/tests/ixdyn.rs index 3d96967a0..3c96cd746 100644 --- a/tests/ixdyn.rs +++ b/tests/ixdyn.rs @@ -155,6 +155,7 @@ fn test_0_add_broad() { } #[test] +#[cfg(feature = "std")] fn test_into_dimension() { let a = Array::linspace(0., 41., 6 * 7).into_shape((6, 7)).unwrap(); let a2 = a.clone().into_shape(IxDyn(&[6, 7])).unwrap(); diff --git a/tests/numeric.rs b/tests/numeric.rs index 13f8433c5..15df53287 100644 --- a/tests/numeric.rs +++ b/tests/numeric.rs @@ -65,12 +65,14 @@ fn sum_mean_empty() { } #[test] +#[cfg(feature = "std")] fn var() { let a = array![1., -4.32, 1.14, 0.32]; assert_abs_diff_eq!(a.var(0.), 5.049875, epsilon = 1e-8); } #[test] +#[cfg(feature = "std")] #[should_panic] fn var_negative_ddof() { let a = array![1., 2., 3.]; @@ -78,6 +80,7 @@ fn var_negative_ddof() { } #[test] +#[cfg(feature = "std")] #[should_panic] fn var_too_large_ddof() { let a = array![1., 2., 3.]; @@ -85,6 +88,7 @@ fn var_too_large_ddof() { } #[test] +#[cfg(feature = "std")] fn var_nan_ddof() { let a = Array2::::zeros((2, 3)); let v = a.var(::std::f64::NAN); @@ -92,18 +96,21 @@ fn var_nan_ddof() { } #[test] +#[cfg(feature = "std")] fn var_empty_arr() { let a: Array1 = array![]; assert!(a.var(0.0).is_nan()); } #[test] +#[cfg(feature = "std")] fn std() { let a = array![1., -4.32, 1.14, 0.32]; assert_abs_diff_eq!(a.std(0.), 2.24719, epsilon = 1e-5); } #[test] +#[cfg(feature = "std")] #[should_panic] fn std_negative_ddof() { let a = array![1., 2., 3.]; @@ -111,6 +118,7 @@ fn std_negative_ddof() { } #[test] +#[cfg(feature = "std")] #[should_panic] fn std_too_large_ddof() { let a = array![1., 2., 3.]; @@ -118,6 +126,7 @@ fn std_too_large_ddof() { } #[test] +#[cfg(feature = "std")] fn std_nan_ddof() { let a = Array2::::zeros((2, 3)); let v = a.std(::std::f64::NAN); @@ -125,6 +134,7 @@ fn std_nan_ddof() { } #[test] +#[cfg(feature = "std")] fn std_empty_arr() { let a: Array1 = array![]; assert!(a.std(0.0).is_nan()); @@ -249,6 +259,7 @@ fn std_axis() { #[test] #[should_panic] +#[cfg(feature = "std")] fn var_axis_negative_ddof() { let a = array![1., 2., 3.]; a.var_axis(Axis(0), -1.); @@ -256,12 +267,14 @@ fn var_axis_negative_ddof() { #[test] #[should_panic] +#[cfg(feature = "std")] fn var_axis_too_large_ddof() { let a = array![1., 2., 3.]; a.var_axis(Axis(0), 4.); } #[test] +#[cfg(feature = "std")] fn var_axis_nan_ddof() { let a = Array2::::zeros((2, 3)); let v = a.var_axis(Axis(1), ::std::f64::NAN); @@ -270,6 +283,7 @@ fn var_axis_nan_ddof() { } #[test] +#[cfg(feature = "std")] fn var_axis_empty_axis() { let a = Array2::::zeros((2, 0)); let v = a.var_axis(Axis(1), 0.); @@ -279,12 +293,14 @@ fn var_axis_empty_axis() { #[test] #[should_panic] +#[cfg(feature = "std")] fn std_axis_bad_dof() { let a = array![1., 2., 3.]; a.std_axis(Axis(0), 4.); } #[test] +#[cfg(feature = "std")] fn std_axis_empty_axis() { let a = Array2::::zeros((2, 0)); let v = a.std_axis(Axis(1), 0.); diff --git a/tests/oper.rs b/tests/oper.rs index 7193a49da..a3d179760 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -4,7 +4,7 @@ clippy::unreadable_literal, clippy::many_single_char_names )] - +#![cfg(feature = "std")] use ndarray::linalg::general_mat_mul; use ndarray::prelude::*; use ndarray::{rcarr1, rcarr2}; From 225d967e12d56ee20996db12b284eb95c38b3eb4 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 10 Jan 2021 15:18:38 +0100 Subject: [PATCH 144/651] FIX: Use .visit() for array traversal in var Using iterators for multidimensional arrays is discouraged, prefer using higher-order methods (mentioned in the module documentation's main page). --- src/numeric/impl_numeric.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 3d7e15d0d..9d1cef7e1 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -15,8 +15,6 @@ use crate::imp_prelude::*; use crate::itertools::enumerate; use crate::numeric_util; -use crate::{FoldWhile, Zip}; - /// # Numerical Methods for Arrays impl ArrayBase where @@ -169,12 +167,14 @@ where let dof = n - ddof; let mut mean = A::zero(); let mut sum_sq = A::zero(); - for (i, &x) in self.into_iter().enumerate() { + let mut i = 0; + self.visit(|&x| { let count = A::from_usize(i + 1).expect("Converting index to `A` must not fail."); let delta = x - mean; mean = mean + delta / count; sum_sq = (x - mean).mul_add(delta, sum_sq); - } + i += 1; + }); sum_sq / dof } From 45966a0f8b1556a51b081502b6eda2d0db2ffcab Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 10 Jan 2021 15:24:01 +0100 Subject: [PATCH 145/651] FIX: Fix unused imports for std/no_std --- src/arrayformat.rs | 4 ++-- src/impl_constructors.rs | 4 +++- src/linalg_traits.rs | 8 +++++++- src/stacking.rs | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/arrayformat.rs b/src/arrayformat.rs index de6caf12c..c1ca352e3 100644 --- a/src/arrayformat.rs +++ b/src/arrayformat.rs @@ -9,8 +9,6 @@ use super::{ArrayBase, ArrayView, Axis, Data, Dimension, NdProducer}; use crate::aliases::{Ix1, IxDyn}; use std::fmt; use alloc::format; -use alloc::string::String; -use alloc::vec::Vec; /// Default threshold, below this element count, we don't ellipsize const ARRAY_MANY_ELEMENT_LIMIT: usize = 500; @@ -290,6 +288,8 @@ where mod formatting_with_omit { use itertools::Itertools; use std::fmt; + use alloc::string::String; + use alloc::vec::Vec; use super::*; use crate::prelude::*; diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 9b84f3105..4d76490c8 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -24,7 +24,9 @@ use crate::extension::nonnull::nonnull_from_vec_data; use crate::imp_prelude::*; use crate::indexes; use crate::indices; -use crate::iterators::{to_vec, to_vec_mapped}; +#[cfg(feature = "std")] +use crate::iterators::to_vec; +use crate::iterators::to_vec_mapped; use crate::StrideShape; #[cfg(feature = "std")] use crate::{geomspace, linspace, logspace}; diff --git a/src/linalg_traits.rs b/src/linalg_traits.rs index 43fe5f8d3..d52e7f54f 100644 --- a/src/linalg_traits.rs +++ b/src/linalg_traits.rs @@ -5,14 +5,20 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::ScalarOperand; + #[cfg(feature = "std")] use num_traits::Float; use num_traits::{One, Zero}; + +#[cfg(feature = "std")] use std::fmt; use std::ops::{Add, Div, Mul, Sub}; +#[cfg(feature = "std")] use std::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign}; +#[cfg(feature = "std")] +use crate::ScalarOperand; + /// Elements that support linear algebra operations. /// /// `'static` for type-based specialization, `Copy` so that they don't need move diff --git a/src/stacking.rs b/src/stacking.rs index 39126a2dc..604a7cc5b 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -5,7 +5,7 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -use alloc::vec::Vec; + use crate::error::{from_kind, ErrorKind, ShapeError}; use crate::imp_prelude::*; use crate::traversal_utils::assign_to; From 97e33c520767ff0097ebd515017a7a9806cb9487 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 10 Jan 2021 15:42:02 +0100 Subject: [PATCH 146/651] MAINT: Fix rayon, serde, blas features for no-std changes rayon: implies std, because threads to serde: works fine without std blas: works fine without std if we use libc (libc is a new dependency for this feature flag, but it's already used by blas transitively). A slight hack is used for rayon (renaming the dependency) because we need to use a feature flag to enable std in this crate too. --- Cargo.toml | 9 ++++++--- README.rst | 8 +++----- serialization-tests/Cargo.toml | 1 + src/lib.rs | 14 ++++++-------- src/linalg/impl_linalg.rs | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5acdd7589..a712783d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,16 +32,18 @@ num-integer = { version = "0.1.39", default-features = false } num-traits = { version = "0.2", default-features = false } num-complex = { version = "0.3", default-features = false } -rayon = { version = "1.0.3", optional = true } +# Use via the `rayon` crate feature! +rayon_ = { version = "1.0.3", optional = true, package = "rayon" } approx = { version = "0.4", optional = true , default-features = false } # Use via the `blas` crate feature! cblas-sys = { version = "0.1.4", optional = true, default-features = false } blas-src = { version = "0.6.1", optional = true, default-features = false } +libc = { version = "0.2.82", optional = true } matrixmultiply = { version = "0.2.0", default-features = false} -serde = { version = "1.0", optional = true } +serde = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } rawpointer = { version = "0.2" } [dev-dependencies] @@ -55,7 +57,7 @@ default = ["std"] # Enable blas usage # See README for more instructions -blas = ["cblas-sys", "blas-src"] +blas = ["cblas-sys", "blas-src", "libc"] # Old name for the serde feature serde-1 = ["serde"] @@ -68,6 +70,7 @@ test = ["test-blas-openblas-sys"] docs = ["approx", "serde", "rayon"] std = ["num-traits/std", "matrixmultiply/std"] +rayon = ["rayon_", "std"] [profile.release] [profile.bench] diff --git a/README.rst b/README.rst index 0dc32b19b..3cfcc8253 100644 --- a/README.rst +++ b/README.rst @@ -50,7 +50,7 @@ your `Cargo.toml`. - ``std`` - - Rust Standard Library + - Rust standard library (enabled by default) - This crate can be used without the standard library by disabling the default `std` feature. To do so, use this in your `Cargo.toml`: @@ -63,20 +63,18 @@ your `Cargo.toml`. - ``serde`` - - Optional, compatible with Rust stable - Enables serialization support for serde 1.x - ``rayon`` - - Optional, compatible with Rust stable - Enables parallel iterators, parallelized methods and ``par_azip!``. + - Implies std - ``blas`` - - Optional and experimental, compatible with Rust stable - Enable transparent BLAS support for matrix multiplication. Uses ``blas-src`` for pluggable backend, which needs to be configured - separately. + separately (see below). How to use with cargo --------------------- diff --git a/serialization-tests/Cargo.toml b/serialization-tests/Cargo.toml index 3aaad639c..973a688fe 100644 --- a/serialization-tests/Cargo.toml +++ b/serialization-tests/Cargo.toml @@ -15,6 +15,7 @@ default = ["ron"] [dev-dependencies.serde] version = "1.0.100" +default-features = false [dev-dependencies.serde_json] version = "1.0.40" diff --git a/src/lib.rs b/src/lib.rs index 6ca3c0be0..11064d32d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,27 +70,23 @@ //! `Cargo.toml`. //! //! - `std` -//! - Rust Standard Library +//! - Rust standard library (enabled by default) //! - This crate can be used without the standard library by disabling the //! default `std` feature. To do so, use `default-features = false` in //! your `Cargo.toml`. //! - The `geomspace` `linspace` `logspace` `range` `std` `var` `var_axis` -//! and `std_axis` methods are only available when `std` is -//! enabled. +//! and `std_axis` methods are only available when `std` is enabled. //! - `serde` -//! - Optional, compatible with Rust stable //! - Enables serialization support for serde 1.x //! - `rayon` -//! - Optional, compatible with Rust stable //! - Enables parallel iterators, parallelized methods and [`par_azip!`]. +//! - Implies std //! - `approx` -//! - Optional, compatible with Rust stable //! - Enables implementations of traits from the [`approx`] crate. //! - `blas` -//! - Optional and experimental, compatible with Rust stable //! - Enable transparent BLAS support for matrix multiplication. //! Uses ``blas-src`` for pluggable backend, which needs to be configured -//! separately. +//! separately (see the README). //! //! ## Documentation //! @@ -1594,6 +1590,8 @@ where // parallel methods #[cfg(feature = "rayon")] +extern crate rayon_ as rayon; +#[cfg(feature = "rayon")] pub mod parallel; mod impl_1d; diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index 5201d55e2..c227a68aa 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -19,7 +19,7 @@ use std::cmp; #[cfg(feature = "blas")] use std::mem::swap; #[cfg(feature = "blas")] -use std::os::raw::c_int; +use libc::c_int; #[cfg(feature = "blas")] use cblas_sys as blas_sys; From 56177243f1dbc68c705b5dca859252da5f81d803 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 8 Jan 2021 23:53:16 +0100 Subject: [PATCH 147/651] FEAT: Update to matrixmultiply 0.3 This version supports multithreading with the threading feature. --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a712783d0..b225418f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,8 @@ cblas-sys = { version = "0.1.4", optional = true, default-features = false } blas-src = { version = "0.6.1", optional = true, default-features = false } libc = { version = "0.2.82", optional = true } -matrixmultiply = { version = "0.2.0", default-features = false} +matrixmultiply = { version = "0.3.0", default-features = false} + serde = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } rawpointer = { version = "0.2" } From dcf38e8bb56141abe7b021a05f307ada952612b7 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 15 Feb 2020 13:21:23 -0500 Subject: [PATCH 148/651] FEAT: Performance improvement for &Array + scalar and scalar + &Array * The new implementation avoids cloning the elements twice, and it avoids iterating over the elements twice. (The old implementation called `.to_owned()` followed by the arithmetic operation, while the new implementation clones the elements and performs the arithmetic operation in the same iteration.) On my machine, this change improves the performance for both contiguous and discontiguous arrays. (`scalar_add_1/2` go from ~530 ns/iter to ~380 ns/iter, and `scalar_add_strided_1/2` go from ~1540 ns/iter to ~1420 ns/iter.) (Other changes to impl applicability removed from this commit.) Co-authored-by: bluss --- src/impl_ops.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 4804356e8..51d432ee6 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -159,8 +159,8 @@ impl<'a, A, S, D, B> $trt for &'a ArrayBase B: ScalarOperand, { type Output = Array; - fn $mth(self, x: B) -> Array { - self.to_owned().$mth(x) + fn $mth(self, x: B) -> Self::Output { + self.map(move |elt| elt.clone() $operator x.clone()) } } ); @@ -210,11 +210,11 @@ impl<'a, S, D> $trt<&'a ArrayBase> for $scalar D: Dimension, { type Output = Array<$scalar, D>; - fn $mth(self, rhs: &ArrayBase) -> Array<$scalar, D> { + fn $mth(self, rhs: &ArrayBase) -> Self::Output { if_commutative!($commutative { rhs.$mth(self) } or { - self.$mth(rhs.to_owned()) + rhs.map(move |elt| self.clone() $operator elt.clone()) }) } } From 524079b772b8fce0537161f3ec5d782df3c4d915 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 10 Jan 2021 22:35:48 +0100 Subject: [PATCH 149/651] API: Update blas-src dep to 0.7.0 --- Cargo.toml | 2 +- README.rst | 13 +++++++------ blas-tests/Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b225418f1..301d1b566 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ approx = { version = "0.4", optional = true , default-features = false } # Use via the `blas` crate feature! cblas-sys = { version = "0.1.4", optional = true, default-features = false } -blas-src = { version = "0.6.1", optional = true, default-features = false } +blas-src = { version = "0.7.0", optional = true, default-features = false } libc = { version = "0.2.82", optional = true } matrixmultiply = { version = "0.3.0", default-features = false} diff --git a/README.rst b/README.rst index 3cfcc8253..05ff48fb8 100644 --- a/README.rst +++ b/README.rst @@ -86,14 +86,14 @@ How to use with cargo How to enable blas integration. Depend on ``blas-src`` directly to pick a blas provider. Depend on the same ``blas-src`` version as ``ndarray`` does, for the -selection to work. A proposed configuration using system openblas is shown +selection to work. An example configuration using system openblas is shown below. Note that only end-user projects (not libraries) should select provider:: [dependencies] ndarray = { version = "0.14.0", features = ["blas"] } - blas-src = { version = "0.6.1", default-features = false, features = ["openblas"] } + blas-src = { version = "0.7.0", default-features = false, features = ["openblas"] } openblas-src = { version = "0.9", default-features = false, features = ["cblas", "system"] } For official releases of ``ndarray``, the versions are: @@ -101,10 +101,11 @@ For official releases of ``ndarray``, the versions are: =========== ============ ================ ``ndarray`` ``blas-src`` ``openblas-src`` =========== ============ ================ -0.14.0 0.6.1 0.9.0 -0.13.0 0.2.0 0.6.0 -0.12.\* 0.2.0 0.6.0 -0.11.\* 0.1.2 0.5.0 +0.15 0.7.0 0.9.0 +0.14 0.6.1 0.9.0 +0.13 0.2.0 0.6.0 +0.12 0.2.0 0.6.0 +0.11 0.1.2 0.5.0 =========== ============ ================ Recent Changes diff --git a/blas-tests/Cargo.toml b/blas-tests/Cargo.toml index da9aad98f..4d0792fd0 100644 --- a/blas-tests/Cargo.toml +++ b/blas-tests/Cargo.toml @@ -10,7 +10,7 @@ test = false [dev-dependencies] approx = "0.4" ndarray = { path = "../", features = ["approx", "blas"] } -blas-src = { version = "0.6.1", default-features = false, features = ["openblas"] } +blas-src = { version = "0.7.0", default-features = false, features = ["openblas"] } openblas-src = { version = "0.9.0", default-features = false, features = ["cblas", "system"] } defmac = "0.2" num-traits = "0.2" From f0dafb39bf48c1d3a9ab40299d24993b4889699a Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Wed, 6 Jan 2021 16:00:53 +0800 Subject: [PATCH 150/651] FEAT: Fix memory continuity judgment when stride is negative --- blas-tests/tests/oper.rs | 4 ++-- src/dimension/dimension_trait.rs | 15 ++++++------ src/dimension/mod.rs | 39 ++++++++++++++++++++------------ src/impl_constructors.rs | 10 +++++--- src/impl_methods.rs | 24 +++++++++++++------- tests/dimension.rs | 19 +++++++++++++++- tests/oper.rs | 4 ++-- 7 files changed, 76 insertions(+), 39 deletions(-) diff --git a/blas-tests/tests/oper.rs b/blas-tests/tests/oper.rs index 2741123f9..b7c2d769d 100644 --- a/blas-tests/tests/oper.rs +++ b/blas-tests/tests/oper.rs @@ -173,7 +173,7 @@ where S2: Data, { let ((m, _), k) = (lhs.dim(), rhs.dim()); - reference_mat_mul(lhs, &rhs.to_owned().into_shape((k, 1)).unwrap()) + reference_mat_mul(lhs, &rhs.as_standard_layout().into_shape((k, 1)).unwrap()) .into_shape(m) .unwrap() } @@ -186,7 +186,7 @@ where S2: Data, { let (m, (_, n)) = (lhs.dim(), rhs.dim()); - reference_mat_mul(&lhs.to_owned().into_shape((1, m)).unwrap(), rhs) + reference_mat_mul(&lhs.as_standard_layout().into_shape((1, m)).unwrap(), rhs) .into_shape(n) .unwrap() } diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index b2ef92e43..e38fa75af 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -286,17 +286,16 @@ pub trait Dimension: return true; } if dim.ndim() == 1 { - return false; + return strides[0] as isize == -1; } let order = strides._fastest_varying_stride_order(); let strides = strides.slice(); - // FIXME: Negative strides let dim_slice = dim.slice(); let mut cstride = 1; for &i in order.slice() { // a dimension of length 1 can have unequal strides - if dim_slice[i] != 1 && strides[i] != cstride { + if dim_slice[i] != 1 && (strides[i] as isize).abs() as usize != cstride { return false; } cstride *= dim_slice[i]; @@ -307,8 +306,7 @@ pub trait Dimension: /// Return the axis ordering corresponding to the fastest variation /// (in ascending order). /// - /// Assumes that no stride value appears twice. This cannot yield the correct - /// result the strides are not positive. + /// Assumes that no stride value appears twice. #[doc(hidden)] fn _fastest_varying_stride_order(&self) -> Self { let mut indices = self.clone(); @@ -316,7 +314,8 @@ pub trait Dimension: *elt = i; } let strides = self.slice(); - indices.slice_mut().sort_by_key(|&i| strides[i]); + indices.slice_mut() + .sort_by_key(|&i| (strides[i] as isize).abs()); indices } @@ -645,7 +644,7 @@ impl Dimension for Dim<[Ix; 2]> { #[inline] fn _fastest_varying_stride_order(&self) -> Self { - if get!(self, 0) as Ixs <= get!(self, 1) as Ixs { + if (get!(self, 0) as Ixs).abs() <= (get!(self, 1) as Ixs).abs() { Ix2(0, 1) } else { Ix2(1, 0) @@ -805,7 +804,7 @@ impl Dimension for Dim<[Ix; 3]> { let mut order = Ix3(0, 1, 2); macro_rules! swap { ($stride:expr, $order:expr, $x:expr, $y:expr) => { - if $stride[$x] > $stride[$y] { + if ($stride[$x] as isize).abs() > ($stride[$y] as isize).abs() { $stride.swap($x, $y); $order.ixm().swap($x, $y); } diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 9ee1f44d6..36f5720d3 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -46,15 +46,12 @@ pub fn stride_offset(n: Ix, stride: Ix) -> isize { /// There is overlap if, when iterating through the dimensions in order of /// increasing stride, the current stride is less than or equal to the maximum /// possible offset along the preceding axes. (Axes of length ≤1 are ignored.) -/// -/// The current implementation assumes that strides of axes with length > 1 are -/// nonnegative. Additionally, it does not check for overflow. pub fn dim_stride_overlap(dim: &D, strides: &D) -> bool { let order = strides._fastest_varying_stride_order(); let mut sum_prev_offsets = 0; for &index in order.slice() { let d = dim[index]; - let s = strides[index] as isize; + let s = (strides[index] as isize).abs(); match d { 0 => return false, 1 => {} @@ -210,8 +207,7 @@ where /// /// 2. The product of non-zero axis lengths must not exceed `isize::MAX`. /// -/// 3. For axes with length > 1, the stride must be nonnegative. This is -/// necessary to make sure the pointer cannot move backwards outside the +/// 3. For axes with length > 1, the pointer cannot move outside the /// slice. For axes with length ≤ 1, the stride can be anything. /// /// 4. If the array will be empty (any axes are zero-length), the difference @@ -257,14 +253,6 @@ fn can_index_slice_impl( return Err(from_kind(ErrorKind::OutOfBounds)); } - // Check condition 3. - for (&d, &s) in izip!(dim.slice(), strides.slice()) { - let s = s as isize; - if d > 1 && s < 0 { - return Err(from_kind(ErrorKind::Unsupported)); - } - } - // Check condition 5. if !is_empty && dim_stride_overlap(dim, strides) { return Err(from_kind(ErrorKind::Unsupported)); @@ -394,6 +382,19 @@ fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize) { (start, end, step) } +/// This function computes the offset from the logically first element to the first element in +/// memory of the array. The result is always <= 0. +pub fn offset_from_ptr_to_memory(dim: &[Ix], strides: &[Ix]) -> isize { + let offset = izip!(dim, strides).fold(0, |_offset, (d, s)| { + if (*s as isize) < 0 { + _offset + *s as isize * (*d as isize - 1) + } else { + _offset + } + }); + offset +} + /// Modify dimension, stride and return data pointer offset /// /// **Panics** if stride is 0 or if any index is out of bounds. @@ -693,13 +694,21 @@ mod test { let dim = (2, 3, 2).into_dimension(); let strides = (5, 2, 1).into_dimension(); assert!(super::dim_stride_overlap(&dim, &strides)); + let strides = (-5isize as usize, 2, -1isize as usize).into_dimension(); + assert!(super::dim_stride_overlap(&dim, &strides)); let strides = (6, 2, 1).into_dimension(); assert!(!super::dim_stride_overlap(&dim, &strides)); + let strides = (6, -2isize as usize, 1).into_dimension(); + assert!(!super::dim_stride_overlap(&dim, &strides)); let strides = (6, 0, 1).into_dimension(); assert!(super::dim_stride_overlap(&dim, &strides)); + let strides = (-6isize as usize, 0, 1).into_dimension(); + assert!(super::dim_stride_overlap(&dim, &strides)); let dim = (2, 2).into_dimension(); let strides = (3, 2).into_dimension(); assert!(!super::dim_stride_overlap(&dim, &strides)); + let strides = (3, -2isize as usize).into_dimension(); + assert!(!super::dim_stride_overlap(&dim, &strides)); } #[test] @@ -736,7 +745,7 @@ mod test { can_index_slice::(&[1], &Ix1(2), &Ix1(1)).unwrap_err(); can_index_slice::(&[1, 2], &Ix1(2), &Ix1(0)).unwrap_err(); can_index_slice::(&[1, 2], &Ix1(2), &Ix1(1)).unwrap(); - can_index_slice::(&[1, 2], &Ix1(2), &Ix1(-1isize as usize)).unwrap_err(); + can_index_slice::(&[1, 2], &Ix1(2), &Ix1(-1isize as usize)).unwrap(); } #[test] diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 4353a827e..06ba581d0 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -16,6 +16,7 @@ use num_traits::{Float, One, Zero}; use std::mem::MaybeUninit; use crate::dimension; +use crate::dimension::offset_from_ptr_to_memory; use crate::error::{self, ShapeError}; use crate::extension::nonnull::nonnull_from_vec_data; use crate::imp_prelude::*; @@ -24,6 +25,7 @@ use crate::indices; use crate::iterators::{to_vec, to_vec_mapped}; use crate::StrideShape; use crate::{geomspace, linspace, logspace}; +use rawpointer::PointerExt; /// # Constructor Methods for Owned Arrays /// @@ -431,7 +433,8 @@ where /// /// 2. The product of non-zero axis lengths must not exceed `isize::MAX`. /// - /// 3. For axes with length > 1, the stride must be nonnegative. + /// 3. For axes with length > 1, the pointer cannot move outside the + /// slice. /// /// 4. If the array will be empty (any axes are zero-length), the /// difference between the least address and greatest address accessible @@ -457,7 +460,8 @@ where // debug check for issues that indicates wrong use of this constructor debug_assert!(dimension::can_index_slice(&v, &dim, &strides).is_ok()); ArrayBase { - ptr: nonnull_from_vec_data(&mut v), + ptr: nonnull_from_vec_data(&mut v) + .offset(offset_from_ptr_to_memory(dim.slice(), strides.slice()).abs()), data: DataOwned::new(v), strides, dim, @@ -483,7 +487,7 @@ where /// /// This constructor is limited to elements where `A: Copy` (no destructors) /// to avoid users shooting themselves too hard in the foot. - /// + /// /// (Also note that the constructors `from_shape_vec` and /// `from_shape_vec_unchecked` allow the user yet more control, in the sense /// that Arrays can be created from arbitrary vectors.) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index cccc195a1..8b288430e 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -17,7 +17,8 @@ use crate::arraytraits; use crate::dimension; use crate::dimension::IntoDimension; use crate::dimension::{ - abs_index, axes_of, do_slice, merge_axes, size_of_shape_checked, stride_offset, Axes, + abs_index, axes_of, do_slice, merge_axes, offset_from_ptr_to_memory, size_of_shape_checked, + stride_offset, Axes, }; use crate::error::{self, ErrorKind, ShapeError}; use crate::itertools::zip; @@ -1280,9 +1281,6 @@ where } /// Return true if the array is known to be contiguous. - /// - /// Will detect c- and f-contig arrays correctly, but otherwise - /// There are some false negatives. pub(crate) fn is_contiguous(&self) -> bool { D::is_contiguous(&self.dim, &self.strides) } @@ -1404,14 +1402,18 @@ where /// /// If this function returns `Some(_)`, then the elements in the slice /// have whatever order the elements have in memory. - /// - /// Implementation notes: Does not yet support negatively strided arrays. pub fn as_slice_memory_order(&self) -> Option<&[A]> where S: Data, { if self.is_contiguous() { - unsafe { Some(slice::from_raw_parts(self.ptr.as_ptr(), self.len())) } + let offset = offset_from_ptr_to_memory(self.dim.slice(), self.strides.slice()); + unsafe { + Some(slice::from_raw_parts( + self.ptr.offset(offset).as_ptr(), + self.len(), + )) + } } else { None } @@ -1425,7 +1427,13 @@ where { if self.is_contiguous() { self.ensure_unique(); - unsafe { Some(slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len())) } + let offset = offset_from_ptr_to_memory(self.dim.slice(), self.strides.slice()); + unsafe { + Some(slice::from_raw_parts_mut( + self.ptr.offset(offset).as_ptr(), + self.len(), + )) + } } else { None } diff --git a/tests/dimension.rs b/tests/dimension.rs index 7e76132aa..86a7e5ed7 100644 --- a/tests/dimension.rs +++ b/tests/dimension.rs @@ -118,19 +118,36 @@ fn fastest_varying_order() { let order = strides._fastest_varying_stride_order(); assert_eq!(order.slice(), &[3, 0, 2, 1]); + let strides = Dim([-2isize as usize, 8, -4isize as usize, -1isize as usize]); + let order = strides._fastest_varying_stride_order(); + assert_eq!(order.slice(), &[3, 0, 2, 1]); + assert_eq!(Dim([1, 3])._fastest_varying_stride_order(), Dim([0, 1])); + assert_eq!( + Dim([1, -3isize as usize])._fastest_varying_stride_order(), + Dim([0, 1]) + ); assert_eq!(Dim([7, 2])._fastest_varying_stride_order(), Dim([1, 0])); + assert_eq!( + Dim([-7isize as usize, 2])._fastest_varying_stride_order(), + Dim([1, 0]) + ); assert_eq!( Dim([6, 1, 3])._fastest_varying_stride_order(), Dim([1, 2, 0]) ); + assert_eq!( + Dim([-6isize as usize, 1, -3isize as usize])._fastest_varying_stride_order(), + Dim([1, 2, 0]) + ); // it's important that it produces distinct indices. Prefer the stable order // where 0 is before 1 when they are equal. assert_eq!(Dim([2, 2])._fastest_varying_stride_order(), [0, 1]); assert_eq!(Dim([2, 2, 1])._fastest_varying_stride_order(), [2, 0, 1]); assert_eq!( - Dim([2, 2, 3, 1, 2])._fastest_varying_stride_order(), + Dim([-2isize as usize, -2isize as usize, 3, 1, -2isize as usize]) + ._fastest_varying_stride_order(), [3, 0, 1, 4, 2] ); } diff --git a/tests/oper.rs b/tests/oper.rs index 7193a49da..91205c49e 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -707,7 +707,7 @@ fn gen_mat_vec_mul() { S2: Data, { let ((m, _), k) = (lhs.dim(), rhs.dim()); - reference_mat_mul(lhs, &rhs.to_owned().into_shape((k, 1)).unwrap()) + reference_mat_mul(lhs, &rhs.as_standard_layout().into_shape((k, 1)).unwrap()) .into_shape(m) .unwrap() } @@ -772,7 +772,7 @@ fn vec_mat_mul() { S2: Data, { let (m, (_, n)) = (lhs.dim(), rhs.dim()); - reference_mat_mul(&lhs.to_owned().into_shape((1, m)).unwrap(), rhs) + reference_mat_mul(&lhs.as_standard_layout().into_shape((1, m)).unwrap(), rhs) .into_shape(n) .unwrap() } From 90dfc6c40eda375fb1eb17651f48cfaa9c8a4112 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Fri, 8 Jan 2021 18:35:11 +0800 Subject: [PATCH 151/651] Added memory continuity test with negative step size. Update documentation --- src/dimension/dimension_trait.rs | 3 +- src/dimension/mod.rs | 21 +++++++----- src/impl_constructors.rs | 3 +- src/impl_methods.rs | 16 ++++----- tests/array.rs | 58 ++++++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 20 deletions(-) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index e38fa75af..4223e9ed0 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -314,7 +314,8 @@ pub trait Dimension: *elt = i; } let strides = self.slice(); - indices.slice_mut() + indices + .slice_mut() .sort_by_key(|&i| (strides[i] as isize).abs()); indices } diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 36f5720d3..abe75966c 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -207,10 +207,7 @@ where /// /// 2. The product of non-zero axis lengths must not exceed `isize::MAX`. /// -/// 3. For axes with length > 1, the pointer cannot move outside the -/// slice. For axes with length ≤ 1, the stride can be anything. -/// -/// 4. If the array will be empty (any axes are zero-length), the difference +/// 3. If the array will be empty (any axes are zero-length), the difference /// between the least address and greatest address accessible by moving /// along all axes must be ≤ `data.len()`. (It's fine in this case to move /// one byte past the end of the slice since the pointers will be offset but @@ -221,13 +218,19 @@ where /// `data.len()`. This and #3 ensure that all dereferenceable pointers point /// to elements within the slice. /// -/// 5. The strides must not allow any element to be referenced by two different +/// 4. The strides must not allow any element to be referenced by two different /// indices. /// /// Note that since slices cannot contain more than `isize::MAX` bytes, /// condition 4 is sufficient to guarantee that the absolute difference in /// units of `A` and in units of bytes between the least address and greatest /// address accessible by moving along all axes does not exceed `isize::MAX`. +/// +/// Warning: This function is sufficient to check the invariants of ArrayBase only +/// if the pointer to the first element of the array is chosen such that the element +/// with the smallest memory address is at the start of data. (In other words, the +/// pointer to the first element of the array must be computed using offset_from_ptr_to_memory +/// so that negative strides are correctly handled.) pub(crate) fn can_index_slice( data: &[A], dim: &D, @@ -244,7 +247,7 @@ fn can_index_slice_impl( dim: &D, strides: &D, ) -> Result<(), ShapeError> { - // Check condition 4. + // Check condition 3. let is_empty = dim.slice().iter().any(|&d| d == 0); if is_empty && max_offset > data_len { return Err(from_kind(ErrorKind::OutOfBounds)); @@ -253,7 +256,7 @@ fn can_index_slice_impl( return Err(from_kind(ErrorKind::OutOfBounds)); } - // Check condition 5. + // Check condition 4. if !is_empty && dim_stride_overlap(dim, strides) { return Err(from_kind(ErrorKind::Unsupported)); } @@ -384,8 +387,8 @@ fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize) { /// This function computes the offset from the logically first element to the first element in /// memory of the array. The result is always <= 0. -pub fn offset_from_ptr_to_memory(dim: &[Ix], strides: &[Ix]) -> isize { - let offset = izip!(dim, strides).fold(0, |_offset, (d, s)| { +pub fn offset_from_ptr_to_memory(dim: &D, strides: &D) -> isize { + let offset = izip!(dim.slice(), strides.slice()).fold(0, |_offset, (d, s)| { if (*s as isize) < 0 { _offset + *s as isize * (*d as isize - 1) } else { diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 06ba581d0..56948caec 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -460,8 +460,7 @@ where // debug check for issues that indicates wrong use of this constructor debug_assert!(dimension::can_index_slice(&v, &dim, &strides).is_ok()); ArrayBase { - ptr: nonnull_from_vec_data(&mut v) - .offset(offset_from_ptr_to_memory(dim.slice(), strides.slice()).abs()), + ptr: nonnull_from_vec_data(&mut v).offset(-offset_from_ptr_to_memory(&dim, &strides)), data: DataOwned::new(v), strides, dim, diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 8b288430e..3921dcbfc 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -154,12 +154,12 @@ where /// Return an uniquely owned copy of the array. /// - /// If the input array is contiguous and its strides are positive, then the - /// output array will have the same memory layout. Otherwise, the layout of - /// the output array is unspecified. If you need a particular layout, you - /// can allocate a new array with the desired memory layout and - /// [`.assign()`](#method.assign) the data. Alternatively, you can collect - /// an iterator, like this for a result in standard layout: + /// If the input array is contiguous, then the output array will have the same + /// memory layout. Otherwise, the layout of the output array is unspecified. + /// If you need a particular layout, you can allocate a new array with the + /// desired memory layout and [`.assign()`](#method.assign) the data. + /// Alternatively, you can collectan iterator, like this for a result in + /// standard layout: /// /// ``` /// # use ndarray::prelude::*; @@ -1407,7 +1407,7 @@ where S: Data, { if self.is_contiguous() { - let offset = offset_from_ptr_to_memory(self.dim.slice(), self.strides.slice()); + let offset = offset_from_ptr_to_memory(&self.dim, &self.strides); unsafe { Some(slice::from_raw_parts( self.ptr.offset(offset).as_ptr(), @@ -1427,7 +1427,7 @@ where { if self.is_contiguous() { self.ensure_unique(); - let offset = offset_from_ptr_to_memory(self.dim.slice(), self.strides.slice()); + let offset = offset_from_ptr_to_memory(&self.dim, &self.strides); unsafe { Some(slice::from_raw_parts_mut( self.ptr.offset(offset).as_ptr(), diff --git a/tests/array.rs b/tests/array.rs index 3d917f72b..13e4f816e 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1786,6 +1786,64 @@ fn test_contiguous() { assert!(b.as_slice_memory_order().is_some()); } +#[test] +fn test_contiguous_neg_strides() { + let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; + let mut a = ArrayView::from_shape((2, 3, 2).strides((1, 4, 2)), &s).unwrap(); + assert_eq!( + a, + arr3(&[[[0, 2], [4, 6], [8, 10]], [[1, 3], [5, 7], [9, 11]]]) + ); + assert!(a.as_slice_memory_order().is_some()); + + let mut b = a.slice(s![..;1, ..;-1, ..;-1]); + assert_eq!( + b, + arr3(&[[[10, 8], [6, 4], [2, 0]], [[11, 9], [7, 5], [3, 1]]]) + ); + assert!(b.as_slice_memory_order().is_some()); + + b.swap_axes(1, 2); + assert_eq!(b, arr3(&[[[10, 6, 2], [8, 4, 0]], [[11, 7, 3], [9, 5, 1]]])); + assert!(b.as_slice_memory_order().is_some()); + + b.invert_axis(Axis(0)); + assert_eq!(b, arr3(&[[[11, 7, 3], [9, 5, 1]], [[10, 6, 2], [8, 4, 0]]])); + assert!(b.as_slice_memory_order().is_some()); + + let mut c = b.reversed_axes(); + assert_eq!( + c, + arr3(&[[[11, 10], [9, 8]], [[7, 6], [5, 4]], [[3, 2], [1, 0]]]) + ); + assert!(c.as_slice_memory_order().is_some()); + + c.merge_axes(Axis(1), Axis(2)); + assert_eq!(c, arr3(&[[[11, 10, 9, 8]], [[7, 6, 5, 4]], [[3, 2, 1, 0]]])); + assert!(c.as_slice_memory_order().is_some()); + + let d = b.remove_axis(Axis(1)); + assert_eq!(d, arr2(&[[11, 7, 3], [10, 6, 2]])); + assert!(d.as_slice_memory_order().is_none()); + + let e = b.remove_axis(Axis(2)); + assert_eq!(e, arr2(&[[11, 9], [10, 8]])); + assert!(e.as_slice_memory_order().is_some()); + + let f = e.insert_axis(Axis(2)); + assert_eq!(f, arr3(&[[[11], [9]], [[10], [8]]])); + assert!(f.as_slice_memory_order().is_some()); + + let mut g = b.clone(); + g.collapse_axis(Axis(1), 0); + assert_eq!(g, arr3(&[[[11, 7, 3]], [[10, 6, 2]]])); + assert!(g.as_slice_memory_order().is_none()); + + b.collapse_axis(Axis(2), 0); + assert_eq!(b, arr3(&[[[11], [9]], [[10], [8]]])); + assert!(b.as_slice_memory_order().is_some()); +} + #[test] fn test_swap() { let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9]]); From 0d8b965e4fc9e21b615770fca8654948ae9b3493 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 11 Jan 2021 20:50:15 +0100 Subject: [PATCH 152/651] TEST: Add test case for .to_owned() with negative strides --- tests/array.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/array.rs b/tests/array.rs index 13e4f816e..9523a8d21 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1717,10 +1717,18 @@ fn to_owned_memory_order() { // input. let c = arr2(&[[1, 2, 3], [4, 5, 6]]); let mut f = c.view(); + + // transposed array f.swap_axes(0, 1); let fo = f.to_owned(); assert_eq!(f, fo); assert_eq!(f.strides(), fo.strides()); + + // negated stride axis + f.invert_axis(Axis(1)); + let fo2 = f.to_owned(); + assert_eq!(f, fo2); + assert_eq!(f.strides(), fo2.strides()); } #[test] @@ -1729,6 +1737,7 @@ fn to_owned_neg_stride() { c.slice_collapse(s![.., ..;-1]); let co = c.to_owned(); assert_eq!(c, co); + assert_eq!(c.strides(), co.strides()); } #[test] From e6a4f105725655a03d53e93a22f877d749ef9bbb Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 13 Jan 2021 23:14:06 +0100 Subject: [PATCH 153/651] API: Add requirement of non-negative strides to raw ptr constructors For raw views and array views, require non-negative strides and check this with a debug assertion. --- src/dimension/mod.rs | 13 +++++++++++++ src/impl_raw_views.rs | 12 ++++++++++++ src/impl_views/constructors.rs | 10 ++++++++++ tests/raw_views.rs | 15 +++++++++++++++ 4 files changed, 50 insertions(+) diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index abe75966c..f52ba889b 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -280,6 +280,19 @@ pub fn stride_offset_checked(dim: &[Ix], strides: &[Ix], index: &[Ix]) -> Option Some(offset) } +/// Checks if strides are non-negative. +pub fn strides_non_negative(strides: &D) -> Result<(), ShapeError> +where + D: Dimension, +{ + for &stride in strides.slice() { + if (stride as isize) < 0 { + return Err(from_kind(ErrorKind::Unsupported)); + } + } + Ok(()) +} + /// Implementation-specific extensions to `Dimension` pub trait DimensionExt { // note: many extensions go in the main trait if they need to be special- diff --git a/src/impl_raw_views.rs b/src/impl_raw_views.rs index 8377dc93c..e3447cac5 100644 --- a/src/impl_raw_views.rs +++ b/src/impl_raw_views.rs @@ -62,6 +62,11 @@ where /// [`.offset()`] regardless of the starting point due to past offsets. /// /// * The product of non-zero axis lengths must not exceed `isize::MAX`. + /// + /// * Strides must be non-negative. + /// + /// This function can use debug assertions to check some of these requirements, + /// but it's not a complete check. /// /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset pub unsafe fn from_shape_ptr(shape: Sh, ptr: *const A) -> Self @@ -73,6 +78,7 @@ where if cfg!(debug_assertions) { assert!(!ptr.is_null(), "The pointer must be non-null."); if let Strides::Custom(strides) = &shape.strides { + dimension::strides_non_negative(strides).unwrap(); dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); } else { dimension::size_of_shape_checked(&dim).unwrap(); @@ -202,6 +208,11 @@ where /// [`.offset()`] regardless of the starting point due to past offsets. /// /// * The product of non-zero axis lengths must not exceed `isize::MAX`. + /// + /// * Strides must be non-negative. + /// + /// This function can use debug assertions to check some of these requirements, + /// but it's not a complete check. /// /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset pub unsafe fn from_shape_ptr(shape: Sh, ptr: *mut A) -> Self @@ -213,6 +224,7 @@ where if cfg!(debug_assertions) { assert!(!ptr.is_null(), "The pointer must be non-null."); if let Strides::Custom(strides) = &shape.strides { + dimension::strides_non_negative(strides).unwrap(); dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); } else { dimension::size_of_shape_checked(&dim).unwrap(); diff --git a/src/impl_views/constructors.rs b/src/impl_views/constructors.rs index bc1602af4..c6e5f9988 100644 --- a/src/impl_views/constructors.rs +++ b/src/impl_views/constructors.rs @@ -96,6 +96,11 @@ where /// /// * The product of non-zero axis lengths must not exceed `isize::MAX`. /// + /// * Strides must be non-negative. + /// + /// This function can use debug assertions to check some of these requirements, + /// but it's not a complete check. + /// /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset pub unsafe fn from_shape_ptr(shape: Sh, ptr: *const A) -> Self where @@ -188,6 +193,11 @@ where /// /// * The product of non-zero axis lengths must not exceed `isize::MAX`. /// + /// * Strides must be non-negative. + /// + /// This function can use debug assertions to check some of these requirements, + /// but it's not a complete check. + /// /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset pub unsafe fn from_shape_ptr(shape: Sh, ptr: *mut A) -> Self where diff --git a/tests/raw_views.rs b/tests/raw_views.rs index b63e42926..b955cac6b 100644 --- a/tests/raw_views.rs +++ b/tests/raw_views.rs @@ -81,3 +81,18 @@ fn raw_view_deref_into_view_misaligned() { let data: [u16; 2] = [0x0011, 0x2233]; misaligned_deref(&data); } + +#[test] +#[cfg(debug_assertions)] +#[should_panic = "Unsupported"] +fn raw_view_negative_strides() { + fn misaligned_deref(data: &[u16; 2]) -> ArrayView1<'_, u16> { + let ptr: *const u16 = data.as_ptr(); + unsafe { + let raw_view = RawArrayView::from_shape_ptr(1.strides((-1isize) as usize), ptr); + raw_view.deref_into_view() + } + } + let data: [u16; 2] = [0x0011, 0x2233]; + misaligned_deref(&data); +} From c4482eb28aa88a69934919004a8932d1fadb00a7 Mon Sep 17 00:00:00 2001 From: Sparrow Li Date: Fri, 15 Jan 2021 22:29:31 +0800 Subject: [PATCH 154/651] Fix to_shared to avoid cloning when it is already ArcArray (#893) Fix to_shared to avoid cloning when it is already AryArray --- src/data_traits.rs | 21 +++++++++++++++++++++ src/impl_methods.rs | 6 +++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index 3d6ce88b6..ef8a3ceaa 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -96,6 +96,18 @@ pub unsafe trait Data: RawData { where Self::Elem: Clone, D: Dimension; + + /// Return a shared ownership (copy on write) array based on the existing one, + /// cloning elements if necessary. + #[doc(hidden)] + fn to_shared(self_: &ArrayBase) -> ArrayBase, D> + where + Self::Elem: Clone, + D: Dimension, + { + // clone to shared + self_.to_owned().into_shared() + } } /// Array representation trait. @@ -238,6 +250,15 @@ unsafe impl Data for OwnedArcRepr { strides: self_.strides, } } + + fn to_shared(self_: &ArrayBase) -> ArrayBase, D> + where + Self::Elem: Clone, + D: Dimension, + { + // to shared using clone of OwnedArcRepr without clone of raw data. + self_.clone() + } } unsafe impl DataMut for OwnedArcRepr where A: Clone {} diff --git a/src/impl_methods.rs b/src/impl_methods.rs index aade25cc6..c9ad9bb0c 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -215,14 +215,14 @@ where } } - /// Return a shared ownership (copy on write) array. + /// Return a shared ownership (copy on write) array, cloning the array + /// elements if necessary. pub fn to_shared(&self) -> ArcArray where A: Clone, S: Data, { - // FIXME: Avoid copying if it’s already an ArcArray. - self.to_owned().into_shared() + S::to_shared(self) } /// Turn the array into a uniquely owned array, cloning the array elements From f4dfa50a582476503839aadda0accd5f306620a4 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 5 Dec 2020 12:29:57 +0100 Subject: [PATCH 155/651] API: Rename Zip::apply to Zip::for_each and same for par_apply Rename apply to for_each, par_apply to par_for_each. --- examples/sort-axis.rs | 2 +- examples/zip_many.rs | 4 +-- src/impl_methods.rs | 4 +-- src/linalg/impl_linalg.rs | 4 +-- src/parallel/impl_par_methods.rs | 15 +++++++++- src/parallel/zipmacro.rs | 4 +-- src/zip/mod.rs | 47 +++++++++++++++++++------------- src/zip/zipmacro.rs | 8 +++--- tests/array-construct.rs | 4 +-- tests/azip.rs | 26 +++++++++--------- tests/iterators.rs | 12 ++++---- tests/par_zip.rs | 10 +++---- tests/raw_views.rs | 2 +- tests/windows.rs | 2 +- 14 files changed, 83 insertions(+), 61 deletions(-) diff --git a/examples/sort-axis.rs b/examples/sort-axis.rs index ab5dfdab0..a02dad603 100644 --- a/examples/sort-axis.rs +++ b/examples/sort-axis.rs @@ -112,7 +112,7 @@ where let perm_i = perm.indices[i]; Zip::from(result.index_axis_mut(axis, perm_i)) .and(self.index_axis(axis, i)) - .apply(|to, from| { + .for_each(|to, from| { copy_nonoverlapping(from, to.as_mut_ptr(), 1); moved_elements += 1; }); diff --git a/examples/zip_many.rs b/examples/zip_many.rs index 0059cd650..40b855df2 100644 --- a/examples/zip_many.rs +++ b/examples/zip_many.rs @@ -41,11 +41,11 @@ fn main() { // Let's imagine we split to parallelize { let (x, y) = Zip::indexed(&mut a).split(); - x.apply(|(_, j), elt| { + x.for_each(|(_, j), elt| { *elt = elt.powi(j as i32); }); - y.apply(|(_, j), elt| { + y.for_each(|(_, j), elt| { *elt = elt.powi(j as i32); }); } diff --git a/src/impl_methods.rs b/src/impl_methods.rs index aade25cc6..10918a571 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1985,7 +1985,7 @@ where let dim = self.raw_dim(); Zip::from(LanesMut::new(self.view_mut(), Axis(n - 1))) .and(Lanes::new(rhs.broadcast_assume(dim), Axis(n - 1))) - .apply(move |s_row, r_row| Zip::from(s_row).and(r_row).apply(|a, b| f(a, b))); + .for_each(move |s_row, r_row| Zip::from(s_row).and(r_row).for_each(|a, b| f(a, b))); } fn zip_mut_with_elem(&mut self, rhs_elem: &B, mut f: F) @@ -2343,7 +2343,7 @@ where prev.slice_axis_inplace(axis, Slice::from(..-1)); curr.slice_axis_inplace(axis, Slice::from(1..)); // This implementation relies on `Zip` iterating along `axis` in order. - Zip::from(prev).and(curr).apply(|prev, curr| unsafe { + Zip::from(prev).and(curr).for_each(|prev, curr| unsafe { // These pointer dereferences and borrows are safe because: // // 1. They're pointers to elements in the array. diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index c227a68aa..0b559d9c5 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -679,11 +679,11 @@ unsafe fn general_mat_vec_mul_impl( if beta.is_zero() { // when beta is zero, c may be uninitialized - Zip::from(a.outer_iter()).and(y).apply(|row, elt| { + Zip::from(a.outer_iter()).and(y).for_each(|row, elt| { elt.write(row.dot(x) * alpha); }); } else { - Zip::from(a.outer_iter()).and(y).apply(|row, elt| { + Zip::from(a.outer_iter()).and(y).for_each(|row, elt| { *elt = *elt * beta + row.dot(x) * alpha; }); } diff --git a/src/parallel/impl_par_methods.rs b/src/parallel/impl_par_methods.rs index a4efa560f..21303736d 100644 --- a/src/parallel/impl_par_methods.rs +++ b/src/parallel/impl_par_methods.rs @@ -59,12 +59,25 @@ macro_rules! zip_impl { D: Dimension, $($p: NdProducer ,)* { + /// The `par_for_each` method for `Zip`. + /// + /// This is a shorthand for using `.into_par_iter().for_each()` on + /// `Zip`. + /// + /// Requires crate feature `rayon`. + pub fn par_for_each(self, function: F) + where F: Fn($($p::Item),*) + Sync + Send + { + self.into_par_iter().for_each(move |($($p,)*)| function($($p),*)) + } + /// The `par_apply` method for `Zip`. /// /// This is a shorthand for using `.into_par_iter().for_each()` on /// `Zip`. /// /// Requires crate feature `rayon`. + #[deprecated(note="Renamed to .par_for_each()", since="0.15.0")] pub fn par_apply(self, function: F) where F: Fn($($p::Item),*) + Sync + Send { @@ -133,7 +146,7 @@ macro_rules! zip_impl { Q::Output: Send, { self.and(into) - .par_apply(move |$($p, )* output_| { + .par_for_each(move |$($p, )* output_| { output_.assign_elem(f($($p ),*)); }); } diff --git a/src/parallel/zipmacro.rs b/src/parallel/zipmacro.rs index eadd36a84..95111c933 100644 --- a/src/parallel/zipmacro.rs +++ b/src/parallel/zipmacro.rs @@ -24,7 +24,7 @@ /// Is equivalent to: /// /// ```rust,ignore -/// Zip::from(&mut a).and(&b).and(&c).par_apply(|a, &b, &c| { +/// Zip::from(&mut a).and(&b).and(&c).par_for_each(|a, &b, &c| { /// *a = b + c; /// }); /// ``` @@ -54,6 +54,6 @@ /// ``` macro_rules! par_azip { ($($t:tt)*) => { - $crate::azip!(@build par_apply $($t)*) + $crate::azip!(@build par_for_each $($t)*) }; } diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 3c61d8110..e6121d99b 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -518,7 +518,7 @@ impl NdProducer for RawArrayViewMut { /// The order elements are visited is not specified. The producers don’t have to /// have the same item type. /// -/// The `Zip` has two methods for function application: `apply` and +/// The `Zip` has two methods for function application: `for_each` and /// `fold_while`. The zip object can be split, which allows parallelization. /// A read-only zip object (no mutable producers) can be cloned. /// @@ -546,7 +546,7 @@ impl NdProducer for RawArrayViewMut { /// .and(&b) /// .and(&c) /// .and(&d) -/// .apply(|w, &x, &y, &z| { +/// .for_each(|w, &x, &y, &z| { /// *w += x + y * z; /// }); /// @@ -563,7 +563,7 @@ impl NdProducer for RawArrayViewMut { /// /// Zip::from(&mut totals) /// .and(a.rows()) -/// .apply(|totals, row| *totals = row.sum()); +/// .for_each(|totals, row| *totals = row.sum()); /// /// // Check the result against the built in `.sum_axis()` along axis 1. /// assert_eq!(totals, a.sum_axis(Axis(1))); @@ -692,7 +692,7 @@ impl Zip where D: Dimension, { - fn apply_core(&mut self, acc: Acc, mut function: F) -> FoldWhile + fn for_each_core(&mut self, acc: Acc, mut function: F) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, P: ZippableTuple, @@ -700,13 +700,13 @@ where if self.dimension.ndim() == 0 { function(acc, unsafe { self.parts.as_ref(self.parts.as_ptr()) }) } else if self.layout.is(CORDER | FORDER) { - self.apply_core_contiguous(acc, function) + self.for_each_core_contiguous(acc, function) } else { - self.apply_core_strided(acc, function) + self.for_each_core_strided(acc, function) } } - fn apply_core_contiguous(&mut self, acc: Acc, mut function: F) -> FoldWhile + fn for_each_core_contiguous(&mut self, acc: Acc, mut function: F) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, P: ZippableTuple, @@ -744,7 +744,7 @@ where } - fn apply_core_strided(&mut self, acc: Acc, function: F) -> FoldWhile + fn for_each_core_strided(&mut self, acc: Acc, function: F) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, P: ZippableTuple, @@ -754,14 +754,14 @@ where panic!("Unreachable: ndim == 0 is contiguous") } if n == 1 || self.layout_tendency >= 0 { - self.apply_core_strided_c(acc, function) + self.for_each_core_strided_c(acc, function) } else { - self.apply_core_strided_f(acc, function) + self.for_each_core_strided_f(acc, function) } } // Non-contiguous but preference for C - unroll over Axis(ndim - 1) - fn apply_core_strided_c(&mut self, mut acc: Acc, mut function: F) -> FoldWhile + fn for_each_core_strided_c(&mut self, mut acc: Acc, mut function: F) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, P: ZippableTuple, @@ -785,7 +785,7 @@ where } // Non-contiguous but preference for F - unroll over Axis(0) - fn apply_core_strided_f(&mut self, mut acc: Acc, mut function: F) -> FoldWhile + fn for_each_core_strided_f(&mut self, mut acc: Acc, mut function: F) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, P: ZippableTuple, @@ -939,15 +939,24 @@ macro_rules! map_impl { { /// Apply a function to all elements of the input arrays, /// visiting elements in lock step. - pub fn apply(mut self, mut function: F) + pub fn for_each(mut self, mut function: F) where F: FnMut($($p::Item),*) { - self.apply_core((), move |(), args| { + self.for_each_core((), move |(), args| { let ($($p,)*) = args; FoldWhile::Continue(function($($p),*)) }); } + /// Apply a function to all elements of the input arrays, + /// visiting elements in lock step. + #[deprecated(note="Renamed to .for_each()", since="0.15.0")] + pub fn apply(self, function: F) + where F: FnMut($($p::Item),*) + { + self.for_each(function) + } + /// Apply a fold function to all elements of the input arrays, /// visiting elements in lock step. /// @@ -980,7 +989,7 @@ macro_rules! map_impl { where F: FnMut(Acc, $($p::Item),*) -> Acc, { - self.apply_core(acc, move |acc, args| { + self.for_each_core(acc, move |acc, args| { let ($($p,)*) = args; FoldWhile::Continue(function(acc, $($p),*)) }).into_inner() @@ -993,7 +1002,7 @@ macro_rules! map_impl { -> FoldWhile where F: FnMut(Acc, $($p::Item),*) -> FoldWhile { - self.apply_core(acc, move |acc, args| { + self.for_each_core(acc, move |acc, args| { let ($($p,)*) = args; function(acc, $($p),*) }) @@ -1015,7 +1024,7 @@ macro_rules! map_impl { pub fn all(mut self, mut predicate: F) -> bool where F: FnMut($($p::Item),*) -> bool { - !self.apply_core((), move |_, args| { + !self.for_each_core((), move |_, args| { let ($($p,)*) = args; if predicate($($p),*) { FoldWhile::Continue(()) @@ -1096,7 +1105,7 @@ macro_rules! map_impl { Q::Item: AssignElem { self.and(into) - .apply(move |$($p, )* output_| { + .for_each(move |$($p, )* output_| { output_.assign_elem(f($($p ),*)); }); } @@ -1150,7 +1159,7 @@ macro_rules! map_impl { // Apply the mapping function on this zip // if we panic with unwinding; Partial will drop the written elements. let partial_len = &mut partial.len; - self.apply(move |$($p,)* output_elem: *mut R| { + self.for_each(move |$($p,)* output_elem: *mut R| { output_elem.write(f($($p),*)); if std::mem::needs_drop::() { *partial_len += 1; diff --git a/src/zip/zipmacro.rs b/src/zip/zipmacro.rs index 5353a6378..f25e33d31 100644 --- a/src/zip/zipmacro.rs +++ b/src/zip/zipmacro.rs @@ -12,7 +12,7 @@ /// Is equivalent to: /// /// ```rust,ignore -/// Zip::from(&mut a).and(&b).and(&c).apply(|a, &b, &c| { +/// Zip::from(&mut a).and(&b).and(&c).for_each(|a, &b, &c| { /// *a = b + c /// }); /// ``` @@ -27,8 +27,8 @@ /// /// The *expr* are expressions whose types must implement `IntoNdProducer`, the /// *pat* are the patterns of the parameters to the closure called by -/// `Zip::apply`, and *body_expr* is the body of the closure called by -/// `Zip::apply`. You can think of each *pat* `in` *expr* as being analogous to +/// `Zip::for_each`, and *body_expr* is the body of the closure called by +/// `Zip::for_each`. You can think of each *pat* `in` *expr* as being analogous to /// the `pat in expr` of a normal loop `for pat in expr { statements }`: a /// pattern, followed by `in`, followed by an expression that implements /// `IntoNdProducer` (analogous to `IntoIterator` for a `for` loop). @@ -129,6 +129,6 @@ macro_rules! azip { // catch-all rule (@build $($t:tt)*) => { compile_error!("Invalid syntax in azip!()") }; ($($t:tt)*) => { - $crate::azip!(@build apply $($t)*) + $crate::azip!(@build for_each $($t)*) }; } diff --git a/tests/array-construct.rs b/tests/array-construct.rs index 4513dd453..2c195480f 100644 --- a/tests/array-construct.rs +++ b/tests/array-construct.rs @@ -231,12 +231,12 @@ fn maybe_uninit_1() { let mut a = Mat::maybe_uninit((10, 10)); let v = a.raw_view_mut(); Zip::from(v) - .apply(|ptr| *(*ptr).as_mut_ptr() = 1.); + .for_each(|ptr| *(*ptr).as_mut_ptr() = 1.); let u = a.raw_view_mut().assume_init(); Zip::from(u) - .apply(|ptr| assert_eq!(*ptr, 1.)); + .for_each(|ptr| assert_eq!(*ptr, 1.)); } } diff --git a/tests/azip.rs b/tests/azip.rs index b553d7fb5..34328169a 100644 --- a/tests/azip.rs +++ b/tests/azip.rs @@ -243,7 +243,7 @@ fn test_broadcast() { .and_broadcast(&b) .and_broadcast(&d) .and_broadcast(&e); - z.apply(|x, &y, &z, &w| *x = y + z + w); + z.for_each(|x, &y, &z, &w| *x = y + z + w); } let sum = &b + &d + &e; assert_abs_diff_eq!(a, sum.broadcast((n, n)).unwrap(), epsilon = 1e-4); @@ -293,11 +293,11 @@ fn test_clone() { let z = Zip::from(&a).and(a.exact_chunks((1, 1, 1))); let w = z.clone(); let mut result = Vec::new(); - z.apply(|x, y| { + z.for_each(|x, y| { result.push((x, y)); }); let mut i = 0; - w.apply(|x, y| { + w.for_each(|x, y| { assert_eq!(result[i], (x, y)); i += 1; }); @@ -324,7 +324,7 @@ fn test_indices_1() { } let mut count = 0; - Zip::indexed(&a1).apply(|i, elt| { + Zip::indexed(&a1).for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); @@ -334,12 +334,12 @@ fn test_indices_1() { let len = a1.len(); let (x, y) = Zip::indexed(&mut a1).split(); - x.apply(|i, elt| { + x.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, len / 2); - y.apply(|i, elt| { + y.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); @@ -364,12 +364,12 @@ fn test_indices_2() { let len = a1.len(); let (x, y) = Zip::indexed(&mut a1).split(); - x.apply(|i, elt| { + x.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, len / 2); - y.apply(|i, elt| { + y.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); @@ -384,7 +384,7 @@ fn test_indices_3() { } let mut count = 0; - Zip::indexed(&a1).apply(|i, elt| { + Zip::indexed(&a1).for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); @@ -394,12 +394,12 @@ fn test_indices_3() { let len = a1.len(); let (x, y) = Zip::indexed(&mut a1).split(); - x.apply(|i, elt| { + x.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, len / 2); - y.apply(|i, elt| { + y.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); @@ -418,12 +418,12 @@ fn test_indices_split_1() { let mut seen = Vec::new(); let mut ac = 0; - a.apply(|i, _| { + a.for_each(|i, _| { ac += 1; seen.push(i); }); let mut bc = 0; - b.apply(|i, _| { + b.for_each(|i, _| { bc += 1; seen.push(i); }); diff --git a/tests/iterators.rs b/tests/iterators.rs index 7bb856adf..fc345200e 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -308,7 +308,7 @@ fn axis_iter_zip() { let a = Array::from_iter(0..5); let iter = a.axis_iter(Axis(0)); let mut b = Array::zeros(5); - Zip::from(&mut b).and(iter).apply(|b, a| *b = a[()]); + Zip::from(&mut b).and(iter).for_each(|b, a| *b = a[()]); assert_eq!(a, b); } @@ -320,7 +320,7 @@ fn axis_iter_zip_partially_consumed() { while iter.next().is_some() { consumed += 1; let mut b = Array::zeros(a.len() - consumed); - Zip::from(&mut b).and(iter.clone()).apply(|b, a| *b = a[()]); + Zip::from(&mut b).and(iter.clone()).for_each(|b, a| *b = a[()]); assert_eq!(a.slice(s![consumed..]), b); } } @@ -334,7 +334,7 @@ fn axis_iter_zip_partially_consumed_discontiguous() { consumed += 1; let mut b = Array::zeros((a.len() - consumed) * 2); b.slice_collapse(s![..;2]); - Zip::from(&mut b).and(iter.clone()).apply(|b, a| *b = a[()]); + Zip::from(&mut b).and(iter.clone()).for_each(|b, a| *b = a[()]); assert_eq!(a.slice(s![consumed..]), b); } } @@ -487,7 +487,7 @@ fn axis_iter_mut_zip() { let mut cloned = orig.clone(); let iter = cloned.axis_iter_mut(Axis(0)); let mut b = Array::zeros(5); - Zip::from(&mut b).and(iter).apply(|b, mut a| { + Zip::from(&mut b).and(iter).for_each(|b, mut a| { a[()] += 1; *b = a[()]; }); @@ -505,7 +505,7 @@ fn axis_iter_mut_zip_partially_consumed() { iter.next(); } let mut b = Array::zeros(remaining); - Zip::from(&mut b).and(iter).apply(|b, a| *b = a[()]); + Zip::from(&mut b).and(iter).for_each(|b, a| *b = a[()]); assert_eq!(a.slice(s![consumed..]), b); } } @@ -521,7 +521,7 @@ fn axis_iter_mut_zip_partially_consumed_discontiguous() { } let mut b = Array::zeros(remaining * 2); b.slice_collapse(s![..;2]); - Zip::from(&mut b).and(iter).apply(|b, a| *b = a[()]); + Zip::from(&mut b).and(iter).for_each(|b, a| *b = a[()]); assert_eq!(a.slice(s![consumed..]), b); } } diff --git a/tests/par_zip.rs b/tests/par_zip.rs index 36d7f43c2..6e508de15 100644 --- a/tests/par_zip.rs +++ b/tests/par_zip.rs @@ -11,14 +11,14 @@ const N: usize = 100; fn test_zip_1() { let mut a = Array2::::zeros((M, N)); - Zip::from(&mut a).par_apply(|x| *x = x.exp()); + Zip::from(&mut a).par_for_each(|x| *x = x.exp()); } #[test] fn test_zip_index_1() { let mut a = Array2::default((10, 10)); - Zip::indexed(&mut a).par_apply(|i, x| { + Zip::indexed(&mut a).par_for_each(|i, x| { *x = i; }); @@ -31,7 +31,7 @@ fn test_zip_index_1() { fn test_zip_index_2() { let mut a = Array2::default((M, N)); - Zip::indexed(&mut a).par_apply(|i, x| { + Zip::indexed(&mut a).par_for_each(|i, x| { *x = i; }); @@ -44,7 +44,7 @@ fn test_zip_index_2() { fn test_zip_index_3() { let mut a = Array::default((1, 2, 1, 2, 3)); - Zip::indexed(&mut a).par_apply(|i, x| { + Zip::indexed(&mut a).par_for_each(|i, x| { *x = i; }); @@ -58,7 +58,7 @@ fn test_zip_index_4() { let mut a = Array2::zeros((M, N)); let mut b = Array2::zeros((M, N)); - Zip::indexed(&mut a).and(&mut b).par_apply(|(i, j), x, y| { + Zip::indexed(&mut a).and(&mut b).par_for_each(|(i, j), x, y| { *x = i; *y = j; }); diff --git a/tests/raw_views.rs b/tests/raw_views.rs index b955cac6b..bb39547e8 100644 --- a/tests/raw_views.rs +++ b/tests/raw_views.rs @@ -14,7 +14,7 @@ fn raw_view_cast_cell() { let raw_cell_view = a.raw_view_mut().cast::>(); let cell_view = unsafe { raw_cell_view.deref_into_view() }; - Zip::from(cell_view).apply(|elt| elt.set(elt.get() + 1.)); + Zip::from(cell_view).for_each(|elt| elt.set(elt.get() + 1.)); } assert_eq!(a, answer); } diff --git a/tests/windows.rs b/tests/windows.rs index eb1a33411..d1c41f017 100644 --- a/tests/windows.rs +++ b/tests/windows.rs @@ -104,7 +104,7 @@ fn test_window_zip() { for x in 1..4 { for y in 1..4 { for z in 1..4 { - Zip::indexed(a.windows((x, y, z))).apply(|(i, j, k), window| { + Zip::indexed(a.windows((x, y, z))).for_each(|(i, j, k), window| { let x = x as isize; let y = y as isize; let z = z as isize; From 1f5b8d5ab81b167fb8d31da74ce3f2617356365c Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 14 Jan 2021 00:43:42 +0100 Subject: [PATCH 156/651] API: Rename apply_collect, apply_collect_into to map_collect, map_collect_into --- src/parallel/impl_par_methods.rs | 35 ++++++++++++++++++++++++++++---- src/traversal_utils.rs | 2 +- src/zip/mod.rs | 31 +++++++++++++++++++++++----- tests/azip.rs | 14 ++++++------- tests/par_zip.rs | 8 ++++---- tests/views.rs | 2 +- 6 files changed, 70 insertions(+), 22 deletions(-) diff --git a/src/parallel/impl_par_methods.rs b/src/parallel/impl_par_methods.rs index 21303736d..33d393345 100644 --- a/src/parallel/impl_par_methods.rs +++ b/src/parallel/impl_par_methods.rs @@ -86,11 +86,11 @@ macro_rules! zip_impl { expand_if!(@bool [$notlast] - /// Apply and collect the results into a new array, which has the same size as the + /// Map and collect the results into a new array, which has the same size as the /// inputs. /// /// If all inputs are c- or f-order respectively, that is preserved in the output. - pub fn par_apply_collect(self, f: impl Fn($($p::Item,)* ) -> R + Sync + Send) + pub fn par_map_collect(self, f: impl Fn($($p::Item,)* ) -> R + Sync + Send) -> Array where R: Send { @@ -135,12 +135,24 @@ macro_rules! zip_impl { } } - /// Apply and assign the results into the producer `into`, which should have the same + /// Map and collect the results into a new array, which has the same size as the + /// inputs. + /// + /// If all inputs are c- or f-order respectively, that is preserved in the output. + #[deprecated(note="Renamed to .par_map_collect()", since="0.15.0")] + pub fn par_apply_collect(self, f: impl Fn($($p::Item,)* ) -> R + Sync + Send) + -> Array + where R: Send + { + self.par_map_collect(f) + } + + /// Map and assign the results into the producer `into`, which should have the same /// size as the other inputs. /// /// The producer should have assignable items as dictated by the `AssignElem` trait, /// for example `&mut R`. - pub fn par_apply_assign_into(self, into: Q, f: impl Fn($($p::Item,)* ) -> R + Sync + Send) + pub fn par_map_assign_into(self, into: Q, f: impl Fn($($p::Item,)* ) -> R + Sync + Send) where Q: IntoNdProducer, Q::Item: AssignElem + Send, Q::Output: Send, @@ -150,6 +162,21 @@ macro_rules! zip_impl { output_.assign_elem(f($($p ),*)); }); } + + /// Apply and assign the results into the producer `into`, which should have the same + /// size as the other inputs. + /// + /// The producer should have assignable items as dictated by the `AssignElem` trait, + /// for example `&mut R`. + #[deprecated(note="Renamed to .par_map_assign_into()", since="0.15.0")] + pub fn par_apply_assign_into(self, into: Q, f: impl Fn($($p::Item,)* ) -> R + Sync + Send) + where Q: IntoNdProducer, + Q::Item: AssignElem + Send, + Q::Output: Send, + { + self.par_map_assign_into(into, f) + } + ); } )+ diff --git a/src/traversal_utils.rs b/src/traversal_utils.rs index b77d410a2..3f4d017bf 100644 --- a/src/traversal_utils.rs +++ b/src/traversal_utils.rs @@ -21,6 +21,6 @@ pub(crate) fn assign_to<'a, P1, P2, A>(from: P1, to: P2) A: Clone + 'a { Zip::from(from) - .apply_assign_into(to, A::clone); + .map_assign_into(to, A::clone); } diff --git a/src/zip/mod.rs b/src/zip/mod.rs index e6121d99b..dd0bce3f8 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -1074,12 +1074,11 @@ macro_rules! map_impl { } } - /// Apply and collect the results into a new array, which has the same size as the + /// Map and collect the results into a new array, which has the same size as the /// inputs. /// /// If all inputs are c- or f-order respectively, that is preserved in the output. - pub fn apply_collect(self, f: impl FnMut($($p::Item,)* ) -> R) -> Array - { + pub fn map_collect(self, f: impl FnMut($($p::Item,)* ) -> R) -> Array { // Make uninit result let mut output = self.uninitalized_for_current_layout::(); @@ -1095,12 +1094,21 @@ macro_rules! map_impl { } } - /// Apply and assign the results into the producer `into`, which should have the same + /// Map and collect the results into a new array, which has the same size as the + /// inputs. + /// + /// If all inputs are c- or f-order respectively, that is preserved in the output. + #[deprecated(note="Renamed to .map_collect()", since="0.15.0")] + pub fn apply_collect(self, f: impl FnMut($($p::Item,)* ) -> R) -> Array { + self.map_collect(f) + } + + /// Map and assign the results into the producer `into`, which should have the same /// size as the other inputs. /// /// The producer should have assignable items as dictated by the `AssignElem` trait, /// for example `&mut R`. - pub fn apply_assign_into(self, into: Q, mut f: impl FnMut($($p::Item,)* ) -> R) + pub fn map_assign_into(self, into: Q, mut f: impl FnMut($($p::Item,)* ) -> R) where Q: IntoNdProducer, Q::Item: AssignElem { @@ -1110,6 +1118,19 @@ macro_rules! map_impl { }); } + /// Map and assign the results into the producer `into`, which should have the same + /// size as the other inputs. + /// + /// The producer should have assignable items as dictated by the `AssignElem` trait, + /// for example `&mut R`. + #[deprecated(note="Renamed to .map_assign_into()", since="0.15.0")] + pub fn apply_assign_into(self, into: Q, f: impl FnMut($($p::Item,)* ) -> R) + where Q: IntoNdProducer, + Q::Item: AssignElem + { + self.map_assign_into(into, f) + } + ); diff --git a/tests/azip.rs b/tests/azip.rs index 34328169a..5075a89ba 100644 --- a/tests/azip.rs +++ b/tests/azip.rs @@ -54,13 +54,13 @@ fn test_azip2_3() { fn test_zip_collect() { use approx::assert_abs_diff_eq; - // test Zip::apply_collect and that it preserves c/f layout. + // test Zip::map_collect and that it preserves c/f layout. let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j + 1) as f32); let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); { - let a = Zip::from(&b).and(&c).apply_collect(|x, y| x + y); + let a = Zip::from(&b).and(&c).map_collect(|x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); assert_eq!(a.strides(), b.strides()); @@ -70,7 +70,7 @@ fn test_zip_collect() { let b = b.t(); let c = c.t(); - let a = Zip::from(&b).and(&c).apply_collect(|x, y| x + y); + let a = Zip::from(&b).and(&c).map_collect(|x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); assert_eq!(a.strides(), b.strides()); @@ -86,7 +86,7 @@ fn test_zip_assign_into() { let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j + 1) as f32); let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); - Zip::from(&b).and(&c).apply_assign_into(&mut a, |x, y| x + y); + Zip::from(&b).and(&c).map_assign_into(&mut a, |x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); } @@ -101,7 +101,7 @@ fn test_zip_assign_into_cell() { let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j + 1) as f32); let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); - Zip::from(&b).and(&c).apply_assign_into(&a, |x, y| x + y); + Zip::from(&b).and(&c).map_assign_into(&a, |x, y| x + y); let a2 = a.mapv(|elt| elt.get()); assert_abs_diff_eq!(a2, &b + &c, epsilon = 1e-6); @@ -154,7 +154,7 @@ fn test_zip_collect_drop() { } let _result = panic::catch_unwind(panic::AssertUnwindSafe(|| { - Zip::from(&a).and(&b).apply_collect(|&elt, _| { + Zip::from(&a).and(&b).map_collect(|&elt, _| { if elt.0 > 3 && will_panic { panic!(); } @@ -308,7 +308,7 @@ fn test_indices_0() { let a1 = arr0(3); let mut count = 0; - Zip::indexed(&a1).apply(|i, elt| { + Zip::indexed(&a1).for_each(|i, elt| { count += 1; assert_eq!(i, ()); assert_eq!(*elt, 3); diff --git a/tests/par_zip.rs b/tests/par_zip.rs index 6e508de15..f30d5c7ef 100644 --- a/tests/par_zip.rs +++ b/tests/par_zip.rs @@ -82,7 +82,7 @@ fn test_zip_collect() { let c = Array::from_shape_fn((M, N), |(i, j)| f32::ln((1 + i + j) as f32)); { - let a = Zip::from(&b).and(&c).par_apply_collect(|x, y| x + y); + let a = Zip::from(&b).and(&c).par_map_collect(|x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); assert_eq!(a.strides(), b.strides()); @@ -92,7 +92,7 @@ fn test_zip_collect() { let b = b.t(); let c = c.t(); - let a = Zip::from(&b).and(&c).par_apply_collect(|x, y| x + y); + let a = Zip::from(&b).and(&c).par_map_collect(|x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); assert_eq!(a.strides(), b.strides()); @@ -112,7 +112,7 @@ fn test_zip_small_collect() { let c = Array::from_shape_fn(dim, |(i, j)| f32::ln((1 + i + j) as f32)); { - let a = Zip::from(&b).and(&c).par_apply_collect(|x, y| x + y); + let a = Zip::from(&b).and(&c).par_map_collect(|x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); assert_eq!(a.strides(), b.strides()); @@ -130,7 +130,7 @@ fn test_zip_assign_into() { let b = Array::from_shape_fn((M, N), |(i, j)| 1. / (i + 2 * j + 1) as f32); let c = Array::from_shape_fn((M, N), |(i, j)| f32::ln((1 + i + j) as f32)); - Zip::from(&b).and(&c).par_apply_assign_into(&mut a, |x, y| x + y); + Zip::from(&b).and(&c).par_map_assign_into(&mut a, |x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); } diff --git a/tests/views.rs b/tests/views.rs index 216e55402..ecef72fe8 100644 --- a/tests/views.rs +++ b/tests/views.rs @@ -10,7 +10,7 @@ fn cell_view() { let cv1 = a.cell_view(); let cv2 = cv1; - Zip::from(cv1).and(cv2).apply(|a, b| a.set(b.get() + 1.)); + Zip::from(cv1).and(cv2).for_each(|a, b| a.set(b.get() + 1.)); } assert_eq!(a, answer); } From 2af780f08afa469ea4d0e003729f2ce12e776d0a Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 14 Jan 2021 00:27:15 +0100 Subject: [PATCH 157/651] FIX: Use faster iterator collect in as_standard_layout --- src/impl_methods.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 58cc463b4..8f604756d 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1331,15 +1331,15 @@ where if self.is_standard_layout() { CowArray::from(self.view()) } else { - let v: Vec = self.iter().cloned().collect(); + let v = crate::iterators::to_vec_mapped(self.iter(), A::clone); let dim = self.dim.clone(); - assert_eq!(v.len(), dim.size()); - let owned_array: Array = unsafe { + debug_assert_eq!(v.len(), dim.size()); + + unsafe { // Safe because the shape and element type are from the existing array // and the strides are the default strides. - Array::from_shape_vec_unchecked(dim, v) - }; - CowArray::from(owned_array) + CowArray::from(Array::from_shape_vec_unchecked(dim, v)) + } } } From 9b8b4558096342d300a359db67f3347f9829a839 Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 26 Jan 2021 17:31:09 +0100 Subject: [PATCH 158/651] FIX: Broken debug assertion in collect to f-order array This fixes a "bug" in layout_impl - if an array is effectively one-dimensional, like for example has dimension 2 x 1 x 1 (3d array with len two), then it's possible it is both C and F-contig. This was the reason for the debug assertion firing: collection starts into an f-order output array, but when we've split the array enough times, it's still an f-order array but in shape 1 x 1 x 1 or 2 x 1 x 1 in this case - and we detected that as a c-order array. --- src/zip/mod.rs | 18 +++++++++++++++--- tests/par_zip.rs | 24 +++++++++++++++--------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/zip/mod.rs b/src/zip/mod.rs index dd0bce3f8..4b233d09b 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -58,7 +58,8 @@ where pub(crate) fn layout_impl(&self) -> Layout { let n = self.ndim(); if self.is_standard_layout() { - if n <= 1 { + // effectively one-dimensional => C and F layout compatible + if n <= 1 || self.shape().iter().filter(|&&len| len > 1).count() <= 1 { Layout::one_dimensional() } else { Layout::c() @@ -1173,8 +1174,19 @@ macro_rules! map_impl { { // Get the last producer; and make a Partial that aliases its data pointer let (.., ref output) = &self.parts; - debug_assert!(output.layout().is(CORDER | FORDER)); - debug_assert_eq!(output.layout().tendency() >= 0, self.layout_tendency >= 0); + + // debug assert that the output is contiguous in the memory layout we need + if cfg!(debug_assertions) { + let out_layout = output.layout(); + assert!(out_layout.is(CORDER | FORDER)); + assert!( + (self.layout_tendency <= 0 && out_layout.tendency() <= 0) || + (self.layout_tendency >= 0 && out_layout.tendency() >= 0), + "layout tendency violation for self layout {:?}, output layout {:?},\ + output shape {:?}", + self.layout, out_layout, output.raw_dim()); + } + let mut partial = Partial::new(output.as_ptr()); // Apply the mapping function on this zip diff --git a/tests/par_zip.rs b/tests/par_zip.rs index f30d5c7ef..019029119 100644 --- a/tests/par_zip.rs +++ b/tests/par_zip.rs @@ -107,20 +107,26 @@ fn test_zip_small_collect() { for m in 0..32 { for n in 0..16 { - let dim = (m, n); - let b = Array::from_shape_fn(dim, |(i, j)| 1. / (i + 2 * j + 1) as f32); - let c = Array::from_shape_fn(dim, |(i, j)| f32::ln((1 + i + j) as f32)); - - { - let a = Zip::from(&b).and(&c).par_map_collect(|x, y| x + y); - - assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); - assert_eq!(a.strides(), b.strides()); + for &is_f in &[false, true] { + let dim = (m, n).set_f(is_f); + let b = Array::from_shape_fn(dim, |(i, j)| 1. / (i + 2 * j + 1) as f32); + let c = Array::from_shape_fn(dim, |(i, j)| f32::ln((1 + i + j) as f32)); + + { + let a = Zip::from(&b).and(&c).par_map_collect(|x, y| x + y); + + assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); + if m > 1 && n > 1 { + assert_eq!(a.strides(), b.strides(), + "Failure for {}x{} c/f: {:?}", m, n, is_f); + } + } } } } } + #[test] #[cfg(feature = "approx")] fn test_zip_assign_into() { From b9c12b0d4a018d876332dfacdf5aa0f566c5c4fb Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 28 Jan 2021 18:01:20 +0100 Subject: [PATCH 159/651] FEAT: Rename maybe_uninit to Array::uninit and move to base type Old type situation: `Array, D>::maybe_uninit()` New type situation: `Array::uninit()` The link between the regular array storage type and the maybeuninit version of it is made explicit in the DataOwned trait and this makes it much easier to work with this constructor in generic code. The new name is "uninit", just like many types in std (likej `Box::uninit`). The old name is deprecated. Because of the unfortunate generics situation of the old name, the implementation is a copy & paste (short allegory - inside ArrayBase::uninit we have types S and S::MaybeUninit "available" as known type, inside ArrayBase::maybe_uninit we only have a `DataOwned>` type "avaialable" and no way to find the corresponding "plain" storage type.) --- benches/bench1.rs | 2 +- examples/sort-axis.rs | 2 +- src/data_traits.rs | 10 ++++++++++ src/impl_constructors.rs | 33 +++++++++++++++++++++++++-------- src/linalg/impl_linalg.rs | 2 +- src/stacking.rs | 4 ++-- src/zip/mod.rs | 2 +- tests/array-construct.rs | 10 +++++----- 8 files changed, 46 insertions(+), 19 deletions(-) diff --git a/benches/bench1.rs b/benches/bench1.rs index c446b1e20..c24729d6f 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -271,7 +271,7 @@ fn add_2d_alloc_zip_uninit(bench: &mut test::Bencher) { let a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| unsafe { - let mut c = Array::, _>::maybe_uninit(a.dim()); + let mut c = Array::::uninit(a.dim()); azip!((&a in &a, &b in &b, c in c.raw_view_mut().cast::()) c.write(a + b) ); diff --git a/examples/sort-axis.rs b/examples/sort-axis.rs index a02dad603..09410e819 100644 --- a/examples/sort-axis.rs +++ b/examples/sort-axis.rs @@ -102,7 +102,7 @@ where assert_eq!(axis_len, perm.indices.len()); debug_assert!(perm.correct()); - let mut result = Array::maybe_uninit(self.dim()); + let mut result = Array::uninit(self.dim()); unsafe { // logically move ownership of all elements from self into result diff --git a/src/data_traits.rs b/src/data_traits.rs index ef8a3ceaa..4bbe870fa 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -9,7 +9,9 @@ //! The data (inner representation) traits for ndarray use rawpointer::PointerExt; + use std::mem::{self, size_of}; +use std::mem::MaybeUninit; use std::ptr::NonNull; use alloc::sync::Arc; use alloc::vec::Vec; @@ -401,6 +403,10 @@ unsafe impl<'a, A> DataMut for ViewRepr<&'a mut A> {} /// /// ***Internal trait, see `Data`.*** pub unsafe trait DataOwned: Data { + /// Corresponding owned data with MaybeUninit elements + type MaybeUninit: DataOwned> + + RawDataSubst; + #[doc(hidden)] fn new(elements: Vec) -> Self; @@ -421,6 +427,8 @@ unsafe impl DataShared for OwnedArcRepr {} unsafe impl<'a, A> DataShared for ViewRepr<&'a A> {} unsafe impl DataOwned for OwnedRepr { + type MaybeUninit = OwnedRepr>; + fn new(elements: Vec) -> Self { OwnedRepr::from(elements) } @@ -430,6 +438,8 @@ unsafe impl DataOwned for OwnedRepr { } unsafe impl DataOwned for OwnedArcRepr { + type MaybeUninit = OwnedArcRepr>; + fn new(elements: Vec) -> Self { OwnedArcRepr(Arc::new(OwnedRepr::from(elements))) } diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 622448c58..56ba3fcfe 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -480,7 +480,7 @@ where /// Create an array with uninitalized elements, shape `shape`. /// - /// Prefer to use [`maybe_uninit()`](ArrayBase::maybe_uninit) if possible, because it is + /// Prefer to use [`uninit()`](ArrayBase::uninit) if possible, because it is /// easier to use correctly. /// /// **Panics** if the number of elements in `shape` would overflow isize. @@ -512,13 +512,7 @@ where v.set_len(size); Self::from_shape_vec_unchecked(shape, v) } -} -impl ArrayBase -where - S: DataOwned>, - D: Dimension, -{ /// Create an array with uninitalized elements, shape `shape`. /// /// The uninitialized elements of type `A` are represented by the type `MaybeUninit`, @@ -550,7 +544,7 @@ where /// /// fn shift_by_two(a: &Array2) -> Array2 { /// // create an uninitialized array - /// let mut b = Array2::maybe_uninit(a.dim()); + /// let mut b = Array2::uninit(a.dim()); /// /// // two first columns in b are two last in a /// // rest of columns in b are the initial columns in a @@ -580,6 +574,29 @@ where /// /// # shift_by_two(&Array2::zeros((8, 8))); /// ``` + pub fn uninit(shape: Sh) -> ArrayBase + where + Sh: ShapeBuilder, + { + unsafe { + let shape = shape.into_shape(); + let size = size_of_shape_checked_unwrap!(&shape.dim); + let mut v = Vec::with_capacity(size); + v.set_len(size); + ArrayBase::from_shape_vec_unchecked(shape, v) + } + } +} + +impl ArrayBase +where + S: DataOwned>, + D: Dimension, +{ + /// Create an array with uninitalized elements, shape `shape`. + /// + /// This method has been renamed to `uninit` + #[deprecated(note = "Renamed to `uninit`", since = "0.15.0")] pub fn maybe_uninit(shape: Sh) -> Self where Sh: ShapeBuilder, diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index 0b559d9c5..878444c26 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -326,7 +326,7 @@ where // Avoid initializing the memory in vec -- set it during iteration unsafe { - let mut c = Array1::maybe_uninit(m); + let mut c = Array1::uninit(m); general_mat_vec_mul_impl(A::one(), self, rhs, A::zero(), c.raw_view_mut().cast::()); c.assume_init() } diff --git a/src/stacking.rs b/src/stacking.rs index 604a7cc5b..6f4f378f4 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -91,7 +91,7 @@ where // we can safely use uninitialized values here because we will // overwrite every one of them. - let mut res = Array::maybe_uninit(res_dim); + let mut res = Array::uninit(res_dim); { let mut assign_view = res.view_mut(); @@ -159,7 +159,7 @@ where // we can safely use uninitialized values here because we will // overwrite every one of them. - let mut res = Array::maybe_uninit(res_dim); + let mut res = Array::uninit(res_dim); res.axis_iter_mut(axis) .zip(arrays.iter()) diff --git a/src/zip/mod.rs b/src/zip/mod.rs index dd0bce3f8..19b17e741 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -814,7 +814,7 @@ where pub(crate) fn uninitalized_for_current_layout(&self) -> Array, D> { let is_f = self.prefer_f(); - Array::maybe_uninit(self.dimension.clone().set_f(is_f)) + Array::uninit(self.dimension.clone().set_f(is_f)) } } diff --git a/tests/array-construct.rs b/tests/array-construct.rs index 2c195480f..993c2aa04 100644 --- a/tests/array-construct.rs +++ b/tests/array-construct.rs @@ -205,18 +205,18 @@ fn maybe_uninit_1() { unsafe { // Array - type Mat = Array, D>; + type Mat = Array; - let mut a = Mat::maybe_uninit((10, 10)); + let mut a = Mat::uninit((10, 10)); a.mapv_inplace(|_| MaybeUninit::new(1.)); let a_init = a.assume_init(); assert_eq!(a_init, Array2::from_elem(a_init.dim(), 1.)); // ArcArray - type ArcMat = ArcArray, D>; + type ArcMat = ArcArray; - let mut a = ArcMat::maybe_uninit((10, 10)); + let mut a = ArcMat::uninit((10, 10)); a.mapv_inplace(|_| MaybeUninit::new(1.)); let a2 = a.clone(); @@ -228,7 +228,7 @@ fn maybe_uninit_1() { assert_eq!(av_init, Array2::from_elem(a_init.dim(), 1.)); // RawArrayViewMut - let mut a = Mat::maybe_uninit((10, 10)); + let mut a = Mat::uninit((10, 10)); let v = a.raw_view_mut(); Zip::from(v) .for_each(|ptr| *(*ptr).as_mut_ptr() = 1.); From 00a8d4871a6af76413a3084988cd972b1f5f83c5 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 30 Jan 2021 11:17:20 +0100 Subject: [PATCH 160/651] FEAT: Add constructor method build_uninit This method allows creating any owned array and initializing it - the method helps by giving mutable access even to normally copy-on-write arrays, because when first created, the array is unshared. --- src/data_traits.rs | 7 +++++++ src/impl_constructors.rs | 33 +++++++++++++++++++++++++++++++++ src/impl_methods.rs | 11 +++++++++++ 3 files changed, 51 insertions(+) diff --git a/src/data_traits.rs b/src/data_traits.rs index 4bbe870fa..9e814a28c 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -402,6 +402,13 @@ unsafe impl<'a, A> DataMut for ViewRepr<&'a mut A> {} /// A representation that is a unique or shared owner of its data. /// /// ***Internal trait, see `Data`.*** +// The owned storage represents the ownership and allocation of the array's elements. +// The storage may be unique or shared ownership style; it must be an aliasable owner +// (permit aliasing pointers, such as our separate array head pointer). +// +// The array storage must be initially mutable - copy on write arrays may require copying for +// unsharing storage before mutating it. The initially allocated storage must be mutable so +// that it can be mutated directly - through .raw_view_mut_unchecked() - for initialization. pub unsafe trait DataOwned: Data { /// Corresponding owned data with MaybeUninit elements type MaybeUninit: DataOwned> diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 56ba3fcfe..449362133 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -586,6 +586,39 @@ where ArrayBase::from_shape_vec_unchecked(shape, v) } } + + /// Create an array with uninitalized elements, shape `shape`. + /// + /// The uninitialized elements of type `A` are represented by the type `MaybeUninit`, + /// an easier way to handle uninit values correctly. + /// + /// The `builder` closure gets unshared access to the array through a raw view + /// and can use it to modify the array before it is returned. This allows initializing + /// the array for any owned array type (avoiding clone requirements for copy-on-write, + /// because the array is unshared when initially created). + /// + /// Only *when* the array is completely initialized with valid elements, can it be + /// converted to an array of `A` elements using [`.assume_init()`]. + /// + /// **Panics** if the number of elements in `shape` would overflow isize. + /// + /// ### Safety + /// + /// The whole of the array must be initialized before it is converted + /// using [`.assume_init()`] or otherwise traversed. + /// + pub(crate) fn build_uninit(shape: Sh, builder: F) -> ArrayBase + where + Sh: ShapeBuilder, + F: FnOnce(RawArrayViewMut, D>), + { + let mut array = Self::uninit(shape); + // Safe because: the array is unshared here + unsafe { + builder(array.raw_view_mut_unchecked()); + } + array + } } impl ArrayBase diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 8f604756d..8b1b5a333 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1383,6 +1383,17 @@ where unsafe { RawArrayViewMut::new(self.ptr, self.dim.clone(), self.strides.clone()) } } + /// Return a raw mutable view of the array. + /// + /// Safety: The caller must ensure that the owned array is unshared when this is called + #[inline] + pub(crate) unsafe fn raw_view_mut_unchecked(&mut self) -> RawArrayViewMut + where + S: DataOwned, + { + RawArrayViewMut::new(self.ptr, self.dim.clone(), self.strides.clone()) + } + /// Return the array’s data as a slice, if it is contiguous and in standard order. /// Return `None` otherwise. /// From 82150cb0db92182bb4d4f23b98c99dacb3d22b35 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 30 Jan 2021 11:21:13 +0100 Subject: [PATCH 161/651] FEAT: Add internal map_collect method that collects to any array storage This generalizes .map_collect() so that it supports any DataOwned array storage. It is a crate internal method initially - it can be exported later with more experience. --- src/zip/mod.rs | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 19b17e741..f29d3e1ee 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -9,6 +9,7 @@ #[macro_use] mod zipmacro; +#[cfg(feature = "rayon")] use std::mem::MaybeUninit; use alloc::vec::Vec; @@ -811,6 +812,7 @@ where FoldWhile::Continue(acc) } + #[cfg(feature = "rayon")] pub(crate) fn uninitalized_for_current_layout(&self) -> Array, D> { let is_f = self.prefer_f(); @@ -1079,17 +1081,27 @@ macro_rules! map_impl { /// /// If all inputs are c- or f-order respectively, that is preserved in the output. pub fn map_collect(self, f: impl FnMut($($p::Item,)* ) -> R) -> Array { - // Make uninit result - let mut output = self.uninitalized_for_current_layout::(); + self.map_collect_owned(f) + } - // Use partial to counts the number of filled elements, and can drop the right - // number of elements on unwinding (if it happens during apply/collect). + pub(crate) fn map_collect_owned(self, f: impl FnMut($($p::Item,)* ) -> R) + -> ArrayBase + where S: DataOwned + { + // safe because: all elements are written before the array is completed + + let shape = self.dimension.clone().set_f(self.prefer_f()); + let output = >::build_uninit(shape, |output| { + // Use partial to count the number of filled elements, and can drop the right + // number of elements on unwinding (if it happens during apply/collect). + unsafe { + let output_view = output.cast::(); + self.and(output_view) + .collect_with_partial(f) + .release_ownership(); + } + }); unsafe { - let output_view = output.raw_view_mut().cast::(); - self.and(output_view) - .collect_with_partial(f) - .release_ownership(); - output.assume_init() } } From fdb96189f759123c8526b423aef1ef2138c29ac9 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 30 Jan 2021 11:53:03 +0100 Subject: [PATCH 162/651] API: Deprecate Array::uninitialized. Use Array::uninit instead Array::uninitialized is very hard to use correctly (internally and for our users). Prefer the new API with Array::uninit using `MaybeUninit<_>` instead. --- src/impl_constructors.rs | 73 +++++++++++++++++++++------------------- tests/array-construct.rs | 10 +++++- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 449362133..1e743fbbf 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -478,41 +478,6 @@ where } } - /// Create an array with uninitalized elements, shape `shape`. - /// - /// Prefer to use [`uninit()`](ArrayBase::uninit) if possible, because it is - /// easier to use correctly. - /// - /// **Panics** if the number of elements in `shape` would overflow isize. - /// - /// ### Safety - /// - /// Accessing uninitalized values is undefined behaviour. You must overwrite *all* the elements - /// in the array after it is created; for example using - /// [`raw_view_mut`](ArrayBase::raw_view_mut) or other low-level element access. - /// - /// The contents of the array is indeterminate before initialization and it - /// is an error to perform operations that use the previous values. For - /// example it would not be legal to use `a += 1.;` on such an array. - /// - /// This constructor is limited to elements where `A: Copy` (no destructors) - /// to avoid users shooting themselves too hard in the foot. - /// - /// (Also note that the constructors `from_shape_vec` and - /// `from_shape_vec_unchecked` allow the user yet more control, in the sense - /// that Arrays can be created from arbitrary vectors.) - pub unsafe fn uninitialized(shape: Sh) -> Self - where - A: Copy, - Sh: ShapeBuilder, - { - let shape = shape.into_shape(); - let size = size_of_shape_checked_unwrap!(&shape.dim); - let mut v = Vec::with_capacity(size); - v.set_len(size); - Self::from_shape_vec_unchecked(shape, v) - } - /// Create an array with uninitalized elements, shape `shape`. /// /// The uninitialized elements of type `A` are represented by the type `MaybeUninit`, @@ -619,6 +584,44 @@ where } array } + + #[deprecated(note = "This method is hard to use correctly. Use `uninit` instead.", + since = "0.15.0")] + /// Create an array with uninitalized elements, shape `shape`. + /// + /// Prefer to use [`uninit()`](ArrayBase::uninit) if possible, because it is + /// easier to use correctly. + /// + /// **Panics** if the number of elements in `shape` would overflow isize. + /// + /// ### Safety + /// + /// Accessing uninitalized values is undefined behaviour. You must overwrite *all* the elements + /// in the array after it is created; for example using + /// [`raw_view_mut`](ArrayBase::raw_view_mut) or other low-level element access. + /// + /// The contents of the array is indeterminate before initialization and it + /// is an error to perform operations that use the previous values. For + /// example it would not be legal to use `a += 1.;` on such an array. + /// + /// This constructor is limited to elements where `A: Copy` (no destructors) + /// to avoid users shooting themselves too hard in the foot. + /// + /// (Also note that the constructors `from_shape_vec` and + /// `from_shape_vec_unchecked` allow the user yet more control, in the sense + /// that Arrays can be created from arbitrary vectors.) + pub unsafe fn uninitialized(shape: Sh) -> Self + where + A: Copy, + Sh: ShapeBuilder, + { + let shape = shape.into_shape(); + let size = size_of_shape_checked_unwrap!(&shape.dim); + let mut v = Vec::with_capacity(size); + v.set_len(size); + Self::from_shape_vec_unchecked(shape, v) + } + } impl ArrayBase diff --git a/tests/array-construct.rs b/tests/array-construct.rs index 993c2aa04..2b72ab039 100644 --- a/tests/array-construct.rs +++ b/tests/array-construct.rs @@ -52,6 +52,7 @@ fn test_arcarray_thread_safe() { #[test] #[cfg(feature = "std")] +#[allow(deprecated)] // uninitialized fn test_uninit() { unsafe { let mut a = Array::::uninitialized((3, 4).f()); @@ -192,12 +193,19 @@ fn deny_wraparound_from_shape_fn() { #[should_panic] #[test] -fn deny_wraparound_uninit() { +#[allow(deprecated)] // uninitialized +fn deny_wraparound_uninitialized() { unsafe { let _five_large = Array::::uninitialized((3, 7, 29, 36760123, 823996703)); } } +#[should_panic] +#[test] +fn deny_wraparound_uninit() { + let _five_large = Array::::uninit((3, 7, 29, 36760123, 823996703)); +} + #[test] fn maybe_uninit_1() { From 802aa5419d220fce01ae1b4f1dde3261a3da0d5f Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 30 Jan 2021 12:07:13 +0100 Subject: [PATCH 163/651] MAINT: Increase MSRV to 1.49 1.49 had a lot of fixes to associated types and bounds, and that enables the new DataOwned changes to compile. --- .github/workflows/ci.yml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86aa8b727..92c184ca8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: - stable - beta - nightly - - 1.42.0 # MSRV + - 1.49.0 # MSRV steps: - uses: actions/checkout@v2 diff --git a/src/lib.rs b/src/lib.rs index 11064d32d..e9a60af15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,7 +62,7 @@ //! needs matching memory layout to be efficient (with some exceptions). //! + Efficient floating point matrix multiplication even for very large //! matrices; can optionally use BLAS to improve it further. -//! - **Requires Rust 1.42 or later** +//! - **Requires Rust 1.49 or later** //! //! ## Crate Feature Flags //! From f954bf8cc32e0ca74623798e1c850a22c081f1f3 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 30 Jan 2021 12:51:05 +0100 Subject: [PATCH 164/651] TEST: Add tests for C+F contig layouts --- src/layout/mod.rs | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 24dd09958..625fc7ff2 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -78,6 +78,30 @@ mod tests { use crate::NdProducer; type M = Array2; + type M1 = Array1; + type M0 = Array0; + + macro_rules! assert_layouts { + ($mat:expr, $($layout:expr),*) => {{ + let layout = $mat.view().layout(); + $( + assert!(layout.is($layout), "Assertion failed: array {:?} is not layout {}", + $mat, + stringify!($layout)); + )* + }} + } + + macro_rules! assert_not_layouts { + ($mat:expr, $($layout:expr),*) => {{ + let layout = $mat.view().layout(); + $( + assert!(!layout.is($layout), "Assertion failed: array {:?} show not have layout {}", + $mat, + stringify!($layout)); + )* + }} + } #[test] fn contig_layouts() { @@ -91,6 +115,34 @@ mod tests { assert!(af.is(FORDER) && af.is(FPREFER)); } + #[test] + fn contig_cf_layouts() { + let a = M::zeros((5, 1)); + let b = M::zeros((1, 5).f()); + assert_layouts!(a, CORDER, CPREFER, FORDER, FPREFER); + assert_layouts!(b, CORDER, CPREFER, FORDER, FPREFER); + + let a = M1::zeros(5); + let b = M1::zeros(5.f()); + assert_layouts!(a, CORDER, CPREFER, FORDER, FPREFER); + assert_layouts!(b, CORDER, CPREFER, FORDER, FPREFER); + + let a = M0::zeros(()); + assert_layouts!(a, CORDER, CPREFER, FORDER, FPREFER); + + let a = M::zeros((5, 5)); + let b = M::zeros((5, 5).f()); + let arow = a.slice(s![..1, ..]); + let bcol = b.slice(s![.., ..1]); + assert_layouts!(arow, CORDER, CPREFER, FORDER, FPREFER); + assert_layouts!(bcol, CORDER, CPREFER, FORDER, FPREFER); + + let acol = a.slice(s![.., ..1]); + let brow = b.slice(s![..1, ..]); + assert_not_layouts!(acol, CORDER, CPREFER, FORDER, FPREFER); + assert_not_layouts!(brow, CORDER, CPREFER, FORDER, FPREFER); + } + #[test] fn stride_layouts() { let a = M::zeros((5, 5)); From f528d00495a5fce9068ccca237b86f53a71c8c5c Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 30 Jan 2021 15:47:01 +0100 Subject: [PATCH 165/651] FIX: Use raw pointers for .swap(i, j) This fixes a minor issue where we want to avoid overlapping &mut from the same array. --- src/impl_methods.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 8b1b5a333..f8c0ee919 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -6,7 +6,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::ptr as std_ptr; use alloc::slice; use alloc::vec; use alloc::vec::Vec; @@ -630,11 +629,18 @@ where S: DataMut, I: NdIndex, { - let ptr1: *mut _ = &mut self[index1]; - let ptr2: *mut _ = &mut self[index2]; - unsafe { - std_ptr::swap(ptr1, ptr2); + let ptr = self.as_mut_ptr(); + let offset1 = index1.index_checked(&self.dim, &self.strides); + let offset2 = index2.index_checked(&self.dim, &self.strides); + if let Some(offset1) = offset1 { + if let Some(offset2) = offset2 { + unsafe { + std::ptr::swap(ptr.offset(offset1), ptr.offset(offset2)); + } + return; + } } + panic!("swap: index out of bounds for indices {:?} {:?}", index1, index2); } /// Swap elements *unchecked* at indices `index1` and `index2`. @@ -661,7 +667,7 @@ where arraytraits::debug_bounds_check(self, &index2); let off1 = index1.index_unchecked(&self.strides); let off2 = index2.index_unchecked(&self.strides); - std_ptr::swap( + std::ptr::swap( self.ptr.as_ptr().offset(off1), self.ptr.as_ptr().offset(off2), ); From fb77592f54beccd6e607a60d8e6b971acaf9d7a5 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Mon, 1 Feb 2021 15:31:56 +0800 Subject: [PATCH 166/651] replace Zip::apply with Zip::for_each in benches and docs --- benches/bench1.rs | 22 +++++++++++----------- benches/iter.rs | 6 +++--- benches/par_rayon.rs | 4 ++-- benches/zip.rs | 12 ++++++------ src/impl_constructors.rs | 2 +- src/lib.rs | 2 +- src/parallel/mod.rs | 8 ++++---- src/zip/mod.rs | 8 ++++---- tests/par_zip.rs | 2 +- 9 files changed, 33 insertions(+), 33 deletions(-) diff --git a/benches/bench1.rs b/benches/bench1.rs index c24729d6f..0486bd092 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -255,7 +255,7 @@ fn add_2d_zip(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| { - Zip::from(&mut a).and(&b).apply(|a, &b| *a += b); + Zip::from(&mut a).and(&b).for_each(|a, &b| *a += b); }); } @@ -284,7 +284,7 @@ fn add_2d_alloc_zip_collect(bench: &mut test::Bencher) { let a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| { - Zip::from(&a).and(&b).apply_collect(|&x, &y| x + y) + Zip::from(&a).and(&b).map_collect(|&x, &y| x + y) }); } @@ -300,7 +300,7 @@ fn vec_string_collect(bench: &mut test::Bencher) { fn array_string_collect(bench: &mut test::Bencher) { let v = Array::from(vec![""; 10240]); bench.iter(|| { - Zip::from(&v).apply_collect(|s| s.to_owned()) + Zip::from(&v).map_collect(|s| s.to_owned()) }); } @@ -316,7 +316,7 @@ fn vec_f64_collect(bench: &mut test::Bencher) { fn array_f64_collect(bench: &mut test::Bencher) { let v = Array::from(vec![1.; 10240]); bench.iter(|| { - Zip::from(&v).apply_collect(|s| s + 1.) + Zip::from(&v).map_collect(|s| s + 1.) }); } @@ -350,7 +350,7 @@ fn add_2d_zip_cutout(bench: &mut test::Bencher) { let mut acut = a.slice_mut(s![1..-1, 1..-1]); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| { - Zip::from(&mut acut).and(&b).apply(|a, &b| *a += b); + Zip::from(&mut acut).and(&b).for_each(|a, &b| *a += b); }); } @@ -363,7 +363,7 @@ fn add_2d_cutouts_by_4(bench: &mut test::Bencher) { bench.iter(|| { Zip::from(a.exact_chunks_mut(chunksz)) .and(b.exact_chunks(chunksz)) - .apply(|mut a, b| a += &b); + .for_each(|mut a, b| a += &b); }); } @@ -376,7 +376,7 @@ fn add_2d_cutouts_by_16(bench: &mut test::Bencher) { bench.iter(|| { Zip::from(a.exact_chunks_mut(chunksz)) .and(b.exact_chunks(chunksz)) - .apply(|mut a, b| a += &b); + .for_each(|mut a, b| a += &b); }); } @@ -389,7 +389,7 @@ fn add_2d_cutouts_by_32(bench: &mut test::Bencher) { bench.iter(|| { Zip::from(a.exact_chunks_mut(chunksz)) .and(b.exact_chunks(chunksz)) - .apply(|mut a, b| a += &b); + .for_each(|mut a, b| a += &b); }); } @@ -511,7 +511,7 @@ fn add_2d_zip_strided(bench: &mut test::Bencher) { let mut a = a.slice_mut(s![.., ..;2]); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| { - Zip::from(&mut a).and(&b).apply(|a, &b| *a += b); + Zip::from(&mut a).and(&b).for_each(|a, &b| *a += b); }); } @@ -531,7 +531,7 @@ fn add_2d_zip_one_transposed(bench: &mut test::Bencher) { a.swap_axes(0, 1); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| { - Zip::from(&mut a).and(&b).apply(|a, &b| *a += b); + Zip::from(&mut a).and(&b).for_each(|a, &b| *a += b); }); } @@ -553,7 +553,7 @@ fn add_2d_zip_both_transposed(bench: &mut test::Bencher) { let mut b = Array::::zeros((ADD2DSZ, ADD2DSZ)); b.swap_axes(0, 1); bench.iter(|| { - Zip::from(&mut a).and(&b).apply(|a, &b| *a += b); + Zip::from(&mut a).and(&b).for_each(|a, &b| *a += b); }); } diff --git a/benches/iter.rs b/benches/iter.rs index 8b89e721b..d8c716dc8 100644 --- a/benches/iter.rs +++ b/benches/iter.rs @@ -249,7 +249,7 @@ fn indexed_zip_1d_ix1(bench: &mut Bencher) { } bench.iter(|| { - Zip::indexed(&a).apply(|i, &_elt| { + Zip::indexed(&a).for_each(|i, &_elt| { black_box(i); //assert!(a[i] == elt); }); @@ -278,7 +278,7 @@ fn indexed_zip_2d_ix2(bench: &mut Bencher) { } bench.iter(|| { - Zip::indexed(&a).apply(|i, &_elt| { + Zip::indexed(&a).for_each(|i, &_elt| { black_box(i); //assert!(a[i] == elt); }); @@ -308,7 +308,7 @@ fn indexed_zip_3d_ix3(bench: &mut Bencher) { } bench.iter(|| { - Zip::indexed(&a).apply(|i, &_elt| { + Zip::indexed(&a).for_each(|i, &_elt| { black_box(i); //assert!(a[i] == elt); }); diff --git a/benches/par_rayon.rs b/benches/par_rayon.rs index cca35e00b..18176d846 100644 --- a/benches/par_rayon.rs +++ b/benches/par_rayon.rs @@ -152,7 +152,7 @@ fn vec_string_collect(bench: &mut test::Bencher) { fn array_string_collect(bench: &mut test::Bencher) { let v = Array::from_elem((COLL_STRING_N, COLL_STRING_N), ""); bench.iter(|| { - Zip::from(&v).par_apply_collect(|s| s.to_owned()) + Zip::from(&v).par_map_collect(|s| s.to_owned()) }); } @@ -168,7 +168,7 @@ fn vec_f64_collect(bench: &mut test::Bencher) { fn array_f64_collect(bench: &mut test::Bencher) { let v = Array::from_elem((COLL_F64_N, COLL_F64_N), 1.); bench.iter(|| { - Zip::from(&v).par_apply_collect(|s| s + 1.) + Zip::from(&v).par_map_collect(|s| s + 1.) }); } diff --git a/benches/zip.rs b/benches/zip.rs index ac7bef589..10e1dee3c 100644 --- a/benches/zip.rs +++ b/benches/zip.rs @@ -10,7 +10,7 @@ pub fn zip_copy<'a, A, P, Q>(data: P, out: Q) Q: IntoNdProducer, A: Copy + 'a { - Zip::from(data).and(out).apply(|&i, o| { + Zip::from(data).and(out).for_each(|&i, o| { *o = i; }); } @@ -25,14 +25,14 @@ pub fn zip_copy_split<'a, A, P, Q>(data: P, out: Q) let (z11, z12) = z1.split(); let (z21, z22) = z2.split(); let f = |&i: &A, o: &mut A| *o = i; - z11.apply(f); - z12.apply(f); - z21.apply(f); - z22.apply(f); + z11.for_each(f); + z12.for_each(f); + z21.for_each(f); + z22.for_each(f); } pub fn zip_indexed(data: &Array3, out: &mut Array3) { - Zip::indexed(data).and(out).apply(|idx, &i, o| { + Zip::indexed(data).and(out).for_each(|idx, &i, o| { let _ = black_box(idx); *o = i; }); diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 1e743fbbf..d556934d9 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -534,7 +534,7 @@ where /// A: Clone + 'a /// { /// Zip::from(from) - /// .apply_assign_into(to, A::clone); + /// .map_assign_into(to, A::clone); /// } /// /// # shift_by_two(&Array2::zeros((8, 8))); diff --git a/src/lib.rs b/src/lib.rs index e9a60af15..c71f09aaf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -447,7 +447,7 @@ pub type Ixs = isize; /// /// Zip::from(a.rows()) /// .and(&mut b) -/// .apply(|a_row, b_elt| { +/// .for_each(|a_row, b_elt| { /// *b_elt = a_row[a.ncols() - 1] - a_row[0]; /// }); /// ``` diff --git a/src/parallel/mod.rs b/src/parallel/mod.rs index 12059cee4..dd3b89341 100644 --- a/src/parallel/mod.rs +++ b/src/parallel/mod.rs @@ -21,9 +21,9 @@ //! //! - [`ArrayBase::par_map_inplace()`] //! - [`ArrayBase::par_mapv_inplace()`] -//! - [`Zip::par_apply()`] (all arities) -//! - [`Zip::par_apply_collect()`] (all arities) -//! - [`Zip::par_apply_assign_into()`] (all arities) +//! - [`Zip::par_for_each()`] (all arities) +//! - [`Zip::par_map_collect()`] (all arities) +//! - [`Zip::par_map_assign_into()`] (all arities) //! //! Note that you can use the parallel iterator for [Zip] to access all other //! rayon parallel iterator methods. @@ -115,7 +115,7 @@ //! Zip::from(&mut c) //! .and(&a) //! .and(&b) -//! .par_apply(|c, &a, &b| { +//! .par_for_each(|c, &a, &b| { //! *c += a - b; //! }); //! } diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 2e8421cde..e0d456fc0 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -571,9 +571,9 @@ impl NdProducer for RawArrayViewMut { /// assert_eq!(totals, a.sum_axis(Axis(1))); /// /// -/// // Example 3: Recreate Example 2 using apply_collect to make a new array +/// // Example 3: Recreate Example 2 using map_collect to make a new array /// -/// let mut totals2 = Zip::from(a.rows()).apply_collect(|row| row.sum()); +/// let mut totals2 = Zip::from(a.rows()).map_collect(|row| row.sum()); /// /// // Check the result against the previous example. /// assert_eq!(totals, totals2); @@ -722,7 +722,7 @@ where } } - /// The innermost loop of the Zip apply methods + /// The innermost loop of the Zip for_each methods /// /// Run the fold while operation on a stretch of elements with constant strides /// @@ -1165,7 +1165,7 @@ macro_rules! map_impl { $($p: NdProducer ,)* PLast: NdProducer, { - /// The inner workings of apply_collect and par_apply_collect + /// The inner workings of map_collect and par_map_collect /// /// Apply the function and collect the results into the output (last producer) /// which should be a raw array view; a Partial that owns the written diff --git a/tests/par_zip.rs b/tests/par_zip.rs index 019029119..ee929f36e 100644 --- a/tests/par_zip.rs +++ b/tests/par_zip.rs @@ -76,7 +76,7 @@ fn test_zip_index_4() { fn test_zip_collect() { use approx::assert_abs_diff_eq; - // test Zip::apply_collect and that it preserves c/f layout. + // test Zip::map_collect and that it preserves c/f layout. let b = Array::from_shape_fn((M, N), |(i, j)| 1. / (i + 2 * j + 1) as f32); let c = Array::from_shape_fn((M, N), |(i, j)| f32::ln((1 + i + j) as f32)); From 73b4f848394876da49f1f2ed51f90c28dab3aa04 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 2 Feb 2021 20:02:27 -0500 Subject: [PATCH 167/651] Rename .visit() to .for_each() This is more consistent with `std::iter::Iterator` and `ndarray::Zip`. --- src/doc/ndarray_for_numpy_users/mod.rs | 4 ++-- src/impl_methods.rs | 17 +++++++++++++++-- src/numeric/impl_numeric.rs | 2 +- tests/array.rs | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index fff72ce25..45ef3ed06 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -282,7 +282,7 @@ //! Note that [`.mapv()`][.mapv()] has corresponding methods [`.map()`][.map()], //! [`.mapv_into()`][.mapv_into()], [`.map_inplace()`][.map_inplace()], and //! [`.mapv_inplace()`][.mapv_inplace()]. Also look at [`.fold()`][.fold()], -//! [`.visit()`][.visit()], [`.fold_axis()`][.fold_axis()], and +//! [`.for_each()`][.for_each()], [`.fold_axis()`][.fold_axis()], and //! [`.map_axis()`][.map_axis()]. //! //! @@ -648,7 +648,7 @@ //! [.sum_axis()]: ../../struct.ArrayBase.html#method.sum_axis //! [.t()]: ../../struct.ArrayBase.html#method.t //! [vec-* dot]: ../../struct.ArrayBase.html#method.dot -//! [.visit()]: ../../struct.ArrayBase.html#method.visit +//! [.for_each()]: ../../struct.ArrayBase.html#method.for_each //! [::zeros()]: ../../struct.ArrayBase.html#method.zeros //! [Zip]: ../../struct.Zip.html diff --git a/src/impl_methods.rs b/src/impl_methods.rs index f8c0ee919..214d454f8 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2220,17 +2220,30 @@ where self.unordered_foreach_mut(move |x| *x = f(x.clone())); } + /// Call `f` for each element in the array. + /// + /// Elements are visited in arbitrary order. + pub fn for_each<'a, F>(&'a self, mut f: F) + where + F: FnMut(&'a A), + A: 'a, + S: Data, + { + self.fold((), move |(), elt| f(elt)) + } + /// Visit each element in the array by calling `f` by reference /// on each element. /// /// Elements are visited in arbitrary order. - pub fn visit<'a, F>(&'a self, mut f: F) + #[deprecated(note="Renamed to .for_each()", since="0.15.0")] + pub fn visit<'a, F>(&'a self, f: F) where F: FnMut(&'a A), A: 'a, S: Data, { - self.fold((), move |(), elt| f(elt)) + self.for_each(f) } /// Fold along an axis. diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 9d1cef7e1..fbec80418 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -168,7 +168,7 @@ where let mut mean = A::zero(); let mut sum_sq = A::zero(); let mut i = 0; - self.visit(|&x| { + self.for_each(|&x| { let count = A::from_usize(i + 1).expect("Converting index to `A` must not fail."); let delta = x - mean; mean = mean + delta / count; diff --git a/tests/array.rs b/tests/array.rs index 6581e572e..dddcd5e1e 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -925,7 +925,7 @@ fn zero_axes() { } a.map(|_| panic!()); a.map_inplace(|_| panic!()); - a.visit(|_| panic!()); + a.for_each(|_| panic!()); println!("{:?}", a); let b = arr2::(&[[], [], [], []]); println!("{:?}\n{:?}", b.shape(), b); From 5ff79b173ee8f10f88b28439bc0b497845f5ec58 Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 2 Feb 2021 19:07:45 +0100 Subject: [PATCH 168/651] TEST: Benchmarks for .into_dimensionality() --- benches/bench1.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/benches/bench1.rs b/benches/bench1.rs index 0486bd092..a6fa86deb 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -14,6 +14,7 @@ use std::mem::MaybeUninit; use ndarray::ShapeBuilder; use ndarray::{arr0, arr1, arr2, azip, s}; use ndarray::{Array, Array1, Array2, Axis, Ix, Zip}; +use ndarray::{Ix1, Ix2, Ix3, Ix5, IxDyn}; use test::black_box; @@ -941,3 +942,59 @@ fn sum_axis1(bench: &mut test::Bencher) { let a = range_mat(MEAN_SUM_N, MEAN_SUM_N); bench.iter(|| a.sum_axis(Axis(1))); } + +#[bench] +fn into_dimensionality_ix1_ok(bench: &mut test::Bencher) { + let a = Array::::zeros(Ix1(10)); + let a = a.view(); + bench.iter(|| a.into_dimensionality::()); +} + +#[bench] +fn into_dimensionality_ix3_ok(bench: &mut test::Bencher) { + let a = Array::::zeros(Ix3(10, 10, 10)); + let a = a.view(); + bench.iter(|| a.into_dimensionality::()); +} + +#[bench] +fn into_dimensionality_ix3_err(bench: &mut test::Bencher) { + let a = Array::::zeros(Ix3(10, 10, 10)); + let a = a.view(); + bench.iter(|| a.into_dimensionality::()); +} + +#[bench] +fn into_dimensionality_dyn_to_ix3(bench: &mut test::Bencher) { + let a = Array::::zeros(IxDyn(&[10, 10, 10])); + let a = a.view(); + bench.iter(|| a.clone().into_dimensionality::()); +} + +#[bench] +fn into_dimensionality_dyn_to_dyn(bench: &mut test::Bencher) { + let a = Array::::zeros(IxDyn(&[10, 10, 10])); + let a = a.view(); + bench.iter(|| a.clone().into_dimensionality::()); +} + +#[bench] +fn into_dyn_ix3(bench: &mut test::Bencher) { + let a = Array::::zeros(Ix3(10, 10, 10)); + let a = a.view(); + bench.iter(|| a.into_dyn()); +} + +#[bench] +fn into_dyn_ix5(bench: &mut test::Bencher) { + let a = Array::::zeros(Ix5(2, 2, 2, 2, 2)); + let a = a.view(); + bench.iter(|| a.into_dyn()); +} + +#[bench] +fn into_dyn_dyn(bench: &mut test::Bencher) { + let a = Array::::zeros(IxDyn(&[10, 10, 10])); + let a = a.view(); + bench.iter(|| a.clone().into_dyn()); +} From a62b54126f70ee2119c7803a0c1d5e296aeb8ed5 Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 2 Feb 2021 19:08:04 +0100 Subject: [PATCH 169/651] FIX: Add fast case for .into_dimensionality() no-op. This case has most impact for IxDyn, the benchmark for const dims is unchanged (it was already fast). --- src/impl_methods.rs | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index f8c0ee919..341a90286 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::mem::{size_of, ManuallyDrop}; use alloc::slice; use alloc::vec; use alloc::vec::Vec; @@ -1583,8 +1584,11 @@ where } } - /// Convert an array or array view to another with the same type, but - /// different dimensionality type. Errors if the dimensions don't agree. + /// Convert an array or array view to another with the same type, but different dimensionality + /// type. Errors if the dimensions don't agree (the number of axes must match). + /// + /// Note that conversion to a dynamic dimensional array will never fail (and is equivalent to + /// the `into_dyn` method). /// /// ``` /// use ndarray::{ArrayD, Ix2, IxDyn}; @@ -1600,8 +1604,11 @@ where where D2: Dimension, { - if let Some(dim) = D2::from_dimension(&self.dim) { - if let Some(strides) = D2::from_dimension(&self.strides) { + if D::NDIM == D2::NDIM { + // safe because D == D2 + unsafe { + let dim = unlimited_transmute::(self.dim); + let strides = unlimited_transmute::(self.strides); return Ok(ArrayBase { data: self.data, ptr: self.ptr, @@ -1609,6 +1616,17 @@ where strides, }); } + } else if D::NDIM == None || D2::NDIM == None { // one is dynamic dim + if let Some(dim) = D2::from_dimension(&self.dim) { + if let Some(strides) = D2::from_dimension(&self.strides) { + return Ok(ArrayBase { + data: self.data, + ptr: self.ptr, + dim, + strides, + }); + } + } } Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)) } @@ -2375,3 +2393,18 @@ where }); } } + + +/// Transmute from A to B. +/// +/// Like transmute, but does not have the compile-time size check which blocks +/// using regular transmute in some cases. +/// +/// **Panics** if the size of A and B are different. +#[inline] +unsafe fn unlimited_transmute(data: A) -> B { + // safe when sizes are equal and caller guarantees that representations are equal + assert_eq!(size_of::(), size_of::()); + let old_data = ManuallyDrop::new(data); + (&*old_data as *const A as *const B).read() +} From 83cf00dae120de62377844bc47a02d975037f4a6 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 3 Feb 2021 10:29:09 +0100 Subject: [PATCH 170/651] FIX: Add fast case for dyn -> dyn conversion to speed up .into_dyn() This method was simply not special cased for `IxDyn`. --- src/dimension/dimension_trait.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index aa1f7f95a..18a10c3f0 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -929,6 +929,11 @@ impl Dimension for IxDyn { fn from_dimension(d: &D2) -> Option { Some(IxDyn(d.slice())) } + + fn into_dyn(self) -> IxDyn { + self + } + private_impl! {} } From 50419d330be805f7120675632118167d36fc1940 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 3 Feb 2021 13:01:25 +0100 Subject: [PATCH 171/651] FEAT: Add internal array builder methods from_data_ptr and with_strides_dim These methods will be used instead of the ArrayBase struct constructor expression. --- src/impl_internal_constructors.rs | 65 +++++++++++++++++++++++++++++++ src/impl_methods.rs | 2 +- src/lib.rs | 1 + 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/impl_internal_constructors.rs diff --git a/src/impl_internal_constructors.rs b/src/impl_internal_constructors.rs new file mode 100644 index 000000000..18e8e6874 --- /dev/null +++ b/src/impl_internal_constructors.rs @@ -0,0 +1,65 @@ +// Copyright 2021 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ptr::NonNull; + +use crate::imp_prelude::*; + +// internal "builder-like" methods +impl ArrayBase +where + S: RawData, +{ + /// Create an (initially) empty one-dimensional array from the given data and array head + /// pointer + /// + /// ## Safety + /// + /// The caller must ensure that the data storage and pointer is valid. + /// + /// See ArrayView::from_shape_ptr for general pointer validity documentation. + pub(crate) unsafe fn from_data_ptr(data: S, ptr: NonNull) -> Self { + let array = ArrayBase { + data: data, + ptr: ptr, + dim: Ix1(0), + strides: Ix1(1), + }; + debug_assert!(array.pointer_is_inbounds()); + array + } +} + +// internal "builder-like" methods +impl ArrayBase +where + S: RawData, + D: Dimension, +{ + + /// Set strides and dimension of the array to the new values + /// + /// The argument order with strides before dimensions is used because strides are often + /// computed as derived from the dimension. + /// + /// ## Safety + /// + /// The caller needs to ensure that the new strides and dimensions are correct + /// for the array data. + pub(crate) unsafe fn with_strides_dim(self, strides: E, dim: E) -> ArrayBase + where + E: Dimension + { + ArrayBase { + data: self.data, + ptr: self.ptr, + dim, + strides, + } + } +} diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 7b6f3f6f5..227c2063a 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1942,7 +1942,7 @@ where self.index_axis_move(axis, 0) } - fn pointer_is_inbounds(&self) -> bool { + pub(crate) fn pointer_is_inbounds(&self) -> bool { match self.data._data_slice() { None => { // special case for non-owned views diff --git a/src/lib.rs b/src/lib.rs index c71f09aaf..1d56dc254 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1488,6 +1488,7 @@ impl<'a, A> CowRepr<'a, A> { mod impl_clone; +mod impl_internal_constructors; mod impl_constructors; mod impl_methods; From bb6c4ab0e0507d804d478bb581ceb9326fae6886 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 3 Feb 2021 13:23:41 +0100 Subject: [PATCH 172/651] FIX: Use array builder from_data_ptr(..).with_strides_dim() where possible This avoids using the naked ArrayBase { .. } and similar struct constructors, which should make it easier for us to change representation in the future if needed. The constructor methods are unsafe since they rely on the caller to assert certain invariants; this means that this change increases the number of `unsafe` code blocks in ndarray, but that's only correct since every ArrayBase construction is safety-critical w.r.t the validity of the pointer, dimensions and strides taken together. The resulting code is often easier to read - especially when we are only updating the strides and dimensions of an existing array or view. The .with_strides_dim() method requires the Dimension trait so that appropriate debug assertions can be added in this method if desired. This precludes us from using this method in the `Clone` impl, but that's just a minor flaw and the only exception. --- src/data_traits.rs | 18 ++--- src/impl_clone.rs | 1 + src/impl_constructors.rs | 9 +-- src/impl_cow.rs | 18 ++--- src/impl_methods.rs | 127 +++++++++++------------------- src/impl_raw_views.rs | 16 +--- src/impl_special_element_types.rs | 8 +- src/impl_views/constructors.rs | 14 +--- src/lib.rs | 8 +- 9 files changed, 77 insertions(+), 142 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index 9e814a28c..2b59fcb8d 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -245,11 +245,10 @@ unsafe impl Data for OwnedArcRepr { { Self::ensure_unique(&mut self_); let data = Arc::try_unwrap(self_.data.0).ok().unwrap(); - ArrayBase { - data, - ptr: self_.ptr, - dim: self_.dim, - strides: self_.strides, + // safe because data is equivalent + unsafe { + ArrayBase::from_data_ptr(data, self_.ptr) + .with_strides_dim(self_.strides, self_.dim) } } @@ -544,11 +543,10 @@ unsafe impl<'a, A> Data for CowRepr<'a, A> { { match self_.data { CowRepr::View(_) => self_.to_owned(), - CowRepr::Owned(data) => ArrayBase { - data, - ptr: self_.ptr, - dim: self_.dim, - strides: self_.strides, + CowRepr::Owned(data) => unsafe { + // safe because the data is equivalent so ptr, dims remain valid + ArrayBase::from_data_ptr(data, self_.ptr) + .with_strides_dim(self_.strides, self_.dim) }, } } diff --git a/src/impl_clone.rs b/src/impl_clone.rs index 603708849..e2e111a12 100644 --- a/src/impl_clone.rs +++ b/src/impl_clone.rs @@ -11,6 +11,7 @@ use crate::RawDataClone; impl Clone for ArrayBase { fn clone(&self) -> ArrayBase { + // safe because `clone_with_ptr` promises to provide equivalent data and ptr unsafe { let (data, ptr) = self.data.clone_with_ptr(self.ptr); ArrayBase { diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index d556934d9..9af8048f6 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -470,12 +470,9 @@ where 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).is_ok()); - ArrayBase { - ptr: nonnull_from_vec_data(&mut v).offset(-offset_from_ptr_to_memory(&dim, &strides)), - data: DataOwned::new(v), - strides, - dim, - } + + let ptr = nonnull_from_vec_data(&mut v).offset(-offset_from_ptr_to_memory(&dim, &strides)); + ArrayBase::from_data_ptr(DataOwned::new(v), ptr).with_strides_dim(strides, dim) } /// Create an array with uninitalized elements, shape `shape`. diff --git a/src/impl_cow.rs b/src/impl_cow.rs index 52a06bfa2..b880fd62c 100644 --- a/src/impl_cow.rs +++ b/src/impl_cow.rs @@ -33,11 +33,10 @@ where D: Dimension, { fn from(view: ArrayView<'a, A, D>) -> CowArray<'a, A, D> { - ArrayBase { - data: CowRepr::View(view.data), - ptr: view.ptr, - dim: view.dim, - strides: view.strides, + // safe because equivalent data + unsafe { + ArrayBase::from_data_ptr(CowRepr::View(view.data), view.ptr) + .with_strides_dim(view.strides, view.dim) } } } @@ -47,11 +46,10 @@ where D: Dimension, { fn from(array: Array) -> CowArray<'a, A, D> { - ArrayBase { - data: CowRepr::Owned(array.data), - ptr: array.ptr, - dim: array.dim, - strides: array.strides, + // safe because equivalent data + unsafe { + ArrayBase::from_data_ptr(CowRepr::Owned(array.data), array.ptr) + .with_strides_dim(array.strides, array.dim) } } } diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 227c2063a..9eea42d15 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -242,11 +242,9 @@ where S: DataOwned, { let data = self.data.into_shared(); - ArrayBase { - data, - ptr: self.ptr, - dim: self.dim, - strides: self.strides, + // safe because: equivalent unmoved data, ptr and dims remain valid + unsafe { + ArrayBase::from_data_ptr(data, self.ptr).with_strides_dim(self.strides, self.dim) } } @@ -434,11 +432,9 @@ where *new_s = *s; }); - ArrayBase { - ptr: self.ptr, - data: self.data, - dim: new_dim, - strides: new_strides, + // safe because new dimension, strides allow access to a subset of old data + unsafe { + self.with_strides_dim(new_strides, new_dim) } } @@ -757,11 +753,9 @@ where self.collapse_axis(axis, index); let dim = self.dim.remove_axis(axis); let strides = self.strides.remove_axis(axis); - ArrayBase { - ptr: self.ptr, - data: self.data, - dim, - strides, + // safe because new dimension, strides allow access to a subset of old data + unsafe { + self.with_strides_dim(strides, dim) } } @@ -1244,11 +1238,9 @@ where /// Return the diagonal as a one-dimensional array. pub fn into_diag(self) -> ArrayBase { let (len, stride) = self.diag_params(); - ArrayBase { - data: self.data, - ptr: self.ptr, - dim: Ix1(len), - strides: Ix1(stride as Ix), + // safe because new len stride allows access to a subset of the current elements + unsafe { + self.with_strides_dim(Ix1(stride as Ix), Ix1(len)) } } @@ -1498,22 +1490,15 @@ where return Err(error::incompatible_shapes(&self.dim, &shape)); } // Check if contiguous, if not => copy all, else just adapt strides - if self.is_standard_layout() { - Ok(ArrayBase { - data: self.data, - ptr: self.ptr, - strides: shape.default_strides(), - dim: shape, - }) - } else if self.ndim() > 1 && self.raw_view().reversed_axes().is_standard_layout() { - Ok(ArrayBase { - data: self.data, - ptr: self.ptr, - strides: shape.fortran_strides(), - dim: shape, - }) - } else { - Err(error::from_kind(error::ErrorKind::IncompatibleLayout)) + unsafe { + // safe because arrays are contiguous and len is unchanged + if self.is_standard_layout() { + Ok(self.with_strides_dim(shape.default_strides(), shape)) + } else if self.ndim() > 1 && self.raw_view().reversed_axes().is_standard_layout() { + Ok(self.with_strides_dim(shape.fortran_strides(), shape)) + } else { + Err(error::from_kind(error::ErrorKind::IncompatibleLayout)) + } } } @@ -1554,11 +1539,9 @@ where // Check if contiguous, if not => copy all, else just adapt strides if self.is_standard_layout() { let cl = self.clone(); - ArrayBase { - data: cl.data, - ptr: cl.ptr, - strides: shape.default_strides(), - dim: shape, + // safe because array is contiguous and shape has equal number of elements + unsafe { + cl.with_strides_dim(shape.default_strides(), shape) } } else { let v = self.iter().cloned().collect::>(); @@ -1576,11 +1559,10 @@ where /// [3, 4]]).into_dyn(); /// ``` pub fn into_dyn(self) -> ArrayBase { - ArrayBase { - data: self.data, - ptr: self.ptr, - dim: self.dim.into_dyn(), - strides: self.strides.into_dyn(), + // safe because new dims equivalent + unsafe { + ArrayBase::from_data_ptr(self.data, self.ptr) + .with_strides_dim(self.strides.into_dyn(), self.dim.into_dyn()) } } @@ -1604,27 +1586,19 @@ where where D2: Dimension, { - if D::NDIM == D2::NDIM { - // safe because D == D2 - unsafe { + unsafe { + if D::NDIM == D2::NDIM { + // safe because D == D2 let dim = unlimited_transmute::(self.dim); let strides = unlimited_transmute::(self.strides); - return Ok(ArrayBase { - data: self.data, - ptr: self.ptr, - dim, - strides, - }); - } - } else if D::NDIM == None || D2::NDIM == None { // one is dynamic dim - if let Some(dim) = D2::from_dimension(&self.dim) { - if let Some(strides) = D2::from_dimension(&self.strides) { - return Ok(ArrayBase { - data: self.data, - ptr: self.ptr, - dim, - strides, - }); + return Ok(ArrayBase::from_data_ptr(self.data, self.ptr) + .with_strides_dim(strides, dim)); + } else if D::NDIM == None || D2::NDIM == None { // one is dynamic dim + // safe because dim, strides are equivalent under a different type + if let Some(dim) = D2::from_dimension(&self.dim) { + if let Some(strides) = D2::from_dimension(&self.strides) { + return Ok(self.with_strides_dim(strides, dim)); + } } } } @@ -1792,10 +1766,9 @@ where new_strides[new_axis] = strides[axis]; } } - ArrayBase { - dim: new_dim, - strides: new_strides, - ..self + // safe because axis invariants are checked above; they are a permutation of the old + unsafe { + self.with_strides_dim(new_strides, new_dim) } } @@ -1915,17 +1888,11 @@ where /// ***Panics*** if the axis is out of bounds. pub fn insert_axis(self, axis: Axis) -> ArrayBase { assert!(axis.index() <= self.ndim()); - let ArrayBase { - ptr, - data, - dim, - strides, - } = self; - ArrayBase { - ptr, - data, - dim: dim.insert_axis(axis), - strides: strides.insert_axis(axis), + // safe because a new axis of length one does not affect memory layout + unsafe { + let strides = self.strides.insert_axis(axis); + let dim = self.dim.insert_axis(axis); + self.with_strides_dim(strides, dim) } } diff --git a/src/impl_raw_views.rs b/src/impl_raw_views.rs index e3447cac5..2ac5c08c7 100644 --- a/src/impl_raw_views.rs +++ b/src/impl_raw_views.rs @@ -17,12 +17,8 @@ where /// meet all of the invariants of the `ArrayBase` type. #[inline] pub(crate) unsafe fn new(ptr: NonNull, dim: D, strides: D) -> Self { - RawArrayView { - data: RawViewRepr::new(), - ptr, - dim, - strides, - } + RawArrayView::from_data_ptr(RawViewRepr::new(), ptr) + .with_strides_dim(strides, dim) } unsafe fn new_(ptr: *const A, dim: D, strides: D) -> Self { @@ -163,12 +159,8 @@ where /// meet all of the invariants of the `ArrayBase` type. #[inline] pub(crate) unsafe fn new(ptr: NonNull, dim: D, strides: D) -> Self { - RawArrayViewMut { - data: RawViewRepr::new(), - ptr, - dim, - strides, - } + RawArrayViewMut::from_data_ptr(RawViewRepr::new(), ptr) + .with_strides_dim(strides, dim) } unsafe fn new_(ptr: *mut A, dim: D, strides: D) -> Self { diff --git a/src/impl_special_element_types.rs b/src/impl_special_element_types.rs index e41177dbf..bf4384e42 100644 --- a/src/impl_special_element_types.rs +++ b/src/impl_special_element_types.rs @@ -40,12 +40,6 @@ where // "transmute" from storage of MaybeUninit to storage of A let data = S::data_subst(data); let ptr = ptr.cast::(); - - ArrayBase { - data, - ptr, - dim, - strides, - } + ArrayBase::from_data_ptr(data, ptr).with_strides_dim(strides, dim) } } diff --git a/src/impl_views/constructors.rs b/src/impl_views/constructors.rs index c6e5f9988..a133ee1d1 100644 --- a/src/impl_views/constructors.rs +++ b/src/impl_views/constructors.rs @@ -230,12 +230,7 @@ where assert!(is_aligned(ptr.as_ptr()), "The pointer must be aligned."); dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); } - ArrayView { - data: ViewRepr::new(), - ptr, - dim, - strides, - } + ArrayView::from_data_ptr(ViewRepr::new(), ptr).with_strides_dim(strides, dim) } /// Unsafe because: `ptr` must be valid for the given dimension and strides. @@ -258,12 +253,7 @@ where assert!(is_aligned(ptr.as_ptr()), "The pointer must be aligned."); dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); } - ArrayViewMut { - data: ViewRepr::new(), - ptr, - dim, - strides, - } + ArrayViewMut::from_data_ptr(ViewRepr::new(), ptr).with_strides_dim(strides, dim) } /// Create a new `ArrayView` diff --git a/src/lib.rs b/src/lib.rs index 1d56dc254..0c7caf735 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1565,11 +1565,9 @@ where fn try_remove_axis(self, axis: Axis) -> ArrayBase { let d = self.dim.try_remove_axis(axis); let s = self.strides.try_remove_axis(axis); - ArrayBase { - ptr: self.ptr, - data: self.data, - dim: d, - strides: s, + // safe because new dimension, strides allow access to a subset of old data + unsafe { + self.with_strides_dim(s, d) } } From 0d4272f7850181a9e5a3fcf25b9cf450a13f63d3 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 4 Feb 2021 00:27:01 +0100 Subject: [PATCH 173/651] FIX: Add debug assertion in with_strides_dim Co-authored-by: Jim Turner --- src/impl_internal_constructors.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/impl_internal_constructors.rs b/src/impl_internal_constructors.rs index 18e8e6874..0ed60a622 100644 --- a/src/impl_internal_constructors.rs +++ b/src/impl_internal_constructors.rs @@ -55,6 +55,7 @@ where where E: Dimension { + debug_assert_eq!(strides.ndim(), dim.ndim()); ArrayBase { data: self.data, ptr: self.ptr, From a0640233defe2c8c60232fc7fa35960dc98cc839 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Thu, 4 Feb 2021 13:11:37 -0500 Subject: [PATCH 174/651] Lengthen life in map_inplace and unify impl with fold --- src/dimension/mod.rs | 30 ++++++++++++++++++++++++ src/impl_methods.rs | 55 +++++++++++++++++++++----------------------- 2 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 3b14ea221..1359b8f39 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -678,6 +678,36 @@ where } } +/// Move the axis which has the smallest absolute stride and a length +/// greater than one to be the last axis. +pub fn move_min_stride_axis_to_last(dim: &mut D, strides: &mut D) +where + D: Dimension, +{ + debug_assert_eq!(dim.ndim(), strides.ndim()); + match dim.ndim() { + 0 | 1 => {} + 2 => { + if dim[1] <= 1 + || dim[0] > 1 && (strides[0] as isize).abs() < (strides[1] as isize).abs() + { + dim.slice_mut().swap(0, 1); + strides.slice_mut().swap(0, 1); + } + } + n => { + if let Some(min_stride_axis) = (0..n) + .filter(|&ax| dim[ax] > 1) + .min_by_key(|&ax| (strides[ax] as isize).abs()) + { + let last = n - 1; + dim.slice_mut().swap(last, min_stride_axis); + strides.slice_mut().swap(last, min_stride_axis); + } + } + } +} + #[cfg(test)] mod test { use super::{ diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 7b6f3f6f5..8faa1620d 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -18,8 +18,8 @@ use crate::arraytraits; use crate::dimension; use crate::dimension::IntoDimension; use crate::dimension::{ - abs_index, axes_of, do_slice, merge_axes, offset_from_ptr_to_memory, size_of_shape_checked, - stride_offset, Axes, + abs_index, axes_of, do_slice, merge_axes, move_min_stride_axis_to_last, + offset_from_ptr_to_memory, size_of_shape_checked, stride_offset, Axes, }; use crate::error::{self, ErrorKind, ShapeError}; use crate::math_cell::MathCell; @@ -1456,6 +1456,15 @@ where /// Return the array’s data as a slice if it is contiguous, /// return `None` otherwise. pub fn as_slice_memory_order_mut(&mut self) -> Option<&mut [A]> + where + S: DataMut, + { + self.try_as_slice_memory_order_mut().ok() + } + + /// Return the array’s data as a slice if it is contiguous, otherwise + /// return `self` in the `Err` variant. + pub(crate) fn try_as_slice_memory_order_mut(&mut self) -> Result<&mut [A], &mut Self> where S: DataMut, { @@ -1463,13 +1472,13 @@ where self.ensure_unique(); let offset = offset_from_ptr_to_memory(&self.dim, &self.strides); unsafe { - Some(slice::from_raw_parts_mut( + Ok(slice::from_raw_parts_mut( self.ptr.offset(offset).as_ptr(), self.len(), )) } } else { - None + Err(self) } } @@ -2070,27 +2079,7 @@ where slc.iter().fold(init, f) } else { let mut v = self.view(); - // put the narrowest axis at the last position - match v.ndim() { - 0 | 1 => {} - 2 => { - if self.len_of(Axis(1)) <= 1 - || self.len_of(Axis(0)) > 1 - && self.stride_of(Axis(0)).abs() < self.stride_of(Axis(1)).abs() - { - v.swap_axes(0, 1); - } - } - n => { - let last = n - 1; - let narrow_axis = v - .axes() - .filter(|ax| ax.len() > 1) - .min_by_key(|ax| ax.stride().abs()) - .map_or(last, |ax| ax.axis().index()); - v.swap_axes(last, narrow_axis); - } - } + move_min_stride_axis_to_last(&mut v.dim, &mut v.strides); v.into_elements_base().fold(init, f) } } @@ -2200,12 +2189,20 @@ where /// Modify the array in place by calling `f` by mutable reference on each element. /// /// Elements are visited in arbitrary order. - pub fn map_inplace(&mut self, f: F) + pub fn map_inplace<'a, F>(&'a mut self, f: F) where S: DataMut, - F: FnMut(&mut A), - { - self.unordered_foreach_mut(f); + A: 'a, + F: FnMut(&'a mut A), + { + match self.try_as_slice_memory_order_mut() { + Ok(slc) => slc.iter_mut().for_each(f), + Err(arr) => { + let mut v = arr.view_mut(); + move_min_stride_axis_to_last(&mut v.dim, &mut v.strides); + v.into_elements_base().for_each(f); + } + } } /// Modify the array in place by calling `f` by **v**alue on each element. From 0dce73a581df009262760cec5a1d2aa2f6458d66 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 3 Feb 2021 15:29:58 -0500 Subject: [PATCH 175/651] Replace unordered_foreach_mut with map_inplace --- src/impl_methods.rs | 6 +++--- src/impl_ops.rs | 10 +++++----- src/lib.rs | 27 +-------------------------- 3 files changed, 9 insertions(+), 34 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 8faa1620d..81002291f 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1985,7 +1985,7 @@ where S: DataMut, A: Clone, { - self.unordered_foreach_mut(move |elt| *elt = x.clone()); + self.map_inplace(move |elt| *elt = x.clone()); } fn zip_mut_with_same_shape(&mut self, rhs: &ArrayBase, mut f: F) @@ -2037,7 +2037,7 @@ where S: DataMut, F: FnMut(&mut A, &B), { - self.unordered_foreach_mut(move |elt| f(elt, rhs_elem)); + self.map_inplace(move |elt| f(elt, rhs_elem)); } /// Traverse two arrays in unspecified order, in lock step, @@ -2232,7 +2232,7 @@ where F: FnMut(A) -> A, A: Clone, { - self.unordered_foreach_mut(move |x| *x = f(x.clone())); + self.map_inplace(move |x| *x = f(x.clone())); } /// Call `f` for each element in the array. diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 51d432ee6..256bee3e5 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -141,7 +141,7 @@ impl $trt for ArrayBase { type Output = ArrayBase; fn $mth(mut self, x: B) -> ArrayBase { - self.unordered_foreach_mut(move |elt| { + self.map_inplace(move |elt| { *elt = elt.clone() $operator x.clone(); }); self @@ -194,7 +194,7 @@ impl $trt> for $scalar rhs.$mth(self) } or {{ let mut rhs = rhs; - rhs.unordered_foreach_mut(move |elt| { + rhs.map_inplace(move |elt| { *elt = self $operator *elt; }); rhs @@ -299,7 +299,7 @@ mod arithmetic_ops { type Output = Self; /// Perform an elementwise negation of `self` and return the result. fn neg(mut self) -> Self { - self.unordered_foreach_mut(|elt| { + self.map_inplace(|elt| { *elt = -elt.clone(); }); self @@ -329,7 +329,7 @@ mod arithmetic_ops { type Output = Self; /// Perform an elementwise unary not of `self` and return the result. fn not(mut self) -> Self { - self.unordered_foreach_mut(|elt| { + self.map_inplace(|elt| { *elt = !elt.clone(); }); self @@ -386,7 +386,7 @@ mod assign_ops { D: Dimension, { fn $method(&mut self, rhs: A) { - self.unordered_foreach_mut(move |elt| { + self.map_inplace(move |elt| { elt.$method(rhs.clone()); }); } diff --git a/src/lib.rs b/src/lib.rs index c71f09aaf..8faaf5ba7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,7 +143,7 @@ pub use crate::indexes::{indices, indices_of}; pub use crate::slice::{Slice, SliceInfo, SliceNextDim, SliceOrIndex}; use crate::iterators::Baseiter; -use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes, LanesMut}; +use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes}; pub use crate::arraytraits::AsArray; #[cfg(feature = "std")] @@ -1544,22 +1544,6 @@ where self.strides.clone() } - /// Apply closure `f` to each element in the array, in whatever - /// order is the fastest to visit. - fn unordered_foreach_mut(&mut self, mut f: F) - where - S: DataMut, - F: FnMut(&mut A), - { - if let Some(slc) = self.as_slice_memory_order_mut() { - slc.iter_mut().for_each(f); - } else { - for row in self.inner_rows_mut() { - row.into_iter_().fold((), |(), elt| f(elt)); - } - } - } - /// Remove array axis `axis` and return the result. fn try_remove_axis(self, axis: Axis) -> ArrayBase { let d = self.dim.try_remove_axis(axis); @@ -1577,15 +1561,6 @@ where let n = self.ndim(); Lanes::new(self.view(), Axis(n.saturating_sub(1))) } - - /// n-d generalization of rows, just like inner iter - fn inner_rows_mut(&mut self) -> iterators::LanesMut<'_, A, D::Smaller> - where - S: DataMut, - { - let n = self.ndim(); - LanesMut::new(self.view_mut(), Axis(n.saturating_sub(1))) - } } // parallel methods From 566b53138a1e054ad0605282dc552077ff45f600 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Fri, 5 Feb 2021 17:23:32 -0500 Subject: [PATCH 176/651] Add slice_each_axis/_mut/_inplace methods --- src/impl_methods.rs | 58 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 31 ++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 9eea42d15..a6143c28a 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -25,6 +25,7 @@ use crate::error::{self, ErrorKind, ShapeError}; use crate::math_cell::MathCell; use crate::itertools::zip; use crate::zip::Zip; +use crate::AxisDescription; use crate::iter::{ AxisChunksIter, AxisChunksIterMut, AxisIter, AxisIterMut, ExactChunks, ExactChunksMut, @@ -511,6 +512,63 @@ where debug_assert!(self.pointer_is_inbounds()); } + /// Return a view of a slice of the array, with a closure specifying the + /// slice for each axis. + /// + /// This is especially useful for code which is generic over the + /// dimensionality of the array. + /// + /// **Panics** if an index is out of bounds or step size is zero. + pub fn slice_each_axis(&self, f: F) -> ArrayView<'_, A, D> + where + F: FnMut(AxisDescription) -> Slice, + S: Data, + { + let mut view = self.view(); + view.slice_each_axis_inplace(f); + view + } + + /// Return a mutable view of a slice of the array, with a closure + /// specifying the slice for each axis. + /// + /// This is especially useful for code which is generic over the + /// dimensionality of the array. + /// + /// **Panics** if an index is out of bounds or step size is zero. + pub fn slice_each_axis_mut(&mut self, f: F) -> ArrayViewMut<'_, A, D> + where + F: FnMut(AxisDescription) -> Slice, + S: DataMut, + { + let mut view = self.view_mut(); + view.slice_each_axis_inplace(f); + view + } + + /// Slice the array in place, with a closure specifying the slice for each + /// axis. + /// + /// This is especially useful for code which is generic over the + /// dimensionality of the array. + /// + /// **Panics** if an index is out of bounds or step size is zero. + pub fn slice_each_axis_inplace(&mut self, mut f: F) + where + F: FnMut(AxisDescription) -> Slice, + { + (0..self.ndim()).for_each(|ax| { + self.slice_axis_inplace( + Axis(ax), + f(AxisDescription( + Axis(ax), + self.dim[ax], + self.strides[ax] as isize, + )), + ) + }) + } + /// Return a reference to the element at `index`, or return `None` /// if the index is out of bounds. /// diff --git a/src/lib.rs b/src/lib.rs index 0c7caf735..03fd96253 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -503,6 +503,19 @@ pub type Ixs = isize; /// [`.slice_move()`]: #method.slice_move /// [`.slice_collapse()`]: #method.slice_collapse /// +/// When slicing arrays with generic dimensionality, creating an instance of +/// [`&SliceInfo`] to pass to the multi-axis slicing methods like [`.slice()`] +/// is awkward. In these cases, it's usually more convenient to use +/// [`.slice_each_axis()`]/[`.slice_each_axis_mut()`]/[`.slice_each_axis_inplace()`] +/// or to create a view and then slice individual axes of the view using +/// methods such as [`.slice_axis_inplace()`] and [`.collapse_axis()`]. +/// +/// [`.slice_each_axis()`]: #method.slice_each_axis +/// [`.slice_each_axis_mut()`]: #method.slice_each_axis_mut +/// [`.slice_each_axis_inplace()`]: #method.slice_each_axis_inplace +/// [`.slice_axis_inplace()`]: #method.slice_axis_inplace +/// [`.collapse_axis()`]: #method.collapse_axis +/// /// It's possible to take multiple simultaneous *mutable* slices with /// [`.multi_slice_mut()`] or (for [`ArrayViewMut`] only) /// [`.multi_slice_move()`]. @@ -511,8 +524,7 @@ pub type Ixs = isize; /// [`.multi_slice_move()`]: type.ArrayViewMut.html#method.multi_slice_move /// /// ``` -/// -/// use ndarray::{arr2, arr3, s}; +/// use ndarray::{arr2, arr3, s, ArrayBase, DataMut, Dimension, Slice}; /// /// // 2 submatrices of 2 rows with 3 elements per row, means a shape of `[2, 2, 3]`. /// @@ -571,6 +583,21 @@ pub type Ixs = isize; /// [5, 7]]); /// assert_eq!(s0, i); /// assert_eq!(s1, j); +/// +/// // Generic function which assigns the specified value to the elements which +/// // have indices in the lower half along all axes. +/// fn fill_lower(arr: &mut ArrayBase, x: S::Elem) +/// where +/// S: DataMut, +/// S::Elem: Clone, +/// D: Dimension, +/// { +/// arr.slice_each_axis_mut(|ax| Slice::from(0..ax.len() / 2)).fill(x); +/// } +/// fill_lower(&mut h, 9); +/// let k = arr2(&[[9, 9, 2, 3], +/// [4, 5, 6, 7]]); +/// assert_eq!(h, k); /// ``` /// /// ## Subviews From abf38faf9a75f2368c2575823738e80314571bed Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Fri, 19 Apr 2019 01:44:44 -0400 Subject: [PATCH 177/651] Add links to #rust-sci in README --- README.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 05ff48fb8..2464e0f91 100644 --- a/README.rst +++ b/README.rst @@ -9,7 +9,7 @@ or take a look at the `quickstart tutorial <./README-quick-start.md>`_. __ https://docs.rs/ndarray/ -|build_status|_ |crates|_ +|build_status|_ |crates|_ |matrix-chat|_ |irc|_ .. |build_status| image:: https://github.com/rust-ndarray/ndarray/workflows/Continuous%20integration/badge.svg?branch=master .. _build_status: https://github.com/rust-ndarray/ndarray/actions @@ -17,6 +17,12 @@ __ https://docs.rs/ndarray/ .. |crates| image:: http://meritbadge.herokuapp.com/ndarray .. _crates: https://crates.io/crates/ndarray +.. |matrix-chat| image:: https://img.shields.io/badge/Matrix-%23rust--sci%3Amatrix.org-lightgrey +.. _matrix-chat: https://matrix.to/#/#rust-sci:matrix.org + +.. |irc| image:: https://img.shields.io/badge/IRC-%23rust--sci%20on%20OFTC-lightgrey +.. _irc: https://webchat.oftc.net/?channels=rust-sci + Highlights ---------- From b1359b73a5644a8e0d1f3ebd0985baf6661f4adc Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 8 Feb 2021 16:17:49 -0500 Subject: [PATCH 178/651] Add alt text to badges so that they're accessible --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 2464e0f91..8884ac9f3 100644 --- a/README.rst +++ b/README.rst @@ -12,15 +12,19 @@ __ https://docs.rs/ndarray/ |build_status|_ |crates|_ |matrix-chat|_ |irc|_ .. |build_status| image:: https://github.com/rust-ndarray/ndarray/workflows/Continuous%20integration/badge.svg?branch=master + :alt: CI build status .. _build_status: https://github.com/rust-ndarray/ndarray/actions .. |crates| image:: http://meritbadge.herokuapp.com/ndarray + :alt: ndarray at crates.io .. _crates: https://crates.io/crates/ndarray .. |matrix-chat| image:: https://img.shields.io/badge/Matrix-%23rust--sci%3Amatrix.org-lightgrey + :alt: Matrix chat at #rust-sci:matrix.org .. _matrix-chat: https://matrix.to/#/#rust-sci:matrix.org .. |irc| image:: https://img.shields.io/badge/IRC-%23rust--sci%20on%20OFTC-lightgrey + :alt: IRC at #rust-sci on OFTC .. _irc: https://webchat.oftc.net/?channels=rust-sci Highlights From 76399d586cdb0a27ddfcff7e29035fc9d6ad3d10 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 8 Feb 2021 19:20:56 -0500 Subject: [PATCH 179/651] Make AxisDescription a non-tuple struct --- examples/axis_ops.rs | 14 ++++++------ src/dimension/axes.rs | 38 +++++++++++++++++++------------- src/dimension/dimension_trait.rs | 10 ++++----- src/impl_methods.rs | 10 ++++----- 4 files changed, 40 insertions(+), 32 deletions(-) diff --git a/examples/axis_ops.rs b/examples/axis_ops.rs index 3dbf0eee9..624af32c3 100644 --- a/examples/axis_ops.rs +++ b/examples/axis_ops.rs @@ -14,21 +14,21 @@ where { println!("Regularize:\n{:?}", a); // reverse all neg axes - while let Some(ax) = a.axes().find(|ax| ax.stride() <= 0) { - if ax.stride() == 0 { + while let Some(ax) = a.axes().find(|ax| ax.stride <= 0) { + if ax.stride == 0 { return Err(()); } // reverse ax - println!("Reverse {:?}", ax.axis()); - a.invert_axis(ax.axis()); + println!("Reverse {:?}", ax.axis); + a.invert_axis(ax.axis); } // sort by least stride let mut i = 0; let n = a.ndim(); - while let Some(ax) = a.axes().rev().skip(i).min_by_key(|ax| ax.stride().abs()) { - a.swap_axes(n - 1 - i, ax.axis().index()); - println!("Swap {:?} <=> {}", ax.axis(), n - 1 - i); + while let Some(ax) = a.axes().rev().skip(i).min_by_key(|ax| ax.stride.abs()) { + a.swap_axes(n - 1 - i, ax.axis.index()); + println!("Swap {:?} <=> {}", ax.axis, n - 1 - i); i += 1; } diff --git a/src/dimension/axes.rs b/src/dimension/axes.rs index 50830f3c6..ee6904fb3 100644 --- a/src/dimension/axes.rs +++ b/src/dimension/axes.rs @@ -42,7 +42,11 @@ pub struct Axes<'a, D> { /// Description of the axis, its length and its stride. #[derive(Debug)] -pub struct AxisDescription(pub Axis, pub Ix, pub Ixs); +pub struct AxisDescription { + pub axis: Axis, + pub len: usize, + pub stride: isize, +} copy_and_clone!(AxisDescription); @@ -53,17 +57,17 @@ impl AxisDescription { /// Return axis #[inline(always)] pub fn axis(self) -> Axis { - self.0 + self.axis } /// Return length #[inline(always)] pub fn len(self) -> Ix { - self.1 + self.len } /// Return stride #[inline(always)] pub fn stride(self) -> Ixs { - self.2 + self.stride } } @@ -79,11 +83,11 @@ where fn next(&mut self) -> Option { if self.start < self.end { let i = self.start.post_inc(); - Some(AxisDescription( - Axis(i), - self.dim[i], - self.strides[i] as Ixs, - )) + Some(AxisDescription { + axis: Axis(i), + len: self.dim[i], + stride: self.strides[i] as Ixs, + }) } else { None } @@ -94,7 +98,11 @@ where F: FnMut(B, AxisDescription) -> B, { (self.start..self.end) - .map(move |i| AxisDescription(Axis(i), self.dim[i], self.strides[i] as isize)) + .map(move |i| AxisDescription { + axis: Axis(i), + len: self.dim[i], + stride: self.strides[i] as isize, + }) .fold(init, f) } @@ -111,11 +119,11 @@ where fn next_back(&mut self) -> Option { if self.start < self.end { let i = self.end.pre_dec(); - Some(AxisDescription( - Axis(i), - self.dim[i], - self.strides[i] as Ixs, - )) + Some(AxisDescription { + axis: Axis(i), + len: self.dim[i], + stride: self.strides[i] as Ixs, + }) } else { None } diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 18a10c3f0..92f241189 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -332,8 +332,8 @@ pub trait Dimension: }; axes_of(self, strides) .rev() - .min_by_key(|ax| ax.stride().abs()) - .map_or(Axis(n - 1), |ax| ax.axis()) + .min_by_key(|ax| ax.stride.abs()) + .map_or(Axis(n - 1), |ax| ax.axis) } /// Compute the maximum stride axis (absolute value), under the constraint @@ -346,9 +346,9 @@ pub trait Dimension: _ => {} } axes_of(self, strides) - .filter(|ax| ax.len() > 1) - .max_by_key(|ax| ax.stride().abs()) - .map_or(Axis(0), |ax| ax.axis()) + .filter(|ax| ax.len > 1) + .max_by_key(|ax| ax.stride.abs()) + .map_or(Axis(0), |ax| ax.axis) } /// Convert the dimensional into a dynamic dimensional (IxDyn). diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 19733fc6d..1adfff689 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -560,11 +560,11 @@ where (0..self.ndim()).for_each(|ax| { self.slice_axis_inplace( Axis(ax), - f(AxisDescription( - Axis(ax), - self.dim[ax], - self.strides[ax] as isize, - )), + f(AxisDescription { + axis: Axis(ax), + len: self.dim[ax], + stride: self.strides[ax] as isize, + }), ) }) } From 1f0a72a183ad0c2a47099fbb8ff47c2d927450aa Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 8 Feb 2021 19:21:32 -0500 Subject: [PATCH 180/651] Deprecate AxisDescription's accessor methods --- src/dimension/axes.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dimension/axes.rs b/src/dimension/axes.rs index ee6904fb3..a678e78ca 100644 --- a/src/dimension/axes.rs +++ b/src/dimension/axes.rs @@ -55,16 +55,19 @@ copy_and_clone!(AxisDescription); #[allow(clippy::len_without_is_empty)] impl AxisDescription { /// Return axis + #[deprecated(note = "Use .axis field instead", since = "0.15.0")] #[inline(always)] pub fn axis(self) -> Axis { self.axis } /// Return length + #[deprecated(note = "Use .len field instead", since = "0.15.0")] #[inline(always)] pub fn len(self) -> Ix { self.len } /// Return stride + #[deprecated(note = "Use .stride field instead", since = "0.15.0")] #[inline(always)] pub fn stride(self) -> Ixs { self.stride From c264ecf8977f5f6eef151dd770781bedd7b0afa4 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 9 Feb 2021 16:36:11 -0500 Subject: [PATCH 181/651] Change for_each to for loop in slice_each_axis_inplace --- src/impl_methods.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 1adfff689..9943e7c8e 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -557,7 +557,7 @@ where where F: FnMut(AxisDescription) -> Slice, { - (0..self.ndim()).for_each(|ax| { + for ax in 0..self.ndim() { self.slice_axis_inplace( Axis(ax), f(AxisDescription { @@ -566,7 +566,7 @@ where stride: self.strides[ax] as isize, }), ) - }) + } } /// Return a reference to the element at `index`, or return `None` From c27626a6a755be160907c4fab747f36ad6f933f9 Mon Sep 17 00:00:00 2001 From: Dave McDaniel Date: Mon, 8 Feb 2021 22:01:13 -0500 Subject: [PATCH 182/651] add test to sort-axis example to catch issue of swapped perm_i and i also replaced outer iteration with zip directly since it takes slices. --- examples/sort-axis.rs | 80 ++++++++++++++++++++++++++++++++++++++----- scripts/all-tests.sh | 1 + 2 files changed, 72 insertions(+), 9 deletions(-) diff --git a/examples/sort-axis.rs b/examples/sort-axis.rs index 09410e819..7ae67fb07 100644 --- a/examples/sort-axis.rs +++ b/examples/sort-axis.rs @@ -102,21 +102,27 @@ where assert_eq!(axis_len, perm.indices.len()); debug_assert!(perm.correct()); + if self.is_empty() { + return self; + } + let mut result = Array::uninit(self.dim()); unsafe { // logically move ownership of all elements from self into result // the result realizes this ownership at .assume_init() further down let mut moved_elements = 0; - for i in 0..axis_len { - let perm_i = perm.indices[i]; - Zip::from(result.index_axis_mut(axis, perm_i)) - .and(self.index_axis(axis, i)) - .for_each(|to, from| { - copy_nonoverlapping(from, to.as_mut_ptr(), 1); - moved_elements += 1; - }); - } + Zip::from(&perm.indices) + .and(result.axis_iter_mut(axis)) + .for_each(|&perm_i, result_pane| { + // possible improvement: use unchecked indexing for `index_axis` + Zip::from(result_pane) + .and(self.index_axis(axis, perm_i)) + .for_each(|to, from| { + copy_nonoverlapping(from, to.as_mut_ptr(), 1); + moved_elements += 1; + }); + }); debug_assert_eq!(result.len(), moved_elements); // panic-critical begin: we must not panic // forget moved array elements but not its vec @@ -129,6 +135,7 @@ where } } } + #[cfg(feature = "std")] fn main() { let a = Array::linspace(0., 63., 64).into_shape((8, 8)).unwrap(); @@ -143,5 +150,60 @@ fn main() { let c = strings.permute_axis(Axis(1), &perm); println!("{:?}", c); } + #[cfg(not(feature = "std"))] fn main() {} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_permute_axis() { + let a = array![ + [107998.96, 1.], + [107999.08, 2.], + [107999.20, 3.], + [108000.33, 4.], + [107999.45, 5.], + [107999.57, 6.], + [108010.69, 7.], + [107999.81, 8.], + [107999.94, 9.], + [75600.09, 10.], + [75600.21, 11.], + [75601.33, 12.], + [75600.45, 13.], + [75600.58, 14.], + [109000.70, 15.], + [75600.82, 16.], + [75600.94, 17.], + [75601.06, 18.], + ]; + + let perm = a.sort_axis_by(Axis(0), |i, j| a[[i, 0]] < a[[j, 0]]); + let b = a.permute_axis(Axis(0), &perm); + assert_eq!( + b, + array![ + [75600.09, 10.], + [75600.21, 11.], + [75600.45, 13.], + [75600.58, 14.], + [75600.82, 16.], + [75600.94, 17.], + [75601.06, 18.], + [75601.33, 12.], + [107998.96, 1.], + [107999.08, 2.], + [107999.20, 3.], + [107999.45, 5.], + [107999.57, 6.], + [107999.81, 8.], + [107999.94, 9.], + [108000.33, 4.], + [108010.69, 7.], + [109000.70, 15.], + ] + ); + } +} diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index 22f1d1f94..61dfc56dc 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -16,5 +16,6 @@ cargo test --manifest-path=ndarray-rand/Cargo.toml --no-default-features --verbo cargo test --manifest-path=ndarray-rand/Cargo.toml --features quickcheck --verbose cargo test --manifest-path=serialization-tests/Cargo.toml --verbose cargo test --manifest-path=blas-tests/Cargo.toml --verbose +cargo test --examples CARGO_TARGET_DIR=target/ cargo test --manifest-path=numeric-tests/Cargo.toml --verbose ([ "$CHANNEL" != "nightly" ] || cargo bench --no-run --verbose --features "$FEATURES") From 69c14b227d62fa9c3254e030e75a78f5df6b5789 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 5 Feb 2021 12:04:42 +0100 Subject: [PATCH 183/651] MAINT: Move NdProducer implementation to separate submodule --- src/zip/mod.rs | 400 +----------------------------------------- src/zip/ndproducer.rs | 400 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 403 insertions(+), 397 deletions(-) create mode 100644 src/zip/ndproducer.rs diff --git a/src/zip/mod.rs b/src/zip/mod.rs index e0d456fc0..38dbeb579 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -8,22 +8,23 @@ #[macro_use] mod zipmacro; +mod ndproducer; #[cfg(feature = "rayon")] use std::mem::MaybeUninit; -use alloc::vec::Vec; use crate::imp_prelude::*; use crate::AssignElem; use crate::IntoDimension; use crate::Layout; -use crate::NdIndex; use crate::partial::Partial; use crate::indexes::{indices, Indices}; use crate::layout::{CORDER, FORDER}; use crate::split_at::{SplitPreference, SplitAt}; +pub use self::ndproducer::{NdProducer, IntoNdProducer, Offset}; + /// Return if the expression is a break value. macro_rules! fold_while { ($e:expr) => { @@ -94,119 +95,6 @@ where private_impl! {} } -/// Argument conversion into a producer. -/// -/// Slices and vectors can be used (equivalent to 1-dimensional array views). -/// -/// This trait is like `IntoIterator` for `NdProducers` instead of iterators. -pub trait IntoNdProducer { - /// The element produced per iteration. - type Item; - /// Dimension type of the producer - type Dim: Dimension; - type Output: NdProducer; - /// Convert the value into an `NdProducer`. - fn into_producer(self) -> Self::Output; -} - -impl

IntoNdProducer for P +where + P: NdProducer, +{ + type Item = P::Item; + type Dim = P::Dim; + type Output = Self; + fn into_producer(self) -> Self::Output { + self + } +} + +/// A producer of an n-dimensional set of elements; +/// for example an array view, mutable array view or an iterator +/// that yields chunks. +/// +/// Producers are used as a arguments to [`Zip`](struct.Zip.html) and +/// [`azip!()`](macro.azip.html). +/// +/// # Comparison to `IntoIterator` +/// +/// Most `NdProducers` are *iterable* (implement `IntoIterator`) but not directly +/// iterators. This separation is needed because the producer represents +/// a multidimensional set of items, it can be split along a particular axis for +/// parallelization, and it has no fixed correspondance to a sequence. +/// +/// The natural exception is one dimensional producers, like `AxisIter`, which +/// implement `Iterator` directly +/// (`AxisIter` traverses a one dimensional sequence, along an axis, while +/// *producing* multidimensional items). +/// +/// See also [`IntoNdProducer`](trait.IntoNdProducer.html) +pub trait NdProducer { + /// The element produced per iteration. + type Item; + // Internal use / Pointee type + /// Dimension type + type Dim: Dimension; + + // The pointer Ptr is used by an array view to simply point to the + // current element. It doesn't have to be a pointer (see Indices). + // Its main function is that it can be incremented with a particular + // stride (= along a particular axis) + #[doc(hidden)] + /// Pointer or stand-in for pointer + type Ptr: Offset; + #[doc(hidden)] + /// Pointer stride + type Stride: Copy; + + #[doc(hidden)] + fn layout(&self) -> Layout; + #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim; + #[doc(hidden)] + fn equal_dim(&self, dim: &Self::Dim) -> bool { + self.raw_dim() == *dim + } + #[doc(hidden)] + fn as_ptr(&self) -> Self::Ptr; + #[doc(hidden)] + unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item; + #[doc(hidden)] + unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr; + #[doc(hidden)] + fn stride_of(&self, axis: Axis) -> ::Stride; + #[doc(hidden)] + fn contiguous_stride(&self) -> Self::Stride; + #[doc(hidden)] + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) + where + Self: Sized; + + private_decl! {} +} + +pub trait Offset: Copy { + type Stride: Copy; + unsafe fn stride_offset(self, s: Self::Stride, index: usize) -> Self; + private_decl! {} +} + +impl Offset for *const T { + type Stride = isize; + unsafe fn stride_offset(self, s: Self::Stride, index: usize) -> Self { + self.offset(s * (index as isize)) + } + private_impl! {} +} + +impl Offset for *mut T { + type Stride = isize; + unsafe fn stride_offset(self, s: Self::Stride, index: usize) -> Self { + self.offset(s * (index as isize)) + } + private_impl! {} +} + +/// An array reference is an n-dimensional producer of element references +/// (like ArrayView). +impl<'a, A: 'a, S, D> IntoNdProducer for &'a ArrayBase +where + D: Dimension, + S: Data, +{ + type Item = &'a A; + type Dim = D; + type Output = ArrayView<'a, A, D>; + fn into_producer(self) -> Self::Output { + self.view() + } +} + +/// A mutable array reference is an n-dimensional producer of mutable element +/// references (like ArrayViewMut). +impl<'a, A: 'a, S, D> IntoNdProducer for &'a mut ArrayBase +where + D: Dimension, + S: DataMut, +{ + type Item = &'a mut A; + type Dim = D; + type Output = ArrayViewMut<'a, A, D>; + fn into_producer(self) -> Self::Output { + self.view_mut() + } +} + +/// A slice is a one-dimensional producer +impl<'a, A: 'a> IntoNdProducer for &'a [A] { + type Item = ::Item; + type Dim = Ix1; + type Output = ArrayView1<'a, A>; + fn into_producer(self) -> Self::Output { + <_>::from(self) + } +} + +/// A mutable slice is a mutable one-dimensional producer +impl<'a, A: 'a> IntoNdProducer for &'a mut [A] { + type Item = ::Item; + type Dim = Ix1; + type Output = ArrayViewMut1<'a, A>; + fn into_producer(self) -> Self::Output { + <_>::from(self) + } +} + +/// A Vec is a one-dimensional producer +impl<'a, A: 'a> IntoNdProducer for &'a Vec { + type Item = ::Item; + type Dim = Ix1; + type Output = ArrayView1<'a, A>; + fn into_producer(self) -> Self::Output { + <_>::from(self) + } +} + +/// A mutable Vec is a mutable one-dimensional producer +impl<'a, A: 'a> IntoNdProducer for &'a mut Vec { + type Item = ::Item; + type Dim = Ix1; + type Output = ArrayViewMut1<'a, A>; + fn into_producer(self) -> Self::Output { + <_>::from(self) + } +} + +impl<'a, A, D: Dimension> NdProducer for ArrayView<'a, A, D> { + type Item = &'a A; + type Dim = D; + type Ptr = *mut A; + type Stride = isize; + + private_impl! {} + #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { + self.raw_dim() + } + + #[doc(hidden)] + fn equal_dim(&self, dim: &Self::Dim) -> bool { + self.dim.equal(dim) + } + + #[doc(hidden)] + fn as_ptr(&self) -> *mut A { + self.as_ptr() as _ + } + + #[doc(hidden)] + fn layout(&self) -> Layout { + self.layout_impl() + } + + #[doc(hidden)] + unsafe fn as_ref(&self, ptr: *mut A) -> Self::Item { + &*ptr + } + + #[doc(hidden)] + unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { + self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) + } + + #[doc(hidden)] + fn stride_of(&self, axis: Axis) -> isize { + self.stride_of(axis) + } + + #[inline(always)] + fn contiguous_stride(&self) -> Self::Stride { + 1 + } + + #[doc(hidden)] + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { + self.split_at(axis, index) + } +} + +impl<'a, A, D: Dimension> NdProducer for ArrayViewMut<'a, A, D> { + type Item = &'a mut A; + type Dim = D; + type Ptr = *mut A; + type Stride = isize; + + private_impl! {} + #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { + self.raw_dim() + } + + #[doc(hidden)] + fn equal_dim(&self, dim: &Self::Dim) -> bool { + self.dim.equal(dim) + } + + #[doc(hidden)] + fn as_ptr(&self) -> *mut A { + self.as_ptr() as _ + } + + #[doc(hidden)] + fn layout(&self) -> Layout { + self.layout_impl() + } + + #[doc(hidden)] + unsafe fn as_ref(&self, ptr: *mut A) -> Self::Item { + &mut *ptr + } + + #[doc(hidden)] + unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { + self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) + } + + #[doc(hidden)] + fn stride_of(&self, axis: Axis) -> isize { + self.stride_of(axis) + } + + #[inline(always)] + fn contiguous_stride(&self) -> Self::Stride { + 1 + } + + #[doc(hidden)] + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { + self.split_at(axis, index) + } +} + +impl NdProducer for RawArrayView { + type Item = *const A; + type Dim = D; + type Ptr = *const A; + type Stride = isize; + + private_impl! {} + #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { + self.raw_dim() + } + + #[doc(hidden)] + fn equal_dim(&self, dim: &Self::Dim) -> bool { + self.dim.equal(dim) + } + + #[doc(hidden)] + fn as_ptr(&self) -> *const A { + self.as_ptr() + } + + #[doc(hidden)] + fn layout(&self) -> Layout { + self.layout_impl() + } + + #[doc(hidden)] + unsafe fn as_ref(&self, ptr: *const A) -> *const A { + ptr + } + + #[doc(hidden)] + unsafe fn uget_ptr(&self, i: &Self::Dim) -> *const A { + self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) + } + + #[doc(hidden)] + fn stride_of(&self, axis: Axis) -> isize { + self.stride_of(axis) + } + + #[inline(always)] + fn contiguous_stride(&self) -> Self::Stride { + 1 + } + + #[doc(hidden)] + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { + self.split_at(axis, index) + } +} + +impl NdProducer for RawArrayViewMut { + type Item = *mut A; + type Dim = D; + type Ptr = *mut A; + type Stride = isize; + + private_impl! {} + #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { + self.raw_dim() + } + + #[doc(hidden)] + fn equal_dim(&self, dim: &Self::Dim) -> bool { + self.dim.equal(dim) + } + + #[doc(hidden)] + fn as_ptr(&self) -> *mut A { + self.as_ptr() as _ + } + + #[doc(hidden)] + fn layout(&self) -> Layout { + self.layout_impl() + } + + #[doc(hidden)] + unsafe fn as_ref(&self, ptr: *mut A) -> *mut A { + ptr + } + + #[doc(hidden)] + unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { + self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) + } + + #[doc(hidden)] + fn stride_of(&self, axis: Axis) -> isize { + self.stride_of(axis) + } + + #[inline(always)] + fn contiguous_stride(&self) -> Self::Stride { + 1 + } + + #[doc(hidden)] + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { + self.split_at(axis, index) + } +} + From 9a4184b4291b8aa7b3193004e610db5c9838f6af Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 12 Feb 2021 23:07:13 +0100 Subject: [PATCH 184/651] FIX: Fix new file ndproducer for no-std --- src/zip/ndproducer.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/zip/ndproducer.rs b/src/zip/ndproducer.rs index d8ec6f826..b6df10f51 100644 --- a/src/zip/ndproducer.rs +++ b/src/zip/ndproducer.rs @@ -2,6 +2,8 @@ use crate::imp_prelude::*; use crate::Layout; use crate::NdIndex; +#[cfg(not(features = "std"))] +use alloc::vec::Vec; /// Argument conversion into a producer. /// From 1e75ab68afa7676e1e2c28d74a22a87b03371fdc Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 15 Feb 2021 18:38:26 -0500 Subject: [PATCH 185/651] Document NdProducer::raw_dim This is useful because, otherwise, users have to manually calculate the shape of the producer to e.g. allocate an array of the correct shape to zip with it. --- src/zip/ndproducer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zip/ndproducer.rs b/src/zip/ndproducer.rs index b6df10f51..7c201741c 100644 --- a/src/zip/ndproducer.rs +++ b/src/zip/ndproducer.rs @@ -72,7 +72,7 @@ pub trait NdProducer { #[doc(hidden)] fn layout(&self) -> Layout; - #[doc(hidden)] + /// Return the shape of the producer. fn raw_dim(&self) -> Self::Dim; #[doc(hidden)] fn equal_dim(&self, dim: &Self::Dim) -> bool { From d62e0717f79b42812908542cb432e6911f7510df Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 16 Feb 2021 11:11:21 +0100 Subject: [PATCH 186/651] FEAT: Add back Array::from_vec and Array::from_iter Inherent methods are always available, easier to use and have high visibility in docs. These important user interfaces are undeprecated and added back respectively. --- src/arraytraits.rs | 11 ++--------- src/impl_constructors.rs | 25 ++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 449d57781..521822345 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -7,7 +7,6 @@ // except according to those terms. use std::hash; -use std::isize; use std::iter::FromIterator; use std::iter::IntoIterator; use std::mem; @@ -135,13 +134,7 @@ where /// let array = Array::from(vec![1., 2., 3., 4.]); /// ``` fn from(v: Vec) -> Self { - if mem::size_of::() == 0 { - assert!( - v.len() <= isize::MAX as usize, - "Length must fit in `isize`.", - ); - } - unsafe { Self::from_shape_vec_unchecked(v.len() as Ix, v) } + Self::from_vec(v) } } @@ -165,7 +158,7 @@ where where I: IntoIterator, { - Self::from(iterable.into_iter().collect::>()) + Self::from_iter(iterable) } } diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 9af8048f6..71ca8b32d 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -14,6 +14,7 @@ #[cfg(feature = "std")] use num_traits::Float; use num_traits::{One, Zero}; +use std::mem; use std::mem::MaybeUninit; use alloc::vec; use alloc::vec::Vec; @@ -51,11 +52,29 @@ where /// ```rust /// use ndarray::Array; /// - /// let array = Array::from(vec![1., 2., 3., 4.]); + /// let array = Array::from_vec(vec![1., 2., 3., 4.]); /// ``` - #[deprecated(note = "use standard `from`", since = "0.13.0")] pub fn from_vec(v: Vec) -> Self { - Self::from(v) + if mem::size_of::() == 0 { + assert!( + v.len() <= isize::MAX as usize, + "Length must fit in `isize`.", + ); + } + unsafe { Self::from_shape_vec_unchecked(v.len() as Ix, v) } + } + + /// Create a one-dimensional array from an iterator or iterable. + /// + /// **Panics** if the length is greater than `isize::MAX`. + /// + /// ```rust + /// use ndarray::Array; + /// + /// let array = Array::from_iter(0..10); + /// ``` + pub fn from_iter>(iterable: I) -> Self { + Self::from_vec(iterable.into_iter().collect()) } /// Create a one-dimensional array with `n` evenly spaced elements from From 48b63ee5b72eca0a0b7bea25f467861b4f766804 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Thu, 18 Feb 2021 13:45:42 -0500 Subject: [PATCH 187/651] Treat warnings as errors in CI This helps avoid accidentally missing a warning. --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92c184ca8..42e5e6584 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,7 @@ env: CARGO_TERM_COLOR: always HOST: x86_64-unknown-linux-gnu FEATURES: "test docs" + RUSTFLAGS: "-D warnings" jobs: tests: From fbf2f280358907e97a78c420460f67b969a15c85 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Thu, 18 Feb 2021 14:00:13 -0500 Subject: [PATCH 188/651] Fix compilation warnings --- benches/higher-order.rs | 1 - blas-tests/tests/oper.rs | 1 - examples/life.rs | 1 - tests/array.rs | 3 +-- tests/azip.rs | 1 - tests/dimension.rs | 3 ++- tests/iterator_chunks.rs | 2 +- tests/iterators.rs | 45 ++++++++++++++++++++-------------------- tests/ixdyn.rs | 4 +++- tests/numeric.rs | 2 +- tests/oper.rs | 2 -- tests/windows.rs | 1 - 12 files changed, 30 insertions(+), 36 deletions(-) diff --git a/benches/higher-order.rs b/benches/higher-order.rs index 2ea0721af..6bbd57177 100644 --- a/benches/higher-order.rs +++ b/benches/higher-order.rs @@ -6,7 +6,6 @@ clippy::many_single_char_names )] extern crate test; -use std::iter::FromIterator; use test::black_box; use test::Bencher; diff --git a/blas-tests/tests/oper.rs b/blas-tests/tests/oper.rs index b7c2d769d..51ac7824c 100644 --- a/blas-tests/tests/oper.rs +++ b/blas-tests/tests/oper.rs @@ -8,7 +8,6 @@ use ndarray::linalg::general_mat_vec_mul; use ndarray::prelude::*; use ndarray::{Data, LinalgScalar}; use ndarray::{Ix, Ixs, SliceInfo, SliceOrIndex}; -use std::iter::FromIterator; use approx::{assert_abs_diff_eq, assert_relative_eq}; use defmac::defmac; diff --git a/examples/life.rs b/examples/life.rs index 748f16053..48f1d609f 100644 --- a/examples/life.rs +++ b/examples/life.rs @@ -6,7 +6,6 @@ )] use ndarray::prelude::*; -use std::iter::FromIterator; const INPUT: &[u8] = include_bytes!("life.txt"); diff --git a/tests/array.rs b/tests/array.rs index dddcd5e1e..6b72bb5c4 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -13,7 +13,6 @@ use ndarray::indices; use ndarray::prelude::*; use ndarray::{arr3, rcarr2}; use ndarray::{Slice, SliceInfo, SliceOrIndex}; -use std::iter::FromIterator; macro_rules! assert_panics { ($body:expr) => { @@ -1803,7 +1802,7 @@ fn test_contiguous() { #[test] fn test_contiguous_neg_strides() { let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; - let mut a = ArrayView::from_shape((2, 3, 2).strides((1, 4, 2)), &s).unwrap(); + let a = ArrayView::from_shape((2, 3, 2).strides((1, 4, 2)), &s).unwrap(); assert_eq!( a, arr3(&[[[0, 2], [4, 6], [8, 10]], [[1, 3], [5, 7], [9, 11]]]) diff --git a/tests/azip.rs b/tests/azip.rs index 5075a89ba..6f0327f5d 100644 --- a/tests/azip.rs +++ b/tests/azip.rs @@ -8,7 +8,6 @@ use ndarray::prelude::*; use ndarray::Zip; -use std::iter::FromIterator; use itertools::{assert_equal, cloned}; diff --git a/tests/dimension.rs b/tests/dimension.rs index 7f1ee7ec5..939b4f0e3 100644 --- a/tests/dimension.rs +++ b/tests/dimension.rs @@ -2,7 +2,7 @@ use defmac::defmac; -use ndarray::{arr2, ArcArray, Array, Axis, Dim, Dimension, IntoDimension, IxDyn, RemoveAxis}; +use ndarray::{arr2, ArcArray, Array, Axis, Dim, Dimension, IxDyn, RemoveAxis}; use std::hash::{Hash, Hasher}; @@ -307,6 +307,7 @@ fn test_array_view() { #[cfg(feature = "std")] #[allow(clippy::cognitive_complexity)] fn test_all_ndindex() { + use ndarray::IntoDimension; macro_rules! ndindex { ($($i:expr),*) => { for &rev in &[false, true] { diff --git a/tests/iterator_chunks.rs b/tests/iterator_chunks.rs index 995557173..67ddd1e38 100644 --- a/tests/iterator_chunks.rs +++ b/tests/iterator_chunks.rs @@ -7,11 +7,11 @@ )] use ndarray::prelude::*; -use ndarray::NdProducer; #[test] #[cfg(feature = "std")] fn chunks() { + use ndarray::NdProducer; let a = >::linspace(1., 100., 10 * 10) .into_shape((10, 10)) .unwrap(); diff --git a/tests/iterators.rs b/tests/iterators.rs index fc345200e..4e4bbc666 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -6,12 +6,9 @@ )] use ndarray::prelude::*; -use ndarray::Ix; -use ndarray::{arr2, arr3, aview1, indices, s, Axis, Data, Dimension, Slice, Zip}; +use ndarray::{arr3, aview1, indices, s, Axis, Slice, Zip}; -use itertools::assert_equal; -use itertools::{enumerate, rev}; -use std::iter::FromIterator; +use itertools::{assert_equal, enumerate}; macro_rules! assert_panics { ($body:expr) => { @@ -37,7 +34,7 @@ fn double_ended() { assert_eq!(it.next(), Some(1.)); assert_eq!(it.rev().last(), Some(2.)); assert_equal(aview1(&[1, 2, 3]), &[1, 2, 3]); - assert_equal(rev(aview1(&[1, 2, 3])), rev(&[1, 2, 3])); + assert_equal(aview1(&[1, 2, 3]).into_iter().rev(), [1, 2, 3].iter().rev()); } #[test] @@ -63,7 +60,7 @@ fn iter_size_hint() { fn indexed() { let a = ArcArray::linspace(0., 7., 8); for (i, elt) in a.indexed_iter() { - assert_eq!(i, *elt as Ix); + assert_eq!(i, *elt as usize); } let a = a.reshape((2, 4, 1)); let (mut i, mut j, k) = (0, 0, 0); @@ -78,22 +75,24 @@ fn indexed() { } } -fn assert_slice_correct(v: &ArrayBase) -where - S: Data, - D: Dimension, - A: PartialEq + std::fmt::Debug, -{ - let slc = v.as_slice(); - assert!(slc.is_some()); - let slc = slc.unwrap(); - assert_eq!(v.len(), slc.len()); - assert_equal(v.iter(), slc); -} - #[test] #[cfg(feature = "std")] fn as_slice() { + use ndarray::Data; + + fn assert_slice_correct(v: &ArrayBase) + where + S: Data, + D: Dimension, + A: PartialEq + std::fmt::Debug, + { + let slc = v.as_slice(); + assert!(slc.is_some()); + let slc = slc.unwrap(); + assert_eq!(v.len(), slc.len()); + assert_equal(v.iter(), slc); + } + let a = ArcArray::linspace(0., 7., 8); let a = a.reshape((2, 4, 1)); @@ -544,9 +543,9 @@ fn axis_chunks_iter_corner_cases() { assert_equal( it, vec![ - arr2(&[[7.], [6.], [5.]]), - arr2(&[[4.], [3.], [2.]]), - arr2(&[[1.], [0.]]), + array![[7.], [6.], [5.]], + array![[4.], [3.], [2.]], + array![[1.], [0.]], ], ); diff --git a/tests/ixdyn.rs b/tests/ixdyn.rs index 3c96cd746..11af2c97a 100644 --- a/tests/ixdyn.rs +++ b/tests/ixdyn.rs @@ -9,7 +9,7 @@ use ndarray::Array; use ndarray::IntoDimension; use ndarray::ShapeBuilder; -use ndarray::{Ix0, Ix1, Ix2, Ix3, IxDyn}; +use ndarray::Ix3; #[test] fn test_ixdyn() { @@ -157,6 +157,8 @@ fn test_0_add_broad() { #[test] #[cfg(feature = "std")] fn test_into_dimension() { + use ndarray::{Ix0, Ix1, Ix2, IxDyn}; + let a = Array::linspace(0., 41., 6 * 7).into_shape((6, 7)).unwrap(); let a2 = a.clone().into_shape(IxDyn(&[6, 7])).unwrap(); let b = a2.clone().into_dimensionality::().unwrap(); diff --git a/tests/numeric.rs b/tests/numeric.rs index 15df53287..e08979d29 100644 --- a/tests/numeric.rs +++ b/tests/numeric.rs @@ -40,7 +40,7 @@ fn test_mean_with_array_of_floats() { #[test] fn sum_mean() { - let a = arr2(&[[1., 2.], [3., 4.]]); + let a: Array2 = arr2(&[[1., 2.], [3., 4.]]); assert_eq!(a.sum_axis(Axis(0)), arr1(&[4., 6.])); assert_eq!(a.sum_axis(Axis(1)), arr1(&[3., 7.])); assert_eq!(a.mean_axis(Axis(0)), Some(arr1(&[2., 3.]))); diff --git a/tests/oper.rs b/tests/oper.rs index 66c24c7f8..5d24de9f7 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -11,11 +11,9 @@ use ndarray::{rcarr1, rcarr2}; use ndarray::{Data, LinalgScalar}; use ndarray::{Ix, Ixs}; use num_traits::Zero; -use std::iter::FromIterator; use approx::assert_abs_diff_eq; use defmac::defmac; -use std::ops::Neg; fn test_oper(op: &str, a: &[f32], b: &[f32], c: &[f32]) { let aa = rcarr1(a); diff --git a/tests/windows.rs b/tests/windows.rs index d1c41f017..9fb8cc8ae 100644 --- a/tests/windows.rs +++ b/tests/windows.rs @@ -7,7 +7,6 @@ use ndarray::prelude::*; use ndarray::Zip; -use std::iter::FromIterator; // Edge Cases for Windows iterator: // From 87a644893d354cd1d1057a66c6542063cc50c4c0 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Thu, 18 Feb 2021 14:07:02 -0500 Subject: [PATCH 189/651] Fix clippy warnings --- src/data_traits.rs | 2 ++ src/dimension/dynindeximpl.rs | 2 +- src/impl_constructors.rs | 1 + src/impl_internal_constructors.rs | 4 ++-- src/linalg/impl_linalg.rs | 1 + 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index 2b59fcb8d..1e191c468 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -94,6 +94,7 @@ pub unsafe trait RawDataClone: RawData { pub unsafe trait Data: RawData { /// Converts the array to a uniquely owned array, cloning elements if necessary. #[doc(hidden)] + #[allow(clippy::wrong_self_convention)] fn into_owned(self_: ArrayBase) -> ArrayBase, D> where Self::Elem: Clone, @@ -102,6 +103,7 @@ pub unsafe trait Data: RawData { /// Return a shared ownership (copy on write) array based on the existing one, /// cloning elements if necessary. #[doc(hidden)] + #[allow(clippy::wrong_self_convention)] fn to_shared(self_: &ArrayBase) -> ArrayBase, D> where Self::Elem: Clone, diff --git a/src/dimension/dynindeximpl.rs b/src/dimension/dynindeximpl.rs index 5c9cd0d61..b75db91c5 100644 --- a/src/dimension/dynindeximpl.rs +++ b/src/dimension/dynindeximpl.rs @@ -51,7 +51,7 @@ impl IxDynRepr { pub fn copy_from(x: &[T]) -> Self { if x.len() <= CAP { let mut arr = [T::zero(); CAP]; - arr[..x.len()].copy_from_slice(&x[..]); + arr[..x.len()].copy_from_slice(x); IxDynRepr::Inline(x.len() as _, arr) } else { Self::from(x) diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 71ca8b32d..63688ea7f 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -73,6 +73,7 @@ where /// /// let array = Array::from_iter(0..10); /// ``` + #[allow(clippy::should_implement_trait)] pub fn from_iter>(iterable: I) -> Self { Self::from_vec(iterable.into_iter().collect()) } diff --git a/src/impl_internal_constructors.rs b/src/impl_internal_constructors.rs index 0ed60a622..5d47c9897 100644 --- a/src/impl_internal_constructors.rs +++ b/src/impl_internal_constructors.rs @@ -25,8 +25,8 @@ where /// See ArrayView::from_shape_ptr for general pointer validity documentation. pub(crate) unsafe fn from_data_ptr(data: S, ptr: NonNull) -> Self { let array = ArrayBase { - data: data, - ptr: ptr, + data, + ptr, dim: Ix1(0), strides: Ix1(1), }; diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index 878444c26..61b91eaed 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -613,6 +613,7 @@ pub fn general_mat_vec_mul( /// /// The caller must ensure that the raw view is valid for writing. /// the destination may be uninitialized iff beta is zero. +#[allow(clippy::collapsible_else_if)] unsafe fn general_mat_vec_mul_impl( alpha: A, a: &ArrayBase, From a3bceb8d80242f0586e5e7761abf9b0597be3335 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 15 Feb 2021 23:54:32 +0100 Subject: [PATCH 190/651] FEAT: Speed up applying the permutation in sort-axis example Speed it up by avoiding bounds checking when looking up the pane to move in the source array. This works because for any given element pointer in the array we have the relationship: .index_axis(axis, 0) + .stride_of(axis) * j == .index_axis(axis, j) where + is pointer arithmetic on the element pointers. --- examples/sort-axis.rs | 101 +++++++++++++++++++++++++++++------------- 1 file changed, 70 insertions(+), 31 deletions(-) diff --git a/examples/sort-axis.rs b/examples/sort-axis.rs index 7ae67fb07..d721571b2 100644 --- a/examples/sort-axis.rs +++ b/examples/sort-axis.rs @@ -1,6 +1,8 @@ use ndarray::prelude::*; use ndarray::{Data, RemoveAxis, Zip}; +use rawpointer::PointerExt; + use std::cmp::Ordering; use std::ptr::copy_nonoverlapping; @@ -97,8 +99,8 @@ where where D: RemoveAxis, { - let axis = axis; let axis_len = self.len_of(axis); + let axis_stride = self.stride_of(axis); assert_eq!(axis_len, perm.indices.len()); debug_assert!(perm.correct()); @@ -112,26 +114,48 @@ where // logically move ownership of all elements from self into result // the result realizes this ownership at .assume_init() further down let mut moved_elements = 0; + + // the permutation vector is used like this: + // + // index: 0 1 2 3 (index in result) + // permut: 2 3 0 1 (index in the source) + // + // move source 2 -> result 0, + // move source 3 -> result 1, + // move source 0 -> result 2, + // move source 1 -> result 3, + // et.c. + + let source_0 = self.raw_view().index_axis_move(axis, 0); + Zip::from(&perm.indices) .and(result.axis_iter_mut(axis)) .for_each(|&perm_i, result_pane| { - // possible improvement: use unchecked indexing for `index_axis` + // Use a shortcut to avoid bounds checking in `index_axis` for the source. + // + // It works because for any given element pointer in the array we have the + // relationship: + // + // .index_axis(axis, 0) + .stride_of(axis) * j == .index_axis(axis, j) + // + // where + is pointer arithmetic on the element pointers. + // + // Here source_0 and the offset is equivalent to self.index_axis(axis, perm_i) Zip::from(result_pane) - .and(self.index_axis(axis, perm_i)) - .for_each(|to, from| { + .and(source_0.clone()) + .for_each(|to, from_0| { + let from = from_0.stride_offset(axis_stride, perm_i); copy_nonoverlapping(from, to.as_mut_ptr(), 1); moved_elements += 1; }); }); debug_assert_eq!(result.len(), moved_elements); - // panic-critical begin: we must not panic - // forget moved array elements but not its vec - // old_storage drops empty + // forget the old elements but not the allocation let mut old_storage = self.into_raw_vec(); old_storage.set_len(0); + // transfer ownership of the elements into the result result.assume_init() - // panic-critical end } } } @@ -179,31 +203,46 @@ mod tests { [75600.94, 17.], [75601.06, 18.], ]; + let answer = array![ + [75600.09, 10.], + [75600.21, 11.], + [75600.45, 13.], + [75600.58, 14.], + [75600.82, 16.], + [75600.94, 17.], + [75601.06, 18.], + [75601.33, 12.], + [107998.96, 1.], + [107999.08, 2.], + [107999.20, 3.], + [107999.45, 5.], + [107999.57, 6.], + [107999.81, 8.], + [107999.94, 9.], + [108000.33, 4.], + [108010.69, 7.], + [109000.70, 15.], + ]; + + // f layout copy of a + let mut af = Array::zeros(a.dim().f()); + af.assign(&a); + + // transposed copy of a + let at = a.t().to_owned(); + // c layout permute let perm = a.sort_axis_by(Axis(0), |i, j| a[[i, 0]] < a[[j, 0]]); + let b = a.permute_axis(Axis(0), &perm); - assert_eq!( - b, - array![ - [75600.09, 10.], - [75600.21, 11.], - [75600.45, 13.], - [75600.58, 14.], - [75600.82, 16.], - [75600.94, 17.], - [75601.06, 18.], - [75601.33, 12.], - [107998.96, 1.], - [107999.08, 2.], - [107999.20, 3.], - [107999.45, 5.], - [107999.57, 6.], - [107999.81, 8.], - [107999.94, 9.], - [108000.33, 4.], - [108010.69, 7.], - [109000.70, 15.], - ] - ); + assert_eq!(b, answer); + + // f layout permute + let bf = af.permute_axis(Axis(0), &perm); + assert_eq!(bf, answer); + + // transposed permute + let bt = at.permute_axis(Axis(1), &perm); + assert_eq!(bt, answer.t()); } } From 784a9a842b42313eb3ce71b793bf80c777a1fef0 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 9 Mar 2021 19:04:51 -0500 Subject: [PATCH 191/651] Reduce test size for miri This makes `cargo +nightly miri test` run in a more reasonable length of time (<10 minutes on my machine). Before, testing with miri took >30 minutes to complete. (I'm not sure how long it would have ultimately taken; I killed the test at that point.) --- tests/oper.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/oper.rs b/tests/oper.rs index 5d24de9f7..0d659fa1e 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -666,17 +666,21 @@ fn gemm_64_1_f() { fn gen_mat_mul_i32() { let alpha = -1; let beta = 2; - let sizes = vec![ - (4, 4, 4), - (8, 8, 8), - (17, 15, 16), - (4, 17, 3), - (17, 3, 22), - (19, 18, 2), - (16, 17, 15), - (15, 16, 17), - (67, 63, 62), - ]; + let sizes = if cfg!(miri) { + vec![(4, 4, 4), (4, 7, 3)] + } else { + vec![ + (4, 4, 4), + (8, 8, 8), + (17, 15, 16), + (4, 17, 3), + (17, 3, 22), + (19, 18, 2), + (16, 17, 15), + (15, 16, 17), + (67, 63, 62), + ] + }; for &(m, k, n) in &sizes { let a = range_i32(m, k); let b = range_i32(k, n); From f7a94916e7e496c99c00e8535077d8ce18bd0e72 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Wed, 20 Jan 2021 11:29:12 +0800 Subject: [PATCH 192/651] Fix co_broadcast in operator overloading --- src/dimension/broadcast.rs | 105 ++++++++++++++++++++++++++++++++++++ src/dimension/mod.rs | 2 + src/impl_ops.rs | 85 +++++++++++++++++++++-------- src/lib.rs | 1 + src/numeric/impl_numeric.rs | 5 +- tests/array.rs | 94 +++++++++++++++++++++++++++++++- 6 files changed, 266 insertions(+), 26 deletions(-) create mode 100644 src/dimension/broadcast.rs diff --git a/src/dimension/broadcast.rs b/src/dimension/broadcast.rs new file mode 100644 index 000000000..f29936cd1 --- /dev/null +++ b/src/dimension/broadcast.rs @@ -0,0 +1,105 @@ +use crate::error::*; +use crate::{Dimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn}; + +/// Calculate the co_broadcast shape of two dimensions. Return error if shapes are +/// not compatible. +fn broadcast_shape(shape1: &D1, shape2: &D2) -> Result +where + D1: Dimension, + D2: Dimension, + Output: Dimension, +{ + let (k, overflow) = shape1.ndim().overflowing_sub(shape2.ndim()); + // Swap the order if d2 is longer. + if overflow { + return broadcast_shape::(shape2, shape1); + } + // The output should be the same length as shape1. + let mut out = Output::zeros(shape1.ndim()); + let out_slice = out.slice_mut(); + let s1 = shape1.slice(); + let s2 = shape2.slice(); + // Uses the [NumPy broadcasting rules] + // (https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#general-broadcasting-rules). + // + // Zero dimension element is not in the original rules of broadcasting. + // We currently treat it as the same as 1. Especially, when one side is + // zero with one side is empty, or both sides are zero, the result will + // remain zero. + for i in 0..shape1.ndim() { + out_slice[i] = s1[i]; + } + for i in 0..shape2.ndim() { + if out_slice[i + k] != s2[i] && s2[i] != 0 { + if out_slice[i + k] <= 1 { + out_slice[i + k] = s2[i] + } else if s2[i] != 1 { + return Err(from_kind(ErrorKind::IncompatibleShape)); + } + } + } + Ok(out) +} + +pub trait BroadcastShape: Dimension { + /// The resulting dimension type after broadcasting. + type BroadcastOutput: Dimension; + + /// Determines the shape after broadcasting the dimensions together. + /// + /// If the dimensions are not compatible, returns `Err`. + /// + /// Uses the [NumPy broadcasting rules] + /// (https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#general-broadcasting-rules). + fn broadcast_shape(&self, other: &Other) -> Result { + broadcast_shape::(self, other) + } +} + +/// Dimensions of the same type remain unchanged when co_broadcast. +/// So you can directly use D as the resulting type. +/// (Instead of >::BroadcastOutput) +impl BroadcastShape for D { + type BroadcastOutput = D; +} + +macro_rules! impl_broadcast_distinct_fixed { + ($smaller:ty, $larger:ty) => { + impl BroadcastShape<$larger> for $smaller { + type BroadcastOutput = $larger; + } + + impl BroadcastShape<$smaller> for $larger { + type BroadcastOutput = $larger; + } + }; +} + +impl_broadcast_distinct_fixed!(Ix0, Ix1); +impl_broadcast_distinct_fixed!(Ix0, Ix2); +impl_broadcast_distinct_fixed!(Ix0, Ix3); +impl_broadcast_distinct_fixed!(Ix0, Ix4); +impl_broadcast_distinct_fixed!(Ix0, Ix5); +impl_broadcast_distinct_fixed!(Ix0, Ix6); +impl_broadcast_distinct_fixed!(Ix1, Ix2); +impl_broadcast_distinct_fixed!(Ix1, Ix3); +impl_broadcast_distinct_fixed!(Ix1, Ix4); +impl_broadcast_distinct_fixed!(Ix1, Ix5); +impl_broadcast_distinct_fixed!(Ix1, Ix6); +impl_broadcast_distinct_fixed!(Ix2, Ix3); +impl_broadcast_distinct_fixed!(Ix2, Ix4); +impl_broadcast_distinct_fixed!(Ix2, Ix5); +impl_broadcast_distinct_fixed!(Ix2, Ix6); +impl_broadcast_distinct_fixed!(Ix3, Ix4); +impl_broadcast_distinct_fixed!(Ix3, Ix5); +impl_broadcast_distinct_fixed!(Ix3, Ix6); +impl_broadcast_distinct_fixed!(Ix4, Ix5); +impl_broadcast_distinct_fixed!(Ix4, Ix6); +impl_broadcast_distinct_fixed!(Ix5, Ix6); +impl_broadcast_distinct_fixed!(Ix0, IxDyn); +impl_broadcast_distinct_fixed!(Ix1, IxDyn); +impl_broadcast_distinct_fixed!(Ix2, IxDyn); +impl_broadcast_distinct_fixed!(Ix3, IxDyn); +impl_broadcast_distinct_fixed!(Ix4, IxDyn); +impl_broadcast_distinct_fixed!(Ix5, IxDyn); +impl_broadcast_distinct_fixed!(Ix6, IxDyn); diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 1359b8f39..98572ac59 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -12,6 +12,7 @@ use num_integer::div_floor; pub use self::axes::{axes_of, Axes, AxisDescription}; pub use self::axis::Axis; +pub use self::broadcast::BroadcastShape; pub use self::conversion::IntoDimension; pub use self::dim::*; pub use self::dimension_trait::Dimension; @@ -28,6 +29,7 @@ use std::mem; mod macros; mod axes; mod axis; +mod broadcast; mod conversion; pub mod dim; mod dimension_trait; diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 256bee3e5..d7645b8cb 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use crate::dimension::BroadcastShape; use num_complex::Complex; /// Elements that can be used as direct operands in arithmetic with arrays. @@ -53,24 +54,48 @@ macro_rules! impl_binary_op( /// Perform elementwise #[doc=$doc] /// between `self` and `rhs`, -/// and return the result (based on `self`). -/// -/// `self` must be an `Array` or `ArcArray`. +/// and return the result. /// -/// If their shapes disagree, `rhs` is broadcast to the shape of `self`. +/// If their shapes disagree, `self` is broadcast to their broadcast shape, +/// cloning the data if needed. /// /// **Panics** if broadcasting isn’t possible. impl $trt> for ArrayBase where A: Clone + $trt, B: Clone, - S: DataOwned + DataMut, + S: Data, S2: Data, - D: Dimension, + D: Dimension + BroadcastShape, E: Dimension, { - type Output = ArrayBase; - fn $mth(self, rhs: ArrayBase) -> ArrayBase + type Output = Array>::BroadcastOutput>; + fn $mth(self, rhs: ArrayBase) -> Self::Output + { + self.$mth(&rhs) + } +} + +/// Perform elementwise +#[doc=$doc] +/// between reference `self` and `rhs`, +/// and return the result as a new `Array`. +/// +/// If their shapes disagree, `self` is broadcast to their broadcast shape, +/// cloning the data if needed. +/// +/// **Panics** if broadcasting isn’t possible. +impl<'a, A, B, S, S2, D, E> $trt> for &'a ArrayBase +where + A: Clone + $trt, + B: Clone, + S: Data, + S2: Data, + D: Dimension + BroadcastShape, + E: Dimension, +{ + type Output = Array>::BroadcastOutput>; + fn $mth(self, rhs: ArrayBase) -> Self::Output { self.$mth(&rhs) } @@ -79,27 +104,34 @@ where /// Perform elementwise #[doc=$doc] /// between `self` and reference `rhs`, -/// and return the result (based on `self`). +/// and return the result. /// -/// If their shapes disagree, `rhs` is broadcast to the shape of `self`. +/// If their shapes disagree, `self` is broadcast to their broadcast shape, +/// cloning the data if needed. /// /// **Panics** if broadcasting isn’t possible. impl<'a, A, B, S, S2, D, E> $trt<&'a ArrayBase> for ArrayBase where A: Clone + $trt, B: Clone, - S: DataOwned + DataMut, + S: Data, S2: Data, - D: Dimension, + D: Dimension + BroadcastShape, E: Dimension, { - type Output = ArrayBase; - fn $mth(mut self, rhs: &ArrayBase) -> ArrayBase + type Output = Array>::BroadcastOutput>; + fn $mth(self, rhs: &ArrayBase) -> Self::Output { - self.zip_mut_with(rhs, |x, y| { + let shape = self.dim.broadcast_shape(&rhs.dim).unwrap(); + let mut self_ = if shape.slice() == self.dim.slice() { + self.into_owned().into_dimensionality::<>::BroadcastOutput>().unwrap() + } else { + self.broadcast(shape).unwrap().to_owned() + }; + self_.zip_mut_with(rhs, |x, y| { *x = x.clone() $operator y.clone(); }); - self + self_ } } @@ -108,7 +140,8 @@ where /// between references `self` and `rhs`, /// and return the result as a new `Array`. /// -/// If their shapes disagree, `rhs` is broadcast to the shape of `self`. +/// If their shapes disagree, `self` is broadcast to their broadcast shape, +/// cloning the data if needed. /// /// **Panics** if broadcasting isn’t possible. impl<'a, A, B, S, S2, D, E> $trt<&'a ArrayBase> for &'a ArrayBase @@ -117,13 +150,21 @@ where B: Clone, S: Data, S2: Data, - D: Dimension, + D: Dimension + BroadcastShape, E: Dimension, { - type Output = Array; - fn $mth(self, rhs: &'a ArrayBase) -> Array { - // FIXME: Can we co-broadcast arrays here? And how? - self.to_owned().$mth(rhs) + type Output = Array>::BroadcastOutput>; + fn $mth(self, rhs: &'a ArrayBase) -> Self::Output { + let shape = self.dim.broadcast_shape(&rhs.dim).unwrap(); + let mut self_ = if shape.slice() == self.dim.slice() { + self.to_owned().into_dimensionality::<>::BroadcastOutput>().unwrap() + } else { + self.broadcast(shape).unwrap().to_owned() + }; + self_.zip_mut_with(rhs, |x, y| { + *x = x.clone() $operator y.clone(); + }); + self_ } } diff --git a/src/lib.rs b/src/lib.rs index 9cd7dc3f3..1a760bf31 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -134,6 +134,7 @@ use std::marker::PhantomData; use alloc::sync::Arc; pub use crate::dimension::dim::*; +pub use crate::dimension::BroadcastShape; pub use crate::dimension::{Axis, AxisDescription, Dimension, IntoDimension, RemoveAxis}; pub use crate::dimension::IxDynImpl; diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index fbec80418..5485a774e 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -13,7 +13,7 @@ use std::ops::{Add, Div, Mul}; use crate::imp_prelude::*; use crate::itertools::enumerate; -use crate::numeric_util; +use crate::{numeric_util, BroadcastShape}; /// # Numerical Methods for Arrays impl ArrayBase @@ -283,10 +283,11 @@ where /// a.mean_axis(Axis(0)).unwrap().mean_axis(Axis(0)).unwrap() == aview0(&3.5) /// ); /// ``` - pub fn mean_axis(&self, axis: Axis) -> Option> + pub fn mean_axis(&self, axis: Axis) -> Option>::BroadcastOutput>> where A: Clone + Zero + FromPrimitive + Add + Div, D: RemoveAxis, + D::Smaller: BroadcastShape, { let axis_length = self.len_of(axis); if axis_length == 0 { diff --git a/tests/array.rs b/tests/array.rs index 6b72bb5c4..599504d3d 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -9,9 +9,9 @@ use defmac::defmac; use itertools::{enumerate, zip, Itertools}; -use ndarray::indices; use ndarray::prelude::*; use ndarray::{arr3, rcarr2}; +use ndarray::{indices, BroadcastShape, ErrorKind, IxDynImpl, ShapeError}; use ndarray::{Slice, SliceInfo, SliceOrIndex}; macro_rules! assert_panics { @@ -484,7 +484,7 @@ fn test_add() { } let B = A.clone(); - A = A + &B; + let A = A + &B; assert_eq!(A[[0, 0]], 0); assert_eq!(A[[0, 1]], 2); assert_eq!(A[[1, 0]], 4); @@ -1557,6 +1557,53 @@ fn insert_axis_view() { ); } +#[test] +fn test_broadcast_shape() { + fn test_co( + d1: &D1, + d2: &D2, + r: Result<>::BroadcastOutput, ShapeError>, + ) where + D1: Dimension + BroadcastShape, + D2: Dimension, + { + let d = d1.broadcast_shape(&d2); + assert_eq!(d, r); + } + test_co(&Dim([2, 3]), &Dim([4, 1, 3]), Ok(Dim([4, 2, 3]))); + test_co( + &Dim([1, 2, 2]), + &Dim([1, 3, 4]), + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), + ); + test_co(&Dim([3, 4, 5]), &Ix0(), Ok(Dim([3, 4, 5]))); + let v = vec![1, 2, 3, 4, 5, 6, 7]; + test_co( + &Dim(vec![1, 1, 3, 1, 5, 1, 7]), + &Dim([2, 1, 4, 1, 6, 1]), + Ok(Dim(IxDynImpl::from(v.as_slice()))), + ); + let d = Dim([1, 2, 1, 3]); + test_co(&d, &d, Ok(d)); + test_co( + &Dim([2, 1, 2]).into_dyn(), + &Dim(0), + Ok(Dim([2, 1, 2]).into_dyn()), + ); + test_co( + &Dim([2, 1, 1]), + &Dim([0, 0, 0, 3, 4]), + Ok(Dim([0, 0, 2, 3, 4])), + ); + test_co(&Dim([0]), &Dim([0, 0, 0]), Ok(Dim([0, 0, 0]))); + test_co(&Dim(1), &Dim([1, 0, 0]), Ok(Dim([1, 0, 1]))); + test_co( + &Dim([1, 3, 0, 1, 1]), + &Dim([1, 2, 3, 1]), + Ok(Dim([1, 3, 2, 3, 1])), + ); +} + #[test] fn arithmetic_broadcast() { let mut a = arr2(&[[1., 2.], [3., 4.]]); @@ -1565,6 +1612,49 @@ fn arithmetic_broadcast() { a.swap_axes(0, 1); let b = a.clone() / aview0(&1.); assert_eq!(a, b); + + // reference + let a = arr2(&[[2], [3], [4]]); + let b = arr1(&[5, 6, 7]); + assert_eq!(&a + &b, arr2(&[[7, 8, 9], [8, 9, 10], [9, 10, 11]])); + assert_eq!( + a.clone() - &b, + arr2(&[[-3, -4, -5], [-2, -3, -4], [-1, -2, -3]]) + ); + assert_eq!( + a.clone() * b.clone(), + arr2(&[[10, 12, 14], [15, 18, 21], [20, 24, 28]]) + ); + assert_eq!(&b / a, arr2(&[[2, 3, 3], [1, 2, 2], [1, 1, 1]])); + + // Negative strides and non-contiguous memory + let s = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + let s = Array3::from_shape_vec((2, 3, 2).strides((1, 4, 2)), s.to_vec()).unwrap(); + let a = s.slice(s![..;-1,..;2,..]); + let b = s.slice(s![..2, -1, ..]); + let mut c = s.clone(); + c.collapse_axis(Axis(2), 1); + let c = c.slice(s![1,..;2,..]); + assert_eq!( + &a.to_owned() + &b, + arr3(&[[[11, 15], [20, 24]], [[10, 14], [19, 23]]]) + ); + assert_eq!( + &a + b + c.into_owned(), + arr3(&[[[15, 19], [32, 36]], [[14, 18], [31, 35]]]) + ); + + // shared array + let sa = a.to_shared(); + let sa2 = sa.to_shared(); + let sb = b.to_shared(); + let sb2 = sb.to_shared(); + let sc = c.to_shared(); + let sc2 = sc.into_shared(); + assert_eq!( + sa2 + sb2 + sc2.into_owned(), + arr3(&[[[15, 19], [32, 36]], [[14, 18], [31, 35]]]) + ); } #[test] From 5e77eed8701b27c5edab6d9791af0f359de390c7 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Sat, 23 Jan 2021 19:19:04 +0800 Subject: [PATCH 193/651] Add BroadCastShape to the Dimension definition --- src/dimension/broadcast.rs | 18 ++++++++++++++---- src/dimension/dimension_trait.rs | 5 ++++- src/numeric/impl_numeric.rs | 5 ++--- tests/array.rs | 2 +- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/dimension/broadcast.rs b/src/dimension/broadcast.rs index f29936cd1..15d656f7f 100644 --- a/src/dimension/broadcast.rs +++ b/src/dimension/broadcast.rs @@ -41,7 +41,7 @@ where Ok(out) } -pub trait BroadcastShape: Dimension { +pub trait BroadcastShape { /// The resulting dimension type after broadcasting. type BroadcastOutput: Dimension; @@ -51,9 +51,7 @@ pub trait BroadcastShape: Dimension { /// /// Uses the [NumPy broadcasting rules] /// (https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#general-broadcasting-rules). - fn broadcast_shape(&self, other: &Other) -> Result { - broadcast_shape::(self, other) - } + fn broadcast_shape(&self, other: &Other) -> Result; } /// Dimensions of the same type remain unchanged when co_broadcast. @@ -61,16 +59,28 @@ pub trait BroadcastShape: Dimension { /// (Instead of >::BroadcastOutput) impl BroadcastShape for D { type BroadcastOutput = D; + + fn broadcast_shape(&self, other: &D) -> Result { + broadcast_shape::(self, other) + } } macro_rules! impl_broadcast_distinct_fixed { ($smaller:ty, $larger:ty) => { impl BroadcastShape<$larger> for $smaller { type BroadcastOutput = $larger; + + fn broadcast_shape(&self, other: &$larger) -> Result { + broadcast_shape::(self, other) + } } impl BroadcastShape<$smaller> for $larger { type BroadcastOutput = $larger; + + fn broadcast_shape(&self, other: &$smaller) -> Result { + broadcast_shape::(self, other) + } } }; } diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 92f241189..6c2893e4d 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -15,7 +15,7 @@ use super::axes_of; use super::conversion::Convert; use super::{stride_offset, stride_offset_checked}; use crate::itertools::{enumerate, zip}; -use crate::Axis; +use crate::{Axis, BroadcastShape}; use crate::IntoDimension; use crate::RemoveAxis; use crate::{ArrayView1, ArrayViewMut1}; @@ -46,6 +46,9 @@ pub trait Dimension: + MulAssign + for<'x> MulAssign<&'x Self> + MulAssign + + BroadcastShape + + BroadcastShape + + BroadcastShape { /// For fixed-size dimension representations (e.g. `Ix2`), this should be /// `Some(ndim)`, and for variable-size dimension representations (e.g. diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 5485a774e..fbec80418 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -13,7 +13,7 @@ use std::ops::{Add, Div, Mul}; use crate::imp_prelude::*; use crate::itertools::enumerate; -use crate::{numeric_util, BroadcastShape}; +use crate::numeric_util; /// # Numerical Methods for Arrays impl ArrayBase @@ -283,11 +283,10 @@ where /// a.mean_axis(Axis(0)).unwrap().mean_axis(Axis(0)).unwrap() == aview0(&3.5) /// ); /// ``` - pub fn mean_axis(&self, axis: Axis) -> Option>::BroadcastOutput>> + pub fn mean_axis(&self, axis: Axis) -> Option> where A: Clone + Zero + FromPrimitive + Add + Div, D: RemoveAxis, - D::Smaller: BroadcastShape, { let axis_length = self.len_of(axis); if axis_length == 0 { diff --git a/tests/array.rs b/tests/array.rs index 599504d3d..a49ae65b3 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1567,7 +1567,7 @@ fn test_broadcast_shape() { D1: Dimension + BroadcastShape, D2: Dimension, { - let d = d1.broadcast_shape(&d2); + let d = d1.broadcast_shape(d2); assert_eq!(d, r); } test_co(&Dim([2, 3]), &Dim([4, 1, 3]), Ok(Dim([4, 2, 3]))); From e8b29e1c3d7299bea394021cb6f2bf4ab9bb36a7 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Sat, 23 Jan 2021 23:15:48 +0800 Subject: [PATCH 194/651] add BroadCastShape<::Smaller> --- src/dimension/dimension_trait.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 6c2893e4d..89d106521 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -49,6 +49,8 @@ pub trait Dimension: + BroadcastShape + BroadcastShape + BroadcastShape + + BroadcastShape<::Smaller, BroadcastOutput=Self> + + BroadcastShape<::Larger, BroadcastOutput=::Larger> { /// For fixed-size dimension representations (e.g. `Ix2`), this should be /// `Some(ndim)`, and for variable-size dimension representations (e.g. From f7c9da16b483d6b4909f43eb055acf1a896e96ab Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Mon, 25 Jan 2021 16:47:42 +0800 Subject: [PATCH 195/651] use uninitialized to avoid traversing or cloning --- src/impl_methods.rs | 40 +++++++++++++++ src/impl_ops.rs | 99 +++++++++++++++++++++++++------------ src/numeric/impl_numeric.rs | 4 +- tests/array.rs | 6 +-- 4 files changed, 113 insertions(+), 36 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 9943e7c8e..e7d5e7328 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2037,6 +2037,46 @@ where self.zip_mut_with_by_rows(rhs, f); } + /// Traverse two arrays in unspecified order, in lock step, + /// calling the closure `f` on each element pair, and put + /// the result into the corresponding element of self. + pub fn zip_mut_from_pair(&mut self, lhs: &ArrayBase, rhs: &ArrayBase, f: F, ) + where + S: DataMut, + S1: Data, + S2: Data, + F: Fn(&B, &C) -> A, + { + debug_assert_eq!(self.shape(), lhs.shape()); + debug_assert_eq!(self.shape(), rhs.shape()); + + if self.dim.strides_equivalent(&self.strides, &lhs.strides) + && self.dim.strides_equivalent(&self.strides, &rhs.strides) + { + if let Some(self_s) = self.as_slice_memory_order_mut() { + if let Some(lhs_s) = lhs.as_slice_memory_order() { + if let Some(rhs_s) = rhs.as_slice_memory_order() { + for (s, (l, r)) in + self_s.iter_mut().zip(lhs_s.iter().zip(rhs_s)) { + *s = f(&l, &r); + } + return; + } + } + } + } + + // Otherwise, fall back to the outer iter + let n = self.ndim(); + let dim = self.raw_dim(); + Zip::from(LanesMut::new(self.view_mut(), Axis(n - 1))) + .and(Lanes::new(lhs.broadcast_assume(dim.clone()), Axis(n - 1))) + .and(Lanes::new(rhs.broadcast_assume(dim), Axis(n - 1))) + .for_each(move |s_row, l_row, r_row| { + Zip::from(s_row).and(l_row).and(r_row).for_each(|s, a, b| *s = f(a, b)) + }); + } + // zip two arrays where they have different layout or strides #[inline(always)] fn zip_mut_with_by_rows(&mut self, rhs: &ArrayBase, mut f: F) diff --git a/src/impl_ops.rs b/src/impl_ops.rs index d7645b8cb..7e28d48cf 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -56,20 +56,22 @@ macro_rules! impl_binary_op( /// between `self` and `rhs`, /// and return the result. /// +/// `self` must be an `Array` or `ArcArray`. +/// /// If their shapes disagree, `self` is broadcast to their broadcast shape, /// cloning the data if needed. /// /// **Panics** if broadcasting isn’t possible. impl $trt> for ArrayBase where - A: Clone + $trt, + A: Copy + $trt, B: Clone, - S: Data, + S: DataOwned + DataMut, S2: Data, D: Dimension + BroadcastShape, E: Dimension, { - type Output = Array>::BroadcastOutput>; + type Output = ArrayBase>::BroadcastOutput>; fn $mth(self, rhs: ArrayBase) -> Self::Output { self.$mth(&rhs) @@ -79,7 +81,9 @@ where /// Perform elementwise #[doc=$doc] /// between reference `self` and `rhs`, -/// and return the result as a new `Array`. +/// and return the result. +/// +/// `rhs` must be an `Array` or `ArcArray`. /// /// If their shapes disagree, `self` is broadcast to their broadcast shape, /// cloning the data if needed. @@ -87,17 +91,36 @@ where /// **Panics** if broadcasting isn’t possible. impl<'a, A, B, S, S2, D, E> $trt> for &'a ArrayBase where - A: Clone + $trt, - B: Clone, + A: Clone + $trt, + B: Copy, S: Data, - S2: Data, - D: Dimension + BroadcastShape, - E: Dimension, + S2: DataOwned + DataMut, + D: Dimension, + E: Dimension + BroadcastShape, { - type Output = Array>::BroadcastOutput>; + type Output = ArrayBase>::BroadcastOutput>; fn $mth(self, rhs: ArrayBase) -> Self::Output { - self.$mth(&rhs) + let shape = rhs.dim.broadcast_shape(&self.dim).unwrap(); + if shape.slice() == rhs.dim.slice() { + let mut out = rhs.into_dimensionality::<>::BroadcastOutput>().unwrap(); + out.zip_mut_with(self, |x, y| { + *x = y.clone() $operator x.clone(); + }); + out + } else { + // SAFETY: Overwrite all the elements in the array after + // it is created via `zip_mut_from_pair`. + let mut out = unsafe { + Self::Output::uninitialized(shape.clone().into_pattern()) + }; + let lhs = self.broadcast(shape.clone()).unwrap(); + let rhs = rhs.broadcast(shape).unwrap(); + out.zip_mut_from_pair(&lhs, &rhs, |x, y| { + x.clone() $operator y.clone() + }); + out + } } } @@ -106,32 +129,44 @@ where /// between `self` and reference `rhs`, /// and return the result. /// +/// `rhs` must be an `Array` or `ArcArray`. +/// /// If their shapes disagree, `self` is broadcast to their broadcast shape, /// cloning the data if needed. /// /// **Panics** if broadcasting isn’t possible. impl<'a, A, B, S, S2, D, E> $trt<&'a ArrayBase> for ArrayBase where - A: Clone + $trt, + A: Copy + $trt, B: Clone, - S: Data, + S: DataOwned + DataMut, S2: Data, D: Dimension + BroadcastShape, E: Dimension, { - type Output = Array>::BroadcastOutput>; + type Output = ArrayBase>::BroadcastOutput>; fn $mth(self, rhs: &ArrayBase) -> Self::Output { let shape = self.dim.broadcast_shape(&rhs.dim).unwrap(); - let mut self_ = if shape.slice() == self.dim.slice() { - self.into_owned().into_dimensionality::<>::BroadcastOutput>().unwrap() + if shape.slice() == self.dim.slice() { + let mut out = self.into_dimensionality::<>::BroadcastOutput>().unwrap(); + out.zip_mut_with(rhs, |x, y| { + *x = x.clone() $operator y.clone(); + }); + out } else { - self.broadcast(shape).unwrap().to_owned() - }; - self_.zip_mut_with(rhs, |x, y| { - *x = x.clone() $operator y.clone(); - }); - self_ + // SAFETY: Overwrite all the elements in the array after + // it is created via `zip_mut_from_pair`. + let mut out = unsafe { + Self::Output::uninitialized(shape.clone().into_pattern()) + }; + let lhs = self.broadcast(shape.clone()).unwrap(); + let rhs = rhs.broadcast(shape).unwrap(); + out.zip_mut_from_pair(&lhs, &rhs, |x, y| { + x.clone() $operator y.clone() + }); + out + } } } @@ -140,13 +175,13 @@ where /// between references `self` and `rhs`, /// and return the result as a new `Array`. /// -/// If their shapes disagree, `self` is broadcast to their broadcast shape, +/// If their shapes disagree, `self` and `rhs` is broadcast to their broadcast shape, /// cloning the data if needed. /// /// **Panics** if broadcasting isn’t possible. impl<'a, A, B, S, S2, D, E> $trt<&'a ArrayBase> for &'a ArrayBase where - A: Clone + $trt, + A: Copy + $trt, B: Clone, S: Data, S2: Data, @@ -156,15 +191,17 @@ where type Output = Array>::BroadcastOutput>; fn $mth(self, rhs: &'a ArrayBase) -> Self::Output { let shape = self.dim.broadcast_shape(&rhs.dim).unwrap(); - let mut self_ = if shape.slice() == self.dim.slice() { - self.to_owned().into_dimensionality::<>::BroadcastOutput>().unwrap() - } else { - self.broadcast(shape).unwrap().to_owned() + // SAFETY: Overwrite all the elements in the array after + // it is created via `zip_mut_from_pair`. + let mut out = unsafe { + Self::Output::uninitialized(shape.clone().into_pattern()) }; - self_.zip_mut_with(rhs, |x, y| { - *x = x.clone() $operator y.clone(); + let lhs = self.broadcast(shape.clone()).unwrap(); + let rhs = rhs.broadcast(shape).unwrap(); + out.zip_mut_from_pair(&lhs, &rhs, |x, y| { + x.clone() $operator y.clone() }); - self_ + out } } diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index fbec80418..0c5b4820d 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -243,7 +243,7 @@ where /// **Panics** if `axis` is out of bounds. pub fn sum_axis(&self, axis: Axis) -> Array where - A: Clone + Zero + Add, + A: Copy + Zero + Add, D: RemoveAxis, { let n = self.len_of(axis); @@ -285,7 +285,7 @@ where /// ``` pub fn mean_axis(&self, axis: Axis) -> Option> where - A: Clone + Zero + FromPrimitive + Add + Div, + A: Copy + Zero + FromPrimitive + Add + Div, D: RemoveAxis, { let axis_length = self.len_of(axis); diff --git a/tests/array.rs b/tests/array.rs index a49ae65b3..370600bd3 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -484,7 +484,7 @@ fn test_add() { } let B = A.clone(); - let A = A + &B; + A = A + &B; assert_eq!(A[[0, 0]], 0); assert_eq!(A[[0, 1]], 2); assert_eq!(A[[1, 0]], 4); @@ -1640,7 +1640,7 @@ fn arithmetic_broadcast() { arr3(&[[[11, 15], [20, 24]], [[10, 14], [19, 23]]]) ); assert_eq!( - &a + b + c.into_owned(), + &a + b.into_owned() + c, arr3(&[[[15, 19], [32, 36]], [[14, 18], [31, 35]]]) ); @@ -1652,7 +1652,7 @@ fn arithmetic_broadcast() { let sc = c.to_shared(); let sc2 = sc.into_shared(); assert_eq!( - sa2 + sb2 + sc2.into_owned(), + sa2 + &sb2 + sc2.into_owned(), arr3(&[[[15, 19], [32, 36]], [[14, 18], [31, 35]]]) ); } From 0561c1325c88086ad7239a5fe596cf66870c6c91 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Tue, 26 Jan 2021 16:35:25 +0800 Subject: [PATCH 196/651] Use MaybeUninitSubst and Zip to avoid uninitialized(), rename BroadCastOutput to Output, remove zip_mut_from_pair() --- src/data_traits.rs | 28 +++++-- src/dimension/broadcast.rs | 22 +++--- src/dimension/dimension_trait.rs | 10 +-- src/impl_methods.rs | 40 ---------- src/impl_ops.rs | 129 ++++++++++++++++--------------- src/lib.rs | 2 +- tests/array.rs | 2 +- 7 files changed, 108 insertions(+), 125 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index 1e191c468..eabe1795b 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -10,15 +10,12 @@ use rawpointer::PointerExt; -use std::mem::{self, size_of}; -use std::mem::MaybeUninit; +use std::mem::{self, size_of};use std::mem::MaybeUninit; use std::ptr::NonNull; use alloc::sync::Arc; use alloc::vec::Vec; -use crate::{ - ArrayBase, CowRepr, Dimension, OwnedArcRepr, OwnedRepr, RawViewRepr, ViewRepr, -}; +use crate::{ArrayBase, CowRepr, Dimension, OwnedArcRepr, OwnedRepr, RawViewRepr, ViewRepr}; /// Array representation trait. /// @@ -414,7 +411,6 @@ pub unsafe trait DataOwned: Data { /// Corresponding owned data with MaybeUninit elements type MaybeUninit: DataOwned> + RawDataSubst; - #[doc(hidden)] fn new(elements: Vec) -> Self; @@ -440,6 +436,7 @@ unsafe impl DataOwned for OwnedRepr { fn new(elements: Vec) -> Self { OwnedRepr::from(elements) } + fn into_shared(self) -> OwnedArcRepr { OwnedArcRepr(Arc::new(self)) } @@ -622,3 +619,22 @@ impl<'a, A: 'a, B: 'a> RawDataSubst for ViewRepr<&'a mut A> { ViewRepr::new() } } + +/// Array representation trait. +/// +/// The MaybeUninitSubst trait maps the MaybeUninit type of element, while +/// mapping the MaybeUninit type back to origin element type. +/// +/// For example, `MaybeUninitSubst` can map the type `OwnedRepr` to `OwnedRepr>`, +/// and use `Output as RawDataSubst` to map `OwnedRepr>` back to `OwnedRepr`. +pub trait MaybeUninitSubst: DataOwned { + type Output: DataOwned> + RawDataSubst>; +} + +impl MaybeUninitSubst for OwnedRepr { + type Output = OwnedRepr>; +} + +impl MaybeUninitSubst for OwnedArcRepr { + type Output = OwnedArcRepr>; +} diff --git a/src/dimension/broadcast.rs b/src/dimension/broadcast.rs index 15d656f7f..d13e2ac3a 100644 --- a/src/dimension/broadcast.rs +++ b/src/dimension/broadcast.rs @@ -43,7 +43,7 @@ where pub trait BroadcastShape { /// The resulting dimension type after broadcasting. - type BroadcastOutput: Dimension; + type Output: Dimension; /// Determines the shape after broadcasting the dimensions together. /// @@ -51,35 +51,35 @@ pub trait BroadcastShape { /// /// Uses the [NumPy broadcasting rules] /// (https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#general-broadcasting-rules). - fn broadcast_shape(&self, other: &Other) -> Result; + fn broadcast_shape(&self, other: &Other) -> Result; } /// Dimensions of the same type remain unchanged when co_broadcast. /// So you can directly use D as the resulting type. /// (Instead of >::BroadcastOutput) impl BroadcastShape for D { - type BroadcastOutput = D; + type Output = D; - fn broadcast_shape(&self, other: &D) -> Result { - broadcast_shape::(self, other) + fn broadcast_shape(&self, other: &D) -> Result { + broadcast_shape::(self, other) } } macro_rules! impl_broadcast_distinct_fixed { ($smaller:ty, $larger:ty) => { impl BroadcastShape<$larger> for $smaller { - type BroadcastOutput = $larger; + type Output = $larger; - fn broadcast_shape(&self, other: &$larger) -> Result { - broadcast_shape::(self, other) + fn broadcast_shape(&self, other: &$larger) -> Result { + broadcast_shape::(self, other) } } impl BroadcastShape<$smaller> for $larger { - type BroadcastOutput = $larger; + type Output = $larger; - fn broadcast_shape(&self, other: &$smaller) -> Result { - broadcast_shape::(self, other) + fn broadcast_shape(&self, other: &$smaller) -> Result { + broadcast_shape::(self, other) } } }; diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 89d106521..e3a1ebd09 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -46,11 +46,11 @@ pub trait Dimension: + MulAssign + for<'x> MulAssign<&'x Self> + MulAssign - + BroadcastShape - + BroadcastShape - + BroadcastShape - + BroadcastShape<::Smaller, BroadcastOutput=Self> - + BroadcastShape<::Larger, BroadcastOutput=::Larger> + + BroadcastShape + + BroadcastShape + + BroadcastShape + + BroadcastShape<::Smaller, Output=Self> + + BroadcastShape<::Larger, Output=::Larger> { /// For fixed-size dimension representations (e.g. `Ix2`), this should be /// `Some(ndim)`, and for variable-size dimension representations (e.g. diff --git a/src/impl_methods.rs b/src/impl_methods.rs index e7d5e7328..9943e7c8e 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2037,46 +2037,6 @@ where self.zip_mut_with_by_rows(rhs, f); } - /// Traverse two arrays in unspecified order, in lock step, - /// calling the closure `f` on each element pair, and put - /// the result into the corresponding element of self. - pub fn zip_mut_from_pair(&mut self, lhs: &ArrayBase, rhs: &ArrayBase, f: F, ) - where - S: DataMut, - S1: Data, - S2: Data, - F: Fn(&B, &C) -> A, - { - debug_assert_eq!(self.shape(), lhs.shape()); - debug_assert_eq!(self.shape(), rhs.shape()); - - if self.dim.strides_equivalent(&self.strides, &lhs.strides) - && self.dim.strides_equivalent(&self.strides, &rhs.strides) - { - if let Some(self_s) = self.as_slice_memory_order_mut() { - if let Some(lhs_s) = lhs.as_slice_memory_order() { - if let Some(rhs_s) = rhs.as_slice_memory_order() { - for (s, (l, r)) in - self_s.iter_mut().zip(lhs_s.iter().zip(rhs_s)) { - *s = f(&l, &r); - } - return; - } - } - } - } - - // Otherwise, fall back to the outer iter - let n = self.ndim(); - let dim = self.raw_dim(); - Zip::from(LanesMut::new(self.view_mut(), Axis(n - 1))) - .and(Lanes::new(lhs.broadcast_assume(dim.clone()), Axis(n - 1))) - .and(Lanes::new(rhs.broadcast_assume(dim), Axis(n - 1))) - .for_each(move |s_row, l_row, r_row| { - Zip::from(s_row).and(l_row).and(r_row).for_each(|s, a, b| *s = f(a, b)) - }); - } - // zip two arrays where they have different layout or strides #[inline(always)] fn zip_mut_with_by_rows(&mut self, rhs: &ArrayBase, mut f: F) diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 7e28d48cf..065edbad8 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -7,6 +7,8 @@ // except according to those terms. use crate::dimension::BroadcastShape; +use crate::data_traits::MaybeUninitSubst; +use crate::Zip; use num_complex::Complex; /// Elements that can be used as direct operands in arithmetic with arrays. @@ -64,14 +66,15 @@ macro_rules! impl_binary_op( /// **Panics** if broadcasting isn’t possible. impl $trt> for ArrayBase where - A: Copy + $trt, + A: Clone + $trt, B: Clone, - S: DataOwned + DataMut, + S: DataOwned + DataMut + MaybeUninitSubst, + >::Output: DataMut, S2: Data, D: Dimension + BroadcastShape, E: Dimension, { - type Output = ArrayBase>::BroadcastOutput>; + type Output = ArrayBase>::Output>; fn $mth(self, rhs: ArrayBase) -> Self::Output { self.$mth(&rhs) @@ -80,7 +83,7 @@ where /// Perform elementwise #[doc=$doc] -/// between reference `self` and `rhs`, +/// between `self` and reference `rhs`, /// and return the result. /// /// `rhs` must be an `Array` or `ArcArray`. @@ -89,44 +92,49 @@ where /// cloning the data if needed. /// /// **Panics** if broadcasting isn’t possible. -impl<'a, A, B, S, S2, D, E> $trt> for &'a ArrayBase +impl<'a, A, B, S, S2, D, E> $trt<&'a ArrayBase> for ArrayBase where - A: Clone + $trt, - B: Copy, - S: Data, - S2: DataOwned + DataMut, - D: Dimension, - E: Dimension + BroadcastShape, + A: Clone + $trt, + B: Clone, + S: DataOwned + DataMut + MaybeUninitSubst, + >::Output: DataMut, + S2: Data, + D: Dimension + BroadcastShape, + E: Dimension, { - type Output = ArrayBase>::BroadcastOutput>; - fn $mth(self, rhs: ArrayBase) -> Self::Output + type Output = ArrayBase>::Output>; + fn $mth(self, rhs: &ArrayBase) -> Self::Output { - let shape = rhs.dim.broadcast_shape(&self.dim).unwrap(); - if shape.slice() == rhs.dim.slice() { - let mut out = rhs.into_dimensionality::<>::BroadcastOutput>().unwrap(); - out.zip_mut_with(self, |x, y| { - *x = y.clone() $operator x.clone(); + let shape = self.dim.broadcast_shape(&rhs.dim).unwrap(); + if shape.slice() == self.dim.slice() { + let mut out = self.into_dimensionality::<>::Output>().unwrap(); + out.zip_mut_with(rhs, |x, y| { + *x = x.clone() $operator y.clone(); }); out } else { - // SAFETY: Overwrite all the elements in the array after - // it is created via `zip_mut_from_pair`. - let mut out = unsafe { - Self::Output::uninitialized(shape.clone().into_pattern()) - }; let lhs = self.broadcast(shape.clone()).unwrap(); - let rhs = rhs.broadcast(shape).unwrap(); - out.zip_mut_from_pair(&lhs, &rhs, |x, y| { - x.clone() $operator y.clone() - }); - out + let rhs = rhs.broadcast(shape.clone()).unwrap(); + // SAFETY: Overwrite all the elements in the array after + // it is created via `raw_view_mut`. + unsafe { + let mut out =ArrayBase::<>::Output, >::Output>::maybe_uninit(shape.into_pattern()); + let output_view = out.raw_view_mut().cast::(); + Zip::from(&lhs).and(&rhs) + .and(output_view) + .collect_with_partial(|x, y| { + x.clone() $operator y.clone() + }) + .release_ownership(); + out.assume_init() + } } } } /// Perform elementwise #[doc=$doc] -/// between `self` and reference `rhs`, +/// between reference `self` and `rhs`, /// and return the result. /// /// `rhs` must be an `Array` or `ArcArray`. @@ -135,37 +143,43 @@ where /// cloning the data if needed. /// /// **Panics** if broadcasting isn’t possible. -impl<'a, A, B, S, S2, D, E> $trt<&'a ArrayBase> for ArrayBase +impl<'a, A, B, S, S2, D, E> $trt> for &'a ArrayBase where - A: Copy + $trt, + A: Clone + $trt, B: Clone, - S: DataOwned + DataMut, - S2: Data, - D: Dimension + BroadcastShape, - E: Dimension, + S: Data, + S2: DataOwned + DataMut + MaybeUninitSubst, + >::Output: DataMut, + D: Dimension, + E: Dimension + BroadcastShape, { - type Output = ArrayBase>::BroadcastOutput>; - fn $mth(self, rhs: &ArrayBase) -> Self::Output + type Output = ArrayBase>::Output>; + fn $mth(self, rhs: ArrayBase) -> Self::Output + where { - let shape = self.dim.broadcast_shape(&rhs.dim).unwrap(); - if shape.slice() == self.dim.slice() { - let mut out = self.into_dimensionality::<>::BroadcastOutput>().unwrap(); - out.zip_mut_with(rhs, |x, y| { - *x = x.clone() $operator y.clone(); + let shape = rhs.dim.broadcast_shape(&self.dim).unwrap(); + if shape.slice() == rhs.dim.slice() { + let mut out = rhs.into_dimensionality::<>::Output>().unwrap(); + out.zip_mut_with(self, |x, y| { + *x = y.clone() $operator x.clone(); }); out } else { - // SAFETY: Overwrite all the elements in the array after - // it is created via `zip_mut_from_pair`. - let mut out = unsafe { - Self::Output::uninitialized(shape.clone().into_pattern()) - }; let lhs = self.broadcast(shape.clone()).unwrap(); - let rhs = rhs.broadcast(shape).unwrap(); - out.zip_mut_from_pair(&lhs, &rhs, |x, y| { - x.clone() $operator y.clone() - }); - out + let rhs = rhs.broadcast(shape.clone()).unwrap(); + // SAFETY: Overwrite all the elements in the array after + // it is created via `raw_view_mut`. + unsafe { + let mut out =ArrayBase::<>::Output, >::Output>::maybe_uninit(shape.into_pattern()); + let output_view = out.raw_view_mut().cast::(); + Zip::from(&lhs).and(&rhs) + .and(output_view) + .collect_with_partial(|x, y| { + x.clone() $operator y.clone() + }) + .release_ownership(); + out.assume_init() + } } } } @@ -188,19 +202,12 @@ where D: Dimension + BroadcastShape, E: Dimension, { - type Output = Array>::BroadcastOutput>; + type Output = Array>::Output>; fn $mth(self, rhs: &'a ArrayBase) -> Self::Output { let shape = self.dim.broadcast_shape(&rhs.dim).unwrap(); - // SAFETY: Overwrite all the elements in the array after - // it is created via `zip_mut_from_pair`. - let mut out = unsafe { - Self::Output::uninitialized(shape.clone().into_pattern()) - }; let lhs = self.broadcast(shape.clone()).unwrap(); let rhs = rhs.broadcast(shape).unwrap(); - out.zip_mut_from_pair(&lhs, &rhs, |x, y| { - x.clone() $operator y.clone() - }); + let out = Zip::from(&lhs).and(&rhs).map_collect(|x, y| x.clone() $operator y.clone()); out } } diff --git a/src/lib.rs b/src/lib.rs index 1a760bf31..844226ed9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -179,7 +179,7 @@ pub use crate::aliases::*; pub use crate::data_traits::{ Data, DataMut, DataOwned, DataShared, RawData, RawDataClone, RawDataMut, - RawDataSubst, + RawDataSubst, MaybeUninitSubst, }; mod free_functions; diff --git a/tests/array.rs b/tests/array.rs index 370600bd3..6f07baff8 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1562,7 +1562,7 @@ fn test_broadcast_shape() { fn test_co( d1: &D1, d2: &D2, - r: Result<>::BroadcastOutput, ShapeError>, + r: Result<>::Output, ShapeError>, ) where D1: Dimension + BroadcastShape, D2: Dimension, From fc969bdadc830f3e7c4f624a3245b4051e5c9e7d Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Wed, 27 Jan 2021 11:16:14 +0800 Subject: [PATCH 197/651] treat zero dimension like numpy does --- src/dimension/broadcast.rs | 8 +++----- src/numeric/impl_numeric.rs | 4 ++-- tests/array.rs | 8 ++++---- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/dimension/broadcast.rs b/src/dimension/broadcast.rs index d13e2ac3a..7c158f5b2 100644 --- a/src/dimension/broadcast.rs +++ b/src/dimension/broadcast.rs @@ -23,15 +23,13 @@ where // (https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#general-broadcasting-rules). // // Zero dimension element is not in the original rules of broadcasting. - // We currently treat it as the same as 1. Especially, when one side is - // zero with one side is empty, or both sides are zero, the result will - // remain zero. + // We currently treat it like any other number greater than 1. As numpy does. for i in 0..shape1.ndim() { out_slice[i] = s1[i]; } for i in 0..shape2.ndim() { - if out_slice[i + k] != s2[i] && s2[i] != 0 { - if out_slice[i + k] <= 1 { + if out_slice[i + k] != s2[i] { + if out_slice[i + k] == 1 { out_slice[i + k] = s2[i] } else if s2[i] != 1 { return Err(from_kind(ErrorKind::IncompatibleShape)); diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 0c5b4820d..fbec80418 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -243,7 +243,7 @@ where /// **Panics** if `axis` is out of bounds. pub fn sum_axis(&self, axis: Axis) -> Array where - A: Copy + Zero + Add, + A: Clone + Zero + Add, D: RemoveAxis, { let n = self.len_of(axis); @@ -285,7 +285,7 @@ where /// ``` pub fn mean_axis(&self, axis: Axis) -> Option> where - A: Copy + Zero + FromPrimitive + Add + Div, + A: Clone + Zero + FromPrimitive + Add + Div, D: RemoveAxis, { let axis_length = self.len_of(axis); diff --git a/tests/array.rs b/tests/array.rs index 6f07baff8..a380423d7 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1588,19 +1588,19 @@ fn test_broadcast_shape() { test_co( &Dim([2, 1, 2]).into_dyn(), &Dim(0), - Ok(Dim([2, 1, 2]).into_dyn()), + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), ); test_co( &Dim([2, 1, 1]), - &Dim([0, 0, 0, 3, 4]), + &Dim([0, 0, 1, 3, 4]), Ok(Dim([0, 0, 2, 3, 4])), ); test_co(&Dim([0]), &Dim([0, 0, 0]), Ok(Dim([0, 0, 0]))); - test_co(&Dim(1), &Dim([1, 0, 0]), Ok(Dim([1, 0, 1]))); + test_co(&Dim(1), &Dim([1, 0, 0]), Ok(Dim([1, 0, 0]))); test_co( &Dim([1, 3, 0, 1, 1]), &Dim([1, 2, 3, 1]), - Ok(Dim([1, 3, 2, 3, 1])), + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), ); } From a19149b73b93d227f5eaec14fe7e4da398869c2f Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Mon, 1 Feb 2021 12:20:05 +0800 Subject: [PATCH 198/651] rebase and use map_collect_owned in impl_ops.rs --- src/data_traits.rs | 21 ++----------- src/impl_methods.rs | 2 +- src/impl_ops.rs | 76 +++++++++++++++++---------------------------- src/lib.rs | 2 +- 4 files changed, 33 insertions(+), 68 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index eabe1795b..7ac63d54e 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -10,7 +10,8 @@ use rawpointer::PointerExt; -use std::mem::{self, size_of};use std::mem::MaybeUninit; +use std::mem::{self, size_of}; +use std::mem::MaybeUninit; use std::ptr::NonNull; use alloc::sync::Arc; use alloc::vec::Vec; @@ -620,21 +621,3 @@ impl<'a, A: 'a, B: 'a> RawDataSubst for ViewRepr<&'a mut A> { } } -/// Array representation trait. -/// -/// The MaybeUninitSubst trait maps the MaybeUninit type of element, while -/// mapping the MaybeUninit type back to origin element type. -/// -/// For example, `MaybeUninitSubst` can map the type `OwnedRepr` to `OwnedRepr>`, -/// and use `Output as RawDataSubst` to map `OwnedRepr>` back to `OwnedRepr`. -pub trait MaybeUninitSubst: DataOwned { - type Output: DataOwned> + RawDataSubst>; -} - -impl MaybeUninitSubst for OwnedRepr { - type Output = OwnedRepr>; -} - -impl MaybeUninitSubst for OwnedArcRepr { - type Output = OwnedArcRepr>; -} diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 9943e7c8e..9a1e0fac8 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2013,7 +2013,7 @@ where self.map_inplace(move |elt| *elt = x.clone()); } - fn zip_mut_with_same_shape(&mut self, rhs: &ArrayBase, mut f: F) + pub(crate) fn zip_mut_with_same_shape(&mut self, rhs: &ArrayBase, mut f: F) where S: DataMut, S2: Data, diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 065edbad8..6e45ea629 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -7,7 +7,6 @@ // except according to those terms. use crate::dimension::BroadcastShape; -use crate::data_traits::MaybeUninitSubst; use crate::Zip; use num_complex::Complex; @@ -68,8 +67,8 @@ impl $trt> for ArrayBase where A: Clone + $trt, B: Clone, - S: DataOwned + DataMut + MaybeUninitSubst, - >::Output: DataMut, + S: DataOwned + DataMut, + S::MaybeUninit: DataMut, S2: Data, D: Dimension + BroadcastShape, E: Dimension, @@ -96,8 +95,8 @@ impl<'a, A, B, S, S2, D, E> $trt<&'a ArrayBase> for ArrayBase where A: Clone + $trt, B: Clone, - S: DataOwned + DataMut + MaybeUninitSubst, - >::Output: DataMut, + S: DataOwned + DataMut, + S::MaybeUninit: DataMut, S2: Data, D: Dimension + BroadcastShape, E: Dimension, @@ -105,29 +104,15 @@ where type Output = ArrayBase>::Output>; fn $mth(self, rhs: &ArrayBase) -> Self::Output { - let shape = self.dim.broadcast_shape(&rhs.dim).unwrap(); - if shape.slice() == self.dim.slice() { + if self.ndim() == rhs.ndim() && self.shape() == rhs.shape() { let mut out = self.into_dimensionality::<>::Output>().unwrap(); - out.zip_mut_with(rhs, |x, y| { - *x = x.clone() $operator y.clone(); - }); + out.zip_mut_with_same_shape(rhs, clone_iopf(A::$mth)); out } else { + let shape = self.dim.broadcast_shape(&rhs.dim).unwrap(); let lhs = self.broadcast(shape.clone()).unwrap(); - let rhs = rhs.broadcast(shape.clone()).unwrap(); - // SAFETY: Overwrite all the elements in the array after - // it is created via `raw_view_mut`. - unsafe { - let mut out =ArrayBase::<>::Output, >::Output>::maybe_uninit(shape.into_pattern()); - let output_view = out.raw_view_mut().cast::(); - Zip::from(&lhs).and(&rhs) - .and(output_view) - .collect_with_partial(|x, y| { - x.clone() $operator y.clone() - }) - .release_ownership(); - out.assume_init() - } + let rhs = rhs.broadcast(shape).unwrap(); + Zip::from(&lhs).and(&rhs).map_collect_owned(clone_opf(A::$mth)) } } } @@ -148,8 +133,8 @@ where A: Clone + $trt, B: Clone, S: Data, - S2: DataOwned + DataMut + MaybeUninitSubst, - >::Output: DataMut, + S2: DataOwned + DataMut, + S2::MaybeUninit: DataMut, D: Dimension, E: Dimension + BroadcastShape, { @@ -157,29 +142,15 @@ where fn $mth(self, rhs: ArrayBase) -> Self::Output where { - let shape = rhs.dim.broadcast_shape(&self.dim).unwrap(); - if shape.slice() == rhs.dim.slice() { + if self.ndim() == rhs.ndim() && self.shape() == rhs.shape() { let mut out = rhs.into_dimensionality::<>::Output>().unwrap(); - out.zip_mut_with(self, |x, y| { - *x = y.clone() $operator x.clone(); - }); + out.zip_mut_with_same_shape(self, clone_iopf_rev(A::$mth)); out } else { + let shape = rhs.dim.broadcast_shape(&self.dim).unwrap(); let lhs = self.broadcast(shape.clone()).unwrap(); - let rhs = rhs.broadcast(shape.clone()).unwrap(); - // SAFETY: Overwrite all the elements in the array after - // it is created via `raw_view_mut`. - unsafe { - let mut out =ArrayBase::<>::Output, >::Output>::maybe_uninit(shape.into_pattern()); - let output_view = out.raw_view_mut().cast::(); - Zip::from(&lhs).and(&rhs) - .and(output_view) - .collect_with_partial(|x, y| { - x.clone() $operator y.clone() - }) - .release_ownership(); - out.assume_init() - } + let rhs = rhs.broadcast(shape).unwrap(); + Zip::from(&lhs).and(&rhs).map_collect_owned(clone_opf(A::$mth)) } } } @@ -207,8 +178,7 @@ where let shape = self.dim.broadcast_shape(&rhs.dim).unwrap(); let lhs = self.broadcast(shape.clone()).unwrap(); let rhs = rhs.broadcast(shape).unwrap(); - let out = Zip::from(&lhs).and(&rhs).map_collect(|x, y| x.clone() $operator y.clone()); - out + Zip::from(&lhs).and(&rhs).map_collect(clone_opf(A::$mth)) } } @@ -313,6 +283,18 @@ mod arithmetic_ops { use num_complex::Complex; use std::ops::*; + fn clone_opf(f: impl Fn(A, B) -> C) -> impl FnMut(&A, &B) -> C { + move |x, y| f(x.clone(), y.clone()) + } + + fn clone_iopf(f: impl Fn(A, B) -> A) -> impl FnMut(&mut A, &B) { + move |x, y| *x = f(x.clone(), y.clone()) + } + + fn clone_iopf_rev(f: impl Fn(A, B) -> B) -> impl FnMut(&mut B, &A) { + move |x, y| *x = f(y.clone(), x.clone()) + } + impl_binary_op!(Add, +, add, +=, "addition"); impl_binary_op!(Sub, -, sub, -=, "subtraction"); impl_binary_op!(Mul, *, mul, *=, "multiplication"); diff --git a/src/lib.rs b/src/lib.rs index 844226ed9..1a760bf31 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -179,7 +179,7 @@ pub use crate::aliases::*; pub use crate::data_traits::{ Data, DataMut, DataOwned, DataShared, RawData, RawDataClone, RawDataMut, - RawDataSubst, MaybeUninitSubst, + RawDataSubst, }; mod free_functions; From b1f4f9526e264609e54b8d3e2ab2053cdef8915c Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Wed, 3 Feb 2021 21:49:22 +0800 Subject: [PATCH 199/651] use izip in index loop in broadcast.rs --- src/dimension/broadcast.rs | 17 +++++++---------- src/impl_ops.rs | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/dimension/broadcast.rs b/src/dimension/broadcast.rs index 7c158f5b2..3f4b8bdbe 100644 --- a/src/dimension/broadcast.rs +++ b/src/dimension/broadcast.rs @@ -16,22 +16,19 @@ where } // The output should be the same length as shape1. let mut out = Output::zeros(shape1.ndim()); - let out_slice = out.slice_mut(); - let s1 = shape1.slice(); - let s2 = shape2.slice(); // Uses the [NumPy broadcasting rules] // (https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#general-broadcasting-rules). // // Zero dimension element is not in the original rules of broadcasting. // We currently treat it like any other number greater than 1. As numpy does. - for i in 0..shape1.ndim() { - out_slice[i] = s1[i]; + for (out, s) in izip!(out.slice_mut(), shape1.slice()) { + *out = *s; } - for i in 0..shape2.ndim() { - if out_slice[i + k] != s2[i] { - if out_slice[i + k] == 1 { - out_slice[i + k] = s2[i] - } else if s2[i] != 1 { + for (out, s2) in izip!(&mut out.slice_mut()[k..], shape2.slice()) { + if *out != *s2 { + if *out == 1 { + *out = *s2 + } else if *s2 != 1 { return Err(from_kind(ErrorKind::IncompatibleShape)); } } diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 6e45ea629..585b3ed4c 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -166,7 +166,7 @@ where /// **Panics** if broadcasting isn’t possible. impl<'a, A, B, S, S2, D, E> $trt<&'a ArrayBase> for &'a ArrayBase where - A: Copy + $trt, + A: Clone + $trt, B: Clone, S: Data, S2: Data, From c9eb88e5a22c91be0ca2b90a4b50e5bcba1a7688 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Fri, 5 Feb 2021 20:40:33 +0800 Subject: [PATCH 200/651] Update documentation and function names --- src/dimension/broadcast.rs | 35 ++++++++++++-------------- src/impl_ops.rs | 3 --- tests/array.rs | 49 +------------------------------------ tests/dimension.rs | 50 +++++++++++++++++++++++++++++++++++++- 4 files changed, 65 insertions(+), 72 deletions(-) diff --git a/src/dimension/broadcast.rs b/src/dimension/broadcast.rs index 3f4b8bdbe..74ac9e634 100644 --- a/src/dimension/broadcast.rs +++ b/src/dimension/broadcast.rs @@ -1,26 +1,24 @@ use crate::error::*; use crate::{Dimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn}; -/// Calculate the co_broadcast shape of two dimensions. Return error if shapes are -/// not compatible. -fn broadcast_shape(shape1: &D1, shape2: &D2) -> Result -where - D1: Dimension, - D2: Dimension, - Output: Dimension, +/// Calculate the common shape for a pair of array shapes, which can be broadcasted +/// to each other. Return an error if shapes are not compatible. +/// +/// Uses the [NumPy broadcasting rules] +// (https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#general-broadcasting-rules). +fn co_broadcasting(shape1: &D1, shape2: &D2) -> Result + where + D1: Dimension, + D2: Dimension, + Output: Dimension, { let (k, overflow) = shape1.ndim().overflowing_sub(shape2.ndim()); // Swap the order if d2 is longer. if overflow { - return broadcast_shape::(shape2, shape1); + return co_broadcasting::(shape2, shape1); } // The output should be the same length as shape1. let mut out = Output::zeros(shape1.ndim()); - // Uses the [NumPy broadcasting rules] - // (https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#general-broadcasting-rules). - // - // Zero dimension element is not in the original rules of broadcasting. - // We currently treat it like any other number greater than 1. As numpy does. for (out, s) in izip!(out.slice_mut(), shape1.slice()) { *out = *s; } @@ -42,10 +40,7 @@ pub trait BroadcastShape { /// Determines the shape after broadcasting the dimensions together. /// - /// If the dimensions are not compatible, returns `Err`. - /// - /// Uses the [NumPy broadcasting rules] - /// (https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#general-broadcasting-rules). + /// If the shapes are not compatible, returns `Err`. fn broadcast_shape(&self, other: &Other) -> Result; } @@ -56,7 +51,7 @@ impl BroadcastShape for D { type Output = D; fn broadcast_shape(&self, other: &D) -> Result { - broadcast_shape::(self, other) + co_broadcasting::(self, other) } } @@ -66,7 +61,7 @@ macro_rules! impl_broadcast_distinct_fixed { type Output = $larger; fn broadcast_shape(&self, other: &$larger) -> Result { - broadcast_shape::(self, other) + co_broadcasting::(self, other) } } @@ -74,7 +69,7 @@ macro_rules! impl_broadcast_distinct_fixed { type Output = $larger; fn broadcast_shape(&self, other: &$smaller) -> Result { - broadcast_shape::(self, other) + co_broadcasting::(self, other) } } }; diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 585b3ed4c..5bd518a00 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -68,7 +68,6 @@ where A: Clone + $trt, B: Clone, S: DataOwned + DataMut, - S::MaybeUninit: DataMut, S2: Data, D: Dimension + BroadcastShape, E: Dimension, @@ -96,7 +95,6 @@ where A: Clone + $trt, B: Clone, S: DataOwned + DataMut, - S::MaybeUninit: DataMut, S2: Data, D: Dimension + BroadcastShape, E: Dimension, @@ -134,7 +132,6 @@ where B: Clone, S: Data, S2: DataOwned + DataMut, - S2::MaybeUninit: DataMut, D: Dimension, E: Dimension + BroadcastShape, { diff --git a/tests/array.rs b/tests/array.rs index a380423d7..b0a28ca41 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -11,7 +11,7 @@ use defmac::defmac; use itertools::{enumerate, zip, Itertools}; use ndarray::prelude::*; use ndarray::{arr3, rcarr2}; -use ndarray::{indices, BroadcastShape, ErrorKind, IxDynImpl, ShapeError}; +use ndarray::indices; use ndarray::{Slice, SliceInfo, SliceOrIndex}; macro_rules! assert_panics { @@ -1557,53 +1557,6 @@ fn insert_axis_view() { ); } -#[test] -fn test_broadcast_shape() { - fn test_co( - d1: &D1, - d2: &D2, - r: Result<>::Output, ShapeError>, - ) where - D1: Dimension + BroadcastShape, - D2: Dimension, - { - let d = d1.broadcast_shape(d2); - assert_eq!(d, r); - } - test_co(&Dim([2, 3]), &Dim([4, 1, 3]), Ok(Dim([4, 2, 3]))); - test_co( - &Dim([1, 2, 2]), - &Dim([1, 3, 4]), - Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), - ); - test_co(&Dim([3, 4, 5]), &Ix0(), Ok(Dim([3, 4, 5]))); - let v = vec![1, 2, 3, 4, 5, 6, 7]; - test_co( - &Dim(vec![1, 1, 3, 1, 5, 1, 7]), - &Dim([2, 1, 4, 1, 6, 1]), - Ok(Dim(IxDynImpl::from(v.as_slice()))), - ); - let d = Dim([1, 2, 1, 3]); - test_co(&d, &d, Ok(d)); - test_co( - &Dim([2, 1, 2]).into_dyn(), - &Dim(0), - Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), - ); - test_co( - &Dim([2, 1, 1]), - &Dim([0, 0, 1, 3, 4]), - Ok(Dim([0, 0, 2, 3, 4])), - ); - test_co(&Dim([0]), &Dim([0, 0, 0]), Ok(Dim([0, 0, 0]))); - test_co(&Dim(1), &Dim([1, 0, 0]), Ok(Dim([1, 0, 0]))); - test_co( - &Dim([1, 3, 0, 1, 1]), - &Dim([1, 2, 3, 1]), - Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), - ); -} - #[test] fn arithmetic_broadcast() { let mut a = arr2(&[[1., 2.], [3., 4.]]); diff --git a/tests/dimension.rs b/tests/dimension.rs index 939b4f0e3..ede0dc7d2 100644 --- a/tests/dimension.rs +++ b/tests/dimension.rs @@ -2,7 +2,8 @@ use defmac::defmac; -use ndarray::{arr2, ArcArray, Array, Axis, Dim, Dimension, IxDyn, RemoveAxis}; +use ndarray::{arr2, ArcArray, Array, Axis, Dim, Dimension, Ix0, IxDyn, IxDynImpl, RemoveAxis, + ErrorKind, ShapeError, BroadcastShape}; use std::hash::{Hash, Hasher}; @@ -340,3 +341,50 @@ fn test_all_ndindex() { ndindex!(10, 4, 3, 2, 2); ndindex!(10, 4, 3, 2, 2, 2); } + +#[test] +fn test_broadcast_shape() { + fn test_co( + d1: &D1, + d2: &D2, + r: Result<>::Output, ShapeError>, + ) where + D1: Dimension + BroadcastShape, + D2: Dimension, + { + let d = d1.broadcast_shape(d2); + assert_eq!(d, r); + } + test_co(&Dim([2, 3]), &Dim([4, 1, 3]), Ok(Dim([4, 2, 3]))); + test_co( + &Dim([1, 2, 2]), + &Dim([1, 3, 4]), + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), + ); + test_co(&Dim([3, 4, 5]), &Ix0(), Ok(Dim([3, 4, 5]))); + let v = vec![1, 2, 3, 4, 5, 6, 7]; + test_co( + &Dim(vec![1, 1, 3, 1, 5, 1, 7]), + &Dim([2, 1, 4, 1, 6, 1]), + Ok(Dim(IxDynImpl::from(v.as_slice()))), + ); + let d = Dim([1, 2, 1, 3]); + test_co(&d, &d, Ok(d)); + test_co( + &Dim([2, 1, 2]).into_dyn(), + &Dim(0), + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), + ); + test_co( + &Dim([2, 1, 1]), + &Dim([0, 0, 1, 3, 4]), + Ok(Dim([0, 0, 2, 3, 4])), + ); + test_co(&Dim([0]), &Dim([0, 0, 0]), Ok(Dim([0, 0, 0]))); + test_co(&Dim(1), &Dim([1, 0, 0]), Ok(Dim([1, 0, 0]))); + test_co( + &Dim([1, 3, 0, 1, 1]), + &Dim([1, 2, 3, 1]), + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), + ); +} From e3b73cc50330743d735872e3ecfb0a880dcbe252 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Thu, 18 Feb 2021 19:40:43 +0800 Subject: [PATCH 201/651] Update documentation and function names --- src/dimension/broadcast.rs | 16 ++++++++-------- src/impl_ops.rs | 3 +-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/dimension/broadcast.rs b/src/dimension/broadcast.rs index 74ac9e634..377c8509e 100644 --- a/src/dimension/broadcast.rs +++ b/src/dimension/broadcast.rs @@ -1,12 +1,12 @@ use crate::error::*; use crate::{Dimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn}; -/// Calculate the common shape for a pair of array shapes, which can be broadcasted -/// to each other. Return an error if shapes are not compatible. +/// Calculate the common shape for a pair of array shapes, that they can be broadcasted +/// to. Return an error if the shapes are not compatible. /// /// Uses the [NumPy broadcasting rules] // (https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#general-broadcasting-rules). -fn co_broadcasting(shape1: &D1, shape2: &D2) -> Result +fn co_broadcast(shape1: &D1, shape2: &D2) -> Result where D1: Dimension, D2: Dimension, @@ -15,7 +15,7 @@ fn co_broadcasting(shape1: &D1, shape2: &D2) -> Result(shape2, shape1); + return co_broadcast::(shape2, shape1); } // The output should be the same length as shape1. let mut out = Output::zeros(shape1.ndim()); @@ -38,7 +38,7 @@ pub trait BroadcastShape { /// The resulting dimension type after broadcasting. type Output: Dimension; - /// Determines the shape after broadcasting the dimensions together. + /// Determines the shape after broadcasting the shapes together. /// /// If the shapes are not compatible, returns `Err`. fn broadcast_shape(&self, other: &Other) -> Result; @@ -51,7 +51,7 @@ impl BroadcastShape for D { type Output = D; fn broadcast_shape(&self, other: &D) -> Result { - co_broadcasting::(self, other) + co_broadcast::(self, other) } } @@ -61,7 +61,7 @@ macro_rules! impl_broadcast_distinct_fixed { type Output = $larger; fn broadcast_shape(&self, other: &$larger) -> Result { - co_broadcasting::(self, other) + co_broadcast::(self, other) } } @@ -69,7 +69,7 @@ macro_rules! impl_broadcast_distinct_fixed { type Output = $larger; fn broadcast_shape(&self, other: &$smaller) -> Result { - co_broadcasting::(self, other) + co_broadcast::(self, other) } } }; diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 5bd518a00..69c6f698e 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -59,8 +59,7 @@ macro_rules! impl_binary_op( /// /// `self` must be an `Array` or `ArcArray`. /// -/// If their shapes disagree, `self` is broadcast to their broadcast shape, -/// cloning the data if needed. +/// If their shapes disagree, `self` is broadcast to their broadcast shape. /// /// **Panics** if broadcasting isn’t possible. impl $trt> for ArrayBase From b6232398675b00af31ae8870ce045785fc332cc2 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Thu, 18 Feb 2021 21:03:57 +0800 Subject: [PATCH 202/651] Add function broadcast_with --- src/impl_methods.rs | 34 ++++++++++++++++++++++++++++++++-- src/impl_ops.rs | 12 +++--------- tests/broadcast.rs | 31 +++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 9a1e0fac8..4202ec257 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -14,14 +14,14 @@ use rawpointer::PointerExt; use crate::imp_prelude::*; -use crate::arraytraits; +use crate::{arraytraits, BroadcastShape}; use crate::dimension; use crate::dimension::IntoDimension; use crate::dimension::{ abs_index, axes_of, do_slice, merge_axes, move_min_stride_axis_to_last, offset_from_ptr_to_memory, size_of_shape_checked, stride_offset, Axes, }; -use crate::error::{self, ErrorKind, ShapeError}; +use crate::error::{self, ErrorKind, ShapeError, from_kind}; use crate::math_cell::MathCell; use crate::itertools::zip; use crate::zip::Zip; @@ -1766,6 +1766,36 @@ where unsafe { Some(ArrayView::new(self.ptr, dim, broadcast_strides)) } } + /// Calculate the views of two ArrayBases after broadcasting each other, if possible. + /// + /// Return `ShapeError` if their shapes can not be broadcast together. + /// + /// ``` + /// use ndarray::{arr1, arr2}; + /// + /// let a = arr2(&[[2], [3], [4]]); + /// let b = arr1(&[5, 6, 7]); + /// let (a1, b1) = a.broadcast_with(&b).unwrap(); + /// assert_eq!(a1, arr2(&[[2, 2, 2], [3, 3, 3], [4, 4, 4]])); + /// assert_eq!(b1, arr2(&[[5, 6, 7], [5, 6, 7], [5, 6, 7]])); + /// ``` + pub fn broadcast_with<'a, 'b, B, S2, E>(&'a self, other: &'b ArrayBase) -> + Result<(ArrayView<'a, A, >::Output>, ArrayView<'b, B, >::Output>), ShapeError> + where + S: Data, + S2: Data, + D: Dimension + BroadcastShape, + E: Dimension, + { + let shape = self.dim.broadcast_shape(&other.dim)?; + if let Some(view1) = self.broadcast(shape.clone()) { + if let Some(view2) = other.broadcast(shape) { + return Ok((view1, view2)) + } + } + return Err(from_kind(ErrorKind::IncompatibleShape)); + } + /// Swap axes `ax` and `bx`. /// /// This does not move any data, it just adjusts the array’s dimensions diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 69c6f698e..80aa8e11a 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -106,9 +106,7 @@ where out.zip_mut_with_same_shape(rhs, clone_iopf(A::$mth)); out } else { - let shape = self.dim.broadcast_shape(&rhs.dim).unwrap(); - let lhs = self.broadcast(shape.clone()).unwrap(); - let rhs = rhs.broadcast(shape).unwrap(); + let (lhs, rhs) = self.broadcast_with(rhs).unwrap(); Zip::from(&lhs).and(&rhs).map_collect_owned(clone_opf(A::$mth)) } } @@ -143,9 +141,7 @@ where out.zip_mut_with_same_shape(self, clone_iopf_rev(A::$mth)); out } else { - let shape = rhs.dim.broadcast_shape(&self.dim).unwrap(); - let lhs = self.broadcast(shape.clone()).unwrap(); - let rhs = rhs.broadcast(shape).unwrap(); + let (rhs, lhs) = rhs.broadcast_with(self).unwrap(); Zip::from(&lhs).and(&rhs).map_collect_owned(clone_opf(A::$mth)) } } @@ -171,9 +167,7 @@ where { type Output = Array>::Output>; fn $mth(self, rhs: &'a ArrayBase) -> Self::Output { - let shape = self.dim.broadcast_shape(&rhs.dim).unwrap(); - let lhs = self.broadcast(shape.clone()).unwrap(); - let rhs = rhs.broadcast(shape).unwrap(); + let (lhs, rhs) = self.broadcast_with(rhs).unwrap(); Zip::from(&lhs).and(&rhs).map_collect(clone_opf(A::$mth)) } } diff --git a/tests/broadcast.rs b/tests/broadcast.rs index 5416e9017..26111c780 100644 --- a/tests/broadcast.rs +++ b/tests/broadcast.rs @@ -1,4 +1,5 @@ use ndarray::prelude::*; +use ndarray::{ShapeError, ErrorKind, arr3}; #[test] #[cfg(feature = "std")] @@ -81,3 +82,33 @@ fn test_broadcast_1d() { println!("b2=\n{:?}", b2); assert_eq!(b0, b2); } + +#[test] +fn test_broadcast_with() { + let a = arr2(&[[1., 2.], [3., 4.]]); + let b = aview0(&1.); + let (a1, b1) = a.broadcast_with(&b).unwrap(); + assert_eq!(a1, arr2(&[[1.0, 2.0], [3.0, 4.0]])); + assert_eq!(b1, arr2(&[[1.0, 1.0], [1.0, 1.0]])); + + let a = arr2(&[[2], [3], [4]]); + let b = arr1(&[5, 6, 7]); + let (a1, b1) = a.broadcast_with(&b).unwrap(); + assert_eq!(a1, arr2(&[[2, 2, 2], [3, 3, 3], [4, 4, 4]])); + assert_eq!(b1, arr2(&[[5, 6, 7], [5, 6, 7], [5, 6, 7]])); + + // Negative strides and non-contiguous memory + let s = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + let s = Array3::from_shape_vec((2, 3, 2).strides((1, 4, 2)), s.to_vec()).unwrap(); + let a = s.slice(s![..;-1,..;2,..]); + let b = s.slice(s![..2, -1, ..]); + let (a1, b1) = a.broadcast_with(&b).unwrap(); + assert_eq!(a1, arr3(&[[[2, 4], [10, 12]], [[1, 3], [9, 11]]])); + assert_eq!(b1, arr3(&[[[9, 11], [10, 12]], [[9, 11], [10, 12]]])); + + // ShapeError + let a = arr2(&[[2, 2], [3, 3], [4, 4]]); + let b = arr1(&[5, 6, 7]); + let e = a.broadcast_with(&b); + assert_eq!(e, Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); +} From c24e602be41aaabe1e3c457ed0ce23d9754cbca5 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Fri, 19 Feb 2021 09:08:16 +0800 Subject: [PATCH 203/651] Modify the docs and visibility of broadcast_with --- src/impl_methods.rs | 15 +++------------ tests/broadcast.rs | 30 ------------------------------ 2 files changed, 3 insertions(+), 42 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 4202ec257..c9b2d6f49 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1766,20 +1766,11 @@ where unsafe { Some(ArrayView::new(self.ptr, dim, broadcast_strides)) } } - /// Calculate the views of two ArrayBases after broadcasting each other, if possible. + /// For two arrays or views, find their common shape if possible and + /// broadcast them as array views into that shape. /// /// Return `ShapeError` if their shapes can not be broadcast together. - /// - /// ``` - /// use ndarray::{arr1, arr2}; - /// - /// let a = arr2(&[[2], [3], [4]]); - /// let b = arr1(&[5, 6, 7]); - /// let (a1, b1) = a.broadcast_with(&b).unwrap(); - /// assert_eq!(a1, arr2(&[[2, 2, 2], [3, 3, 3], [4, 4, 4]])); - /// assert_eq!(b1, arr2(&[[5, 6, 7], [5, 6, 7], [5, 6, 7]])); - /// ``` - pub fn broadcast_with<'a, 'b, B, S2, E>(&'a self, other: &'b ArrayBase) -> + pub(crate) fn broadcast_with<'a, 'b, B, S2, E>(&'a self, other: &'b ArrayBase) -> Result<(ArrayView<'a, A, >::Output>, ArrayView<'b, B, >::Output>), ShapeError> where S: Data, diff --git a/tests/broadcast.rs b/tests/broadcast.rs index 26111c780..e3d377139 100644 --- a/tests/broadcast.rs +++ b/tests/broadcast.rs @@ -82,33 +82,3 @@ fn test_broadcast_1d() { println!("b2=\n{:?}", b2); assert_eq!(b0, b2); } - -#[test] -fn test_broadcast_with() { - let a = arr2(&[[1., 2.], [3., 4.]]); - let b = aview0(&1.); - let (a1, b1) = a.broadcast_with(&b).unwrap(); - assert_eq!(a1, arr2(&[[1.0, 2.0], [3.0, 4.0]])); - assert_eq!(b1, arr2(&[[1.0, 1.0], [1.0, 1.0]])); - - let a = arr2(&[[2], [3], [4]]); - let b = arr1(&[5, 6, 7]); - let (a1, b1) = a.broadcast_with(&b).unwrap(); - assert_eq!(a1, arr2(&[[2, 2, 2], [3, 3, 3], [4, 4, 4]])); - assert_eq!(b1, arr2(&[[5, 6, 7], [5, 6, 7], [5, 6, 7]])); - - // Negative strides and non-contiguous memory - let s = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; - let s = Array3::from_shape_vec((2, 3, 2).strides((1, 4, 2)), s.to_vec()).unwrap(); - let a = s.slice(s![..;-1,..;2,..]); - let b = s.slice(s![..2, -1, ..]); - let (a1, b1) = a.broadcast_with(&b).unwrap(); - assert_eq!(a1, arr3(&[[[2, 4], [10, 12]], [[1, 3], [9, 11]]])); - assert_eq!(b1, arr3(&[[[9, 11], [10, 12]], [[9, 11], [10, 12]]])); - - // ShapeError - let a = arr2(&[[2, 2], [3, 3], [4, 4]]); - let b = arr1(&[5, 6, 7]); - let e = a.broadcast_with(&b); - assert_eq!(e, Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); -} From 16f382b1eeacf1ea7072bd5ea410bc8f44e6ae2e Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 12 Mar 2021 10:37:31 +0100 Subject: [PATCH 204/651] TEST: Remove unused imports in tests/broadcast.rs --- tests/broadcast.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/broadcast.rs b/tests/broadcast.rs index e3d377139..5416e9017 100644 --- a/tests/broadcast.rs +++ b/tests/broadcast.rs @@ -1,5 +1,4 @@ use ndarray::prelude::*; -use ndarray::{ShapeError, ErrorKind, arr3}; #[test] #[cfg(feature = "std")] From b39593ebc2c2a22c66f58e82fd84f2857c458508 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 12 Mar 2021 10:37:49 +0100 Subject: [PATCH 205/651] FIX: Rename BroadcastShape to DimMax For consistency with other dimension traits (to come); with >, Output is the maximum of A and B. --- src/dimension/broadcast.rs | 10 +++++----- src/dimension/dimension_trait.rs | 12 ++++++------ src/dimension/mod.rs | 2 +- src/impl_methods.rs | 6 +++--- src/impl_ops.rs | 22 +++++++++++----------- src/lib.rs | 2 +- tests/dimension.rs | 6 +++--- 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/dimension/broadcast.rs b/src/dimension/broadcast.rs index 377c8509e..6da4375d0 100644 --- a/src/dimension/broadcast.rs +++ b/src/dimension/broadcast.rs @@ -34,7 +34,7 @@ fn co_broadcast(shape1: &D1, shape2: &D2) -> Result { +pub trait DimMax { /// The resulting dimension type after broadcasting. type Output: Dimension; @@ -46,8 +46,8 @@ pub trait BroadcastShape { /// Dimensions of the same type remain unchanged when co_broadcast. /// So you can directly use D as the resulting type. -/// (Instead of >::BroadcastOutput) -impl BroadcastShape for D { +/// (Instead of >::BroadcastOutput) +impl DimMax for D { type Output = D; fn broadcast_shape(&self, other: &D) -> Result { @@ -57,7 +57,7 @@ impl BroadcastShape for D { macro_rules! impl_broadcast_distinct_fixed { ($smaller:ty, $larger:ty) => { - impl BroadcastShape<$larger> for $smaller { + impl DimMax<$larger> for $smaller { type Output = $larger; fn broadcast_shape(&self, other: &$larger) -> Result { @@ -65,7 +65,7 @@ macro_rules! impl_broadcast_distinct_fixed { } } - impl BroadcastShape<$smaller> for $larger { + impl DimMax<$smaller> for $larger { type Output = $larger; fn broadcast_shape(&self, other: &$smaller) -> Result { diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index e3a1ebd09..6007f93ab 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -15,7 +15,7 @@ use super::axes_of; use super::conversion::Convert; use super::{stride_offset, stride_offset_checked}; use crate::itertools::{enumerate, zip}; -use crate::{Axis, BroadcastShape}; +use crate::{Axis, DimMax}; use crate::IntoDimension; use crate::RemoveAxis; use crate::{ArrayView1, ArrayViewMut1}; @@ -46,11 +46,11 @@ pub trait Dimension: + MulAssign + for<'x> MulAssign<&'x Self> + MulAssign - + BroadcastShape - + BroadcastShape - + BroadcastShape - + BroadcastShape<::Smaller, Output=Self> - + BroadcastShape<::Larger, Output=::Larger> + + DimMax + + DimMax + + DimMax + + DimMax<::Smaller, Output=Self> + + DimMax<::Larger, Output=::Larger> { /// For fixed-size dimension representations (e.g. `Ix2`), this should be /// `Some(ndim)`, and for variable-size dimension representations (e.g. diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 98572ac59..7f5eeeaf7 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -12,7 +12,7 @@ use num_integer::div_floor; pub use self::axes::{axes_of, Axes, AxisDescription}; pub use self::axis::Axis; -pub use self::broadcast::BroadcastShape; +pub use self::broadcast::DimMax; pub use self::conversion::IntoDimension; pub use self::dim::*; pub use self::dimension_trait::Dimension; diff --git a/src/impl_methods.rs b/src/impl_methods.rs index c9b2d6f49..3079c9d2a 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -14,7 +14,7 @@ use rawpointer::PointerExt; use crate::imp_prelude::*; -use crate::{arraytraits, BroadcastShape}; +use crate::{arraytraits, DimMax}; use crate::dimension; use crate::dimension::IntoDimension; use crate::dimension::{ @@ -1771,11 +1771,11 @@ where /// /// Return `ShapeError` if their shapes can not be broadcast together. pub(crate) fn broadcast_with<'a, 'b, B, S2, E>(&'a self, other: &'b ArrayBase) -> - Result<(ArrayView<'a, A, >::Output>, ArrayView<'b, B, >::Output>), ShapeError> + Result<(ArrayView<'a, A, >::Output>, ArrayView<'b, B, >::Output>), ShapeError> where S: Data, S2: Data, - D: Dimension + BroadcastShape, + D: Dimension + DimMax, E: Dimension, { let shape = self.dim.broadcast_shape(&other.dim)?; diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 80aa8e11a..d38cb566a 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -6,7 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::dimension::BroadcastShape; +use crate::dimension::DimMax; use crate::Zip; use num_complex::Complex; @@ -68,10 +68,10 @@ where B: Clone, S: DataOwned + DataMut, S2: Data, - D: Dimension + BroadcastShape, + D: Dimension + DimMax, E: Dimension, { - type Output = ArrayBase>::Output>; + type Output = ArrayBase>::Output>; fn $mth(self, rhs: ArrayBase) -> Self::Output { self.$mth(&rhs) @@ -95,14 +95,14 @@ where B: Clone, S: DataOwned + DataMut, S2: Data, - D: Dimension + BroadcastShape, + D: Dimension + DimMax, E: Dimension, { - type Output = ArrayBase>::Output>; + type Output = ArrayBase>::Output>; fn $mth(self, rhs: &ArrayBase) -> Self::Output { if self.ndim() == rhs.ndim() && self.shape() == rhs.shape() { - let mut out = self.into_dimensionality::<>::Output>().unwrap(); + let mut out = self.into_dimensionality::<>::Output>().unwrap(); out.zip_mut_with_same_shape(rhs, clone_iopf(A::$mth)); out } else { @@ -130,14 +130,14 @@ where S: Data, S2: DataOwned + DataMut, D: Dimension, - E: Dimension + BroadcastShape, + E: Dimension + DimMax, { - type Output = ArrayBase>::Output>; + type Output = ArrayBase>::Output>; fn $mth(self, rhs: ArrayBase) -> Self::Output where { if self.ndim() == rhs.ndim() && self.shape() == rhs.shape() { - let mut out = rhs.into_dimensionality::<>::Output>().unwrap(); + let mut out = rhs.into_dimensionality::<>::Output>().unwrap(); out.zip_mut_with_same_shape(self, clone_iopf_rev(A::$mth)); out } else { @@ -162,10 +162,10 @@ where B: Clone, S: Data, S2: Data, - D: Dimension + BroadcastShape, + D: Dimension + DimMax, E: Dimension, { - type Output = Array>::Output>; + type Output = Array>::Output>; fn $mth(self, rhs: &'a ArrayBase) -> Self::Output { let (lhs, rhs) = self.broadcast_with(rhs).unwrap(); Zip::from(&lhs).and(&rhs).map_collect(clone_opf(A::$mth)) diff --git a/src/lib.rs b/src/lib.rs index 1a760bf31..c079b4817 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -134,7 +134,7 @@ use std::marker::PhantomData; use alloc::sync::Arc; pub use crate::dimension::dim::*; -pub use crate::dimension::BroadcastShape; +pub use crate::dimension::DimMax; pub use crate::dimension::{Axis, AxisDescription, Dimension, IntoDimension, RemoveAxis}; pub use crate::dimension::IxDynImpl; diff --git a/tests/dimension.rs b/tests/dimension.rs index ede0dc7d2..2bbc50a68 100644 --- a/tests/dimension.rs +++ b/tests/dimension.rs @@ -3,7 +3,7 @@ use defmac::defmac; use ndarray::{arr2, ArcArray, Array, Axis, Dim, Dimension, Ix0, IxDyn, IxDynImpl, RemoveAxis, - ErrorKind, ShapeError, BroadcastShape}; + ErrorKind, ShapeError, DimMax}; use std::hash::{Hash, Hasher}; @@ -347,9 +347,9 @@ fn test_broadcast_shape() { fn test_co( d1: &D1, d2: &D2, - r: Result<>::Output, ShapeError>, + r: Result<>::Output, ShapeError>, ) where - D1: Dimension + BroadcastShape, + D1: Dimension + DimMax, D2: Dimension, { let d = d1.broadcast_shape(d2); From 38f7341c692f00297146b3d7a28e32ba3e097494 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 12 Mar 2021 10:47:47 +0100 Subject: [PATCH 206/651] FIX: Remove broadcast_shape from the DimMax trait While calling co_broadcast directly is less convenient, for now they are two different functions. --- src/dimension/broadcast.rs | 82 ++++++++++++++++++++++++++++---------- src/dimension/mod.rs | 2 +- src/impl_methods.rs | 3 +- tests/dimension.rs | 50 +---------------------- 4 files changed, 64 insertions(+), 73 deletions(-) diff --git a/src/dimension/broadcast.rs b/src/dimension/broadcast.rs index 6da4375d0..dc1513f04 100644 --- a/src/dimension/broadcast.rs +++ b/src/dimension/broadcast.rs @@ -6,11 +6,11 @@ use crate::{Dimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn}; /// /// Uses the [NumPy broadcasting rules] // (https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#general-broadcasting-rules). -fn co_broadcast(shape1: &D1, shape2: &D2) -> Result - where - D1: Dimension, - D2: Dimension, - Output: Dimension, +pub(crate) fn co_broadcast(shape1: &D1, shape2: &D2) -> Result +where + D1: Dimension, + D2: Dimension, + Output: Dimension, { let (k, overflow) = shape1.ndim().overflowing_sub(shape2.ndim()); // Swap the order if d2 is longer. @@ -37,11 +37,6 @@ fn co_broadcast(shape1: &D1, shape2: &D2) -> Result { /// The resulting dimension type after broadcasting. type Output: Dimension; - - /// Determines the shape after broadcasting the shapes together. - /// - /// If the shapes are not compatible, returns `Err`. - fn broadcast_shape(&self, other: &Other) -> Result; } /// Dimensions of the same type remain unchanged when co_broadcast. @@ -49,28 +44,16 @@ pub trait DimMax { /// (Instead of >::BroadcastOutput) impl DimMax for D { type Output = D; - - fn broadcast_shape(&self, other: &D) -> Result { - co_broadcast::(self, other) - } } macro_rules! impl_broadcast_distinct_fixed { ($smaller:ty, $larger:ty) => { impl DimMax<$larger> for $smaller { type Output = $larger; - - fn broadcast_shape(&self, other: &$larger) -> Result { - co_broadcast::(self, other) - } } impl DimMax<$smaller> for $larger { type Output = $larger; - - fn broadcast_shape(&self, other: &$smaller) -> Result { - co_broadcast::(self, other) - } } }; } @@ -103,3 +86,58 @@ impl_broadcast_distinct_fixed!(Ix3, IxDyn); impl_broadcast_distinct_fixed!(Ix4, IxDyn); impl_broadcast_distinct_fixed!(Ix5, IxDyn); impl_broadcast_distinct_fixed!(Ix6, IxDyn); + + +#[cfg(test)] +#[cfg(feature = "std")] +mod tests { + use super::co_broadcast; + use crate::{Dimension, Dim, DimMax, ShapeError, Ix0, IxDynImpl, ErrorKind}; + + #[test] + fn test_broadcast_shape() { + fn test_co( + d1: &D1, + d2: &D2, + r: Result<>::Output, ShapeError>, + ) where + D1: Dimension + DimMax, + D2: Dimension, + { + let d = co_broadcast::>::Output>(&d1, d2); + assert_eq!(d, r); + } + test_co(&Dim([2, 3]), &Dim([4, 1, 3]), Ok(Dim([4, 2, 3]))); + test_co( + &Dim([1, 2, 2]), + &Dim([1, 3, 4]), + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), + ); + test_co(&Dim([3, 4, 5]), &Ix0(), Ok(Dim([3, 4, 5]))); + let v = vec![1, 2, 3, 4, 5, 6, 7]; + test_co( + &Dim(vec![1, 1, 3, 1, 5, 1, 7]), + &Dim([2, 1, 4, 1, 6, 1]), + Ok(Dim(IxDynImpl::from(v.as_slice()))), + ); + let d = Dim([1, 2, 1, 3]); + test_co(&d, &d, Ok(d)); + test_co( + &Dim([2, 1, 2]).into_dyn(), + &Dim(0), + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), + ); + test_co( + &Dim([2, 1, 1]), + &Dim([0, 0, 1, 3, 4]), + Ok(Dim([0, 0, 2, 3, 4])), + ); + test_co(&Dim([0]), &Dim([0, 0, 0]), Ok(Dim([0, 0, 0]))); + test_co(&Dim(1), &Dim([1, 0, 0]), Ok(Dim([1, 0, 0]))); + test_co( + &Dim([1, 3, 0, 1, 1]), + &Dim([1, 2, 3, 1]), + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), + ); + } +} diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 7f5eeeaf7..2505681b5 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -29,7 +29,7 @@ use std::mem; mod macros; mod axes; mod axis; -mod broadcast; +pub(crate) mod broadcast; mod conversion; pub mod dim; mod dimension_trait; diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 3079c9d2a..40c7fe1f2 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -21,6 +21,7 @@ use crate::dimension::{ abs_index, axes_of, do_slice, merge_axes, move_min_stride_axis_to_last, offset_from_ptr_to_memory, size_of_shape_checked, stride_offset, Axes, }; +use crate::dimension::broadcast::co_broadcast; use crate::error::{self, ErrorKind, ShapeError, from_kind}; use crate::math_cell::MathCell; use crate::itertools::zip; @@ -1778,7 +1779,7 @@ where D: Dimension + DimMax, E: Dimension, { - let shape = self.dim.broadcast_shape(&other.dim)?; + let shape = co_broadcast::>::Output>(&self.dim, &other.dim)?; if let Some(view1) = self.broadcast(shape.clone()) { if let Some(view2) = other.broadcast(shape) { return Ok((view1, view2)) diff --git a/tests/dimension.rs b/tests/dimension.rs index 2bbc50a68..939b4f0e3 100644 --- a/tests/dimension.rs +++ b/tests/dimension.rs @@ -2,8 +2,7 @@ use defmac::defmac; -use ndarray::{arr2, ArcArray, Array, Axis, Dim, Dimension, Ix0, IxDyn, IxDynImpl, RemoveAxis, - ErrorKind, ShapeError, DimMax}; +use ndarray::{arr2, ArcArray, Array, Axis, Dim, Dimension, IxDyn, RemoveAxis}; use std::hash::{Hash, Hasher}; @@ -341,50 +340,3 @@ fn test_all_ndindex() { ndindex!(10, 4, 3, 2, 2); ndindex!(10, 4, 3, 2, 2, 2); } - -#[test] -fn test_broadcast_shape() { - fn test_co( - d1: &D1, - d2: &D2, - r: Result<>::Output, ShapeError>, - ) where - D1: Dimension + DimMax, - D2: Dimension, - { - let d = d1.broadcast_shape(d2); - assert_eq!(d, r); - } - test_co(&Dim([2, 3]), &Dim([4, 1, 3]), Ok(Dim([4, 2, 3]))); - test_co( - &Dim([1, 2, 2]), - &Dim([1, 3, 4]), - Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), - ); - test_co(&Dim([3, 4, 5]), &Ix0(), Ok(Dim([3, 4, 5]))); - let v = vec![1, 2, 3, 4, 5, 6, 7]; - test_co( - &Dim(vec![1, 1, 3, 1, 5, 1, 7]), - &Dim([2, 1, 4, 1, 6, 1]), - Ok(Dim(IxDynImpl::from(v.as_slice()))), - ); - let d = Dim([1, 2, 1, 3]); - test_co(&d, &d, Ok(d)); - test_co( - &Dim([2, 1, 2]).into_dyn(), - &Dim(0), - Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), - ); - test_co( - &Dim([2, 1, 1]), - &Dim([0, 0, 1, 3, 4]), - Ok(Dim([0, 0, 2, 3, 4])), - ); - test_co(&Dim([0]), &Dim([0, 0, 0]), Ok(Dim([0, 0, 0]))); - test_co(&Dim(1), &Dim([1, 0, 0]), Ok(Dim([1, 0, 0]))); - test_co( - &Dim([1, 3, 0, 1, 1]), - &Dim([1, 2, 3, 1]), - Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), - ); -} From 03cfdfc445e5672ae47cca34012a3cfbda219db3 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 12 Mar 2021 11:50:11 +0100 Subject: [PATCH 207/651] MAINT: Fix clippy warnings for broadcast_with --- src/impl_methods.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 40c7fe1f2..958fc3f1c 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1771,8 +1771,9 @@ where /// broadcast them as array views into that shape. /// /// Return `ShapeError` if their shapes can not be broadcast together. + #[allow(clippy::type_complexity)] pub(crate) fn broadcast_with<'a, 'b, B, S2, E>(&'a self, other: &'b ArrayBase) -> - Result<(ArrayView<'a, A, >::Output>, ArrayView<'b, B, >::Output>), ShapeError> + Result<(ArrayView<'a, A, DimMaxOf>, ArrayView<'b, B, DimMaxOf>), ShapeError> where S: Data, S2: Data, @@ -1782,10 +1783,10 @@ where let shape = co_broadcast::>::Output>(&self.dim, &other.dim)?; if let Some(view1) = self.broadcast(shape.clone()) { if let Some(view2) = other.broadcast(shape) { - return Ok((view1, view2)) + return Ok((view1, view2)); } } - return Err(from_kind(ErrorKind::IncompatibleShape)); + Err(from_kind(ErrorKind::IncompatibleShape)) } /// Swap axes `ax` and `bx`. @@ -2465,3 +2466,5 @@ unsafe fn unlimited_transmute(data: A) -> B { let old_data = ManuallyDrop::new(data); (&*old_data as *const A as *const B).read() } + +type DimMaxOf = >::Output; From abcea8a960c455350a0fbbbe16973d9268ce757d Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 12 Mar 2021 14:22:43 +0100 Subject: [PATCH 208/651] API: Deprecate stack_new_axis (Use name stack) --- src/doc/ndarray_for_numpy_users/mod.rs | 8 ++++---- src/lib.rs | 1 + src/stacking.rs | 3 +++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index 45ef3ed06..aea97da3f 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -530,8 +530,8 @@ //! ------|-----------|------ //! `a[:] = 3.` | [`a.fill(3.)`][.fill()] | set all array elements to the same scalar value //! `a[:] = b` | [`a.assign(&b)`][.assign()] | copy the data from array `b` into array `a` -//! `np.concatenate((a,b), axis=1)` | [`stack![Axis(1), a, b]`][stack!] or [`stack(Axis(1), &[a.view(), b.view()])`][stack()] | concatenate arrays `a` and `b` along axis 1 -//! `np.stack((a,b), axis=1)` | [`stack_new_axis![Axis(1), a, b]`][stack_new_axis!] or [`stack_new_axis(Axis(1), vec![a.view(), b.view()])`][stack_new_axis()] | stack arrays `a` and `b` along axis 1 +//! `np.concatenate((a,b), axis=1)` | [`concatenate![Axis(1), a, b]`][concatenate!] or [`concatenate(Axis(1), &[a.view(), b.view()])`][concatenate()] | concatenate arrays `a` and `b` along axis 1 +//! `np.stack((a,b), axis=1)` | [`stack![Axis(1), a, b]`][stack!] or [`stack(Axis(1), vec![a.view(), b.view()])`][stack()] | stack arrays `a` and `b` along axis 1 //! `a[:,np.newaxis]` or `np.expand_dims(a, axis=1)` | [`a.insert_axis(Axis(1))`][.insert_axis()] | create an array from `a`, inserting a new axis 1 //! `a.transpose()` or `a.T` | [`a.t()`][.t()] or [`a.reversed_axes()`][.reversed_axes()] | transpose of array `a` (view for `.t()` or by-move for `.reversed_axes()`) //! `np.diag(a)` | [`a.diag()`][.diag()] | view the diagonal of `a` @@ -585,6 +585,8 @@ //! [.ncols()]: ../../struct.ArrayBase.html#method.ncols //! [.column()]: ../../struct.ArrayBase.html#method.column //! [.column_mut()]: ../../struct.ArrayBase.html#method.column_mut +//! [concatenate!]: ../../macro.concatenate.html +//! [concatenate()]: ../../fn.concatenate.html //! [CowArray]: ../../type.CowArray.html //! [::default()]: ../../struct.ArrayBase.html#method.default //! [.diag()]: ../../struct.ArrayBase.html#method.diag @@ -641,8 +643,6 @@ //! [.shape()]: ../../struct.ArrayBase.html#method.shape //! [stack!]: ../../macro.stack.html //! [stack()]: ../../fn.stack.html -//! [stack_new_axis!]: ../../macro.stack_new_axis.html -//! [stack_new_axis()]: ../../fn.stack_new_axis.html //! [.strides()]: ../../struct.ArrayBase.html#method.strides //! [.index_axis()]: ../../struct.ArrayBase.html#method.index_axis //! [.sum_axis()]: ../../struct.ArrayBase.html#method.sum_axis diff --git a/src/lib.rs b/src/lib.rs index c079b4817..4d35a1064 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -151,6 +151,7 @@ pub use crate::arraytraits::AsArray; pub use crate::linalg_traits::NdFloat; pub use crate::linalg_traits::LinalgScalar; +#[allow(deprecated)] // stack_new_axis pub use crate::stacking::{concatenate, stack, stack_new_axis}; pub use crate::math_cell::MathCell; diff --git a/src/stacking.rs b/src/stacking.rs index 6f4f378f4..580d94b43 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -43,6 +43,7 @@ where D: Dimension, D::Larger: RemoveAxis, { + #[allow(deprecated)] stack_new_axis(axis, arrays) } @@ -108,6 +109,7 @@ where } } +#[deprecated(note="Use under the name stack instead.", since="0.15.0")] /// Stack arrays along the new axis. /// /// ***Errors*** if the arrays have mismatching shapes. @@ -273,6 +275,7 @@ macro_rules! concatenate { /// # } /// ``` #[macro_export] +#[deprecated(note="Use under the name stack instead.", since="0.15.0")] macro_rules! stack_new_axis { ($axis:expr, $( $array:expr ),+ ) => { $crate::stack_new_axis($axis, &[ $($crate::ArrayView::from(&$array) ),* ]).unwrap() From 24a3299007ae569c9c2aa6c4038ccd7e2302ee8d Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 9 Dec 2018 15:42:53 -0500 Subject: [PATCH 209/651] Rename SliceOrIndex to AxisSliceInfo --- blas-tests/tests/oper.rs | 8 +- src/dimension/dimension_trait.rs | 22 +++--- src/dimension/mod.rs | 14 ++-- src/impl_methods.rs | 14 ++-- src/lib.rs | 2 +- src/slice.rs | 130 +++++++++++++++---------------- tests/array.rs | 26 +++---- tests/oper.rs | 8 +- 8 files changed, 112 insertions(+), 112 deletions(-) diff --git a/blas-tests/tests/oper.rs b/blas-tests/tests/oper.rs index 51ac7824c..2475c4e2d 100644 --- a/blas-tests/tests/oper.rs +++ b/blas-tests/tests/oper.rs @@ -6,8 +6,8 @@ extern crate num_traits; use ndarray::linalg::general_mat_mul; use ndarray::linalg::general_mat_vec_mul; use ndarray::prelude::*; +use ndarray::{AxisSliceInfo, Ix, Ixs, SliceInfo}; use ndarray::{Data, LinalgScalar}; -use ndarray::{Ix, Ixs, SliceInfo, SliceOrIndex}; use approx::{assert_abs_diff_eq, assert_relative_eq}; use defmac::defmac; @@ -420,11 +420,11 @@ fn scaled_add_3() { let mut answer = a.clone(); let cdim = if n == 1 { vec![q] } else { vec![n, q] }; let cslice = if n == 1 { - vec![SliceOrIndex::from(..).step_by(s2)] + vec![AxisSliceInfo::from(..).step_by(s2)] } else { vec![ - SliceOrIndex::from(..).step_by(s1), - SliceOrIndex::from(..).step_by(s2), + AxisSliceInfo::from(..).step_by(s1), + AxisSliceInfo::from(..).step_by(s2), ] }; diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 6007f93ab..c152ae3da 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -19,7 +19,7 @@ use crate::{Axis, DimMax}; use crate::IntoDimension; use crate::RemoveAxis; use crate::{ArrayView1, ArrayViewMut1}; -use crate::{Dim, Ix, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, IxDynImpl, Ixs, SliceOrIndex}; +use crate::{AxisSliceInfo, Dim, Ix, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, IxDynImpl, Ixs}; /// Array shape and index trait. /// @@ -63,14 +63,14 @@ pub trait Dimension: /// size, which you pass by reference. For the dynamic dimension it is /// a slice. /// - /// - For `Ix1`: `[SliceOrIndex; 1]` - /// - For `Ix2`: `[SliceOrIndex; 2]` + /// - For `Ix1`: `[AxisSliceInfo; 1]` + /// - For `Ix2`: `[AxisSliceInfo; 2]` /// - and so on.. - /// - For `IxDyn`: `[SliceOrIndex]` + /// - For `IxDyn`: `[AxisSliceInfo]` /// /// The easiest way to create a `&SliceInfo` is using the /// [`s![]`](macro.s!.html) macro. - type SliceArg: ?Sized + AsRef<[SliceOrIndex]>; + type SliceArg: ?Sized + AsRef<[AxisSliceInfo]>; /// Pattern matching friendly form of the dimension value. /// /// - For `Ix1`: `usize`, @@ -399,7 +399,7 @@ macro_rules! impl_insert_axis_array( impl Dimension for Dim<[Ix; 0]> { const NDIM: Option = Some(0); - type SliceArg = [SliceOrIndex; 0]; + type SliceArg = [AxisSliceInfo; 0]; type Pattern = (); type Smaller = Self; type Larger = Ix1; @@ -443,7 +443,7 @@ impl Dimension for Dim<[Ix; 0]> { impl Dimension for Dim<[Ix; 1]> { const NDIM: Option = Some(1); - type SliceArg = [SliceOrIndex; 1]; + type SliceArg = [AxisSliceInfo; 1]; type Pattern = Ix; type Smaller = Ix0; type Larger = Ix2; @@ -559,7 +559,7 @@ impl Dimension for Dim<[Ix; 1]> { impl Dimension for Dim<[Ix; 2]> { const NDIM: Option = Some(2); - type SliceArg = [SliceOrIndex; 2]; + type SliceArg = [AxisSliceInfo; 2]; type Pattern = (Ix, Ix); type Smaller = Ix1; type Larger = Ix3; @@ -716,7 +716,7 @@ impl Dimension for Dim<[Ix; 2]> { impl Dimension for Dim<[Ix; 3]> { const NDIM: Option = Some(3); - type SliceArg = [SliceOrIndex; 3]; + type SliceArg = [AxisSliceInfo; 3]; type Pattern = (Ix, Ix, Ix); type Smaller = Ix2; type Larger = Ix4; @@ -839,7 +839,7 @@ macro_rules! large_dim { ($n:expr, $name:ident, $pattern:ty, $larger:ty, { $($insert_axis:tt)* }) => ( impl Dimension for Dim<[Ix; $n]> { const NDIM: Option = Some($n); - type SliceArg = [SliceOrIndex; $n]; + type SliceArg = [AxisSliceInfo; $n]; type Pattern = $pattern; type Smaller = Dim<[Ix; $n - 1]>; type Larger = $larger; @@ -890,7 +890,7 @@ large_dim!(6, Ix6, (Ix, Ix, Ix, Ix, Ix, Ix), IxDyn, { /// and memory wasteful, but it allows an arbitrary and dynamic number of axes. impl Dimension for IxDyn { const NDIM: Option = None; - type SliceArg = [SliceOrIndex]; + type SliceArg = [AxisSliceInfo]; type Pattern = Self; type Smaller = Self; type Larger = Self; diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 2505681b5..9bc603c53 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -7,7 +7,7 @@ // except according to those terms. use crate::error::{from_kind, ErrorKind, ShapeError}; -use crate::{Ix, Ixs, Slice, SliceOrIndex}; +use crate::{AxisSliceInfo, Ix, Ixs, Slice}; use num_integer::div_floor; pub use self::axes::{axes_of, Axes, AxisDescription}; @@ -601,15 +601,15 @@ pub fn slices_intersect( ) -> bool { debug_assert_eq!(indices1.as_ref().len(), indices2.as_ref().len()); for (&axis_len, &si1, &si2) in izip!(dim.slice(), indices1.as_ref(), indices2.as_ref()) { - // The slices do not intersect iff any pair of `SliceOrIndex` does not intersect. + // The slices do not intersect iff any pair of `AxisSliceInfo` does not intersect. match (si1, si2) { ( - SliceOrIndex::Slice { + AxisSliceInfo::Slice { start: start1, end: end1, step: step1, }, - SliceOrIndex::Slice { + AxisSliceInfo::Slice { start: start2, end: end2, step: step2, @@ -630,8 +630,8 @@ pub fn slices_intersect( return false; } } - (SliceOrIndex::Slice { start, end, step }, SliceOrIndex::Index(ind)) - | (SliceOrIndex::Index(ind), SliceOrIndex::Slice { start, end, step }) => { + (AxisSliceInfo::Slice { start, end, step }, AxisSliceInfo::Index(ind)) + | (AxisSliceInfo::Index(ind), AxisSliceInfo::Slice { start, end, step }) => { let ind = abs_index(axis_len, ind); let (min, max) = match slice_min_max(axis_len, Slice::new(start, end, step)) { Some(m) => m, @@ -641,7 +641,7 @@ pub fn slices_intersect( return false; } } - (SliceOrIndex::Index(ind1), SliceOrIndex::Index(ind2)) => { + (AxisSliceInfo::Index(ind1), AxisSliceInfo::Index(ind2)) => { let ind1 = abs_index(axis_len, ind1); let ind2 = abs_index(axis_len, ind2); if ind1 != ind2 { diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 958fc3f1c..02b182cb3 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -34,7 +34,7 @@ use crate::iter::{ }; use crate::slice::MultiSlice; use crate::stacking::concatenate; -use crate::{NdIndex, Slice, SliceInfo, SliceOrIndex}; +use crate::{AxisSliceInfo, NdIndex, Slice, SliceInfo}; /// # Methods For All Array Types impl ArrayBase @@ -417,7 +417,7 @@ where // Slice and collapse in-place without changing the number of dimensions. self.slice_collapse(&*info); - let indices: &[SliceOrIndex] = (**info).as_ref(); + let indices: &[AxisSliceInfo] = (**info).as_ref(); // Copy the dim and strides that remain after removing the subview axes. let out_ndim = info.out_ndim(); @@ -425,8 +425,8 @@ where let mut new_strides = Do::zeros(out_ndim); izip!(self.dim.slice(), self.strides.slice(), indices) .filter_map(|(d, s, slice_or_index)| match slice_or_index { - SliceOrIndex::Slice { .. } => Some((d, s)), - SliceOrIndex::Index(_) => None, + AxisSliceInfo::Slice { .. } => Some((d, s)), + AxisSliceInfo::Index(_) => None, }) .zip(izip!(new_dim.slice_mut(), new_strides.slice_mut())) .for_each(|((d, s), (new_d, new_s))| { @@ -455,16 +455,16 @@ where /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `indices` does not match the number of array axes.) pub fn slice_collapse(&mut self, indices: &D::SliceArg) { - let indices: &[SliceOrIndex] = indices.as_ref(); + let indices: &[AxisSliceInfo] = indices.as_ref(); assert_eq!(indices.len(), self.ndim()); indices .iter() .enumerate() .for_each(|(axis, &slice_or_index)| match slice_or_index { - SliceOrIndex::Slice { start, end, step } => { + AxisSliceInfo::Slice { start, end, step } => { self.slice_axis_inplace(Axis(axis), Slice { start, end, step }) } - SliceOrIndex::Index(index) => { + AxisSliceInfo::Index(index) => { let i_usize = abs_index(self.len_of(Axis(axis)), index); self.collapse_axis(Axis(axis), i_usize) } diff --git a/src/lib.rs b/src/lib.rs index 4d35a1064..d1da388b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -141,7 +141,7 @@ pub use crate::dimension::IxDynImpl; pub use crate::dimension::NdIndex; pub use crate::error::{ErrorKind, ShapeError}; pub use crate::indexes::{indices, indices_of}; -pub use crate::slice::{Slice, SliceInfo, SliceNextDim, SliceOrIndex}; +pub use crate::slice::{AxisSliceInfo, Slice, SliceInfo, SliceNextDim}; use crate::iterators::Baseiter; use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes}; diff --git a/src/slice.rs b/src/slice.rs index 86a2b0b8f..dbd8aea23 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -70,29 +70,29 @@ impl Slice { /// A slice (range with step) or an index. /// /// See also the [`s![]`](macro.s!.html) macro for a convenient way to create a -/// `&SliceInfo<[SliceOrIndex; n], D>`. +/// `&SliceInfo<[AxisSliceInfo; n], D>`. /// /// ## Examples /// -/// `SliceOrIndex::Index(a)` is the index `a`. It can also be created with -/// `SliceOrIndex::from(a)`. The Python equivalent is `[a]`. The macro +/// `AxisSliceInfo::Index(a)` is the index `a`. It can also be created with +/// `AxisSliceInfo::from(a)`. The Python equivalent is `[a]`. The macro /// equivalent is `s![a]`. /// -/// `SliceOrIndex::Slice { start: 0, end: None, step: 1 }` is the full range of -/// an axis. It can also be created with `SliceOrIndex::from(..)`. The Python -/// equivalent is `[:]`. The macro equivalent is `s![..]`. +/// `AxisSliceInfo::Slice { start: 0, end: None, step: 1 }` is the full range +/// of an axis. It can also be created with `AxisSliceInfo::from(..)`. The +/// Python equivalent is `[:]`. The macro equivalent is `s![..]`. /// -/// `SliceOrIndex::Slice { start: a, end: Some(b), step: 2 }` is every second +/// `AxisSliceInfo::Slice { start: a, end: Some(b), step: 2 }` is every second /// element from `a` until `b`. It can also be created with -/// `SliceOrIndex::from(a..b).step_by(2)`. The Python equivalent is `[a:b:2]`. +/// `AxisSliceInfo::from(a..b).step_by(2)`. The Python equivalent is `[a:b:2]`. /// The macro equivalent is `s![a..b;2]`. /// -/// `SliceOrIndex::Slice { start: a, end: None, step: -1 }` is every element, +/// `AxisSliceInfo::Slice { start: a, end: None, step: -1 }` is every element, /// from `a` until the end, in reverse order. It can also be created with -/// `SliceOrIndex::from(a..).step_by(-1)`. The Python equivalent is `[a::-1]`. +/// `AxisSliceInfo::from(a..).step_by(-1)`. The Python equivalent is `[a::-1]`. /// The macro equivalent is `s![a..;-1]`. #[derive(Debug, PartialEq, Eq, Hash)] -pub enum SliceOrIndex { +pub enum AxisSliceInfo { /// A range with step size. `end` is an exclusive index. Negative `begin` /// or `end` indexes are counted from the back of the axis. If `end` is /// `None`, the slice extends to the end of the axis. @@ -105,47 +105,47 @@ pub enum SliceOrIndex { Index(isize), } -copy_and_clone! {SliceOrIndex} +copy_and_clone! {AxisSliceInfo} -impl SliceOrIndex { +impl AxisSliceInfo { /// Returns `true` if `self` is a `Slice` value. pub fn is_slice(&self) -> bool { - matches!(self, SliceOrIndex::Slice { .. }) + matches!(self, AxisSliceInfo::Slice { .. }) } /// Returns `true` if `self` is an `Index` value. pub fn is_index(&self) -> bool { - matches!(self, SliceOrIndex::Index(_)) + matches!(self, AxisSliceInfo::Index(_)) } - /// Returns a new `SliceOrIndex` with the given step size (multiplied with + /// Returns a new `AxisSliceInfo` with the given step size (multiplied with /// the previous step size). /// /// `step` must be nonzero. /// (This method checks with a debug assertion that `step` is not zero.) #[inline] pub fn step_by(self, step: isize) -> Self { - debug_assert_ne!(step, 0, "SliceOrIndex::step_by: step must be nonzero"); + debug_assert_ne!(step, 0, "AxisSliceInfo::step_by: step must be nonzero"); match self { - SliceOrIndex::Slice { + AxisSliceInfo::Slice { start, end, step: orig_step, - } => SliceOrIndex::Slice { + } => AxisSliceInfo::Slice { start, end, step: orig_step * step, }, - SliceOrIndex::Index(s) => SliceOrIndex::Index(s), + AxisSliceInfo::Index(s) => AxisSliceInfo::Index(s), } } } -impl fmt::Display for SliceOrIndex { +impl fmt::Display for AxisSliceInfo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - SliceOrIndex::Index(index) => write!(f, "{}", index)?, - SliceOrIndex::Slice { start, end, step } => { + AxisSliceInfo::Index(index) => write!(f, "{}", index)?, + AxisSliceInfo::Slice { start, end, step } => { if start != 0 { write!(f, "{}", start)?; } @@ -225,9 +225,9 @@ macro_rules! impl_slice_variant_from_range { impl_slice_variant_from_range!(Slice, Slice, isize); impl_slice_variant_from_range!(Slice, Slice, usize); impl_slice_variant_from_range!(Slice, Slice, i32); -impl_slice_variant_from_range!(SliceOrIndex, SliceOrIndex::Slice, isize); -impl_slice_variant_from_range!(SliceOrIndex, SliceOrIndex::Slice, usize); -impl_slice_variant_from_range!(SliceOrIndex, SliceOrIndex::Slice, i32); +impl_slice_variant_from_range!(AxisSliceInfo, AxisSliceInfo::Slice, isize); +impl_slice_variant_from_range!(AxisSliceInfo, AxisSliceInfo::Slice, usize); +impl_slice_variant_from_range!(AxisSliceInfo, AxisSliceInfo::Slice, i32); impl From for Slice { #[inline] @@ -240,10 +240,10 @@ impl From for Slice { } } -impl From for SliceOrIndex { +impl From for AxisSliceInfo { #[inline] - fn from(_: RangeFull) -> SliceOrIndex { - SliceOrIndex::Slice { + fn from(_: RangeFull) -> AxisSliceInfo { + AxisSliceInfo::Slice { start: 0, end: None, step: 1, @@ -251,10 +251,10 @@ impl From for SliceOrIndex { } } -impl From for SliceOrIndex { +impl From for AxisSliceInfo { #[inline] - fn from(s: Slice) -> SliceOrIndex { - SliceOrIndex::Slice { + fn from(s: Slice) -> AxisSliceInfo { + AxisSliceInfo::Slice { start: s.start, end: s.end, step: s.step, @@ -262,24 +262,24 @@ impl From for SliceOrIndex { } } -macro_rules! impl_sliceorindex_from_index { +macro_rules! impl_axissliceinfo_from_index { ($index:ty) => { - impl From<$index> for SliceOrIndex { + impl From<$index> for AxisSliceInfo { #[inline] - fn from(r: $index) -> SliceOrIndex { - SliceOrIndex::Index(r as isize) + fn from(r: $index) -> AxisSliceInfo { + AxisSliceInfo::Index(r as isize) } } }; } -impl_sliceorindex_from_index!(isize); -impl_sliceorindex_from_index!(usize); -impl_sliceorindex_from_index!(i32); +impl_axissliceinfo_from_index!(isize); +impl_axissliceinfo_from_index!(usize); +impl_axissliceinfo_from_index!(i32); /// Represents all of the necessary information to perform a slice. /// -/// The type `T` is typically `[SliceOrIndex; n]`, `[SliceOrIndex]`, or -/// `Vec`. The type `D` is the output dimension after calling +/// The type `T` is typically `[AxisSliceInfo; n]`, `[AxisSliceInfo]`, or +/// `Vec`. The type `D` is the output dimension after calling /// [`.slice()`]. /// /// [`.slice()`]: struct.ArrayBase.html#method.slice @@ -316,7 +316,7 @@ where impl SliceInfo where - T: AsRef<[SliceOrIndex]>, + T: AsRef<[AxisSliceInfo]>, D: Dimension, { /// Returns a new `SliceInfo` instance. @@ -337,7 +337,7 @@ where impl SliceInfo where - T: AsRef<[SliceOrIndex]>, + T: AsRef<[AxisSliceInfo]>, D: Dimension, { /// Returns the number of dimensions after calling @@ -358,29 +358,29 @@ where } } -impl AsRef<[SliceOrIndex]> for SliceInfo +impl AsRef<[AxisSliceInfo]> for SliceInfo where - T: AsRef<[SliceOrIndex]>, + T: AsRef<[AxisSliceInfo]>, D: Dimension, { - fn as_ref(&self) -> &[SliceOrIndex] { + fn as_ref(&self) -> &[AxisSliceInfo] { self.indices.as_ref() } } -impl AsRef> for SliceInfo +impl AsRef> for SliceInfo where - T: AsRef<[SliceOrIndex]>, + T: AsRef<[AxisSliceInfo]>, D: Dimension, { - fn as_ref(&self) -> &SliceInfo<[SliceOrIndex], D> { + fn as_ref(&self) -> &SliceInfo<[AxisSliceInfo], D> { unsafe { // This is okay because the only non-zero-sized member of - // `SliceInfo` is `indices`, so `&SliceInfo<[SliceOrIndex], D>` + // `SliceInfo` is `indices`, so `&SliceInfo<[AxisSliceInfo], D>` // should have the same bitwise representation as - // `&[SliceOrIndex]`. - &*(self.indices.as_ref() as *const [SliceOrIndex] - as *const SliceInfo<[SliceOrIndex], D>) + // `&[AxisSliceInfo]`. + &*(self.indices.as_ref() as *const [AxisSliceInfo] + as *const SliceInfo<[AxisSliceInfo], D>) } } } @@ -452,8 +452,8 @@ impl_slicenextdim_larger!((), Slice); /// counted from the end of the axis. Step sizes are also signed and may be /// negative, but must not be zero. /// -/// The syntax is `s![` *[ axis-slice-or-index [, axis-slice-or-index [ , ... ] -/// ] ]* `]`, where *axis-slice-or-index* is any of the following: +/// The syntax is `s![` *[ axis-slice-info [, axis-slice-info [ , ... ] ] ]* +/// `]`, where *axis-slice-info* is any of the following: /// /// * *index*: an index to use for taking a subview with respect to that axis. /// (The index is selected. The axis is removed except with @@ -466,12 +466,12 @@ impl_slicenextdim_larger!((), Slice); /// /// [`Slice`]: struct.Slice.html /// -/// The number of *axis-slice-or-index* must match the number of axes in the -/// array. *index*, *range*, *slice*, and *step* can be expressions. *index* -/// must be of type `isize`, `usize`, or `i32`. *range* must be of type -/// `Range`, `RangeTo`, `RangeFrom`, or `RangeFull` where `I` is -/// `isize`, `usize`, or `i32`. *step* must be a type that can be converted to -/// `isize` with the `as` keyword. +/// The number of *axis-slice-info* must match the number of axes in the array. +/// *index*, *range*, *slice*, and *step* can be expressions. *index* must be +/// of type `isize`, `usize`, or `i32`. *range* must be of type `Range`, +/// `RangeTo`, `RangeFrom`, or `RangeFull` where `I` is `isize`, `usize`, +/// or `i32`. *step* must be a type that can be converted to `isize` with the +/// `as` keyword. /// /// For example `s![0..4;2, 6, 1..5]` is a slice of the first axis for 0..4 /// with step size 2, a subview of the second axis at index 6, and a slice of @@ -606,13 +606,13 @@ macro_rules! s( }; // Catch-all clause for syntax errors (@parse $($t:tt)*) => { compile_error!("Invalid syntax in s![] call.") }; - // convert range/index into SliceOrIndex + // convert range/index into AxisSliceInfo (@convert $r:expr) => { - <$crate::SliceOrIndex as ::std::convert::From<_>>::from($r) + <$crate::AxisSliceInfo as ::std::convert::From<_>>::from($r) }; - // convert range/index and step into SliceOrIndex + // convert range/index and step into AxisSliceInfo (@convert $r:expr, $s:expr) => { - <$crate::SliceOrIndex as ::std::convert::From<_>>::from($r).step_by($s as isize) + <$crate::AxisSliceInfo as ::std::convert::From<_>>::from($r).step_by($s as isize) }; ($($t:tt)*) => { // The extra `*&` is a workaround for this compiler bug: diff --git a/tests/array.rs b/tests/array.rs index b0a28ca41..4b30c15b4 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -12,7 +12,7 @@ use itertools::{enumerate, zip, Itertools}; use ndarray::prelude::*; use ndarray::{arr3, rcarr2}; use ndarray::indices; -use ndarray::{Slice, SliceInfo, SliceOrIndex}; +use ndarray::{AxisSliceInfo, Slice, SliceInfo}; macro_rules! assert_panics { ($body:expr) => { @@ -217,9 +217,9 @@ fn test_slice_dyninput_array_fixed() { fn test_slice_array_dyn() { let mut arr = Array3::::zeros((5, 2, 5)); let info = &SliceInfo::<_, IxDyn>::new([ - SliceOrIndex::from(1..), - SliceOrIndex::from(1), - SliceOrIndex::from(..).step_by(2), + AxisSliceInfo::from(1..), + AxisSliceInfo::from(1), + AxisSliceInfo::from(..).step_by(2), ]) .unwrap(); arr.slice(info); @@ -232,9 +232,9 @@ fn test_slice_array_dyn() { fn test_slice_dyninput_array_dyn() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); let info = &SliceInfo::<_, IxDyn>::new([ - SliceOrIndex::from(1..), - SliceOrIndex::from(1), - SliceOrIndex::from(..).step_by(2), + AxisSliceInfo::from(1..), + AxisSliceInfo::from(1), + AxisSliceInfo::from(..).step_by(2), ]) .unwrap(); arr.slice(info); @@ -247,9 +247,9 @@ fn test_slice_dyninput_array_dyn() { fn test_slice_dyninput_vec_fixed() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); let info = &SliceInfo::<_, Ix2>::new(vec![ - SliceOrIndex::from(1..), - SliceOrIndex::from(1), - SliceOrIndex::from(..).step_by(2), + AxisSliceInfo::from(1..), + AxisSliceInfo::from(1), + AxisSliceInfo::from(..).step_by(2), ]) .unwrap(); arr.slice(info.as_ref()); @@ -262,9 +262,9 @@ fn test_slice_dyninput_vec_fixed() { fn test_slice_dyninput_vec_dyn() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); let info = &SliceInfo::<_, IxDyn>::new(vec![ - SliceOrIndex::from(1..), - SliceOrIndex::from(1), - SliceOrIndex::from(..).step_by(2), + AxisSliceInfo::from(1..), + AxisSliceInfo::from(1), + AxisSliceInfo::from(..).step_by(2), ]) .unwrap(); arr.slice(info.as_ref()); diff --git a/tests/oper.rs b/tests/oper.rs index 0d659fa1e..22dc6603e 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -561,7 +561,7 @@ fn scaled_add_2() { #[test] fn scaled_add_3() { use approx::assert_relative_eq; - use ndarray::{SliceInfo, SliceOrIndex}; + use ndarray::{SliceInfo, AxisSliceInfo}; let beta = -2.3; let sizes = vec![ @@ -583,11 +583,11 @@ fn scaled_add_3() { let mut answer = a.clone(); let cdim = if n == 1 { vec![q] } else { vec![n, q] }; let cslice = if n == 1 { - vec![SliceOrIndex::from(..).step_by(s2)] + vec![AxisSliceInfo::from(..).step_by(s2)] } else { vec![ - SliceOrIndex::from(..).step_by(s1), - SliceOrIndex::from(..).step_by(s2), + AxisSliceInfo::from(..).step_by(s1), + AxisSliceInfo::from(..).step_by(s2), ] }; From 6a16b881647d405ed9123ba9b04f59f876f97487 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 9 Dec 2018 16:39:00 -0500 Subject: [PATCH 210/651] Switch from Dimension::SliceArg to CanSlice trait --- blas-tests/tests/oper.rs | 2 +- src/dimension/dimension_trait.rs | 23 +-- src/dimension/mod.rs | 7 +- src/impl_methods.rs | 80 +++++---- src/lib.rs | 2 +- src/slice.rs | 286 ++++++++++++++++++++++--------- tests/array.rs | 28 +-- tests/oper.rs | 2 +- 8 files changed, 273 insertions(+), 157 deletions(-) diff --git a/blas-tests/tests/oper.rs b/blas-tests/tests/oper.rs index 2475c4e2d..25d26b7ba 100644 --- a/blas-tests/tests/oper.rs +++ b/blas-tests/tests/oper.rs @@ -432,7 +432,7 @@ fn scaled_add_3() { { let mut av = a.slice_mut(s![..;s1, ..;s2]); - let c = c.slice(SliceInfo::<_, IxDyn>::new(cslice).unwrap().as_ref()); + let c = c.slice(&SliceInfo::<_, IxDyn, IxDyn>::new(cslice).unwrap()); let mut answerv = answer.slice_mut(s![..;s1, ..;s2]); answerv += &(beta * &c); diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index c152ae3da..df38904f4 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -19,7 +19,7 @@ use crate::{Axis, DimMax}; use crate::IntoDimension; use crate::RemoveAxis; use crate::{ArrayView1, ArrayViewMut1}; -use crate::{AxisSliceInfo, Dim, Ix, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, IxDynImpl, Ixs}; +use crate::{Dim, Ix, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, IxDynImpl, Ixs}; /// Array shape and index trait. /// @@ -56,21 +56,6 @@ pub trait Dimension: /// `Some(ndim)`, and for variable-size dimension representations (e.g. /// `IxDyn`), this should be `None`. const NDIM: Option; - /// `SliceArg` is the type which is used to specify slicing for this - /// dimension. - /// - /// For the fixed size dimensions it is a fixed size array of the correct - /// size, which you pass by reference. For the dynamic dimension it is - /// a slice. - /// - /// - For `Ix1`: `[AxisSliceInfo; 1]` - /// - For `Ix2`: `[AxisSliceInfo; 2]` - /// - and so on.. - /// - For `IxDyn`: `[AxisSliceInfo]` - /// - /// The easiest way to create a `&SliceInfo` is using the - /// [`s![]`](macro.s!.html) macro. - type SliceArg: ?Sized + AsRef<[AxisSliceInfo]>; /// Pattern matching friendly form of the dimension value. /// /// - For `Ix1`: `usize`, @@ -399,7 +384,6 @@ macro_rules! impl_insert_axis_array( impl Dimension for Dim<[Ix; 0]> { const NDIM: Option = Some(0); - type SliceArg = [AxisSliceInfo; 0]; type Pattern = (); type Smaller = Self; type Larger = Ix1; @@ -443,7 +427,6 @@ impl Dimension for Dim<[Ix; 0]> { impl Dimension for Dim<[Ix; 1]> { const NDIM: Option = Some(1); - type SliceArg = [AxisSliceInfo; 1]; type Pattern = Ix; type Smaller = Ix0; type Larger = Ix2; @@ -559,7 +542,6 @@ impl Dimension for Dim<[Ix; 1]> { impl Dimension for Dim<[Ix; 2]> { const NDIM: Option = Some(2); - type SliceArg = [AxisSliceInfo; 2]; type Pattern = (Ix, Ix); type Smaller = Ix1; type Larger = Ix3; @@ -716,7 +698,6 @@ impl Dimension for Dim<[Ix; 2]> { impl Dimension for Dim<[Ix; 3]> { const NDIM: Option = Some(3); - type SliceArg = [AxisSliceInfo; 3]; type Pattern = (Ix, Ix, Ix); type Smaller = Ix2; type Larger = Ix4; @@ -839,7 +820,6 @@ macro_rules! large_dim { ($n:expr, $name:ident, $pattern:ty, $larger:ty, { $($insert_axis:tt)* }) => ( impl Dimension for Dim<[Ix; $n]> { const NDIM: Option = Some($n); - type SliceArg = [AxisSliceInfo; $n]; type Pattern = $pattern; type Smaller = Dim<[Ix; $n - 1]>; type Larger = $larger; @@ -890,7 +870,6 @@ large_dim!(6, Ix6, (Ix, Ix, Ix, Ix, Ix, Ix), IxDyn, { /// and memory wasteful, but it allows an arbitrary and dynamic number of axes. impl Dimension for IxDyn { const NDIM: Option = None; - type SliceArg = [AxisSliceInfo]; type Pattern = Self; type Smaller = Self; type Larger = Self; diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 9bc603c53..a1e245884 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -7,6 +7,7 @@ // except according to those terms. use crate::error::{from_kind, ErrorKind, ShapeError}; +use crate::slice::CanSlice; use crate::{AxisSliceInfo, Ix, Ixs, Slice}; use num_integer::div_floor; @@ -596,10 +597,10 @@ fn slice_min_max(axis_len: usize, slice: Slice) -> Option<(usize, usize)> { /// Returns `true` iff the slices intersect. pub fn slices_intersect( dim: &D, - indices1: &D::SliceArg, - indices2: &D::SliceArg, + indices1: &impl CanSlice, + indices2: &impl CanSlice, ) -> bool { - debug_assert_eq!(indices1.as_ref().len(), indices2.as_ref().len()); + debug_assert_eq!(indices1.in_ndim(), indices2.in_ndim()); for (&axis_len, &si1, &si2) in izip!(dim.slice(), indices1.as_ref(), indices2.as_ref()) { // The slices do not intersect iff any pair of `AxisSliceInfo` does not intersect. match (si1, si2) { diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 02b182cb3..2cbfcacf2 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -32,9 +32,9 @@ use crate::iter::{ AxisChunksIter, AxisChunksIterMut, AxisIter, AxisIterMut, ExactChunks, ExactChunksMut, IndexedIter, IndexedIterMut, Iter, IterMut, Lanes, LanesMut, Windows, }; -use crate::slice::MultiSlice; +use crate::slice::{CanSlice, MultiSlice}; use crate::stacking::concatenate; -use crate::{AxisSliceInfo, NdIndex, Slice, SliceInfo}; +use crate::{AxisSliceInfo, NdIndex, Slice}; /// # Methods For All Array Types impl ArrayBase @@ -341,9 +341,9 @@ where /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) - pub fn slice(&self, info: &SliceInfo) -> ArrayView<'_, A, Do> + pub fn slice(&self, info: &I) -> ArrayView<'_, A, I::OutDim> where - Do: Dimension, + I: CanSlice, S: Data, { self.view().slice_move(info) @@ -359,9 +359,9 @@ where /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) - pub fn slice_mut(&mut self, info: &SliceInfo) -> ArrayViewMut<'_, A, Do> + pub fn slice_mut(&mut self, info: &I) -> ArrayViewMut<'_, A, I::OutDim> where - Do: Dimension, + I: CanSlice, S: DataMut, { self.view_mut().slice_move(info) @@ -410,29 +410,37 @@ where /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) - pub fn slice_move(mut self, info: &SliceInfo) -> ArrayBase + pub fn slice_move(mut self, info: &I) -> ArrayBase where - Do: Dimension, + I: CanSlice, { // Slice and collapse in-place without changing the number of dimensions. - self.slice_collapse(&*info); + self.slice_collapse(info); - let indices: &[AxisSliceInfo] = (**info).as_ref(); - - // Copy the dim and strides that remain after removing the subview axes. let out_ndim = info.out_ndim(); - let mut new_dim = Do::zeros(out_ndim); - let mut new_strides = Do::zeros(out_ndim); - izip!(self.dim.slice(), self.strides.slice(), indices) - .filter_map(|(d, s, slice_or_index)| match slice_or_index { - AxisSliceInfo::Slice { .. } => Some((d, s)), - AxisSliceInfo::Index(_) => None, - }) - .zip(izip!(new_dim.slice_mut(), new_strides.slice_mut())) - .for_each(|((d, s), (new_d, new_s))| { - *new_d = *d; - *new_s = *s; + let mut new_dim = I::OutDim::zeros(out_ndim); + let mut new_strides = I::OutDim::zeros(out_ndim); + + // Write the dim and strides to the correct new axes. + { + let mut old_axis = 0; + let mut new_axis = 0; + info.as_ref().iter().for_each(|ax_info| match ax_info { + AxisSliceInfo::Slice { .. } => { + // Copy the old dim and stride to corresponding axis. + new_dim[new_axis] = self.dim[old_axis]; + new_strides[new_axis] = self.strides[old_axis]; + old_axis += 1; + new_axis += 1; + } + AxisSliceInfo::Index(_) => { + // Skip the old axis since it should be removed. + old_axis += 1; + } }); + debug_assert_eq!(old_axis, self.ndim()); + debug_assert_eq!(new_axis, out_ndim); + } // safe because new dimension, strides allow access to a subset of old data unsafe { @@ -442,25 +450,23 @@ where /// Slice the array in place without changing the number of dimensions. /// - /// Note that [`&SliceInfo`](struct.SliceInfo.html) (produced by the - /// [`s![]`](macro.s!.html) macro) will usually coerce into `&D::SliceArg` - /// automatically, but in some cases (e.g. if `D` is `IxDyn`), you may need - /// to call `.as_ref()`. - /// /// See [*Slicing*](#slicing) for full documentation. - /// See also [`D::SliceArg`]. - /// - /// [`D::SliceArg`]: trait.Dimension.html#associatedtype.SliceArg /// /// **Panics** if an index is out of bounds or step size is zero.
- /// (**Panics** if `D` is `IxDyn` and `indices` does not match the number of array axes.) - pub fn slice_collapse(&mut self, indices: &D::SliceArg) { - let indices: &[AxisSliceInfo] = indices.as_ref(); - assert_eq!(indices.len(), self.ndim()); - indices + /// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) + pub fn slice_collapse(&mut self, info: &I) + where + I: CanSlice, + { + assert_eq!( + info.in_ndim(), + self.ndim(), + "The input dimension of `info` must match the array to be sliced.", + ); + info.as_ref() .iter() .enumerate() - .for_each(|(axis, &slice_or_index)| match slice_or_index { + .for_each(|(axis, &ax_info)| match ax_info { AxisSliceInfo::Slice { start, end, step } => { self.slice_axis_inplace(Axis(axis), Slice { start, end, step }) } diff --git a/src/lib.rs b/src/lib.rs index d1da388b6..7c9439411 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -141,7 +141,7 @@ pub use crate::dimension::IxDynImpl; pub use crate::dimension::NdIndex; pub use crate::error::{ErrorKind, ShapeError}; pub use crate::indexes::{indices, indices_of}; -pub use crate::slice::{AxisSliceInfo, Slice, SliceInfo, SliceNextDim}; +pub use crate::slice::{AxisSliceInfo, Slice, SliceInfo, SliceNextInDim, SliceNextOutDim}; use crate::iterators::Baseiter; use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes}; diff --git a/src/slice.rs b/src/slice.rs index dbd8aea23..dc4ad9d7a 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -7,10 +7,10 @@ // except according to those terms. use crate::dimension::slices_intersect; use crate::error::{ErrorKind, ShapeError}; -use crate::{ArrayViewMut, Dimension}; use std::fmt; use std::marker::PhantomData; use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; +use crate::{ArrayViewMut, Dimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn}; /// A slice (range with step size). /// @@ -70,7 +70,7 @@ impl Slice { /// A slice (range with step) or an index. /// /// See also the [`s![]`](macro.s!.html) macro for a convenient way to create a -/// `&SliceInfo<[AxisSliceInfo; n], D>`. +/// `&SliceInfo<[AxisSliceInfo; n], Di, Do>`. /// /// ## Examples /// @@ -276,23 +276,85 @@ impl_axissliceinfo_from_index!(isize); impl_axissliceinfo_from_index!(usize); impl_axissliceinfo_from_index!(i32); +/// A type that can slice an array of dimension `D`. +/// +/// This trait is unsafe to implement because the implementation must ensure +/// that `D`, `Self::OutDim`, `self.in_dim()`, and `self.out_ndim()` are +/// consistent with the `&[AxisSliceInfo]` returned by `self.as_ref()`. +pub unsafe trait CanSlice: AsRef<[AxisSliceInfo]> { + type OutDim: Dimension; + + fn in_ndim(&self) -> usize; + + fn out_ndim(&self) -> usize; +} + +macro_rules! impl_canslice_samedim { + ($in_dim:ty) => { + unsafe impl CanSlice<$in_dim> for SliceInfo + where + T: AsRef<[AxisSliceInfo]>, + Do: Dimension, + { + type OutDim = Do; + + fn in_ndim(&self) -> usize { + self.in_ndim() + } + + fn out_ndim(&self) -> usize { + self.out_ndim() + } + } + }; +} +impl_canslice_samedim!(Ix0); +impl_canslice_samedim!(Ix1); +impl_canslice_samedim!(Ix2); +impl_canslice_samedim!(Ix3); +impl_canslice_samedim!(Ix4); +impl_canslice_samedim!(Ix5); +impl_canslice_samedim!(Ix6); + +unsafe impl CanSlice for SliceInfo +where + T: AsRef<[AxisSliceInfo]>, + Di: Dimension, + Do: Dimension, +{ + type OutDim = Do; + + fn in_ndim(&self) -> usize { + self.in_ndim() + } + + fn out_ndim(&self) -> usize { + self.out_ndim() + } +} + /// Represents all of the necessary information to perform a slice. /// /// The type `T` is typically `[AxisSliceInfo; n]`, `[AxisSliceInfo]`, or -/// `Vec`. The type `D` is the output dimension after calling -/// [`.slice()`]. +/// `Vec`. The type `Di` is the dimension of the array to be +/// sliced, and `Do` is the output dimension after calling [`.slice()`]. Note +/// that if `Di` is a fixed dimension type (`Ix0`, `Ix1`, `Ix2`, etc.), the +/// `SliceInfo` instance can still be used to slice an array with dimension +/// `IxDyn` as long as the number of axes matches. /// /// [`.slice()`]: struct.ArrayBase.html#method.slice #[derive(Debug)] #[repr(C)] -pub struct SliceInfo { - out_dim: PhantomData, +pub struct SliceInfo { + in_dim: PhantomData, + out_dim: PhantomData, indices: T, } -impl Deref for SliceInfo +impl Deref for SliceInfo where - D: Dimension, + Di: Dimension, + Do: Dimension, { type Target = T; fn deref(&self) -> &Self::Target { @@ -300,55 +362,78 @@ where } } -impl SliceInfo +impl SliceInfo where - D: Dimension, + Di: Dimension, + Do: Dimension, { /// Returns a new `SliceInfo` instance. /// - /// If you call this method, you are guaranteeing that `out_dim` is - /// consistent with `indices`. + /// If you call this method, you are guaranteeing that `in_dim` and + /// `out_dim` are consistent with `indices`. #[doc(hidden)] - pub unsafe fn new_unchecked(indices: T, out_dim: PhantomData) -> SliceInfo { - SliceInfo { out_dim, indices } + pub unsafe fn new_unchecked( + indices: T, + in_dim: PhantomData, + out_dim: PhantomData, + ) -> SliceInfo { + SliceInfo { + in_dim: in_dim, + out_dim: out_dim, + indices: indices, + } } } -impl SliceInfo +impl SliceInfo where T: AsRef<[AxisSliceInfo]>, - D: Dimension, + Di: Dimension, + Do: Dimension, { /// Returns a new `SliceInfo` instance. /// - /// Errors if `D` is not consistent with `indices`. - pub fn new(indices: T) -> Result, ShapeError> { - if let Some(ndim) = D::NDIM { + /// Errors if `Di` or `Do` is not consistent with `indices`. + pub fn new(indices: T) -> Result, ShapeError> { + if let Some(ndim) = Di::NDIM { + if ndim != indices.as_ref().len() { + return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); + } + } + if let Some(ndim) = Do::NDIM { if ndim != indices.as_ref().iter().filter(|s| s.is_slice()).count() { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); } } Ok(SliceInfo { + in_dim: PhantomData, out_dim: PhantomData, indices, }) } } -impl SliceInfo +impl SliceInfo where T: AsRef<[AxisSliceInfo]>, - D: Dimension, + Di: Dimension, + Do: Dimension, { + /// Returns the number of dimensions of the input array for + /// [`.slice()`](struct.ArrayBase.html#method.slice). + pub fn in_ndim(&self) -> usize { + Di::NDIM.unwrap_or_else(|| self.indices.as_ref().len()) + } + /// Returns the number of dimensions after calling /// [`.slice()`](struct.ArrayBase.html#method.slice) (including taking /// subviews). /// - /// If `D` is a fixed-size dimension type, then this is equivalent to - /// `D::NDIM.unwrap()`. Otherwise, the value is calculated by iterating - /// over the ranges/indices. + /// If `Do` is a fixed-size dimension type, then this is equivalent to + /// `Do::NDIM.unwrap()`. Otherwise, the value is calculated by iterating + /// over the `AxisSliceInfo` elements. pub fn out_ndim(&self) -> usize { - D::NDIM.unwrap_or_else(|| { + Do::NDIM.unwrap_or_else(|| { self.indices .as_ref() .iter() @@ -358,47 +443,52 @@ where } } -impl AsRef<[AxisSliceInfo]> for SliceInfo +impl AsRef<[AxisSliceInfo]> for SliceInfo where T: AsRef<[AxisSliceInfo]>, - D: Dimension, + Di: Dimension, + Do: Dimension, { fn as_ref(&self) -> &[AxisSliceInfo] { self.indices.as_ref() } } -impl AsRef> for SliceInfo +impl AsRef> for SliceInfo where T: AsRef<[AxisSliceInfo]>, - D: Dimension, + Di: Dimension, + Do: Dimension, { - fn as_ref(&self) -> &SliceInfo<[AxisSliceInfo], D> { + fn as_ref(&self) -> &SliceInfo<[AxisSliceInfo], Di, Do> { unsafe { // This is okay because the only non-zero-sized member of - // `SliceInfo` is `indices`, so `&SliceInfo<[AxisSliceInfo], D>` + // `SliceInfo` is `indices`, so `&SliceInfo<[AxisSliceInfo], Di, Do>` // should have the same bitwise representation as // `&[AxisSliceInfo]`. &*(self.indices.as_ref() as *const [AxisSliceInfo] - as *const SliceInfo<[AxisSliceInfo], D>) + as *const SliceInfo<[AxisSliceInfo], Di, Do>) } } } -impl Copy for SliceInfo +impl Copy for SliceInfo where T: Copy, - D: Dimension, + Di: Dimension, + Do: Dimension, { } -impl Clone for SliceInfo +impl Clone for SliceInfo where T: Clone, - D: Dimension, + Di: Dimension, + Do: Dimension, { fn clone(&self) -> Self { SliceInfo { + in_dim: PhantomData, out_dim: PhantomData, indices: self.indices.clone(), } @@ -406,39 +496,64 @@ where } #[doc(hidden)] -pub trait SliceNextDim { +pub trait SliceNextInDim { + fn next_dim(&self, _: PhantomData) -> PhantomData; +} + +macro_rules! impl_slicenextindim_larger { + (($($generics:tt)*), $self:ty) => { + impl SliceNextInDim for $self { + fn next_dim(&self, _: PhantomData) -> PhantomData { + PhantomData + } + } + } +} +impl_slicenextindim_larger!((), isize); +impl_slicenextindim_larger!((), usize); +impl_slicenextindim_larger!((), i32); +impl_slicenextindim_larger!((T), Range); +impl_slicenextindim_larger!((T), RangeInclusive); +impl_slicenextindim_larger!((T), RangeFrom); +impl_slicenextindim_larger!((T), RangeTo); +impl_slicenextindim_larger!((T), RangeToInclusive); +impl_slicenextindim_larger!((), RangeFull); +impl_slicenextindim_larger!((), Slice); + +#[doc(hidden)] +pub trait SliceNextOutDim { fn next_dim(&self, _: PhantomData) -> PhantomData; } -macro_rules! impl_slicenextdim_equal { +macro_rules! impl_slicenextoutdim_equal { ($self:ty) => { - impl SliceNextDim for $self { + impl SliceNextOutDim for $self { fn next_dim(&self, _: PhantomData) -> PhantomData { PhantomData } } }; } -impl_slicenextdim_equal!(isize); -impl_slicenextdim_equal!(usize); -impl_slicenextdim_equal!(i32); +impl_slicenextoutdim_equal!(isize); +impl_slicenextoutdim_equal!(usize); +impl_slicenextoutdim_equal!(i32); -macro_rules! impl_slicenextdim_larger { +macro_rules! impl_slicenextoutdim_larger { (($($generics:tt)*), $self:ty) => { - impl SliceNextDim for $self { + impl SliceNextOutDim for $self { fn next_dim(&self, _: PhantomData) -> PhantomData { PhantomData } } } } -impl_slicenextdim_larger!((T), Range); -impl_slicenextdim_larger!((T), RangeInclusive); -impl_slicenextdim_larger!((T), RangeFrom); -impl_slicenextdim_larger!((T), RangeTo); -impl_slicenextdim_larger!((T), RangeToInclusive); -impl_slicenextdim_larger!((), RangeFull); -impl_slicenextdim_larger!((), Slice); +impl_slicenextoutdim_larger!((T), Range); +impl_slicenextoutdim_larger!((T), RangeInclusive); +impl_slicenextoutdim_larger!((T), RangeFrom); +impl_slicenextoutdim_larger!((T), RangeTo); +impl_slicenextoutdim_larger!((T), RangeToInclusive); +impl_slicenextoutdim_larger!((), RangeFull); +impl_slicenextoutdim_larger!((), Slice); /// Slice argument constructor. /// @@ -534,14 +649,16 @@ impl_slicenextdim_larger!((), Slice); #[macro_export] macro_rules! s( // convert a..b;c into @convert(a..b, c), final item - (@parse $dim:expr, [$($stack:tt)*] $r:expr;$s:expr) => { + (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr;$s:expr) => { match $r { r => { - let out_dim = $crate::SliceNextDim::next_dim(&r, $dim); + let in_dim = $crate::SliceNextInDim::next_dim(&r, $in_dim); + let out_dim = $crate::SliceNextOutDim::next_dim(&r, $out_dim); #[allow(unsafe_code)] unsafe { $crate::SliceInfo::new_unchecked( [$($stack)* $crate::s!(@convert r, $s)], + in_dim, out_dim, ) } @@ -549,14 +666,16 @@ macro_rules! s( } }; // convert a..b into @convert(a..b), final item - (@parse $dim:expr, [$($stack:tt)*] $r:expr) => { + (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr) => { match $r { r => { - let out_dim = $crate::SliceNextDim::next_dim(&r, $dim); + let in_dim = $crate::SliceNextInDim::next_dim(&r, $in_dim); + let out_dim = $crate::SliceNextOutDim::next_dim(&r, $out_dim); #[allow(unsafe_code)] unsafe { $crate::SliceInfo::new_unchecked( [$($stack)* $crate::s!(@convert r)], + in_dim, out_dim, ) } @@ -564,19 +683,20 @@ macro_rules! s( } }; // convert a..b;c into @convert(a..b, c), final item, trailing comma - (@parse $dim:expr, [$($stack:tt)*] $r:expr;$s:expr ,) => { - $crate::s![@parse $dim, [$($stack)*] $r;$s] + (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr;$s:expr ,) => { + $crate::s![@parse $in_dim, $out_dim, [$($stack)*] $r;$s] }; // convert a..b into @convert(a..b), final item, trailing comma - (@parse $dim:expr, [$($stack:tt)*] $r:expr ,) => { - $crate::s![@parse $dim, [$($stack)*] $r] + (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr ,) => { + $crate::s![@parse $in_dim, $out_dim, [$($stack)*] $r] }; // convert a..b;c into @convert(a..b, c) - (@parse $dim:expr, [$($stack:tt)*] $r:expr;$s:expr, $($t:tt)*) => { + (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr;$s:expr, $($t:tt)*) => { match $r { r => { $crate::s![@parse - $crate::SliceNextDim::next_dim(&r, $dim), + $crate::SliceNextInDim::next_dim(&r, $in_dim), + $crate::SliceNextOutDim::next_dim(&r, $out_dim), [$($stack)* $crate::s!(@convert r, $s),] $($t)* ] @@ -584,11 +704,12 @@ macro_rules! s( } }; // convert a..b into @convert(a..b) - (@parse $dim:expr, [$($stack:tt)*] $r:expr, $($t:tt)*) => { + (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr, $($t:tt)*) => { match $r { r => { $crate::s![@parse - $crate::SliceNextDim::next_dim(&r, $dim), + $crate::SliceNextInDim::next_dim(&r, $in_dim), + $crate::SliceNextOutDim::next_dim(&r, $out_dim), [$($stack)* $crate::s!(@convert r),] $($t)* ] @@ -596,11 +717,15 @@ macro_rules! s( } }; // empty call, i.e. `s![]` - (@parse ::std::marker::PhantomData::<$crate::Ix0>, []) => { + (@parse ::std::marker::PhantomData::<$crate::Ix0>, ::std::marker::PhantomData::<$crate::Ix0>, []) => { { #[allow(unsafe_code)] unsafe { - $crate::SliceInfo::new_unchecked([], ::std::marker::PhantomData::<$crate::Ix0>) + $crate::SliceInfo::new_unchecked( + [], + ::std::marker::PhantomData::<$crate::Ix0>, + ::std::marker::PhantomData::<$crate::Ix0>, + ) } } }; @@ -617,7 +742,12 @@ macro_rules! s( ($($t:tt)*) => { // The extra `*&` is a workaround for this compiler bug: // https://github.com/rust-lang/rust/issues/23014 - &*&$crate::s![@parse ::std::marker::PhantomData::<$crate::Ix0>, [] $($t)*] + &*&$crate::s![@parse + ::std::marker::PhantomData::<$crate::Ix0>, + ::std::marker::PhantomData::<$crate::Ix0>, + [] + $($t)* + ] }; ); @@ -650,13 +780,13 @@ where fn multi_slice_move(&self, _view: ArrayViewMut<'a, A, D>) -> Self::Output {} } -impl<'a, A, D, Do0> MultiSlice<'a, A, D> for (&SliceInfo,) +impl<'a, A, D, I0> MultiSlice<'a, A, D> for (&I0,) where A: 'a, D: Dimension, - Do0: Dimension, + I0: CanSlice, { - type Output = (ArrayViewMut<'a, A, Do0>,); + type Output = (ArrayViewMut<'a, A, I0::OutDim>,); fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output { (view.slice_move(self.0),) @@ -668,17 +798,17 @@ macro_rules! impl_multislice_tuple { impl_multislice_tuple!(@def_impl ($($but_last,)* $last,), [$($but_last)*] $last); }; (@def_impl ($($all:ident,)*), [$($but_last:ident)*] $last:ident) => { - impl<'a, A, D, $($all,)*> MultiSlice<'a, A, D> for ($(&SliceInfo,)*) + impl<'a, A, D, $($all,)*> MultiSlice<'a, A, D> for ($(&$all,)*) where A: 'a, D: Dimension, - $($all: Dimension,)* + $($all: CanSlice,)* { - type Output = ($(ArrayViewMut<'a, A, $all>,)*); + type Output = ($(ArrayViewMut<'a, A, $all::OutDim>,)*); fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output { #[allow(non_snake_case)] - let ($($all,)*) = self; + let &($($all,)*) = self; let shape = view.raw_dim(); assert!(!impl_multislice_tuple!(@intersects_self &shape, ($($all,)*))); @@ -702,11 +832,11 @@ macro_rules! impl_multislice_tuple { }; } -impl_multislice_tuple!([Do0] Do1); -impl_multislice_tuple!([Do0 Do1] Do2); -impl_multislice_tuple!([Do0 Do1 Do2] Do3); -impl_multislice_tuple!([Do0 Do1 Do2 Do3] Do4); -impl_multislice_tuple!([Do0 Do1 Do2 Do3 Do4] Do5); +impl_multislice_tuple!([I0] I1); +impl_multislice_tuple!([I0 I1] I2); +impl_multislice_tuple!([I0 I1 I2] I3); +impl_multislice_tuple!([I0 I1 I2 I3] I4); +impl_multislice_tuple!([I0 I1 I2 I3 I4] I5); impl<'a, A, D, T> MultiSlice<'a, A, D> for &T where diff --git a/tests/array.rs b/tests/array.rs index 4b30c15b4..3da6ba1f7 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -210,13 +210,13 @@ fn test_slice_dyninput_array_fixed() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info.as_ref()); + arr.view().slice_collapse(info); } #[test] fn test_slice_array_dyn() { let mut arr = Array3::::zeros((5, 2, 5)); - let info = &SliceInfo::<_, IxDyn>::new([ + let info = &SliceInfo::<_, Ix3, IxDyn>::new([ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), AxisSliceInfo::from(..).step_by(2), @@ -231,7 +231,7 @@ fn test_slice_array_dyn() { #[test] fn test_slice_dyninput_array_dyn() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); - let info = &SliceInfo::<_, IxDyn>::new([ + let info = &SliceInfo::<_, Ix3, IxDyn>::new([ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), AxisSliceInfo::from(..).step_by(2), @@ -240,37 +240,37 @@ fn test_slice_dyninput_array_dyn() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info.as_ref()); + arr.view().slice_collapse(info); } #[test] fn test_slice_dyninput_vec_fixed() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); - let info = &SliceInfo::<_, Ix2>::new(vec![ + let info = &SliceInfo::<_, Ix3, Ix2>::new(vec![ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), AxisSliceInfo::from(..).step_by(2), ]) .unwrap(); - arr.slice(info.as_ref()); - arr.slice_mut(info.as_ref()); - arr.view().slice_move(info.as_ref()); - arr.view().slice_collapse(info.as_ref()); + arr.slice(info); + arr.slice_mut(info); + arr.view().slice_move(info); + arr.view().slice_collapse(info); } #[test] fn test_slice_dyninput_vec_dyn() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); - let info = &SliceInfo::<_, IxDyn>::new(vec![ + let info = &SliceInfo::<_, Ix3, IxDyn>::new(vec![ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), AxisSliceInfo::from(..).step_by(2), ]) .unwrap(); - arr.slice(info.as_ref()); - arr.slice_mut(info.as_ref()); - arr.view().slice_move(info.as_ref()); - arr.view().slice_collapse(info.as_ref()); + arr.slice(info); + arr.slice_mut(info); + arr.view().slice_move(info); + arr.view().slice_collapse(info); } #[test] diff --git a/tests/oper.rs b/tests/oper.rs index 22dc6603e..16f3edbc6 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -595,7 +595,7 @@ fn scaled_add_3() { { let mut av = a.slice_mut(s![..;s1, ..;s2]); - let c = c.slice(SliceInfo::<_, IxDyn>::new(cslice).unwrap().as_ref()); + let c = c.slice(&SliceInfo::<_, IxDyn, IxDyn>::new(cslice).unwrap()); let mut answerv = answer.slice_mut(s![..;s1, ..;s2]); answerv += &(beta * &c); From 546b69cd6e3cae7509716b1aa692daf6eb0a9ce4 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 9 Dec 2018 16:53:27 -0500 Subject: [PATCH 211/651] Add support for inserting new axes while slicing --- src/dimension/mod.rs | 51 +++++++++++--- src/doc/ndarray_for_numpy_users/mod.rs | 2 +- src/impl_methods.rs | 22 ++++-- src/lib.rs | 23 ++++--- src/prelude.rs | 3 + src/slice.rs | 93 +++++++++++++++++++------- tests/array.rs | 48 +++++++------ 7 files changed, 173 insertions(+), 69 deletions(-) diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index a1e245884..28bdc7f91 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -601,7 +601,11 @@ pub fn slices_intersect( indices2: &impl CanSlice, ) -> bool { debug_assert_eq!(indices1.in_ndim(), indices2.in_ndim()); - for (&axis_len, &si1, &si2) in izip!(dim.slice(), indices1.as_ref(), indices2.as_ref()) { + for (&axis_len, &si1, &si2) in izip!( + dim.slice(), + indices1.as_ref().iter().filter(|si| !si.is_new_axis()), + indices2.as_ref().iter().filter(|si| !si.is_new_axis()), + ) { // The slices do not intersect iff any pair of `AxisSliceInfo` does not intersect. match (si1, si2) { ( @@ -649,6 +653,7 @@ pub fn slices_intersect( return false; } } + (AxisSliceInfo::NewAxis, _) | (_, AxisSliceInfo::NewAxis) => unreachable!(), } } true @@ -720,7 +725,7 @@ mod test { }; use crate::error::{from_kind, ErrorKind}; use crate::slice::Slice; - use crate::{Dim, Dimension, Ix0, Ix1, Ix2, Ix3, IxDyn}; + use crate::{Dim, Dimension, Ix0, Ix1, Ix2, Ix3, IxDyn, NewAxis}; use num_integer::gcd; use quickcheck::{quickcheck, TestResult}; @@ -994,17 +999,45 @@ mod test { #[test] fn slices_intersect_true() { - assert!(slices_intersect(&Dim([4, 5]), s![.., ..], s![.., ..])); - assert!(slices_intersect(&Dim([4, 5]), s![0, ..], s![0, ..])); - assert!(slices_intersect(&Dim([4, 5]), s![..;2, ..], s![..;3, ..])); - assert!(slices_intersect(&Dim([4, 5]), s![.., ..;2], s![.., 1..;3])); + assert!(slices_intersect( + &Dim([4, 5]), + s![NewAxis, .., NewAxis, ..], + s![.., NewAxis, .., NewAxis] + )); + assert!(slices_intersect( + &Dim([4, 5]), + s![NewAxis, 0, ..], + s![0, ..] + )); + assert!(slices_intersect( + &Dim([4, 5]), + s![..;2, ..], + s![..;3, NewAxis, ..] + )); + assert!(slices_intersect( + &Dim([4, 5]), + s![.., ..;2], + s![.., 1..;3, NewAxis] + )); assert!(slices_intersect(&Dim([4, 10]), s![.., ..;9], s![.., 3..;6])); } #[test] fn slices_intersect_false() { - assert!(!slices_intersect(&Dim([4, 5]), s![..;2, ..], s![1..;2, ..])); - assert!(!slices_intersect(&Dim([4, 5]), s![..;2, ..], s![1..;3, ..])); - assert!(!slices_intersect(&Dim([4, 5]), s![.., ..;9], s![.., 3..;6])); + assert!(!slices_intersect( + &Dim([4, 5]), + s![..;2, ..], + s![NewAxis, 1..;2, ..] + )); + assert!(!slices_intersect( + &Dim([4, 5]), + s![..;2, NewAxis, ..], + s![1..;3, ..] + )); + assert!(!slices_intersect( + &Dim([4, 5]), + s![.., ..;9], + s![.., 3..;6, NewAxis] + )); } } diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index aea97da3f..478cc2cec 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -532,7 +532,7 @@ //! `a[:] = b` | [`a.assign(&b)`][.assign()] | copy the data from array `b` into array `a` //! `np.concatenate((a,b), axis=1)` | [`concatenate![Axis(1), a, b]`][concatenate!] or [`concatenate(Axis(1), &[a.view(), b.view()])`][concatenate()] | concatenate arrays `a` and `b` along axis 1 //! `np.stack((a,b), axis=1)` | [`stack![Axis(1), a, b]`][stack!] or [`stack(Axis(1), vec![a.view(), b.view()])`][stack()] | stack arrays `a` and `b` along axis 1 -//! `a[:,np.newaxis]` or `np.expand_dims(a, axis=1)` | [`a.insert_axis(Axis(1))`][.insert_axis()] | create an array from `a`, inserting a new axis 1 +//! `a[:,np.newaxis]` or `np.expand_dims(a, axis=1)` | [`a.slice(s![.., NewAxis])`][.slice()] or [`a.insert_axis(Axis(1))`][.insert_axis()] | create an view of 1-D array `a`, inserting a new axis 1 //! `a.transpose()` or `a.T` | [`a.t()`][.t()] or [`a.reversed_axes()`][.reversed_axes()] | transpose of array `a` (view for `.t()` or by-move for `.reversed_axes()`) //! `np.diag(a)` | [`a.diag()`][.diag()] | view the diagonal of `a` //! `a.flatten()` | [`use std::iter::FromIterator; Array::from_iter(a.iter().cloned())`][::from_iter()] | create a 1-D array by flattening `a` diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 2cbfcacf2..143824850 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -437,6 +437,12 @@ where // Skip the old axis since it should be removed. old_axis += 1; } + AxisSliceInfo::NewAxis => { + // Set the dim and stride of the new axis. + new_dim[new_axis] = 1; + new_strides[new_axis] = 0; + new_axis += 1; + } }); debug_assert_eq!(old_axis, self.ndim()); debug_assert_eq!(new_axis, out_ndim); @@ -450,6 +456,8 @@ where /// Slice the array in place without changing the number of dimensions. /// + /// Note that `NewAxis` elements in `info` are ignored. + /// /// See [*Slicing*](#slicing) for full documentation. /// /// **Panics** if an index is out of bounds or step size is zero.
@@ -463,18 +471,20 @@ where self.ndim(), "The input dimension of `info` must match the array to be sliced.", ); - info.as_ref() - .iter() - .enumerate() - .for_each(|(axis, &ax_info)| match ax_info { + let mut axis = 0; + info.as_ref().iter().for_each(|&ax_info| match ax_info { AxisSliceInfo::Slice { start, end, step } => { - self.slice_axis_inplace(Axis(axis), Slice { start, end, step }) + self.slice_axis_inplace(Axis(axis), Slice { start, end, step }); + axis += 1; } AxisSliceInfo::Index(index) => { let i_usize = abs_index(self.len_of(Axis(axis)), index); - self.collapse_axis(Axis(axis), i_usize) + self.collapse_axis(Axis(axis), i_usize); + axis += 1; } + AxisSliceInfo::NewAxis => {} }); + debug_assert_eq!(axis, self.ndim()); } /// Return a view of the array, sliced along the specified axis. diff --git a/src/lib.rs b/src/lib.rs index 7c9439411..ae750f3d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -141,7 +141,7 @@ pub use crate::dimension::IxDynImpl; pub use crate::dimension::NdIndex; pub use crate::error::{ErrorKind, ShapeError}; pub use crate::indexes::{indices, indices_of}; -pub use crate::slice::{AxisSliceInfo, Slice, SliceInfo, SliceNextInDim, SliceNextOutDim}; +pub use crate::slice::{AxisSliceInfo, NewAxis, Slice, SliceInfo, SliceNextInDim, SliceNextOutDim}; use crate::iterators::Baseiter; use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes}; @@ -496,14 +496,16 @@ pub type Ixs = isize; /// /// If a range is used, the axis is preserved. If an index is used, that index /// is selected and the axis is removed; this selects a subview. See -/// [*Subviews*](#subviews) for more information about subviews. Note that -/// [`.slice_collapse()`] behaves like [`.collapse_axis()`] by preserving -/// the number of dimensions. +/// [*Subviews*](#subviews) for more information about subviews. If a +/// [`NewAxis`] instance is used, a new axis is inserted. Note that +/// [`.slice_collapse()`] ignores `NewAxis` elements and behaves like +/// [`.collapse_axis()`] by preserving the number of dimensions. /// /// [`.slice()`]: #method.slice /// [`.slice_mut()`]: #method.slice_mut /// [`.slice_move()`]: #method.slice_move /// [`.slice_collapse()`]: #method.slice_collapse +/// [`NewAxis`]: struct.NewAxis.html /// /// When slicing arrays with generic dimensionality, creating an instance of /// [`&SliceInfo`] to pass to the multi-axis slicing methods like [`.slice()`] @@ -526,7 +528,7 @@ pub type Ixs = isize; /// [`.multi_slice_move()`]: type.ArrayViewMut.html#method.multi_slice_move /// /// ``` -/// use ndarray::{arr2, arr3, s, ArrayBase, DataMut, Dimension, Slice}; +/// use ndarray::{arr2, arr3, s, ArrayBase, DataMut, Dimension, NewAxis, Slice}; /// /// // 2 submatrices of 2 rows with 3 elements per row, means a shape of `[2, 2, 3]`. /// @@ -561,16 +563,17 @@ pub type Ixs = isize; /// assert_eq!(d, e); /// assert_eq!(d.shape(), &[2, 1, 3]); /// -/// // Let’s create a slice while selecting a subview with +/// // Let’s create a slice while selecting a subview and inserting a new axis with /// // /// // - Both submatrices of the greatest dimension: `..` /// // - The last row in each submatrix, removing that axis: `-1` /// // - Row elements in reverse order: `..;-1` -/// let f = a.slice(s![.., -1, ..;-1]); -/// let g = arr2(&[[ 6, 5, 4], -/// [12, 11, 10]]); +/// // - A new axis at the end. +/// let f = a.slice(s![.., -1, ..;-1, NewAxis]); +/// let g = arr3(&[[ [6], [5], [4]], +/// [[12], [11], [10]]]); /// assert_eq!(f, g); -/// assert_eq!(f.shape(), &[2, 3]); +/// assert_eq!(f.shape(), &[2, 3, 1]); /// /// // Let's take two disjoint, mutable slices of a matrix with /// // diff --git a/src/prelude.rs b/src/prelude.rs index def236841..ea6dfb08f 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -49,6 +49,9 @@ pub use crate::{array, azip, s}; #[doc(no_inline)] pub use crate::ShapeBuilder; +#[doc(no_inline)] +pub use crate::NewAxis; + #[doc(no_inline)] pub use crate::AsArray; diff --git a/src/slice.rs b/src/slice.rs index dc4ad9d7a..5e2a9ac7f 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -67,7 +67,12 @@ impl Slice { } } -/// A slice (range with step) or an index. +/// Token to represent a new axis in a slice description. +/// +/// See also the [`s![]`](macro.s!.html) macro. +pub struct NewAxis; + +/// A slice (range with step), an index, or a new axis token. /// /// See also the [`s![]`](macro.s!.html) macro for a convenient way to create a /// `&SliceInfo<[AxisSliceInfo; n], Di, Do>`. @@ -91,6 +96,10 @@ impl Slice { /// from `a` until the end, in reverse order. It can also be created with /// `AxisSliceInfo::from(a..).step_by(-1)`. The Python equivalent is `[a::-1]`. /// The macro equivalent is `s![a..;-1]`. +/// +/// `AxisSliceInfo::NewAxis` is a new axis of length 1. It can also be created +/// with `AxisSliceInfo::from(NewAxis)`. The Python equivalent is +/// `[np.newaxis]`. The macro equivalent is `s![NewAxis]`. #[derive(Debug, PartialEq, Eq, Hash)] pub enum AxisSliceInfo { /// A range with step size. `end` is an exclusive index. Negative `begin` @@ -103,6 +112,8 @@ pub enum AxisSliceInfo { }, /// A single index. Index(isize), + /// A new axis of length 1. + NewAxis, } copy_and_clone! {AxisSliceInfo} @@ -118,6 +129,11 @@ impl AxisSliceInfo { matches!(self, AxisSliceInfo::Index(_)) } + /// Returns `true` if `self` is a `NewAxis` value. + pub fn is_new_axis(&self) -> bool { + matches!(self, AxisSliceInfo::NewAxis) + } + /// Returns a new `AxisSliceInfo` with the given step size (multiplied with /// the previous step size). /// @@ -137,6 +153,7 @@ impl AxisSliceInfo { step: orig_step * step, }, AxisSliceInfo::Index(s) => AxisSliceInfo::Index(s), + AxisSliceInfo::NewAxis => AxisSliceInfo::NewAxis, } } } @@ -157,6 +174,7 @@ impl fmt::Display for AxisSliceInfo { write!(f, ";{}", step)?; } } + AxisSliceInfo::NewAxis => write!(f, "NewAxis")?, } Ok(()) } @@ -276,6 +294,13 @@ impl_axissliceinfo_from_index!(isize); impl_axissliceinfo_from_index!(usize); impl_axissliceinfo_from_index!(i32); +impl From for AxisSliceInfo { + #[inline] + fn from(_: NewAxis) -> AxisSliceInfo { + AxisSliceInfo::NewAxis + } +} + /// A type that can slice an array of dimension `D`. /// /// This trait is unsafe to implement because the implementation must ensure @@ -396,12 +421,12 @@ where /// Errors if `Di` or `Do` is not consistent with `indices`. pub fn new(indices: T) -> Result, ShapeError> { if let Some(ndim) = Di::NDIM { - if ndim != indices.as_ref().len() { + if ndim != indices.as_ref().iter().filter(|s| !s.is_new_axis()).count() { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); } } if let Some(ndim) = Do::NDIM { - if ndim != indices.as_ref().iter().filter(|s| s.is_slice()).count() { + if ndim != indices.as_ref().iter().filter(|s| !s.is_index()).count() { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); } } @@ -421,8 +446,18 @@ where { /// Returns the number of dimensions of the input array for /// [`.slice()`](struct.ArrayBase.html#method.slice). + /// + /// If `Di` is a fixed-size dimension type, then this is equivalent to + /// `Di::NDIM.unwrap()`. Otherwise, the value is calculated by iterating + /// over the `AxisSliceInfo` elements. pub fn in_ndim(&self) -> usize { - Di::NDIM.unwrap_or_else(|| self.indices.as_ref().len()) + Di::NDIM.unwrap_or_else(|| { + self.indices + .as_ref() + .iter() + .filter(|s| !s.is_new_axis()) + .count() + }) } /// Returns the number of dimensions after calling @@ -437,7 +472,7 @@ where self.indices .as_ref() .iter() - .filter(|s| s.is_slice()) + .filter(|s| !s.is_index()) .count() }) } @@ -500,6 +535,12 @@ pub trait SliceNextInDim { fn next_dim(&self, _: PhantomData) -> PhantomData; } +impl SliceNextInDim for NewAxis { + fn next_dim(&self, _: PhantomData) -> PhantomData { + PhantomData + } +} + macro_rules! impl_slicenextindim_larger { (($($generics:tt)*), $self:ty) => { impl SliceNextInDim for $self { @@ -554,12 +595,13 @@ impl_slicenextoutdim_larger!((T), RangeTo); impl_slicenextoutdim_larger!((T), RangeToInclusive); impl_slicenextoutdim_larger!((), RangeFull); impl_slicenextoutdim_larger!((), Slice); +impl_slicenextoutdim_larger!((), NewAxis); /// Slice argument constructor. /// -/// `s![]` takes a list of ranges/slices/indices, separated by comma, with -/// optional step sizes that are separated from the range by a semicolon. It is -/// converted into a [`&SliceInfo`] instance. +/// `s![]` takes a list of ranges/slices/indices/new-axes, separated by comma, +/// with optional step sizes that are separated from the range by a semicolon. +/// It is converted into a [`&SliceInfo`] instance. /// /// [`&SliceInfo`]: struct.SliceInfo.html /// @@ -578,22 +620,25 @@ impl_slicenextoutdim_larger!((), Slice); /// * *slice*: a [`Slice`] instance to use for slicing that axis. /// * *slice* `;` *step*: a range constructed from the start and end of a [`Slice`] /// instance, with new step size *step*, to use for slicing that axis. +/// * *new-axis*: a [`NewAxis`] instance that represents the creation of a new axis. /// /// [`Slice`]: struct.Slice.html -/// -/// The number of *axis-slice-info* must match the number of axes in the array. -/// *index*, *range*, *slice*, and *step* can be expressions. *index* must be -/// of type `isize`, `usize`, or `i32`. *range* must be of type `Range`, -/// `RangeTo`, `RangeFrom`, or `RangeFull` where `I` is `isize`, `usize`, -/// or `i32`. *step* must be a type that can be converted to `isize` with the -/// `as` keyword. -/// -/// For example `s![0..4;2, 6, 1..5]` is a slice of the first axis for 0..4 -/// with step size 2, a subview of the second axis at index 6, and a slice of -/// the third axis for 1..5 with default step size 1. The input array must have -/// 3 dimensions. The resulting slice would have shape `[2, 4]` for -/// [`.slice()`], [`.slice_mut()`], and [`.slice_move()`], and shape -/// `[2, 1, 4]` for [`.slice_collapse()`]. +/// [`NewAxis`]: struct.NewAxis.html +/// +/// The number of *axis-slice-info*, not including *new-axis*, must match the +/// number of axes in the array. *index*, *range*, *slice*, *step*, and +/// *new-axis* can be expressions. *index* must be of type `isize`, `usize`, or +/// `i32`. *range* must be of type `Range`, `RangeTo`, `RangeFrom`, or +/// `RangeFull` where `I` is `isize`, `usize`, or `i32`. *step* must be a type +/// that can be converted to `isize` with the `as` keyword. +/// +/// For example `s![0..4;2, 6, 1..5, NewAxis]` is a slice of the first axis for +/// 0..4 with step size 2, a subview of the second axis at index 6, a slice of +/// the third axis for 1..5 with default step size 1, and a new axis of length +/// 1 at the end of the shape. The input array must have 3 dimensions. The +/// resulting slice would have shape `[2, 4, 1]` for [`.slice()`], +/// [`.slice_mut()`], and [`.slice_move()`], and shape `[2, 1, 4]` for +/// [`.slice_collapse()`]. /// /// [`.slice()`]: struct.ArrayBase.html#method.slice /// [`.slice_mut()`]: struct.ArrayBase.html#method.slice_mut @@ -731,11 +776,11 @@ macro_rules! s( }; // Catch-all clause for syntax errors (@parse $($t:tt)*) => { compile_error!("Invalid syntax in s![] call.") }; - // convert range/index into AxisSliceInfo + // convert range/index/new-axis into AxisSliceInfo (@convert $r:expr) => { <$crate::AxisSliceInfo as ::std::convert::From<_>>::from($r) }; - // convert range/index and step into AxisSliceInfo + // convert range/index/new-axis and step into AxisSliceInfo (@convert $r:expr, $s:expr) => { <$crate::AxisSliceInfo as ::std::convert::From<_>>::from($r).step_by($s as isize) }; diff --git a/tests/array.rs b/tests/array.rs index 3da6ba1f7..51f59fcb1 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -85,8 +85,8 @@ fn test_slice() { *elt = i; } - let vi = A.slice(s![1.., ..;2, Slice::new(0, None, 2)]); - assert_eq!(vi.shape(), &[2, 2, 3]); + let vi = A.slice(s![1.., ..;2, NewAxis, Slice::new(0, None, 2)]); + assert_eq!(vi.shape(), &[2, 2, 1, 3]); let vi = A.slice(s![.., .., ..]); assert_eq!(vi.shape(), A.shape()); assert!(vi.iter().zip(A.iter()).all(|(a, b)| a == b)); @@ -138,8 +138,8 @@ fn test_slice_with_many_dim() { *elt = i; } - let vi = A.slice(s![..2, .., ..;2, ..1, ..1, 1.., ..]); - let new_shape = &[2, 1, 2, 1, 1, 1, 1][..]; + let vi = A.slice(s![..2, NewAxis, .., ..;2, NewAxis, ..1, ..1, 1.., ..]); + let new_shape = &[2, 1, 1, 2, 1, 1, 1, 1, 1][..]; assert_eq!(vi.shape(), new_shape); let correct = array![ [A[&[0, 0, 0, 0, 0, 1, 0][..]], A[&[0, 0, 2, 0, 0, 1, 0][..]]], @@ -196,7 +196,7 @@ fn test_slice_args_eval_step_once() { #[test] fn test_slice_array_fixed() { let mut arr = Array3::::zeros((5, 2, 5)); - let info = s![1.., 1, ..;2]; + let info = s![1.., 1, NewAxis, ..;2]; arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); @@ -206,7 +206,7 @@ fn test_slice_array_fixed() { #[test] fn test_slice_dyninput_array_fixed() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); - let info = s![1.., 1, ..;2]; + let info = s![1.., 1, NewAxis, ..;2]; arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); @@ -219,6 +219,7 @@ fn test_slice_array_dyn() { let info = &SliceInfo::<_, Ix3, IxDyn>::new([ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), + AxisSliceInfo::from(NewAxis), AxisSliceInfo::from(..).step_by(2), ]) .unwrap(); @@ -234,6 +235,7 @@ fn test_slice_dyninput_array_dyn() { let info = &SliceInfo::<_, Ix3, IxDyn>::new([ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), + AxisSliceInfo::from(NewAxis), AxisSliceInfo::from(..).step_by(2), ]) .unwrap(); @@ -246,9 +248,10 @@ fn test_slice_dyninput_array_dyn() { #[test] fn test_slice_dyninput_vec_fixed() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); - let info = &SliceInfo::<_, Ix3, Ix2>::new(vec![ + let info = &SliceInfo::<_, Ix3, Ix3>::new(vec![ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), + AxisSliceInfo::from(NewAxis), AxisSliceInfo::from(..).step_by(2), ]) .unwrap(); @@ -264,6 +267,7 @@ fn test_slice_dyninput_vec_dyn() { let info = &SliceInfo::<_, Ix3, IxDyn>::new(vec![ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), + AxisSliceInfo::from(NewAxis), AxisSliceInfo::from(..).step_by(2), ]) .unwrap(); @@ -274,27 +278,33 @@ fn test_slice_dyninput_vec_dyn() { } #[test] -fn test_slice_with_subview() { +fn test_slice_with_subview_and_new_axis() { let mut arr = ArcArray::::zeros((3, 5, 4)); for (i, elt) in arr.iter_mut().enumerate() { *elt = i; } - let vi = arr.slice(s![1.., 2, ..;2]); - assert_eq!(vi.shape(), &[2, 2]); + let vi = arr.slice(s![NewAxis, 1.., 2, ..;2]); + assert_eq!(vi.shape(), &[1, 2, 2]); assert!(vi .iter() - .zip(arr.index_axis(Axis(1), 2).slice(s![1.., ..;2]).iter()) + .zip( + arr.index_axis(Axis(1), 2) + .slice(s![1.., ..;2]) + .insert_axis(Axis(0)) + .iter() + ) .all(|(a, b)| a == b)); - let vi = arr.slice(s![1, 2, ..;2]); - assert_eq!(vi.shape(), &[2]); + let vi = arr.slice(s![1, NewAxis, 2, ..;2]); + assert_eq!(vi.shape(), &[1, 2]); assert!(vi .iter() .zip( arr.index_axis(Axis(0), 1) .index_axis(Axis(0), 2) .slice(s![..;2]) + .insert_axis(Axis(0)) .iter() ) .all(|(a, b)| a == b)); @@ -313,7 +323,7 @@ fn test_slice_collapse_with_indices() { { let mut vi = arr.view(); - vi.slice_collapse(s![1.., 2, ..;2]); + vi.slice_collapse(s![NewAxis, 1.., 2, ..;2]); assert_eq!(vi.shape(), &[2, 1, 2]); assert!(vi .iter() @@ -321,7 +331,7 @@ fn test_slice_collapse_with_indices() { .all(|(a, b)| a == b)); let mut vi = arr.view(); - vi.slice_collapse(s![1, 2, ..;2]); + vi.slice_collapse(s![1, NewAxis, 2, ..;2]); assert_eq!(vi.shape(), &[1, 1, 2]); assert!(vi .iter() @@ -329,7 +339,7 @@ fn test_slice_collapse_with_indices() { .all(|(a, b)| a == b)); let mut vi = arr.view(); - vi.slice_collapse(s![1, 2, 3]); + vi.slice_collapse(s![1, 2, NewAxis, 3]); assert_eq!(vi.shape(), &[1, 1, 1]); assert_eq!(vi, Array3::from_elem((1, 1, 1), arr[(1, 2, 3)])); } @@ -337,7 +347,7 @@ fn test_slice_collapse_with_indices() { // Do it to the ArcArray itself let elem = arr[(1, 2, 3)]; let mut vi = arr; - vi.slice_collapse(s![1, 2, 3]); + vi.slice_collapse(s![1, 2, 3, NewAxis]); assert_eq!(vi.shape(), &[1, 1, 1]); assert_eq!(vi, Array3::from_elem((1, 1, 1), elem)); } @@ -382,7 +392,7 @@ fn test_multislice() { fn test_multislice_intersecting() { assert_panics!({ let mut arr = Array2::::zeros((8, 6)); - arr.multi_slice_mut((s![3, ..], s![3, ..])); + arr.multi_slice_mut((s![3, .., NewAxis], s![3, ..])); }); assert_panics!({ let mut arr = Array2::::zeros((8, 6)); @@ -390,7 +400,7 @@ fn test_multislice_intersecting() { }); assert_panics!({ let mut arr = Array2::::zeros((8, 6)); - arr.multi_slice_mut((s![3, ..], s![..;3, ..])); + arr.multi_slice_mut((s![3, ..], s![..;3, NewAxis, ..])); }); assert_panics!({ let mut arr = Array2::::zeros((8, 6)); From 6e335ca21a1310ecfe084751ca70fc0f131296cc Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 16 Dec 2018 23:31:07 -0500 Subject: [PATCH 212/651] Rename SliceInfo generic params to Din and Dout --- src/slice.rs | 108 +++++++++++++++++++++++++-------------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/src/slice.rs b/src/slice.rs index 5e2a9ac7f..e0ba0df78 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -75,7 +75,7 @@ pub struct NewAxis; /// A slice (range with step), an index, or a new axis token. /// /// See also the [`s![]`](macro.s!.html) macro for a convenient way to create a -/// `&SliceInfo<[AxisSliceInfo; n], Di, Do>`. +/// `&SliceInfo<[AxisSliceInfo; n], Din, Dout>`. /// /// ## Examples /// @@ -316,12 +316,12 @@ pub unsafe trait CanSlice: AsRef<[AxisSliceInfo]> { macro_rules! impl_canslice_samedim { ($in_dim:ty) => { - unsafe impl CanSlice<$in_dim> for SliceInfo + unsafe impl CanSlice<$in_dim> for SliceInfo where T: AsRef<[AxisSliceInfo]>, - Do: Dimension, + Dout: Dimension, { - type OutDim = Do; + type OutDim = Dout; fn in_ndim(&self) -> usize { self.in_ndim() @@ -341,13 +341,13 @@ impl_canslice_samedim!(Ix4); impl_canslice_samedim!(Ix5); impl_canslice_samedim!(Ix6); -unsafe impl CanSlice for SliceInfo +unsafe impl CanSlice for SliceInfo where T: AsRef<[AxisSliceInfo]>, - Di: Dimension, - Do: Dimension, + Din: Dimension, + Dout: Dimension, { - type OutDim = Do; + type OutDim = Dout; fn in_ndim(&self) -> usize { self.in_ndim() @@ -361,25 +361,25 @@ where /// Represents all of the necessary information to perform a slice. /// /// The type `T` is typically `[AxisSliceInfo; n]`, `[AxisSliceInfo]`, or -/// `Vec`. The type `Di` is the dimension of the array to be -/// sliced, and `Do` is the output dimension after calling [`.slice()`]. Note -/// that if `Di` is a fixed dimension type (`Ix0`, `Ix1`, `Ix2`, etc.), the +/// `Vec`. The type `Din` is the dimension of the array to be +/// sliced, and `Dout` is the output dimension after calling [`.slice()`]. Note +/// that if `Din` is a fixed dimension type (`Ix0`, `Ix1`, `Ix2`, etc.), the /// `SliceInfo` instance can still be used to slice an array with dimension /// `IxDyn` as long as the number of axes matches. /// /// [`.slice()`]: struct.ArrayBase.html#method.slice #[derive(Debug)] #[repr(C)] -pub struct SliceInfo { - in_dim: PhantomData, - out_dim: PhantomData, +pub struct SliceInfo { + in_dim: PhantomData, + out_dim: PhantomData, indices: T, } -impl Deref for SliceInfo +impl Deref for SliceInfo where - Di: Dimension, - Do: Dimension, + Din: Dimension, + Dout: Dimension, { type Target = T; fn deref(&self) -> &Self::Target { @@ -387,10 +387,10 @@ where } } -impl SliceInfo +impl SliceInfo where - Di: Dimension, - Do: Dimension, + Din: Dimension, + Dout: Dimension, { /// Returns a new `SliceInfo` instance. /// @@ -399,9 +399,9 @@ where #[doc(hidden)] pub unsafe fn new_unchecked( indices: T, - in_dim: PhantomData, - out_dim: PhantomData, - ) -> SliceInfo { + in_dim: PhantomData, + out_dim: PhantomData, + ) -> SliceInfo { SliceInfo { in_dim: in_dim, out_dim: out_dim, @@ -410,22 +410,22 @@ where } } -impl SliceInfo +impl SliceInfo where T: AsRef<[AxisSliceInfo]>, - Di: Dimension, - Do: Dimension, + Din: Dimension, + Dout: Dimension, { /// Returns a new `SliceInfo` instance. /// - /// Errors if `Di` or `Do` is not consistent with `indices`. - pub fn new(indices: T) -> Result, ShapeError> { - if let Some(ndim) = Di::NDIM { + /// Errors if `Din` or `Dout` is not consistent with `indices`. + pub fn new(indices: T) -> Result, ShapeError> { + if let Some(ndim) = Din::NDIM { if ndim != indices.as_ref().iter().filter(|s| !s.is_new_axis()).count() { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); } } - if let Some(ndim) = Do::NDIM { + if let Some(ndim) = Dout::NDIM { if ndim != indices.as_ref().iter().filter(|s| !s.is_index()).count() { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); } @@ -438,20 +438,20 @@ where } } -impl SliceInfo +impl SliceInfo where T: AsRef<[AxisSliceInfo]>, - Di: Dimension, - Do: Dimension, + Din: Dimension, + Dout: Dimension, { /// Returns the number of dimensions of the input array for /// [`.slice()`](struct.ArrayBase.html#method.slice). /// - /// If `Di` is a fixed-size dimension type, then this is equivalent to - /// `Di::NDIM.unwrap()`. Otherwise, the value is calculated by iterating + /// If `Din` is a fixed-size dimension type, then this is equivalent to + /// `Din::NDIM.unwrap()`. Otherwise, the value is calculated by iterating /// over the `AxisSliceInfo` elements. pub fn in_ndim(&self) -> usize { - Di::NDIM.unwrap_or_else(|| { + Din::NDIM.unwrap_or_else(|| { self.indices .as_ref() .iter() @@ -464,11 +464,11 @@ where /// [`.slice()`](struct.ArrayBase.html#method.slice) (including taking /// subviews). /// - /// If `Do` is a fixed-size dimension type, then this is equivalent to - /// `Do::NDIM.unwrap()`. Otherwise, the value is calculated by iterating + /// If `Dout` is a fixed-size dimension type, then this is equivalent to + /// `Dout::NDIM.unwrap()`. Otherwise, the value is calculated by iterating /// over the `AxisSliceInfo` elements. pub fn out_ndim(&self) -> usize { - Do::NDIM.unwrap_or_else(|| { + Dout::NDIM.unwrap_or_else(|| { self.indices .as_ref() .iter() @@ -478,48 +478,48 @@ where } } -impl AsRef<[AxisSliceInfo]> for SliceInfo +impl AsRef<[AxisSliceInfo]> for SliceInfo where T: AsRef<[AxisSliceInfo]>, - Di: Dimension, - Do: Dimension, + Din: Dimension, + Dout: Dimension, { fn as_ref(&self) -> &[AxisSliceInfo] { self.indices.as_ref() } } -impl AsRef> for SliceInfo +impl AsRef> for SliceInfo where T: AsRef<[AxisSliceInfo]>, - Di: Dimension, - Do: Dimension, + Din: Dimension, + Dout: Dimension, { - fn as_ref(&self) -> &SliceInfo<[AxisSliceInfo], Di, Do> { + fn as_ref(&self) -> &SliceInfo<[AxisSliceInfo], Din, Dout> { unsafe { // This is okay because the only non-zero-sized member of - // `SliceInfo` is `indices`, so `&SliceInfo<[AxisSliceInfo], Di, Do>` + // `SliceInfo` is `indices`, so `&SliceInfo<[AxisSliceInfo], Din, Dout>` // should have the same bitwise representation as // `&[AxisSliceInfo]`. &*(self.indices.as_ref() as *const [AxisSliceInfo] - as *const SliceInfo<[AxisSliceInfo], Di, Do>) + as *const SliceInfo<[AxisSliceInfo], Din, Dout>) } } } -impl Copy for SliceInfo +impl Copy for SliceInfo where T: Copy, - Di: Dimension, - Do: Dimension, + Din: Dimension, + Dout: Dimension, { } -impl Clone for SliceInfo +impl Clone for SliceInfo where T: Clone, - Di: Dimension, - Do: Dimension, + Din: Dimension, + Dout: Dimension, { fn clone(&self) -> Self { SliceInfo { From d6b9cb0d3655f7ee4c9b6cff72b504f294fbfa83 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 16 Dec 2018 23:35:03 -0500 Subject: [PATCH 213/651] Improve code style --- src/slice.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/slice.rs b/src/slice.rs index e0ba0df78..e7a2fe172 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -451,13 +451,15 @@ where /// `Din::NDIM.unwrap()`. Otherwise, the value is calculated by iterating /// over the `AxisSliceInfo` elements. pub fn in_ndim(&self) -> usize { - Din::NDIM.unwrap_or_else(|| { + if let Some(ndim) = Din::NDIM { + ndim + } else { self.indices .as_ref() .iter() .filter(|s| !s.is_new_axis()) .count() - }) + } } /// Returns the number of dimensions after calling @@ -468,13 +470,15 @@ where /// `Dout::NDIM.unwrap()`. Otherwise, the value is calculated by iterating /// over the `AxisSliceInfo` elements. pub fn out_ndim(&self) -> usize { - Dout::NDIM.unwrap_or_else(|| { + if let Some(ndim) = Dout::NDIM { + ndim + } else { self.indices .as_ref() .iter() .filter(|s| !s.is_index()) .count() - }) + } } } From 438d69abcfabcbba149cbb70be055ec77cecdc85 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 16 Dec 2018 23:35:23 -0500 Subject: [PATCH 214/651] Derive Clone, Copy, and Debug for NewAxis --- src/slice.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slice.rs b/src/slice.rs index e7a2fe172..90d67ba16 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -70,6 +70,7 @@ impl Slice { /// Token to represent a new axis in a slice description. /// /// See also the [`s![]`](macro.s!.html) macro. +#[derive(Clone, Copy, Debug)] pub struct NewAxis; /// A slice (range with step), an index, or a new axis token. From 6050df3bbd0b54e62b1871c985df8126be418932 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 17 Dec 2018 19:02:45 -0500 Subject: [PATCH 215/651] Use stringify! for string literal of type name --- src/slice.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slice.rs b/src/slice.rs index 90d67ba16..75083f193 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -175,7 +175,7 @@ impl fmt::Display for AxisSliceInfo { write!(f, ";{}", step)?; } } - AxisSliceInfo::NewAxis => write!(f, "NewAxis")?, + AxisSliceInfo::NewAxis => write!(f, stringify!(NewAxis))?, } Ok(()) } From 8d45268435bd8484162efcbd27ad0d3c88c70496 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 17 Dec 2018 19:06:29 -0500 Subject: [PATCH 216/651] Make step_by panic for variants other than Slice --- src/slice.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slice.rs b/src/slice.rs index 75083f193..24cbafda2 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -140,6 +140,8 @@ impl AxisSliceInfo { /// /// `step` must be nonzero. /// (This method checks with a debug assertion that `step` is not zero.) + /// + /// **Panics** if `self` is not the `AxisSliceInfo::Slice` variant. #[inline] pub fn step_by(self, step: isize) -> Self { debug_assert_ne!(step, 0, "AxisSliceInfo::step_by: step must be nonzero"); @@ -153,8 +155,7 @@ impl AxisSliceInfo { end, step: orig_step * step, }, - AxisSliceInfo::Index(s) => AxisSliceInfo::Index(s), - AxisSliceInfo::NewAxis => AxisSliceInfo::NewAxis, + _ => panic!("AxisSliceInfo::step_by: `self` must be the `Slice` variant"), } } } From 1d15275eb5b38610bb04145b22e1fc745e90970f Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 17 Dec 2018 19:53:47 -0500 Subject: [PATCH 217/651] Add DimAdd trait --- src/dimension/mod.rs | 2 + src/dimension/ops.rs | 95 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 +- 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/dimension/ops.rs diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 28bdc7f91..a83c49015 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -19,6 +19,7 @@ pub use self::dim::*; pub use self::dimension_trait::Dimension; pub use self::dynindeximpl::IxDynImpl; pub use self::ndindex::NdIndex; +pub use self::ops::DimAdd; pub use self::remove_axis::RemoveAxis; use crate::shape_builder::Strides; @@ -36,6 +37,7 @@ pub mod dim; mod dimension_trait; mod dynindeximpl; mod ndindex; +mod ops; mod remove_axis; /// Calculate offset from `Ix` stride converting sign properly diff --git a/src/dimension/ops.rs b/src/dimension/ops.rs new file mode 100644 index 000000000..c31d67412 --- /dev/null +++ b/src/dimension/ops.rs @@ -0,0 +1,95 @@ +use crate::imp_prelude::*; + +/// Adds the two dimensions at compile time. +pub trait DimAdd: Dimension { + /// The sum of the two dimensions. + type Out: Dimension; +} + +macro_rules! impl_dimadd_const_out_const { + ($lhs:expr, $rhs:expr) => { + impl DimAdd> for Dim<[usize; $lhs]> { + type Out = Dim<[usize; $lhs + $rhs]>; + } + }; +} + +macro_rules! impl_dimadd_const_out_dyn { + ($lhs:expr, IxDyn) => { + impl DimAdd for Dim<[usize; $lhs]> { + type Out = IxDyn; + } + }; + ($lhs:expr, $rhs:expr) => { + impl DimAdd> for Dim<[usize; $lhs]> { + type Out = IxDyn; + } + }; +} + +impl_dimadd_const_out_const!(0, 0); +impl_dimadd_const_out_const!(0, 1); +impl_dimadd_const_out_const!(0, 2); +impl_dimadd_const_out_const!(0, 3); +impl_dimadd_const_out_const!(0, 4); +impl_dimadd_const_out_const!(0, 5); +impl_dimadd_const_out_const!(0, 6); +impl_dimadd_const_out_dyn!(0, IxDyn); + +impl_dimadd_const_out_const!(1, 0); +impl_dimadd_const_out_const!(1, 1); +impl_dimadd_const_out_const!(1, 2); +impl_dimadd_const_out_const!(1, 3); +impl_dimadd_const_out_const!(1, 4); +impl_dimadd_const_out_const!(1, 5); +impl_dimadd_const_out_dyn!(1, 6); +impl_dimadd_const_out_dyn!(1, IxDyn); + +impl_dimadd_const_out_const!(2, 0); +impl_dimadd_const_out_const!(2, 1); +impl_dimadd_const_out_const!(2, 2); +impl_dimadd_const_out_const!(2, 3); +impl_dimadd_const_out_const!(2, 4); +impl_dimadd_const_out_dyn!(2, 5); +impl_dimadd_const_out_dyn!(2, 6); +impl_dimadd_const_out_dyn!(2, IxDyn); + +impl_dimadd_const_out_const!(3, 0); +impl_dimadd_const_out_const!(3, 1); +impl_dimadd_const_out_const!(3, 2); +impl_dimadd_const_out_const!(3, 3); +impl_dimadd_const_out_dyn!(3, 4); +impl_dimadd_const_out_dyn!(3, 5); +impl_dimadd_const_out_dyn!(3, 6); +impl_dimadd_const_out_dyn!(3, IxDyn); + +impl_dimadd_const_out_const!(4, 0); +impl_dimadd_const_out_const!(4, 1); +impl_dimadd_const_out_const!(4, 2); +impl_dimadd_const_out_dyn!(4, 3); +impl_dimadd_const_out_dyn!(4, 4); +impl_dimadd_const_out_dyn!(4, 5); +impl_dimadd_const_out_dyn!(4, 6); +impl_dimadd_const_out_dyn!(4, IxDyn); + +impl_dimadd_const_out_const!(5, 0); +impl_dimadd_const_out_const!(5, 1); +impl_dimadd_const_out_dyn!(5, 2); +impl_dimadd_const_out_dyn!(5, 3); +impl_dimadd_const_out_dyn!(5, 4); +impl_dimadd_const_out_dyn!(5, 5); +impl_dimadd_const_out_dyn!(5, 6); +impl_dimadd_const_out_dyn!(5, IxDyn); + +impl_dimadd_const_out_const!(6, 0); +impl_dimadd_const_out_dyn!(6, 1); +impl_dimadd_const_out_dyn!(6, 2); +impl_dimadd_const_out_dyn!(6, 3); +impl_dimadd_const_out_dyn!(6, 4); +impl_dimadd_const_out_dyn!(6, 5); +impl_dimadd_const_out_dyn!(6, 6); +impl_dimadd_const_out_dyn!(6, IxDyn); + +impl DimAdd for IxDyn { + type Out = IxDyn; +} diff --git a/src/lib.rs b/src/lib.rs index ae750f3d8..5c1ef755e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -134,8 +134,8 @@ use std::marker::PhantomData; use alloc::sync::Arc; pub use crate::dimension::dim::*; -pub use crate::dimension::DimMax; pub use crate::dimension::{Axis, AxisDescription, Dimension, IntoDimension, RemoveAxis}; +pub use crate::dimension::{DimAdd, DimMax}; pub use crate::dimension::IxDynImpl; pub use crate::dimension::NdIndex; From 41cc4a1248f8b4698a5c98490d4847b5642ac0fd Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 17 Dec 2018 20:08:22 -0500 Subject: [PATCH 218/651] Replace SliceNextIn/OutDim with SliceArg trait --- src/lib.rs | 2 +- src/slice.rs | 107 +++++++++++++++++++++------------------------------ 2 files changed, 44 insertions(+), 65 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5c1ef755e..d82a99649 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -141,7 +141,7 @@ pub use crate::dimension::IxDynImpl; pub use crate::dimension::NdIndex; pub use crate::error::{ErrorKind, ShapeError}; pub use crate::indexes::{indices, indices_of}; -pub use crate::slice::{AxisSliceInfo, NewAxis, Slice, SliceInfo, SliceNextInDim, SliceNextOutDim}; +pub use crate::slice::{AxisSliceInfo, NewAxis, Slice, SliceArg, SliceInfo}; use crate::iterators::Baseiter; use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes}; diff --git a/src/slice.rs b/src/slice.rs index 24cbafda2..d19a889e9 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -7,10 +7,10 @@ // except according to those terms. use crate::dimension::slices_intersect; use crate::error::{ErrorKind, ShapeError}; +use crate::{ArrayViewMut, DimAdd, Dimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn}; use std::fmt; use std::marker::PhantomData; use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; -use crate::{ArrayViewMut, Dimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn}; /// A slice (range with step size). /// @@ -536,72 +536,51 @@ where } } +/// Trait for determining dimensionality of input and output for [`s!`] macro. #[doc(hidden)] -pub trait SliceNextInDim { - fn next_dim(&self, _: PhantomData) -> PhantomData; -} +pub trait SliceArg { + /// Number of dimensions that this slicing argument consumes in the input array. + type InDim: Dimension; + /// Number of dimensions that this slicing argument produces in the output array. + type OutDim: Dimension; -impl SliceNextInDim for NewAxis { - fn next_dim(&self, _: PhantomData) -> PhantomData { + fn next_in_dim(&self, _: PhantomData) -> PhantomData + where + D: Dimension + DimAdd, + { PhantomData } -} -macro_rules! impl_slicenextindim_larger { - (($($generics:tt)*), $self:ty) => { - impl SliceNextInDim for $self { - fn next_dim(&self, _: PhantomData) -> PhantomData { - PhantomData - } - } + fn next_out_dim(&self, _: PhantomData) -> PhantomData + where + D: Dimension + DimAdd, + { + PhantomData } } -impl_slicenextindim_larger!((), isize); -impl_slicenextindim_larger!((), usize); -impl_slicenextindim_larger!((), i32); -impl_slicenextindim_larger!((T), Range); -impl_slicenextindim_larger!((T), RangeInclusive); -impl_slicenextindim_larger!((T), RangeFrom); -impl_slicenextindim_larger!((T), RangeTo); -impl_slicenextindim_larger!((T), RangeToInclusive); -impl_slicenextindim_larger!((), RangeFull); -impl_slicenextindim_larger!((), Slice); - -#[doc(hidden)] -pub trait SliceNextOutDim { - fn next_dim(&self, _: PhantomData) -> PhantomData; -} -macro_rules! impl_slicenextoutdim_equal { - ($self:ty) => { - impl SliceNextOutDim for $self { - fn next_dim(&self, _: PhantomData) -> PhantomData { - PhantomData - } +macro_rules! impl_slicearg { + (($($generics:tt)*), $self:ty, $in:ty, $out:ty) => { + impl<$($generics)*> SliceArg for $self { + type InDim = $in; + type OutDim = $out; } }; } -impl_slicenextoutdim_equal!(isize); -impl_slicenextoutdim_equal!(usize); -impl_slicenextoutdim_equal!(i32); - -macro_rules! impl_slicenextoutdim_larger { - (($($generics:tt)*), $self:ty) => { - impl SliceNextOutDim for $self { - fn next_dim(&self, _: PhantomData) -> PhantomData { - PhantomData - } - } - } -} -impl_slicenextoutdim_larger!((T), Range); -impl_slicenextoutdim_larger!((T), RangeInclusive); -impl_slicenextoutdim_larger!((T), RangeFrom); -impl_slicenextoutdim_larger!((T), RangeTo); -impl_slicenextoutdim_larger!((T), RangeToInclusive); -impl_slicenextoutdim_larger!((), RangeFull); -impl_slicenextoutdim_larger!((), Slice); -impl_slicenextoutdim_larger!((), NewAxis); + +impl_slicearg!((), isize, Ix1, Ix0); +impl_slicearg!((), usize, Ix1, Ix0); +impl_slicearg!((), i32, Ix1, Ix0); + +impl_slicearg!((T), Range, Ix1, Ix1); +impl_slicearg!((T), RangeInclusive, Ix1, Ix1); +impl_slicearg!((T), RangeFrom, Ix1, Ix1); +impl_slicearg!((T), RangeTo, Ix1, Ix1); +impl_slicearg!((T), RangeToInclusive, Ix1, Ix1); +impl_slicearg!((), RangeFull, Ix1, Ix1); +impl_slicearg!((), Slice, Ix1, Ix1); + +impl_slicearg!((), NewAxis, Ix0, Ix1); /// Slice argument constructor. /// @@ -703,8 +682,8 @@ macro_rules! s( (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr;$s:expr) => { match $r { r => { - let in_dim = $crate::SliceNextInDim::next_dim(&r, $in_dim); - let out_dim = $crate::SliceNextOutDim::next_dim(&r, $out_dim); + let in_dim = $crate::SliceArg::next_in_dim(&r, $in_dim); + let out_dim = $crate::SliceArg::next_out_dim(&r, $out_dim); #[allow(unsafe_code)] unsafe { $crate::SliceInfo::new_unchecked( @@ -720,8 +699,8 @@ macro_rules! s( (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr) => { match $r { r => { - let in_dim = $crate::SliceNextInDim::next_dim(&r, $in_dim); - let out_dim = $crate::SliceNextOutDim::next_dim(&r, $out_dim); + let in_dim = $crate::SliceArg::next_in_dim(&r, $in_dim); + let out_dim = $crate::SliceArg::next_out_dim(&r, $out_dim); #[allow(unsafe_code)] unsafe { $crate::SliceInfo::new_unchecked( @@ -746,8 +725,8 @@ macro_rules! s( match $r { r => { $crate::s![@parse - $crate::SliceNextInDim::next_dim(&r, $in_dim), - $crate::SliceNextOutDim::next_dim(&r, $out_dim), + $crate::SliceArg::next_in_dim(&r, $in_dim), + $crate::SliceArg::next_out_dim(&r, $out_dim), [$($stack)* $crate::s!(@convert r, $s),] $($t)* ] @@ -759,8 +738,8 @@ macro_rules! s( match $r { r => { $crate::s![@parse - $crate::SliceNextInDim::next_dim(&r, $in_dim), - $crate::SliceNextOutDim::next_dim(&r, $out_dim), + $crate::SliceArg::next_in_dim(&r, $in_dim), + $crate::SliceArg::next_out_dim(&r, $out_dim), [$($stack)* $crate::s!(@convert r),] $($t)* ] From c66ad8ca932374c91b567b6e6442a4d0ea9c2e43 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 7 Feb 2021 16:29:43 -0500 Subject: [PATCH 219/651] Combine DimAdd impls for Ix0 --- src/dimension/ops.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/dimension/ops.rs b/src/dimension/ops.rs index c31d67412..855b3b4e1 100644 --- a/src/dimension/ops.rs +++ b/src/dimension/ops.rs @@ -27,14 +27,9 @@ macro_rules! impl_dimadd_const_out_dyn { }; } -impl_dimadd_const_out_const!(0, 0); -impl_dimadd_const_out_const!(0, 1); -impl_dimadd_const_out_const!(0, 2); -impl_dimadd_const_out_const!(0, 3); -impl_dimadd_const_out_const!(0, 4); -impl_dimadd_const_out_const!(0, 5); -impl_dimadd_const_out_const!(0, 6); -impl_dimadd_const_out_dyn!(0, IxDyn); +impl DimAdd for Ix0 { + type Out = D; +} impl_dimadd_const_out_const!(1, 0); impl_dimadd_const_out_const!(1, 1); From 7776bfcc7069aa4e159a7816b35933d9a7de0a0f Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 14 Feb 2021 03:21:59 -0500 Subject: [PATCH 220/651] Implement CanSlice for [AxisSliceInfo] --- blas-tests/tests/oper.rs | 4 ++-- src/impl_methods.rs | 8 ++++---- src/slice.rs | 32 ++++++++++++++++++-------------- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/blas-tests/tests/oper.rs b/blas-tests/tests/oper.rs index 25d26b7ba..6b6797f12 100644 --- a/blas-tests/tests/oper.rs +++ b/blas-tests/tests/oper.rs @@ -6,7 +6,7 @@ extern crate num_traits; use ndarray::linalg::general_mat_mul; use ndarray::linalg::general_mat_vec_mul; use ndarray::prelude::*; -use ndarray::{AxisSliceInfo, Ix, Ixs, SliceInfo}; +use ndarray::{AxisSliceInfo, Ix, Ixs}; use ndarray::{Data, LinalgScalar}; use approx::{assert_abs_diff_eq, assert_relative_eq}; @@ -432,7 +432,7 @@ fn scaled_add_3() { { let mut av = a.slice_mut(s![..;s1, ..;s2]); - let c = c.slice(&SliceInfo::<_, IxDyn, IxDyn>::new(cslice).unwrap()); + let c = c.slice(&*cslice); let mut answerv = answer.slice_mut(s![..;s1, ..;s2]); answerv += &(beta * &c); diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 143824850..fb7097d56 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -343,7 +343,7 @@ where /// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) pub fn slice(&self, info: &I) -> ArrayView<'_, A, I::OutDim> where - I: CanSlice, + I: CanSlice + ?Sized, S: Data, { self.view().slice_move(info) @@ -361,7 +361,7 @@ where /// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) pub fn slice_mut(&mut self, info: &I) -> ArrayViewMut<'_, A, I::OutDim> where - I: CanSlice, + I: CanSlice + ?Sized, S: DataMut, { self.view_mut().slice_move(info) @@ -412,7 +412,7 @@ where /// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) pub fn slice_move(mut self, info: &I) -> ArrayBase where - I: CanSlice, + I: CanSlice + ?Sized, { // Slice and collapse in-place without changing the number of dimensions. self.slice_collapse(info); @@ -464,7 +464,7 @@ where /// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) pub fn slice_collapse(&mut self, info: &I) where - I: CanSlice, + I: CanSlice + ?Sized, { assert_eq!( info.in_ndim(), diff --git a/src/slice.rs b/src/slice.rs index d19a889e9..ed567c8fc 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -360,6 +360,18 @@ where } } +unsafe impl CanSlice for [AxisSliceInfo] { + type OutDim = IxDyn; + + fn in_ndim(&self) -> usize { + self.iter().filter(|s| !s.is_new_axis()).count() + } + + fn out_ndim(&self) -> usize { + self.iter().filter(|s| !s.is_index()).count() + } +} + /// Represents all of the necessary information to perform a slice. /// /// The type `T` is typically `[AxisSliceInfo; n]`, `[AxisSliceInfo]`, or @@ -422,13 +434,13 @@ where /// /// Errors if `Din` or `Dout` is not consistent with `indices`. pub fn new(indices: T) -> Result, ShapeError> { - if let Some(ndim) = Din::NDIM { - if ndim != indices.as_ref().iter().filter(|s| !s.is_new_axis()).count() { + if let Some(in_ndim) = Din::NDIM { + if in_ndim != indices.as_ref().in_ndim() { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); } } - if let Some(ndim) = Dout::NDIM { - if ndim != indices.as_ref().iter().filter(|s| !s.is_index()).count() { + if let Some(out_ndim) = Dout::NDIM { + if out_ndim != indices.as_ref().out_ndim() { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); } } @@ -456,11 +468,7 @@ where if let Some(ndim) = Din::NDIM { ndim } else { - self.indices - .as_ref() - .iter() - .filter(|s| !s.is_new_axis()) - .count() + self.indices.as_ref().in_ndim() } } @@ -475,11 +483,7 @@ where if let Some(ndim) = Dout::NDIM { ndim } else { - self.indices - .as_ref() - .iter() - .filter(|s| !s.is_index()) - .count() + self.indices.as_ref().out_ndim() } } } From ab79d2857b8fcd8ab5d86558590f48aa87b46c61 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 14 Feb 2021 20:28:47 -0500 Subject: [PATCH 221/651] Change SliceInfo to be repr(transparent) --- src/slice.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slice.rs b/src/slice.rs index ed567c8fc..8d9d85fd6 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -383,7 +383,7 @@ unsafe impl CanSlice for [AxisSliceInfo] { /// /// [`.slice()`]: struct.ArrayBase.html#method.slice #[derive(Debug)] -#[repr(C)] +#[repr(transparent)] pub struct SliceInfo { in_dim: PhantomData, out_dim: PhantomData, From 615113e0eb3a853ff4a56576881e611634558095 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 14 Feb 2021 21:04:42 -0500 Subject: [PATCH 222/651] Add debug assertions to SliceInfo::new_unchecked --- src/slice.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/slice.rs b/src/slice.rs index 8d9d85fd6..204e15785 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -403,6 +403,7 @@ where impl SliceInfo where + T: AsRef<[AxisSliceInfo]>, Din: Dimension, Dout: Dimension, { @@ -410,16 +411,26 @@ where /// /// If you call this method, you are guaranteeing that `in_dim` and /// `out_dim` are consistent with `indices`. + /// + /// **Note:** only unchecked for non-debug builds of `ndarray`. #[doc(hidden)] pub unsafe fn new_unchecked( indices: T, in_dim: PhantomData, out_dim: PhantomData, ) -> SliceInfo { + if cfg!(debug_assertions) { + if let Some(in_ndim) = Din::NDIM { + assert_eq!(in_ndim, indices.as_ref().in_ndim()); + } + if let Some(out_ndim) = Dout::NDIM { + assert_eq!(out_ndim, indices.as_ref().out_ndim()); + } + } SliceInfo { - in_dim: in_dim, - out_dim: out_dim, - indices: indices, + in_dim, + out_dim, + indices, } } } From e66e3c89f1304f770679b45cfc554d83bc8a702f Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 15 Feb 2021 16:31:11 -0500 Subject: [PATCH 223/651] Fix safety of SliceInfo::new --- src/slice.rs | 19 +++++++++++---- tests/array.rs | 64 ++++++++++++++++++++++++++++---------------------- tests/oper.rs | 2 +- 3 files changed, 51 insertions(+), 34 deletions(-) diff --git a/src/slice.rs b/src/slice.rs index 204e15785..2c0b7d323 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -307,7 +307,8 @@ impl From for AxisSliceInfo { /// /// This trait is unsafe to implement because the implementation must ensure /// that `D`, `Self::OutDim`, `self.in_dim()`, and `self.out_ndim()` are -/// consistent with the `&[AxisSliceInfo]` returned by `self.as_ref()`. +/// consistent with the `&[AxisSliceInfo]` returned by `self.as_ref()` and that +/// `self.as_ref()` always returns the same value when called multiple times. pub unsafe trait CanSlice: AsRef<[AxisSliceInfo]> { type OutDim: Dimension; @@ -409,10 +410,13 @@ where { /// Returns a new `SliceInfo` instance. /// - /// If you call this method, you are guaranteeing that `in_dim` and - /// `out_dim` are consistent with `indices`. - /// /// **Note:** only unchecked for non-debug builds of `ndarray`. + /// + /// # Safety + /// + /// The caller must ensure that `in_dim` and `out_dim` are consistent with + /// `indices` and that `indices.as_ref()` always returns the same value + /// when called multiple times. #[doc(hidden)] pub unsafe fn new_unchecked( indices: T, @@ -444,7 +448,12 @@ where /// Returns a new `SliceInfo` instance. /// /// Errors if `Din` or `Dout` is not consistent with `indices`. - pub fn new(indices: T) -> Result, ShapeError> { + /// + /// # Safety + /// + /// The caller must ensure `indices.as_ref()` always returns the same value + /// when called multiple times. + pub unsafe fn new(indices: T) -> Result, ShapeError> { if let Some(in_ndim) = Din::NDIM { if in_ndim != indices.as_ref().in_ndim() { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); diff --git a/tests/array.rs b/tests/array.rs index 51f59fcb1..f9bd0d67b 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -216,13 +216,15 @@ fn test_slice_dyninput_array_fixed() { #[test] fn test_slice_array_dyn() { let mut arr = Array3::::zeros((5, 2, 5)); - let info = &SliceInfo::<_, Ix3, IxDyn>::new([ - AxisSliceInfo::from(1..), - AxisSliceInfo::from(1), - AxisSliceInfo::from(NewAxis), - AxisSliceInfo::from(..).step_by(2), - ]) - .unwrap(); + let info = &unsafe { + SliceInfo::<_, Ix3, IxDyn>::new([ + AxisSliceInfo::from(1..), + AxisSliceInfo::from(1), + AxisSliceInfo::from(NewAxis), + AxisSliceInfo::from(..).step_by(2), + ]) + .unwrap() + }; arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); @@ -232,13 +234,15 @@ fn test_slice_array_dyn() { #[test] fn test_slice_dyninput_array_dyn() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); - let info = &SliceInfo::<_, Ix3, IxDyn>::new([ - AxisSliceInfo::from(1..), - AxisSliceInfo::from(1), - AxisSliceInfo::from(NewAxis), - AxisSliceInfo::from(..).step_by(2), - ]) - .unwrap(); + let info = &unsafe { + SliceInfo::<_, Ix3, IxDyn>::new([ + AxisSliceInfo::from(1..), + AxisSliceInfo::from(1), + AxisSliceInfo::from(NewAxis), + AxisSliceInfo::from(..).step_by(2), + ]) + .unwrap() + }; arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); @@ -248,13 +252,15 @@ fn test_slice_dyninput_array_dyn() { #[test] fn test_slice_dyninput_vec_fixed() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); - let info = &SliceInfo::<_, Ix3, Ix3>::new(vec![ - AxisSliceInfo::from(1..), - AxisSliceInfo::from(1), - AxisSliceInfo::from(NewAxis), - AxisSliceInfo::from(..).step_by(2), - ]) - .unwrap(); + let info = &unsafe { + SliceInfo::<_, Ix3, Ix3>::new(vec![ + AxisSliceInfo::from(1..), + AxisSliceInfo::from(1), + AxisSliceInfo::from(NewAxis), + AxisSliceInfo::from(..).step_by(2), + ]) + .unwrap() + }; arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); @@ -264,13 +270,15 @@ fn test_slice_dyninput_vec_fixed() { #[test] fn test_slice_dyninput_vec_dyn() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); - let info = &SliceInfo::<_, Ix3, IxDyn>::new(vec![ - AxisSliceInfo::from(1..), - AxisSliceInfo::from(1), - AxisSliceInfo::from(NewAxis), - AxisSliceInfo::from(..).step_by(2), - ]) - .unwrap(); + let info = &unsafe { + SliceInfo::<_, Ix3, IxDyn>::new(vec![ + AxisSliceInfo::from(1..), + AxisSliceInfo::from(1), + AxisSliceInfo::from(NewAxis), + AxisSliceInfo::from(..).step_by(2), + ]) + .unwrap() + }; arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); diff --git a/tests/oper.rs b/tests/oper.rs index 16f3edbc6..3a4a26ac8 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -595,7 +595,7 @@ fn scaled_add_3() { { let mut av = a.slice_mut(s![..;s1, ..;s2]); - let c = c.slice(&SliceInfo::<_, IxDyn, IxDyn>::new(cslice).unwrap()); + let c = c.slice(&unsafe { SliceInfo::<_, IxDyn, IxDyn>::new(cslice).unwrap() }); let mut answerv = answer.slice_mut(s![..;s1, ..;s2]); answerv += &(beta * &c); From 3ba6ceb8cd0463cb5164e19b295a26b376cd5261 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 15 Feb 2021 17:21:52 -0500 Subject: [PATCH 224/651] Add some impls of TryFrom for SliceInfo --- src/slice.rs | 114 ++++++++++++++++++++++++++++++++++++++++++------- tests/array.rs | 65 +++++++++++++--------------- tests/oper.rs | 3 +- 3 files changed, 129 insertions(+), 53 deletions(-) diff --git a/src/slice.rs b/src/slice.rs index 2c0b7d323..f2da78038 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -8,6 +8,8 @@ use crate::dimension::slices_intersect; use crate::error::{ErrorKind, ShapeError}; use crate::{ArrayViewMut, DimAdd, Dimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn}; +use alloc::vec::Vec; +use std::convert::TryFrom; use std::fmt; use std::marker::PhantomData; use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; @@ -402,6 +404,24 @@ where } } +fn check_dims_for_sliceinfo(indices: &[AxisSliceInfo]) -> Result<(), ShapeError> +where + Din: Dimension, + Dout: Dimension, +{ + if let Some(in_ndim) = Din::NDIM { + if in_ndim != indices.in_ndim() { + return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); + } + } + if let Some(out_ndim) = Dout::NDIM { + if out_ndim != indices.out_ndim() { + return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); + } + } + Ok(()) +} + impl SliceInfo where T: AsRef<[AxisSliceInfo]>, @@ -424,12 +444,8 @@ where out_dim: PhantomData, ) -> SliceInfo { if cfg!(debug_assertions) { - if let Some(in_ndim) = Din::NDIM { - assert_eq!(in_ndim, indices.as_ref().in_ndim()); - } - if let Some(out_ndim) = Dout::NDIM { - assert_eq!(out_ndim, indices.as_ref().out_ndim()); - } + check_dims_for_sliceinfo::(indices.as_ref()) + .expect("`Din` and `Dout` must be consistent with `indices`."); } SliceInfo { in_dim, @@ -449,21 +465,14 @@ where /// /// Errors if `Din` or `Dout` is not consistent with `indices`. /// + /// For common types, a safe alternative is to use `TryFrom` instead. + /// /// # Safety /// /// The caller must ensure `indices.as_ref()` always returns the same value /// when called multiple times. pub unsafe fn new(indices: T) -> Result, ShapeError> { - if let Some(in_ndim) = Din::NDIM { - if in_ndim != indices.as_ref().in_ndim() { - return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); - } - } - if let Some(out_ndim) = Dout::NDIM { - if out_ndim != indices.as_ref().out_ndim() { - return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); - } - } + check_dims_for_sliceinfo::(indices.as_ref())?; Ok(SliceInfo { in_dim: PhantomData, out_dim: PhantomData, @@ -508,6 +517,79 @@ where } } +impl<'a, Din, Dout> TryFrom<&'a [AxisSliceInfo]> for &'a SliceInfo<[AxisSliceInfo], Din, Dout> +where + Din: Dimension, + Dout: Dimension, +{ + type Error = ShapeError; + + fn try_from( + indices: &'a [AxisSliceInfo], + ) -> Result<&'a SliceInfo<[AxisSliceInfo], Din, Dout>, ShapeError> { + check_dims_for_sliceinfo::(indices)?; + unsafe { + // This is okay because we've already checked the correctness of + // `Din` and `Dout`, and the only non-zero-sized member of + // `SliceInfo` is `indices`, so `&SliceInfo<[AxisSliceInfo], Din, + // Dout>` should have the same bitwise representation as + // `&[AxisSliceInfo]`. + Ok(&*(indices as *const [AxisSliceInfo] + as *const SliceInfo<[AxisSliceInfo], Din, Dout>)) + } + } +} + +impl TryFrom> for SliceInfo, Din, Dout> +where + Din: Dimension, + Dout: Dimension, +{ + type Error = ShapeError; + + fn try_from( + indices: Vec, + ) -> Result, Din, Dout>, ShapeError> { + unsafe { + // This is okay because `Vec` always returns the same value for + // `.as_ref()`. + Self::new(indices) + } + } +} + +macro_rules! impl_tryfrom_array_for_sliceinfo { + ($len:expr) => { + impl TryFrom<[AxisSliceInfo; $len]> + for SliceInfo<[AxisSliceInfo; $len], Din, Dout> + where + Din: Dimension, + Dout: Dimension, + { + type Error = ShapeError; + + fn try_from( + indices: [AxisSliceInfo; $len], + ) -> Result, ShapeError> { + unsafe { + // This is okay because `[AxisSliceInfo; N]` always returns + // the same value for `.as_ref()`. + Self::new(indices) + } + } + } + }; +} +impl_tryfrom_array_for_sliceinfo!(0); +impl_tryfrom_array_for_sliceinfo!(1); +impl_tryfrom_array_for_sliceinfo!(2); +impl_tryfrom_array_for_sliceinfo!(3); +impl_tryfrom_array_for_sliceinfo!(4); +impl_tryfrom_array_for_sliceinfo!(5); +impl_tryfrom_array_for_sliceinfo!(6); +impl_tryfrom_array_for_sliceinfo!(7); +impl_tryfrom_array_for_sliceinfo!(8); + impl AsRef<[AxisSliceInfo]> for SliceInfo where T: AsRef<[AxisSliceInfo]>, diff --git a/tests/array.rs b/tests/array.rs index f9bd0d67b..ac4791d6f 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -13,6 +13,7 @@ use ndarray::prelude::*; use ndarray::{arr3, rcarr2}; use ndarray::indices; use ndarray::{AxisSliceInfo, Slice, SliceInfo}; +use std::convert::TryFrom; macro_rules! assert_panics { ($body:expr) => { @@ -216,15 +217,13 @@ fn test_slice_dyninput_array_fixed() { #[test] fn test_slice_array_dyn() { let mut arr = Array3::::zeros((5, 2, 5)); - let info = &unsafe { - SliceInfo::<_, Ix3, IxDyn>::new([ - AxisSliceInfo::from(1..), - AxisSliceInfo::from(1), - AxisSliceInfo::from(NewAxis), - AxisSliceInfo::from(..).step_by(2), - ]) - .unwrap() - }; + let info = &SliceInfo::<_, Ix3, IxDyn>::try_from([ + AxisSliceInfo::from(1..), + AxisSliceInfo::from(1), + AxisSliceInfo::from(NewAxis), + AxisSliceInfo::from(..).step_by(2), + ]) + .unwrap(); arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); @@ -234,15 +233,13 @@ fn test_slice_array_dyn() { #[test] fn test_slice_dyninput_array_dyn() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); - let info = &unsafe { - SliceInfo::<_, Ix3, IxDyn>::new([ - AxisSliceInfo::from(1..), - AxisSliceInfo::from(1), - AxisSliceInfo::from(NewAxis), - AxisSliceInfo::from(..).step_by(2), - ]) - .unwrap() - }; + let info = &SliceInfo::<_, Ix3, IxDyn>::try_from([ + AxisSliceInfo::from(1..), + AxisSliceInfo::from(1), + AxisSliceInfo::from(NewAxis), + AxisSliceInfo::from(..).step_by(2), + ]) + .unwrap(); arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); @@ -252,15 +249,13 @@ fn test_slice_dyninput_array_dyn() { #[test] fn test_slice_dyninput_vec_fixed() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); - let info = &unsafe { - SliceInfo::<_, Ix3, Ix3>::new(vec![ - AxisSliceInfo::from(1..), - AxisSliceInfo::from(1), - AxisSliceInfo::from(NewAxis), - AxisSliceInfo::from(..).step_by(2), - ]) - .unwrap() - }; + let info = &SliceInfo::<_, Ix3, Ix3>::try_from(vec![ + AxisSliceInfo::from(1..), + AxisSliceInfo::from(1), + AxisSliceInfo::from(NewAxis), + AxisSliceInfo::from(..).step_by(2), + ]) + .unwrap(); arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); @@ -270,15 +265,13 @@ fn test_slice_dyninput_vec_fixed() { #[test] fn test_slice_dyninput_vec_dyn() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); - let info = &unsafe { - SliceInfo::<_, Ix3, IxDyn>::new(vec![ - AxisSliceInfo::from(1..), - AxisSliceInfo::from(1), - AxisSliceInfo::from(NewAxis), - AxisSliceInfo::from(..).step_by(2), - ]) - .unwrap() - }; + let info = &SliceInfo::<_, Ix3, IxDyn>::try_from(vec![ + AxisSliceInfo::from(1..), + AxisSliceInfo::from(1), + AxisSliceInfo::from(NewAxis), + AxisSliceInfo::from(..).step_by(2), + ]) + .unwrap(); arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); diff --git a/tests/oper.rs b/tests/oper.rs index 3a4a26ac8..b91a9bd85 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -562,6 +562,7 @@ fn scaled_add_2() { fn scaled_add_3() { use approx::assert_relative_eq; use ndarray::{SliceInfo, AxisSliceInfo}; + use std::convert::TryFrom; let beta = -2.3; let sizes = vec![ @@ -595,7 +596,7 @@ fn scaled_add_3() { { let mut av = a.slice_mut(s![..;s1, ..;s2]); - let c = c.slice(&unsafe { SliceInfo::<_, IxDyn, IxDyn>::new(cslice).unwrap() }); + let c = c.slice(&SliceInfo::<_, IxDyn, IxDyn>::try_from(cslice).unwrap()); let mut answerv = answer.slice_mut(s![..;s1, ..;s2]); answerv += &(beta * &c); From 815e708d5b05de1bf69099617ad5de1e6f6c1b35 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 15 Feb 2021 22:31:45 -0500 Subject: [PATCH 225/651] Make slice_move not call slice_collapse This isn't much more code and simplifies the logic somewhat. --- src/impl_methods.rs | 69 ++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index fb7097d56..e9f87583f 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -414,44 +414,49 @@ where where I: CanSlice + ?Sized, { - // Slice and collapse in-place without changing the number of dimensions. - self.slice_collapse(info); - + assert_eq!( + info.in_ndim(), + self.ndim(), + "The input dimension of `info` must match the array to be sliced.", + ); let out_ndim = info.out_ndim(); let mut new_dim = I::OutDim::zeros(out_ndim); let mut new_strides = I::OutDim::zeros(out_ndim); - // Write the dim and strides to the correct new axes. - { - let mut old_axis = 0; - let mut new_axis = 0; - info.as_ref().iter().for_each(|ax_info| match ax_info { - AxisSliceInfo::Slice { .. } => { - // Copy the old dim and stride to corresponding axis. - new_dim[new_axis] = self.dim[old_axis]; - new_strides[new_axis] = self.strides[old_axis]; - old_axis += 1; - new_axis += 1; - } - AxisSliceInfo::Index(_) => { - // Skip the old axis since it should be removed. - old_axis += 1; - } - AxisSliceInfo::NewAxis => { - // Set the dim and stride of the new axis. - new_dim[new_axis] = 1; - new_strides[new_axis] = 0; - new_axis += 1; - } - }); - debug_assert_eq!(old_axis, self.ndim()); - debug_assert_eq!(new_axis, out_ndim); - } + let mut old_axis = 0; + let mut new_axis = 0; + info.as_ref().iter().for_each(|&ax_info| match ax_info { + AxisSliceInfo::Slice { start, end, step } => { + // Slice the axis in-place to update the `dim`, `strides`, and `ptr`. + self.slice_axis_inplace(Axis(old_axis), Slice { start, end, step }); + // Copy the sliced dim and stride to corresponding axis. + new_dim[new_axis] = self.dim[old_axis]; + new_strides[new_axis] = self.strides[old_axis]; + old_axis += 1; + new_axis += 1; + } + AxisSliceInfo::Index(index) => { + // Collapse the axis in-place to update the `ptr`. + let i_usize = abs_index(self.len_of(Axis(old_axis)), index); + self.collapse_axis(Axis(old_axis), i_usize); + // Skip copying the axis since it should be removed. Note that + // removing this axis is safe because `.collapse_axis()` panics + // if the index is out-of-bounds, so it will panic if the axis + // is zero length. + old_axis += 1; + } + AxisSliceInfo::NewAxis => { + // Set the dim and stride of the new axis. + new_dim[new_axis] = 1; + new_strides[new_axis] = 0; + new_axis += 1; + } + }); + debug_assert_eq!(old_axis, self.ndim()); + debug_assert_eq!(new_axis, out_ndim); // safe because new dimension, strides allow access to a subset of old data - unsafe { - self.with_strides_dim(new_strides, new_dim) - } + unsafe { self.with_strides_dim(new_strides, new_dim) } } /// Slice the array in place without changing the number of dimensions. From 25a7bb0b699ef302d0682c970535ce26bd72f23c Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 15 Feb 2021 22:35:13 -0500 Subject: [PATCH 226/651] Make slice_collapse return Err(_) for NewAxis --- examples/axis_ops.rs | 4 +-- serialization-tests/tests/serialize.rs | 6 ++-- src/impl_methods.rs | 36 +++++++++++++-------- tests/array.rs | 44 +++++++++++++------------- tests/iterators.rs | 4 +-- 5 files changed, 52 insertions(+), 42 deletions(-) diff --git a/examples/axis_ops.rs b/examples/axis_ops.rs index 624af32c3..1ff4a3105 100644 --- a/examples/axis_ops.rs +++ b/examples/axis_ops.rs @@ -51,7 +51,7 @@ fn main() { } a.swap_axes(0, 1); a.swap_axes(0, 2); - a.slice_collapse(s![.., ..;-1, ..]); + a.slice_collapse(s![.., ..;-1, ..]).unwrap(); regularize(&mut a).ok(); let mut b = Array::::zeros((2, 3, 4)); @@ -68,6 +68,6 @@ fn main() { for (i, elt) in (0..).zip(&mut a) { *elt = i; } - a.slice_collapse(s![..;-1, ..;2, ..]); + a.slice_collapse(s![..;-1, ..;2, ..]).unwrap(); regularize(&mut a).ok(); } diff --git a/serialization-tests/tests/serialize.rs b/serialization-tests/tests/serialize.rs index efb3bacd9..0afaffe1b 100644 --- a/serialization-tests/tests/serialize.rs +++ b/serialization-tests/tests/serialize.rs @@ -46,7 +46,7 @@ fn serial_many_dim_serde() { { // Test a sliced array. let mut a = ArcArray::linspace(0., 31., 32).reshape((2, 2, 2, 4)); - a.slice_collapse(s![..;-1, .., .., ..2]); + a.slice_collapse(s![..;-1, .., .., ..2]).unwrap(); let serial = serde_json::to_string(&a).unwrap(); println!("Encode {:?} => {:?}", a, serial); let res = serde_json::from_str::>(&serial); @@ -156,7 +156,7 @@ fn serial_many_dim_serde_msgpack() { { // Test a sliced array. let mut a = ArcArray::linspace(0., 31., 32).reshape((2, 2, 2, 4)); - a.slice_collapse(s![..;-1, .., .., ..2]); + a.slice_collapse(s![..;-1, .., .., ..2]).unwrap(); let mut buf = Vec::new(); serde::Serialize::serialize(&a, &mut rmp_serde::Serializer::new(&mut buf)) @@ -209,7 +209,7 @@ fn serial_many_dim_ron() { { // Test a sliced array. let mut a = ArcArray::linspace(0., 31., 32).reshape((2, 2, 2, 4)); - a.slice_collapse(s![..;-1, .., .., ..2]); + a.slice_collapse(s![..;-1, .., .., ..2]).unwrap(); let a_s = ron_serialize(&a).unwrap(); diff --git a/src/impl_methods.rs b/src/impl_methods.rs index e9f87583f..354eb0534 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -461,13 +461,15 @@ where /// Slice the array in place without changing the number of dimensions. /// - /// Note that `NewAxis` elements in `info` are ignored. + /// If there are any `NewAxis` elements in `info`, slicing is performed + /// using the other elements in `info` (i.e. ignoring the `NewAxis` + /// elements), and `Err(_)` is returned to notify the caller. /// /// See [*Slicing*](#slicing) for full documentation. /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) - pub fn slice_collapse(&mut self, info: &I) + pub fn slice_collapse(&mut self, info: &I) -> Result<(), ShapeError> where I: CanSlice + ?Sized, { @@ -476,20 +478,28 @@ where self.ndim(), "The input dimension of `info` must match the array to be sliced.", ); + let mut new_axis_in_info = false; let mut axis = 0; info.as_ref().iter().for_each(|&ax_info| match ax_info { - AxisSliceInfo::Slice { start, end, step } => { - self.slice_axis_inplace(Axis(axis), Slice { start, end, step }); - axis += 1; - } - AxisSliceInfo::Index(index) => { - let i_usize = abs_index(self.len_of(Axis(axis)), index); - self.collapse_axis(Axis(axis), i_usize); - axis += 1; - } - AxisSliceInfo::NewAxis => {} - }); + AxisSliceInfo::Slice { start, end, step } => { + self.slice_axis_inplace(Axis(axis), Slice { start, end, step }); + axis += 1; + } + AxisSliceInfo::Index(index) => { + let i_usize = abs_index(self.len_of(Axis(axis)), index); + self.collapse_axis(Axis(axis), i_usize); + axis += 1; + } + AxisSliceInfo::NewAxis => { + new_axis_in_info = true; + } + }); debug_assert_eq!(axis, self.ndim()); + if new_axis_in_info { + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)) + } else { + Ok(()) + } } /// Return a view of the array, sliced along the specified axis. diff --git a/tests/array.rs b/tests/array.rs index ac4791d6f..8e5c01f53 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -103,10 +103,10 @@ fn test_slice_ix0() { #[test] fn test_slice_edge_cases() { let mut arr = Array3::::zeros((3, 4, 5)); - arr.slice_collapse(s![0..0;-1, .., ..]); + arr.slice_collapse(s![0..0;-1, .., ..]).unwrap(); assert_eq!(arr.shape(), &[0, 4, 5]); let mut arr = Array2::::from_shape_vec((1, 1).strides((10, 1)), vec![5]).unwrap(); - arr.slice_collapse(s![1..1, ..]); + arr.slice_collapse(s![1..1, ..]).unwrap(); assert_eq!(arr.shape(), &[0, 1]); } @@ -201,7 +201,7 @@ fn test_slice_array_fixed() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info); + arr.view().slice_collapse(info).unwrap_err(); } #[test] @@ -211,7 +211,7 @@ fn test_slice_dyninput_array_fixed() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info); + arr.view().slice_collapse(info).unwrap_err(); } #[test] @@ -227,7 +227,7 @@ fn test_slice_array_dyn() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info); + arr.view().slice_collapse(info).unwrap_err(); } #[test] @@ -243,7 +243,7 @@ fn test_slice_dyninput_array_dyn() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info); + arr.view().slice_collapse(info).unwrap_err(); } #[test] @@ -259,7 +259,7 @@ fn test_slice_dyninput_vec_fixed() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info); + arr.view().slice_collapse(info).unwrap_err(); } #[test] @@ -275,7 +275,7 @@ fn test_slice_dyninput_vec_dyn() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info); + arr.view().slice_collapse(info).unwrap_err(); } #[test] @@ -324,7 +324,7 @@ fn test_slice_collapse_with_indices() { { let mut vi = arr.view(); - vi.slice_collapse(s![NewAxis, 1.., 2, ..;2]); + vi.slice_collapse(s![NewAxis, 1.., 2, ..;2]).unwrap_err(); assert_eq!(vi.shape(), &[2, 1, 2]); assert!(vi .iter() @@ -332,7 +332,7 @@ fn test_slice_collapse_with_indices() { .all(|(a, b)| a == b)); let mut vi = arr.view(); - vi.slice_collapse(s![1, NewAxis, 2, ..;2]); + vi.slice_collapse(s![1, NewAxis, 2, ..;2]).unwrap_err(); assert_eq!(vi.shape(), &[1, 1, 2]); assert!(vi .iter() @@ -340,7 +340,7 @@ fn test_slice_collapse_with_indices() { .all(|(a, b)| a == b)); let mut vi = arr.view(); - vi.slice_collapse(s![1, 2, NewAxis, 3]); + vi.slice_collapse(s![1, 2, 3]).unwrap(); assert_eq!(vi.shape(), &[1, 1, 1]); assert_eq!(vi, Array3::from_elem((1, 1, 1), arr[(1, 2, 3)])); } @@ -348,7 +348,7 @@ fn test_slice_collapse_with_indices() { // Do it to the ArcArray itself let elem = arr[(1, 2, 3)]; let mut vi = arr; - vi.slice_collapse(s![1, 2, 3, NewAxis]); + vi.slice_collapse(s![1, 2, 3, NewAxis]).unwrap_err(); assert_eq!(vi.shape(), &[1, 1, 1]); assert_eq!(vi, Array3::from_elem((1, 1, 1), elem)); } @@ -567,7 +567,7 @@ fn test_cow() { assert_eq!(n[[0, 1]], 0); assert_eq!(n.get((0, 1)), Some(&0)); let mut rev = mat.reshape(4); - rev.slice_collapse(s![..;-1]); + rev.slice_collapse(s![..;-1]).unwrap(); assert_eq!(rev[0], 4); assert_eq!(rev[1], 3); assert_eq!(rev[2], 2); @@ -591,7 +591,7 @@ fn test_cow_shrink() { // mutation shrinks the array and gives it different strides // let mut mat = ArcArray::zeros((2, 3)); - //mat.slice_collapse(s![.., ..;2]); + //mat.slice_collapse(s![.., ..;2]).unwrap(); mat[[0, 0]] = 1; let n = mat.clone(); mat[[0, 1]] = 2; @@ -606,7 +606,7 @@ fn test_cow_shrink() { assert_eq!(n.get((0, 1)), Some(&0)); // small has non-C strides this way let mut small = mat.reshape(6); - small.slice_collapse(s![4..;-1]); + small.slice_collapse(s![4..;-1]).unwrap(); assert_eq!(small[0], 6); assert_eq!(small[1], 5); let before = small.clone(); @@ -886,7 +886,7 @@ fn assign() { let mut a = arr2(&[[1, 2], [3, 4]]); { let mut v = a.view_mut(); - v.slice_collapse(s![..1, ..]); + v.slice_collapse(s![..1, ..]).unwrap(); v.fill(0); } assert_eq!(a, arr2(&[[0, 0], [3, 4]])); @@ -1093,7 +1093,7 @@ fn owned_array_discontiguous_drop() { .collect(); let mut a = Array::from_shape_vec((2, 6), v).unwrap(); // discontiguous and non-zero offset - a.slice_collapse(s![.., 1..]); + a.slice_collapse(s![.., 1..]).unwrap(); } // each item was dropped exactly once itertools::assert_equal(set.borrow().iter().cloned(), 0..12); @@ -1792,7 +1792,7 @@ fn to_owned_memory_order() { #[test] fn to_owned_neg_stride() { let mut c = arr2(&[[1, 2, 3], [4, 5, 6]]); - c.slice_collapse(s![.., ..;-1]); + c.slice_collapse(s![.., ..;-1]).unwrap(); let co = c.to_owned(); assert_eq!(c, co); assert_eq!(c.strides(), co.strides()); @@ -1801,7 +1801,7 @@ fn to_owned_neg_stride() { #[test] fn discontiguous_owned_to_owned() { let mut c = arr2(&[[1, 2, 3], [4, 5, 6]]); - c.slice_collapse(s![.., ..;2]); + c.slice_collapse(s![.., ..;2]).unwrap(); let co = c.to_owned(); assert_eq!(c.strides(), &[3, 2]); @@ -2062,10 +2062,10 @@ fn test_accumulate_axis_inplace_nonstandard_layout() { fn test_to_vec() { let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); - a.slice_collapse(s![..;-1, ..]); + a.slice_collapse(s![..;-1, ..]).unwrap(); assert_eq!(a.row(3).to_vec(), vec![1, 2, 3]); assert_eq!(a.column(2).to_vec(), vec![12, 9, 6, 3]); - a.slice_collapse(s![.., ..;-1]); + a.slice_collapse(s![.., ..;-1]).unwrap(); assert_eq!(a.row(3).to_vec(), vec![3, 2, 1]); } @@ -2081,7 +2081,7 @@ fn test_array_clone_unalias() { #[test] fn test_array_clone_same_view() { let mut a = Array::from_iter(0..9).into_shape((3, 3)).unwrap(); - a.slice_collapse(s![..;-1, ..;-1]); + a.slice_collapse(s![..;-1, ..;-1]).unwrap(); let b = a.clone(); assert_eq!(a, b); } diff --git a/tests/iterators.rs b/tests/iterators.rs index 4e4bbc666..20693e228 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -332,7 +332,7 @@ fn axis_iter_zip_partially_consumed_discontiguous() { while iter.next().is_some() { consumed += 1; let mut b = Array::zeros((a.len() - consumed) * 2); - b.slice_collapse(s![..;2]); + b.slice_collapse(s![..;2]).unwrap(); Zip::from(&mut b).and(iter.clone()).for_each(|b, a| *b = a[()]); assert_eq!(a.slice(s![consumed..]), b); } @@ -519,7 +519,7 @@ fn axis_iter_mut_zip_partially_consumed_discontiguous() { iter.next(); } let mut b = Array::zeros(remaining * 2); - b.slice_collapse(s![..;2]); + b.slice_collapse(s![..;2]).unwrap(); Zip::from(&mut b).and(iter).for_each(|b, a| *b = a[()]); assert_eq!(a.slice(s![consumed..]), b); } From 5202a5021f0801d2ebd0771cc4c4ab27399d5a5d Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 15 Feb 2021 22:54:17 -0500 Subject: [PATCH 227/651] Expose CanSlice trait in public API This makes it visible in the docs, but the private marker trick prevents other crates from implementing it. --- src/lib.rs | 2 +- src/slice.rs | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index d82a99649..9a4f67394 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -141,7 +141,7 @@ pub use crate::dimension::IxDynImpl; pub use crate::dimension::NdIndex; pub use crate::error::{ErrorKind, ShapeError}; pub use crate::indexes::{indices, indices_of}; -pub use crate::slice::{AxisSliceInfo, NewAxis, Slice, SliceArg, SliceInfo}; +pub use crate::slice::{AxisSliceInfo, CanSlice, NewAxis, Slice, SliceArg, SliceInfo}; use crate::iterators::Baseiter; use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes}; diff --git a/src/slice.rs b/src/slice.rs index f2da78038..597507246 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -312,11 +312,16 @@ impl From for AxisSliceInfo { /// consistent with the `&[AxisSliceInfo]` returned by `self.as_ref()` and that /// `self.as_ref()` always returns the same value when called multiple times. pub unsafe trait CanSlice: AsRef<[AxisSliceInfo]> { + /// Dimensionality of the output array. type OutDim: Dimension; + /// Returns the number of axes in the input array. fn in_ndim(&self) -> usize; + /// Returns the number of axes in the output array. fn out_ndim(&self) -> usize; + + private_decl! {} } macro_rules! impl_canslice_samedim { @@ -335,6 +340,8 @@ macro_rules! impl_canslice_samedim { fn out_ndim(&self) -> usize { self.out_ndim() } + + private_impl! {} } }; } @@ -361,6 +368,8 @@ where fn out_ndim(&self) -> usize { self.out_ndim() } + + private_impl! {} } unsafe impl CanSlice for [AxisSliceInfo] { @@ -373,6 +382,8 @@ unsafe impl CanSlice for [AxisSliceInfo] { fn out_ndim(&self) -> usize { self.iter().filter(|s| !s.is_index()).count() } + + private_impl! {} } /// Represents all of the necessary information to perform a slice. From 319701de7281638b8fb7d7c93d82c54f4257131c Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 15 Feb 2021 23:02:50 -0500 Subject: [PATCH 228/651] Expose MultiSlice trait in public API This makes it visible in the docs, but the private marker trick prevents other crates from implementing it. --- src/lib.rs | 2 +- src/slice.rs | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 9a4f67394..a0f28e352 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -141,7 +141,7 @@ pub use crate::dimension::IxDynImpl; pub use crate::dimension::NdIndex; pub use crate::error::{ErrorKind, ShapeError}; pub use crate::indexes::{indices, indices_of}; -pub use crate::slice::{AxisSliceInfo, CanSlice, NewAxis, Slice, SliceArg, SliceInfo}; +pub use crate::slice::{AxisSliceInfo, CanSlice, MultiSlice, NewAxis, Slice, SliceArg, SliceInfo}; use crate::iterators::Baseiter; use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes}; diff --git a/src/slice.rs b/src/slice.rs index 597507246..8021d9a83 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -915,6 +915,8 @@ where /// **Panics** if performing any individual slice panics or if the slices /// are not disjoint (i.e. if they intersect). fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output; + + private_decl! {} } impl<'a, A, D> MultiSlice<'a, A, D> for () @@ -925,6 +927,8 @@ where type Output = (); fn multi_slice_move(&self, _view: ArrayViewMut<'a, A, D>) -> Self::Output {} + + private_impl! {} } impl<'a, A, D, I0> MultiSlice<'a, A, D> for (&I0,) @@ -938,6 +942,8 @@ where fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output { (view.slice_move(self.0),) } + + private_impl! {} } macro_rules! impl_multislice_tuple { @@ -968,6 +974,8 @@ macro_rules! impl_multislice_tuple { ) } } + + private_impl! {} } }; (@intersects_self $shape:expr, ($head:expr,)) => { @@ -996,4 +1004,6 @@ where fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output { T::multi_slice_move(self, view) } + + private_impl! {} } From d5d6482d3b3a524257bca2e06b2cc30036ead440 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 16 Feb 2021 01:12:24 -0500 Subject: [PATCH 229/651] Add DimAdd bounds to Dimension trait This reduces how often an explicit `DimAdd` bound is necessary. --- src/dimension/dimension_trait.rs | 7 +++++++ src/dimension/ops.rs | 2 +- src/slice.rs | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index df38904f4..a49cc815e 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -13,6 +13,7 @@ use alloc::vec::Vec; use super::axes_of; use super::conversion::Convert; +use super::ops::DimAdd; use super::{stride_offset, stride_offset_checked}; use crate::itertools::{enumerate, zip}; use crate::{Axis, DimMax}; @@ -51,6 +52,12 @@ pub trait Dimension: + DimMax + DimMax<::Smaller, Output=Self> + DimMax<::Larger, Output=::Larger> + + DimAdd + + DimAdd<::Smaller> + + DimAdd<::Larger> + + DimAdd + + DimAdd::Larger> + + DimAdd { /// For fixed-size dimension representations (e.g. `Ix2`), this should be /// `Some(ndim)`, and for variable-size dimension representations (e.g. diff --git a/src/dimension/ops.rs b/src/dimension/ops.rs index 855b3b4e1..10855f6c7 100644 --- a/src/dimension/ops.rs +++ b/src/dimension/ops.rs @@ -1,7 +1,7 @@ use crate::imp_prelude::*; /// Adds the two dimensions at compile time. -pub trait DimAdd: Dimension { +pub trait DimAdd { /// The sum of the two dimensions. type Out: Dimension; } diff --git a/src/slice.rs b/src/slice.rs index 8021d9a83..e11543b72 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -661,14 +661,14 @@ pub trait SliceArg { /// Number of dimensions that this slicing argument produces in the output array. type OutDim: Dimension; - fn next_in_dim(&self, _: PhantomData) -> PhantomData + fn next_in_dim(&self, _: PhantomData) -> PhantomData<>::Out> where D: Dimension + DimAdd, { PhantomData } - fn next_out_dim(&self, _: PhantomData) -> PhantomData + fn next_out_dim(&self, _: PhantomData) -> PhantomData<>::Out> where D: Dimension + DimAdd, { From 9614b13701b5389e10ae5d2a483cf06265450b15 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 16 Feb 2021 22:34:35 -0500 Subject: [PATCH 230/651] Revert "Make slice_collapse return Err(_) for NewAxis" This reverts commit 381546267a102d0dfa258b8e037a811a6770445e. --- examples/axis_ops.rs | 4 +-- serialization-tests/tests/serialize.rs | 6 ++-- src/impl_methods.rs | 36 ++++++++------------- tests/array.rs | 44 +++++++++++++------------- tests/iterators.rs | 4 +-- 5 files changed, 42 insertions(+), 52 deletions(-) diff --git a/examples/axis_ops.rs b/examples/axis_ops.rs index 1ff4a3105..624af32c3 100644 --- a/examples/axis_ops.rs +++ b/examples/axis_ops.rs @@ -51,7 +51,7 @@ fn main() { } a.swap_axes(0, 1); a.swap_axes(0, 2); - a.slice_collapse(s![.., ..;-1, ..]).unwrap(); + a.slice_collapse(s![.., ..;-1, ..]); regularize(&mut a).ok(); let mut b = Array::::zeros((2, 3, 4)); @@ -68,6 +68,6 @@ fn main() { for (i, elt) in (0..).zip(&mut a) { *elt = i; } - a.slice_collapse(s![..;-1, ..;2, ..]).unwrap(); + a.slice_collapse(s![..;-1, ..;2, ..]); regularize(&mut a).ok(); } diff --git a/serialization-tests/tests/serialize.rs b/serialization-tests/tests/serialize.rs index 0afaffe1b..efb3bacd9 100644 --- a/serialization-tests/tests/serialize.rs +++ b/serialization-tests/tests/serialize.rs @@ -46,7 +46,7 @@ fn serial_many_dim_serde() { { // Test a sliced array. let mut a = ArcArray::linspace(0., 31., 32).reshape((2, 2, 2, 4)); - a.slice_collapse(s![..;-1, .., .., ..2]).unwrap(); + a.slice_collapse(s![..;-1, .., .., ..2]); let serial = serde_json::to_string(&a).unwrap(); println!("Encode {:?} => {:?}", a, serial); let res = serde_json::from_str::>(&serial); @@ -156,7 +156,7 @@ fn serial_many_dim_serde_msgpack() { { // Test a sliced array. let mut a = ArcArray::linspace(0., 31., 32).reshape((2, 2, 2, 4)); - a.slice_collapse(s![..;-1, .., .., ..2]).unwrap(); + a.slice_collapse(s![..;-1, .., .., ..2]); let mut buf = Vec::new(); serde::Serialize::serialize(&a, &mut rmp_serde::Serializer::new(&mut buf)) @@ -209,7 +209,7 @@ fn serial_many_dim_ron() { { // Test a sliced array. let mut a = ArcArray::linspace(0., 31., 32).reshape((2, 2, 2, 4)); - a.slice_collapse(s![..;-1, .., .., ..2]).unwrap(); + a.slice_collapse(s![..;-1, .., .., ..2]); let a_s = ron_serialize(&a).unwrap(); diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 354eb0534..e9f87583f 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -461,15 +461,13 @@ where /// Slice the array in place without changing the number of dimensions. /// - /// If there are any `NewAxis` elements in `info`, slicing is performed - /// using the other elements in `info` (i.e. ignoring the `NewAxis` - /// elements), and `Err(_)` is returned to notify the caller. + /// Note that `NewAxis` elements in `info` are ignored. /// /// See [*Slicing*](#slicing) for full documentation. /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) - pub fn slice_collapse(&mut self, info: &I) -> Result<(), ShapeError> + pub fn slice_collapse(&mut self, info: &I) where I: CanSlice + ?Sized, { @@ -478,28 +476,20 @@ where self.ndim(), "The input dimension of `info` must match the array to be sliced.", ); - let mut new_axis_in_info = false; let mut axis = 0; info.as_ref().iter().for_each(|&ax_info| match ax_info { - AxisSliceInfo::Slice { start, end, step } => { - self.slice_axis_inplace(Axis(axis), Slice { start, end, step }); - axis += 1; - } - AxisSliceInfo::Index(index) => { - let i_usize = abs_index(self.len_of(Axis(axis)), index); - self.collapse_axis(Axis(axis), i_usize); - axis += 1; - } - AxisSliceInfo::NewAxis => { - new_axis_in_info = true; - } - }); + AxisSliceInfo::Slice { start, end, step } => { + self.slice_axis_inplace(Axis(axis), Slice { start, end, step }); + axis += 1; + } + AxisSliceInfo::Index(index) => { + let i_usize = abs_index(self.len_of(Axis(axis)), index); + self.collapse_axis(Axis(axis), i_usize); + axis += 1; + } + AxisSliceInfo::NewAxis => {} + }); debug_assert_eq!(axis, self.ndim()); - if new_axis_in_info { - Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)) - } else { - Ok(()) - } } /// Return a view of the array, sliced along the specified axis. diff --git a/tests/array.rs b/tests/array.rs index 8e5c01f53..ac4791d6f 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -103,10 +103,10 @@ fn test_slice_ix0() { #[test] fn test_slice_edge_cases() { let mut arr = Array3::::zeros((3, 4, 5)); - arr.slice_collapse(s![0..0;-1, .., ..]).unwrap(); + arr.slice_collapse(s![0..0;-1, .., ..]); assert_eq!(arr.shape(), &[0, 4, 5]); let mut arr = Array2::::from_shape_vec((1, 1).strides((10, 1)), vec![5]).unwrap(); - arr.slice_collapse(s![1..1, ..]).unwrap(); + arr.slice_collapse(s![1..1, ..]); assert_eq!(arr.shape(), &[0, 1]); } @@ -201,7 +201,7 @@ fn test_slice_array_fixed() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info).unwrap_err(); + arr.view().slice_collapse(info); } #[test] @@ -211,7 +211,7 @@ fn test_slice_dyninput_array_fixed() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info).unwrap_err(); + arr.view().slice_collapse(info); } #[test] @@ -227,7 +227,7 @@ fn test_slice_array_dyn() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info).unwrap_err(); + arr.view().slice_collapse(info); } #[test] @@ -243,7 +243,7 @@ fn test_slice_dyninput_array_dyn() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info).unwrap_err(); + arr.view().slice_collapse(info); } #[test] @@ -259,7 +259,7 @@ fn test_slice_dyninput_vec_fixed() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info).unwrap_err(); + arr.view().slice_collapse(info); } #[test] @@ -275,7 +275,7 @@ fn test_slice_dyninput_vec_dyn() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info).unwrap_err(); + arr.view().slice_collapse(info); } #[test] @@ -324,7 +324,7 @@ fn test_slice_collapse_with_indices() { { let mut vi = arr.view(); - vi.slice_collapse(s![NewAxis, 1.., 2, ..;2]).unwrap_err(); + vi.slice_collapse(s![NewAxis, 1.., 2, ..;2]); assert_eq!(vi.shape(), &[2, 1, 2]); assert!(vi .iter() @@ -332,7 +332,7 @@ fn test_slice_collapse_with_indices() { .all(|(a, b)| a == b)); let mut vi = arr.view(); - vi.slice_collapse(s![1, NewAxis, 2, ..;2]).unwrap_err(); + vi.slice_collapse(s![1, NewAxis, 2, ..;2]); assert_eq!(vi.shape(), &[1, 1, 2]); assert!(vi .iter() @@ -340,7 +340,7 @@ fn test_slice_collapse_with_indices() { .all(|(a, b)| a == b)); let mut vi = arr.view(); - vi.slice_collapse(s![1, 2, 3]).unwrap(); + vi.slice_collapse(s![1, 2, NewAxis, 3]); assert_eq!(vi.shape(), &[1, 1, 1]); assert_eq!(vi, Array3::from_elem((1, 1, 1), arr[(1, 2, 3)])); } @@ -348,7 +348,7 @@ fn test_slice_collapse_with_indices() { // Do it to the ArcArray itself let elem = arr[(1, 2, 3)]; let mut vi = arr; - vi.slice_collapse(s![1, 2, 3, NewAxis]).unwrap_err(); + vi.slice_collapse(s![1, 2, 3, NewAxis]); assert_eq!(vi.shape(), &[1, 1, 1]); assert_eq!(vi, Array3::from_elem((1, 1, 1), elem)); } @@ -567,7 +567,7 @@ fn test_cow() { assert_eq!(n[[0, 1]], 0); assert_eq!(n.get((0, 1)), Some(&0)); let mut rev = mat.reshape(4); - rev.slice_collapse(s![..;-1]).unwrap(); + rev.slice_collapse(s![..;-1]); assert_eq!(rev[0], 4); assert_eq!(rev[1], 3); assert_eq!(rev[2], 2); @@ -591,7 +591,7 @@ fn test_cow_shrink() { // mutation shrinks the array and gives it different strides // let mut mat = ArcArray::zeros((2, 3)); - //mat.slice_collapse(s![.., ..;2]).unwrap(); + //mat.slice_collapse(s![.., ..;2]); mat[[0, 0]] = 1; let n = mat.clone(); mat[[0, 1]] = 2; @@ -606,7 +606,7 @@ fn test_cow_shrink() { assert_eq!(n.get((0, 1)), Some(&0)); // small has non-C strides this way let mut small = mat.reshape(6); - small.slice_collapse(s![4..;-1]).unwrap(); + small.slice_collapse(s![4..;-1]); assert_eq!(small[0], 6); assert_eq!(small[1], 5); let before = small.clone(); @@ -886,7 +886,7 @@ fn assign() { let mut a = arr2(&[[1, 2], [3, 4]]); { let mut v = a.view_mut(); - v.slice_collapse(s![..1, ..]).unwrap(); + v.slice_collapse(s![..1, ..]); v.fill(0); } assert_eq!(a, arr2(&[[0, 0], [3, 4]])); @@ -1093,7 +1093,7 @@ fn owned_array_discontiguous_drop() { .collect(); let mut a = Array::from_shape_vec((2, 6), v).unwrap(); // discontiguous and non-zero offset - a.slice_collapse(s![.., 1..]).unwrap(); + a.slice_collapse(s![.., 1..]); } // each item was dropped exactly once itertools::assert_equal(set.borrow().iter().cloned(), 0..12); @@ -1792,7 +1792,7 @@ fn to_owned_memory_order() { #[test] fn to_owned_neg_stride() { let mut c = arr2(&[[1, 2, 3], [4, 5, 6]]); - c.slice_collapse(s![.., ..;-1]).unwrap(); + c.slice_collapse(s![.., ..;-1]); let co = c.to_owned(); assert_eq!(c, co); assert_eq!(c.strides(), co.strides()); @@ -1801,7 +1801,7 @@ fn to_owned_neg_stride() { #[test] fn discontiguous_owned_to_owned() { let mut c = arr2(&[[1, 2, 3], [4, 5, 6]]); - c.slice_collapse(s![.., ..;2]).unwrap(); + c.slice_collapse(s![.., ..;2]); let co = c.to_owned(); assert_eq!(c.strides(), &[3, 2]); @@ -2062,10 +2062,10 @@ fn test_accumulate_axis_inplace_nonstandard_layout() { fn test_to_vec() { let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); - a.slice_collapse(s![..;-1, ..]).unwrap(); + a.slice_collapse(s![..;-1, ..]); assert_eq!(a.row(3).to_vec(), vec![1, 2, 3]); assert_eq!(a.column(2).to_vec(), vec![12, 9, 6, 3]); - a.slice_collapse(s![.., ..;-1]).unwrap(); + a.slice_collapse(s![.., ..;-1]); assert_eq!(a.row(3).to_vec(), vec![3, 2, 1]); } @@ -2081,7 +2081,7 @@ fn test_array_clone_unalias() { #[test] fn test_array_clone_same_view() { let mut a = Array::from_iter(0..9).into_shape((3, 3)).unwrap(); - a.slice_collapse(s![..;-1, ..;-1]).unwrap(); + a.slice_collapse(s![..;-1, ..;-1]); let b = a.clone(); assert_eq!(a, b); } diff --git a/tests/iterators.rs b/tests/iterators.rs index 20693e228..4e4bbc666 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -332,7 +332,7 @@ fn axis_iter_zip_partially_consumed_discontiguous() { while iter.next().is_some() { consumed += 1; let mut b = Array::zeros((a.len() - consumed) * 2); - b.slice_collapse(s![..;2]).unwrap(); + b.slice_collapse(s![..;2]); Zip::from(&mut b).and(iter.clone()).for_each(|b, a| *b = a[()]); assert_eq!(a.slice(s![consumed..]), b); } @@ -519,7 +519,7 @@ fn axis_iter_mut_zip_partially_consumed_discontiguous() { iter.next(); } let mut b = Array::zeros(remaining * 2); - b.slice_collapse(s![..;2]).unwrap(); + b.slice_collapse(s![..;2]); Zip::from(&mut b).and(iter).for_each(|b, a| *b = a[()]); assert_eq!(a.slice(s![consumed..]), b); } From 61cf7c049f6195f80dc51b0064d42ecbe7aa7d03 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 16 Feb 2021 22:59:28 -0500 Subject: [PATCH 231/651] Make slice_collapse panic on NewAxis --- src/impl_methods.rs | 13 ++++++----- src/lib.rs | 2 +- src/slice.rs | 16 ++++++++------ tests/array.rs | 53 ++++++++++++++++++++++++++++++++++++--------- 4 files changed, 61 insertions(+), 23 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index e9f87583f..0c40f4c0e 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -461,12 +461,15 @@ where /// Slice the array in place without changing the number of dimensions. /// - /// Note that `NewAxis` elements in `info` are ignored. - /// /// See [*Slicing*](#slicing) for full documentation. /// - /// **Panics** if an index is out of bounds or step size is zero.
- /// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) + /// **Panics** in the following cases: + /// + /// - if an index is out of bounds + /// - if a step size is zero + /// - if [`AxisSliceInfo::NewAxis`] is in `info`, e.g. if [`NewAxis`] was + /// used in the [`s!`] macro + /// - if `D` is `IxDyn` and `info` does not match the number of array axes pub fn slice_collapse(&mut self, info: &I) where I: CanSlice + ?Sized, @@ -487,7 +490,7 @@ where self.collapse_axis(Axis(axis), i_usize); axis += 1; } - AxisSliceInfo::NewAxis => {} + AxisSliceInfo::NewAxis => panic!("`slice_collapse` does not support `NewAxis`."), }); debug_assert_eq!(axis, self.ndim()); } diff --git a/src/lib.rs b/src/lib.rs index a0f28e352..9bc5aa08d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -498,7 +498,7 @@ pub type Ixs = isize; /// is selected and the axis is removed; this selects a subview. See /// [*Subviews*](#subviews) for more information about subviews. If a /// [`NewAxis`] instance is used, a new axis is inserted. Note that -/// [`.slice_collapse()`] ignores `NewAxis` elements and behaves like +/// [`.slice_collapse()`] panics on `NewAxis` elements and behaves like /// [`.collapse_axis()`] by preserving the number of dimensions. /// /// [`.slice()`]: #method.slice diff --git a/src/slice.rs b/src/slice.rs index e11543b72..8582ee8bb 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -723,6 +723,7 @@ impl_slicearg!((), NewAxis, Ix0, Ix1); /// * *slice* `;` *step*: a range constructed from the start and end of a [`Slice`] /// instance, with new step size *step*, to use for slicing that axis. /// * *new-axis*: a [`NewAxis`] instance that represents the creation of a new axis. +/// (Except for [`.slice_collapse()`], which panics on [`NewAxis`] elements.) /// /// [`Slice`]: struct.Slice.html /// [`NewAxis`]: struct.NewAxis.html @@ -734,13 +735,14 @@ impl_slicearg!((), NewAxis, Ix0, Ix1); /// `RangeFull` where `I` is `isize`, `usize`, or `i32`. *step* must be a type /// that can be converted to `isize` with the `as` keyword. /// -/// For example `s![0..4;2, 6, 1..5, NewAxis]` is a slice of the first axis for -/// 0..4 with step size 2, a subview of the second axis at index 6, a slice of -/// the third axis for 1..5 with default step size 1, and a new axis of length -/// 1 at the end of the shape. The input array must have 3 dimensions. The -/// resulting slice would have shape `[2, 4, 1]` for [`.slice()`], -/// [`.slice_mut()`], and [`.slice_move()`], and shape `[2, 1, 4]` for -/// [`.slice_collapse()`]. +/// For example, `s![0..4;2, 6, 1..5, NewAxis]` is a slice of the first axis +/// for 0..4 with step size 2, a subview of the second axis at index 6, a slice +/// of the third axis for 1..5 with default step size 1, and a new axis of +/// length 1 at the end of the shape. The input array must have 3 dimensions. +/// The resulting slice would have shape `[2, 4, 1]` for [`.slice()`], +/// [`.slice_mut()`], and [`.slice_move()`], while [`.slice_collapse()`] would +/// panic. Without the `NewAxis`, i.e. `s![0..4;2, 6, 1..5]`, +/// [`.slice_collapse()`] would result in an array of shape `[2, 1, 4]`. /// /// [`.slice()`]: struct.ArrayBase.html#method.slice /// [`.slice_mut()`]: struct.ArrayBase.html#method.slice_mut diff --git a/tests/array.rs b/tests/array.rs index ac4791d6f..fa5da4419 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -201,7 +201,8 @@ fn test_slice_array_fixed() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info); + let info2 = s![1.., 1, ..;2]; + arr.view().slice_collapse(info2); } #[test] @@ -211,7 +212,8 @@ fn test_slice_dyninput_array_fixed() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info); + let info2 = s![1.., 1, ..;2]; + arr.view().slice_collapse(info2); } #[test] @@ -227,7 +229,13 @@ fn test_slice_array_dyn() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info); + let info2 = &SliceInfo::<_, Ix3, IxDyn>::try_from([ + AxisSliceInfo::from(1..), + AxisSliceInfo::from(1), + AxisSliceInfo::from(..).step_by(2), + ]) + .unwrap(); + arr.view().slice_collapse(info2); } #[test] @@ -243,7 +251,13 @@ fn test_slice_dyninput_array_dyn() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info); + let info2 = &SliceInfo::<_, Ix3, IxDyn>::try_from([ + AxisSliceInfo::from(1..), + AxisSliceInfo::from(1), + AxisSliceInfo::from(..).step_by(2), + ]) + .unwrap(); + arr.view().slice_collapse(info2); } #[test] @@ -259,7 +273,13 @@ fn test_slice_dyninput_vec_fixed() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info); + let info2 = &SliceInfo::<_, Ix3, Ix2>::try_from(vec![ + AxisSliceInfo::from(1..), + AxisSliceInfo::from(1), + AxisSliceInfo::from(..).step_by(2), + ]) + .unwrap(); + arr.view().slice_collapse(info2); } #[test] @@ -275,7 +295,13 @@ fn test_slice_dyninput_vec_dyn() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - arr.view().slice_collapse(info); + let info2 = &SliceInfo::<_, Ix3, IxDyn>::try_from(vec![ + AxisSliceInfo::from(1..), + AxisSliceInfo::from(1), + AxisSliceInfo::from(..).step_by(2), + ]) + .unwrap(); + arr.view().slice_collapse(info2); } #[test] @@ -324,7 +350,7 @@ fn test_slice_collapse_with_indices() { { let mut vi = arr.view(); - vi.slice_collapse(s![NewAxis, 1.., 2, ..;2]); + vi.slice_collapse(s![1.., 2, ..;2]); assert_eq!(vi.shape(), &[2, 1, 2]); assert!(vi .iter() @@ -332,7 +358,7 @@ fn test_slice_collapse_with_indices() { .all(|(a, b)| a == b)); let mut vi = arr.view(); - vi.slice_collapse(s![1, NewAxis, 2, ..;2]); + vi.slice_collapse(s![1, 2, ..;2]); assert_eq!(vi.shape(), &[1, 1, 2]); assert!(vi .iter() @@ -340,7 +366,7 @@ fn test_slice_collapse_with_indices() { .all(|(a, b)| a == b)); let mut vi = arr.view(); - vi.slice_collapse(s![1, 2, NewAxis, 3]); + vi.slice_collapse(s![1, 2, 3]); assert_eq!(vi.shape(), &[1, 1, 1]); assert_eq!(vi, Array3::from_elem((1, 1, 1), arr[(1, 2, 3)])); } @@ -348,11 +374,18 @@ fn test_slice_collapse_with_indices() { // Do it to the ArcArray itself let elem = arr[(1, 2, 3)]; let mut vi = arr; - vi.slice_collapse(s![1, 2, 3, NewAxis]); + vi.slice_collapse(s![1, 2, 3]); assert_eq!(vi.shape(), &[1, 1, 1]); assert_eq!(vi, Array3::from_elem((1, 1, 1), elem)); } +#[test] +#[should_panic] +fn test_slice_collapse_with_newaxis() { + let mut arr = Array2::::zeros((2, 3)); + arr.slice_collapse(s![0, 0, NewAxis]); +} + #[test] fn test_multislice() { macro_rules! do_test { From 91dbf3f987aa044f775ee3e5480aa642ff857d83 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 16 Feb 2021 23:01:27 -0500 Subject: [PATCH 232/651] Rename DimAdd::Out to DimAdd::Output --- src/dimension/dimension_trait.rs | 6 +++--- src/dimension/ops.rs | 12 ++++++------ src/slice.rs | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index a49cc815e..1eace34a7 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -55,9 +55,9 @@ pub trait Dimension: + DimAdd + DimAdd<::Smaller> + DimAdd<::Larger> - + DimAdd - + DimAdd::Larger> - + DimAdd + + DimAdd + + DimAdd::Larger> + + DimAdd { /// For fixed-size dimension representations (e.g. `Ix2`), this should be /// `Some(ndim)`, and for variable-size dimension representations (e.g. diff --git a/src/dimension/ops.rs b/src/dimension/ops.rs index 10855f6c7..dd23216f6 100644 --- a/src/dimension/ops.rs +++ b/src/dimension/ops.rs @@ -3,13 +3,13 @@ use crate::imp_prelude::*; /// Adds the two dimensions at compile time. pub trait DimAdd { /// The sum of the two dimensions. - type Out: Dimension; + type Output: Dimension; } macro_rules! impl_dimadd_const_out_const { ($lhs:expr, $rhs:expr) => { impl DimAdd> for Dim<[usize; $lhs]> { - type Out = Dim<[usize; $lhs + $rhs]>; + type Output = Dim<[usize; $lhs + $rhs]>; } }; } @@ -17,18 +17,18 @@ macro_rules! impl_dimadd_const_out_const { macro_rules! impl_dimadd_const_out_dyn { ($lhs:expr, IxDyn) => { impl DimAdd for Dim<[usize; $lhs]> { - type Out = IxDyn; + type Output = IxDyn; } }; ($lhs:expr, $rhs:expr) => { impl DimAdd> for Dim<[usize; $lhs]> { - type Out = IxDyn; + type Output = IxDyn; } }; } impl DimAdd for Ix0 { - type Out = D; + type Output = D; } impl_dimadd_const_out_const!(1, 0); @@ -86,5 +86,5 @@ impl_dimadd_const_out_dyn!(6, 6); impl_dimadd_const_out_dyn!(6, IxDyn); impl DimAdd for IxDyn { - type Out = IxDyn; + type Output = IxDyn; } diff --git a/src/slice.rs b/src/slice.rs index 8582ee8bb..a59fa13ff 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -661,14 +661,14 @@ pub trait SliceArg { /// Number of dimensions that this slicing argument produces in the output array. type OutDim: Dimension; - fn next_in_dim(&self, _: PhantomData) -> PhantomData<>::Out> + fn next_in_dim(&self, _: PhantomData) -> PhantomData<>::Output> where D: Dimension + DimAdd, { PhantomData } - fn next_out_dim(&self, _: PhantomData) -> PhantomData<>::Out> + fn next_out_dim(&self, _: PhantomData) -> PhantomData<>::Output> where D: Dimension + DimAdd, { From 5dc77bd9f18643677388966a7d45555a9b839cbf Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 16 Feb 2021 23:32:37 -0500 Subject: [PATCH 233/651] Rename SliceArg to SliceNextDim --- src/lib.rs | 4 +++- src/slice.rs | 44 ++++++++++++++++++++++---------------------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9bc5aa08d..0da511b01 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -141,7 +141,9 @@ pub use crate::dimension::IxDynImpl; pub use crate::dimension::NdIndex; pub use crate::error::{ErrorKind, ShapeError}; pub use crate::indexes::{indices, indices_of}; -pub use crate::slice::{AxisSliceInfo, CanSlice, MultiSlice, NewAxis, Slice, SliceArg, SliceInfo}; +pub use crate::slice::{ + AxisSliceInfo, CanSlice, MultiSlice, NewAxis, Slice, SliceInfo, SliceNextDim, +}; use crate::iterators::Baseiter; use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes}; diff --git a/src/slice.rs b/src/slice.rs index a59fa13ff..eea10d3e4 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -655,7 +655,7 @@ where /// Trait for determining dimensionality of input and output for [`s!`] macro. #[doc(hidden)] -pub trait SliceArg { +pub trait SliceNextDim { /// Number of dimensions that this slicing argument consumes in the input array. type InDim: Dimension; /// Number of dimensions that this slicing argument produces in the output array. @@ -676,28 +676,28 @@ pub trait SliceArg { } } -macro_rules! impl_slicearg { +macro_rules! impl_slicenextdim { (($($generics:tt)*), $self:ty, $in:ty, $out:ty) => { - impl<$($generics)*> SliceArg for $self { + impl<$($generics)*> SliceNextDim for $self { type InDim = $in; type OutDim = $out; } }; } -impl_slicearg!((), isize, Ix1, Ix0); -impl_slicearg!((), usize, Ix1, Ix0); -impl_slicearg!((), i32, Ix1, Ix0); +impl_slicenextdim!((), isize, Ix1, Ix0); +impl_slicenextdim!((), usize, Ix1, Ix0); +impl_slicenextdim!((), i32, Ix1, Ix0); -impl_slicearg!((T), Range, Ix1, Ix1); -impl_slicearg!((T), RangeInclusive, Ix1, Ix1); -impl_slicearg!((T), RangeFrom, Ix1, Ix1); -impl_slicearg!((T), RangeTo, Ix1, Ix1); -impl_slicearg!((T), RangeToInclusive, Ix1, Ix1); -impl_slicearg!((), RangeFull, Ix1, Ix1); -impl_slicearg!((), Slice, Ix1, Ix1); +impl_slicenextdim!((T), Range, Ix1, Ix1); +impl_slicenextdim!((T), RangeInclusive, Ix1, Ix1); +impl_slicenextdim!((T), RangeFrom, Ix1, Ix1); +impl_slicenextdim!((T), RangeTo, Ix1, Ix1); +impl_slicenextdim!((T), RangeToInclusive, Ix1, Ix1); +impl_slicenextdim!((), RangeFull, Ix1, Ix1); +impl_slicenextdim!((), Slice, Ix1, Ix1); -impl_slicearg!((), NewAxis, Ix0, Ix1); +impl_slicenextdim!((), NewAxis, Ix0, Ix1); /// Slice argument constructor. /// @@ -801,8 +801,8 @@ macro_rules! s( (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr;$s:expr) => { match $r { r => { - let in_dim = $crate::SliceArg::next_in_dim(&r, $in_dim); - let out_dim = $crate::SliceArg::next_out_dim(&r, $out_dim); + let in_dim = $crate::SliceNextDim::next_in_dim(&r, $in_dim); + let out_dim = $crate::SliceNextDim::next_out_dim(&r, $out_dim); #[allow(unsafe_code)] unsafe { $crate::SliceInfo::new_unchecked( @@ -818,8 +818,8 @@ macro_rules! s( (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr) => { match $r { r => { - let in_dim = $crate::SliceArg::next_in_dim(&r, $in_dim); - let out_dim = $crate::SliceArg::next_out_dim(&r, $out_dim); + let in_dim = $crate::SliceNextDim::next_in_dim(&r, $in_dim); + let out_dim = $crate::SliceNextDim::next_out_dim(&r, $out_dim); #[allow(unsafe_code)] unsafe { $crate::SliceInfo::new_unchecked( @@ -844,8 +844,8 @@ macro_rules! s( match $r { r => { $crate::s![@parse - $crate::SliceArg::next_in_dim(&r, $in_dim), - $crate::SliceArg::next_out_dim(&r, $out_dim), + $crate::SliceNextDim::next_in_dim(&r, $in_dim), + $crate::SliceNextDim::next_out_dim(&r, $out_dim), [$($stack)* $crate::s!(@convert r, $s),] $($t)* ] @@ -857,8 +857,8 @@ macro_rules! s( match $r { r => { $crate::s![@parse - $crate::SliceArg::next_in_dim(&r, $in_dim), - $crate::SliceArg::next_out_dim(&r, $out_dim), + $crate::SliceNextDim::next_in_dim(&r, $in_dim), + $crate::SliceNextDim::next_out_dim(&r, $out_dim), [$($stack)* $crate::s!(@convert r),] $($t)* ] From 87515c6672000fe1020b0388bda13f5494eb5f1d Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 17 Feb 2021 00:22:27 -0500 Subject: [PATCH 234/651] Rename CanSlice to SliceArg --- src/dimension/mod.rs | 6 +++--- src/impl_methods.rs | 31 ++++++++++--------------------- src/impl_views/splitting.rs | 6 ++---- src/lib.rs | 2 +- src/slice.rs | 28 ++++++++++++++-------------- 5 files changed, 30 insertions(+), 43 deletions(-) diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index a83c49015..243d5eeea 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -7,7 +7,7 @@ // except according to those terms. use crate::error::{from_kind, ErrorKind, ShapeError}; -use crate::slice::CanSlice; +use crate::slice::SliceArg; use crate::{AxisSliceInfo, Ix, Ixs, Slice}; use num_integer::div_floor; @@ -599,8 +599,8 @@ fn slice_min_max(axis_len: usize, slice: Slice) -> Option<(usize, usize)> { /// Returns `true` iff the slices intersect. pub fn slices_intersect( dim: &D, - indices1: &impl CanSlice, - indices2: &impl CanSlice, + indices1: &impl SliceArg, + indices2: &impl SliceArg, ) -> bool { debug_assert_eq!(indices1.in_ndim(), indices2.in_ndim()); for (&axis_len, &si1, &si2) in izip!( diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 0c40f4c0e..8eaae18a7 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -32,7 +32,7 @@ use crate::iter::{ AxisChunksIter, AxisChunksIterMut, AxisIter, AxisIterMut, ExactChunks, ExactChunksMut, IndexedIter, IndexedIterMut, Iter, IterMut, Lanes, LanesMut, Windows, }; -use crate::slice::{CanSlice, MultiSlice}; +use crate::slice::{MultiSlice, SliceArg}; use crate::stacking::concatenate; use crate::{AxisSliceInfo, NdIndex, Slice}; @@ -334,16 +334,13 @@ where /// Return a sliced view of the array. /// /// See [*Slicing*](#slicing) for full documentation. - /// See also [`SliceInfo`] and [`D::SliceArg`]. - /// - /// [`SliceInfo`]: struct.SliceInfo.html - /// [`D::SliceArg`]: trait.Dimension.html#associatedtype.SliceArg + /// See also [`s!`], [`SliceArg`], and [`SliceInfo`](crate::SliceInfo). /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) pub fn slice(&self, info: &I) -> ArrayView<'_, A, I::OutDim> where - I: CanSlice + ?Sized, + I: SliceArg + ?Sized, S: Data, { self.view().slice_move(info) @@ -352,16 +349,13 @@ where /// Return a sliced read-write view of the array. /// /// See [*Slicing*](#slicing) for full documentation. - /// See also [`SliceInfo`] and [`D::SliceArg`]. - /// - /// [`SliceInfo`]: struct.SliceInfo.html - /// [`D::SliceArg`]: trait.Dimension.html#associatedtype.SliceArg + /// See also [`s!`], [`SliceArg`], and [`SliceInfo`](crate::SliceInfo). /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) pub fn slice_mut(&mut self, info: &I) -> ArrayViewMut<'_, A, I::OutDim> where - I: CanSlice + ?Sized, + I: SliceArg + ?Sized, S: DataMut, { self.view_mut().slice_move(info) @@ -370,10 +364,7 @@ where /// Return multiple disjoint, sliced, mutable views of the array. /// /// See [*Slicing*](#slicing) for full documentation. - /// See also [`SliceInfo`] and [`D::SliceArg`]. - /// - /// [`SliceInfo`]: struct.SliceInfo.html - /// [`D::SliceArg`]: trait.Dimension.html#associatedtype.SliceArg + /// See also [`s!`], [`SliceArg`], and [`SliceInfo`](crate::SliceInfo). /// /// **Panics** if any of the following occur: /// @@ -403,16 +394,13 @@ where /// Slice the array, possibly changing the number of dimensions. /// /// See [*Slicing*](#slicing) for full documentation. - /// See also [`SliceInfo`] and [`D::SliceArg`]. - /// - /// [`SliceInfo`]: struct.SliceInfo.html - /// [`D::SliceArg`]: trait.Dimension.html#associatedtype.SliceArg + /// See also [`s!`], [`SliceArg`], and [`SliceInfo`](crate::SliceInfo). /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) pub fn slice_move(mut self, info: &I) -> ArrayBase where - I: CanSlice + ?Sized, + I: SliceArg + ?Sized, { assert_eq!( info.in_ndim(), @@ -462,6 +450,7 @@ where /// Slice the array in place without changing the number of dimensions. /// /// See [*Slicing*](#slicing) for full documentation. + /// See also [`s!`], [`SliceArg`], and [`SliceInfo`](crate::SliceInfo). /// /// **Panics** in the following cases: /// @@ -472,7 +461,7 @@ where /// - if `D` is `IxDyn` and `info` does not match the number of array axes pub fn slice_collapse(&mut self, info: &I) where - I: CanSlice + ?Sized, + I: SliceArg + ?Sized, { assert_eq!( info.in_ndim(), diff --git a/src/impl_views/splitting.rs b/src/impl_views/splitting.rs index 38d07594a..dd39c7e22 100644 --- a/src/impl_views/splitting.rs +++ b/src/impl_views/splitting.rs @@ -117,12 +117,10 @@ where /// consumes `self` and produces views with lifetimes matching that of /// `self`. /// - /// See [*Slicing*](#slicing) for full documentation. - /// See also [`SliceInfo`] and [`D::SliceArg`]. + /// See [*Slicing*](#slicing) for full documentation. See also [`s!`], + /// [`SliceArg`](crate::SliceArg), and [`SliceInfo`](crate::SliceInfo). /// /// [`.multi_slice_mut()`]: struct.ArrayBase.html#method.multi_slice_mut - /// [`SliceInfo`]: struct.SliceInfo.html - /// [`D::SliceArg`]: trait.Dimension.html#associatedtype.SliceArg /// /// **Panics** if any of the following occur: /// diff --git a/src/lib.rs b/src/lib.rs index 0da511b01..3a6d169cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,7 +142,7 @@ pub use crate::dimension::NdIndex; pub use crate::error::{ErrorKind, ShapeError}; pub use crate::indexes::{indices, indices_of}; pub use crate::slice::{ - AxisSliceInfo, CanSlice, MultiSlice, NewAxis, Slice, SliceInfo, SliceNextDim, + AxisSliceInfo, MultiSlice, NewAxis, Slice, SliceArg, SliceInfo, SliceNextDim, }; use crate::iterators::Baseiter; diff --git a/src/slice.rs b/src/slice.rs index eea10d3e4..c7fceefa7 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -311,7 +311,7 @@ impl From for AxisSliceInfo { /// that `D`, `Self::OutDim`, `self.in_dim()`, and `self.out_ndim()` are /// consistent with the `&[AxisSliceInfo]` returned by `self.as_ref()` and that /// `self.as_ref()` always returns the same value when called multiple times. -pub unsafe trait CanSlice: AsRef<[AxisSliceInfo]> { +pub unsafe trait SliceArg: AsRef<[AxisSliceInfo]> { /// Dimensionality of the output array. type OutDim: Dimension; @@ -324,9 +324,9 @@ pub unsafe trait CanSlice: AsRef<[AxisSliceInfo]> { private_decl! {} } -macro_rules! impl_canslice_samedim { +macro_rules! impl_slicearg_samedim { ($in_dim:ty) => { - unsafe impl CanSlice<$in_dim> for SliceInfo + unsafe impl SliceArg<$in_dim> for SliceInfo where T: AsRef<[AxisSliceInfo]>, Dout: Dimension, @@ -345,15 +345,15 @@ macro_rules! impl_canslice_samedim { } }; } -impl_canslice_samedim!(Ix0); -impl_canslice_samedim!(Ix1); -impl_canslice_samedim!(Ix2); -impl_canslice_samedim!(Ix3); -impl_canslice_samedim!(Ix4); -impl_canslice_samedim!(Ix5); -impl_canslice_samedim!(Ix6); +impl_slicearg_samedim!(Ix0); +impl_slicearg_samedim!(Ix1); +impl_slicearg_samedim!(Ix2); +impl_slicearg_samedim!(Ix3); +impl_slicearg_samedim!(Ix4); +impl_slicearg_samedim!(Ix5); +impl_slicearg_samedim!(Ix6); -unsafe impl CanSlice for SliceInfo +unsafe impl SliceArg for SliceInfo where T: AsRef<[AxisSliceInfo]>, Din: Dimension, @@ -372,7 +372,7 @@ where private_impl! {} } -unsafe impl CanSlice for [AxisSliceInfo] { +unsafe impl SliceArg for [AxisSliceInfo] { type OutDim = IxDyn; fn in_ndim(&self) -> usize { @@ -937,7 +937,7 @@ impl<'a, A, D, I0> MultiSlice<'a, A, D> for (&I0,) where A: 'a, D: Dimension, - I0: CanSlice, + I0: SliceArg, { type Output = (ArrayViewMut<'a, A, I0::OutDim>,); @@ -957,7 +957,7 @@ macro_rules! impl_multislice_tuple { where A: 'a, D: Dimension, - $($all: CanSlice,)* + $($all: SliceArg,)* { type Output = ($(ArrayViewMut<'a, A, $all::OutDim>,)*); From c4efbbfca33e1d993f0216b0bb32a6652a89f74a Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 17 Feb 2021 00:27:13 -0500 Subject: [PATCH 235/651] Rename MultiSlice to MultiSliceArg --- src/impl_methods.rs | 9 +++++---- src/impl_views/splitting.rs | 9 +++++---- src/lib.rs | 2 +- src/slice.rs | 12 ++++++------ 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 8eaae18a7..afd915786 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -32,7 +32,7 @@ use crate::iter::{ AxisChunksIter, AxisChunksIterMut, AxisIter, AxisIterMut, ExactChunks, ExactChunksMut, IndexedIter, IndexedIterMut, Iter, IterMut, Lanes, LanesMut, Windows, }; -use crate::slice::{MultiSlice, SliceArg}; +use crate::slice::{MultiSliceArg, SliceArg}; use crate::stacking::concatenate; use crate::{AxisSliceInfo, NdIndex, Slice}; @@ -363,8 +363,9 @@ where /// Return multiple disjoint, sliced, mutable views of the array. /// - /// See [*Slicing*](#slicing) for full documentation. - /// See also [`s!`], [`SliceArg`], and [`SliceInfo`](crate::SliceInfo). + /// See [*Slicing*](#slicing) for full documentation. See also + /// [`MultiSliceArg`], [`s!`], [`SliceArg`], and + /// [`SliceInfo`](crate::SliceInfo). /// /// **Panics** if any of the following occur: /// @@ -385,7 +386,7 @@ where /// ``` pub fn multi_slice_mut<'a, M>(&'a mut self, info: M) -> M::Output where - M: MultiSlice<'a, A, D>, + M: MultiSliceArg<'a, A, D>, S: DataMut, { info.multi_slice_move(self.view_mut()) diff --git a/src/impl_views/splitting.rs b/src/impl_views/splitting.rs index dd39c7e22..a36ae4ddb 100644 --- a/src/impl_views/splitting.rs +++ b/src/impl_views/splitting.rs @@ -7,7 +7,7 @@ // except according to those terms. use crate::imp_prelude::*; -use crate::slice::MultiSlice; +use crate::slice::MultiSliceArg; /// Methods for read-only array views. impl<'a, A, D> ArrayView<'a, A, D> @@ -117,8 +117,9 @@ where /// consumes `self` and produces views with lifetimes matching that of /// `self`. /// - /// See [*Slicing*](#slicing) for full documentation. See also [`s!`], - /// [`SliceArg`](crate::SliceArg), and [`SliceInfo`](crate::SliceInfo). + /// See [*Slicing*](#slicing) for full documentation. See also + /// [`MultiSliceArg`], [`s!`], [`SliceArg`](crate::SliceArg), and + /// [`SliceInfo`](crate::SliceInfo). /// /// [`.multi_slice_mut()`]: struct.ArrayBase.html#method.multi_slice_mut /// @@ -129,7 +130,7 @@ where /// * if `D` is `IxDyn` and `info` does not match the number of array axes pub fn multi_slice_move(self, info: M) -> M::Output where - M: MultiSlice<'a, A, D>, + M: MultiSliceArg<'a, A, D>, { info.multi_slice_move(self) } diff --git a/src/lib.rs b/src/lib.rs index 3a6d169cb..f48da4b32 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,7 +142,7 @@ pub use crate::dimension::NdIndex; pub use crate::error::{ErrorKind, ShapeError}; pub use crate::indexes::{indices, indices_of}; pub use crate::slice::{ - AxisSliceInfo, MultiSlice, NewAxis, Slice, SliceArg, SliceInfo, SliceNextDim, + AxisSliceInfo, MultiSliceArg, NewAxis, Slice, SliceArg, SliceInfo, SliceNextDim, }; use crate::iterators::Baseiter; diff --git a/src/slice.rs b/src/slice.rs index c7fceefa7..43785555d 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -904,7 +904,7 @@ macro_rules! s( /// /// It's unfortunate that we need `'a` and `A` to be parameters of the trait, /// but they're necessary until Rust supports generic associated types. -pub trait MultiSlice<'a, A, D> +pub trait MultiSliceArg<'a, A, D> where A: 'a, D: Dimension, @@ -921,7 +921,7 @@ where private_decl! {} } -impl<'a, A, D> MultiSlice<'a, A, D> for () +impl<'a, A, D> MultiSliceArg<'a, A, D> for () where A: 'a, D: Dimension, @@ -933,7 +933,7 @@ where private_impl! {} } -impl<'a, A, D, I0> MultiSlice<'a, A, D> for (&I0,) +impl<'a, A, D, I0> MultiSliceArg<'a, A, D> for (&I0,) where A: 'a, D: Dimension, @@ -953,7 +953,7 @@ macro_rules! impl_multislice_tuple { impl_multislice_tuple!(@def_impl ($($but_last,)* $last,), [$($but_last)*] $last); }; (@def_impl ($($all:ident,)*), [$($but_last:ident)*] $last:ident) => { - impl<'a, A, D, $($all,)*> MultiSlice<'a, A, D> for ($(&$all,)*) + impl<'a, A, D, $($all,)*> MultiSliceArg<'a, A, D> for ($(&$all,)*) where A: 'a, D: Dimension, @@ -995,11 +995,11 @@ impl_multislice_tuple!([I0 I1 I2] I3); impl_multislice_tuple!([I0 I1 I2 I3] I4); impl_multislice_tuple!([I0 I1 I2 I3 I4] I5); -impl<'a, A, D, T> MultiSlice<'a, A, D> for &T +impl<'a, A, D, T> MultiSliceArg<'a, A, D> for &T where A: 'a, D: Dimension, - T: MultiSlice<'a, A, D>, + T: MultiSliceArg<'a, A, D>, { type Output = T::Output; From 7506f9051a04057297c799eeccdd94963c4e5633 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 17 Feb 2021 00:27:34 -0500 Subject: [PATCH 236/651] Clarify docs of .slice_collapse() --- src/impl_methods.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index afd915786..d039d68a6 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -450,6 +450,14 @@ where /// Slice the array in place without changing the number of dimensions. /// + /// In particular, if an axis is sliced with an index, the axis is + /// collapsed, as in [`.collapse_axis()`], rather than removed, as in + /// [`.slice_move()`] or [`.index_axis_move()`]. + /// + /// [`.collapse_axis()`]: #method.collapse_axis + /// [`.slice_move()`]: #method.slice_move + /// [`.index_axis_move()`]: #method.index_axis_move + /// /// See [*Slicing*](#slicing) for full documentation. /// See also [`s!`], [`SliceArg`], and [`SliceInfo`](crate::SliceInfo). /// From 5e77095ca7dd5e2841af7cc4a35088f0e8cff559 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 13 Mar 2021 14:44:19 +0100 Subject: [PATCH 237/651] API: Add crate feature matrixmultiply-threading This feature enables threading in the matrixmultiply crate. --- Cargo.toml | 2 ++ README.rst | 4 ++++ src/lib.rs | 2 ++ 3 files changed, 8 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 301d1b566..22686c5be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,8 @@ docs = ["approx", "serde", "rayon"] std = ["num-traits/std", "matrixmultiply/std"] rayon = ["rayon_", "std"] +matrixmultiply-threading = ["matrixmultiply/threading"] + [profile.release] [profile.bench] debug = true diff --git a/README.rst b/README.rst index 8884ac9f3..36b98598f 100644 --- a/README.rst +++ b/README.rst @@ -86,6 +86,10 @@ your `Cargo.toml`. Uses ``blas-src`` for pluggable backend, which needs to be configured separately (see below). +- ``matrixmultiply-threading`` + + - Enable the ``threading`` feature in the matrixmultiply package + How to use with cargo --------------------- diff --git a/src/lib.rs b/src/lib.rs index f48da4b32..2183697f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,6 +87,8 @@ //! - Enable transparent BLAS support for matrix multiplication. //! Uses ``blas-src`` for pluggable backend, which needs to be configured //! separately (see the README). +//! - `matrixmultiply-threading` +//! - Enable the ``threading`` feature in the matrixmultiply package //! //! ## Documentation //! From 9c072bee749ba3dfff25f2e697d98f5b001031c1 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 14 Mar 2021 19:49:58 -0400 Subject: [PATCH 238/651] Implement SliceArg for &T --- src/slice.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/slice.rs b/src/slice.rs index 43785555d..d4f70d5e1 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -324,6 +324,24 @@ pub unsafe trait SliceArg: AsRef<[AxisSliceInfo]> { private_decl! {} } +unsafe impl SliceArg for &T +where + T: SliceArg + ?Sized, + D: Dimension, +{ + type OutDim = T::OutDim; + + fn in_ndim(&self) -> usize { + T::in_ndim(self) + } + + fn out_ndim(&self) -> usize { + T::out_ndim(self) + } + + private_impl! {} +} + macro_rules! impl_slicearg_samedim { ($in_dim:ty) => { unsafe impl SliceArg<$in_dim> for SliceInfo From 24c6786ceebdaf953001bf3513331c26e7a51e48 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 14 Mar 2021 19:52:03 -0400 Subject: [PATCH 239/651] Allow passing owned slice arg to slicing methods This allows the `s![]` macro to return an owned type, without requiring an explicit `&` when calling slicing methods. --- src/dimension/mod.rs | 4 ++-- src/impl_methods.rs | 16 ++++++++-------- src/lib.rs | 7 +++---- src/slice.rs | 18 +++++++----------- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 243d5eeea..f94a7135c 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -599,8 +599,8 @@ fn slice_min_max(axis_len: usize, slice: Slice) -> Option<(usize, usize)> { /// Returns `true` iff the slices intersect. pub fn slices_intersect( dim: &D, - indices1: &impl SliceArg, - indices2: &impl SliceArg, + indices1: impl SliceArg, + indices2: impl SliceArg, ) -> bool { debug_assert_eq!(indices1.in_ndim(), indices2.in_ndim()); for (&axis_len, &si1, &si2) in izip!( diff --git a/src/impl_methods.rs b/src/impl_methods.rs index d039d68a6..a0833048e 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -338,9 +338,9 @@ where /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) - pub fn slice(&self, info: &I) -> ArrayView<'_, A, I::OutDim> + pub fn slice(&self, info: I) -> ArrayView<'_, A, I::OutDim> where - I: SliceArg + ?Sized, + I: SliceArg, S: Data, { self.view().slice_move(info) @@ -353,9 +353,9 @@ where /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) - pub fn slice_mut(&mut self, info: &I) -> ArrayViewMut<'_, A, I::OutDim> + pub fn slice_mut(&mut self, info: I) -> ArrayViewMut<'_, A, I::OutDim> where - I: SliceArg + ?Sized, + I: SliceArg, S: DataMut, { self.view_mut().slice_move(info) @@ -399,9 +399,9 @@ where /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) - pub fn slice_move(mut self, info: &I) -> ArrayBase + pub fn slice_move(mut self, info: I) -> ArrayBase where - I: SliceArg + ?Sized, + I: SliceArg, { assert_eq!( info.in_ndim(), @@ -468,9 +468,9 @@ where /// - if [`AxisSliceInfo::NewAxis`] is in `info`, e.g. if [`NewAxis`] was /// used in the [`s!`] macro /// - if `D` is `IxDyn` and `info` does not match the number of array axes - pub fn slice_collapse(&mut self, info: &I) + pub fn slice_collapse(&mut self, info: I) where - I: SliceArg + ?Sized, + I: SliceArg, { assert_eq!( info.in_ndim(), diff --git a/src/lib.rs b/src/lib.rs index f48da4b32..172289efe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -492,9 +492,8 @@ pub type Ixs = isize; /// /// The slicing argument can be passed using the macro [`s![]`](macro.s!.html), /// which will be used in all examples. (The explicit form is an instance of -/// [`&SliceInfo`]; see its docs for more information.) -/// -/// [`&SliceInfo`]: struct.SliceInfo.html +/// [`SliceInfo`] or another type which implements [`SliceArg`]; see their docs +/// for more information.) /// /// If a range is used, the axis is preserved. If an index is used, that index /// is selected and the axis is removed; this selects a subview. See @@ -510,7 +509,7 @@ pub type Ixs = isize; /// [`NewAxis`]: struct.NewAxis.html /// /// When slicing arrays with generic dimensionality, creating an instance of -/// [`&SliceInfo`] to pass to the multi-axis slicing methods like [`.slice()`] +/// [`SliceInfo`] to pass to the multi-axis slicing methods like [`.slice()`] /// is awkward. In these cases, it's usually more convenient to use /// [`.slice_each_axis()`]/[`.slice_each_axis_mut()`]/[`.slice_each_axis_inplace()`] /// or to create a view and then slice individual axes of the view using diff --git a/src/slice.rs b/src/slice.rs index d4f70d5e1..dbb3873e1 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -78,7 +78,7 @@ pub struct NewAxis; /// A slice (range with step), an index, or a new axis token. /// /// See also the [`s![]`](macro.s!.html) macro for a convenient way to create a -/// `&SliceInfo<[AxisSliceInfo; n], Din, Dout>`. +/// `SliceInfo<[AxisSliceInfo; n], Din, Dout>`. /// /// ## Examples /// @@ -721,9 +721,7 @@ impl_slicenextdim!((), NewAxis, Ix0, Ix1); /// /// `s![]` takes a list of ranges/slices/indices/new-axes, separated by comma, /// with optional step sizes that are separated from the range by a semicolon. -/// It is converted into a [`&SliceInfo`] instance. -/// -/// [`&SliceInfo`]: struct.SliceInfo.html +/// It is converted into a [`SliceInfo`] instance. /// /// Each range/slice/index uses signed indices, where a negative value is /// counted from the end of the axis. Step sizes are also signed and may be @@ -907,9 +905,7 @@ macro_rules! s( <$crate::AxisSliceInfo as ::std::convert::From<_>>::from($r).step_by($s as isize) }; ($($t:tt)*) => { - // The extra `*&` is a workaround for this compiler bug: - // https://github.com/rust-lang/rust/issues/23014 - &*&$crate::s![@parse + $crate::s![@parse ::std::marker::PhantomData::<$crate::Ix0>, ::std::marker::PhantomData::<$crate::Ix0>, [] @@ -951,7 +947,7 @@ where private_impl! {} } -impl<'a, A, D, I0> MultiSliceArg<'a, A, D> for (&I0,) +impl<'a, A, D, I0> MultiSliceArg<'a, A, D> for (I0,) where A: 'a, D: Dimension, @@ -960,7 +956,7 @@ where type Output = (ArrayViewMut<'a, A, I0::OutDim>,); fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output { - (view.slice_move(self.0),) + (view.slice_move(&self.0),) } private_impl! {} @@ -971,7 +967,7 @@ macro_rules! impl_multislice_tuple { impl_multislice_tuple!(@def_impl ($($but_last,)* $last,), [$($but_last)*] $last); }; (@def_impl ($($all:ident,)*), [$($but_last:ident)*] $last:ident) => { - impl<'a, A, D, $($all,)*> MultiSliceArg<'a, A, D> for ($(&$all,)*) + impl<'a, A, D, $($all,)*> MultiSliceArg<'a, A, D> for ($($all,)*) where A: 'a, D: Dimension, @@ -981,7 +977,7 @@ macro_rules! impl_multislice_tuple { fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output { #[allow(non_snake_case)] - let &($($all,)*) = self; + let ($($all,)*) = self; let shape = view.raw_dim(); assert!(!impl_multislice_tuple!(@intersects_self &shape, ($($all,)*))); From 4326b2523e7b222c0ac224d0d09eecb2a240ce00 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 14 Mar 2021 21:34:51 -0400 Subject: [PATCH 240/651] Make SliceInfo be Sized, and remove pointer casts The original reason for `SliceInfo` being `?Sized` and these conversions was to work around the limitations of the slicing methods taking a type involving `Dimension::SliceArg`. Now that the slicing methods take `I: SliceArg` as the parameter type, these conversions are no longer necessary, and `SliceInfo` doesn't need to be `?Sized`. --- src/slice.rs | 52 +++++++++++++++------------------------------------- 1 file changed, 15 insertions(+), 37 deletions(-) diff --git a/src/slice.rs b/src/slice.rs index dbb3873e1..03544bb5e 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -406,7 +406,7 @@ unsafe impl SliceArg for [AxisSliceInfo] { /// Represents all of the necessary information to perform a slice. /// -/// The type `T` is typically `[AxisSliceInfo; n]`, `[AxisSliceInfo]`, or +/// The type `T` is typically `[AxisSliceInfo; n]`, `&[AxisSliceInfo]`, or /// `Vec`. The type `Din` is the dimension of the array to be /// sliced, and `Dout` is the output dimension after calling [`.slice()`]. Note /// that if `Din` is a fixed dimension type (`Ix0`, `Ix1`, `Ix2`, etc.), the @@ -415,14 +415,13 @@ unsafe impl SliceArg for [AxisSliceInfo] { /// /// [`.slice()`]: struct.ArrayBase.html#method.slice #[derive(Debug)] -#[repr(transparent)] -pub struct SliceInfo { +pub struct SliceInfo { in_dim: PhantomData, out_dim: PhantomData, indices: T, } -impl Deref for SliceInfo +impl Deref for SliceInfo where Din: Dimension, Dout: Dimension, @@ -482,14 +481,7 @@ where indices, } } -} -impl SliceInfo -where - T: AsRef<[AxisSliceInfo]>, - Din: Dimension, - Dout: Dimension, -{ /// Returns a new `SliceInfo` instance. /// /// Errors if `Din` or `Dout` is not consistent with `indices`. @@ -508,14 +500,7 @@ where indices, }) } -} -impl SliceInfo -where - T: AsRef<[AxisSliceInfo]>, - Din: Dimension, - Dout: Dimension, -{ /// Returns the number of dimensions of the input array for /// [`.slice()`](struct.ArrayBase.html#method.slice). /// @@ -546,7 +531,7 @@ where } } -impl<'a, Din, Dout> TryFrom<&'a [AxisSliceInfo]> for &'a SliceInfo<[AxisSliceInfo], Din, Dout> +impl<'a, Din, Dout> TryFrom<&'a [AxisSliceInfo]> for SliceInfo<&'a [AxisSliceInfo], Din, Dout> where Din: Dimension, Dout: Dimension, @@ -555,16 +540,11 @@ where fn try_from( indices: &'a [AxisSliceInfo], - ) -> Result<&'a SliceInfo<[AxisSliceInfo], Din, Dout>, ShapeError> { - check_dims_for_sliceinfo::(indices)?; + ) -> Result, ShapeError> { unsafe { - // This is okay because we've already checked the correctness of - // `Din` and `Dout`, and the only non-zero-sized member of - // `SliceInfo` is `indices`, so `&SliceInfo<[AxisSliceInfo], Din, - // Dout>` should have the same bitwise representation as - // `&[AxisSliceInfo]`. - Ok(&*(indices as *const [AxisSliceInfo] - as *const SliceInfo<[AxisSliceInfo], Din, Dout>)) + // This is okay because `&[AxisSliceInfo]` always returns the same + // value for `.as_ref()`. + Self::new(indices) } } } @@ -630,20 +610,18 @@ where } } -impl AsRef> for SliceInfo +impl<'a, T, Din, Dout> From<&'a SliceInfo> + for SliceInfo<&'a [AxisSliceInfo], Din, Dout> where T: AsRef<[AxisSliceInfo]>, Din: Dimension, Dout: Dimension, { - fn as_ref(&self) -> &SliceInfo<[AxisSliceInfo], Din, Dout> { - unsafe { - // This is okay because the only non-zero-sized member of - // `SliceInfo` is `indices`, so `&SliceInfo<[AxisSliceInfo], Din, Dout>` - // should have the same bitwise representation as - // `&[AxisSliceInfo]`. - &*(self.indices.as_ref() as *const [AxisSliceInfo] - as *const SliceInfo<[AxisSliceInfo], Din, Dout>) + fn from(info: &'a SliceInfo) -> SliceInfo<&'a [AxisSliceInfo], Din, Dout> { + SliceInfo { + in_dim: info.in_dim, + out_dim: info.out_dim, + indices: info.indices.as_ref(), } } } From fb7e94dd6954bc92a02938fdeb6250820b9afde6 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 14 Mar 2021 22:10:44 -0400 Subject: [PATCH 241/651] Remove unnecessary `&` in tests --- tests/array.rs | 12 ++++++------ tests/oper.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/array.rs b/tests/array.rs index fa5da4419..1cbd34e34 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -219,7 +219,7 @@ fn test_slice_dyninput_array_fixed() { #[test] fn test_slice_array_dyn() { let mut arr = Array3::::zeros((5, 2, 5)); - let info = &SliceInfo::<_, Ix3, IxDyn>::try_from([ + let info = SliceInfo::<_, Ix3, IxDyn>::try_from([ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), AxisSliceInfo::from(NewAxis), @@ -229,7 +229,7 @@ fn test_slice_array_dyn() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - let info2 = &SliceInfo::<_, Ix3, IxDyn>::try_from([ + let info2 = SliceInfo::<_, Ix3, IxDyn>::try_from([ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), AxisSliceInfo::from(..).step_by(2), @@ -241,7 +241,7 @@ fn test_slice_array_dyn() { #[test] fn test_slice_dyninput_array_dyn() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); - let info = &SliceInfo::<_, Ix3, IxDyn>::try_from([ + let info = SliceInfo::<_, Ix3, IxDyn>::try_from([ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), AxisSliceInfo::from(NewAxis), @@ -251,7 +251,7 @@ fn test_slice_dyninput_array_dyn() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - let info2 = &SliceInfo::<_, Ix3, IxDyn>::try_from([ + let info2 = SliceInfo::<_, Ix3, IxDyn>::try_from([ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), AxisSliceInfo::from(..).step_by(2), @@ -273,7 +273,7 @@ fn test_slice_dyninput_vec_fixed() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - let info2 = &SliceInfo::<_, Ix3, Ix2>::try_from(vec![ + let info2 = SliceInfo::<_, Ix3, Ix2>::try_from(vec![ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), AxisSliceInfo::from(..).step_by(2), @@ -295,7 +295,7 @@ fn test_slice_dyninput_vec_dyn() { arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); - let info2 = &SliceInfo::<_, Ix3, IxDyn>::try_from(vec![ + let info2 = SliceInfo::<_, Ix3, IxDyn>::try_from(vec![ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), AxisSliceInfo::from(..).step_by(2), diff --git a/tests/oper.rs b/tests/oper.rs index b91a9bd85..3533faa8e 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -596,7 +596,7 @@ fn scaled_add_3() { { let mut av = a.slice_mut(s![..;s1, ..;s2]); - let c = c.slice(&SliceInfo::<_, IxDyn, IxDyn>::try_from(cslice).unwrap()); + let c = c.slice(SliceInfo::<_, IxDyn, IxDyn>::try_from(cslice).unwrap()); let mut answerv = answer.slice_mut(s![..;s1, ..;s2]); answerv += &(beta * &c); From 71b53ab8a6955d010fbaca2dfb8d718ca0d5297d Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 15 Mar 2021 19:41:57 -0400 Subject: [PATCH 242/651] Check expr type for ;step in s![] at compile time Before, if the `expr;step` notation was used with an index as the `expr`, e.g. `s![4;2]`, the macro would panic at runtime. Now, it errors at compile time. This is accomplished by converting the `expr` to a `Slice` before converting it into the final `AxisSliceInfo`. --- src/slice.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slice.rs b/src/slice.rs index 03544bb5e..468be2453 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -880,7 +880,9 @@ macro_rules! s( }; // convert range/index/new-axis and step into AxisSliceInfo (@convert $r:expr, $s:expr) => { - <$crate::AxisSliceInfo as ::std::convert::From<_>>::from($r).step_by($s as isize) + <$crate::AxisSliceInfo as ::std::convert::From<_>>::from( + <$crate::Slice as ::std::convert::From<_>>::from($r).step_by($s as isize) + ) }; ($($t:tt)*) => { $crate::s![@parse From 7e1f7d74f9b926403b5077226bcffa9df6fb263f Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 15 Mar 2021 19:47:55 -0400 Subject: [PATCH 243/651] Remove AxisSliceInfo::step_by method This method is awkward because it panics for all cases except the `Slice` variant. This method is no longer necessary because the `s![]` macro now calls `Slice::step_by()` instead. --- blas-tests/tests/oper.rs | 10 +++++----- src/slice.rs | 32 ++++---------------------------- tests/array.rs | 16 ++++++++-------- tests/oper.rs | 10 +++++----- 4 files changed, 22 insertions(+), 46 deletions(-) diff --git a/blas-tests/tests/oper.rs b/blas-tests/tests/oper.rs index 6b6797f12..27bf94f09 100644 --- a/blas-tests/tests/oper.rs +++ b/blas-tests/tests/oper.rs @@ -6,7 +6,7 @@ extern crate num_traits; use ndarray::linalg::general_mat_mul; use ndarray::linalg::general_mat_vec_mul; use ndarray::prelude::*; -use ndarray::{AxisSliceInfo, Ix, Ixs}; +use ndarray::{AxisSliceInfo, Ix, Ixs, Slice}; use ndarray::{Data, LinalgScalar}; use approx::{assert_abs_diff_eq, assert_relative_eq}; @@ -419,12 +419,12 @@ fn scaled_add_3() { let mut a = range_mat64(m, k); let mut answer = a.clone(); let cdim = if n == 1 { vec![q] } else { vec![n, q] }; - let cslice = if n == 1 { - vec![AxisSliceInfo::from(..).step_by(s2)] + let cslice: Vec = if n == 1 { + vec![Slice::from(..).step_by(s2).into()] } else { vec![ - AxisSliceInfo::from(..).step_by(s1), - AxisSliceInfo::from(..).step_by(s2), + Slice::from(..).step_by(s1).into(), + Slice::from(..).step_by(s2).into(), ] }; diff --git a/src/slice.rs b/src/slice.rs index 468be2453..a5d65a2e3 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -92,13 +92,13 @@ pub struct NewAxis; /// /// `AxisSliceInfo::Slice { start: a, end: Some(b), step: 2 }` is every second /// element from `a` until `b`. It can also be created with -/// `AxisSliceInfo::from(a..b).step_by(2)`. The Python equivalent is `[a:b:2]`. -/// The macro equivalent is `s![a..b;2]`. +/// `AxisSliceInfo::from(Slice::from(a..b).step_by(2))`. The Python equivalent +/// is `[a:b:2]`. The macro equivalent is `s![a..b;2]`. /// /// `AxisSliceInfo::Slice { start: a, end: None, step: -1 }` is every element, /// from `a` until the end, in reverse order. It can also be created with -/// `AxisSliceInfo::from(a..).step_by(-1)`. The Python equivalent is `[a::-1]`. -/// The macro equivalent is `s![a..;-1]`. +/// `AxisSliceInfo::from(Slice::from(a..).step_by(-1))`. The Python equivalent +/// is `[a::-1]`. The macro equivalent is `s![a..;-1]`. /// /// `AxisSliceInfo::NewAxis` is a new axis of length 1. It can also be created /// with `AxisSliceInfo::from(NewAxis)`. The Python equivalent is @@ -136,30 +136,6 @@ impl AxisSliceInfo { pub fn is_new_axis(&self) -> bool { matches!(self, AxisSliceInfo::NewAxis) } - - /// Returns a new `AxisSliceInfo` with the given step size (multiplied with - /// the previous step size). - /// - /// `step` must be nonzero. - /// (This method checks with a debug assertion that `step` is not zero.) - /// - /// **Panics** if `self` is not the `AxisSliceInfo::Slice` variant. - #[inline] - pub fn step_by(self, step: isize) -> Self { - debug_assert_ne!(step, 0, "AxisSliceInfo::step_by: step must be nonzero"); - match self { - AxisSliceInfo::Slice { - start, - end, - step: orig_step, - } => AxisSliceInfo::Slice { - start, - end, - step: orig_step * step, - }, - _ => panic!("AxisSliceInfo::step_by: `self` must be the `Slice` variant"), - } - } } impl fmt::Display for AxisSliceInfo { diff --git a/tests/array.rs b/tests/array.rs index 1cbd34e34..6bcdf7633 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -223,7 +223,7 @@ fn test_slice_array_dyn() { AxisSliceInfo::from(1..), AxisSliceInfo::from(1), AxisSliceInfo::from(NewAxis), - AxisSliceInfo::from(..).step_by(2), + AxisSliceInfo::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.slice(info); @@ -232,7 +232,7 @@ fn test_slice_array_dyn() { let info2 = SliceInfo::<_, Ix3, IxDyn>::try_from([ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), - AxisSliceInfo::from(..).step_by(2), + AxisSliceInfo::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.view().slice_collapse(info2); @@ -245,7 +245,7 @@ fn test_slice_dyninput_array_dyn() { AxisSliceInfo::from(1..), AxisSliceInfo::from(1), AxisSliceInfo::from(NewAxis), - AxisSliceInfo::from(..).step_by(2), + AxisSliceInfo::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.slice(info); @@ -254,7 +254,7 @@ fn test_slice_dyninput_array_dyn() { let info2 = SliceInfo::<_, Ix3, IxDyn>::try_from([ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), - AxisSliceInfo::from(..).step_by(2), + AxisSliceInfo::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.view().slice_collapse(info2); @@ -267,7 +267,7 @@ fn test_slice_dyninput_vec_fixed() { AxisSliceInfo::from(1..), AxisSliceInfo::from(1), AxisSliceInfo::from(NewAxis), - AxisSliceInfo::from(..).step_by(2), + AxisSliceInfo::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.slice(info); @@ -276,7 +276,7 @@ fn test_slice_dyninput_vec_fixed() { let info2 = SliceInfo::<_, Ix3, Ix2>::try_from(vec![ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), - AxisSliceInfo::from(..).step_by(2), + AxisSliceInfo::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.view().slice_collapse(info2); @@ -289,7 +289,7 @@ fn test_slice_dyninput_vec_dyn() { AxisSliceInfo::from(1..), AxisSliceInfo::from(1), AxisSliceInfo::from(NewAxis), - AxisSliceInfo::from(..).step_by(2), + AxisSliceInfo::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.slice(info); @@ -298,7 +298,7 @@ fn test_slice_dyninput_vec_dyn() { let info2 = SliceInfo::<_, Ix3, IxDyn>::try_from(vec![ AxisSliceInfo::from(1..), AxisSliceInfo::from(1), - AxisSliceInfo::from(..).step_by(2), + AxisSliceInfo::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.view().slice_collapse(info2); diff --git a/tests/oper.rs b/tests/oper.rs index 3533faa8e..1decd2125 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -561,7 +561,7 @@ fn scaled_add_2() { #[test] fn scaled_add_3() { use approx::assert_relative_eq; - use ndarray::{SliceInfo, AxisSliceInfo}; + use ndarray::{AxisSliceInfo, Slice, SliceInfo}; use std::convert::TryFrom; let beta = -2.3; @@ -583,12 +583,12 @@ fn scaled_add_3() { let mut a = range_mat64(m, k); let mut answer = a.clone(); let cdim = if n == 1 { vec![q] } else { vec![n, q] }; - let cslice = if n == 1 { - vec![AxisSliceInfo::from(..).step_by(s2)] + let cslice: Vec = if n == 1 { + vec![Slice::from(..).step_by(s2).into()] } else { vec![ - AxisSliceInfo::from(..).step_by(s1), - AxisSliceInfo::from(..).step_by(s2), + Slice::from(..).step_by(s1).into(), + Slice::from(..).step_by(s2).into(), ] }; From 24aebf0c2ec15f95bbbdf36c7263afa7890a2b65 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 16 Mar 2021 00:52:02 -0400 Subject: [PATCH 244/651] Rename AxisSliceInfo to SliceInfoElem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The name `AxisSliceInfo` isn't a great description for this type because it doesn't correspond to slicing information for a single input axis or a single output axis. This will be especially true if we add another variant for ellipsis-type functionality. The name `SliceInfoElem` more clearly describes the purpose of this type – to be an element in a list describing the slicing information. --- blas-tests/tests/oper.rs | 5 +- src/dimension/mod.rs | 16 ++--- src/impl_methods.rs | 16 ++--- src/lib.rs | 2 +- src/slice.rs | 152 +++++++++++++++++++-------------------- tests/array.rs | 58 +++++++-------- tests/oper.rs | 4 +- 7 files changed, 126 insertions(+), 127 deletions(-) diff --git a/blas-tests/tests/oper.rs b/blas-tests/tests/oper.rs index 27bf94f09..d6230cced 100644 --- a/blas-tests/tests/oper.rs +++ b/blas-tests/tests/oper.rs @@ -6,8 +6,7 @@ extern crate num_traits; use ndarray::linalg::general_mat_mul; use ndarray::linalg::general_mat_vec_mul; use ndarray::prelude::*; -use ndarray::{AxisSliceInfo, Ix, Ixs, Slice}; -use ndarray::{Data, LinalgScalar}; +use ndarray::{Data, Ix, Ixs, LinalgScalar, Slice, SliceInfoElem}; use approx::{assert_abs_diff_eq, assert_relative_eq}; use defmac::defmac; @@ -419,7 +418,7 @@ fn scaled_add_3() { let mut a = range_mat64(m, k); let mut answer = a.clone(); let cdim = if n == 1 { vec![q] } else { vec![n, q] }; - let cslice: Vec = if n == 1 { + let cslice: Vec = if n == 1 { vec![Slice::from(..).step_by(s2).into()] } else { vec![ diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index f94a7135c..fb062513e 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -8,7 +8,7 @@ use crate::error::{from_kind, ErrorKind, ShapeError}; use crate::slice::SliceArg; -use crate::{AxisSliceInfo, Ix, Ixs, Slice}; +use crate::{Ix, Ixs, Slice, SliceInfoElem}; use num_integer::div_floor; pub use self::axes::{axes_of, Axes, AxisDescription}; @@ -608,15 +608,15 @@ pub fn slices_intersect( indices1.as_ref().iter().filter(|si| !si.is_new_axis()), indices2.as_ref().iter().filter(|si| !si.is_new_axis()), ) { - // The slices do not intersect iff any pair of `AxisSliceInfo` does not intersect. + // The slices do not intersect iff any pair of `SliceInfoElem` does not intersect. match (si1, si2) { ( - AxisSliceInfo::Slice { + SliceInfoElem::Slice { start: start1, end: end1, step: step1, }, - AxisSliceInfo::Slice { + SliceInfoElem::Slice { start: start2, end: end2, step: step2, @@ -637,8 +637,8 @@ pub fn slices_intersect( return false; } } - (AxisSliceInfo::Slice { start, end, step }, AxisSliceInfo::Index(ind)) - | (AxisSliceInfo::Index(ind), AxisSliceInfo::Slice { start, end, step }) => { + (SliceInfoElem::Slice { start, end, step }, SliceInfoElem::Index(ind)) + | (SliceInfoElem::Index(ind), SliceInfoElem::Slice { start, end, step }) => { let ind = abs_index(axis_len, ind); let (min, max) = match slice_min_max(axis_len, Slice::new(start, end, step)) { Some(m) => m, @@ -648,14 +648,14 @@ pub fn slices_intersect( return false; } } - (AxisSliceInfo::Index(ind1), AxisSliceInfo::Index(ind2)) => { + (SliceInfoElem::Index(ind1), SliceInfoElem::Index(ind2)) => { let ind1 = abs_index(axis_len, ind1); let ind2 = abs_index(axis_len, ind2); if ind1 != ind2 { return false; } } - (AxisSliceInfo::NewAxis, _) | (_, AxisSliceInfo::NewAxis) => unreachable!(), + (SliceInfoElem::NewAxis, _) | (_, SliceInfoElem::NewAxis) => unreachable!(), } } true diff --git a/src/impl_methods.rs b/src/impl_methods.rs index a0833048e..4696fccbd 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -34,7 +34,7 @@ use crate::iter::{ }; use crate::slice::{MultiSliceArg, SliceArg}; use crate::stacking::concatenate; -use crate::{AxisSliceInfo, NdIndex, Slice}; +use crate::{NdIndex, Slice, SliceInfoElem}; /// # Methods For All Array Types impl ArrayBase @@ -415,7 +415,7 @@ where let mut old_axis = 0; let mut new_axis = 0; info.as_ref().iter().for_each(|&ax_info| match ax_info { - AxisSliceInfo::Slice { start, end, step } => { + SliceInfoElem::Slice { start, end, step } => { // Slice the axis in-place to update the `dim`, `strides`, and `ptr`. self.slice_axis_inplace(Axis(old_axis), Slice { start, end, step }); // Copy the sliced dim and stride to corresponding axis. @@ -424,7 +424,7 @@ where old_axis += 1; new_axis += 1; } - AxisSliceInfo::Index(index) => { + SliceInfoElem::Index(index) => { // Collapse the axis in-place to update the `ptr`. let i_usize = abs_index(self.len_of(Axis(old_axis)), index); self.collapse_axis(Axis(old_axis), i_usize); @@ -434,7 +434,7 @@ where // is zero length. old_axis += 1; } - AxisSliceInfo::NewAxis => { + SliceInfoElem::NewAxis => { // Set the dim and stride of the new axis. new_dim[new_axis] = 1; new_strides[new_axis] = 0; @@ -465,7 +465,7 @@ where /// /// - if an index is out of bounds /// - if a step size is zero - /// - if [`AxisSliceInfo::NewAxis`] is in `info`, e.g. if [`NewAxis`] was + /// - if [`SliceInfoElem::NewAxis`] is in `info`, e.g. if [`NewAxis`] was /// used in the [`s!`] macro /// - if `D` is `IxDyn` and `info` does not match the number of array axes pub fn slice_collapse(&mut self, info: I) @@ -479,16 +479,16 @@ where ); let mut axis = 0; info.as_ref().iter().for_each(|&ax_info| match ax_info { - AxisSliceInfo::Slice { start, end, step } => { + SliceInfoElem::Slice { start, end, step } => { self.slice_axis_inplace(Axis(axis), Slice { start, end, step }); axis += 1; } - AxisSliceInfo::Index(index) => { + SliceInfoElem::Index(index) => { let i_usize = abs_index(self.len_of(Axis(axis)), index); self.collapse_axis(Axis(axis), i_usize); axis += 1; } - AxisSliceInfo::NewAxis => panic!("`slice_collapse` does not support `NewAxis`."), + SliceInfoElem::NewAxis => panic!("`slice_collapse` does not support `NewAxis`."), }); debug_assert_eq!(axis, self.ndim()); } diff --git a/src/lib.rs b/src/lib.rs index 10a16ea8b..263afa2d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,7 +144,7 @@ pub use crate::dimension::NdIndex; pub use crate::error::{ErrorKind, ShapeError}; pub use crate::indexes::{indices, indices_of}; pub use crate::slice::{ - AxisSliceInfo, MultiSliceArg, NewAxis, Slice, SliceArg, SliceInfo, SliceNextDim, + MultiSliceArg, NewAxis, Slice, SliceArg, SliceInfo, SliceInfoElem, SliceNextDim, }; use crate::iterators::Baseiter; diff --git a/src/slice.rs b/src/slice.rs index a5d65a2e3..1965defd2 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -78,33 +78,33 @@ pub struct NewAxis; /// A slice (range with step), an index, or a new axis token. /// /// See also the [`s![]`](macro.s!.html) macro for a convenient way to create a -/// `SliceInfo<[AxisSliceInfo; n], Din, Dout>`. +/// `SliceInfo<[SliceInfoElem; n], Din, Dout>`. /// /// ## Examples /// -/// `AxisSliceInfo::Index(a)` is the index `a`. It can also be created with -/// `AxisSliceInfo::from(a)`. The Python equivalent is `[a]`. The macro +/// `SliceInfoElem::Index(a)` is the index `a`. It can also be created with +/// `SliceInfoElem::from(a)`. The Python equivalent is `[a]`. The macro /// equivalent is `s![a]`. /// -/// `AxisSliceInfo::Slice { start: 0, end: None, step: 1 }` is the full range -/// of an axis. It can also be created with `AxisSliceInfo::from(..)`. The +/// `SliceInfoElem::Slice { start: 0, end: None, step: 1 }` is the full range +/// of an axis. It can also be created with `SliceInfoElem::from(..)`. The /// Python equivalent is `[:]`. The macro equivalent is `s![..]`. /// -/// `AxisSliceInfo::Slice { start: a, end: Some(b), step: 2 }` is every second +/// `SliceInfoElem::Slice { start: a, end: Some(b), step: 2 }` is every second /// element from `a` until `b`. It can also be created with -/// `AxisSliceInfo::from(Slice::from(a..b).step_by(2))`. The Python equivalent +/// `SliceInfoElem::from(Slice::from(a..b).step_by(2))`. The Python equivalent /// is `[a:b:2]`. The macro equivalent is `s![a..b;2]`. /// -/// `AxisSliceInfo::Slice { start: a, end: None, step: -1 }` is every element, +/// `SliceInfoElem::Slice { start: a, end: None, step: -1 }` is every element, /// from `a` until the end, in reverse order. It can also be created with -/// `AxisSliceInfo::from(Slice::from(a..).step_by(-1))`. The Python equivalent +/// `SliceInfoElem::from(Slice::from(a..).step_by(-1))`. The Python equivalent /// is `[a::-1]`. The macro equivalent is `s![a..;-1]`. /// -/// `AxisSliceInfo::NewAxis` is a new axis of length 1. It can also be created -/// with `AxisSliceInfo::from(NewAxis)`. The Python equivalent is +/// `SliceInfoElem::NewAxis` is a new axis of length 1. It can also be created +/// with `SliceInfoElem::from(NewAxis)`. The Python equivalent is /// `[np.newaxis]`. The macro equivalent is `s![NewAxis]`. #[derive(Debug, PartialEq, Eq, Hash)] -pub enum AxisSliceInfo { +pub enum SliceInfoElem { /// A range with step size. `end` is an exclusive index. Negative `begin` /// or `end` indexes are counted from the back of the axis. If `end` is /// `None`, the slice extends to the end of the axis. @@ -119,30 +119,30 @@ pub enum AxisSliceInfo { NewAxis, } -copy_and_clone! {AxisSliceInfo} +copy_and_clone! {SliceInfoElem} -impl AxisSliceInfo { +impl SliceInfoElem { /// Returns `true` if `self` is a `Slice` value. pub fn is_slice(&self) -> bool { - matches!(self, AxisSliceInfo::Slice { .. }) + matches!(self, SliceInfoElem::Slice { .. }) } /// Returns `true` if `self` is an `Index` value. pub fn is_index(&self) -> bool { - matches!(self, AxisSliceInfo::Index(_)) + matches!(self, SliceInfoElem::Index(_)) } /// Returns `true` if `self` is a `NewAxis` value. pub fn is_new_axis(&self) -> bool { - matches!(self, AxisSliceInfo::NewAxis) + matches!(self, SliceInfoElem::NewAxis) } } -impl fmt::Display for AxisSliceInfo { +impl fmt::Display for SliceInfoElem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - AxisSliceInfo::Index(index) => write!(f, "{}", index)?, - AxisSliceInfo::Slice { start, end, step } => { + SliceInfoElem::Index(index) => write!(f, "{}", index)?, + SliceInfoElem::Slice { start, end, step } => { if start != 0 { write!(f, "{}", start)?; } @@ -154,7 +154,7 @@ impl fmt::Display for AxisSliceInfo { write!(f, ";{}", step)?; } } - AxisSliceInfo::NewAxis => write!(f, stringify!(NewAxis))?, + SliceInfoElem::NewAxis => write!(f, stringify!(NewAxis))?, } Ok(()) } @@ -223,9 +223,9 @@ macro_rules! impl_slice_variant_from_range { impl_slice_variant_from_range!(Slice, Slice, isize); impl_slice_variant_from_range!(Slice, Slice, usize); impl_slice_variant_from_range!(Slice, Slice, i32); -impl_slice_variant_from_range!(AxisSliceInfo, AxisSliceInfo::Slice, isize); -impl_slice_variant_from_range!(AxisSliceInfo, AxisSliceInfo::Slice, usize); -impl_slice_variant_from_range!(AxisSliceInfo, AxisSliceInfo::Slice, i32); +impl_slice_variant_from_range!(SliceInfoElem, SliceInfoElem::Slice, isize); +impl_slice_variant_from_range!(SliceInfoElem, SliceInfoElem::Slice, usize); +impl_slice_variant_from_range!(SliceInfoElem, SliceInfoElem::Slice, i32); impl From for Slice { #[inline] @@ -238,10 +238,10 @@ impl From for Slice { } } -impl From for AxisSliceInfo { +impl From for SliceInfoElem { #[inline] - fn from(_: RangeFull) -> AxisSliceInfo { - AxisSliceInfo::Slice { + fn from(_: RangeFull) -> SliceInfoElem { + SliceInfoElem::Slice { start: 0, end: None, step: 1, @@ -249,10 +249,10 @@ impl From for AxisSliceInfo { } } -impl From for AxisSliceInfo { +impl From for SliceInfoElem { #[inline] - fn from(s: Slice) -> AxisSliceInfo { - AxisSliceInfo::Slice { + fn from(s: Slice) -> SliceInfoElem { + SliceInfoElem::Slice { start: s.start, end: s.end, step: s.step, @@ -260,24 +260,24 @@ impl From for AxisSliceInfo { } } -macro_rules! impl_axissliceinfo_from_index { +macro_rules! impl_sliceinfoelem_from_index { ($index:ty) => { - impl From<$index> for AxisSliceInfo { + impl From<$index> for SliceInfoElem { #[inline] - fn from(r: $index) -> AxisSliceInfo { - AxisSliceInfo::Index(r as isize) + fn from(r: $index) -> SliceInfoElem { + SliceInfoElem::Index(r as isize) } } }; } -impl_axissliceinfo_from_index!(isize); -impl_axissliceinfo_from_index!(usize); -impl_axissliceinfo_from_index!(i32); +impl_sliceinfoelem_from_index!(isize); +impl_sliceinfoelem_from_index!(usize); +impl_sliceinfoelem_from_index!(i32); -impl From for AxisSliceInfo { +impl From for SliceInfoElem { #[inline] - fn from(_: NewAxis) -> AxisSliceInfo { - AxisSliceInfo::NewAxis + fn from(_: NewAxis) -> SliceInfoElem { + SliceInfoElem::NewAxis } } @@ -285,9 +285,9 @@ impl From for AxisSliceInfo { /// /// This trait is unsafe to implement because the implementation must ensure /// that `D`, `Self::OutDim`, `self.in_dim()`, and `self.out_ndim()` are -/// consistent with the `&[AxisSliceInfo]` returned by `self.as_ref()` and that +/// consistent with the `&[SliceInfoElem]` returned by `self.as_ref()` and that /// `self.as_ref()` always returns the same value when called multiple times. -pub unsafe trait SliceArg: AsRef<[AxisSliceInfo]> { +pub unsafe trait SliceArg: AsRef<[SliceInfoElem]> { /// Dimensionality of the output array. type OutDim: Dimension; @@ -322,7 +322,7 @@ macro_rules! impl_slicearg_samedim { ($in_dim:ty) => { unsafe impl SliceArg<$in_dim> for SliceInfo where - T: AsRef<[AxisSliceInfo]>, + T: AsRef<[SliceInfoElem]>, Dout: Dimension, { type OutDim = Dout; @@ -349,7 +349,7 @@ impl_slicearg_samedim!(Ix6); unsafe impl SliceArg for SliceInfo where - T: AsRef<[AxisSliceInfo]>, + T: AsRef<[SliceInfoElem]>, Din: Dimension, Dout: Dimension, { @@ -366,7 +366,7 @@ where private_impl! {} } -unsafe impl SliceArg for [AxisSliceInfo] { +unsafe impl SliceArg for [SliceInfoElem] { type OutDim = IxDyn; fn in_ndim(&self) -> usize { @@ -382,8 +382,8 @@ unsafe impl SliceArg for [AxisSliceInfo] { /// Represents all of the necessary information to perform a slice. /// -/// The type `T` is typically `[AxisSliceInfo; n]`, `&[AxisSliceInfo]`, or -/// `Vec`. The type `Din` is the dimension of the array to be +/// The type `T` is typically `[SliceInfoElem; n]`, `&[SliceInfoElem]`, or +/// `Vec`. The type `Din` is the dimension of the array to be /// sliced, and `Dout` is the output dimension after calling [`.slice()`]. Note /// that if `Din` is a fixed dimension type (`Ix0`, `Ix1`, `Ix2`, etc.), the /// `SliceInfo` instance can still be used to slice an array with dimension @@ -408,7 +408,7 @@ where } } -fn check_dims_for_sliceinfo(indices: &[AxisSliceInfo]) -> Result<(), ShapeError> +fn check_dims_for_sliceinfo(indices: &[SliceInfoElem]) -> Result<(), ShapeError> where Din: Dimension, Dout: Dimension, @@ -428,7 +428,7 @@ where impl SliceInfo where - T: AsRef<[AxisSliceInfo]>, + T: AsRef<[SliceInfoElem]>, Din: Dimension, Dout: Dimension, { @@ -482,7 +482,7 @@ where /// /// If `Din` is a fixed-size dimension type, then this is equivalent to /// `Din::NDIM.unwrap()`. Otherwise, the value is calculated by iterating - /// over the `AxisSliceInfo` elements. + /// over the `SliceInfoElem` elements. pub fn in_ndim(&self) -> usize { if let Some(ndim) = Din::NDIM { ndim @@ -497,7 +497,7 @@ where /// /// If `Dout` is a fixed-size dimension type, then this is equivalent to /// `Dout::NDIM.unwrap()`. Otherwise, the value is calculated by iterating - /// over the `AxisSliceInfo` elements. + /// over the `SliceInfoElem` elements. pub fn out_ndim(&self) -> usize { if let Some(ndim) = Dout::NDIM { ndim @@ -507,7 +507,7 @@ where } } -impl<'a, Din, Dout> TryFrom<&'a [AxisSliceInfo]> for SliceInfo<&'a [AxisSliceInfo], Din, Dout> +impl<'a, Din, Dout> TryFrom<&'a [SliceInfoElem]> for SliceInfo<&'a [SliceInfoElem], Din, Dout> where Din: Dimension, Dout: Dimension, @@ -515,17 +515,17 @@ where type Error = ShapeError; fn try_from( - indices: &'a [AxisSliceInfo], - ) -> Result, ShapeError> { + indices: &'a [SliceInfoElem], + ) -> Result, ShapeError> { unsafe { - // This is okay because `&[AxisSliceInfo]` always returns the same + // This is okay because `&[SliceInfoElem]` always returns the same // value for `.as_ref()`. Self::new(indices) } } } -impl TryFrom> for SliceInfo, Din, Dout> +impl TryFrom> for SliceInfo, Din, Dout> where Din: Dimension, Dout: Dimension, @@ -533,8 +533,8 @@ where type Error = ShapeError; fn try_from( - indices: Vec, - ) -> Result, Din, Dout>, ShapeError> { + indices: Vec, + ) -> Result, Din, Dout>, ShapeError> { unsafe { // This is okay because `Vec` always returns the same value for // `.as_ref()`. @@ -545,8 +545,8 @@ where macro_rules! impl_tryfrom_array_for_sliceinfo { ($len:expr) => { - impl TryFrom<[AxisSliceInfo; $len]> - for SliceInfo<[AxisSliceInfo; $len], Din, Dout> + impl TryFrom<[SliceInfoElem; $len]> + for SliceInfo<[SliceInfoElem; $len], Din, Dout> where Din: Dimension, Dout: Dimension, @@ -554,10 +554,10 @@ macro_rules! impl_tryfrom_array_for_sliceinfo { type Error = ShapeError; fn try_from( - indices: [AxisSliceInfo; $len], - ) -> Result, ShapeError> { + indices: [SliceInfoElem; $len], + ) -> Result, ShapeError> { unsafe { - // This is okay because `[AxisSliceInfo; N]` always returns + // This is okay because `[SliceInfoElem; N]` always returns // the same value for `.as_ref()`. Self::new(indices) } @@ -575,25 +575,25 @@ impl_tryfrom_array_for_sliceinfo!(6); impl_tryfrom_array_for_sliceinfo!(7); impl_tryfrom_array_for_sliceinfo!(8); -impl AsRef<[AxisSliceInfo]> for SliceInfo +impl AsRef<[SliceInfoElem]> for SliceInfo where - T: AsRef<[AxisSliceInfo]>, + T: AsRef<[SliceInfoElem]>, Din: Dimension, Dout: Dimension, { - fn as_ref(&self) -> &[AxisSliceInfo] { + fn as_ref(&self) -> &[SliceInfoElem] { self.indices.as_ref() } } impl<'a, T, Din, Dout> From<&'a SliceInfo> - for SliceInfo<&'a [AxisSliceInfo], Din, Dout> + for SliceInfo<&'a [SliceInfoElem], Din, Dout> where - T: AsRef<[AxisSliceInfo]>, + T: AsRef<[SliceInfoElem]>, Din: Dimension, Dout: Dimension, { - fn from(info: &'a SliceInfo) -> SliceInfo<&'a [AxisSliceInfo], Din, Dout> { + fn from(info: &'a SliceInfo) -> SliceInfo<&'a [SliceInfoElem], Din, Dout> { SliceInfo { in_dim: info.in_dim, out_dim: info.out_dim, @@ -681,8 +681,8 @@ impl_slicenextdim!((), NewAxis, Ix0, Ix1); /// counted from the end of the axis. Step sizes are also signed and may be /// negative, but must not be zero. /// -/// The syntax is `s![` *[ axis-slice-info [, axis-slice-info [ , ... ] ] ]* -/// `]`, where *axis-slice-info* is any of the following: +/// The syntax is `s![` *[ elem [, elem [ , ... ] ] ]* `]`, where *elem* is any +/// of the following: /// /// * *index*: an index to use for taking a subview with respect to that axis. /// (The index is selected. The axis is removed except with @@ -698,7 +698,7 @@ impl_slicenextdim!((), NewAxis, Ix0, Ix1); /// [`Slice`]: struct.Slice.html /// [`NewAxis`]: struct.NewAxis.html /// -/// The number of *axis-slice-info*, not including *new-axis*, must match the +/// The number of *elem*, not including *new-axis*, must match the /// number of axes in the array. *index*, *range*, *slice*, *step*, and /// *new-axis* can be expressions. *index* must be of type `isize`, `usize`, or /// `i32`. *range* must be of type `Range`, `RangeTo`, `RangeFrom`, or @@ -850,13 +850,13 @@ macro_rules! s( }; // Catch-all clause for syntax errors (@parse $($t:tt)*) => { compile_error!("Invalid syntax in s![] call.") }; - // convert range/index/new-axis into AxisSliceInfo + // convert range/index/new-axis into SliceInfoElem (@convert $r:expr) => { - <$crate::AxisSliceInfo as ::std::convert::From<_>>::from($r) + <$crate::SliceInfoElem as ::std::convert::From<_>>::from($r) }; - // convert range/index/new-axis and step into AxisSliceInfo + // convert range/index/new-axis and step into SliceInfoElem (@convert $r:expr, $s:expr) => { - <$crate::AxisSliceInfo as ::std::convert::From<_>>::from( + <$crate::SliceInfoElem as ::std::convert::From<_>>::from( <$crate::Slice as ::std::convert::From<_>>::from($r).step_by($s as isize) ) }; diff --git a/tests/array.rs b/tests/array.rs index 6bcdf7633..458b8e792 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -12,7 +12,7 @@ use itertools::{enumerate, zip, Itertools}; use ndarray::prelude::*; use ndarray::{arr3, rcarr2}; use ndarray::indices; -use ndarray::{AxisSliceInfo, Slice, SliceInfo}; +use ndarray::{Slice, SliceInfo, SliceInfoElem}; use std::convert::TryFrom; macro_rules! assert_panics { @@ -220,19 +220,19 @@ fn test_slice_dyninput_array_fixed() { fn test_slice_array_dyn() { let mut arr = Array3::::zeros((5, 2, 5)); let info = SliceInfo::<_, Ix3, IxDyn>::try_from([ - AxisSliceInfo::from(1..), - AxisSliceInfo::from(1), - AxisSliceInfo::from(NewAxis), - AxisSliceInfo::from(Slice::from(..).step_by(2)), + SliceInfoElem::from(1..), + SliceInfoElem::from(1), + SliceInfoElem::from(NewAxis), + SliceInfoElem::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); let info2 = SliceInfo::<_, Ix3, IxDyn>::try_from([ - AxisSliceInfo::from(1..), - AxisSliceInfo::from(1), - AxisSliceInfo::from(Slice::from(..).step_by(2)), + SliceInfoElem::from(1..), + SliceInfoElem::from(1), + SliceInfoElem::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.view().slice_collapse(info2); @@ -242,19 +242,19 @@ fn test_slice_array_dyn() { fn test_slice_dyninput_array_dyn() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); let info = SliceInfo::<_, Ix3, IxDyn>::try_from([ - AxisSliceInfo::from(1..), - AxisSliceInfo::from(1), - AxisSliceInfo::from(NewAxis), - AxisSliceInfo::from(Slice::from(..).step_by(2)), + SliceInfoElem::from(1..), + SliceInfoElem::from(1), + SliceInfoElem::from(NewAxis), + SliceInfoElem::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); let info2 = SliceInfo::<_, Ix3, IxDyn>::try_from([ - AxisSliceInfo::from(1..), - AxisSliceInfo::from(1), - AxisSliceInfo::from(Slice::from(..).step_by(2)), + SliceInfoElem::from(1..), + SliceInfoElem::from(1), + SliceInfoElem::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.view().slice_collapse(info2); @@ -264,19 +264,19 @@ fn test_slice_dyninput_array_dyn() { fn test_slice_dyninput_vec_fixed() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); let info = &SliceInfo::<_, Ix3, Ix3>::try_from(vec![ - AxisSliceInfo::from(1..), - AxisSliceInfo::from(1), - AxisSliceInfo::from(NewAxis), - AxisSliceInfo::from(Slice::from(..).step_by(2)), + SliceInfoElem::from(1..), + SliceInfoElem::from(1), + SliceInfoElem::from(NewAxis), + SliceInfoElem::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); let info2 = SliceInfo::<_, Ix3, Ix2>::try_from(vec![ - AxisSliceInfo::from(1..), - AxisSliceInfo::from(1), - AxisSliceInfo::from(Slice::from(..).step_by(2)), + SliceInfoElem::from(1..), + SliceInfoElem::from(1), + SliceInfoElem::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.view().slice_collapse(info2); @@ -286,19 +286,19 @@ fn test_slice_dyninput_vec_fixed() { fn test_slice_dyninput_vec_dyn() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); let info = &SliceInfo::<_, Ix3, IxDyn>::try_from(vec![ - AxisSliceInfo::from(1..), - AxisSliceInfo::from(1), - AxisSliceInfo::from(NewAxis), - AxisSliceInfo::from(Slice::from(..).step_by(2)), + SliceInfoElem::from(1..), + SliceInfoElem::from(1), + SliceInfoElem::from(NewAxis), + SliceInfoElem::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); let info2 = SliceInfo::<_, Ix3, IxDyn>::try_from(vec![ - AxisSliceInfo::from(1..), - AxisSliceInfo::from(1), - AxisSliceInfo::from(Slice::from(..).step_by(2)), + SliceInfoElem::from(1..), + SliceInfoElem::from(1), + SliceInfoElem::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.view().slice_collapse(info2); diff --git a/tests/oper.rs b/tests/oper.rs index 1decd2125..ed612bad2 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -561,7 +561,7 @@ fn scaled_add_2() { #[test] fn scaled_add_3() { use approx::assert_relative_eq; - use ndarray::{AxisSliceInfo, Slice, SliceInfo}; + use ndarray::{Slice, SliceInfo, SliceInfoElem}; use std::convert::TryFrom; let beta = -2.3; @@ -583,7 +583,7 @@ fn scaled_add_3() { let mut a = range_mat64(m, k); let mut answer = a.clone(); let cdim = if n == 1 { vec![q] } else { vec![n, q] }; - let cslice: Vec = if n == 1 { + let cslice: Vec = if n == 1 { vec![Slice::from(..).step_by(s2).into()] } else { vec![ From 252c2761fa8dc5e13d1202d3731261add4ff23d5 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 16 Mar 2021 18:31:43 -0400 Subject: [PATCH 245/651] Fix docs for slice;step case in s![] Before, the docs were misleading about the calculation of the final step size. --- src/slice.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slice.rs b/src/slice.rs index 1965defd2..3c554a5ca 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -690,8 +690,8 @@ impl_slicenextdim!((), NewAxis, Ix0, Ix1); /// * *range*: a range with step size 1 to use for slicing that axis. /// * *range* `;` *step*: a range with step size *step* to use for slicing that axis. /// * *slice*: a [`Slice`] instance to use for slicing that axis. -/// * *slice* `;` *step*: a range constructed from the start and end of a [`Slice`] -/// instance, with new step size *step*, to use for slicing that axis. +/// * *slice* `;` *step*: a range constructed from a [`Slice`] instance, +/// multiplying the step size by *step*, to use for slicing that axis. /// * *new-axis*: a [`NewAxis`] instance that represents the creation of a new axis. /// (Except for [`.slice_collapse()`], which panics on [`NewAxis`] elements.) /// From 8788849a7a923c9e4660d90c64e170291dc18a96 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 13 Mar 2021 15:13:12 +0100 Subject: [PATCH 246/651] DOC: Update RELEASES.md for 0.15 --- RELEASES.md | 171 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 150 insertions(+), 21 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 2f4104730..afbc6a19c 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4,28 +4,75 @@ Version 0.15.0 (Not released yet) New features ------------ -- Support for compiling ndarray as `no_std` (using core and alloc) by [@xd009642] +- Support inserting new axes while slicing by [@jturner314] - https://github.com/rust-ndarray/ndarray/pull/861 + https://github.com/rust-ndarray/ndarray/pull/570 + +- Support two-sided broadcasting in arithmetic operations with arrays by [@SparrowLii] + + Note that this means that a new trait bound is required in some places when + mixing dimensionality types of arrays in arithmetic operations. + + https://github.com/rust-ndarray/ndarray/pull/898 + +- Support for compiling ndarray as `no_std` (using core and alloc) by + [@xd009642] and [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/861
+ https://github.com/rust-ndarray/ndarray/pull/889 - New methods `.cell_view()` and `ArrayViewMut::into_cell_view` that enable new ways of working with array elements as if they were in Cells - setting - elements through shared views and broadcast views. + elements through shared views and broadcast views, by [@bluss]. https://github.com/rust-ndarray/ndarray/pull/877 +- New methods `slice_each_axis/_mut/_inplace` that make it easier to slice + a dynamic number of axes in some situations, by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/913 Enhancements ------------ -- Fix `Zip` for the 0-dimensional case by [@jturner314] +- New constructors `Array::from_iter` and `Array::from_vec` by [@bluss]. + No new functionality, just that these constructors are avaiable without trait + imports. - https://github.com/rust-ndarray/ndarray/pull/862 + https://github.com/rust-ndarray/ndarray/pull/921 + +- Ndarray can now correctly determine that arrays can be contiguous, even if + they have negative strides, by [@SparrowLii] + + https://github.com/rust-ndarray/ndarray/pull/885 + +- `NdProducer::raw_dim` is now a documented method by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/918 + +- `AxisDescription` is now a struct with field names, not a tuple struct by + [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/915 + +- Improvements to `map_inplace` by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/911 + +- `.into_dimensionality` performance was improved for the `IxDyn` to `IxDyn` + case by [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/906 + +- Improved performance for scalar + &array and &array + scalar operations by + [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/890 API changes ----------- -- Removed deprecated methods by [@bluss]: +- Removed already deprecated methods by [@bluss]: - Remove deprecated `.all_close()` - use approx feature and methods like `.abs_diff_eq` instead - Mark `.scalar_sum()` as deprecated - use `.sum()` instead @@ -34,21 +81,100 @@ API changes https://github.com/rust-ndarray/ndarray/pull/874 -- Remove deprecated methods: rows, cols (for row and column count; the new - names are nrows and ncols) by [@bluss] +- Remove already deprecated methods: rows, cols (for row and column count; the + new names are nrows and ncols) by [@bluss] https://github.com/rust-ndarray/ndarray/pull/872 -- Renamed methods (old names are now deprecated) by [@bluss] +- Renamed `Zip` methods by [@bluss] and [@SparrowLii]: + + - `apply` -> `for_each` + - `apply_collect` -> `map_collect` + - `apply_collect_into` -> `map_collect_into` + - (`par_` prefixed methods renamed accordingly) + + https://github.com/rust-ndarray/ndarray/pull/894
+ https://github.com/rust-ndarray/ndarray/pull/904
+ +- Deprecate `Array::uninitialized` and revamped its replacement by [@bluss] + + Please use new new `Array::uninit` which is based on `MaybeUninit` (renamed + from `Array::maybe_uninit`, the old name is also deprecated). + + https://github.com/rust-ndarray/ndarray/pull/902
+ https://github.com/rust-ndarray/ndarray/pull/876 + +- Renamed methods (old names are now deprecated) by [@bluss] and [@jturner314] - `genrows/_mut` -> `rows/_mut` - `gencolumns/_mut` -> `columns/_mut` + - `stack_new_axis` -> `stack` (the new name already existed) + - `visit` -> `for_each` - https://github.com/rust-ndarray/ndarray/pull/872 + https://github.com/rust-ndarray/ndarray/pull/872
+ https://github.com/rust-ndarray/ndarray/pull/937
+ https://github.com/rust-ndarray/ndarray/pull/907
+ +- `blas-src` dependency updated to 0.7.0 by [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/891 + +- Updated `matrixmultiply` dependency to 0.3.0 by [@bluss] + and adding new feature flag `matrixmultiply-threading` to enable its threading + + https://github.com/rust-ndarray/ndarray/pull/888
+ https://github.com/rust-ndarray/ndarray/pull/938
+ + +Bug fixes +--------- + +- Fix `Zip::indexed` for the 0-dimensional case by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/862 + +- Fix bug in layout computation that broke parallel collect to f-order + array in some circumstances by [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/900 + +- Fix an unwanted panic in shape overflow checking by [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/855 Other changes ------------- +- Various improvements to tests and CI by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/934
+ https://github.com/rust-ndarray/ndarray/pull/924
+ +- The `sort-axis.rs` example file's implementation of sort was bugfixed and now + has tests, by [@dam5h] and [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/916
+ https://github.com/rust-ndarray/ndarray/pull/930 + +- We now link to the #rust-sci room on matrix in the readme by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/619 + +- Internal cleanup with builder-like methods for creating arrays by [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/908 + +- Implementation fix of `.swap(i, j)` by [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/903 + +- Minimum supported Rust version (MSRV) is Rust 1.49. + + https://github.com/rust-ndarray/ndarray/pull/902 + +- Minor improvements to docs by [@insideoutclub] + + https://github.com/rust-ndarray/ndarray/pull/887 Version 0.14.0 (2020-11-28) @@ -1069,21 +1195,24 @@ Earlier releases [@bluss]: https://github.com/bluss [@jturner314]: https://github.com/jturner314 [@LukeMathWalker]: https://github.com/LukeMathWalker -[@max-sixty]: https://github.com/max-sixty +[@acj]: https://github.com/acj +[@andrei-papou]: https://github.com/andrei-papou +[@dam5h]: https://github.com/dam5h +[@d-dorazio]: https://github.com/d-dorazio +[@Eijebong]: https://github.com/Eijebong +[@insideoutclub]: https://github.com/insideoutclub [@JP-Ellis]: https://github.com/JP-Ellis -[@sebasv]: https://github.com/sebasv +[@lifuyang]: https://github.com/liufuyang +[@max-sixty]: https://github.com/max-sixty [@mneumann]: https://github.com/mneumann -[@termoshtt]: https://github.com/termoshtt -[@rth]: https://github.com/rth -[@nitsky]: https://github.com/nitsky -[@d-dorazio]: https://github.com/d-dorazio +[@mockersf]: https://github.com/mockersf [@nilgoyette]: https://github.com/nilgoyette +[@nitsky]: https://github.com/nitsky +[@rth]: https://github.com/rth +[@sebasv]: https://github.com/sebasv +[@SparrowLii]: https://github.com/SparrowLii +[@termoshtt]: https://github.com/termoshtt [@TheLortex]: https://github.com/TheLortex -[@mockersf]: https://github.com/mockersf [@viniciusd]: https://github.com/viniciusd -[@lifuyang]: https://github.com/liufuyang -[@acj]: https://github.com/acj -[@Eijebong]: https://github.com/Eijebong -[@andrei-papou]: https://github.com/andrei-papou [@xd009642]: https://github.com/xd009642 [@Zuse64]: https://github.com/Zuse64 From e870f6372d6fa0efb4c046ae23c65da118884876 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 17 Mar 2021 23:00:15 +0100 Subject: [PATCH 247/651] FEAT: Add new method .assign_to() This method does the same job as Zip::from(a).map_assign_into(f, b) with cloning as the function f. Comparison with existing assign: - Reverse argument order: source, destination - The new method doesn't broadcast. - The new method is generic over AssignElem (assign into &Cell, &mut ManuallyUninit etc). --- src/impl_constructors.rs | 20 ++------------------ src/impl_methods.rs | 20 +++++++++++++++++++- src/lib.rs | 1 - src/stacking.rs | 5 ++--- src/traversal_utils.rs | 26 -------------------------- tests/array.rs | 25 ------------------------- tests/higher_order_f.rs | 34 ++++++++++++++++++++++++++++++++++ 7 files changed, 57 insertions(+), 74 deletions(-) delete mode 100644 src/traversal_utils.rs diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 63688ea7f..d082a5ce3 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -531,30 +531,14 @@ where /// // two first columns in b are two last in a /// // rest of columns in b are the initial columns in a /// - /// assign_to(a.slice(s![.., -2..]), b.slice_mut(s![.., ..2])); - /// assign_to(a.slice(s![.., 2..]), b.slice_mut(s![.., ..-2])); + /// a.slice(s![.., -2..]).assign_to(b.slice_mut(s![.., ..2])); + /// a.slice(s![.., 2..]).assign_to(b.slice_mut(s![.., ..-2])); /// /// // Now we can promise that `b` is safe to use with all operations /// unsafe { /// b.assume_init() /// } /// } - /// - /// use ndarray::{IntoNdProducer, AssignElem}; - /// - /// // This function clones elements from the first input to the second; - /// // the two producers must have the same shape - /// fn assign_to<'a, P1, P2, A>(from: P1, to: P2) - /// where P1: IntoNdProducer, - /// P2: IntoNdProducer, - /// P2::Item: AssignElem
, - /// A: Clone + 'a - /// { - /// Zip::from(from) - /// .map_assign_into(to, A::clone); - /// } - /// - /// # shift_by_two(&Array2::zeros((8, 8))); /// ``` pub fn uninit(shape: Sh) -> ArrayBase where diff --git a/src/impl_methods.rs b/src/impl_methods.rs index d039d68a6..5b17c817d 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -15,6 +15,7 @@ use rawpointer::PointerExt; use crate::imp_prelude::*; use crate::{arraytraits, DimMax}; +use crate::argument_traits::AssignElem; use crate::dimension; use crate::dimension::IntoDimension; use crate::dimension::{ @@ -25,7 +26,7 @@ use crate::dimension::broadcast::co_broadcast; use crate::error::{self, ErrorKind, ShapeError, from_kind}; use crate::math_cell::MathCell; use crate::itertools::zip; -use crate::zip::Zip; +use crate::zip::{IntoNdProducer, Zip}; use crate::AxisDescription; use crate::iter::{ @@ -2049,6 +2050,23 @@ where self.zip_mut_with(rhs, |x, y| *x = y.clone()); } + /// Perform an elementwise assigment of values cloned from `self` into array or producer `to`. + /// + /// The destination `to` can be another array or a producer of assignable elements. + /// [`AssignElem`] determines how elements are assigned. + /// + /// **Panics** if shapes disagree. + pub fn assign_to

(&self, to: P) + where + S: Data, + P: IntoNdProducer, + P::Item: AssignElem, + A: Clone, + { + Zip::from(self) + .map_assign_into(to, A::clone); + } + /// Perform an elementwise assigment to `self` from element `x`. pub fn fill(&mut self, x: A) where diff --git a/src/lib.rs b/src/lib.rs index f48da4b32..778807aff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -206,7 +206,6 @@ mod shape_builder; mod slice; mod split_at; mod stacking; -mod traversal_utils; #[macro_use] mod zip; diff --git a/src/stacking.rs b/src/stacking.rs index 580d94b43..500ded6af 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -8,7 +8,6 @@ use crate::error::{from_kind, ErrorKind, ShapeError}; use crate::imp_prelude::*; -use crate::traversal_utils::assign_to; /// Stack arrays along the new axis. /// @@ -99,7 +98,7 @@ where for array in arrays { let len = array.len_of(axis); let (front, rest) = assign_view.split_at(axis, len); - assign_to(array, front); + array.assign_to(front); assign_view = rest; } debug_assert_eq!(assign_view.len(), 0); @@ -171,7 +170,7 @@ where // but same number of axes). let assign_view = assign_view.into_dimensionality::() .expect("same-dimensionality cast"); - assign_to(array, assign_view); + array.assign_to(assign_view); }); unsafe { diff --git a/src/traversal_utils.rs b/src/traversal_utils.rs deleted file mode 100644 index 3f4d017bf..000000000 --- a/src/traversal_utils.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2020 bluss and ndarray developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use crate::{ - IntoNdProducer, - AssignElem, - Zip, -}; - -/// Assign values from producer P1 to producer P2 -/// P1 and P2 must be of the same shape and dimension -pub(crate) fn assign_to<'a, P1, P2, A>(from: P1, to: P2) - where P1: IntoNdProducer, - P2: IntoNdProducer, - P2::Item: AssignElem, - A: Clone + 'a -{ - Zip::from(from) - .map_assign_into(to, A::clone); -} - diff --git a/tests/array.rs b/tests/array.rs index fa5da4419..28e305f53 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -900,31 +900,6 @@ fn standard_layout() { assert!(x4.is_standard_layout()); } -#[test] -fn assign() { - let mut a = arr2(&[[1., 2.], [3., 4.]]); - let b = arr2(&[[1., 3.], [2., 4.]]); - a.assign(&b); - assert_eq!(a, b); - - /* Test broadcasting */ - a.assign(&ArcArray::zeros(1)); - assert_eq!(a, ArcArray::zeros((2, 2))); - - /* Test other type */ - a.assign(&Array::from_elem((2, 2), 3.)); - assert_eq!(a, ArcArray::from_elem((2, 2), 3.)); - - /* Test mut view */ - let mut a = arr2(&[[1, 2], [3, 4]]); - { - let mut v = a.view_mut(); - v.slice_collapse(s![..1, ..]); - v.fill(0); - } - assert_eq!(a, arr2(&[[0, 0], [3, 4]])); -} - #[test] fn iter_size_hint() { let mut a = arr2(&[[1., 2.], [3., 4.]]); diff --git a/tests/higher_order_f.rs b/tests/higher_order_f.rs index c567eb3e0..1238cc4d8 100644 --- a/tests/higher_order_f.rs +++ b/tests/higher_order_f.rs @@ -6,3 +6,37 @@ fn test_fold_axis_oob() { let a = arr2(&[[1., 2.], [3., 4.]]); a.fold_axis(Axis(2), 0., |x, y| x + y); } + +#[test] +fn assign() { + let mut a = arr2(&[[1., 2.], [3., 4.]]); + let b = arr2(&[[1., 3.], [2., 4.]]); + a.assign(&b); + assert_eq!(a, b); + + /* Test broadcasting */ + a.assign(&ArcArray::zeros(1)); + assert_eq!(a, ArcArray::zeros((2, 2))); + + /* Test other type */ + a.assign(&Array::from_elem((2, 2), 3.)); + assert_eq!(a, ArcArray::from_elem((2, 2), 3.)); + + /* Test mut view */ + let mut a = arr2(&[[1, 2], [3, 4]]); + { + let mut v = a.view_mut(); + v.slice_collapse(s![..1, ..]); + v.fill(0); + } + assert_eq!(a, arr2(&[[0, 0], [3, 4]])); +} + + +#[test] +fn assign_to() { + let mut a = arr2(&[[1., 2.], [3., 4.]]); + let b = arr2(&[[0., 3.], [2., 0.]]); + b.assign_to(&mut a); + assert_eq!(a, b); +} From 5bc7631073612efcdcb247a44ea99a8034c29b7f Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 17 Mar 2021 22:29:10 +0100 Subject: [PATCH 248/651] FEAT: Add "forwards" of the approx methods .abs_diff_eq and .relative_eq This is a nod to the all_close method that was removed. The replacement methods still require the "approx" feature gate, but we forward them as inherent methods on the array, to make them easier to call (no trait import needed). --- src/array_approx.rs | 47 ++++++++++++++++++++++++++++++++++++++++++--- src/lib.rs | 8 ++++++-- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/array_approx.rs b/src/array_approx.rs index a2e9c2327..bb6804a27 100644 --- a/src/array_approx.rs +++ b/src/array_approx.rs @@ -21,9 +21,10 @@ where if self.shape() != other.shape() { return false; } + Zip::from(self) .and(other) - .all(|a, b| A::abs_diff_eq(a, b, epsilon.clone())) + .all(move |a, b| A::abs_diff_eq(a, b, epsilon.clone())) } } @@ -49,9 +50,10 @@ where if self.shape() != other.shape() { return false; } + Zip::from(self) .and(other) - .all(|a, b| A::relative_eq(a, b, epsilon.clone(), max_relative.clone())) + .all(move |a, b| A::relative_eq(a, b, epsilon.clone(), max_relative.clone())) } } @@ -72,12 +74,51 @@ where if self.shape() != other.shape() { return false; } + Zip::from(self) .and(other) - .all(|a, b| A::ulps_eq(a, b, epsilon.clone(), max_ulps)) + .all(move |a, b| A::ulps_eq(a, b, epsilon.clone(), max_ulps)) } } +impl ArrayBase +where + S: Data, + D: Dimension, +{ + /// A test for equality that uses the elementwise absolute difference to compute the + /// approximate equality of two arrays. + /// + /// **Requires crate feature `"approx"`** + pub fn abs_diff_eq(&self, other: &ArrayBase, epsilon: A::Epsilon) -> bool + where + A: AbsDiffEq, + A::Epsilon: Clone, + S2: Data, + { + >::abs_diff_eq(self, other, epsilon) + } + + /// A test for equality that uses an elementwise relative comparison if the values are far + /// apart; and the absolute difference otherwise. + /// + /// **Requires crate feature `"approx"`** + pub fn relative_eq( + &self, + other: &ArrayBase, + epsilon: A::Epsilon, + max_relative: A::Epsilon, + ) -> bool + where + A: RelativeEq, + A::Epsilon: Clone, + S2: Data + { + >::relative_eq(self, other, epsilon, max_relative) + } +} + + #[cfg(test)] mod tests { use crate::prelude::*; diff --git a/src/lib.rs b/src/lib.rs index f48da4b32..7a7a201ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -168,8 +168,6 @@ mod aliases; #[macro_use] mod itertools; mod argument_traits; -#[cfg(feature = "approx")] -mod array_approx; #[cfg(feature = "serde")] mod array_serde; mod arrayformat; @@ -1520,6 +1518,9 @@ impl<'a, A> CowRepr<'a, A> { } } +// NOTE: The order of modules decides in which order methods on the type ArrayBase +// (mainly mentioning that as the most relevant type) show up in the documentation. +// Consider the doc effect of ordering modules here. mod impl_clone; mod impl_internal_constructors; @@ -1613,6 +1614,9 @@ pub mod linalg; mod impl_ops; pub use crate::impl_ops::ScalarOperand; +#[cfg(feature = "approx")] +mod array_approx; + // Array view methods mod impl_views; From 73b6b1c2bddf9a1bc91e0eb08e665aaddfc50fe7 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 17 Mar 2021 20:06:26 -0400 Subject: [PATCH 249/651] DOC: Add more info about slicing changes to RELEASES.md --- RELEASES.md | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index afbc6a19c..b2c13fdd3 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4,7 +4,11 @@ Version 0.15.0 (Not released yet) New features ------------ -- Support inserting new axes while slicing by [@jturner314] +- Support inserting new axes while slicing by [@jturner314]. This is an example: + + ```rust + let view = arr.slice(s![.., -1, 2..;-1, NewAxis]); + ``` https://github.com/rust-ndarray/ndarray/pull/570 @@ -72,6 +76,30 @@ Enhancements API changes ----------- +- Changes to the slicing-related types and macro by [@jturner314] and [@bluss]: + + - Remove the `Dimension::SliceArg` associated type, and add a new `SliceArg` + trait for this purpose. + - Change the return type of the `s![]` macro to an owned `SliceInfo` rather + than a reference. + - Replace the `SliceOrIndex` enum with `SliceInfoElem`, which has an + additional `NewAxis` variant and does not have a `step_by` method. + - Change the type parameters of `SliceInfo` in order to support the `NewAxis` + functionality and remove some tricky `unsafe` code. + - Mark the `SliceInfo::new` method as `unsafe`. The new implementations of + `TryFrom` can be used as a safe alternative. + - Remove the `AsRef> for SliceInfo` + implementation. Add the similar `From<&'a SliceInfo> for + SliceInfo<&'a [SliceInfoElem], Din, Dout>` conversion as an alternative. + - Change the *expr* `;` *step* case in the `s![]` macro to error at compile + time if an unsupported type for *expr* is used, instead of panicking at + runtime. + + https://github.com/rust-ndarray/ndarray/pull/570
+ https://github.com/rust-ndarray/ndarray/pull/940
+ https://github.com/rust-ndarray/ndarray/pull/943
+ https://github.com/rust-ndarray/ndarray/pull/945
+ - Removed already deprecated methods by [@bluss]: - Remove deprecated `.all_close()` - use approx feature and methods like `.abs_diff_eq` instead @@ -142,6 +170,12 @@ Bug fixes https://github.com/rust-ndarray/ndarray/pull/855 +- Mark the `SliceInfo::new` method as `unsafe` due to the requirement that + `indices.as_ref()` always return the same value when called multiple times, + by [@bluss] and [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/570 + Other changes ------------- From 6f6f7074ffb3c77898fc7b03fc0baa885c06badc Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 19 Mar 2021 16:52:50 +0100 Subject: [PATCH 250/651] DOC: Update RELEASES.md for recent assign_to and approx PRs --- RELEASES.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index b2c13fdd3..910378d02 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -36,6 +36,12 @@ New features https://github.com/rust-ndarray/ndarray/pull/913 +- New method `a.assign_to(b)` with the inverse argument order compared to the + existing `b.assign(a)` and some extra features like assigning into + uninitialized arrays, By [@bluss]. + + https://github.com/rust-ndarray/ndarray/pull/947 + Enhancements ------------ @@ -73,6 +79,12 @@ Enhancements https://github.com/rust-ndarray/ndarray/pull/890 +- Methods for array comparison `abs_diff_eq` and `relative_eq` are now + exposed as inherent methods too (no trait import needed), still under the approx + feature flag by [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/946 + API changes ----------- From 6017b2d745fdd8a85520cd52080ad423335ecacc Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Wed, 27 Jan 2021 19:21:03 +0800 Subject: [PATCH 251/651] Fix constructors when strides are negative --- src/impl_views/constructors.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/impl_views/constructors.rs b/src/impl_views/constructors.rs index a133ee1d1..88a289ce7 100644 --- a/src/impl_views/constructors.rs +++ b/src/impl_views/constructors.rs @@ -13,6 +13,7 @@ use crate::error::ShapeError; use crate::extension::nonnull::nonnull_debug_checked_from_ptr; use crate::imp_prelude::*; use crate::{is_aligned, StrideShape}; +use crate::dimension::offset_from_ptr_to_memory; /// Methods for read-only array views. impl<'a, A, D> ArrayView<'a, A, D> @@ -55,7 +56,7 @@ where let dim = shape.dim; dimension::can_index_slice_with_strides(xs, &dim, &shape.strides)?; let strides = shape.strides.strides_for_dim(&dim); - unsafe { Ok(Self::new_(xs.as_ptr(), dim, strides)) } + unsafe { Ok(Self::new_(xs.as_ptr().offset(-offset_from_ptr_to_memory(&dim, &strides)), dim, strides)) } } /// Create an `ArrayView` from shape information and a raw pointer to @@ -152,7 +153,7 @@ where let dim = shape.dim; dimension::can_index_slice_with_strides(xs, &dim, &shape.strides)?; let strides = shape.strides.strides_for_dim(&dim); - unsafe { Ok(Self::new_(xs.as_mut_ptr(), dim, strides)) } + unsafe { Ok(Self::new_(xs.as_mut_ptr().offset(-offset_from_ptr_to_memory(&dim, &strides)), dim, strides)) } } /// Create an `ArrayViewMut` from shape information and a From 7699e0cdbf5208a610fb38bc4c7479b45a78fad3 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 22 Mar 2021 19:03:49 +0100 Subject: [PATCH 252/651] TEST: Test for negative stride constructors for Raw/ArrayView --- src/impl_views/constructors.rs | 1 + tests/array.rs | 61 ++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/src/impl_views/constructors.rs b/src/impl_views/constructors.rs index 88a289ce7..fd9457fa1 100644 --- a/src/impl_views/constructors.rs +++ b/src/impl_views/constructors.rs @@ -30,6 +30,7 @@ where /// use ndarray::arr3; /// use ndarray::ShapeBuilder; /// + /// // advanced example where we are even specifying exact strides to use (which is optional). /// let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; /// let a = ArrayView::from_shape((2, 3, 2).strides((1, 4, 2)), /// &s).unwrap(); diff --git a/tests/array.rs b/tests/array.rs index b1d37da70..8e084e49e 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1827,6 +1827,27 @@ fn map_memory_order() { assert_eq!(amap.strides(), v.strides()); } +#[test] +fn test_view_from_shape() { + let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; + let a = ArrayView::from_shape((2, 3, 2), &s).unwrap(); + let mut answer = Array::from(s.to_vec()).into_shape((2, 3, 2)).unwrap(); + assert_eq!(a, answer); + + // custom strides (row major) + let a = ArrayView::from_shape((2, 3, 2).strides((6, 2, 1)), &s).unwrap(); + assert_eq!(a, answer); + + // custom strides (col major) + let a = ArrayView::from_shape((2, 3, 2).strides((1, 2, 6)), &s).unwrap(); + assert_eq!(a, answer.t()); + + // negative strides + let a = ArrayView::from_shape((2, 3, 2).strides((6, (-2isize) as usize, 1)), &s).unwrap(); + answer.invert_axis(Axis(1)); + assert_eq!(a, answer); +} + #[test] fn test_contiguous() { let c = arr3(&[[[1, 2, 3], [4, 5, 6]], [[4, 5, 6], [7, 7, 7]]]); @@ -1973,6 +1994,46 @@ fn test_view_from_shape_ptr() { assert_eq!(view, aview2(&[[0, 0, 2], [3, 4, 6]])); } +#[should_panic(expected = "Unsupported")] +#[cfg(debug_assertions)] +#[test] +fn test_view_from_shape_ptr_deny_neg_strides() { + let data = [0, 1, 2, 3, 4, 5]; + let _view = unsafe { + ArrayView::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_ptr()) + }; +} + +#[should_panic(expected = "Unsupported")] +#[cfg(debug_assertions)] +#[test] +fn test_view_mut_from_shape_ptr_deny_neg_strides() { + let mut data = [0, 1, 2, 3, 4, 5]; + let _view = unsafe { + ArrayViewMut::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_mut_ptr()) + }; +} + +#[should_panic(expected = "Unsupported")] +#[cfg(debug_assertions)] +#[test] +fn test_raw_view_from_shape_ptr_deny_neg_strides() { + let data = [0, 1, 2, 3, 4, 5]; + let _view = unsafe { + RawArrayView::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_ptr()) + }; +} + +#[should_panic(expected = "Unsupported")] +#[cfg(debug_assertions)] +#[test] +fn test_raw_view_mut_from_shape_ptr_deny_neg_strides() { + let mut data = [0, 1, 2, 3, 4, 5]; + let _view = unsafe { + RawArrayViewMut::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_mut_ptr()) + }; +} + #[test] fn test_default() { let a = as Default>::default(); From 5c6ff774d8f2c5347b437831428b88438d1037d1 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 22 Mar 2021 19:29:05 +0100 Subject: [PATCH 253/651] MAINT: Rename directories for misc and tests in the root Reorganize the repo a bit by just renaming. We have a bunch of subdirectories for tests, name them all xtest- (for package-external test) so that they line up in directory listing. The images we keep around for docs we put in the directory misc/. --- Cargo.toml | 4 ++-- {docgen/images => misc}/axis_iter.svg | 0 {docgen/images => misc}/split_at.svg | 0 scripts/all-tests.sh | 6 +++--- scripts/cross-tests.sh | 4 ++-- {blas-tests => xtest-blas}/Cargo.toml | 0 {blas-tests => xtest-blas}/src/lib.rs | 0 {blas-tests => xtest-blas}/tests/oper.rs | 0 {numeric-tests => xtest-numeric}/Cargo.toml | 0 {numeric-tests => xtest-numeric}/src/lib.rs | 0 {numeric-tests => xtest-numeric}/tests/accuracy.rs | 0 {serialization-tests => xtest-serialization}/Cargo.toml | 0 {serialization-tests => xtest-serialization}/src/lib.rs | 0 .../tests/serialize.rs | 0 14 files changed, 7 insertions(+), 7 deletions(-) rename {docgen/images => misc}/axis_iter.svg (100%) rename {docgen/images => misc}/split_at.svg (100%) rename {blas-tests => xtest-blas}/Cargo.toml (100%) rename {blas-tests => xtest-blas}/src/lib.rs (100%) rename {blas-tests => xtest-blas}/tests/oper.rs (100%) rename {numeric-tests => xtest-numeric}/Cargo.toml (100%) rename {numeric-tests => xtest-numeric}/src/lib.rs (100%) rename {numeric-tests => xtest-numeric}/tests/accuracy.rs (100%) rename {serialization-tests => xtest-serialization}/Cargo.toml (100%) rename {serialization-tests => xtest-serialization}/src/lib.rs (100%) rename {serialization-tests => xtest-serialization}/tests/serialize.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 22686c5be..ddd29c71a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,8 +80,8 @@ matrixmultiply-threading = ["matrixmultiply/threading"] debug = true [workspace] -members = ["ndarray-rand", "serialization-tests", "blas-tests"] -exclude = ["numeric-tests"] +members = ["ndarray-rand", "xtest-serialization", "xtest-blas"] +exclude = ["xtest-numeric"] [package.metadata.release] no-dev-version = true diff --git a/docgen/images/axis_iter.svg b/misc/axis_iter.svg similarity index 100% rename from docgen/images/axis_iter.svg rename to misc/axis_iter.svg diff --git a/docgen/images/split_at.svg b/misc/split_at.svg similarity index 100% rename from docgen/images/split_at.svg rename to misc/split_at.svg diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index 61dfc56dc..a9cd3e758 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -14,8 +14,8 @@ cargo build --verbose --features "$FEATURES" cargo test --verbose --features "$FEATURES" cargo test --manifest-path=ndarray-rand/Cargo.toml --no-default-features --verbose cargo test --manifest-path=ndarray-rand/Cargo.toml --features quickcheck --verbose -cargo test --manifest-path=serialization-tests/Cargo.toml --verbose -cargo test --manifest-path=blas-tests/Cargo.toml --verbose +cargo test --manifest-path=xtest-serialization/Cargo.toml --verbose +cargo test --manifest-path=xtest-blas/Cargo.toml --verbose cargo test --examples -CARGO_TARGET_DIR=target/ cargo test --manifest-path=numeric-tests/Cargo.toml --verbose +CARGO_TARGET_DIR=target/ cargo test --manifest-path=xtest-numeric/Cargo.toml --verbose ([ "$CHANNEL" != "nightly" ] || cargo bench --no-run --verbose --features "$FEATURES") diff --git a/scripts/cross-tests.sh b/scripts/cross-tests.sh index d9bc8347e..7c4f13111 100755 --- a/scripts/cross-tests.sh +++ b/scripts/cross-tests.sh @@ -10,5 +10,5 @@ TARGET=$3 cross build -v --features="$FEATURES" --target=$TARGET cross test -v --no-fail-fast --features="$FEATURES" --target=$TARGET cross test -v --no-fail-fast --target=$TARGET --manifest-path=ndarray-rand/Cargo.toml --features quickcheck -cross test -v --no-fail-fast --target=$TARGET --manifest-path=serialization-tests/Cargo.toml --verbose -CARGO_TARGET_DIR=target/ cross test -v --no-fail-fast --target=$TARGET --manifest-path=numeric-tests/Cargo.toml +cross test -v --no-fail-fast --target=$TARGET --manifest-path=xtest-serialization/Cargo.toml --verbose +CARGO_TARGET_DIR=target/ cross test -v --no-fail-fast --target=$TARGET --manifest-path=xtest-numeric/Cargo.toml diff --git a/blas-tests/Cargo.toml b/xtest-blas/Cargo.toml similarity index 100% rename from blas-tests/Cargo.toml rename to xtest-blas/Cargo.toml diff --git a/blas-tests/src/lib.rs b/xtest-blas/src/lib.rs similarity index 100% rename from blas-tests/src/lib.rs rename to xtest-blas/src/lib.rs diff --git a/blas-tests/tests/oper.rs b/xtest-blas/tests/oper.rs similarity index 100% rename from blas-tests/tests/oper.rs rename to xtest-blas/tests/oper.rs diff --git a/numeric-tests/Cargo.toml b/xtest-numeric/Cargo.toml similarity index 100% rename from numeric-tests/Cargo.toml rename to xtest-numeric/Cargo.toml diff --git a/numeric-tests/src/lib.rs b/xtest-numeric/src/lib.rs similarity index 100% rename from numeric-tests/src/lib.rs rename to xtest-numeric/src/lib.rs diff --git a/numeric-tests/tests/accuracy.rs b/xtest-numeric/tests/accuracy.rs similarity index 100% rename from numeric-tests/tests/accuracy.rs rename to xtest-numeric/tests/accuracy.rs diff --git a/serialization-tests/Cargo.toml b/xtest-serialization/Cargo.toml similarity index 100% rename from serialization-tests/Cargo.toml rename to xtest-serialization/Cargo.toml diff --git a/serialization-tests/src/lib.rs b/xtest-serialization/src/lib.rs similarity index 100% rename from serialization-tests/src/lib.rs rename to xtest-serialization/src/lib.rs diff --git a/serialization-tests/tests/serialize.rs b/xtest-serialization/tests/serialize.rs similarity index 100% rename from serialization-tests/tests/serialize.rs rename to xtest-serialization/tests/serialize.rs From f98e5c04bffc23910214e73c13ba733c9000f319 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 25 Mar 2021 19:43:46 +0100 Subject: [PATCH 254/651] API: Drop direct blas-src dependency, update docs for blas integration We can in theory unchain ourselves from the blas-src version and let the user be responsible for linking in blas in the final product. Update xtest-blas to test this configuration. Update instructions in readme for how to use it. --- Cargo.toml | 3 +-- README.rst | 29 +++++++++++++++++++++-------- src/lib.rs | 2 -- xtest-blas/src/lib.rs | 1 - xtest-blas/tests/oper.rs | 1 + 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ddd29c71a..374838b7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,6 @@ approx = { version = "0.4", optional = true , default-features = false } # Use via the `blas` crate feature! cblas-sys = { version = "0.1.4", optional = true, default-features = false } -blas-src = { version = "0.7.0", optional = true, default-features = false } libc = { version = "0.2.82", optional = true } matrixmultiply = { version = "0.3.0", default-features = false} @@ -58,7 +57,7 @@ default = ["std"] # Enable blas usage # See README for more instructions -blas = ["cblas-sys", "blas-src", "libc"] +blas = ["cblas-sys", "libc"] # Old name for the serde feature serde-1 = ["serde"] diff --git a/README.rst b/README.rst index 36b98598f..1b57ccaa9 100644 --- a/README.rst +++ b/README.rst @@ -96,21 +96,34 @@ How to use with cargo :: [dependencies] - ndarray = "0.14.0" + ndarray = "0.15.0" -How to enable blas integration. Depend on ``blas-src`` directly to pick a blas -provider. Depend on the same ``blas-src`` version as ``ndarray`` does, for the -selection to work. An example configuration using system openblas is shown -below. Note that only end-user projects (not libraries) should select -provider:: +How to enable blas integration +----------------------------- +Blas integration is an optional add-on. + +Depend and link to ``blas-src`` directly to pick a blas provider. Ndarray +presently requires a blas provider that provides the ``cblas-sys`` interface. If +further feature selection is needed then you might need to depend directly on +the backend crate's source too (for example ``openblas-src``, to select +``cblas``). The backend version **must** be the one that ``blas-src`` also +depends on. + +An example configuration using system openblas is shown below. Note that only +end-user projects (not libraries) should select provider:: [dependencies] - ndarray = { version = "0.14.0", features = ["blas"] } + ndarray = { version = "0.15.0", features = ["blas"] } blas-src = { version = "0.7.0", default-features = false, features = ["openblas"] } openblas-src = { version = "0.9", default-features = false, features = ["cblas", "system"] } -For official releases of ``ndarray``, the versions are: +When this is done, your program must also link to ``blas_src`` by using it or +explicitly including it in your code:: + + extern crate blas_src; + +For official releases of ``ndarray``, versions that have been verified to work are: =========== ============ ================ ``ndarray`` ``blas-src`` ``openblas-src`` diff --git a/src/lib.rs b/src/lib.rs index 9ec666c14..56814d787 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,8 +124,6 @@ extern crate std; #[cfg(not(feature = "std"))] extern crate core as std; -#[cfg(feature = "blas")] -extern crate blas_src; #[cfg(feature = "blas")] extern crate cblas_sys; diff --git a/xtest-blas/src/lib.rs b/xtest-blas/src/lib.rs index 8b1378917..e69de29bb 100644 --- a/xtest-blas/src/lib.rs +++ b/xtest-blas/src/lib.rs @@ -1 +0,0 @@ - diff --git a/xtest-blas/tests/oper.rs b/xtest-blas/tests/oper.rs index d6230cced..da3b1ba63 100644 --- a/xtest-blas/tests/oper.rs +++ b/xtest-blas/tests/oper.rs @@ -2,6 +2,7 @@ extern crate approx; extern crate defmac; extern crate ndarray; extern crate num_traits; +extern crate blas_src; use ndarray::linalg::general_mat_mul; use ndarray::linalg::general_mat_vec_mul; From 5b96f1dd5a3c06277517af8b521c750a4e0d958e Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 25 Mar 2021 19:59:18 +0100 Subject: [PATCH 255/651] TEST: Remove ndarray build.rs and test-blas-openblas-sys feature flag This flag was not properly used anymore. Replace it with a proper blas test setup in xtest-numeric. --- Cargo.toml | 5 +---- build.rs | 10 ---------- scripts/all-tests.sh | 1 + xtest-numeric/Cargo.toml | 5 ++++- xtest-numeric/src/lib.rs | 3 +++ xtest-numeric/tests/accuracy.rs | 2 ++ 6 files changed, 11 insertions(+), 15 deletions(-) delete mode 100644 build.rs diff --git a/Cargo.toml b/Cargo.toml index 374838b7e..f48a371e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,6 @@ description = "An n-dimensional array for general elements and for numerics. Lig keywords = ["array", "data-structure", "multidimensional", "matrix", "blas"] categories = ["data-structures", "science"] -build = "build.rs" - exclude = ["docgen/images/*"] [lib] @@ -63,8 +61,7 @@ blas = ["cblas-sys", "libc"] serde-1 = ["serde"] # These features are used for testing -test-blas-openblas-sys = ["blas"] -test = ["test-blas-openblas-sys"] +test = [] # This feature is used for docs docs = ["approx", "serde", "rayon"] diff --git a/build.rs b/build.rs deleted file mode 100644 index dd06c0b81..000000000 --- a/build.rs +++ /dev/null @@ -1,10 +0,0 @@ -/// -/// This build script emits the openblas linking directive if requested -/// - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - if cfg!(feature = "test-blas-openblas-sys") { - println!("cargo:rustc-link-lib=dylib=openblas"); - } -} diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index a9cd3e758..15fb3fe48 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -18,4 +18,5 @@ cargo test --manifest-path=xtest-serialization/Cargo.toml --verbose cargo test --manifest-path=xtest-blas/Cargo.toml --verbose cargo test --examples CARGO_TARGET_DIR=target/ cargo test --manifest-path=xtest-numeric/Cargo.toml --verbose +CARGO_TARGET_DIR=target/ cargo test --manifest-path=xtest-numeric/Cargo.toml --verbose --features test_blas ([ "$CHANNEL" != "nightly" ] || cargo bench --no-run --verbose --features "$FEATURES") diff --git a/xtest-numeric/Cargo.toml b/xtest-numeric/Cargo.toml index fd017bd39..8636fec41 100644 --- a/xtest-numeric/Cargo.toml +++ b/xtest-numeric/Cargo.toml @@ -10,6 +10,9 @@ ndarray = { path = "..", features = ["approx"] } ndarray-rand = { path = "../ndarray-rand/" } rand_distr = "0.4" +blas-src = { optional = true, version = "0.7.0", default-features = false, features = ["openblas"] } +openblas-src = { optional = true, version = "0.9", default-features = false, features = ["cblas", "system"] } + [dependencies.rand] version = "0.8.0" features = ["small_rng"] @@ -18,7 +21,7 @@ features = ["small_rng"] test = false [features] -test_blas = ["ndarray/blas", "ndarray/test-blas-openblas-sys"] +test_blas = ["ndarray/blas", "blas-src", "openblas-src"] [profile.dev] opt-level = 2 diff --git a/xtest-numeric/src/lib.rs b/xtest-numeric/src/lib.rs index e69de29bb..ba1c3b0d9 100644 --- a/xtest-numeric/src/lib.rs +++ b/xtest-numeric/src/lib.rs @@ -0,0 +1,3 @@ +#[cfg(feature = "test_blas")] +extern crate blas_src; + diff --git a/xtest-numeric/tests/accuracy.rs b/xtest-numeric/tests/accuracy.rs index 044ba17cc..4c454bac1 100644 --- a/xtest-numeric/tests/accuracy.rs +++ b/xtest-numeric/tests/accuracy.rs @@ -4,6 +4,8 @@ extern crate ndarray; extern crate ndarray_rand; extern crate rand; +extern crate numeric_tests; + use ndarray_rand::{RandomExt, F32}; use rand::{Rng, SeedableRng}; use rand::rngs::SmallRng; From 888c160d562331c7dcf48756faab5824ff4c748c Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 25 Mar 2021 21:06:50 +0100 Subject: [PATCH 256/651] API: Update num-complex to 0.4 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f48a371e8..4be34b542 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ test = true [dependencies] num-integer = { version = "0.1.39", default-features = false } num-traits = { version = "0.2", default-features = false } -num-complex = { version = "0.3", default-features = false } +num-complex = { version = "0.4", default-features = false } # Use via the `rayon` crate feature! rayon_ = { version = "1.0.3", optional = true, package = "rayon" } From 383ac0e9b04b13bb154b13a1bb5f75b30b01be4c Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 25 Mar 2021 21:08:21 +0100 Subject: [PATCH 257/651] TEST: Update dev-dependency itertools to 0.10 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4be34b542..70e182242 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rawpointer = { version = "0.2" } defmac = "0.2" quickcheck = { version = "0.9", default-features = false } approx = "0.4" -itertools = { version = "0.9.0", default-features = false, features = ["use_std"] } +itertools = { version = "0.10.0", default-features = false, features = ["use_std"] } [features] default = ["std"] From c7ae4eb1aa9837705280243a7ca9e3ec3014866c Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 25 Mar 2021 21:00:08 +0100 Subject: [PATCH 258/651] 0.15.0 --- Cargo.toml | 2 +- RELEASES.md | 60 ++++++++++++++++++++++++----------------- ndarray-rand/Cargo.toml | 4 +-- src/lib.rs | 2 +- 4 files changed, 39 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 70e182242..61c26d4cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ndarray" -version = "0.14.0" +version = "0.15.0" edition = "2018" authors = [ "bluss", diff --git a/RELEASES.md b/RELEASES.md index 910378d02..8e6e0c226 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,5 @@ -Version 0.15.0 (Not released yet) -================================= +Version 0.15.0 (2021-03-25) +=========================== New features ------------ @@ -14,6 +14,9 @@ New features - Support two-sided broadcasting in arithmetic operations with arrays by [@SparrowLii] + This now allows, for example, addition of a 3 x 1 with a 1 x 3 array; the + operands are in this case broadcast to 3 x 3 which is the shape of the result. + Note that this means that a new trait bound is required in some places when mixing dimensionality types of arrays in arithmetic operations. @@ -45,25 +48,11 @@ New features Enhancements ------------ -- New constructors `Array::from_iter` and `Array::from_vec` by [@bluss]. - No new functionality, just that these constructors are avaiable without trait - imports. - - https://github.com/rust-ndarray/ndarray/pull/921 - - Ndarray can now correctly determine that arrays can be contiguous, even if they have negative strides, by [@SparrowLii] - https://github.com/rust-ndarray/ndarray/pull/885 - -- `NdProducer::raw_dim` is now a documented method by [@jturner314] - - https://github.com/rust-ndarray/ndarray/pull/918 - -- `AxisDescription` is now a struct with field names, not a tuple struct by - [@jturner314] - - https://github.com/rust-ndarray/ndarray/pull/915 + https://github.com/rust-ndarray/ndarray/pull/885
+ https://github.com/rust-ndarray/ndarray/pull/948 - Improvements to `map_inplace` by [@jturner314] @@ -79,15 +68,30 @@ Enhancements https://github.com/rust-ndarray/ndarray/pull/890 +API changes +----------- + +- New constructors `Array::from_iter` and `Array::from_vec` by [@bluss]. + No new functionality, just that these constructors are avaiable without trait + imports. + + https://github.com/rust-ndarray/ndarray/pull/921 + +- `NdProducer::raw_dim` is now a documented method by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/918 + +- `AxisDescription` is now a struct with field names, not a tuple struct by + [@jturner314]. Its accessor methods are now deprecated. + + https://github.com/rust-ndarray/ndarray/pull/915 + - Methods for array comparison `abs_diff_eq` and `relative_eq` are now exposed as inherent methods too (no trait import needed), still under the approx feature flag by [@bluss] https://github.com/rust-ndarray/ndarray/pull/946 -API changes ------------ - - Changes to the slicing-related types and macro by [@jturner314] and [@bluss]: - Remove the `Dimension::SliceArg` associated type, and add a new `SliceArg` @@ -155,16 +159,15 @@ API changes https://github.com/rust-ndarray/ndarray/pull/937
https://github.com/rust-ndarray/ndarray/pull/907
-- `blas-src` dependency updated to 0.7.0 by [@bluss] - - https://github.com/rust-ndarray/ndarray/pull/891 - - Updated `matrixmultiply` dependency to 0.3.0 by [@bluss] and adding new feature flag `matrixmultiply-threading` to enable its threading https://github.com/rust-ndarray/ndarray/pull/888
https://github.com/rust-ndarray/ndarray/pull/938
+- Updated `num-complex` dependency to 0.4.0 by [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/952 Bug fixes --------- @@ -191,6 +194,13 @@ Bug fixes Other changes ------------- +- It was changed how we integrate with BLAS and `blas-src`. Users of BLAS need + to read the README for the updated instructions. Ndarray itself no longer + has public dependency on `blas-src`. Changes by [@bluss]. + + https://github.com/rust-ndarray/ndarray/pull/891
+ https://github.com/rust-ndarray/ndarray/pull/951 + - Various improvements to tests and CI by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/934
diff --git a/ndarray-rand/Cargo.toml b/ndarray-rand/Cargo.toml index a02b346aa..4127d5631 100644 --- a/ndarray-rand/Cargo.toml +++ b/ndarray-rand/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ndarray-rand" -version = "0.13.0" +version = "0.14.0" edition = "2018" authors = ["bluss"] license = "MIT OR Apache-2.0" @@ -14,7 +14,7 @@ description = "Constructors for randomized arrays. `rand` integration for `ndarr keywords = ["multidimensional", "matrix", "rand", "ndarray"] [dependencies] -ndarray = { version = "0.14", path = ".." } +ndarray = { version = "0.15", path = ".." } rand_distr = "0.4.0" quickcheck = { version = "0.9", default-features = false, optional = true } diff --git a/src/lib.rs b/src/lib.rs index 56814d787..e92ea6c52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. #![crate_name = "ndarray"] -#![doc(html_root_url = "https://docs.rs/ndarray/0.14/")] +#![doc(html_root_url = "https://docs.rs/ndarray/0.15/")] #![allow( clippy::many_single_char_names, clippy::deref_addrof, From 27802bd933604c97f859f37ca2cd96238a03be53 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 27 Mar 2021 13:02:33 +0100 Subject: [PATCH 259/651] TEST: Reduce number of test in blas-tests There's a lot of tests here that don't make sense for our BLAS integration, delete them (we just copied a file wholesale to create this). --- xtest-blas/tests/oper.rs | 302 +-------------------------------------- 1 file changed, 5 insertions(+), 297 deletions(-) diff --git a/xtest-blas/tests/oper.rs b/xtest-blas/tests/oper.rs index da3b1ba63..0aeb47680 100644 --- a/xtest-blas/tests/oper.rs +++ b/xtest-blas/tests/oper.rs @@ -4,64 +4,15 @@ extern crate ndarray; extern crate num_traits; extern crate blas_src; +use ndarray::prelude::*; + use ndarray::linalg::general_mat_mul; use ndarray::linalg::general_mat_vec_mul; -use ndarray::prelude::*; -use ndarray::{Data, Ix, Ixs, LinalgScalar, Slice, SliceInfoElem}; +use ndarray::{Data, Ix, LinalgScalar}; -use approx::{assert_abs_diff_eq, assert_relative_eq}; +use approx::assert_relative_eq; use defmac::defmac; -fn reference_dot<'a, A, V1, V2>(a: V1, b: V2) -> A -where - A: NdFloat, - V1: AsArray<'a, A>, - V2: AsArray<'a, A>, -{ - let a = a.into(); - let b = b.into(); - a.iter() - .zip(b.iter()) - .fold(A::zero(), |acc, (&x, &y)| acc + x * y) -} - -#[test] -fn dot_product() { - let a = Array::range(0., 69., 1.); - let b = &a * 2. - 7.; - let dot = 197846.; - assert_abs_diff_eq!(a.dot(&b), reference_dot(&a, &b), epsilon = 1e-5); - - // test different alignments - let max = 8 as Ixs; - for i in 1..max { - let a1 = a.slice(s![i..]); - let b1 = b.slice(s![i..]); - assert_abs_diff_eq!(a1.dot(&b1), reference_dot(&a1, &b1), epsilon = 1e-5); - let a2 = a.slice(s![..-i]); - let b2 = b.slice(s![i..]); - assert_abs_diff_eq!(a2.dot(&b2), reference_dot(&a2, &b2), epsilon = 1e-5); - } - - let a = a.map(|f| *f as f32); - let b = b.map(|f| *f as f32); - assert_abs_diff_eq!(a.dot(&b), dot as f32, epsilon = 1e-5); - - let max = 8 as Ixs; - for i in 1..max { - let a1 = a.slice(s![i..]); - let b1 = b.slice(s![i..]); - assert_abs_diff_eq!(a1.dot(&b1), reference_dot(&a1, &b1), epsilon = 1e-5); - let a2 = a.slice(s![..-i]); - let b2 = b.slice(s![i..]); - assert_abs_diff_eq!(a2.dot(&b2), reference_dot(&a2, &b2), epsilon = 1e-5); - } - - let a = a.map(|f| *f as i32); - let b = b.map(|f| *f as i32); - assert_eq!(a.dot(&b), dot as i32); -} - #[test] fn mat_vec_product_1d() { let a = arr2(&[[1.], [2.]]); @@ -70,46 +21,6 @@ fn mat_vec_product_1d() { assert_eq!(a.t().dot(&b), ans); } -// test that we can dot product with a broadcast array -#[test] -fn dot_product_0() { - let a = Array::range(0., 69., 1.); - let x = 1.5; - let b = aview0(&x); - let b = b.broadcast(a.dim()).unwrap(); - assert_abs_diff_eq!(a.dot(&b), reference_dot(&a, &b), epsilon = 1e-5); - - // test different alignments - let max = 8 as Ixs; - for i in 1..max { - let a1 = a.slice(s![i..]); - let b1 = b.slice(s![i..]); - assert_abs_diff_eq!(a1.dot(&b1), reference_dot(&a1, &b1), epsilon = 1e-5); - let a2 = a.slice(s![..-i]); - let b2 = b.slice(s![i..]); - assert_abs_diff_eq!(a2.dot(&b2), reference_dot(&a2, &b2), epsilon = 1e-5); - } -} - -#[test] -fn dot_product_neg_stride() { - // test that we can dot with negative stride - let a = Array::range(0., 69., 1.); - let b = &a * 2. - 7.; - for stride in -10..0 { - // both negative - let a = a.slice(s![..;stride]); - let b = b.slice(s![..;stride]); - assert_abs_diff_eq!(a.dot(&b), reference_dot(&a, &b), epsilon = 1e-5); - } - for stride in -10..0 { - // mixed - let a = a.slice(s![..;-stride]); - let b = b.slice(s![..;stride]); - assert_abs_diff_eq!(a.dot(&b), reference_dot(&a, &b), epsilon = 1e-5); - } -} - fn range_mat(m: Ix, n: Ix) -> Array2 { Array::linspace(0., (m * n) as f32 - 1., m * n) .into_shape((m, n)) @@ -190,71 +101,11 @@ where .unwrap() } -#[test] -fn mat_mul() { - let (m, n, k) = (8, 8, 8); - let a = range_mat(m, n); - let b = range_mat(n, k); - let mut b = b / 4.; - { - let mut c = b.column_mut(0); - c += 1.0; - } - let ab = a.dot(&b); - - let mut af = Array::zeros(a.dim().f()); - let mut bf = Array::zeros(b.dim().f()); - af.assign(&a); - bf.assign(&b); - - assert_eq!(ab, a.dot(&bf)); - assert_eq!(ab, af.dot(&b)); - assert_eq!(ab, af.dot(&bf)); - - let (m, n, k) = (10, 5, 11); - let a = range_mat(m, n); - let b = range_mat(n, k); - let mut b = b / 4.; - { - let mut c = b.column_mut(0); - c += 1.0; - } - let ab = a.dot(&b); - - let mut af = Array::zeros(a.dim().f()); - let mut bf = Array::zeros(b.dim().f()); - af.assign(&a); - bf.assign(&b); - - assert_eq!(ab, a.dot(&bf)); - assert_eq!(ab, af.dot(&b)); - assert_eq!(ab, af.dot(&bf)); - - let (m, n, k) = (10, 8, 1); - let a = range_mat(m, n); - let b = range_mat(n, k); - let mut b = b / 4.; - { - let mut c = b.column_mut(0); - c += 1.0; - } - let ab = a.dot(&b); - - let mut af = Array::zeros(a.dim().f()); - let mut bf = Array::zeros(b.dim().f()); - af.assign(&a); - bf.assign(&b); - - assert_eq!(ab, a.dot(&bf)); - assert_eq!(ab, af.dot(&b)); - assert_eq!(ab, af.dot(&bf)); -} - // Check that matrix multiplication of contiguous matrices returns a // matrix with the same order #[test] fn mat_mul_order() { - let (m, n, k) = (8, 8, 8); + let (m, n, k) = (50, 50, 50); let a = range_mat(m, n); let b = range_mat(n, k); let mut af = Array::zeros(a.dim().f()); @@ -269,27 +120,6 @@ fn mat_mul_order() { assert_eq!(ff.strides()[0], 1); } -// test matrix multiplication shape mismatch -#[test] -#[should_panic] -fn mat_mul_shape_mismatch() { - let (m, k, k2, n) = (8, 8, 9, 8); - let a = range_mat(m, k); - let b = range_mat(k2, n); - a.dot(&b); -} - -// test matrix multiplication shape mismatch -#[test] -#[should_panic] -fn mat_mul_shape_mismatch_2() { - let (m, k, k2, n) = (8, 8, 8, 8); - let a = range_mat(m, k); - let b = range_mat(k2, n); - let mut c = range_mat(m, n + 1); - general_mat_mul(1., &a, &b, 1., &mut c); -} - // Check that matrix multiplication // supports broadcast arrays. #[test] @@ -348,102 +178,6 @@ fn mat_mut_zero_len() { mat_mul_zero_len!(range_i32); } -#[test] -fn scaled_add() { - let a = range_mat(16, 15); - let mut b = range_mat(16, 15); - b.mapv_inplace(f32::exp); - - let alpha = 0.2_f32; - let mut c = a.clone(); - c.scaled_add(alpha, &b); - - let d = alpha * &b + &a; - assert_eq!(c, d); -} - -#[test] -fn scaled_add_2() { - let beta = -2.3; - let sizes = vec![ - (4, 4, 1, 4), - (8, 8, 1, 8), - (17, 15, 17, 15), - (4, 17, 4, 17), - (17, 3, 1, 3), - (19, 18, 19, 18), - (16, 17, 16, 17), - (15, 16, 15, 16), - (67, 63, 1, 63), - ]; - // test different strides - for &s1 in &[1, 2, -1, -2] { - for &s2 in &[1, 2, -1, -2] { - for &(m, k, n, q) in &sizes { - let mut a = range_mat64(m, k); - let mut answer = a.clone(); - let c = range_mat64(n, q); - - { - let mut av = a.slice_mut(s![..;s1, ..;s2]); - let c = c.slice(s![..;s1, ..;s2]); - - let mut answerv = answer.slice_mut(s![..;s1, ..;s2]); - answerv += &(beta * &c); - av.scaled_add(beta, &c); - } - assert_relative_eq!(a, answer, epsilon = 1e-12, max_relative = 1e-7); - } - } - } -} - -#[test] -fn scaled_add_3() { - let beta = -2.3; - let sizes = vec![ - (4, 4, 1, 4), - (8, 8, 1, 8), - (17, 15, 17, 15), - (4, 17, 4, 17), - (17, 3, 1, 3), - (19, 18, 19, 18), - (16, 17, 16, 17), - (15, 16, 15, 16), - (67, 63, 1, 63), - ]; - // test different strides - for &s1 in &[1, 2, -1, -2] { - for &s2 in &[1, 2, -1, -2] { - for &(m, k, n, q) in &sizes { - let mut a = range_mat64(m, k); - let mut answer = a.clone(); - let cdim = if n == 1 { vec![q] } else { vec![n, q] }; - let cslice: Vec = if n == 1 { - vec![Slice::from(..).step_by(s2).into()] - } else { - vec![ - Slice::from(..).step_by(s1).into(), - Slice::from(..).step_by(s2).into(), - ] - }; - - let c = range_mat64(n, q).into_shape(cdim).unwrap(); - - { - let mut av = a.slice_mut(s![..;s1, ..;s2]); - let c = c.slice(&*cslice); - - let mut answerv = answer.slice_mut(s![..;s1, ..;s2]); - answerv += &(beta * &c); - av.scaled_add(beta, &c); - } - assert_relative_eq!(a, answer, epsilon = 1e-12, max_relative = 1e-7); - } - } - } -} - #[test] fn gen_mat_mul() { let alpha = -2.3; @@ -497,32 +231,6 @@ fn gemm_64_1_f() { assert_relative_eq!(y, answer, epsilon = 1e-12, max_relative = 1e-7); } -#[test] -fn gen_mat_mul_i32() { - let alpha = -1; - let beta = 2; - let sizes = vec![ - (4, 4, 4), - (8, 8, 8), - (17, 15, 16), - (4, 17, 3), - (17, 3, 22), - (19, 18, 2), - (16, 17, 15), - (15, 16, 17), - (67, 63, 62), - ]; - for &(m, k, n) in &sizes { - let a = range_i32(m, k); - let b = range_i32(k, n); - let mut c = range_i32(m, n); - - let answer = alpha * reference_mat_mul(&a, &b) + beta * &c; - general_mat_mul(alpha, &a, &b, beta, &mut c); - assert_eq!(&c, &answer); - } -} - #[test] fn gen_mat_vec_mul() { let alpha = -2.3; From 0b7d317008d3f93da3dec5aa67d8e725f1699882 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 27 Mar 2021 13:08:57 +0100 Subject: [PATCH 260/651] DOC: Elaborate a bit more on BLAS integration instructions --- README.rst | 38 +++++++++++++++++++++++--------------- xtest-blas/Cargo.toml | 14 +++++++++++--- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/README.rst b/README.rst index 1b57ccaa9..017622f3f 100644 --- a/README.rst +++ b/README.rst @@ -101,39 +101,47 @@ How to use with cargo How to enable blas integration ----------------------------- -Blas integration is an optional add-on. +Blas integration is an optional add-on. Without BLAS, ndarray uses the +``matrixmultiply`` crate for matrix multiplication for ``f64`` and ``f32`` +arrays (and it's always enabled as a fallback since it supports matrices of +arbitrary strides in both dimensions). Depend and link to ``blas-src`` directly to pick a blas provider. Ndarray presently requires a blas provider that provides the ``cblas-sys`` interface. If -further feature selection is needed then you might need to depend directly on -the backend crate's source too (for example ``openblas-src``, to select -``cblas``). The backend version **must** be the one that ``blas-src`` also -depends on. +further feature selection is wanted or needed then you might need to depend directly on +the backend crate's source too. The backend version **must** be the one that +``blas-src`` also depends on. An example configuration using system openblas is shown below. Note that only end-user projects (not libraries) should select provider:: [dependencies] ndarray = { version = "0.15.0", features = ["blas"] } - blas-src = { version = "0.7.0", default-features = false, features = ["openblas"] } - openblas-src = { version = "0.9", default-features = false, features = ["cblas", "system"] } + blas-src = { version = "0.7.0", features = ["openblas"] } + openblas-src = { version = "0.9", features = ["cblas", "system"] } + +Using system-installed dependencies can save a long time building dependencies. +An example configuration using (compiled) netlib is shown below anyway:: + + [dependencies] + ndarray = { version = "0.15.0", features = ["blas"] } + blas-src = { version = "0.7.0", default-features = false, features = ["netlib"] } When this is done, your program must also link to ``blas_src`` by using it or explicitly including it in your code:: extern crate blas_src; -For official releases of ``ndarray``, versions that have been verified to work are: +The following versions have been verified to work together. For ndarray 0.15 or later, +there is no tight coupling to the ``blas-src`` version, so any version should in theory work. -=========== ============ ================ -``ndarray`` ``blas-src`` ``openblas-src`` -=========== ============ ================ -0.15 0.7.0 0.9.0 +=========== ============ ================ ============== +``ndarray`` ``blas-src`` ``openblas-src`` ``netlib-src`` +=========== ============ ================ ============== +0.15 0.7.0 0.9.0 0.8.0 0.14 0.6.1 0.9.0 0.13 0.2.0 0.6.0 -0.12 0.2.0 0.6.0 -0.11 0.1.2 0.5.0 -=========== ============ ================ +=========== ============ ================ ============== Recent Changes -------------- diff --git a/xtest-blas/Cargo.toml b/xtest-blas/Cargo.toml index 4d0792fd0..9be50d3d0 100644 --- a/xtest-blas/Cargo.toml +++ b/xtest-blas/Cargo.toml @@ -8,9 +8,17 @@ publish = false test = false [dev-dependencies] -approx = "0.4" ndarray = { path = "../", features = ["approx", "blas"] } -blas-src = { version = "0.7.0", default-features = false, features = ["openblas"] } -openblas-src = { version = "0.9.0", default-features = false, features = ["cblas", "system"] } + +approx = "0.4" defmac = "0.2" num-traits = "0.2" + +blas-src = { version = "0.7.0", features = ["openblas"] } +openblas-src = { version = "0.9.0", features = ["system"] } + +#blas-src = { version = "0.7.0", features = ["netlib"] } +#netlib-src = { version = "0.8.0", default-features = false, features = ["cblas", "system"] } + +#blas-src = { version = "0.7.0", features = ["blis"] } +#blis-src = { version = "0.2.0", features = ["system"] } From a241240762784f1fc484c06367b884101c809d30 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 27 Mar 2021 13:11:20 +0100 Subject: [PATCH 261/651] FIX: Simplify .is_square() slightly --- src/impl_2d.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/impl_2d.rs b/src/impl_2d.rs index 64d4860cf..6e2cfa2c7 100644 --- a/src/impl_2d.rs +++ b/src/impl_2d.rs @@ -118,6 +118,7 @@ where /// assert!(!array.is_square()); /// ``` pub fn is_square(&self) -> bool { - self.nrows() == self.ncols() + let (m, n) = self.dim(); + m == n } } From be13f3d2b5f9cac5d590dfe66397027a74524a2f Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 27 Mar 2021 13:11:20 +0100 Subject: [PATCH 262/651] FIX: Update Axes example, clarify docs and adjust reexports Update the example to use fields instead of method calls. (The deprecation warnings in examples are never visible to us.) Use pub(crate) more correctly here, for the items that are not pub. --- src/dimension/axes.rs | 17 +++++++++++------ src/dimension/mod.rs | 3 ++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/dimension/axes.rs b/src/dimension/axes.rs index a678e78ca..d6f1c7280 100644 --- a/src/dimension/axes.rs +++ b/src/dimension/axes.rs @@ -1,7 +1,7 @@ use crate::{Axis, Dimension, Ix, Ixs}; /// Create a new Axes iterator -pub fn axes_of<'a, D>(d: &'a D, strides: &'a D) -> Axes<'a, D> +pub(crate) fn axes_of<'a, D>(d: &'a D, strides: &'a D) -> Axes<'a, D> where D: Dimension, { @@ -15,9 +15,10 @@ where /// An iterator over the length and stride of each axis of an array. /// -/// See [`.axes()`](../struct.ArrayBase.html#method.axes) for more information. +/// This iterator is created from the array method +/// [`.axes()`](crate::ArrayBase::axes). /// -/// Iterator element type is `AxisDescription`. +/// Iterator element type is [`AxisDescription`]. /// /// # Examples /// @@ -27,10 +28,14 @@ where /// /// let a = Array3::::zeros((3, 5, 4)); /// +/// // find the largest axis in the array +/// // check the axis index and its length +/// /// let largest_axis = a.axes() -/// .max_by_key(|ax| ax.len()) -/// .unwrap().axis(); -/// assert_eq!(largest_axis, Axis(1)); +/// .max_by_key(|ax| ax.len) +/// .unwrap(); +/// assert_eq!(largest_axis.axis, Axis(1)); +/// assert_eq!(largest_axis.len, 5); /// ``` #[derive(Debug)] pub struct Axes<'a, D> { diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index fb062513e..f4f46e764 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -11,7 +11,8 @@ use crate::slice::SliceArg; use crate::{Ix, Ixs, Slice, SliceInfoElem}; use num_integer::div_floor; -pub use self::axes::{axes_of, Axes, AxisDescription}; +pub use self::axes::{Axes, AxisDescription}; +pub(crate) use self::axes::axes_of; pub use self::axis::Axis; pub use self::broadcast::DimMax; pub use self::conversion::IntoDimension; From 7be7fd586ef36226040b9d3d544106c068619097 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 27 Mar 2021 13:30:03 +0100 Subject: [PATCH 263/651] DOC: Add comment on Axis about why we don't have conversion traits --- src/dimension/axis.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/dimension/axis.rs b/src/dimension/axis.rs index 42a1ee12c..f4625d2da 100644 --- a/src/dimension/axis.rs +++ b/src/dimension/axis.rs @@ -8,11 +8,21 @@ /// An axis index. /// -/// An axis one of an array’s “dimensions”; an *n*-dimensional array has *n* axes. -/// Axis *0* is the array’s outermost axis and *n*-1 is the innermost. +/// An axis one of an array’s “dimensions”; an *n*-dimensional array has *n* +/// axes. Axis *0* is the array’s outermost axis and *n*-1 is the innermost. /// /// All array axis arguments use this type to make the code easier to write /// correctly and easier to understand. +/// +/// For example: in a method like `index_axis(axis, index)` the code becomes +/// self-explanatory when it's called like `.index_axis(Axis(1), i)`; it's +/// evident which integer is the axis number and which is the index. +/// +/// Note: This type does **not** implement From/Into usize and similar trait +/// based conversions, because we want to preserve code readability and quality. +/// +/// `Axis(1)` in itself is a very clear code style and the style that should be +/// avoided is code like `1.into()`. #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Axis(pub usize); From c542a6bfa2badd4ec031d2db53b60cd687c7f4e6 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 27 Mar 2021 13:37:32 +0100 Subject: [PATCH 264/651] MAINT: Silence one clippy lint --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index e92ea6c52..5b2314c30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,8 @@ #![allow( clippy::many_single_char_names, clippy::deref_addrof, - clippy::unreadable_literal + clippy::unreadable_literal, + clippy::manual_map, // is not an error )] #![cfg_attr(not(feature = "std"), no_std)] From e377e825e114abe2ba6f70f1bb5e4864af86162b Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 27 Mar 2021 14:05:13 +0100 Subject: [PATCH 265/651] FIX: Fix .windows() producer for negative stride arrays Avoid using ArrayView::from_shape_ptr (public constructor) because it does not allow negative stride arrays. Use the internal constructor ArrayView::new, which is easier. --- src/iterators/windows.rs | 2 +- tests/windows.rs | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/iterators/windows.rs b/src/iterators/windows.rs index 2d9095303..4538f7abb 100644 --- a/src/iterators/windows.rs +++ b/src/iterators/windows.rs @@ -41,7 +41,7 @@ impl<'a, A, D: Dimension> Windows<'a, A, D> { unsafe { Windows { - base: ArrayView::from_shape_ptr(size.strides(a.strides), a.ptr.as_ptr()), + base: ArrayView::new(a.ptr, size, a.strides), window, strides: window_strides, } diff --git a/tests/windows.rs b/tests/windows.rs index 9fb8cc8ae..87fdf6ecb 100644 --- a/tests/windows.rs +++ b/tests/windows.rs @@ -116,3 +116,39 @@ fn test_window_zip() { } } } + +#[test] +fn test_window_neg_stride() { + let array = Array::from_iter(1..10).into_shape((3, 3)).unwrap(); + + // window neg/pos stride combinations + + // Make a 2 x 2 array of the windows of the 3 x 3 array + // and compute test answers from here + let mut answer = Array::from_iter(array.windows((2, 2)).into_iter().map(|a| a.to_owned())) + .into_shape((2, 2)).unwrap(); + + answer.invert_axis(Axis(1)); + answer.map_inplace(|a| a.invert_axis(Axis(1))); + + itertools::assert_equal( + array.slice(s![.., ..;-1]).windows((2, 2)), + answer.iter().map(|a| a.view()) + ); + + answer.invert_axis(Axis(0)); + answer.map_inplace(|a| a.invert_axis(Axis(0))); + + itertools::assert_equal( + array.slice(s![..;-1, ..;-1]).windows((2, 2)), + answer.iter().map(|a| a.view()) + ); + + answer.invert_axis(Axis(1)); + answer.map_inplace(|a| a.invert_axis(Axis(1))); + + itertools::assert_equal( + array.slice(s![..;-1, ..]).windows((2, 2)), + answer.iter().map(|a| a.view()) + ); +} From b1b5a0e17fd3b5135e03d13a985d7edf3cbc039c Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 27 Mar 2021 14:54:03 +0100 Subject: [PATCH 266/651] FEAT: Implement PartialEq for array == &array and &array == array This adds missing implementations - so that we can compare a == &b and &a == b where a and b are arrays or array views. The change to the windows test shows why this is beneficial - no need to create views from &Array etc just to do a comparison. --- RELEASES.md | 10 ++++++++++ src/arraytraits.rs | 28 ++++++++++++++++++++++++++++ tests/windows.rs | 6 +++--- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 8e6e0c226..767828254 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,13 @@ +Version 0.15.1 (not released yet) +=========================== + +Enhancements +------------ + +- Arrays and views now implement PartialEq so that it's possible to compare + arrays with references to arrays and vice versa by [@bluss] + + Version 0.15.0 (2021-03-25) =========================== diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 521822345..23f99b8fb 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -112,6 +112,34 @@ where } } +/// Return `true` if the array shapes and all elements of `self` and +/// `rhs` are equal. Return `false` otherwise. +impl<'a, A, B, S, S2, D> PartialEq<&'a ArrayBase> for ArrayBase +where + A: PartialEq, + S: Data, + S2: Data, + D: Dimension, +{ + fn eq(&self, rhs: &&ArrayBase) -> bool { + *self == **rhs + } +} + +/// Return `true` if the array shapes and all elements of `self` and +/// `rhs` are equal. Return `false` otherwise. +impl<'a, A, B, S, S2, D> PartialEq> for &'a ArrayBase +where + A: PartialEq, + S: Data, + S2: Data, + D: Dimension, +{ + fn eq(&self, rhs: &ArrayBase) -> bool { + **self == *rhs + } +} + impl Eq for ArrayBase where D: Dimension, diff --git a/tests/windows.rs b/tests/windows.rs index 87fdf6ecb..b0482e4bd 100644 --- a/tests/windows.rs +++ b/tests/windows.rs @@ -133,7 +133,7 @@ fn test_window_neg_stride() { itertools::assert_equal( array.slice(s![.., ..;-1]).windows((2, 2)), - answer.iter().map(|a| a.view()) + answer.iter() ); answer.invert_axis(Axis(0)); @@ -141,7 +141,7 @@ fn test_window_neg_stride() { itertools::assert_equal( array.slice(s![..;-1, ..;-1]).windows((2, 2)), - answer.iter().map(|a| a.view()) + answer.iter() ); answer.invert_axis(Axis(1)); @@ -149,6 +149,6 @@ fn test_window_neg_stride() { itertools::assert_equal( array.slice(s![..;-1, ..]).windows((2, 2)), - answer.iter().map(|a| a.view()) + answer.iter() ); } From abb0f9e97ed6632d4f11bc3f400ec3bbc4fd02e0 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 27 Mar 2021 15:02:47 +0100 Subject: [PATCH 267/651] ndarray-rand 0.14 --- ndarray-rand/RELEASES.md | 14 ++++++++++---- ndarray-rand/benches/bench.rs | 5 ++--- ndarray-rand/src/lib.rs | 2 ++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/ndarray-rand/RELEASES.md b/ndarray-rand/RELEASES.md index 0142831af..8d7586fbe 100644 --- a/ndarray-rand/RELEASES.md +++ b/ndarray-rand/RELEASES.md @@ -1,12 +1,18 @@ Recent Changes -------------- +- 0.14.0 + + - Require ndarray 0.15 + - Require rand 0.8 (unchanged from previous version) + - The F32 wrapper is now deprecated, it's redundant + - 0.13.0 -- Require ndarray 0.14 (unchanged from previous version) -- Require rand 0.8 -- Require rand_distr 0.4 -- Fix methods `sample_axis` and `sample_axis_using` so that they can be used on array views too. + - Require ndarray 0.14 (unchanged from previous version) + - Require rand 0.8 + - Require rand_distr 0.4 + - Fix methods `sample_axis` and `sample_axis_using` so that they can be used on array views too. - 0.12.0 diff --git a/ndarray-rand/benches/bench.rs b/ndarray-rand/benches/bench.rs index bdd010bc1..b58d80a88 100644 --- a/ndarray-rand/benches/bench.rs +++ b/ndarray-rand/benches/bench.rs @@ -4,7 +4,6 @@ extern crate test; use ndarray::Array; use ndarray_rand::RandomExt; -use ndarray_rand::F32; use rand_distr::Normal; use rand_distr::Uniform; @@ -19,11 +18,11 @@ fn uniform_f32(b: &mut Bencher) { #[bench] fn norm_f32(b: &mut Bencher) { let m = 100; - b.iter(|| Array::random((m, m), F32(Normal::new(0., 1.).unwrap()))); + b.iter(|| Array::random((m, m), Normal::new(0f32, 1.).unwrap())); } #[bench] fn norm_f64(b: &mut Bencher) { let m = 100; - b.iter(|| Array::random((m, m), Normal::new(0., 1.).unwrap())); + b.iter(|| Array::random((m, m), Normal::new(0f64, 1.).unwrap())); } diff --git a/ndarray-rand/src/lib.rs b/ndarray-rand/src/lib.rs index 7dc95ec5d..e293eb561 100644 --- a/ndarray-rand/src/lib.rs +++ b/ndarray-rand/src/lib.rs @@ -333,8 +333,10 @@ fn get_rng() -> SmallRng { /// // [ -0.6810, 0.1678, -0.9487, 0.3150, 1.2981]] /// # } #[derive(Copy, Clone, Debug)] +#[deprecated(since="0.14.0", note="Redundant with rand 0.8")] pub struct F32(pub S); +#[allow(deprecated)] impl Distribution for F32 where S: Distribution, From 3d7df5cbfa29bb7b42671e8cf69f6eb7fc9a372c Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 27 Mar 2021 15:32:35 +0100 Subject: [PATCH 268/651] TEST: Stop using ndarray-rand F32 in tests --- xtest-numeric/tests/accuracy.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xtest-numeric/tests/accuracy.rs b/xtest-numeric/tests/accuracy.rs index 4c454bac1..438b73705 100644 --- a/xtest-numeric/tests/accuracy.rs +++ b/xtest-numeric/tests/accuracy.rs @@ -6,7 +6,7 @@ extern crate rand; extern crate numeric_tests; -use ndarray_rand::{RandomExt, F32}; +use ndarray_rand::RandomExt; use rand::{Rng, SeedableRng}; use rand::rngs::SmallRng; @@ -55,7 +55,7 @@ fn reference_mat_mul(lhs: &ArrayBase, rhs: &ArrayBase fn gen(d: D) -> Array where D: Dimension, { - Array::random(d, F32(Normal::new(0., 1.).unwrap())) + Array::random(d, Normal::new(0., 1.).unwrap()) } fn gen_f64(d: D) -> Array where D: Dimension, From 5b8f6de217df0531c5629235d3c95877be9d6969 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 27 Mar 2021 16:06:38 +0100 Subject: [PATCH 269/651] DOC: Fix .len() -> len in another AxisDescription example --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 5b2314c30..35b64dce8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -596,7 +596,7 @@ pub type Ixs = isize; /// S::Elem: Clone, /// D: Dimension, /// { -/// arr.slice_each_axis_mut(|ax| Slice::from(0..ax.len() / 2)).fill(x); +/// arr.slice_each_axis_mut(|ax| Slice::from(0..ax.len / 2)).fill(x); /// } /// fill_lower(&mut h, 9); /// let k = arr2(&[[9, 9, 2, 3], From 86accd6264c572a361794837552e18937846caad Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 27 Mar 2021 19:08:36 +0100 Subject: [PATCH 270/651] MAINT: Update blas test for blas-src 0.8 The version decoupling in ndarray 0.15 was a success, now it's easy to use ndarray with blas-src 0.7 or 0.8, for example. Update docs and xblas-test (blas-tests) with the new versions. --- README.rst | 5 +++-- scripts/all-tests.sh | 2 +- xtest-blas/Cargo.toml | 23 +++++++++++++++-------- xtest-blas/src/lib.rs | 6 ++++++ xtest-numeric/Cargo.toml | 4 ++-- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/README.rst b/README.rst index 017622f3f..a2e189f58 100644 --- a/README.rst +++ b/README.rst @@ -133,12 +133,13 @@ explicitly including it in your code:: extern crate blas_src; The following versions have been verified to work together. For ndarray 0.15 or later, -there is no tight coupling to the ``blas-src`` version, so any version should in theory work. +there is no tight coupling to the ``blas-src`` version, so version selection is more flexible. =========== ============ ================ ============== ``ndarray`` ``blas-src`` ``openblas-src`` ``netlib-src`` =========== ============ ================ ============== -0.15 0.7.0 0.9.0 0.8.0 +0.15 0.8 0.10 0.8 +0.15 0.7 0.9 0.8 0.14 0.6.1 0.9.0 0.13 0.2.0 0.6.0 =========== ============ ================ ============== diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index 15fb3fe48..98f4e3390 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -15,7 +15,7 @@ cargo test --verbose --features "$FEATURES" cargo test --manifest-path=ndarray-rand/Cargo.toml --no-default-features --verbose cargo test --manifest-path=ndarray-rand/Cargo.toml --features quickcheck --verbose cargo test --manifest-path=xtest-serialization/Cargo.toml --verbose -cargo test --manifest-path=xtest-blas/Cargo.toml --verbose +cargo test --manifest-path=xtest-blas/Cargo.toml --verbose --features openblas-system cargo test --examples CARGO_TARGET_DIR=target/ cargo test --manifest-path=xtest-numeric/Cargo.toml --verbose CARGO_TARGET_DIR=target/ cargo test --manifest-path=xtest-numeric/Cargo.toml --verbose --features test_blas diff --git a/xtest-blas/Cargo.toml b/xtest-blas/Cargo.toml index 9be50d3d0..463be6863 100644 --- a/xtest-blas/Cargo.toml +++ b/xtest-blas/Cargo.toml @@ -8,17 +8,24 @@ publish = false test = false [dev-dependencies] -ndarray = { path = "../", features = ["approx", "blas"] } - approx = "0.4" defmac = "0.2" num-traits = "0.2" -blas-src = { version = "0.7.0", features = ["openblas"] } -openblas-src = { version = "0.9.0", features = ["system"] } +[dependencies] +ndarray = { path = "../", features = ["approx", "blas"] } + +blas-src = { version = "0.8", optional = true } -#blas-src = { version = "0.7.0", features = ["netlib"] } -#netlib-src = { version = "0.8.0", default-features = false, features = ["cblas", "system"] } +openblas-src = { version = "0.10", optional = true } +netlib-src = { version = "0.8", optional = true } +blis-src = { version = "0.2", features = ["system"], optional = true } -#blas-src = { version = "0.7.0", features = ["blis"] } -#blis-src = { version = "0.2.0", features = ["system"] } +[features] +# Just for making an example and to help testing, , multiple different possible +# configurations are selectable here. +openblas-system = ["blas-src", "blas-src/openblas", "openblas-src/system"] +openblas-cache = ["blas-src", "blas-src/openblas", "openblas-src/cache"] +netlib = ["blas-src", "blas-src/netlib"] +netlib-system = ["blas-src", "blas-src/netlib", "netlib-src/system"] +blis-system = ["blas-src", "blas-src/blis", "blis-src/system"] diff --git a/xtest-blas/src/lib.rs b/xtest-blas/src/lib.rs index e69de29bb..857b91a3b 100644 --- a/xtest-blas/src/lib.rs +++ b/xtest-blas/src/lib.rs @@ -0,0 +1,6 @@ + +#[cfg(not(feature = "blas-src"))] +compile_error!("Missing backend: could not compile. + Help: For this testing crate, select one of the blas backend features, for example \ + openblas-system"); + diff --git a/xtest-numeric/Cargo.toml b/xtest-numeric/Cargo.toml index 8636fec41..14bbd16ee 100644 --- a/xtest-numeric/Cargo.toml +++ b/xtest-numeric/Cargo.toml @@ -10,8 +10,8 @@ ndarray = { path = "..", features = ["approx"] } ndarray-rand = { path = "../ndarray-rand/" } rand_distr = "0.4" -blas-src = { optional = true, version = "0.7.0", default-features = false, features = ["openblas"] } -openblas-src = { optional = true, version = "0.9", default-features = false, features = ["cblas", "system"] } +blas-src = { optional = true, version = "0.8", default-features = false, features = ["openblas"] } +openblas-src = { optional = true, version = "0.10", default-features = false, features = ["cblas", "system"] } [dependencies.rand] version = "0.8.0" From a141bc4c0130f4d74641e850bdba29b9a7890c12 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 28 Mar 2021 11:44:03 +0200 Subject: [PATCH 271/651] DOC: Add .std()/.var() feature to release note PR #790 was forgotten in release note (because it was integrated without a merge commit, I missed it). --- RELEASES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 767828254..aea25ad29 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -55,6 +55,11 @@ New features https://github.com/rust-ndarray/ndarray/pull/947 +- New methods `.std()` and `.var()` for standard deviation and variance by + [@kdubovikov] + + https://github.com/rust-ndarray/ndarray/pull/790 + Enhancements ------------ @@ -1269,6 +1274,7 @@ Earlier releases [@insideoutclub]: https://github.com/insideoutclub [@JP-Ellis]: https://github.com/JP-Ellis [@lifuyang]: https://github.com/liufuyang +[@kdubovikov]: https://github.com/kdubovikov [@max-sixty]: https://github.com/max-sixty [@mneumann]: https://github.com/mneumann [@mockersf]: https://github.com/mockersf From 37e4313f3d996bd3c1dc9fecf1f8ed42ae949a26 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 29 Mar 2021 21:37:48 +0200 Subject: [PATCH 272/651] 0.15.1 --- Cargo.toml | 2 +- README.rst | 6 +++--- RELEASES.md | 22 ++++++++++++++++++++-- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 61c26d4cd..b2c53ca29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ndarray" -version = "0.15.0" +version = "0.15.1" edition = "2018" authors = [ "bluss", diff --git a/README.rst b/README.rst index a2e189f58..503024886 100644 --- a/README.rst +++ b/README.rst @@ -117,15 +117,15 @@ end-user projects (not libraries) should select provider:: [dependencies] ndarray = { version = "0.15.0", features = ["blas"] } - blas-src = { version = "0.7.0", features = ["openblas"] } - openblas-src = { version = "0.9", features = ["cblas", "system"] } + blas-src = { version = "0.8", features = ["openblas"] } + openblas-src = { version = "0.10", features = ["cblas", "system"] } Using system-installed dependencies can save a long time building dependencies. An example configuration using (compiled) netlib is shown below anyway:: [dependencies] ndarray = { version = "0.15.0", features = ["blas"] } - blas-src = { version = "0.7.0", default-features = false, features = ["netlib"] } + blas-src = { version = "0.8.0", default-features = false, features = ["netlib"] } When this is done, your program must also link to ``blas_src`` by using it or explicitly including it in your code:: diff --git a/RELEASES.md b/RELEASES.md index aea25ad29..9236b72ad 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4,8 +4,26 @@ Version 0.15.1 (not released yet) Enhancements ------------ -- Arrays and views now implement PartialEq so that it's possible to compare - arrays with references to arrays and vice versa by [@bluss] +- Arrays and views now have additional PartialEq impls so that it's possible to + compare arrays with references to arrays and vice versa by [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/958 + +Bug fixes +--------- + +- Fix panic in creation of `.windows()` producer from negative stride array by + [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/957 + +Other changes +------------- + +- Update BLAS documentation further by @bluss + + https://github.com/rust-ndarray/ndarray/pull/955
+ https://github.com/rust-ndarray/ndarray/pull/959 Version 0.15.0 (2021-03-25) From bae4be677d789d4eb346e7bdae27b7bb1b49fd60 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 29 Mar 2021 23:16:14 +0200 Subject: [PATCH 273/651] DOC: Fix date on 0.15.1 --- RELEASES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 9236b72ad..be479c874 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,4 +1,4 @@ -Version 0.15.1 (not released yet) +Version 0.15.1 (2021-03-29) =========================== Enhancements From 16dccbc8ff5ba53a5dda7b6d12644a408772d4d0 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Tue, 30 Mar 2021 17:16:16 +0800 Subject: [PATCH 274/651] Preserve the allocation of the input array --- src/impl_ops.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/impl_ops.rs b/src/impl_ops.rs index d38cb566a..663dc7183 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -106,8 +106,14 @@ where out.zip_mut_with_same_shape(rhs, clone_iopf(A::$mth)); out } else { - let (lhs, rhs) = self.broadcast_with(rhs).unwrap(); - Zip::from(&lhs).and(&rhs).map_collect_owned(clone_opf(A::$mth)) + let (lhs_view, rhs_view) = self.broadcast_with(&rhs).unwrap(); + if lhs_view.shape() == self.shape() { + let mut out = self.into_dimensionality::<>::Output>().unwrap(); + out.zip_mut_with_same_shape(&rhs_view, clone_iopf(A::$mth)); + out + } else { + Zip::from(&lhs_view).and(&rhs_view).map_collect_owned(clone_opf(A::$mth)) + } } } } @@ -141,8 +147,14 @@ where out.zip_mut_with_same_shape(self, clone_iopf_rev(A::$mth)); out } else { - let (rhs, lhs) = rhs.broadcast_with(self).unwrap(); - Zip::from(&lhs).and(&rhs).map_collect_owned(clone_opf(A::$mth)) + let (rhs_view, lhs_view) = rhs.broadcast_with(self).unwrap(); + if rhs_view.shape() == rhs.shape() { + let mut out = rhs.into_dimensionality::<>::Output>().unwrap(); + out.zip_mut_with_same_shape(&lhs_view, clone_iopf_rev(A::$mth)); + out + } else { + Zip::from(&lhs_view).and(&rhs_view).map_collect_owned(clone_opf(A::$mth)) + } } } } From d810f1c3d70a2f409b3ee4a07ab101e8d2c15c46 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Thu, 1 Apr 2021 16:42:18 +0800 Subject: [PATCH 275/651] Improve performance of "no-broadcasting-needed" scenario in &array + &array operation --- src/impl_methods.rs | 28 ++++++++++++++++++++++++++-- src/impl_ops.rs | 8 +++++++- tests/array.rs | 6 ++++++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 4045f2b59..99df551a6 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1804,14 +1804,38 @@ where E: Dimension, { let shape = co_broadcast::>::Output>(&self.dim, &other.dim)?; - if let Some(view1) = self.broadcast(shape.clone()) { - if let Some(view2) = other.broadcast(shape) { + let view1 = if shape.slice() == self.dim.slice() { + self.to_dimensionality::<>::Output>() + } else { + self.broadcast(shape.clone()) + }; + let view2 = if shape.slice() == other.dim.slice() { + other.to_dimensionality::<>::Output>() + } else { + other.broadcast(shape) + }; + if let Some(view1) = view1 { + if let Some(view2) = view2 { return Ok((view1, view2)); } } Err(from_kind(ErrorKind::IncompatibleShape)) } + /// Creat an array view from an array with the same shape, but different dimensionality + /// type. Return None if the numbers of axes mismatch. + #[inline] + pub(crate) fn to_dimensionality(&self) -> Option> + where + D2: Dimension, + S: Data, + { + let dim = ::from_dimension(&self.dim)?; + let strides = ::from_dimension(&self.strides)?; + + unsafe { Some(ArrayView::new(self.ptr, dim, strides)) } + } + /// Swap axes `ax` and `bx`. /// /// This does not move any data, it just adjusts the array’s dimensions diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 663dc7183..0a72dc462 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -179,7 +179,13 @@ where { type Output = Array>::Output>; fn $mth(self, rhs: &'a ArrayBase) -> Self::Output { - let (lhs, rhs) = self.broadcast_with(rhs).unwrap(); + let (lhs, rhs) = if self.ndim() == rhs.ndim() && self.shape() == rhs.shape() { + let lhs = self.to_dimensionality::<>::Output>().unwrap(); + let rhs = rhs.to_dimensionality::<>::Output>().unwrap(); + (lhs, rhs) + } else { + self.broadcast_with(rhs).unwrap() + }; Zip::from(&lhs).and(&rhs).map_collect(clone_opf(A::$mth)) } } diff --git a/tests/array.rs b/tests/array.rs index 8e084e49e..9e45d161f 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1627,6 +1627,12 @@ fn arithmetic_broadcast() { sa2 + &sb2 + sc2.into_owned(), arr3(&[[[15, 19], [32, 36]], [[14, 18], [31, 35]]]) ); + + // Same shape + let a = s.slice(s![..;-1, ..;2, ..]); + let b = s.slice(s![.., ..;2, ..]); + assert_eq!(a.shape(), b.shape()); + assert_eq!(&a + &b, arr3(&[[[3, 7], [19, 23]], [[3, 7], [19, 23]]])); } #[test] From cc16c863419c83b47855b9a0a322ce7be878c1f9 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Fri, 2 Apr 2021 20:01:29 +0800 Subject: [PATCH 276/651] use view().into_dimensionality() instead --- benches/bench1.rs | 20 +++++++++++++++++++- src/impl_methods.rs | 33 +++++++++------------------------ src/impl_ops.rs | 4 ++-- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/benches/bench1.rs b/benches/bench1.rs index a6fa86deb..c7f18e3c4 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -11,7 +11,7 @@ extern crate test; use std::mem::MaybeUninit; -use ndarray::ShapeBuilder; +use ndarray::{ShapeBuilder, Array3, Array4}; use ndarray::{arr0, arr1, arr2, azip, s}; use ndarray::{Array, Array1, Array2, Axis, Ix, Zip}; use ndarray::{Ix1, Ix2, Ix3, Ix5, IxDyn}; @@ -998,3 +998,21 @@ fn into_dyn_dyn(bench: &mut test::Bencher) { let a = a.view(); bench.iter(|| a.clone().into_dyn()); } + +#[bench] +fn broadcast_same_dim(bench: &mut test::Bencher) { + let s = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + let s = Array4::from_shape_vec((2, 2, 3, 2), s.to_vec()).unwrap(); + let a = s.slice(s![.., ..;-1, ..;2, ..]); + let b = s.slice(s![.., .., ..;2, ..]); + bench.iter(|| &a + &b); +} + +#[bench] +fn broadcast_one_side(bench: &mut test::Bencher) { + let s = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + let s2 = [1 ,2 ,3 ,4 ,5 ,6]; + let a = Array4::from_shape_vec((4, 1, 3, 2), s.to_vec()).unwrap(); + let b = Array3::from_shape_vec((1, 3, 2), s2.to_vec()).unwrap(); + bench.iter(|| &a + &b); +} \ No newline at end of file diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 99df551a6..efea69c4c 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1805,35 +1805,20 @@ where { let shape = co_broadcast::>::Output>(&self.dim, &other.dim)?; let view1 = if shape.slice() == self.dim.slice() { - self.to_dimensionality::<>::Output>() + self.view().into_dimensionality::<>::Output>().unwrap() + } else if let Some(view1) = self.broadcast(shape.clone()) { + view1 } else { - self.broadcast(shape.clone()) + return Err(from_kind(ErrorKind::IncompatibleShape)) }; let view2 = if shape.slice() == other.dim.slice() { - other.to_dimensionality::<>::Output>() + other.view().into_dimensionality::<>::Output>().unwrap() + } else if let Some(view2) = other.broadcast(shape) { + view2 } else { - other.broadcast(shape) + return Err(from_kind(ErrorKind::IncompatibleShape)) }; - if let Some(view1) = view1 { - if let Some(view2) = view2 { - return Ok((view1, view2)); - } - } - Err(from_kind(ErrorKind::IncompatibleShape)) - } - - /// Creat an array view from an array with the same shape, but different dimensionality - /// type. Return None if the numbers of axes mismatch. - #[inline] - pub(crate) fn to_dimensionality(&self) -> Option> - where - D2: Dimension, - S: Data, - { - let dim = ::from_dimension(&self.dim)?; - let strides = ::from_dimension(&self.strides)?; - - unsafe { Some(ArrayView::new(self.ptr, dim, strides)) } + Ok((view1, view2)) } /// Swap axes `ax` and `bx`. diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 0a72dc462..42234a0b2 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -180,8 +180,8 @@ where type Output = Array>::Output>; fn $mth(self, rhs: &'a ArrayBase) -> Self::Output { let (lhs, rhs) = if self.ndim() == rhs.ndim() && self.shape() == rhs.shape() { - let lhs = self.to_dimensionality::<>::Output>().unwrap(); - let rhs = rhs.to_dimensionality::<>::Output>().unwrap(); + let lhs = self.view().into_dimensionality::<>::Output>().unwrap(); + let rhs = rhs.view().into_dimensionality::<>::Output>().unwrap(); (lhs, rhs) } else { self.broadcast_with(rhs).unwrap() From 0136cc3a447a585fd3703e3b3217c43809ecfbba Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Sat, 3 Apr 2021 02:26:12 +0800 Subject: [PATCH 277/651] consume the views --- src/impl_ops.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 42234a0b2..4c255dfff 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -186,7 +186,7 @@ where } else { self.broadcast_with(rhs).unwrap() }; - Zip::from(&lhs).and(&rhs).map_collect(clone_opf(A::$mth)) + Zip::from(lhs).and(rhs).map_collect(clone_opf(A::$mth)) } } From 8d3b845444458a32d5996e10144178a990a577c5 Mon Sep 17 00:00:00 2001 From: Stokhos Date: Sun, 4 Apr 2021 18:31:40 -0400 Subject: [PATCH 278/651] Fix a typo in README-quick-start.md (#968) --- README-quick-start.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README-quick-start.md b/README-quick-start.md index a6f07310d..981ef3e9c 100644 --- a/README-quick-start.md +++ b/README-quick-start.md @@ -100,7 +100,7 @@ fn main() { ``` ### Some common create helper functions -`linspace` - Create a 1-D array with 21 elements with values 0., …, 5. +`linspace` - Create a 1-D array with 11 elements with values 0., …, 5. ```rust use ndarray::prelude::*; use ndarray::{Array, Ix3}; From f75c84b20231567bfb717b59cd58fabf96d49180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Cassiers?= Date: Tue, 6 Apr 2021 15:52:57 +0200 Subject: [PATCH 279/651] Add link to the parallel module in top-level. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 35b64dce8..4dea225ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,7 +80,7 @@ //! - `serde` //! - Enables serialization support for serde 1.x //! - `rayon` -//! - Enables parallel iterators, parallelized methods and [`par_azip!`]. +//! - Enables parallel iterators, parallelized methods, the [`parallel`] module and [`par_azip!`]. //! - Implies std //! - `approx` //! - Enables implementations of traits from the [`approx`] crate. From fcb874123fa93987e85bdf78604336efc3fa7df1 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 6 Apr 2021 23:46:51 -0400 Subject: [PATCH 280/651] Fix indentation of list in docs (#974) --- src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4dea225ef..8fe999cc6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,12 +71,12 @@ //! `Cargo.toml`. //! //! - `std` -//! - Rust standard library (enabled by default) -//! - This crate can be used without the standard library by disabling the -//! default `std` feature. To do so, use `default-features = false` in -//! your `Cargo.toml`. -//! - The `geomspace` `linspace` `logspace` `range` `std` `var` `var_axis` -//! and `std_axis` methods are only available when `std` is enabled. +//! - Rust standard library (enabled by default) +//! - This crate can be used without the standard library by disabling the +//! default `std` feature. To do so, use `default-features = false` in +//! your `Cargo.toml`. +//! - The `geomspace` `linspace` `logspace` `range` `std` `var` `var_axis` +//! and `std_axis` methods are only available when `std` is enabled. //! - `serde` //! - Enables serialization support for serde 1.x //! - `rayon` From a72b71968e41e257d31cc6088aeb099ed3079ecf Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 6 Apr 2021 18:07:54 +0200 Subject: [PATCH 281/651] DOC: Update column_standardize, axis_ops, zip_may examples Update column_standardize to use new functionality in ndarray. Update the examples, clean them up a bit, add comments. --- examples/axis_ops.rs | 36 ++++++++++++++++++------- examples/column_standardize.rs | 26 ++++++------------ examples/sort-axis.rs | 4 +++ examples/zip_many.rs | 49 +++++++++++++++------------------- 4 files changed, 60 insertions(+), 55 deletions(-) diff --git a/examples/axis_ops.rs b/examples/axis_ops.rs index 624af32c3..98d2d3d7f 100644 --- a/examples/axis_ops.rs +++ b/examples/axis_ops.rs @@ -7,7 +7,16 @@ use ndarray::prelude::*; -fn regularize(a: &mut Array) -> Result<(), ()> +/// Reorder a's axes so that they are in "standard" axis order; +/// make sure axes are in positive stride direction, and merge adjacent +/// axes if possible. +/// +/// This changes the logical order of the elments in the +/// array, so that if we read them in row-major order after regularization, +/// it corresponds to their order in memory. +/// +/// Errors if array has a 0-stride axis +fn regularize(a: &mut Array) -> Result<(), &'static str> where D: Dimension, A: ::std::fmt::Debug, @@ -16,7 +25,9 @@ where // reverse all neg axes while let Some(ax) = a.axes().find(|ax| ax.stride <= 0) { if ax.stride == 0 { - return Err(()); + // no real reason to error on this case; other than + // stride == 0 is incompatible with an owned array. + return Err("Cannot regularize array with stride == 0 axis"); } // reverse ax println!("Reverse {:?}", ax.axis); @@ -27,8 +38,11 @@ where let mut i = 0; let n = a.ndim(); while let Some(ax) = a.axes().rev().skip(i).min_by_key(|ax| ax.stride.abs()) { - a.swap_axes(n - 1 - i, ax.axis.index()); - println!("Swap {:?} <=> {}", ax.axis, n - 1 - i); + let cur_axis = Axis(n - 1 - i); + if ax.axis != cur_axis { + a.swap_axes(cur_axis.index(), ax.axis.index()); + println!("Swap {:?} <=> {:?}", cur_axis, ax.axis); + } i += 1; } @@ -40,7 +54,7 @@ where break; } } - println!("{:?}", a); + println!("Result:\n{:?}\n", a); Ok(()) } @@ -52,22 +66,24 @@ fn main() { a.swap_axes(0, 1); a.swap_axes(0, 2); a.slice_collapse(s![.., ..;-1, ..]); - regularize(&mut a).ok(); + regularize(&mut a).unwrap(); let mut b = Array::::zeros((2, 3, 4)); for (i, elt) in (0..).zip(&mut b) { *elt = i; } - regularize(&mut b).ok(); + regularize(&mut b).unwrap(); + let mut b = b.into_shape(a.len()).unwrap(); - regularize(&mut b).ok(); + regularize(&mut b).unwrap(); + b.invert_axis(Axis(0)); - regularize(&mut b).ok(); + regularize(&mut b).unwrap(); let mut a = Array::::zeros((2, 3, 4)); for (i, elt) in (0..).zip(&mut a) { *elt = i; } a.slice_collapse(s![..;-1, ..;2, ..]); - regularize(&mut a).ok(); + regularize(&mut a).unwrap(); } diff --git a/examples/column_standardize.rs b/examples/column_standardize.rs index c360170bd..f9027e152 100644 --- a/examples/column_standardize.rs +++ b/examples/column_standardize.rs @@ -1,31 +1,21 @@ use ndarray::prelude::*; -fn std1d(a: ArrayView1<'_, f64>) -> f64 { - let n = a.len() as f64; - if n == 0. { - return 0.; - } - let mean = a.sum() / n; - (a.fold(0., |acc, &x| acc + (x - mean).powi(2)) / n).sqrt() -} - -fn std(a: &Array2, axis: Axis) -> Array1 { - a.map_axis(axis, std1d) -} - fn main() { - // "recreating the following" + // This example recreates the following from python/numpy // counts -= np.mean(counts, axis=0) // counts /= np.std(counts, axis=0) let mut data = array![[-1., -2., -3.], [1., -3., 5.], [2., 2., 2.]]; println!("{:8.4}", data); - println!("{:8.4} (Mean axis=0)", data.mean_axis(Axis(0)).unwrap()); + println!("Mean along axis=0 (along columns):\n{:8.4}", data.mean_axis(Axis(0)).unwrap()); data -= &data.mean_axis(Axis(0)).unwrap(); - println!("{:8.4}", data); + println!("Centered around mean:\n{:8.4}", data); - data /= &std(&data, Axis(0)); - println!("{:8.4}", data); + data /= &data.std_axis(Axis(0), 0.); + println!("Scaled to normalize std:\n{:8.4}", data); + + println!("New mean:\n{:8.4}", data.mean_axis(Axis(0)).unwrap()); + println!("New std: \n{:8.4}", data.std_axis(Axis(0), 0.)); } diff --git a/examples/sort-axis.rs b/examples/sort-axis.rs index d721571b2..ee5ff3465 100644 --- a/examples/sort-axis.rs +++ b/examples/sort-axis.rs @@ -1,3 +1,7 @@ +//! This is an example of sorting arrays along an axis. +//! This file may not be so instructive except for advanced users, instead it +//! could be a "feature preview" before sorting is added to the main crate. +//! use ndarray::prelude::*; use ndarray::{Data, RemoveAxis, Zip}; diff --git a/examples/zip_many.rs b/examples/zip_many.rs index 40b855df2..e2c5169f2 100644 --- a/examples/zip_many.rs +++ b/examples/zip_many.rs @@ -9,45 +9,40 @@ use ndarray::prelude::*; use ndarray::Zip; fn main() { - let n = 16; + let n = 6; + let mut a = Array::::zeros((n, n)); - let mut b = Array::::from_elem((n, n), 1.); + let mut b = Array::::zeros((n, n)); for ((i, j), elt) in b.indexed_iter_mut() { - *elt /= 1. + (i + 2 * j) as f32; + *elt = 1. / (1. + (i + 2 * j) as f32); } let c = Array::::from_elem((n, n + 1), 1.7); let c = c.slice(s![.., ..-1]); - { - let a = a.view_mut().reversed_axes(); - azip!((a in a, &b in b.t()) *a = b); - } - assert_eq!(a, b); + // Using Zip for arithmetic ops across a, b, c + Zip::from(&mut a).and(&b).and(&c) + .for_each(|a, &b, &c| *a = b + c); + assert_eq!(a, &b + &c); + // and this is how to do the *same thing* with azip!() azip!((a in &mut a, &b in &b, &c in c) *a = b + c); - assert_eq!(a, &b + &c); + + println!("{:8.4}", a); // sum of each row - let ax = Axis(0); - let mut sums = Array::zeros(a.len_of(ax)); - azip!((s in &mut sums, a in a.axis_iter(ax)) *s = a.sum()); + let mut sums = Array::zeros(a.nrows()); + Zip::from(a.rows()).and(&mut sums) + .for_each(|row, sum| *sum = row.sum()); + // show sums as a column matrix + println!("{:8.4}", sums.insert_axis(Axis(1))); - // sum of each chunk + // sum of each 2x2 chunk let chunk_sz = (2, 2); let nchunks = (n / chunk_sz.0, n / chunk_sz.1); let mut sums = Array::zeros(nchunks); - azip!((s in &mut sums, a in a.exact_chunks(chunk_sz)) *s = a.sum()); - - // Let's imagine we split to parallelize - { - let (x, y) = Zip::indexed(&mut a).split(); - x.for_each(|(_, j), elt| { - *elt = elt.powi(j as i32); - }); - - y.for_each(|(_, j), elt| { - *elt = elt.powi(j as i32); - }); - } - println!("{:8.3?}", a); + + Zip::from(a.exact_chunks(chunk_sz)) + .and(&mut sums) + .for_each(|chunk, sum| *sum = chunk.sum()); + println!("{:8.4}", sums); } From 1030fc2749040d13daa5a16ecbac710d1c2ec31b Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 7 Apr 2021 23:39:50 +0200 Subject: [PATCH 282/651] DOC: Fix ci for column_standardize --- examples/column_standardize.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/column_standardize.rs b/examples/column_standardize.rs index f9027e152..4bdc06f69 100644 --- a/examples/column_standardize.rs +++ b/examples/column_standardize.rs @@ -1,5 +1,6 @@ use ndarray::prelude::*; +#[cfg(feature = "std")] fn main() { // This example recreates the following from python/numpy // counts -= np.mean(counts, axis=0) @@ -19,3 +20,6 @@ fn main() { println!("New mean:\n{:8.4}", data.mean_axis(Axis(0)).unwrap()); println!("New std: \n{:8.4}", data.std_axis(Axis(0), 0.)); } + +#[cfg(not(feature = "std"))] +fn main() {} From 05264678e7b0213a9820427a114e4ad5136d1952 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 7 Apr 2021 23:39:50 +0200 Subject: [PATCH 283/651] DOC: Fix ci for column_standardize (again) --- examples/column_standardize.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/column_standardize.rs b/examples/column_standardize.rs index 4bdc06f69..6a1840f03 100644 --- a/examples/column_standardize.rs +++ b/examples/column_standardize.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "std")] use ndarray::prelude::*; #[cfg(feature = "std")] From 5a25defa3579e74947c3ffed3f7cba5d5fa59049 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 8 Apr 2021 22:14:20 +0200 Subject: [PATCH 284/651] TEST: Add tests for column/row matrix layouts These tests - w.r.t C/F preference, failed when they were introduced. --- src/layout/mod.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 625fc7ff2..d4271dd77 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -170,6 +170,34 @@ mod tests { } } + #[test] + fn no_layouts() { + let a = M::zeros((5, 5)); + let b = M::zeros((5, 5).f()); + + // 2D row/column matrixes + let arow = a.slice(s![0..1, ..]); + let acol = a.slice(s![.., 0..1]); + let brow = b.slice(s![0..1, ..]); + let bcol = b.slice(s![.., 0..1]); + assert_layouts!(arow, CORDER, FORDER); + assert_not_layouts!(acol, CORDER, CPREFER, FORDER, FPREFER); + assert_layouts!(bcol, CORDER, FORDER); + assert_not_layouts!(brow, CORDER, CPREFER, FORDER, FPREFER); + + // 2D row/column matrixes - now made with insert axis + for &axis in &[Axis(0), Axis(1)] { + let arow = a.slice(s![0, ..]).insert_axis(axis); + let acol = a.slice(s![.., 0]).insert_axis(axis); + let brow = b.slice(s![0, ..]).insert_axis(axis); + let bcol = b.slice(s![.., 0]).insert_axis(axis); + assert_layouts!(arow, CORDER, FORDER); + assert_not_layouts!(acol, CORDER, CPREFER, FORDER, FPREFER); + assert_layouts!(bcol, CORDER, FORDER); + assert_not_layouts!(brow, CORDER, CPREFER, FORDER, FPREFER); + } + } + #[test] fn skip_layouts() { let a = M::zeros((5, 5)); From ef51f56f27d2b39d4ebf2cff0e947e011d4f672d Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 8 Apr 2021 21:53:00 +0200 Subject: [PATCH 285/651] FIX: For stride == 1 axes, require len > 1 to determine C/F preference The issue was that otherwise, this array: shape: [1, 256] strides: [1, 256] Which was "correctly" determined to be F-preference because it's sliced out of an f-order array. In reality it is discontiguous; every element is 256 steps from the next. This should have layout None, the same as this array would have (ignoring 1-len axes). shape: [256] strides: [256] --- src/zip/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 38dbeb579..5f9b15c5a 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -69,9 +69,9 @@ where } else if n > 1 && self.raw_view().reversed_axes().is_standard_layout() { Layout::f() } else if n > 1 { - if self.stride_of(Axis(0)) == 1 { + if self.len_of(Axis(0)) > 1 && self.stride_of(Axis(0)) == 1 { Layout::fpref() - } else if self.stride_of(Axis(n - 1)) == 1 { + } else if self.len_of(Axis(n - 1)) > 1 && self.stride_of(Axis(n - 1)) == 1 { Layout::cpref() } else { Layout::none() From 653f91267ef20df793d41c34cb4507d07c4b68eb Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 9 Apr 2021 19:01:41 +0200 Subject: [PATCH 286/651] DOC: Doc fields of AxisDescription --- src/dimension/axes.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dimension/axes.rs b/src/dimension/axes.rs index d6f1c7280..fb1ff0995 100644 --- a/src/dimension/axes.rs +++ b/src/dimension/axes.rs @@ -48,8 +48,11 @@ pub struct Axes<'a, D> { /// Description of the axis, its length and its stride. #[derive(Debug)] pub struct AxisDescription { + /// Axis identifier (index) pub axis: Axis, + /// Length in count of elements of the current axis pub len: usize, + /// Stride in count of elements of the current axis pub stride: isize, } From 113f56cf7e36200e984270ce1b4fb7f577f7ad7e Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 9 Apr 2021 19:01:41 +0200 Subject: [PATCH 287/651] DOC: Doc fields for Slice and SliceInfoElem --- src/slice.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/slice.rs b/src/slice.rs index 3c554a5ca..3cbdf2ee9 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -16,7 +16,7 @@ use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, Rang /// A slice (range with step size). /// -/// `end` is an exclusive index. Negative `begin` or `end` indexes are counted +/// `end` is an exclusive index. Negative `start` or `end` indexes are counted /// from the back of the axis. If `end` is `None`, the slice extends to the end /// of the axis. /// @@ -36,8 +36,12 @@ use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, Rang /// The Python equivalent is `[a::-1]`. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct Slice { + /// start index; negative are counted from the back of the axis pub start: isize, + /// end index; negative are counted from the back of the axis; when not present + /// the default is the full length of the axis. pub end: Option, + /// step size in elements; the default is 1, for every element. pub step: isize, } @@ -105,12 +109,16 @@ pub struct NewAxis; /// `[np.newaxis]`. The macro equivalent is `s![NewAxis]`. #[derive(Debug, PartialEq, Eq, Hash)] pub enum SliceInfoElem { - /// A range with step size. `end` is an exclusive index. Negative `begin` + /// A range with step size. `end` is an exclusive index. Negative `start` /// or `end` indexes are counted from the back of the axis. If `end` is /// `None`, the slice extends to the end of the axis. Slice { + /// start index; negative are counted from the back of the axis start: isize, + /// end index; negative are counted from the back of the axis; when not present + /// the default is the full length of the axis. end: Option, + /// step size in elements; the default is 1, for every element. step: isize, }, /// A single index. From 37cf9197f42a53f2407278e6ec04fbf799f4f762 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 9 Apr 2021 19:28:49 +0200 Subject: [PATCH 288/651] FIX: Remove internal .inner_rows() which is the same as .rows() --- src/arraytraits.rs | 2 +- src/lib.rs | 8 +------- src/numeric/impl_numeric.rs | 4 ++-- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 23f99b8fb..0d273ec31 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -252,7 +252,7 @@ where if let Some(self_s) = self.as_slice() { hash::Hash::hash_slice(self_s, state); } else { - for row in self.inner_rows() { + for row in self.rows() { if let Some(row_s) = row.as_slice() { hash::Hash::hash_slice(row_s, state); } else { diff --git a/src/lib.rs b/src/lib.rs index 8fe999cc6..516368367 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -147,7 +147,7 @@ pub use crate::slice::{ }; use crate::iterators::Baseiter; -use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut, Lanes}; +use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut}; pub use crate::arraytraits::AsArray; #[cfg(feature = "std")] @@ -1588,12 +1588,6 @@ where self.with_strides_dim(s, d) } } - - /// n-d generalization of rows, just like inner iter - fn inner_rows(&self) -> iterators::Lanes<'_, A, D::Smaller> { - let n = self.ndim(); - Lanes::new(self.view(), Axis(n.saturating_sub(1))) - } } // parallel methods diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index fbec80418..da170c9e7 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -38,7 +38,7 @@ where return numeric_util::unrolled_fold(slc, A::zero, A::add); } let mut sum = A::zero(); - for row in self.inner_rows() { + for row in self.rows() { if let Some(slc) = row.as_slice() { sum = sum + numeric_util::unrolled_fold(slc, A::zero, A::add); } else { @@ -103,7 +103,7 @@ where return numeric_util::unrolled_fold(slc, A::one, A::mul); } let mut sum = A::one(); - for row in self.inner_rows() { + for row in self.rows() { if let Some(slc) = row.as_slice() { sum = sum * numeric_util::unrolled_fold(slc, A::one, A::mul); } else { From 2ec6bba645b1410f0a2f7170f51cdefb8e3a778f Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 4 Apr 2021 03:24:17 +0200 Subject: [PATCH 289/651] FEAT: Add method .shift_remove_index --- src/impl_methods.rs | 25 +++++++++++++++++++++++++ tests/array.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index efea69c4c..5274f86a4 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2444,6 +2444,31 @@ where } } + /// Remove the `index`th elements along `axis` and shift down elements from higher indexes. + /// + /// Decreases the length of `axis` by one. + /// + /// ***Panics** if `axis` or `index` is out of bounds. + pub fn shift_remove_index(&mut self, axis: Axis, index: usize) + where + S: DataOwned + DataMut, + { + let (_, mut tail) = self.view_mut().split_at(axis, index); + // shift elements to the back + // use swap to keep all elements initialized (as required by owned storage) + Zip::from(tail.lanes_mut(axis)).for_each(|mut lane| { + let mut lane_iter = lane.iter_mut(); + let mut dst = if let Some(dst) = lane_iter.next() { dst } else { return }; + + for elt in lane_iter { + std::mem::swap(dst, elt); + dst = elt; + } + }); + // then slice the axis in place to cut out the removed final element + self.slice_axis_inplace(axis, Slice::new(0, Some(-1), 1)); + } + /// Iterates over pairs of consecutive elements along the axis. /// /// The first argument to the closure is an element, and the second diff --git a/tests/array.rs b/tests/array.rs index 9e45d161f..108e3b1ab 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -2397,3 +2397,45 @@ mod array_cow_tests { }); } } + +#[test] +fn test_shift_remove() { + let mut a = arr2(&[[1, 2, 3], + [4, 5, 6], + [7, 8, 9], + [10,11,12]]); + a.shift_remove_index(Axis(0), 1); + a.shift_remove_index(Axis(1), 2); + assert_eq!(a.shape(), &[3, 2]); + assert_eq!(a, + array![[1, 2], + [7, 8], + [10,11]]); + + let mut a = arr2(&[[1, 2, 3], + [4, 5, 6], + [7, 8, 9], + [10,11,12]]); + a.invert_axis(Axis(0)); + a.shift_remove_index(Axis(0), 1); + a.shift_remove_index(Axis(1), 2); + assert_eq!(a.shape(), &[3, 2]); + assert_eq!(a, + array![[10,11], + [4, 5], + [1, 2]]); + + a.shift_remove_index(Axis(1), 1); + + assert_eq!(a.shape(), &[3, 1]); + assert_eq!(a, + array![[10], + [4], + [1]]); + a.shift_remove_index(Axis(1), 0); + assert_eq!(a.shape(), &[3, 0]); + assert_eq!(a, + array![[], + [], + []]); +} From 6e710b79956070a047e1c90a24b983d06e9c3c53 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 4 Apr 2021 03:35:10 +0200 Subject: [PATCH 290/651] FEAT: Optimize shift_remove_index using MaybeUninit --- src/impl_methods.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 5274f86a4..5fb7b9c98 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -6,7 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::mem::{size_of, ManuallyDrop}; +use std::mem::{size_of, ManuallyDrop, MaybeUninit}; use alloc::slice; use alloc::vec; use alloc::vec::Vec; @@ -2460,9 +2460,22 @@ where let mut lane_iter = lane.iter_mut(); let mut dst = if let Some(dst) = lane_iter.next() { dst } else { return }; - for elt in lane_iter { - std::mem::swap(dst, elt); - dst = elt; + // Logically we do a circular swap here, all elements in a chain + // Using MaybeUninit to avoid unecessary writes in the safe swap solution + // + // for elt in lane_iter { + // std::mem::swap(dst, elt); + // dst = elt; + // } + // + let mut slot = MaybeUninit::
::uninit(); + unsafe { + slot.as_mut_ptr().copy_from_nonoverlapping(dst, 1); + for elt in lane_iter { + (dst as *mut A).copy_from_nonoverlapping(elt, 1); + dst = elt; + } + (dst as *mut A).copy_from_nonoverlapping(slot.as_ptr(), 1); } }); // then slice the axis in place to cut out the removed final element From c568124d4da852960db7a49cf57699cc91ed1b2e Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 5 Apr 2021 14:52:38 +0200 Subject: [PATCH 291/651] API: Name and comment changes for remove_index Co-authored-by: Jim Turner --- src/impl_methods.rs | 6 +++--- tests/array.rs | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 5fb7b9c98..8d4c8435c 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2449,13 +2449,13 @@ where /// Decreases the length of `axis` by one. /// /// ***Panics** if `axis` or `index` is out of bounds. - pub fn shift_remove_index(&mut self, axis: Axis, index: usize) + pub fn remove_index(&mut self, axis: Axis, index: usize) where S: DataOwned + DataMut, { let (_, mut tail) = self.view_mut().split_at(axis, index); - // shift elements to the back - // use swap to keep all elements initialized (as required by owned storage) + // shift elements to the front + // use swapping to keep all elements initialized (as required by owned storage) Zip::from(tail.lanes_mut(axis)).for_each(|mut lane| { let mut lane_iter = lane.iter_mut(); let mut dst = if let Some(dst) = lane_iter.next() { dst } else { return }; diff --git a/tests/array.rs b/tests/array.rs index 108e3b1ab..8bbc6205c 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -2399,13 +2399,13 @@ mod array_cow_tests { } #[test] -fn test_shift_remove() { +fn test_remove_index() { let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10,11,12]]); - a.shift_remove_index(Axis(0), 1); - a.shift_remove_index(Axis(1), 2); + a.remove_index(Axis(0), 1); + a.remove_index(Axis(1), 2); assert_eq!(a.shape(), &[3, 2]); assert_eq!(a, array![[1, 2], @@ -2417,22 +2417,22 @@ fn test_shift_remove() { [7, 8, 9], [10,11,12]]); a.invert_axis(Axis(0)); - a.shift_remove_index(Axis(0), 1); - a.shift_remove_index(Axis(1), 2); + a.remove_index(Axis(0), 1); + a.remove_index(Axis(1), 2); assert_eq!(a.shape(), &[3, 2]); assert_eq!(a, array![[10,11], [4, 5], [1, 2]]); - a.shift_remove_index(Axis(1), 1); + a.remove_index(Axis(1), 1); assert_eq!(a.shape(), &[3, 1]); assert_eq!(a, array![[10], [4], [1]]); - a.shift_remove_index(Axis(1), 0); + a.remove_index(Axis(1), 0); assert_eq!(a.shape(), &[3, 0]); assert_eq!(a, array![[], From 107f51f2cee9de50b086bef521896df624d0d2a8 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 5 Apr 2021 15:02:45 +0200 Subject: [PATCH 292/651] FIX: in remove_axis(axis, index) specify we need index < len_of(axis) Removing the index-past-the end does not make sense (it's an ok split index, so the previous code passed through without action). --- src/impl_methods.rs | 10 +++++++++- tests/array.rs | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 8d4c8435c..f4769a13c 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2446,13 +2446,21 @@ where /// Remove the `index`th elements along `axis` and shift down elements from higher indexes. /// + /// Note that this "removes" the elements by swapping them around to the end of the axis and + /// shortening the length of the axis; the elements are not deinitialized or dropped by this, + /// just moved out of view (this only matters for elements with ownership semantics). It's + /// similar to slicing an owned array in place. + /// /// Decreases the length of `axis` by one. /// - /// ***Panics** if `axis` or `index` is out of bounds. + /// ***Panics*** if `axis` is out of bounds
+ /// ***Panics*** if not `index < self.len_of(axis)`. pub fn remove_index(&mut self, axis: Axis, index: usize) where S: DataOwned + DataMut, { + assert!(index < self.len_of(axis), "index {} must be less than length of Axis({})", + index, axis.index()); let (_, mut tail) = self.view_mut().split_at(axis, index); // shift elements to the front // use swapping to keep all elements initialized (as required by owned storage) diff --git a/tests/array.rs b/tests/array.rs index 8bbc6205c..38d2711aa 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -2439,3 +2439,37 @@ fn test_remove_index() { [], []]); } + +#[should_panic(expected="must be less")] +#[test] +fn test_remove_index_oob1() { + let mut a = arr2(&[[1, 2, 3], + [4, 5, 6], + [7, 8, 9], + [10,11,12]]); + a.remove_index(Axis(0), 4); +} + +#[should_panic(expected="must be less")] +#[test] +fn test_remove_index_oob2() { + let mut a = array![[10], [4], [1]]; + a.remove_index(Axis(1), 0); + assert_eq!(a.shape(), &[3, 0]); + assert_eq!(a, + array![[], + [], + []]); + a.remove_index(Axis(0), 1); // ok + assert_eq!(a, + array![[], + []]); + a.remove_index(Axis(1), 0); // oob +} + +#[should_panic(expected="index out of bounds")] +#[test] +fn test_remove_index_oob3() { + let mut a = array![[10], [4], [1]]; + a.remove_index(Axis(2), 0); +} From 0d5ea8435bf598cd50b8ed56dc2d453bb31d4f75 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 5 Apr 2021 16:48:15 +0200 Subject: [PATCH 293/651] FIX: Add AbortIfPanic guard to remove_index It's a critical section where we can't panic. Mark the reason in the guard and message. --- src/impl_methods.rs | 3 +++ src/lib.rs | 1 + src/low_level_util.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 src/low_level_util.rs diff --git a/src/impl_methods.rs b/src/impl_methods.rs index f4769a13c..51f638d3f 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -26,6 +26,7 @@ use crate::dimension::broadcast::co_broadcast; use crate::error::{self, ErrorKind, ShapeError, from_kind}; use crate::math_cell::MathCell; use crate::itertools::zip; +use crate::low_level_util::AbortIfPanic; use crate::zip::{IntoNdProducer, Zip}; use crate::AxisDescription; @@ -2476,6 +2477,7 @@ where // dst = elt; // } // + let guard = AbortIfPanic(&"remove_index: temporarily moving out of owned value"); let mut slot = MaybeUninit::
::uninit(); unsafe { slot.as_mut_ptr().copy_from_nonoverlapping(dst, 1); @@ -2485,6 +2487,7 @@ where } (dst as *mut A).copy_from_nonoverlapping(slot.as_ptr(), 1); } + guard.defuse(); }); // then slice the axis in place to cut out the removed final element self.slice_axis_inplace(axis, Slice::new(0, Some(-1), 1)); diff --git a/src/lib.rs b/src/lib.rs index 35b64dce8..762a55637 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -205,6 +205,7 @@ mod shape_builder; mod slice; mod split_at; mod stacking; +mod low_level_util; #[macro_use] mod zip; diff --git a/src/low_level_util.rs b/src/low_level_util.rs new file mode 100644 index 000000000..b61b06f0d --- /dev/null +++ b/src/low_level_util.rs @@ -0,0 +1,40 @@ +// Copyright 2021 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +/// Guard value that will abort if it is dropped. +/// To defuse, this value must be forgotten before the end of the scope. +/// +/// The string value is added to the message printed if aborting. +#[must_use] +pub(crate) struct AbortIfPanic(pub(crate) &'static &'static str); + +impl AbortIfPanic { + /// Defuse the AbortIfPanic guard. This *must* be done when finished. + #[inline] + pub(crate) fn defuse(self) { + std::mem::forget(self); + } +} + +impl Drop for AbortIfPanic { + // The compiler should be able to remove this, if it can see through that there + // is no panic in the code section. + fn drop(&mut self) { + #[cfg(feature="std")] + { + eprintln!("ndarray: panic in no-panic section, aborting: {}", self.0); + std::process::abort() + } + #[cfg(not(feature="std"))] + { + // no-std uses panic-in-panic (should abort) + panic!("ndarray: panic in no-panic section, bailing out: {}", self.0); + } + } +} From b90f1f96ebe6d7fccc01d95e6a5614550beeacaf Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 9 Apr 2021 18:29:54 +0200 Subject: [PATCH 294/651] FIX: Factor out rotate1_front function from remove_index --- src/impl_1d.rs | 34 ++++++++++++++++++++++++++++++++++ src/impl_methods.rs | 29 ++--------------------------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/impl_1d.rs b/src/impl_1d.rs index 704d8643d..74eaaad21 100644 --- a/src/impl_1d.rs +++ b/src/impl_1d.rs @@ -8,7 +8,10 @@ //! Methods for one-dimensional arrays. use alloc::vec::Vec; +use std::mem::MaybeUninit; + use crate::imp_prelude::*; +use crate::low_level_util::AbortIfPanic; /// # Methods For 1-D Arrays impl ArrayBase @@ -27,4 +30,35 @@ where crate::iterators::to_vec(self.iter().cloned()) } } + + /// Rotate the elements of the array by 1 element towards the front; + /// the former first element becomes the last. + pub(crate) fn rotate1_front(&mut self) + where + S: DataMut, + { + // use swapping to keep all elements initialized (as required by owned storage) + let mut lane_iter = self.iter_mut(); + let mut dst = if let Some(dst) = lane_iter.next() { dst } else { return }; + + // Logically we do a circular swap here, all elements in a chain + // Using MaybeUninit to avoid unecessary writes in the safe swap solution + // + // for elt in lane_iter { + // std::mem::swap(dst, elt); + // dst = elt; + // } + // + let guard = AbortIfPanic(&"rotate1_front: temporarily moving out of owned value"); + let mut slot = MaybeUninit::::uninit(); + unsafe { + slot.as_mut_ptr().copy_from_nonoverlapping(dst, 1); + for elt in lane_iter { + (dst as *mut A).copy_from_nonoverlapping(elt, 1); + dst = elt; + } + (dst as *mut A).copy_from_nonoverlapping(slot.as_ptr(), 1); + } + guard.defuse(); + } } diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 51f638d3f..03ca09d74 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -6,7 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::mem::{size_of, ManuallyDrop, MaybeUninit}; +use std::mem::{size_of, ManuallyDrop}; use alloc::slice; use alloc::vec; use alloc::vec::Vec; @@ -26,7 +26,6 @@ use crate::dimension::broadcast::co_broadcast; use crate::error::{self, ErrorKind, ShapeError, from_kind}; use crate::math_cell::MathCell; use crate::itertools::zip; -use crate::low_level_util::AbortIfPanic; use crate::zip::{IntoNdProducer, Zip}; use crate::AxisDescription; @@ -2464,31 +2463,7 @@ where index, axis.index()); let (_, mut tail) = self.view_mut().split_at(axis, index); // shift elements to the front - // use swapping to keep all elements initialized (as required by owned storage) - Zip::from(tail.lanes_mut(axis)).for_each(|mut lane| { - let mut lane_iter = lane.iter_mut(); - let mut dst = if let Some(dst) = lane_iter.next() { dst } else { return }; - - // Logically we do a circular swap here, all elements in a chain - // Using MaybeUninit to avoid unecessary writes in the safe swap solution - // - // for elt in lane_iter { - // std::mem::swap(dst, elt); - // dst = elt; - // } - // - let guard = AbortIfPanic(&"remove_index: temporarily moving out of owned value"); - let mut slot = MaybeUninit::::uninit(); - unsafe { - slot.as_mut_ptr().copy_from_nonoverlapping(dst, 1); - for elt in lane_iter { - (dst as *mut A).copy_from_nonoverlapping(elt, 1); - dst = elt; - } - (dst as *mut A).copy_from_nonoverlapping(slot.as_ptr(), 1); - } - guard.defuse(); - }); + Zip::from(tail.lanes_mut(axis)).for_each(|mut lane| lane.rotate1_front()); // then slice the axis in place to cut out the removed final element self.slice_axis_inplace(axis, Slice::new(0, Some(-1), 1)); } From bed9c63337fa8bf7b6116e58d2c6892ff59a9c75 Mon Sep 17 00:00:00 2001 From: Stokhos Date: Fri, 9 Apr 2021 22:53:59 -0400 Subject: [PATCH 295/651] add getter for dim in shape minor fix changed method name --- src/shape_builder.rs | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/shape_builder.rs b/src/shape_builder.rs index 6fc99d0b2..dcfddc1b9 100644 --- a/src/shape_builder.rs +++ b/src/shape_builder.rs @@ -13,7 +13,7 @@ pub struct Shape { } #[derive(Copy, Clone, Debug)] -pub(crate) enum Contiguous { } +pub(crate) enum Contiguous {} impl Shape { pub(crate) fn is_c(&self) -> bool { @@ -21,7 +21,6 @@ impl Shape { } } - /// An array shape of n dimensions in c-order, f-order or custom strides. #[derive(Copy, Clone, Debug)] pub struct StrideShape { @@ -29,6 +28,20 @@ pub struct StrideShape { pub(crate) strides: Strides, } +impl StrideShape +where + D: Dimension, +{ + /// Return a reference to the dimension + pub fn raw_dim(&self) -> &D { + &self.dim + } + /// Return the size of the shape in number of elements + pub fn size(&self) -> usize { + self.dim.size() + } +} + /// Stride description #[derive(Copy, Clone, Debug)] pub(crate) enum Strides { @@ -37,21 +50,26 @@ pub(crate) enum Strides { /// Column-major ("F"-order) F, /// Custom strides - Custom(D) + Custom(D), } impl Strides { /// Return strides for `dim` (computed from dimension if c/f, else return the custom stride) pub(crate) fn strides_for_dim(self, dim: &D) -> D - where D: Dimension + where + D: Dimension, { match self { Strides::C => dim.default_strides(), Strides::F => dim.fortran_strides(), Strides::Custom(c) => { - debug_assert_eq!(c.ndim(), dim.ndim(), + debug_assert_eq!( + c.ndim(), + dim.ndim(), "Custom strides given with {} dimensions, expected {}", - c.ndim(), dim.ndim()); + c.ndim(), + dim.ndim() + ); c } } @@ -94,11 +112,7 @@ where { fn from(value: T) -> Self { let shape = value.into_shape(); - let st = if shape.is_c() { - Strides::C - } else { - Strides::F - }; + let st = if shape.is_c() { Strides::C } else { Strides::F }; StrideShape { strides: st, dim: shape.dim, @@ -161,8 +175,10 @@ impl Shape where D: Dimension, { - // Return a reference to the dimension - //pub fn dimension(&self) -> &D { &self.dim } + /// Return a reference to the dimension + pub fn raw_dim(&self) -> &D { + &self.dim + } /// Return the size of the shape in number of elements pub fn size(&self) -> usize { self.dim.size() From 115b2af24e16eec43cfe13393e85f998a271582c Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 3 Apr 2021 22:01:02 +0200 Subject: [PATCH 296/651] zip: In Zip, move dimension check to free function This reduces the generic parameters the function depends on, so that it is instantiated less often. --- src/zip/mod.rs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 5f9b15c5a..34bfe523f 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -240,22 +240,25 @@ where } } -impl Zip +#[inline] +fn zip_dimension_check(dimension: &D, part: &P) where D: Dimension, + P: NdProducer, { - fn check

(&self, part: &P) - where - P: NdProducer, - { - ndassert!( - part.equal_dim(&self.dimension), - "Zip: Producer dimension mismatch, expected: {:?}, got: {:?}", - self.dimension, - part.raw_dim() - ); - } + ndassert!( + part.equal_dim(&dimension), + "Zip: Producer dimension mismatch, expected: {:?}, got: {:?}", + dimension, + part.raw_dim() + ); +} + +impl Zip +where + D: Dimension, +{ /// Return a the number of element tuples in the Zip pub fn size(&self) -> usize { self.dimension.size() @@ -652,7 +655,7 @@ macro_rules! map_impl { where P: IntoNdProducer, { let part = p.into_producer(); - self.check(&part); + zip_dimension_check(&self.dimension, &part); self.build_and(part) } From 13dab58ee564e067569cc73b1103dc21fe479511 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 5 Mar 2021 20:56:58 +0100 Subject: [PATCH 297/651] append: Add try_append_row and try_append_column For Array2 specifically, and only when the memory layout lines up for efficient and simple append, allow appending rows and/or columns to an array. --- src/data_repr.rs | 38 +++++++++++ src/impl_owned_array.rs | 144 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 +- tests/append.rs | 84 +++++++++++++++++++++++ 4 files changed, 268 insertions(+), 2 deletions(-) create mode 100644 tests/append.rs diff --git a/src/data_repr.rs b/src/data_repr.rs index b34f0a4ca..8a52f64c4 100644 --- a/src/data_repr.rs +++ b/src/data_repr.rs @@ -6,6 +6,8 @@ use alloc::borrow::ToOwned; use alloc::vec::Vec; use crate::extension::nonnull; +use rawpointer::PointerExt; + /// Array's representation. /// /// *Don’t use this type directly—use the type alias @@ -55,6 +57,37 @@ impl OwnedRepr { self.ptr } + /// Return end pointer + pub(crate) fn as_end_nonnull(&self) -> NonNull { + unsafe { + self.ptr.add(self.len) + } + } + + /// Reserve `additional` elements; return the new pointer + /// + /// ## Safety + /// + /// Note that existing pointers into the data are invalidated + #[must_use = "must use new pointer to update existing pointers"] + pub(crate) fn reserve(&mut self, additional: usize) -> NonNull { + self.modify_as_vec(|mut v| { + v.reserve(additional); + v + }); + self.as_nonnull_mut() + } + + /// Set the valid length of the data + /// + /// ## Safety + /// + /// The first `new_len` elements of the data should be valid. + pub(crate) unsafe fn set_len(&mut self, new_len: usize) { + debug_assert!(new_len <= self.capacity); + self.len = new_len; + } + /// Cast self into equivalent repr of other element type /// /// ## Safety @@ -72,6 +105,11 @@ impl OwnedRepr { } } + fn modify_as_vec(&mut self, f: impl FnOnce(Vec) -> Vec) { + let v = self.take_as_vec(); + *self = Self::from(f(v)); + } + fn take_as_vec(&mut self) -> Vec { let capacity = self.capacity; let len = self.len; diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 41eff2b11..33d667778 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -1,6 +1,11 @@ use alloc::vec::Vec; + use crate::imp_prelude::*; +use crate::dimension; +use crate::error::{ErrorKind, ShapeError}; +use crate::OwnedRepr; +use crate::Zip; /// Methods specific to `Array0`. /// @@ -59,3 +64,142 @@ where self.data.into_vec() } } + +/// Methods specific to `Array2`. +/// +/// ***See also all methods for [`ArrayBase`]*** +/// +/// [`ArrayBase`]: struct.ArrayBase.html +impl Array { + /// Append a row to an array with row major memory layout. + /// + /// ***Errors*** with a layout error if the array is not in standard order or + /// if it has holes, even exterior holes (from slicing).
+ /// ***Errors*** with shape error if the length of the input row does not match + /// the length of the rows in the array.
+ /// + /// The memory layout matters, since it determines in which direction the array can easily + /// grow. Notice that an empty array is compatible both ways. The amortized average + /// complexity of the append is O(m) where *m* is the length of the row. + /// + /// ```rust + /// use ndarray::{Array, ArrayView, array}; + /// + /// // create an empty array and append + /// let mut a = Array::zeros((0, 4)); + /// a.try_append_row(ArrayView::from(&[ 1., 2., 3., 4.])).unwrap(); + /// a.try_append_row(ArrayView::from(&[-1., -2., -3., -4.])).unwrap(); + /// + /// assert_eq!( + /// a, + /// array![[ 1., 2., 3., 4.], + /// [-1., -2., -3., -4.]]); + /// ``` + pub fn try_append_row(&mut self, row: ArrayView) -> Result<(), ShapeError> + where + A: Clone, + { + let row_len = row.len(); + if row_len != self.len_of(Axis(1)) { + return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); + } + let mut res_dim = self.raw_dim(); + res_dim[0] += 1; + let new_len = dimension::size_of_shape_checked(&res_dim)?; + + // array must be c-contiguous and be "full" (have no exterior holes) + if !self.is_standard_layout() || self.len() != self.data.len() { + return Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)); + } + + unsafe { + // grow backing storage and update head ptr + debug_assert_eq!(self.data.as_ptr(), self.as_ptr()); + self.ptr = self.data.reserve(row_len); // because we are standard order + + // recompute strides - if the array was previously empty, it could have + // zeros in strides. + let strides = res_dim.default_strides(); + + // copy elements from view to the array now + // + // make a raw view with the new row + // safe because the data was "full" + let tail_ptr = self.data.as_end_nonnull(); + let tail_view = RawArrayViewMut::new(tail_ptr, Ix1(row_len), Ix1(1)); + + struct SetLenOnDrop<'a, A: 'a> { + len: usize, + data: &'a mut OwnedRepr
, + } + + let mut length_guard = SetLenOnDrop { + len: self.data.len(), + data: &mut self.data, + }; + + impl Drop for SetLenOnDrop<'_, A> { + fn drop(&mut self) { + unsafe { + self.data.set_len(self.len); + } + } + } + + // assign the new elements + Zip::from(tail_view).and(row) + .for_each(|to, from| { + to.write(from.clone()); + length_guard.len += 1; + }); + + drop(length_guard); + + // update array dimension + self.strides = strides; + self.dim[0] += 1; + + } + // multiple assertions after pointer & dimension update + debug_assert_eq!(self.data.len(), self.len()); + debug_assert_eq!(self.len(), new_len); + debug_assert!(self.is_standard_layout()); + + Ok(()) + } + + /// Append a column to an array with column major memory layout. + /// + /// ***Errors*** with a layout error if the array is not in column major order or + /// if it has holes, even exterior holes (from slicing).
+ /// ***Errors*** with shape error if the length of the input column does not match + /// the length of the columns in the array.
+ /// + /// The memory layout matters, since it determines in which direction the array can easily + /// grow. Notice that an empty array is compatible both ways. The amortized average + /// complexity of the append is O(m) where *m* is the length of the column. + /// + /// ```rust + /// use ndarray::{Array, ArrayView, array}; + /// + /// // create an empty array and append + /// let mut a = Array::zeros((2, 0)); + /// a.try_append_column(ArrayView::from(&[1., 2.])).unwrap(); + /// a.try_append_column(ArrayView::from(&[-1., -2.])).unwrap(); + /// + /// assert_eq!( + /// a, + /// array![[1., -1.], + /// [2., -2.]]); + /// ``` + pub fn try_append_column(&mut self, column: ArrayView) -> Result<(), ShapeError> + where + A: Clone, + { + self.swap_axes(0, 1); + let ret = self.try_append_row(column); + self.swap_axes(0, 1); + ret + } +} + diff --git a/src/lib.rs b/src/lib.rs index c4a0812a3..802e6f0da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -235,8 +235,8 @@ pub type Ixs = isize; /// An *n*-dimensional array. /// -/// The array is a general container of elements. It cannot grow or shrink, but -/// can be sliced into subsets of its data. +/// The array is a general container of elements. It cannot grow or shrink (with some exceptions), +/// but can be sliced into subsets of its data. /// The array supports arithmetic operations by applying them elementwise. /// /// In *n*-dimensional we include for example 1-dimensional rows or columns, diff --git a/tests/append.rs b/tests/append.rs new file mode 100644 index 000000000..3b7a1179a --- /dev/null +++ b/tests/append.rs @@ -0,0 +1,84 @@ + +use ndarray::prelude::*; +use ndarray::{ShapeError, ErrorKind}; + +#[test] +fn append_row() { + let mut a = Array::zeros((0, 4)); + a.try_append_row(aview1(&[0., 1., 2., 3.])).unwrap(); + a.try_append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + assert_eq!(a.shape(), &[2, 4]); + + assert_eq!(a, + array![[0., 1., 2., 3.], + [4., 5., 6., 7.]]); + + assert_eq!(a.try_append_row(aview1(&[1.])), + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); + assert_eq!(a.try_append_column(aview1(&[1.])), + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); + assert_eq!(a.try_append_column(aview1(&[1., 2.])), + Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); +} + +#[test] +fn append_row_error() { + let mut a = Array::zeros((3, 4)); + + assert_eq!(a.try_append_row(aview1(&[1.])), + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); + assert_eq!(a.try_append_column(aview1(&[1.])), + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); + assert_eq!(a.try_append_column(aview1(&[1., 2., 3.])), + Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); +} + +#[test] +fn append_row_existing() { + let mut a = Array::zeros((1, 4)); + a.try_append_row(aview1(&[0., 1., 2., 3.])).unwrap(); + a.try_append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + assert_eq!(a.shape(), &[3, 4]); + + assert_eq!(a, + array![[0., 0., 0., 0.], + [0., 1., 2., 3.], + [4., 5., 6., 7.]]); + + assert_eq!(a.try_append_row(aview1(&[1.])), + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); + assert_eq!(a.try_append_column(aview1(&[1.])), + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); + assert_eq!(a.try_append_column(aview1(&[1., 2., 3.])), + Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); +} + +#[test] +fn append_row_col_len_1() { + // Test appending 1 row and then cols from shape 1 x 1 + let mut a = Array::zeros((1, 1)); + a.try_append_row(aview1(&[1.])).unwrap(); // shape 2 x 1 + a.try_append_column(aview1(&[2., 3.])).unwrap(); // shape 2 x 2 + assert_eq!(a.try_append_row(aview1(&[1.])), + Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); + assert_eq!(a.try_append_row(aview1(&[1., 2.])), + Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); + a.try_append_column(aview1(&[4., 5.])).unwrap(); // shape 2 x 3 + assert_eq!(a.shape(), &[2, 3]); + + assert_eq!(a, + array![[0., 2., 4.], + [1., 3., 5.]]); +} + +#[test] +fn append_column() { + let mut a = Array::zeros((4, 0)); + a.try_append_column(aview1(&[0., 1., 2., 3.])).unwrap(); + a.try_append_column(aview1(&[4., 5., 6., 7.])).unwrap(); + assert_eq!(a.shape(), &[4, 2]); + + assert_eq!(a.t(), + array![[0., 1., 2., 3.], + [4., 5., 6., 7.]]); +} From af47658e526dff4b596eb41ad5a4c31904d5516f Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 27 Mar 2021 21:49:13 +0100 Subject: [PATCH 298/651] append: .try_append_array() to append array to array Allow appending an array along an axis. This only succeeds if the axis is an appropriate growing axis, where this can be done without moving existing elements. --- src/impl_owned_array.rs | 140 ++++++++++++++++++++++++++++++++++++++++ tests/append.rs | 23 +++++++ 2 files changed, 163 insertions(+) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 33d667778..6a3fc0e49 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -203,3 +203,143 @@ impl
Array { } } +impl Array + where D: Dimension +{ + /// Append a row to an array with row major memory layout. + /// + /// ***Errors*** with a layout error if the array is not in standard order or + /// if it has holes, even exterior holes (from slicing).
+ /// ***Errors*** with shape error if the length of the input row does not match + /// the length of the rows in the array.
+ /// + /// The memory layout matters, since it determines in which direction the array can easily + /// grow. Notice that an empty array is compatible both ways. The amortized average + /// complexity of the append is O(m) where *m* is the length of the row. + /// + /// ```rust + /// use ndarray::{Array, ArrayView, array}; + /// + /// // create an empty array and append + /// let mut a = Array::zeros((0, 4)); + /// a.try_append_row(ArrayView::from(&[1., 2., 3., 4.])).unwrap(); + /// a.try_append_row(ArrayView::from(&[0., -2., -3., -4.])).unwrap(); + /// + /// assert_eq!( + /// a, + /// array![[1., 2., 3., 4.], + /// [0., -2., -3., -4.]]); + /// ``` + pub fn try_append_array(&mut self, axis: Axis, array: ArrayView) + -> Result<(), ShapeError> + where + A: Clone, + D: RemoveAxis, + { + if self.ndim() == 0 { + return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); + } + + let remaining_shape = self.raw_dim().remove_axis(axis); + let array_rem_shape = array.raw_dim().remove_axis(axis); + + if remaining_shape != array_rem_shape { + return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); + } + + let len_to_append = array.len(); + if len_to_append == 0 { + return Ok(()); + } + + let array_shape = array.raw_dim(); + let mut res_dim = self.raw_dim(); + res_dim[axis.index()] += array_shape[axis.index()]; + let new_len = dimension::size_of_shape_checked(&res_dim)?; + + let self_is_empty = self.is_empty(); + + // array must be empty or have `axis` as the outermost (longest stride) + // axis + if !(self_is_empty || + self.axes().max_by_key(|ax| ax.stride).map(|ax| ax.axis) == Some(axis)) + { + return Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)); + } + + // array must be be "full" (have no exterior holes) + if self.len() != self.data.len() { + return Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)); + } + let strides = if self_is_empty { + // recompute strides - if the array was previously empty, it could have + // zeros in strides. + res_dim.default_strides() + } else { + self.strides.clone() + }; + + unsafe { + // grow backing storage and update head ptr + debug_assert_eq!(self.data.as_ptr(), self.as_ptr()); + self.ptr = self.data.reserve(len_to_append); // because we are standard order + + // copy elements from view to the array now + // + // make a raw view with the new row + // safe because the data was "full" + let tail_ptr = self.data.as_end_nonnull(); + let tail_view = RawArrayViewMut::new(tail_ptr, array_shape, strides.clone()); + + struct SetLenOnDrop<'a, A: 'a> { + len: usize, + data: &'a mut OwnedRepr
, + } + + let mut length_guard = SetLenOnDrop { + len: self.data.len(), + data: &mut self.data, + }; + + impl Drop for SetLenOnDrop<'_, A> { + fn drop(&mut self) { + unsafe { + self.data.set_len(self.len); + } + } + } + + // we have a problem here XXX + // + // To be robust for panics and drop the right elements, we want + // to fill the tail in-order, so that we can drop the right elements on + // panic. Don't know how to achieve that. + // + // It might be easier to retrace our steps in a scope guard to drop the right + // elements.. (PartialArray style). + // + // assign the new elements + Zip::from(tail_view).and(array) + .for_each(|to, from| { + to.write(from.clone()); + length_guard.len += 1; + }); + + //length_guard.len += len_to_append; + dbg!(len_to_append); + drop(length_guard); + + // update array dimension + self.strides = strides; + self.dim = res_dim; + dbg!(&self.dim); + + } + // multiple assertions after pointer & dimension update + debug_assert_eq!(self.data.len(), self.len()); + debug_assert_eq!(self.len(), new_len); + debug_assert!(self.is_standard_layout()); + + Ok(()) + } +} diff --git a/tests/append.rs b/tests/append.rs index 3b7a1179a..4c0b093a2 100644 --- a/tests/append.rs +++ b/tests/append.rs @@ -82,3 +82,26 @@ fn append_column() { array![[0., 1., 2., 3.], [4., 5., 6., 7.]]); } + +#[test] +fn append_array1() { + let mut a = Array::zeros((0, 4)); + a.try_append_array(Axis(0), aview2(&[[0., 1., 2., 3.]])).unwrap(); + println!("{:?}", a); + a.try_append_array(Axis(0), aview2(&[[4., 5., 6., 7.]])).unwrap(); + println!("{:?}", a); + //a.try_append_column(aview1(&[4., 5., 6., 7.])).unwrap(); + //assert_eq!(a.shape(), &[4, 2]); + + assert_eq!(a, + array![[0., 1., 2., 3.], + [4., 5., 6., 7.]]); + + a.try_append_array(Axis(0), aview2(&[[5., 5., 4., 4.], [3., 3., 2., 2.]])).unwrap(); + println!("{:?}", a); + assert_eq!(a, + array![[0., 1., 2., 3.], + [4., 5., 6., 7.], + [5., 5., 4., 4.], + [3., 3., 2., 2.]]); +} From a7e3aab4e5e29123f653c66bba92f7c2372993aa Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 3 Apr 2021 21:27:52 +0200 Subject: [PATCH 299/651] append: Update doc comment for try_append_array --- src/impl_owned_array.rs | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 6a3fc0e49..8d179dfbd 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -206,29 +206,50 @@ impl Array { impl Array where D: Dimension { - /// Append a row to an array with row major memory layout. + /// Append an array to the array + /// + /// The axis-to-append-to `axis` must be the array's "growing axis" for this operation + /// to succeed. The growing axis is the outermost or last-visited when elements are visited in + /// memory order: + /// + /// `axis` must be the growing axis of the current array, an axis with length 0 or 1. + /// + /// - This is the 0th axis for standard layout arrays + /// - This is the *n*-1 th axis for fortran layout arrays + /// - If the array is empty (the axis or any other has length 0) or if `axis` + /// has length 1, then the array can always be appended. /// /// ***Errors*** with a layout error if the array is not in standard order or /// if it has holes, even exterior holes (from slicing).
/// ***Errors*** with shape error if the length of the input row does not match /// the length of the rows in the array.
/// - /// The memory layout matters, since it determines in which direction the array can easily - /// grow. Notice that an empty array is compatible both ways. The amortized average - /// complexity of the append is O(m) where *m* is the length of the row. + /// The memory layout of the `self` array matters, since it determines in which direction the + /// array can easily grow. Notice that an empty array is compatible both ways. The amortized + /// average complexity of the append is O(m) where *m* is the number of elements in the + /// array-to-append (equivalent to how `Vec::extend` works). + /// + /// The memory layout of the argument `array` does not matter. /// /// ```rust - /// use ndarray::{Array, ArrayView, array}; + /// use ndarray::{Array, ArrayView, array, Axis}; /// /// // create an empty array and append /// let mut a = Array::zeros((0, 4)); - /// a.try_append_row(ArrayView::from(&[1., 2., 3., 4.])).unwrap(); - /// a.try_append_row(ArrayView::from(&[0., -2., -3., -4.])).unwrap(); + /// let ones = ArrayView::from(&[1.; 8]).into_shape((2, 4)).unwrap(); + /// let zeros = ArrayView::from(&[0.; 8]).into_shape((2, 4)).unwrap(); + /// a.try_append_array(Axis(0), ones).unwrap(); + /// a.try_append_array(Axis(0), zeros).unwrap(); + /// a.try_append_array(Axis(0), ones).unwrap(); /// /// assert_eq!( /// a, - /// array![[1., 2., 3., 4.], - /// [0., -2., -3., -4.]]); + /// array![[1., 1., 1., 1.], + /// [1., 1., 1., 1.], + /// [0., 0., 0., 0.], + /// [0., 0., 0., 0.], + /// [1., 1., 1., 1.], + /// [1., 1., 1., 1.]]); /// ``` pub fn try_append_array(&mut self, axis: Axis, array: ArrayView) -> Result<(), ShapeError> From f003aaa41a890a8aa6193a600240086ff273dff5 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 3 Apr 2021 01:10:49 +0200 Subject: [PATCH 300/651] append: Solve axis iteration order problem by sorting axes --- src/impl_owned_array.rs | 75 ++++++++++++++++++++++++++++++++++------- src/zip/mod.rs | 7 ++++ tests/append.rs | 41 ++++++++++++++++++++++ 3 files changed, 110 insertions(+), 13 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 8d179dfbd..a7e127333 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -251,7 +251,7 @@ impl Array /// [1., 1., 1., 1.], /// [1., 1., 1., 1.]]); /// ``` - pub fn try_append_array(&mut self, axis: Axis, array: ArrayView) + pub fn try_append_array(&mut self, axis: Axis, mut array: ArrayView) -> Result<(), ShapeError> where A: Clone, @@ -310,7 +310,7 @@ impl Array // make a raw view with the new row // safe because the data was "full" let tail_ptr = self.data.as_end_nonnull(); - let tail_view = RawArrayViewMut::new(tail_ptr, array_shape, strides.clone()); + let mut tail_view = RawArrayViewMut::new(tail_ptr, array_shape, strides.clone()); struct SetLenOnDrop<'a, A: 'a> { len: usize, @@ -330,37 +330,86 @@ impl Array } } - // we have a problem here XXX - // // To be robust for panics and drop the right elements, we want // to fill the tail in-order, so that we can drop the right elements on - // panic. Don't know how to achieve that. + // panic. // - // It might be easier to retrace our steps in a scope guard to drop the right - // elements.. (PartialArray style). + // We have: Zip::from(tail_view).and(array) + // Transform tail_view into standard order by inverting and moving its axes. + // Keep the Zip traversal unchanged by applying the same axis transformations to + // `array`. This ensures the Zip traverses the underlying memory in order. // - // assign the new elements + // XXX It would be possible to skip this transformation if the element + // doesn't have drop. However, in the interest of code coverage, all elements + // use this code initially. + + if tail_view.ndim() > 1 { + for i in 0..tail_view.ndim() { + if tail_view.stride_of(Axis(i)) < 0 { + tail_view.invert_axis(Axis(i)); + array.invert_axis(Axis(i)); + } + } + sort_axes_to_standard_order(&mut tail_view, &mut array); + } Zip::from(tail_view).and(array) + .debug_assert_c_order() .for_each(|to, from| { to.write(from.clone()); length_guard.len += 1; }); - //length_guard.len += len_to_append; - dbg!(len_to_append); drop(length_guard); // update array dimension self.strides = strides; self.dim = res_dim; - dbg!(&self.dim); - } // multiple assertions after pointer & dimension update debug_assert_eq!(self.data.len(), self.len()); debug_assert_eq!(self.len(), new_len); - debug_assert!(self.is_standard_layout()); Ok(()) } } + +fn sort_axes_to_standard_order(a: &mut ArrayBase, b: &mut ArrayBase) +where + S: RawData, + S2: RawData, + D: Dimension, +{ + if a.ndim() <= 1 { + return; + } + sort_axes_impl(&mut a.dim, &mut a.strides, &mut b.dim, &mut b.strides); + debug_assert!(a.is_standard_layout()); +} + +fn sort_axes_impl(adim: &mut D, astrides: &mut D, bdim: &mut D, bstrides: &mut D) +where + D: Dimension, +{ + debug_assert!(adim.ndim() > 1); + debug_assert_eq!(adim.ndim(), bdim.ndim()); + // bubble sort axes + let mut changed = true; + while changed { + changed = false; + for i in 0..adim.ndim() - 1 { + let axis_i = i; + let next_axis = i + 1; + + // make sure higher stride axes sort before. + debug_assert!(astrides.slice()[axis_i] as isize >= 0); + if (astrides.slice()[axis_i] as isize) < astrides.slice()[next_axis] as isize { + changed = true; + adim.slice_mut().swap(axis_i, next_axis); + astrides.slice_mut().swap(axis_i, next_axis); + bdim.slice_mut().swap(axis_i, next_axis); + bstrides.slice_mut().swap(axis_i, next_axis); + } + } + } +} + diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 34bfe523f..95f8381b8 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -673,6 +673,13 @@ macro_rules! map_impl { self.build_and(part) } + #[allow(unused)] + #[inline] + pub(crate) fn debug_assert_c_order(self) -> Self { + debug_assert!(self.layout.is(CORDER) || self.layout_tendency >= 0); + self + } + fn build_and

(self, part: P) -> Zip<($($p,)* P, ), D> where P: NdProducer, { diff --git a/tests/append.rs b/tests/append.rs index 4c0b093a2..f3323530d 100644 --- a/tests/append.rs +++ b/tests/append.rs @@ -105,3 +105,44 @@ fn append_array1() { [5., 5., 4., 4.], [3., 3., 2., 2.]]); } + +#[test] +fn append_array_3d() { + let mut a = Array::zeros((0, 2, 2)); + a.try_append_array(Axis(0), array![[[0, 1], [2, 3]]].view()).unwrap(); + println!("{:?}", a); + + let aa = array![[[51, 52], [53, 54]], [[55, 56], [57, 58]]]; + let av = aa.view(); + println!("Send {:?} to append", av); + a.try_append_array(Axis(0), av.clone()).unwrap(); + + a.swap_axes(0, 1); + let aa = array![[[71, 72], [73, 74]], [[75, 76], [77, 78]]]; + let mut av = aa.view(); + av.swap_axes(0, 1); + println!("Send {:?} to append", av); + a.try_append_array(Axis(1), av.clone()).unwrap(); + println!("{:?}", a); + let aa = array![[[81, 82], [83, 84]], [[85, 86], [87, 88]]]; + let mut av = aa.view(); + av.swap_axes(0, 1); + println!("Send {:?} to append", av); + a.try_append_array(Axis(1), av).unwrap(); + println!("{:?}", a); + assert_eq!(a, + array![[[0, 1], + [51, 52], + [55, 56], + [71, 72], + [75, 76], + [81, 82], + [85, 86]], + [[2, 3], + [53, 54], + [57, 58], + [73, 74], + [77, 78], + [83, 84], + [87, 88]]]); +} From 29896d8c2e829022d0d3621ed961c87e51fa43b7 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 3 Apr 2021 02:08:55 +0200 Subject: [PATCH 301/651] append: Fix situations where we need to recompute stride When the axis has length 0, or 1, we need to carefully compute new strides. --- src/impl_owned_array.rs | 46 +++++++++++++++++++++++++++++--------- tests/append.rs | 49 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 10 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index a7e127333..c3cc0c6e1 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -261,6 +261,7 @@ impl Array return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); } + let current_axis_len = self.len_of(axis); let remaining_shape = self.raw_dim().remove_axis(axis); let array_rem_shape = array.raw_dim().remove_axis(axis); @@ -280,22 +281,46 @@ impl Array let self_is_empty = self.is_empty(); - // array must be empty or have `axis` as the outermost (longest stride) - // axis - if !(self_is_empty || - self.axes().max_by_key(|ax| ax.stride).map(|ax| ax.axis) == Some(axis)) - { - return Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)); + // array must be empty or have `axis` as the outermost (longest stride) axis + if !self_is_empty && current_axis_len > 1 { + // `axis` must be max stride axis or equal to its stride + let max_stride_axis = self.axes().max_by_key(|ax| ax.stride).unwrap(); + if max_stride_axis.axis != axis && max_stride_axis.stride > self.stride_of(axis) { + return Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)); + } } // array must be be "full" (have no exterior holes) if self.len() != self.data.len() { return Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)); } + let strides = if self_is_empty { - // recompute strides - if the array was previously empty, it could have - // zeros in strides. - res_dim.default_strides() + // recompute strides - if the array was previously empty, it could have zeros in + // strides. + // The new order is based on c/f-contig but must have `axis` as outermost axis. + if axis == Axis(self.ndim() - 1) { + // prefer f-contig when appending to the last axis + // Axis n - 1 is outermost axis + res_dim.fortran_strides() + } else { + // Default with modification + res_dim.slice_mut().swap(0, axis.index()); + let mut strides = res_dim.default_strides(); + res_dim.slice_mut().swap(0, axis.index()); + strides.slice_mut().swap(0, axis.index()); + strides + } + } else if current_axis_len == 1 { + // This is the outermost/longest stride axis; so we find the max across the other axes + let new_stride = self.axes().fold(1, |acc, ax| { + if ax.axis == axis { acc } else { + Ord::max(acc, ax.len as isize * ax.stride) + } + }); + let mut strides = self.strides.clone(); + strides[axis.index()] = new_stride as usize; + strides } else { self.strides.clone() }; @@ -383,7 +408,8 @@ where return; } sort_axes_impl(&mut a.dim, &mut a.strides, &mut b.dim, &mut b.strides); - debug_assert!(a.is_standard_layout()); + debug_assert!(a.is_standard_layout(), "not std layout dim: {:?}, strides: {:?}", + a.shape(), a.strides()); } fn sort_axes_impl(adim: &mut D, astrides: &mut D, bdim: &mut D, bstrides: &mut D) diff --git a/tests/append.rs b/tests/append.rs index f3323530d..a8a522a32 100644 --- a/tests/append.rs +++ b/tests/append.rs @@ -146,3 +146,52 @@ fn append_array_3d() { [83, 84], [87, 88]]]); } + +#[test] +fn test_append_2d() { + // create an empty array and append + let mut a = Array::zeros((0, 4)); + let ones = ArrayView::from(&[1.; 12]).into_shape((3, 4)).unwrap(); + let zeros = ArrayView::from(&[0.; 8]).into_shape((2, 4)).unwrap(); + a.try_append_array(Axis(0), ones).unwrap(); + a.try_append_array(Axis(0), zeros).unwrap(); + a.try_append_array(Axis(0), ones).unwrap(); + println!("{:?}", a); + assert_eq!(a.shape(), &[8, 4]); + for (i, row) in a.rows().into_iter().enumerate() { + let ones = i < 3 || i >= 5; + assert!(row.iter().all(|&x| x == ones as i32 as f64), "failed on lane {}", i); + } + + let mut a = Array::zeros((0, 4)); + a = a.reversed_axes(); + let ones = ones.reversed_axes(); + let zeros = zeros.reversed_axes(); + a.try_append_array(Axis(1), ones).unwrap(); + a.try_append_array(Axis(1), zeros).unwrap(); + a.try_append_array(Axis(1), ones).unwrap(); + println!("{:?}", a); + assert_eq!(a.shape(), &[4, 8]); + + for (i, row) in a.columns().into_iter().enumerate() { + let ones = i < 3 || i >= 5; + assert!(row.iter().all(|&x| x == ones as i32 as f64), "failed on lane {}", i); + } +} + +#[test] +fn test_append_middle_axis() { + // ensure we can append to Axis(1) by letting it become outermost + let mut a = Array::::zeros((3, 0, 2)); + a.try_append_array(Axis(1), Array::from_iter(0..12).into_shape((3, 2, 2)).unwrap().view()).unwrap(); + println!("{:?}", a); + a.try_append_array(Axis(1), Array::from_iter(12..24).into_shape((3, 2, 2)).unwrap().view()).unwrap(); + println!("{:?}", a); + + // ensure we can append to Axis(1) by letting it become outermost + let mut a = Array::::zeros((3, 1, 2)); + a.try_append_array(Axis(1), Array::from_iter(0..12).into_shape((3, 2, 2)).unwrap().view()).unwrap(); + println!("{:?}", a); + a.try_append_array(Axis(1), Array::from_iter(12..24).into_shape((3, 2, 2)).unwrap().view()).unwrap(); + println!("{:?}", a); +} From 54359a9c2ff39444bb03a9e84a7569e8693308b3 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 3 Apr 2021 22:09:50 +0200 Subject: [PATCH 302/651] append: Use try_append_array for append row/column --- src/impl_owned_array.rs | 73 ++--------------------------------------- 1 file changed, 2 insertions(+), 71 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index c3cc0c6e1..cdab683c7 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -99,73 +99,7 @@ impl Array { where A: Clone, { - let row_len = row.len(); - if row_len != self.len_of(Axis(1)) { - return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); - } - let mut res_dim = self.raw_dim(); - res_dim[0] += 1; - let new_len = dimension::size_of_shape_checked(&res_dim)?; - - // array must be c-contiguous and be "full" (have no exterior holes) - if !self.is_standard_layout() || self.len() != self.data.len() { - return Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)); - } - - unsafe { - // grow backing storage and update head ptr - debug_assert_eq!(self.data.as_ptr(), self.as_ptr()); - self.ptr = self.data.reserve(row_len); // because we are standard order - - // recompute strides - if the array was previously empty, it could have - // zeros in strides. - let strides = res_dim.default_strides(); - - // copy elements from view to the array now - // - // make a raw view with the new row - // safe because the data was "full" - let tail_ptr = self.data.as_end_nonnull(); - let tail_view = RawArrayViewMut::new(tail_ptr, Ix1(row_len), Ix1(1)); - - struct SetLenOnDrop<'a, A: 'a> { - len: usize, - data: &'a mut OwnedRepr, - } - - let mut length_guard = SetLenOnDrop { - len: self.data.len(), - data: &mut self.data, - }; - - impl Drop for SetLenOnDrop<'_, A> { - fn drop(&mut self) { - unsafe { - self.data.set_len(self.len); - } - } - } - - // assign the new elements - Zip::from(tail_view).and(row) - .for_each(|to, from| { - to.write(from.clone()); - length_guard.len += 1; - }); - - drop(length_guard); - - // update array dimension - self.strides = strides; - self.dim[0] += 1; - - } - // multiple assertions after pointer & dimension update - debug_assert_eq!(self.data.len(), self.len()); - debug_assert_eq!(self.len(), new_len); - debug_assert!(self.is_standard_layout()); - - Ok(()) + self.try_append_array(Axis(0), row.insert_axis(Axis(0))) } /// Append a column to an array with column major memory layout. @@ -196,10 +130,7 @@ impl Array { where A: Clone, { - self.swap_axes(0, 1); - let ret = self.try_append_row(column); - self.swap_axes(0, 1); - ret + self.try_append_array(Axis(1), column.insert_axis(Axis(1))) } } From 6ca4275666ae47d8924fa6a5db998f084b9c87c3 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 3 Apr 2021 22:10:57 +0200 Subject: [PATCH 303/651] stacking: Add a few more test cases for concatenate --- tests/stacking.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/stacking.rs b/tests/stacking.rs index 032525ffa..2c223fd66 100644 --- a/tests/stacking.rs +++ b/tests/stacking.rs @@ -1,4 +1,4 @@ -use ndarray::{arr2, arr3, aview1, concatenate, stack, Array2, Axis, ErrorKind, Ix1}; +use ndarray::{arr2, arr3, aview1, aview2, concatenate, stack, Array2, Axis, ErrorKind, Ix1}; #[test] fn concatenating() { @@ -15,6 +15,13 @@ fn concatenating() { let d = concatenate![Axis(0), a.row(0), &[9., 9.]]; assert_eq!(d, aview1(&[2., 2., 9., 9.])); + let d = concatenate![Axis(1), a.row(0).insert_axis(Axis(1)), aview1(&[9., 9.]).insert_axis(Axis(1))]; + assert_eq!(d, aview2(&[[2., 9.], + [2., 9.]])); + + let d = concatenate![Axis(0), a.row(0).insert_axis(Axis(1)), aview1(&[9., 9.]).insert_axis(Axis(1))]; + assert_eq!(d.t(), aview2(&[[2., 2., 9., 9.]])); + let res = ndarray::concatenate(Axis(1), &[a.view(), c.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::IncompatibleShape); From e8629467bfdd9cd95e3abf55bf40f2ec65250634 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 3 Apr 2021 22:11:35 +0200 Subject: [PATCH 304/651] stacking: Port concatenate to append, supporting Clone --- src/stacking.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/stacking.rs b/src/stacking.rs index 500ded6af..874c8cc4d 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -6,6 +6,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use alloc::vec::Vec; + +use crate::dimension; use crate::error::{from_kind, ErrorKind, ShapeError}; use crate::imp_prelude::*; @@ -68,7 +71,7 @@ where /// ``` pub fn concatenate(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> where - A: Copy, + A: Clone, D: RemoveAxis, { if arrays.is_empty() { @@ -88,24 +91,21 @@ where let stacked_dim = arrays.iter().fold(0, |acc, a| acc + a.len_of(axis)); res_dim.set_axis(axis, stacked_dim); + let new_len = dimension::size_of_shape_checked(&res_dim)?; - // we can safely use uninitialized values here because we will - // overwrite every one of them. - let mut res = Array::uninit(res_dim); + // start with empty array with precomputed capacity + // try_append_array's handling of empty arrays makes sure `axis` is ok for appending + res_dim.set_axis(axis, 0); + let mut res = unsafe { + // Safety: dimension is size 0 and vec is empty + Array::from_shape_vec_unchecked(res_dim, Vec::with_capacity(new_len)) + }; - { - let mut assign_view = res.view_mut(); - for array in arrays { - let len = array.len_of(axis); - let (front, rest) = assign_view.split_at(axis, len); - array.assign_to(front); - assign_view = rest; - } - debug_assert_eq!(assign_view.len(), 0); - } - unsafe { - Ok(res.assume_init()) + for array in arrays { + res.try_append_array(axis, array.clone())?; } + debug_assert_eq!(res.len_of(axis), stacked_dim); + Ok(res) } #[deprecated(note="Use under the name stack instead.", since="0.15.0")] From 0159715fe4233a77b02b7e6103f363e685b3f44a Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 3 Apr 2021 21:43:40 +0200 Subject: [PATCH 305/651] stacking: Port stack to append, supporting Clone --- src/stacking.rs | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/stacking.rs b/src/stacking.rs index 874c8cc4d..34bb61485 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -41,7 +41,7 @@ pub fn stack( arrays: &[ArrayView], ) -> Result, ShapeError> where - A: Copy, + A: Clone, D: Dimension, D::Larger: RemoveAxis, { @@ -138,7 +138,7 @@ pub fn stack_new_axis( arrays: &[ArrayView], ) -> Result, ShapeError> where - A: Copy, + A: Clone, D: Dimension, D::Larger: RemoveAxis, { @@ -158,24 +158,22 @@ where res_dim.set_axis(axis, arrays.len()); - // we can safely use uninitialized values here because we will - // overwrite every one of them. - let mut res = Array::uninit(res_dim); + let new_len = dimension::size_of_shape_checked(&res_dim)?; - res.axis_iter_mut(axis) - .zip(arrays.iter()) - .for_each(|(assign_view, array)| { - // assign_view is D::Larger::Smaller which is usually == D - // (but if D is Ix6, we have IxD != Ix6 here; differing types - // but same number of axes). - let assign_view = assign_view.into_dimensionality::() - .expect("same-dimensionality cast"); - array.assign_to(assign_view); - }); + // start with empty array with precomputed capacity + // try_append_array's handling of empty arrays makes sure `axis` is ok for appending + res_dim.set_axis(axis, 0); + let mut res = unsafe { + // Safety: dimension is size 0 and vec is empty + Array::from_shape_vec_unchecked(res_dim, Vec::with_capacity(new_len)) + }; - unsafe { - Ok(res.assume_init()) + for array in arrays { + res.try_append_array(axis, array.clone().insert_axis(axis))?; } + + debug_assert_eq!(res.len_of(axis), arrays.len()); + Ok(res) } /// Stack arrays along the new axis. From 9ad7a0fbe91b166586123de3af09ebbda500adbb Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 3 Apr 2021 21:44:59 +0200 Subject: [PATCH 306/651] stacking: .select() now supports A: Clone Now that concatenate supports Clonable elements (previous: only Copy), we can loosen the generic restriction on .select() as well. --- src/impl_methods.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 03ca09d74..3e893fcd8 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -873,7 +873,7 @@ where /// ``` pub fn select(&self, axis: Axis, indices: &[Ix]) -> Array where - A: Copy, + A: Clone, S: Data, D: RemoveAxis, { From 647715782d8e7381babce36cb81e03a4ccbc3711 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 3 Apr 2021 23:24:26 +0200 Subject: [PATCH 307/651] zip: Fix iteration order debug assertion in Zip We're trying to assert the iteration order is like C-contig or C-preferred. The old assertion tripped when combining these two arrays: - shape 1, 7 strides 7, 1 (layout: CFcf) - shape 1, 7 strides 1, 2 (layout: f) The result was that the first was judged to have "no preference" and second "leaning f", total "leaning f". The debug assertion tripped a false positive because the traversal is one-dimensional, so elements are still visited in the required order. The layout judgment for the second array should ideally be "CFcf" too, but then it needs an understanding of being one-dimensional with contiguous stride of 2, which is not implemented. --- src/zip/mod.rs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 95f8381b8..ffc6a710a 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -430,6 +430,26 @@ where } } +impl Zip<(P1, P2), D> +where + D: Dimension, + P1: NdProducer, + P1: NdProducer, +{ + /// Debug assert traversal order is like c (including 1D case) + // Method placement: only used for binary Zip at the moment. + #[inline] + pub(crate) fn debug_assert_c_order(self) -> Self { + debug_assert!(self.layout.is(CORDER) || self.layout_tendency >= 0 || + self.dimension.slice().iter().filter(|&&d| d > 1).count() <= 1, + "Assertion failed: traversal is not c-order or 1D for \ + layout {:?}, tendency {}, dimension {:?}", + self.layout, self.layout_tendency, self.dimension); + self + } +} + + /* trait Offset : Copy { unsafe fn offset(self, off: isize) -> Self; @@ -673,13 +693,6 @@ macro_rules! map_impl { self.build_and(part) } - #[allow(unused)] - #[inline] - pub(crate) fn debug_assert_c_order(self) -> Self { - debug_assert!(self.layout.is(CORDER) || self.layout_tendency >= 0); - self - } - fn build_and

(self, part: P) -> Zip<($($p,)* P, ), D> where P: NdProducer, { From 0599fd28cc1e0797a6ee94d8ae66c424b7299cdd Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 3 Apr 2021 23:48:47 +0200 Subject: [PATCH 308/651] append: Handle empty arrays in append Fix a problem with appending empty arrays (found by ndarray-rand's quickcheck thank you). The try_append_array method was returning a tad too early in this case. --- src/impl_owned_array.rs | 12 +++++++++--- tests/append.rs | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index cdab683c7..f35bc8c67 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -201,15 +201,21 @@ impl Array } let len_to_append = array.len(); - if len_to_append == 0 { - return Ok(()); - } let array_shape = array.raw_dim(); let mut res_dim = self.raw_dim(); res_dim[axis.index()] += array_shape[axis.index()]; let new_len = dimension::size_of_shape_checked(&res_dim)?; + if len_to_append == 0 { + // There are no elements to append and shapes are compatible: + // either the dimension increment is zero, or there is an existing + // zero in another axis in self. + debug_assert_eq!(self.len(), new_len); + self.dim = res_dim; + return Ok(()); + } + let self_is_empty = self.is_empty(); // array must be empty or have `axis` as the outermost (longest stride) axis diff --git a/tests/append.rs b/tests/append.rs index a8a522a32..bd9617d83 100644 --- a/tests/append.rs +++ b/tests/append.rs @@ -195,3 +195,22 @@ fn test_append_middle_axis() { a.try_append_array(Axis(1), Array::from_iter(12..24).into_shape((3, 2, 2)).unwrap().view()).unwrap(); println!("{:?}", a); } + +#[test] +fn test_append_zero_size() { + { + let mut a = Array::::zeros((0, 0)); + a.try_append_array(Axis(0), aview2(&[[]])).unwrap(); + a.try_append_array(Axis(0), aview2(&[[]])).unwrap(); + assert_eq!(a.len(), 0); + assert_eq!(a.shape(), &[2, 0]); + } + + { + let mut a = Array::::zeros((0, 0)); + a.try_append_array(Axis(1), ArrayView::from(&[]).into_shape((0, 1)).unwrap()).unwrap(); + a.try_append_array(Axis(1), ArrayView::from(&[]).into_shape((0, 1)).unwrap()).unwrap(); + assert_eq!(a.len(), 0); + assert_eq!(a.shape(), &[0, 2]); + } +} From 0c578aab1fa099bed1bfddbc53e52c4c6907a10f Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 3 Apr 2021 23:50:56 +0200 Subject: [PATCH 309/651] append: Add append test that deals with incorrect layout situations The existing code can already be used to clone an array into an appendable situation. I.e. even if a.try_append_array(axis, b) would error, we can always do this: let c = empty array c.try_append_array(axis, &a); c.try_append_array(axis, &b); --- tests/append.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/append.rs b/tests/append.rs index bd9617d83..37cf50287 100644 --- a/tests/append.rs +++ b/tests/append.rs @@ -21,6 +21,32 @@ fn append_row() { Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); } +#[test] +fn append_row_wrong_layout() { + let mut a = Array::zeros((0, 4)); + a.try_append_row(aview1(&[0., 1., 2., 3.])).unwrap(); + a.try_append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + assert_eq!(a.shape(), &[2, 4]); + + assert_eq!(a.try_append_column(aview1(&[1., 2.])), + Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); + + assert_eq!(a, + array![[0., 1., 2., 3.], + [4., 5., 6., 7.]]); + + // Clone the array + + let mut dim = a.raw_dim(); + dim[1] = 0; + let mut b = Array::zeros(dim); + b.try_append_array(Axis(1), a.view()).unwrap(); + assert_eq!(b.try_append_column(aview1(&[1., 2.])), Ok(())); + assert_eq!(b, + array![[0., 1., 2., 3., 1.], + [4., 5., 6., 7., 2.]]); +} + #[test] fn append_row_error() { let mut a = Array::zeros((3, 4)); From aad9c7418dd20c09c0bfdffb659818133422edbb Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 4 Apr 2021 04:29:24 +0200 Subject: [PATCH 310/651] stacking: Add benchmarks for .select() (based on append) --- benches/append.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 benches/append.rs diff --git a/benches/append.rs b/benches/append.rs new file mode 100644 index 000000000..5502fa062 --- /dev/null +++ b/benches/append.rs @@ -0,0 +1,24 @@ +#![feature(test)] + +extern crate test; +use test::Bencher; + +use ndarray::prelude::*; + +#[bench] +fn select_axis0(bench: &mut Bencher) { + let a = Array::::zeros((256, 256)); + let selectable = vec![0, 1, 2, 0, 1, 3, 0, 4, 16, 32, 128, 147, 149, 220, 221, 255, 221, 0, 1]; + bench.iter(|| { + a.select(Axis(0), &selectable) + }); +} + +#[bench] +fn select_axis1(bench: &mut Bencher) { + let a = Array::::zeros((256, 256)); + let selectable = vec![0, 1, 2, 0, 1, 3, 0, 4, 16, 32, 128, 147, 149, 220, 221, 255, 221, 0, 1]; + bench.iter(|| { + a.select(Axis(1), &selectable) + }); +} From 3d9a9ba1dafc83fb821f41dc94b1809b9d458c1a Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 6 Apr 2021 21:31:30 +0200 Subject: [PATCH 311/651] move_into: New method .move_into() for moving all array elements .move_into() lets all elements move out of an Array, into an uninitialized array. We use a DropCounter to check duplication/drops of elements rigorously. The DropCounter code is taken from rayon collect tests, where I wrote it. --- src/impl_owned_array.rs | 187 ++++++++++++++++++++++++++++++- src/lib.rs | 1 + tests/assign.rs | 237 ++++++++++++++++++++++++++++++++++++++++ tests/higher_order_f.rs | 34 ------ 4 files changed, 423 insertions(+), 36 deletions(-) create mode 100644 tests/assign.rs diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index f35bc8c67..d367b8db2 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -1,9 +1,13 @@ use alloc::vec::Vec; +use std::mem::MaybeUninit; use crate::imp_prelude::*; + use crate::dimension; use crate::error::{ErrorKind, ShapeError}; +use crate::iterators::Baseiter; +use crate::low_level_util::AbortIfPanic; use crate::OwnedRepr; use crate::Zip; @@ -137,6 +141,145 @@ impl Array { impl Array where D: Dimension { + /// Move all elements from self into `new_array`, which must be of the same shape but + /// can have a different memory layout. The destination is overwritten completely. + /// + /// The destination should be a mut reference to an array or an `ArrayViewMut` with + /// `MaybeUninit` elements (which are overwritten without dropping any existing value). + /// + /// Minor implementation note: Owned arrays like `self` may be sliced in place and own elements + /// that are not part of their active view; these are dropped at the end of this function, + /// after all elements in the "active view" are moved into `new_array`. If there is a panic in + /// drop of any such element, other elements may be leaked. + /// + /// ***Panics*** if the shapes don't agree. + pub fn move_into<'a, AM>(self, new_array: AM) + where + AM: Into, D>>, + A: 'a, + { + // Remove generic parameter P and call the implementation + self.move_into_impl(new_array.into()) + } + + fn move_into_impl(mut self, new_array: ArrayViewMut, D>) { + unsafe { + // Safety: copy_to_nonoverlapping cannot panic + let guard = AbortIfPanic(&"move_into: moving out of owned value"); + // Move all reachable elements + Zip::from(self.raw_view_mut()) + .and(new_array) + .for_each(|src, dst| { + src.copy_to_nonoverlapping(dst.as_mut_ptr(), 1); + }); + guard.defuse(); + // Drop all unreachable elements + self.drop_unreachable_elements(); + } + } + + /// This drops all "unreachable" elements in the data storage of self. + /// + /// That means those elements that are not visible in the slicing of the array. + /// *Reachable elements are assumed to already have been moved from.* + /// + /// # Safety + /// + /// This is a panic critical section since `self` is already moved-from. + fn drop_unreachable_elements(mut self) -> OwnedRepr { + let self_len = self.len(); + + // "deconstruct" self; the owned repr releases ownership of all elements and we + // and carry on with raw view methods + let data_len = self.data.len(); + + let has_unreachable_elements = self_len != data_len; + if !has_unreachable_elements || std::mem::size_of::() == 0 { + unsafe { + self.data.set_len(0); + } + self.data + } else { + self.drop_unreachable_elements_slow() + } + } + + #[inline(never)] + #[cold] + fn drop_unreachable_elements_slow(mut self) -> OwnedRepr { + // "deconstruct" self; the owned repr releases ownership of all elements and we + // and carry on with raw view methods + let self_len = self.len(); + let data_len = self.data.len(); + let data_ptr = self.data.as_nonnull_mut().as_ptr(); + + let mut self_; + + unsafe { + // Safety: self.data releases ownership of the elements + self_ = self.raw_view_mut(); + self.data.set_len(0); + } + + + // uninvert axes where needed, so that stride > 0 + for i in 0..self_.ndim() { + if self_.stride_of(Axis(i)) < 0 { + self_.invert_axis(Axis(i)); + } + } + + // Sort axes to standard order, Axis(0) has biggest stride and Axis(n - 1) least stride + // Note that self_ has holes, so self_ is not C-contiguous + sort_axes_in_default_order(&mut self_); + + unsafe { + // with uninverted axes this is now the element with lowest address + let array_memory_head_ptr = self_.ptr.as_ptr(); + let data_end_ptr = data_ptr.add(data_len); + debug_assert!(data_ptr <= array_memory_head_ptr); + debug_assert!(array_memory_head_ptr <= data_end_ptr); + + // iter is a raw pointer iterator traversing self_ in its standard order + let mut iter = Baseiter::new(self_.ptr.as_ptr(), self_.dim, self_.strides); + let mut dropped_elements = 0; + + // The idea is simply this: the iterator will yield the elements of self_ in + // increasing address order. + // + // The pointers produced by the iterator are those that we *do not* touch. + // The pointers *not mentioned* by the iterator are those we have to drop. + // + // We have to drop elements in the range from `data_ptr` until (not including) + // `data_end_ptr`, except those that are produced by `iter`. + let mut last_ptr = data_ptr; + + while let Some(elem_ptr) = iter.next() { + // The interval from last_ptr up until (not including) elem_ptr + // should now be dropped. This interval may be empty, then we just skip this loop. + while last_ptr != elem_ptr { + debug_assert!(last_ptr < data_end_ptr); + std::ptr::drop_in_place(last_ptr); + last_ptr = last_ptr.add(1); + dropped_elements += 1; + } + // Next interval will continue one past the current element + last_ptr = elem_ptr.add(1); + } + + while last_ptr < data_end_ptr { + std::ptr::drop_in_place(last_ptr); + last_ptr = last_ptr.add(1); + dropped_elements += 1; + } + + assert_eq!(data_len, dropped_elements + self_len, + "Internal error: inconsistency in move_into"); + } + self.data + } + + /// Append an array to the array /// /// The axis-to-append-to `axis` must be the array's "growing axis" for this operation @@ -312,7 +455,7 @@ impl Array array.invert_axis(Axis(i)); } } - sort_axes_to_standard_order(&mut tail_view, &mut array); + sort_axes_to_standard_order_tandem(&mut tail_view, &mut array); } Zip::from(tail_view).and(array) .debug_assert_c_order() @@ -335,7 +478,21 @@ impl Array } } -fn sort_axes_to_standard_order(a: &mut ArrayBase, b: &mut ArrayBase) +/// Sort axes to standard order, i.e Axis(0) has biggest stride and Axis(n - 1) least stride +/// +/// The axes should have stride >= 0 before calling this method. +fn sort_axes_in_default_order(a: &mut ArrayBase) +where + S: RawData, + D: Dimension, +{ + if a.ndim() <= 1 { + return; + } + sort_axes1_impl(&mut a.dim, &mut a.strides); +} + +fn sort_axes_to_standard_order_tandem(a: &mut ArrayBase, b: &mut ArrayBase) where S: RawData, S2: RawData, @@ -349,6 +506,32 @@ where a.shape(), a.strides()); } +fn sort_axes1_impl(adim: &mut D, astrides: &mut D) +where + D: Dimension, +{ + debug_assert!(adim.ndim() > 1); + debug_assert_eq!(adim.ndim(), astrides.ndim()); + // bubble sort axes + let mut changed = true; + while changed { + changed = false; + for i in 0..adim.ndim() - 1 { + let axis_i = i; + let next_axis = i + 1; + + // make sure higher stride axes sort before. + debug_assert!(astrides.slice()[axis_i] as isize >= 0); + if (astrides.slice()[axis_i] as isize) < astrides.slice()[next_axis] as isize { + changed = true; + adim.slice_mut().swap(axis_i, next_axis); + astrides.slice_mut().swap(axis_i, next_axis); + } + } + } +} + + fn sort_axes_impl(adim: &mut D, astrides: &mut D, bdim: &mut D, bstrides: &mut D) where D: Dimension, diff --git a/src/lib.rs b/src/lib.rs index 802e6f0da..6a3f0a76b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ clippy::deref_addrof, clippy::unreadable_literal, clippy::manual_map, // is not an error + clippy::while_let_on_iterator, // is not an error )] #![cfg_attr(not(feature = "std"), no_std)] diff --git a/tests/assign.rs b/tests/assign.rs new file mode 100644 index 000000000..19156cce8 --- /dev/null +++ b/tests/assign.rs @@ -0,0 +1,237 @@ +use ndarray::prelude::*; + +use std::sync::atomic::{AtomicUsize, Ordering}; + +#[test] +fn assign() { + let mut a = arr2(&[[1., 2.], [3., 4.]]); + let b = arr2(&[[1., 3.], [2., 4.]]); + a.assign(&b); + assert_eq!(a, b); + + /* Test broadcasting */ + a.assign(&ArcArray::zeros(1)); + assert_eq!(a, ArcArray::zeros((2, 2))); + + /* Test other type */ + a.assign(&Array::from_elem((2, 2), 3.)); + assert_eq!(a, ArcArray::from_elem((2, 2), 3.)); + + /* Test mut view */ + let mut a = arr2(&[[1, 2], [3, 4]]); + { + let mut v = a.view_mut(); + v.slice_collapse(s![..1, ..]); + v.fill(0); + } + assert_eq!(a, arr2(&[[0, 0], [3, 4]])); +} + + +#[test] +fn assign_to() { + let mut a = arr2(&[[1., 2.], [3., 4.]]); + let b = arr2(&[[0., 3.], [2., 0.]]); + b.assign_to(&mut a); + assert_eq!(a, b); +} + +#[test] +fn move_into_copy() { + let a = arr2(&[[1., 2.], [3., 4.]]); + let acopy = a.clone(); + let mut b = Array::uninit(a.dim()); + a.move_into(b.view_mut()); + let b = unsafe { b.assume_init() }; + assert_eq!(acopy, b); + + let a = arr2(&[[1., 2.], [3., 4.]]).reversed_axes(); + let acopy = a.clone(); + let mut b = Array::uninit(a.dim()); + a.move_into(b.view_mut()); + let b = unsafe { b.assume_init() }; + assert_eq!(acopy, b); +} + +#[test] +fn move_into_owned() { + // Test various memory layouts and holes while moving String elements. + for &use_f_order in &[false, true] { + for &invert_axis in &[0b00, 0b01, 0b10, 0b11] { // bitmask for axis to invert + for &slice in &[false, true] { + let mut a = Array::from_shape_fn((5, 4).set_f(use_f_order), + |idx| format!("{:?}", idx)); + if slice { + a.slice_collapse(s![1..-1, ..;2]); + } + + if invert_axis & 0b01 != 0 { + a.invert_axis(Axis(0)); + } + if invert_axis & 0b10 != 0 { + a.invert_axis(Axis(1)); + } + + let acopy = a.clone(); + let mut b = Array::uninit(a.dim()); + a.move_into(b.view_mut()); + let b = unsafe { b.assume_init() }; + + assert_eq!(acopy, b); + } + } + } +} + +#[test] +fn move_into_slicing() { + // Count correct number of drops when using move_into and discontiguous arrays (with holes). + for &use_f_order in &[false, true] { + for &invert_axis in &[0b00, 0b01, 0b10, 0b11] { // bitmask for axis to invert + let counter = DropCounter::default(); + { + let (m, n) = (5, 4); + + let mut a = Array::from_shape_fn((m, n).set_f(use_f_order), |_idx| counter.element()); + a.slice_collapse(s![1..-1, ..;2]); + if invert_axis & 0b01 != 0 { + a.invert_axis(Axis(0)); + } + if invert_axis & 0b10 != 0 { + a.invert_axis(Axis(1)); + } + + let mut b = Array::uninit(a.dim()); + a.move_into(b.view_mut()); + let b = unsafe { b.assume_init() }; + + let total = m * n; + let dropped_1 = total - (m - 2) * (n - 2); + assert_eq!(counter.created(), total); + assert_eq!(counter.dropped(), dropped_1); + drop(b); + } + counter.assert_drop_count(); + } + } +} + +#[test] +fn move_into_diag() { + // Count correct number of drops when using move_into and discontiguous arrays (with holes). + for &use_f_order in &[false, true] { + let counter = DropCounter::default(); + { + let (m, n) = (5, 4); + + let a = Array::from_shape_fn((m, n).set_f(use_f_order), |_idx| counter.element()); + let a = a.into_diag(); + + let mut b = Array::uninit(a.dim()); + a.move_into(b.view_mut()); + let b = unsafe { b.assume_init() }; + + let total = m * n; + let dropped_1 = total - Ord::min(m, n); + assert_eq!(counter.created(), total); + assert_eq!(counter.dropped(), dropped_1); + drop(b); + } + counter.assert_drop_count(); + } +} + +#[test] +fn move_into_0dim() { + // Count correct number of drops when using move_into and discontiguous arrays (with holes). + for &use_f_order in &[false, true] { + let counter = DropCounter::default(); + { + let (m, n) = (5, 4); + + // slice into a 0-dim array + let a = Array::from_shape_fn((m, n).set_f(use_f_order), |_idx| counter.element()); + let a = a.slice_move(s![2, 2]); + + assert_eq!(a.ndim(), 0); + let mut b = Array::uninit(a.dim()); + a.move_into(b.view_mut()); + let b = unsafe { b.assume_init() }; + + let total = m * n; + let dropped_1 = total - 1; + assert_eq!(counter.created(), total); + assert_eq!(counter.dropped(), dropped_1); + drop(b); + } + counter.assert_drop_count(); + } +} + +#[test] +fn move_into_empty() { + // Count correct number of drops when using move_into and discontiguous arrays (with holes). + for &use_f_order in &[false, true] { + let counter = DropCounter::default(); + { + let (m, n) = (5, 4); + + // slice into an empty array; + let a = Array::from_shape_fn((m, n).set_f(use_f_order), |_idx| counter.element()); + let a = a.slice_move(s![..0, 1..1]); + assert!(a.is_empty()); + let mut b = Array::uninit(a.dim()); + a.move_into(b.view_mut()); + let b = unsafe { b.assume_init() }; + + let total = m * n; + let dropped_1 = total; + assert_eq!(counter.created(), total); + assert_eq!(counter.dropped(), dropped_1); + drop(b); + } + counter.assert_drop_count(); + } +} + + +/// This counter can create elements, and then count and verify +/// the number of which have actually been dropped again. +#[derive(Default)] +struct DropCounter { + created: AtomicUsize, + dropped: AtomicUsize, +} + +struct Element<'a>(&'a AtomicUsize); + +impl DropCounter { + fn created(&self) -> usize { + self.created.load(Ordering::Relaxed) + } + + fn dropped(&self) -> usize { + self.dropped.load(Ordering::Relaxed) + } + + fn element(&self) -> Element<'_> { + self.created.fetch_add(1, Ordering::Relaxed); + Element(&self.dropped) + } + + fn assert_drop_count(&self) { + assert_eq!( + self.created(), + self.dropped(), + "Expected {} dropped elements, but found {}", + self.created(), + self.dropped() + ); + } +} + +impl<'a> Drop for Element<'a> { + fn drop(&mut self) { + self.0.fetch_add(1, Ordering::Relaxed); + } +} diff --git a/tests/higher_order_f.rs b/tests/higher_order_f.rs index 1238cc4d8..c567eb3e0 100644 --- a/tests/higher_order_f.rs +++ b/tests/higher_order_f.rs @@ -6,37 +6,3 @@ fn test_fold_axis_oob() { let a = arr2(&[[1., 2.], [3., 4.]]); a.fold_axis(Axis(2), 0., |x, y| x + y); } - -#[test] -fn assign() { - let mut a = arr2(&[[1., 2.], [3., 4.]]); - let b = arr2(&[[1., 3.], [2., 4.]]); - a.assign(&b); - assert_eq!(a, b); - - /* Test broadcasting */ - a.assign(&ArcArray::zeros(1)); - assert_eq!(a, ArcArray::zeros((2, 2))); - - /* Test other type */ - a.assign(&Array::from_elem((2, 2), 3.)); - assert_eq!(a, ArcArray::from_elem((2, 2), 3.)); - - /* Test mut view */ - let mut a = arr2(&[[1, 2], [3, 4]]); - { - let mut v = a.view_mut(); - v.slice_collapse(s![..1, ..]); - v.fill(0); - } - assert_eq!(a, arr2(&[[0, 0], [3, 4]])); -} - - -#[test] -fn assign_to() { - let mut a = arr2(&[[1., 2.], [3., 4.]]); - let b = arr2(&[[0., 3.], [2., 0.]]); - b.assign_to(&mut a); - assert_eq!(a, b); -} From f153dc4662bb20f9e5c80b3e9c525dd9aa39bcc1 Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 6 Apr 2021 21:31:30 +0200 Subject: [PATCH 312/651] append: Adapt memory layout automatically in append if needed --- src/impl_owned_array.rs | 51 +++++++++++++++++++++++++++++++++++++++-- tests/append.rs | 25 ++++++++++++++------ 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index d367b8db2..f0512d530 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -279,6 +279,45 @@ impl Array self.data } + /// Create an empty array with an all-zeros shape + /// + /// ***Panics*** if D is zero-dimensional, because it can't be empty + pub(crate) fn empty() -> Array { + assert_ne!(D::NDIM, Some(0)); + let ndim = D::NDIM.unwrap_or(1); + Array::from_shape_simple_fn(D::zeros(ndim), || unreachable!()) + } + + /// Create new_array with the right layout for appending to `growing_axis` + #[cold] + fn change_to_contig_append_layout(&mut self, growing_axis: Axis) { + let ndim = self.ndim(); + let mut dim = self.raw_dim(); + + // The array will be created with 0 (C) or ndim-1 (F) as the biggest stride + // axis. Rearrange the shape so that `growing_axis` is the biggest stride axis + // afterwards. + let prefer_f_layout = growing_axis == Axis(ndim - 1); + if !prefer_f_layout { + dim.slice_mut().swap(0, growing_axis.index()); + } + let mut new_array = Self::uninit(dim.set_f(prefer_f_layout)); + if !prefer_f_layout { + new_array.swap_axes(0, growing_axis.index()); + } + + // self -> old_self. + // dummy array -> self. + // old_self elements are moved -> new_array. + let old_self = std::mem::replace(self, Self::empty()); + old_self.move_into(new_array.view_mut()); + + // new_array -> self. + unsafe { + *self = new_array.assume_init(); + } + } + /// Append an array to the array /// @@ -360,19 +399,27 @@ impl Array } let self_is_empty = self.is_empty(); + let mut incompatible_layout = false; // array must be empty or have `axis` as the outermost (longest stride) axis if !self_is_empty && current_axis_len > 1 { // `axis` must be max stride axis or equal to its stride let max_stride_axis = self.axes().max_by_key(|ax| ax.stride).unwrap(); if max_stride_axis.axis != axis && max_stride_axis.stride > self.stride_of(axis) { - return Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)); + incompatible_layout = true; } } // array must be be "full" (have no exterior holes) if self.len() != self.data.len() { - return Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)); + incompatible_layout = true; + } + + if incompatible_layout { + self.change_to_contig_append_layout(axis); + // safety-check parameters after remodeling + debug_assert_eq!(self_is_empty, self.is_empty()); + debug_assert_eq!(current_axis_len, self.len_of(axis)); } let strides = if self_is_empty { diff --git a/tests/append.rs b/tests/append.rs index 37cf50287..224fa6f3e 100644 --- a/tests/append.rs +++ b/tests/append.rs @@ -18,7 +18,10 @@ fn append_row() { assert_eq!(a.try_append_column(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); assert_eq!(a.try_append_column(aview1(&[1., 2.])), - Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); + Ok(())); + assert_eq!(a, + array![[0., 1., 2., 3., 1.], + [4., 5., 6., 7., 2.]]); } #[test] @@ -28,8 +31,7 @@ fn append_row_wrong_layout() { a.try_append_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[2, 4]); - assert_eq!(a.try_append_column(aview1(&[1., 2.])), - Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); + //assert_eq!(a.try_append_column(aview1(&[1., 2.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); assert_eq!(a, array![[0., 1., 2., 3.], @@ -56,7 +58,13 @@ fn append_row_error() { assert_eq!(a.try_append_column(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); assert_eq!(a.try_append_column(aview1(&[1., 2., 3.])), - Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); + Ok(())); + assert_eq!(a.t(), + array![[0., 0., 0.], + [0., 0., 0.], + [0., 0., 0.], + [0., 0., 0.], + [1., 2., 3.]]); } #[test] @@ -76,7 +84,11 @@ fn append_row_existing() { assert_eq!(a.try_append_column(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); assert_eq!(a.try_append_column(aview1(&[1., 2., 3.])), - Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); + Ok(())); + assert_eq!(a, + array![[0., 0., 0., 0., 1.], + [0., 1., 2., 3., 2.], + [4., 5., 6., 7., 3.]]); } #[test] @@ -87,8 +99,7 @@ fn append_row_col_len_1() { a.try_append_column(aview1(&[2., 3.])).unwrap(); // shape 2 x 2 assert_eq!(a.try_append_row(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); - assert_eq!(a.try_append_row(aview1(&[1., 2.])), - Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); + //assert_eq!(a.try_append_row(aview1(&[1., 2.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); a.try_append_column(aview1(&[4., 5.])).unwrap(); // shape 2 x 3 assert_eq!(a.shape(), &[2, 3]); From e0d07d31173bbcc6b62d6ca04c17fc353cef0d2c Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 7 Apr 2021 23:37:56 +0200 Subject: [PATCH 313/651] append: Handle negative stride arrays --- src/impl_owned_array.rs | 148 +++++++++++++++++++++++----------------- 1 file changed, 87 insertions(+), 61 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index f0512d530..926047501 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -2,6 +2,8 @@ use alloc::vec::Vec; use std::mem::MaybeUninit; +use rawpointer::PointerExt; + use crate::imp_prelude::*; use crate::dimension; @@ -332,15 +334,18 @@ impl Array /// - If the array is empty (the axis or any other has length 0) or if `axis` /// has length 1, then the array can always be appended. /// - /// ***Errors*** with a layout error if the array is not in standard order or - /// if it has holes, even exterior holes (from slicing).
- /// ***Errors*** with shape error if the length of the input row does not match - /// the length of the rows in the array.
+ /// ***Errors*** with shape error if the shape of self does not match the array-to-append; + /// all axes *except* the axis along which it being appended matter for this check. /// - /// The memory layout of the `self` array matters, since it determines in which direction the - /// array can easily grow. Notice that an empty array is compatible both ways. The amortized - /// average complexity of the append is O(m) where *m* is the number of elements in the - /// array-to-append (equivalent to how `Vec::extend` works). + /// The memory layout of the `self` array matters for ensuring that the append is efficient. + /// Appending automatically changes memory layout of the array so that it is appended to + /// along the "growing axis". + /// + /// Ensure appending is efficient by for example starting from an empty array and/or always + /// appending to an array along the same axis. + /// + /// The amortized average complexity of the append, when appending along its growing axis, is + /// O(*m*) where *m* is the length of the row. /// /// The memory layout of the argument `array` does not matter. /// @@ -404,8 +409,11 @@ impl Array // array must be empty or have `axis` as the outermost (longest stride) axis if !self_is_empty && current_axis_len > 1 { // `axis` must be max stride axis or equal to its stride - let max_stride_axis = self.axes().max_by_key(|ax| ax.stride).unwrap(); - if max_stride_axis.axis != axis && max_stride_axis.stride > self.stride_of(axis) { + let max_axis = self.axes().max_by_key(|ax| ax.stride.abs()).unwrap(); + if max_axis.axis != axis && max_axis.stride.abs() > self.stride_of(axis) { + incompatible_layout = true; + } + if self.stride_of(axis) < 0 { incompatible_layout = true; } } @@ -442,7 +450,8 @@ impl Array // This is the outermost/longest stride axis; so we find the max across the other axes let new_stride = self.axes().fold(1, |acc, ax| { if ax.axis == axis { acc } else { - Ord::max(acc, ax.len as isize * ax.stride) + let this_ax = ax.len as isize * ax.stride; + if this_ax.abs() > acc { this_ax } else { acc } } }); let mut strides = self.strides.clone(); @@ -454,26 +463,58 @@ impl Array unsafe { // grow backing storage and update head ptr - debug_assert_eq!(self.data.as_ptr(), self.as_ptr()); - self.ptr = self.data.reserve(len_to_append); // because we are standard order + let data_to_array_offset = if std::mem::size_of::
() != 0 { + self.as_ptr().offset_from(self.data.as_ptr()) + } else { + 0 + }; + debug_assert!(data_to_array_offset >= 0); + self.ptr = self.data.reserve(len_to_append).offset(data_to_array_offset); - // copy elements from view to the array now + // clone elements from view to the array now + // + // To be robust for panics and drop the right elements, we want + // to fill the tail in memory order, so that we can drop the right elements on panic. // - // make a raw view with the new row - // safe because the data was "full" + // We have: Zip::from(tail_view).and(array) + // Transform tail_view into standard order by inverting and moving its axes. + // Keep the Zip traversal unchanged by applying the same axis transformations to + // `array`. This ensures the Zip traverses the underlying memory in order. + // + // XXX It would be possible to skip this transformation if the element + // doesn't have drop. However, in the interest of code coverage, all elements + // use this code initially. + + // Invert axes in tail_view by inverting strides + let mut tail_strides = strides.clone(); + if tail_strides.ndim() > 1 { + for i in 0..tail_strides.ndim() { + let s = tail_strides[i] as isize; + if s < 0 { + tail_strides.set_axis(Axis(i), -s as usize); + array.invert_axis(Axis(i)); + } + } + } + + // With > 0 strides, the current end of data is the correct base pointer for tail_view let tail_ptr = self.data.as_end_nonnull(); - let mut tail_view = RawArrayViewMut::new(tail_ptr, array_shape, strides.clone()); + let mut tail_view = RawArrayViewMut::new(tail_ptr, array_shape, tail_strides); + if tail_view.ndim() > 1 { + sort_axes_in_default_order_tandem(&mut tail_view, &mut array); + debug_assert!(tail_view.is_standard_layout(), + "not std layout dim: {:?}, strides: {:?}", + tail_view.shape(), tail_view.strides()); + } + + // Keep track of currently filled lenght of `self.data` and update it + // on scope exit (panic or loop finish). struct SetLenOnDrop<'a, A: 'a> { len: usize, data: &'a mut OwnedRepr, } - let mut length_guard = SetLenOnDrop { - len: self.data.len(), - data: &mut self.data, - }; - impl Drop for SetLenOnDrop<'_, A> { fn drop(&mut self) { unsafe { @@ -482,36 +523,18 @@ impl Array } } - // To be robust for panics and drop the right elements, we want - // to fill the tail in-order, so that we can drop the right elements on - // panic. - // - // We have: Zip::from(tail_view).and(array) - // Transform tail_view into standard order by inverting and moving its axes. - // Keep the Zip traversal unchanged by applying the same axis transformations to - // `array`. This ensures the Zip traverses the underlying memory in order. - // - // XXX It would be possible to skip this transformation if the element - // doesn't have drop. However, in the interest of code coverage, all elements - // use this code initially. + let mut data_length_guard = SetLenOnDrop { + len: self.data.len(), + data: &mut self.data, + }; - if tail_view.ndim() > 1 { - for i in 0..tail_view.ndim() { - if tail_view.stride_of(Axis(i)) < 0 { - tail_view.invert_axis(Axis(i)); - array.invert_axis(Axis(i)); - } - } - sort_axes_to_standard_order_tandem(&mut tail_view, &mut array); - } Zip::from(tail_view).and(array) .debug_assert_c_order() .for_each(|to, from| { to.write(from.clone()); - length_guard.len += 1; + data_length_guard.len += 1; }); - - drop(length_guard); + drop(data_length_guard); // update array dimension self.strides = strides; @@ -520,6 +543,7 @@ impl Array // multiple assertions after pointer & dimension update debug_assert_eq!(self.data.len(), self.len()); debug_assert_eq!(self.len(), new_len); + debug_assert!(self.pointer_is_inbounds()); Ok(()) } @@ -539,20 +563,6 @@ where sort_axes1_impl(&mut a.dim, &mut a.strides); } -fn sort_axes_to_standard_order_tandem(a: &mut ArrayBase, b: &mut ArrayBase) -where - S: RawData, - S2: RawData, - D: Dimension, -{ - if a.ndim() <= 1 { - return; - } - sort_axes_impl(&mut a.dim, &mut a.strides, &mut b.dim, &mut b.strides); - debug_assert!(a.is_standard_layout(), "not std layout dim: {:?}, strides: {:?}", - a.shape(), a.strides()); -} - fn sort_axes1_impl(adim: &mut D, astrides: &mut D) where D: Dimension, @@ -579,7 +589,23 @@ where } -fn sort_axes_impl(adim: &mut D, astrides: &mut D, bdim: &mut D, bstrides: &mut D) +/// Sort axes to standard order, i.e Axis(0) has biggest stride and Axis(n - 1) least stride +/// +/// Axes in a and b are sorted by the strides of `a`, and `a`'s axes should have stride >= 0 before +/// calling this method. +fn sort_axes_in_default_order_tandem(a: &mut ArrayBase, b: &mut ArrayBase) +where + S: RawData, + S2: RawData, + D: Dimension, +{ + if a.ndim() <= 1 { + return; + } + sort_axes2_impl(&mut a.dim, &mut a.strides, &mut b.dim, &mut b.strides); +} + +fn sort_axes2_impl(adim: &mut D, astrides: &mut D, bdim: &mut D, bstrides: &mut D) where D: Dimension, { From b09c770e1b76f6e4be774f91b047bbfd3bf49a51 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 7 Apr 2021 18:50:42 +0200 Subject: [PATCH 314/651] append: Name methods .append(axis, array), .append_row/column() Now without the tricky memory layout failure mode, these methods can just have names without try_ prefix. It's expected that they work successfully as long as the user matches up array sizes correctly. --- src/impl_owned_array.rs | 72 ++++++++++++++++++------------- src/stacking.rs | 8 ++-- tests/append.rs | 94 ++++++++++++++++++++--------------------- 3 files changed, 93 insertions(+), 81 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 926047501..319e310a4 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -77,66 +77,78 @@ where /// /// [`ArrayBase`]: struct.ArrayBase.html impl Array { - /// Append a row to an array with row major memory layout. + /// Append a row to an array /// - /// ***Errors*** with a layout error if the array is not in standard order or - /// if it has holes, even exterior holes (from slicing).
- /// ***Errors*** with shape error if the length of the input row does not match - /// the length of the rows in the array.
+ /// ***Errors*** with a shape error if the length of the row does not match the length of the + /// rows in the array.
/// - /// The memory layout matters, since it determines in which direction the array can easily - /// grow. Notice that an empty array is compatible both ways. The amortized average - /// complexity of the append is O(m) where *m* is the length of the row. + /// The memory layout of the `self` array matters for ensuring that the append is efficient. + /// Appending automatically changes memory layout of the array so that it is appended to + /// along the "growing axis". + /// + /// Ensure appending is efficient by for example starting appending an empty array and + /// always appending along the same axis. For rows, ndarray's default layout is efficient for + /// appending. + /// + /// Notice that an empty array (where it has an axis of length zero) is the simplest starting + /// point. The amortized average complexity of the append is O(m) where *m* is the length of + /// the row. /// /// ```rust /// use ndarray::{Array, ArrayView, array}; /// /// // create an empty array and append /// let mut a = Array::zeros((0, 4)); - /// a.try_append_row(ArrayView::from(&[ 1., 2., 3., 4.])).unwrap(); - /// a.try_append_row(ArrayView::from(&[-1., -2., -3., -4.])).unwrap(); + /// a.append_row(ArrayView::from(&[ 1., 2., 3., 4.])).unwrap(); + /// a.append_row(ArrayView::from(&[-1., -2., -3., -4.])).unwrap(); /// /// assert_eq!( /// a, /// array![[ 1., 2., 3., 4.], /// [-1., -2., -3., -4.]]); /// ``` - pub fn try_append_row(&mut self, row: ArrayView) -> Result<(), ShapeError> + pub fn append_row(&mut self, row: ArrayView) -> Result<(), ShapeError> where A: Clone, { - self.try_append_array(Axis(0), row.insert_axis(Axis(0))) + self.append(Axis(0), row.insert_axis(Axis(0))) } - /// Append a column to an array with column major memory layout. + /// Append a column to an array + /// + /// ***Errors*** with a shape error if the length of the column does not match the length of + /// the columns in the array.
+ /// + /// The memory layout of the `self` array matters for ensuring that the append is efficient. + /// Appending automatically changes memory layout of the array so that it is appended to + /// along the "growing axis". /// - /// ***Errors*** with a layout error if the array is not in column major order or - /// if it has holes, even exterior holes (from slicing).
- /// ***Errors*** with shape error if the length of the input column does not match - /// the length of the columns in the array.
+ /// Ensure appending is efficient by for example starting appending an empty array and + /// always appending along the same axis. For columns, column major ("F") memory layout is + /// efficient for appending. /// - /// The memory layout matters, since it determines in which direction the array can easily - /// grow. Notice that an empty array is compatible both ways. The amortized average - /// complexity of the append is O(m) where *m* is the length of the column. + /// Notice that an empty array (where it has an axis of length zero) is the simplest starting + /// point. The amortized average complexity of the append is O(m) where *m* is the length of + /// the row. /// /// ```rust /// use ndarray::{Array, ArrayView, array}; /// /// // create an empty array and append /// let mut a = Array::zeros((2, 0)); - /// a.try_append_column(ArrayView::from(&[1., 2.])).unwrap(); - /// a.try_append_column(ArrayView::from(&[-1., -2.])).unwrap(); + /// a.append_column(ArrayView::from(&[1., 2.])).unwrap(); + /// a.append_column(ArrayView::from(&[-1., -2.])).unwrap(); /// /// assert_eq!( /// a, /// array![[1., -1.], /// [2., -2.]]); /// ``` - pub fn try_append_column(&mut self, column: ArrayView) -> Result<(), ShapeError> + pub fn append_column(&mut self, column: ArrayView) -> Result<(), ShapeError> where A: Clone, { - self.try_append_array(Axis(1), column.insert_axis(Axis(1))) + self.append(Axis(1), column.insert_axis(Axis(1))) } } @@ -334,7 +346,7 @@ impl Array /// - If the array is empty (the axis or any other has length 0) or if `axis` /// has length 1, then the array can always be appended. /// - /// ***Errors*** with shape error if the shape of self does not match the array-to-append; + /// ***Errors*** with a shape error if the shape of self does not match the array-to-append; /// all axes *except* the axis along which it being appended matter for this check. /// /// The memory layout of the `self` array matters for ensuring that the append is efficient. @@ -347,7 +359,7 @@ impl Array /// The amortized average complexity of the append, when appending along its growing axis, is /// O(*m*) where *m* is the length of the row. /// - /// The memory layout of the argument `array` does not matter. + /// The memory layout of the argument `array` does not matter to the same extent. /// /// ```rust /// use ndarray::{Array, ArrayView, array, Axis}; @@ -356,9 +368,9 @@ impl Array /// let mut a = Array::zeros((0, 4)); /// let ones = ArrayView::from(&[1.; 8]).into_shape((2, 4)).unwrap(); /// let zeros = ArrayView::from(&[0.; 8]).into_shape((2, 4)).unwrap(); - /// a.try_append_array(Axis(0), ones).unwrap(); - /// a.try_append_array(Axis(0), zeros).unwrap(); - /// a.try_append_array(Axis(0), ones).unwrap(); + /// a.append(Axis(0), ones).unwrap(); + /// a.append(Axis(0), zeros).unwrap(); + /// a.append(Axis(0), ones).unwrap(); /// /// assert_eq!( /// a, @@ -369,7 +381,7 @@ impl Array /// [1., 1., 1., 1.], /// [1., 1., 1., 1.]]); /// ``` - pub fn try_append_array(&mut self, axis: Axis, mut array: ArrayView) + pub fn append(&mut self, axis: Axis, mut array: ArrayView) -> Result<(), ShapeError> where A: Clone, diff --git a/src/stacking.rs b/src/stacking.rs index 34bb61485..fb05c1963 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -94,7 +94,7 @@ where let new_len = dimension::size_of_shape_checked(&res_dim)?; // start with empty array with precomputed capacity - // try_append_array's handling of empty arrays makes sure `axis` is ok for appending + // append's handling of empty arrays makes sure `axis` is ok for appending res_dim.set_axis(axis, 0); let mut res = unsafe { // Safety: dimension is size 0 and vec is empty @@ -102,7 +102,7 @@ where }; for array in arrays { - res.try_append_array(axis, array.clone())?; + res.append(axis, array.clone())?; } debug_assert_eq!(res.len_of(axis), stacked_dim); Ok(res) @@ -161,7 +161,7 @@ where let new_len = dimension::size_of_shape_checked(&res_dim)?; // start with empty array with precomputed capacity - // try_append_array's handling of empty arrays makes sure `axis` is ok for appending + // append's handling of empty arrays makes sure `axis` is ok for appending res_dim.set_axis(axis, 0); let mut res = unsafe { // Safety: dimension is size 0 and vec is empty @@ -169,7 +169,7 @@ where }; for array in arrays { - res.try_append_array(axis, array.clone().insert_axis(axis))?; + res.append(axis, array.clone().insert_axis(axis))?; } debug_assert_eq!(res.len_of(axis), arrays.len()); diff --git a/tests/append.rs b/tests/append.rs index 224fa6f3e..0e24a744d 100644 --- a/tests/append.rs +++ b/tests/append.rs @@ -5,19 +5,19 @@ use ndarray::{ShapeError, ErrorKind}; #[test] fn append_row() { let mut a = Array::zeros((0, 4)); - a.try_append_row(aview1(&[0., 1., 2., 3.])).unwrap(); - a.try_append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + a.append_row(aview1(&[0., 1., 2., 3.])).unwrap(); + a.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[2, 4]); assert_eq!(a, array![[0., 1., 2., 3.], [4., 5., 6., 7.]]); - assert_eq!(a.try_append_row(aview1(&[1.])), + assert_eq!(a.append_row(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); - assert_eq!(a.try_append_column(aview1(&[1.])), + assert_eq!(a.append_column(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); - assert_eq!(a.try_append_column(aview1(&[1., 2.])), + assert_eq!(a.append_column(aview1(&[1., 2.])), Ok(())); assert_eq!(a, array![[0., 1., 2., 3., 1.], @@ -27,11 +27,11 @@ fn append_row() { #[test] fn append_row_wrong_layout() { let mut a = Array::zeros((0, 4)); - a.try_append_row(aview1(&[0., 1., 2., 3.])).unwrap(); - a.try_append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + a.append_row(aview1(&[0., 1., 2., 3.])).unwrap(); + a.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[2, 4]); - //assert_eq!(a.try_append_column(aview1(&[1., 2.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); + //assert_eq!(a.append_column(aview1(&[1., 2.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); assert_eq!(a, array![[0., 1., 2., 3.], @@ -42,8 +42,8 @@ fn append_row_wrong_layout() { let mut dim = a.raw_dim(); dim[1] = 0; let mut b = Array::zeros(dim); - b.try_append_array(Axis(1), a.view()).unwrap(); - assert_eq!(b.try_append_column(aview1(&[1., 2.])), Ok(())); + b.append(Axis(1), a.view()).unwrap(); + assert_eq!(b.append_column(aview1(&[1., 2.])), Ok(())); assert_eq!(b, array![[0., 1., 2., 3., 1.], [4., 5., 6., 7., 2.]]); @@ -53,11 +53,11 @@ fn append_row_wrong_layout() { fn append_row_error() { let mut a = Array::zeros((3, 4)); - assert_eq!(a.try_append_row(aview1(&[1.])), + assert_eq!(a.append_row(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); - assert_eq!(a.try_append_column(aview1(&[1.])), + assert_eq!(a.append_column(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); - assert_eq!(a.try_append_column(aview1(&[1., 2., 3.])), + assert_eq!(a.append_column(aview1(&[1., 2., 3.])), Ok(())); assert_eq!(a.t(), array![[0., 0., 0.], @@ -70,8 +70,8 @@ fn append_row_error() { #[test] fn append_row_existing() { let mut a = Array::zeros((1, 4)); - a.try_append_row(aview1(&[0., 1., 2., 3.])).unwrap(); - a.try_append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + a.append_row(aview1(&[0., 1., 2., 3.])).unwrap(); + a.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[3, 4]); assert_eq!(a, @@ -79,11 +79,11 @@ fn append_row_existing() { [0., 1., 2., 3.], [4., 5., 6., 7.]]); - assert_eq!(a.try_append_row(aview1(&[1.])), + assert_eq!(a.append_row(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); - assert_eq!(a.try_append_column(aview1(&[1.])), + assert_eq!(a.append_column(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); - assert_eq!(a.try_append_column(aview1(&[1., 2., 3.])), + assert_eq!(a.append_column(aview1(&[1., 2., 3.])), Ok(())); assert_eq!(a, array![[0., 0., 0., 0., 1.], @@ -95,12 +95,12 @@ fn append_row_existing() { fn append_row_col_len_1() { // Test appending 1 row and then cols from shape 1 x 1 let mut a = Array::zeros((1, 1)); - a.try_append_row(aview1(&[1.])).unwrap(); // shape 2 x 1 - a.try_append_column(aview1(&[2., 3.])).unwrap(); // shape 2 x 2 - assert_eq!(a.try_append_row(aview1(&[1.])), + a.append_row(aview1(&[1.])).unwrap(); // shape 2 x 1 + a.append_column(aview1(&[2., 3.])).unwrap(); // shape 2 x 2 + assert_eq!(a.append_row(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); - //assert_eq!(a.try_append_row(aview1(&[1., 2.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); - a.try_append_column(aview1(&[4., 5.])).unwrap(); // shape 2 x 3 + //assert_eq!(a.append_row(aview1(&[1., 2.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); + a.append_column(aview1(&[4., 5.])).unwrap(); // shape 2 x 3 assert_eq!(a.shape(), &[2, 3]); assert_eq!(a, @@ -111,8 +111,8 @@ fn append_row_col_len_1() { #[test] fn append_column() { let mut a = Array::zeros((4, 0)); - a.try_append_column(aview1(&[0., 1., 2., 3.])).unwrap(); - a.try_append_column(aview1(&[4., 5., 6., 7.])).unwrap(); + a.append_column(aview1(&[0., 1., 2., 3.])).unwrap(); + a.append_column(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[4, 2]); assert_eq!(a.t(), @@ -123,18 +123,18 @@ fn append_column() { #[test] fn append_array1() { let mut a = Array::zeros((0, 4)); - a.try_append_array(Axis(0), aview2(&[[0., 1., 2., 3.]])).unwrap(); + a.append(Axis(0), aview2(&[[0., 1., 2., 3.]])).unwrap(); println!("{:?}", a); - a.try_append_array(Axis(0), aview2(&[[4., 5., 6., 7.]])).unwrap(); + a.append(Axis(0), aview2(&[[4., 5., 6., 7.]])).unwrap(); println!("{:?}", a); - //a.try_append_column(aview1(&[4., 5., 6., 7.])).unwrap(); + //a.append_column(aview1(&[4., 5., 6., 7.])).unwrap(); //assert_eq!(a.shape(), &[4, 2]); assert_eq!(a, array![[0., 1., 2., 3.], [4., 5., 6., 7.]]); - a.try_append_array(Axis(0), aview2(&[[5., 5., 4., 4.], [3., 3., 2., 2.]])).unwrap(); + a.append(Axis(0), aview2(&[[5., 5., 4., 4.], [3., 3., 2., 2.]])).unwrap(); println!("{:?}", a); assert_eq!(a, array![[0., 1., 2., 3.], @@ -146,26 +146,26 @@ fn append_array1() { #[test] fn append_array_3d() { let mut a = Array::zeros((0, 2, 2)); - a.try_append_array(Axis(0), array![[[0, 1], [2, 3]]].view()).unwrap(); + a.append(Axis(0), array![[[0, 1], [2, 3]]].view()).unwrap(); println!("{:?}", a); let aa = array![[[51, 52], [53, 54]], [[55, 56], [57, 58]]]; let av = aa.view(); println!("Send {:?} to append", av); - a.try_append_array(Axis(0), av.clone()).unwrap(); + a.append(Axis(0), av.clone()).unwrap(); a.swap_axes(0, 1); let aa = array![[[71, 72], [73, 74]], [[75, 76], [77, 78]]]; let mut av = aa.view(); av.swap_axes(0, 1); println!("Send {:?} to append", av); - a.try_append_array(Axis(1), av.clone()).unwrap(); + a.append(Axis(1), av.clone()).unwrap(); println!("{:?}", a); let aa = array![[[81, 82], [83, 84]], [[85, 86], [87, 88]]]; let mut av = aa.view(); av.swap_axes(0, 1); println!("Send {:?} to append", av); - a.try_append_array(Axis(1), av).unwrap(); + a.append(Axis(1), av).unwrap(); println!("{:?}", a); assert_eq!(a, array![[[0, 1], @@ -190,9 +190,9 @@ fn test_append_2d() { let mut a = Array::zeros((0, 4)); let ones = ArrayView::from(&[1.; 12]).into_shape((3, 4)).unwrap(); let zeros = ArrayView::from(&[0.; 8]).into_shape((2, 4)).unwrap(); - a.try_append_array(Axis(0), ones).unwrap(); - a.try_append_array(Axis(0), zeros).unwrap(); - a.try_append_array(Axis(0), ones).unwrap(); + a.append(Axis(0), ones).unwrap(); + a.append(Axis(0), zeros).unwrap(); + a.append(Axis(0), ones).unwrap(); println!("{:?}", a); assert_eq!(a.shape(), &[8, 4]); for (i, row) in a.rows().into_iter().enumerate() { @@ -204,9 +204,9 @@ fn test_append_2d() { a = a.reversed_axes(); let ones = ones.reversed_axes(); let zeros = zeros.reversed_axes(); - a.try_append_array(Axis(1), ones).unwrap(); - a.try_append_array(Axis(1), zeros).unwrap(); - a.try_append_array(Axis(1), ones).unwrap(); + a.append(Axis(1), ones).unwrap(); + a.append(Axis(1), zeros).unwrap(); + a.append(Axis(1), ones).unwrap(); println!("{:?}", a); assert_eq!(a.shape(), &[4, 8]); @@ -220,16 +220,16 @@ fn test_append_2d() { fn test_append_middle_axis() { // ensure we can append to Axis(1) by letting it become outermost let mut a = Array::::zeros((3, 0, 2)); - a.try_append_array(Axis(1), Array::from_iter(0..12).into_shape((3, 2, 2)).unwrap().view()).unwrap(); + a.append(Axis(1), Array::from_iter(0..12).into_shape((3, 2, 2)).unwrap().view()).unwrap(); println!("{:?}", a); - a.try_append_array(Axis(1), Array::from_iter(12..24).into_shape((3, 2, 2)).unwrap().view()).unwrap(); + a.append(Axis(1), Array::from_iter(12..24).into_shape((3, 2, 2)).unwrap().view()).unwrap(); println!("{:?}", a); // ensure we can append to Axis(1) by letting it become outermost let mut a = Array::::zeros((3, 1, 2)); - a.try_append_array(Axis(1), Array::from_iter(0..12).into_shape((3, 2, 2)).unwrap().view()).unwrap(); + a.append(Axis(1), Array::from_iter(0..12).into_shape((3, 2, 2)).unwrap().view()).unwrap(); println!("{:?}", a); - a.try_append_array(Axis(1), Array::from_iter(12..24).into_shape((3, 2, 2)).unwrap().view()).unwrap(); + a.append(Axis(1), Array::from_iter(12..24).into_shape((3, 2, 2)).unwrap().view()).unwrap(); println!("{:?}", a); } @@ -237,16 +237,16 @@ fn test_append_middle_axis() { fn test_append_zero_size() { { let mut a = Array::::zeros((0, 0)); - a.try_append_array(Axis(0), aview2(&[[]])).unwrap(); - a.try_append_array(Axis(0), aview2(&[[]])).unwrap(); + a.append(Axis(0), aview2(&[[]])).unwrap(); + a.append(Axis(0), aview2(&[[]])).unwrap(); assert_eq!(a.len(), 0); assert_eq!(a.shape(), &[2, 0]); } { let mut a = Array::::zeros((0, 0)); - a.try_append_array(Axis(1), ArrayView::from(&[]).into_shape((0, 1)).unwrap()).unwrap(); - a.try_append_array(Axis(1), ArrayView::from(&[]).into_shape((0, 1)).unwrap()).unwrap(); + a.append(Axis(1), ArrayView::from(&[]).into_shape((0, 1)).unwrap()).unwrap(); + a.append(Axis(1), ArrayView::from(&[]).into_shape((0, 1)).unwrap()).unwrap(); assert_eq!(a.len(), 0); assert_eq!(a.shape(), &[0, 2]); } From 7d522b9408e643c226444ea4a5ffa47774c3a404 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 7 Apr 2021 23:37:56 +0200 Subject: [PATCH 315/651] append: Add more append tests for negative stride arrays --- tests/append.rs | 98 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 2 deletions(-) diff --git a/tests/append.rs b/tests/append.rs index 0e24a744d..a67c4e5de 100644 --- a/tests/append.rs +++ b/tests/append.rs @@ -31,11 +31,19 @@ fn append_row_wrong_layout() { a.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[2, 4]); - //assert_eq!(a.append_column(aview1(&[1., 2.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); - assert_eq!(a, array![[0., 1., 2., 3.], [4., 5., 6., 7.]]); + assert_eq!(a.strides(), &[4, 1]); + + // Changing the memory layout to fit the next append + let mut a2 = a.clone(); + a2.append_column(aview1(&[1., 2.])).unwrap(); + assert_eq!(a2, + array![[0., 1., 2., 3., 1.], + [4., 5., 6., 7., 2.]]); + assert_eq!(a2.strides(), &[1, 2]); + // Clone the array @@ -49,6 +57,92 @@ fn append_row_wrong_layout() { [4., 5., 6., 7., 2.]]); } +#[test] +fn append_row_neg_stride_1() { + let mut a = Array::zeros((0, 4)); + a.append_row(aview1(&[0., 1., 2., 3.])).unwrap(); + a.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + assert_eq!(a.shape(), &[2, 4]); + + assert_eq!(a, + array![[0., 1., 2., 3.], + [4., 5., 6., 7.]]); + assert_eq!(a.strides(), &[4, 1]); + + a.invert_axis(Axis(0)); + + // Changing the memory layout to fit the next append + let mut a2 = a.clone(); + println!("a = {:?}", a); + println!("a2 = {:?}", a2); + a2.append_column(aview1(&[1., 2.])).unwrap(); + assert_eq!(a2, + array![[4., 5., 6., 7., 1.], + [0., 1., 2., 3., 2.]]); + assert_eq!(a2.strides(), &[1, 2]); + + a.invert_axis(Axis(1)); + let mut a3 = a.clone(); + a3.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + assert_eq!(a3, + array![[7., 6., 5., 4.], + [3., 2., 1., 0.], + [4., 5., 6., 7.]]); + assert_eq!(a3.strides(), &[4, 1]); + + a.invert_axis(Axis(0)); + let mut a4 = a.clone(); + a4.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + assert_eq!(a4, + array![[3., 2., 1., 0.], + [7., 6., 5., 4.], + [4., 5., 6., 7.]]); + assert_eq!(a4.strides(), &[4, -1]); +} + +#[test] +fn append_row_neg_stride_2() { + let mut a = Array::zeros((0, 4)); + a.append_row(aview1(&[0., 1., 2., 3.])).unwrap(); + a.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + assert_eq!(a.shape(), &[2, 4]); + + assert_eq!(a, + array![[0., 1., 2., 3.], + [4., 5., 6., 7.]]); + assert_eq!(a.strides(), &[4, 1]); + + a.invert_axis(Axis(1)); + + // Changing the memory layout to fit the next append + let mut a2 = a.clone(); + println!("a = {:?}", a); + println!("a2 = {:?}", a2); + a2.append_column(aview1(&[1., 2.])).unwrap(); + assert_eq!(a2, + array![[3., 2., 1., 0., 1.], + [7., 6., 5., 4., 2.]]); + assert_eq!(a2.strides(), &[1, 2]); + + a.invert_axis(Axis(0)); + let mut a3 = a.clone(); + a3.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + assert_eq!(a3, + array![[7., 6., 5., 4.], + [3., 2., 1., 0.], + [4., 5., 6., 7.]]); + assert_eq!(a3.strides(), &[4, 1]); + + a.invert_axis(Axis(1)); + let mut a4 = a.clone(); + a4.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + assert_eq!(a4, + array![[4., 5., 6., 7.], + [0., 1., 2., 3.], + [4., 5., 6., 7.]]); + assert_eq!(a4.strides(), &[4, 1]); +} + #[test] fn append_row_error() { let mut a = Array::zeros((3, 4)); From d4819ccb3f9ab9b6366570b43ed964d306243e94 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 7 Apr 2021 19:11:43 +0200 Subject: [PATCH 316/651] zip: Make sure Layout::tendency is inlinable This method showed up in profiling because it wasn't inlinable. --- src/layout/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/layout/mod.rs b/src/layout/mod.rs index d4271dd77..e7434fbc1 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -58,6 +58,7 @@ impl Layout { /// A simple "score" method which scores positive for preferring C-order, negative for F-order /// Subject to change when we can describe other layouts + #[inline] pub(crate) fn tendency(self) -> i32 { (self.is(CORDER) as i32 - self.is(FORDER) as i32) + (self.is(CPREFER) as i32 - self.is(FPREFER) as i32) From f2fd6c244f7c62b813b8d0897c95b08cbd11bd72 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 7 Apr 2021 19:13:50 +0200 Subject: [PATCH 317/651] zip: Add Zip::and_unchecked and use to skip redundant dimension check This speeds up the benches/append.rs benchmarks by 5-10%. --- src/impl_owned_array.rs | 5 ++++- src/zip/mod.rs | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 319e310a4..597c2d115 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -540,7 +540,10 @@ impl Array data: &mut self.data, }; - Zip::from(tail_view).and(array) + + // Safety: tail_view is constructed to have the same shape as array + Zip::from(tail_view) + .and_unchecked(array) .debug_assert_c_order() .for_each(|to, from| { to.write(from.clone()); diff --git a/src/zip/mod.rs b/src/zip/mod.rs index ffc6a710a..18d07ddfd 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -679,6 +679,26 @@ macro_rules! map_impl { self.build_and(part) } + /// Include the producer `p` in the Zip. + /// + /// ## Safety + /// + /// The caller must ensure that the producer's shape is equal to the Zip's shape. + /// Uses assertions when debug assertions are enabled. + #[allow(unused)] + pub(crate) unsafe fn and_unchecked

(self, p: P) -> Zip<($($p,)* P::Output, ), D> + where P: IntoNdProducer, + { + #[cfg(debug_assertions)] + { + self.and(p) + } + #[cfg(not(debug_assertions))] + { + self.build_and(p.into_producer()) + } + } + /// Include the producer `p` in the Zip, broadcasting if needed. /// /// If their shapes disagree, `rhs` is broadcast to the shape of `self`. From c1a5cebea968367ba6032afd2f5fc1dab3ad0e13 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 8 Apr 2021 20:27:22 +0200 Subject: [PATCH 318/651] stacking: Improve .select() with special case for 1D arrays The 1D case is a simpler gather operation since we only select 1 element per index. Special-case it. The performance increase for this benchmark is that the benchmark runtime changes by -92% with this change. --- benches/append.rs | 11 +++++++++++ src/impl_methods.rs | 37 ++++++++++++++++++++++++++++--------- src/lib.rs | 1 + tests/array.rs | 13 +++++++++++++ 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/benches/append.rs b/benches/append.rs index 5502fa062..1a911a278 100644 --- a/benches/append.rs +++ b/benches/append.rs @@ -22,3 +22,14 @@ fn select_axis1(bench: &mut Bencher) { a.select(Axis(1), &selectable) }); } + +#[bench] +fn select_1d(bench: &mut Bencher) { + let a = Array::::zeros(1024); + let mut selectable = (0..a.len()).step_by(17).collect::>(); + selectable.extend(selectable.clone().iter().rev()); + + bench.iter(|| { + a.select(Axis(0), &selectable) + }); +} diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 3e893fcd8..fccce3179 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -877,16 +877,35 @@ where S: Data, D: RemoveAxis, { - let mut subs = vec![self.view(); indices.len()]; - for (&i, sub) in zip(indices, &mut subs[..]) { - sub.collapse_axis(axis, i); - } - if subs.is_empty() { - let mut dim = self.raw_dim(); - dim.set_axis(axis, 0); - unsafe { Array::from_shape_vec_unchecked(dim, vec![]) } + if self.ndim() == 1 { + // using .len_of(axis) means that we check if `axis` is in bounds too. + let axis_len = self.len_of(axis); + // bounds check the indices first + if let Some(max_index) = indices.iter().cloned().max() { + if max_index >= axis_len { + panic!("ndarray: index {} is out of bounds in array of len {}", + max_index, self.len_of(axis)); + } + } // else: indices empty is ok + let view = self.view().into_dimensionality::().unwrap(); + Array::from_iter(indices.iter().map(move |&index| { + // Safety: bounds checked indexes + unsafe { + view.uget(index).clone() + } + })).into_dimensionality::().unwrap() } else { - concatenate(axis, &subs).unwrap() + let mut subs = vec![self.view(); indices.len()]; + for (&i, sub) in zip(indices, &mut subs[..]) { + sub.collapse_axis(axis, i); + } + if subs.is_empty() { + let mut dim = self.raw_dim(); + dim.set_axis(axis, 0); + unsafe { Array::from_shape_vec_unchecked(dim, vec![]) } + } else { + concatenate(axis, &subs).unwrap() + } } } diff --git a/src/lib.rs b/src/lib.rs index 6a3f0a76b..06aca0150 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ clippy::unreadable_literal, clippy::manual_map, // is not an error clippy::while_let_on_iterator, // is not an error + clippy::from_iter_instead_of_collect, // using from_iter is good style )] #![cfg_attr(not(feature = "std"), no_std)] diff --git a/tests/array.rs b/tests/array.rs index 38d2711aa..976824dfe 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -709,6 +709,19 @@ fn test_select() { assert_abs_diff_eq!(c, c_target); } +#[test] +fn test_select_1d() { + let x = arr1(&[0, 1, 2, 3, 4, 5, 6]); + let r1 = x.select(Axis(0), &[1, 3, 4, 2, 2, 5]); + assert_eq!(r1, arr1(&[1, 3, 4, 2, 2, 5])); + // select nothing + let r2 = x.select(Axis(0), &[]); + assert_eq!(r2, arr1(&[])); + // select nothing from empty + let r3 = r2.select(Axis(0), &[]); + assert_eq!(r3, arr1(&[])); +} + #[test] fn diag() { let d = arr2(&[[1., 2., 3.0f32]]).into_diag(); From 8d44bbc49d712dde731d0227c0852aea2cb73512 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 11 Apr 2021 15:46:51 +0200 Subject: [PATCH 319/651] append: Edits and clarifications in append comments Co-authored-by: Jim Turner --- src/impl_owned_array.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 597c2d115..f0689c146 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -86,12 +86,12 @@ impl Array { /// Appending automatically changes memory layout of the array so that it is appended to /// along the "growing axis". /// - /// Ensure appending is efficient by for example starting appending an empty array and + /// Ensure appending is efficient by, for example, appending to an empty array and then /// always appending along the same axis. For rows, ndarray's default layout is efficient for /// appending. /// /// Notice that an empty array (where it has an axis of length zero) is the simplest starting - /// point. The amortized average complexity of the append is O(m) where *m* is the length of + /// point. When repeatedly appending to a single axis, the amortized average complexity of each append is O(m), where *m* is the length of /// the row. /// /// ```rust @@ -123,12 +123,12 @@ impl Array { /// Appending automatically changes memory layout of the array so that it is appended to /// along the "growing axis". /// - /// Ensure appending is efficient by for example starting appending an empty array and + /// Ensure appending is efficient by, for example, appending to an empty array and then /// always appending along the same axis. For columns, column major ("F") memory layout is /// efficient for appending. /// /// Notice that an empty array (where it has an axis of length zero) is the simplest starting - /// point. The amortized average complexity of the append is O(m) where *m* is the length of + /// point. When repeatedly appending to a single axis, the amortized average complexity of each append is O(m), where *m* is the length of /// the row. /// /// ```rust @@ -222,7 +222,7 @@ impl Array #[cold] fn drop_unreachable_elements_slow(mut self) -> OwnedRepr { // "deconstruct" self; the owned repr releases ownership of all elements and we - // and carry on with raw view methods + // carry on with raw view methods let self_len = self.len(); let data_len = self.data.len(); let data_ptr = self.data.as_nonnull_mut().as_ptr(); @@ -230,7 +230,8 @@ impl Array let mut self_; unsafe { - // Safety: self.data releases ownership of the elements + // Safety: self.data releases ownership of the elements. Any panics below this point + // will result in leaking elements instead of double drops. self_ = self.raw_view_mut(); self.data.set_len(0); } @@ -430,7 +431,7 @@ impl Array } } - // array must be be "full" (have no exterior holes) + // array must be be "full" (contiguous and have no exterior holes) if self.len() != self.data.len() { incompatible_layout = true; } @@ -520,7 +521,7 @@ impl Array tail_view.shape(), tail_view.strides()); } - // Keep track of currently filled lenght of `self.data` and update it + // Keep track of currently filled length of `self.data` and update it // on scope exit (panic or loop finish). struct SetLenOnDrop<'a, A: 'a> { len: usize, @@ -646,4 +647,3 @@ where } } } - From b103515baa57945615536eb98b2369a136bf9645 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 11 Apr 2021 15:47:34 +0200 Subject: [PATCH 320/651] stacking: Update concatenate test Co-authored-by: Jim Turner --- tests/stacking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stacking.rs b/tests/stacking.rs index 2c223fd66..0c4e79c79 100644 --- a/tests/stacking.rs +++ b/tests/stacking.rs @@ -20,7 +20,7 @@ fn concatenating() { [2., 9.]])); let d = concatenate![Axis(0), a.row(0).insert_axis(Axis(1)), aview1(&[9., 9.]).insert_axis(Axis(1))]; - assert_eq!(d.t(), aview2(&[[2., 2., 9., 9.]])); + assert_eq!(d, aview2(&[[2.], [2.], [9.], [9.]])); let res = ndarray::concatenate(Axis(1), &[a.view(), c.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::IncompatibleShape); From 59013cdc7a5a22f0911492ef637231b94c4890da Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 11 Apr 2021 21:41:36 +0200 Subject: [PATCH 321/651] move_into: Implement inner-dimension optimization in move-into If the innermost dimension is contiguous, we can skip it in one go, and save some work in the dropping loop in move_into. Co-authored-by: Jim Turner --- src/impl_owned_array.rs | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index f0689c146..f0ae42c71 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -255,10 +255,6 @@ impl Array debug_assert!(data_ptr <= array_memory_head_ptr); debug_assert!(array_memory_head_ptr <= data_end_ptr); - // iter is a raw pointer iterator traversing self_ in its standard order - let mut iter = Baseiter::new(self_.ptr.as_ptr(), self_.dim, self_.strides); - let mut dropped_elements = 0; - // The idea is simply this: the iterator will yield the elements of self_ in // increasing address order. // @@ -267,6 +263,25 @@ impl Array // // We have to drop elements in the range from `data_ptr` until (not including) // `data_end_ptr`, except those that are produced by `iter`. + + // As an optimization, the innermost axis is removed if it has stride 1, because + // we then have a long stretch of contiguous elements we can skip as one. + let inner_lane_len; + if self_.ndim() > 1 && self_.strides.last_elem() == 1 { + self_.dim.slice_mut().rotate_right(1); + self_.strides.slice_mut().rotate_right(1); + inner_lane_len = self_.dim[0]; + self_.dim[0] = 1; + self_.strides[0] = 1; + } else { + inner_lane_len = 1; + } + + // iter is a raw pointer iterator traversing the array in memory order now with the + // sorted axes. + let mut iter = Baseiter::new(self_.ptr.as_ptr(), self_.dim, self_.strides); + let mut dropped_elements = 0; + let mut last_ptr = data_ptr; while let Some(elem_ptr) = iter.next() { @@ -278,8 +293,8 @@ impl Array last_ptr = last_ptr.add(1); dropped_elements += 1; } - // Next interval will continue one past the current element - last_ptr = elem_ptr.add(1); + // Next interval will continue one past the current lane + last_ptr = elem_ptr.add(inner_lane_len); } while last_ptr < data_end_ptr { From c9dd195b69c6f9c203594b79828a6eb30d2c5fe5 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 11 Apr 2021 21:52:16 +0200 Subject: [PATCH 322/651] append: Add comment explaining the SetLenOnDrop optimization Benchmarks (append benchmarks) show that this is a worthwhile optimization, and that plain self.data.set_len() in the loop performs worse. --- src/impl_owned_array.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index f0ae42c71..94f610b15 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -537,7 +537,9 @@ impl Array } // Keep track of currently filled length of `self.data` and update it - // on scope exit (panic or loop finish). + // on scope exit (panic or loop finish). This "indirect" way to + // write the length is used to help the compiler, the len store to self.data may + // otherwise be mistaken to alias with other stores in the loop. struct SetLenOnDrop<'a, A: 'a> { len: usize, data: &'a mut OwnedRepr, From 3f59442e60e208e2d69a6453604bcd230ba1556b Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 11 Apr 2021 22:12:49 +0200 Subject: [PATCH 323/651] append: Use standard order for non-growing axes in append Imagine that the array-to-append has length 1 along the growing axis. With this memory layout, the resulting assignment when copying the array elements into `self`, can often be a contiguous traversal. Co-authored-by: Jim Turner --- src/impl_owned_array.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 94f610b15..208eee019 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -327,13 +327,14 @@ impl Array // The array will be created with 0 (C) or ndim-1 (F) as the biggest stride // axis. Rearrange the shape so that `growing_axis` is the biggest stride axis // afterwards. - let prefer_f_layout = growing_axis == Axis(ndim - 1); - if !prefer_f_layout { - dim.slice_mut().swap(0, growing_axis.index()); - } - let mut new_array = Self::uninit(dim.set_f(prefer_f_layout)); - if !prefer_f_layout { - new_array.swap_axes(0, growing_axis.index()); + let mut new_array; + if growing_axis == Axis(ndim - 1) { + new_array = Self::uninit(dim.f()); + } else { + dim.slice_mut()[..=growing_axis.index()].rotate_right(1); + new_array = Self::uninit(dim); + new_array.dim.slice_mut()[..=growing_axis.index()].rotate_left(1); + new_array.strides.slice_mut()[..=growing_axis.index()].rotate_left(1); } // self -> old_self. @@ -467,11 +468,13 @@ impl Array // Axis n - 1 is outermost axis res_dim.fortran_strides() } else { - // Default with modification - res_dim.slice_mut().swap(0, axis.index()); + // standard axis order except for the growing axis; + // anticipates that it's likely that `array` has standard order apart from the + // growing axis. + res_dim.slice_mut()[..=axis.index()].rotate_right(1); let mut strides = res_dim.default_strides(); - res_dim.slice_mut().swap(0, axis.index()); - strides.slice_mut().swap(0, axis.index()); + res_dim.slice_mut()[..=axis.index()].rotate_left(1); + strides.slice_mut()[..=axis.index()].rotate_left(1); strides } } else if current_axis_len == 1 { From 1206bd6aacdeb9334202568febcd8261bf16b0dc Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 11 Apr 2021 22:22:49 +0200 Subject: [PATCH 324/651] append: Use positive stride and ignore stride of len 1 axes Fix two bugs that Jim found in how we calculate the new stride for the growing axis. Tests by Jim Turner. Co-authored-by: Jim Turner --- src/impl_owned_array.rs | 8 +++++--- tests/append.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 208eee019..bef2aa60d 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -480,9 +480,11 @@ impl Array } else if current_axis_len == 1 { // This is the outermost/longest stride axis; so we find the max across the other axes let new_stride = self.axes().fold(1, |acc, ax| { - if ax.axis == axis { acc } else { - let this_ax = ax.len as isize * ax.stride; - if this_ax.abs() > acc { this_ax } else { acc } + if ax.axis == axis || ax.len <= 1 { + acc + } else { + let this_ax = ax.len as isize * ax.stride.abs(); + if this_ax > acc { this_ax } else { acc } } }); let mut strides = self.strides.clone(); diff --git a/tests/append.rs b/tests/append.rs index a67c4e5de..6306b2ecf 100644 --- a/tests/append.rs +++ b/tests/append.rs @@ -345,3 +345,34 @@ fn test_append_zero_size() { assert_eq!(a.shape(), &[0, 2]); } } + +#[test] +fn append_row_neg_stride_3() { + let mut a = Array::zeros((0, 4)); + a.append_row(aview1(&[0., 1., 2., 3.])).unwrap(); + a.invert_axis(Axis(1)); + a.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + assert_eq!(a.shape(), &[2, 4]); + assert_eq!(a, array![[3., 2., 1., 0.], [4., 5., 6., 7.]]); + assert_eq!(a.strides(), &[4, -1]); +} + +#[test] +fn append_row_ignore_strides_length_one_axes() { + let strides = &[0, 1, 10, 20]; + for invert in &[vec![], vec![0], vec![1], vec![0, 1]] { + for &stride0 in strides { + for &stride1 in strides { + let mut a = + Array::from_shape_vec([1, 1].strides([stride0, stride1]), vec![0.]).unwrap(); + for &ax in invert { + a.invert_axis(Axis(ax)); + } + a.append_row(aview1(&[1.])).unwrap(); + assert_eq!(a.shape(), &[2, 1]); + assert_eq!(a, array![[0.], [1.]]); + assert_eq!(a.stride_of(Axis(0)), 1); + } + } + } +} From d946360ada25b1218be1a61afb67fd0b3818d46f Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 13 Apr 2021 21:07:56 +0200 Subject: [PATCH 325/651] append: performance optimize stride check This .max_by_key() call showed up in profiling, and this change improves the select_axis0 benchmark by reducing runtime by 15% --- src/impl_owned_array.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index bef2aa60d..a795a354a 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -438,12 +438,19 @@ impl Array // array must be empty or have `axis` as the outermost (longest stride) axis if !self_is_empty && current_axis_len > 1 { // `axis` must be max stride axis or equal to its stride - let max_axis = self.axes().max_by_key(|ax| ax.stride.abs()).unwrap(); - if max_axis.axis != axis && max_axis.stride.abs() > self.stride_of(axis) { - incompatible_layout = true; - } - if self.stride_of(axis) < 0 { + let axis_stride = self.stride_of(axis); + if axis_stride < 0 { incompatible_layout = true; + } else { + for ax in self.axes() { + if ax.axis == axis { + continue; + } + if ax.len > 1 && ax.stride.abs() > axis_stride { + incompatible_layout = true; + break; + } + } } } From 47626e400ab5fdf3786a6f469e907fddef69d8e2 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Thu, 15 Apr 2021 15:26:12 -0400 Subject: [PATCH 326/651] Add logo to docs (#981) --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 06aca0150..a4c59eb66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ // except according to those terms. #![crate_name = "ndarray"] #![doc(html_root_url = "https://docs.rs/ndarray/0.15/")] +#![doc(html_logo_url = "https://rust-ndarray.github.io/images/rust-ndarray_logo.svg")] #![allow( clippy::many_single_char_names, clippy::deref_addrof, From a5a283d27ae09dfaf5f51de285982d4c2bd87388 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 16 Jan 2021 20:22:58 +0100 Subject: [PATCH 327/651] API: Add must_use on slice_axis methods These return views (don't edit the array in place), so must_use is appropriate here. --- src/impl_methods.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index fccce3179..3371c5317 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -498,6 +498,7 @@ where /// /// **Panics** if an index is out of bounds or step size is zero.
/// **Panics** if `axis` is out of bounds. + #[must_use = "slice_axis returns an array view with the sliced result"] pub fn slice_axis(&self, axis: Axis, indices: Slice) -> ArrayView<'_, A, D> where S: Data, @@ -511,6 +512,7 @@ where /// /// **Panics** if an index is out of bounds or step size is zero.
/// **Panics** if `axis` is out of bounds. + #[must_use = "slice_axis_mut returns an array view with the sliced result"] pub fn slice_axis_mut(&mut self, axis: Axis, indices: Slice) -> ArrayViewMut<'_, A, D> where S: DataMut, From 73ffe90810b0a6d9f905a41447a80069f1e2a08b Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 15 Jan 2021 20:34:32 +0100 Subject: [PATCH 328/651] API: Move Layout consts into the impl Layout is fully pub (but hidden) for legacy reasons - used from NdProducer trait. It's not a stable feature. --- src/layout/mod.rs | 72 ++++++++++++++++++++++++----------------------- src/zip/mod.rs | 12 ++++---- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/src/layout/mod.rs b/src/layout/mod.rs index e7434fbc1..9eecf016d 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -9,6 +9,11 @@ mod layoutfmt; pub struct Layout(u32); impl Layout { + pub(crate) const CORDER: u32 = 0b01; + pub(crate) const FORDER: u32 = 0b10; + pub(crate) const CPREFER: u32 = 0b0100; + pub(crate) const FPREFER: u32 = 0b1000; + #[inline(always)] pub(crate) fn is(self, flag: u32) -> bool { self.0 & flag != 0 @@ -33,22 +38,22 @@ impl Layout { #[inline(always)] pub(crate) fn c() -> Layout { - Layout(CORDER | CPREFER) + Layout(Layout::CORDER | Layout::CPREFER) } #[inline(always)] pub(crate) fn f() -> Layout { - Layout(FORDER | FPREFER) + Layout(Layout::FORDER | Layout::FPREFER) } #[inline(always)] pub(crate) fn cpref() -> Layout { - Layout(CPREFER) + Layout(Layout::CPREFER) } #[inline(always)] pub(crate) fn fpref() -> Layout { - Layout(FPREFER) + Layout(Layout::FPREFER) } #[inline(always)] @@ -60,17 +65,12 @@ impl Layout { /// Subject to change when we can describe other layouts #[inline] pub(crate) fn tendency(self) -> i32 { - (self.is(CORDER) as i32 - self.is(FORDER) as i32) + - (self.is(CPREFER) as i32 - self.is(FPREFER) as i32) + (self.is(Layout::CORDER) as i32 - self.is(Layout::FORDER) as i32) + + (self.is(Layout::CPREFER) as i32 - self.is(Layout::FPREFER) as i32) } } -pub const CORDER: u32 = 0b01; -pub const FORDER: u32 = 0b10; -pub const CPREFER: u32 = 0b0100; -pub const FPREFER: u32 = 0b1000; - #[cfg(test)] mod tests { @@ -83,10 +83,11 @@ mod tests { type M0 = Array0; macro_rules! assert_layouts { - ($mat:expr, $($layout:expr),*) => {{ + ($mat:expr, $($layout:ident),*) => {{ let layout = $mat.view().layout(); $( - assert!(layout.is($layout), "Assertion failed: array {:?} is not layout {}", + assert!(layout.is(Layout::$layout), + "Assertion failed: array {:?} is not layout {}", $mat, stringify!($layout)); )* @@ -94,10 +95,11 @@ mod tests { } macro_rules! assert_not_layouts { - ($mat:expr, $($layout:expr),*) => {{ + ($mat:expr, $($layout:ident),*) => {{ let layout = $mat.view().layout(); $( - assert!(!layout.is($layout), "Assertion failed: array {:?} show not have layout {}", + assert!(!layout.is(Layout::$layout), + "Assertion failed: array {:?} show not have layout {}", $mat, stringify!($layout)); )* @@ -110,10 +112,10 @@ mod tests { let b = M::zeros((5, 5).f()); let ac = a.view().layout(); let af = b.view().layout(); - assert!(ac.is(CORDER) && ac.is(CPREFER)); - assert!(!ac.is(FORDER) && !ac.is(FPREFER)); - assert!(!af.is(CORDER) && !af.is(CPREFER)); - assert!(af.is(FORDER) && af.is(FPREFER)); + assert!(ac.is(Layout::CORDER) && ac.is(Layout::CPREFER)); + assert!(!ac.is(Layout::FORDER) && !ac.is(Layout::FPREFER)); + assert!(!af.is(Layout::CORDER) && !af.is(Layout::CPREFER)); + assert!(af.is(Layout::FORDER) && af.is(Layout::FPREFER)); } #[test] @@ -152,10 +154,10 @@ mod tests { let v1 = a.slice(s![1.., ..]).layout(); let v2 = a.slice(s![.., 1..]).layout(); - assert!(v1.is(CORDER) && v1.is(CPREFER)); - assert!(!v1.is(FORDER) && !v1.is(FPREFER)); - assert!(!v2.is(CORDER) && v2.is(CPREFER)); - assert!(!v2.is(FORDER) && !v2.is(FPREFER)); + assert!(v1.is(Layout::CORDER) && v1.is(Layout::CPREFER)); + assert!(!v1.is(Layout::FORDER) && !v1.is(Layout::FPREFER)); + assert!(!v2.is(Layout::CORDER) && v2.is(Layout::CPREFER)); + assert!(!v2.is(Layout::FORDER) && !v2.is(Layout::FPREFER)); } let b = M::zeros((5, 5).f()); @@ -164,10 +166,10 @@ mod tests { let v1 = b.slice(s![1.., ..]).layout(); let v2 = b.slice(s![.., 1..]).layout(); - assert!(!v1.is(CORDER) && !v1.is(CPREFER)); - assert!(!v1.is(FORDER) && v1.is(FPREFER)); - assert!(!v2.is(CORDER) && !v2.is(CPREFER)); - assert!(v2.is(FORDER) && v2.is(FPREFER)); + assert!(!v1.is(Layout::CORDER) && !v1.is(Layout::CPREFER)); + assert!(!v1.is(Layout::FORDER) && v1.is(Layout::FPREFER)); + assert!(!v2.is(Layout::CORDER) && !v2.is(Layout::CPREFER)); + assert!(v2.is(Layout::FORDER) && v2.is(Layout::FPREFER)); } } @@ -206,10 +208,10 @@ mod tests { let v1 = a.slice(s![..;2, ..]).layout(); let v2 = a.slice(s![.., ..;2]).layout(); - assert!(!v1.is(CORDER) && v1.is(CPREFER)); - assert!(!v1.is(FORDER) && !v1.is(FPREFER)); - assert!(!v2.is(CORDER) && !v2.is(CPREFER)); - assert!(!v2.is(FORDER) && !v2.is(FPREFER)); + assert!(!v1.is(Layout::CORDER) && v1.is(Layout::CPREFER)); + assert!(!v1.is(Layout::FORDER) && !v1.is(Layout::FPREFER)); + assert!(!v2.is(Layout::CORDER) && !v2.is(Layout::CPREFER)); + assert!(!v2.is(Layout::FORDER) && !v2.is(Layout::FPREFER)); } let b = M::zeros((5, 5).f()); @@ -217,10 +219,10 @@ mod tests { let v1 = b.slice(s![..;2, ..]).layout(); let v2 = b.slice(s![.., ..;2]).layout(); - assert!(!v1.is(CORDER) && !v1.is(CPREFER)); - assert!(!v1.is(FORDER) && !v1.is(FPREFER)); - assert!(!v2.is(CORDER) && !v2.is(CPREFER)); - assert!(!v2.is(FORDER) && v2.is(FPREFER)); + assert!(!v1.is(Layout::CORDER) && !v1.is(Layout::CPREFER)); + assert!(!v1.is(Layout::FORDER) && !v1.is(Layout::FPREFER)); + assert!(!v2.is(Layout::CORDER) && !v2.is(Layout::CPREFER)); + assert!(!v2.is(Layout::FORDER) && v2.is(Layout::FPREFER)); } } } diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 18d07ddfd..07fcbb062 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -20,7 +20,6 @@ use crate::Layout; use crate::partial::Partial; use crate::indexes::{indices, Indices}; -use crate::layout::{CORDER, FORDER}; use crate::split_at::{SplitPreference, SplitAt}; pub use self::ndproducer::{NdProducer, IntoNdProducer, Offset}; @@ -272,7 +271,8 @@ where } fn prefer_f(&self) -> bool { - !self.layout.is(CORDER) && (self.layout.is(FORDER) || self.layout_tendency < 0) + !self.layout.is(Layout::CORDER) && + (self.layout.is(Layout::FORDER) || self.layout_tendency < 0) } /// Return an *approximation* to the max stride axis; if @@ -310,7 +310,7 @@ where { if self.dimension.ndim() == 0 { function(acc, unsafe { self.parts.as_ref(self.parts.as_ptr()) }) - } else if self.layout.is(CORDER | FORDER) { + } else if self.layout.is(Layout::CORDER | Layout::FORDER) { self.for_each_core_contiguous(acc, function) } else { self.for_each_core_strided(acc, function) @@ -322,7 +322,7 @@ where F: FnMut(Acc, P::Item) -> FoldWhile, P: ZippableTuple, { - debug_assert!(self.layout.is(CORDER | FORDER)); + debug_assert!(self.layout.is(Layout::CORDER | Layout::FORDER)); let size = self.dimension.size(); let ptrs = self.parts.as_ptr(); let inner_strides = self.parts.contiguous_stride(); @@ -440,7 +440,7 @@ where // Method placement: only used for binary Zip at the moment. #[inline] pub(crate) fn debug_assert_c_order(self) -> Self { - debug_assert!(self.layout.is(CORDER) || self.layout_tendency >= 0 || + debug_assert!(self.layout.is(Layout::CORDER) || self.layout_tendency >= 0 || self.dimension.slice().iter().filter(|&&d| d > 1).count() <= 1, "Assertion failed: traversal is not c-order or 1D for \ layout {:?}, tendency {}, dimension {:?}", @@ -839,7 +839,7 @@ macro_rules! map_impl { // debug assert that the output is contiguous in the memory layout we need if cfg!(debug_assertions) { let out_layout = output.layout(); - assert!(out_layout.is(CORDER | FORDER)); + assert!(out_layout.is(Layout::CORDER | Layout::FORDER)); assert!( (self.layout_tendency <= 0 && out_layout.tendency() <= 0) || (self.layout_tendency >= 0 && out_layout.tendency() >= 0), From d54fa806a66f530749cf053192c40a22f8fe03d6 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 15 Jan 2021 21:40:30 +0100 Subject: [PATCH 329/651] shape: Add internal constructor from_shape_trusted_iter_unchecked --- src/impl_constructors.rs | 22 ++++++++++++++++++++++ src/impl_methods.rs | 22 +++++++++------------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index d082a5ce3..3336ec336 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -29,6 +29,7 @@ use crate::indices; #[cfg(feature = "std")] use crate::iterators::to_vec; use crate::iterators::to_vec_mapped; +use crate::iterators::TrustedIterator; use crate::StrideShape; #[cfg(feature = "std")] use crate::{geomspace, linspace, logspace}; @@ -495,6 +496,27 @@ where ArrayBase::from_data_ptr(DataOwned::new(v), ptr).with_strides_dim(strides, dim) } + /// Creates an array from an iterator, mapped by `map` and interpret it according to the + /// provided shape and strides. + /// + /// # Safety + /// + /// See from_shape_vec_unchecked + pub(crate) unsafe fn from_shape_trusted_iter_unchecked(shape: Sh, iter: I, map: F) + -> Self + where + Sh: Into>, + I: TrustedIterator + ExactSizeIterator, + F: FnMut(I::Item) -> A, + { + let shape = shape.into(); + let dim = shape.dim; + let strides = shape.strides.strides_for_dim(&dim); + let v = to_vec_mapped(iter, map); + Self::from_vec_dim_stride_unchecked(dim, strides, v) + } + + /// Create an array with uninitalized elements, shape `shape`. /// /// The uninitialized elements of type `A` are represented by the type `MaybeUninit
`, diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 3371c5317..9ef4277de 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2226,17 +2226,14 @@ where A: 'a, S: Data, { - if let Some(slc) = self.as_slice_memory_order() { - let v = crate::iterators::to_vec_mapped(slc.iter(), f); - unsafe { - ArrayBase::from_shape_vec_unchecked( + unsafe { + if let Some(slc) = self.as_slice_memory_order() { + ArrayBase::from_shape_trusted_iter_unchecked( self.dim.clone().strides(self.strides.clone()), - v, - ) + slc.iter(), f) + } else { + ArrayBase::from_shape_trusted_iter_unchecked(self.dim.clone(), self.iter(), f) } - } else { - let v = crate::iterators::to_vec_mapped(self.iter(), f); - unsafe { ArrayBase::from_shape_vec_unchecked(self.dim.clone(), v) } } } @@ -2256,11 +2253,10 @@ where if self.is_contiguous() { let strides = self.strides.clone(); let slc = self.as_slice_memory_order_mut().unwrap(); - let v = crate::iterators::to_vec_mapped(slc.iter_mut(), f); - unsafe { ArrayBase::from_shape_vec_unchecked(dim.strides(strides), v) } + unsafe { ArrayBase::from_shape_trusted_iter_unchecked(dim.strides(strides), + slc.iter_mut(), f) } } else { - let v = crate::iterators::to_vec_mapped(self.iter_mut(), f); - unsafe { ArrayBase::from_shape_vec_unchecked(dim, v) } + unsafe { ArrayBase::from_shape_trusted_iter_unchecked(dim, self.iter_mut(), f) } } } From 7079aedf634c76892c858c7976ac13524e4cf5b7 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 17 Apr 2021 11:34:25 +0200 Subject: [PATCH 330/651] intoiter: Move inner part of drop unreachable to its own function The IntoIter iterator will reuse the drop unreachable code from move_into, so we move the needed part into a separate function. --- src/impl_owned_array.rs | 151 +++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 73 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index a795a354a..8d8963c1a 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -223,89 +223,18 @@ impl Array fn drop_unreachable_elements_slow(mut self) -> OwnedRepr { // "deconstruct" self; the owned repr releases ownership of all elements and we // carry on with raw view methods - let self_len = self.len(); let data_len = self.data.len(); let data_ptr = self.data.as_nonnull_mut().as_ptr(); - let mut self_; - unsafe { // Safety: self.data releases ownership of the elements. Any panics below this point // will result in leaking elements instead of double drops. - self_ = self.raw_view_mut(); + let self_ = self.raw_view_mut(); self.data.set_len(0); - } - - // uninvert axes where needed, so that stride > 0 - for i in 0..self_.ndim() { - if self_.stride_of(Axis(i)) < 0 { - self_.invert_axis(Axis(i)); - } + drop_unreachable_raw(self_, data_ptr, data_len); } - // Sort axes to standard order, Axis(0) has biggest stride and Axis(n - 1) least stride - // Note that self_ has holes, so self_ is not C-contiguous - sort_axes_in_default_order(&mut self_); - - unsafe { - // with uninverted axes this is now the element with lowest address - let array_memory_head_ptr = self_.ptr.as_ptr(); - let data_end_ptr = data_ptr.add(data_len); - debug_assert!(data_ptr <= array_memory_head_ptr); - debug_assert!(array_memory_head_ptr <= data_end_ptr); - - // The idea is simply this: the iterator will yield the elements of self_ in - // increasing address order. - // - // The pointers produced by the iterator are those that we *do not* touch. - // The pointers *not mentioned* by the iterator are those we have to drop. - // - // We have to drop elements in the range from `data_ptr` until (not including) - // `data_end_ptr`, except those that are produced by `iter`. - - // As an optimization, the innermost axis is removed if it has stride 1, because - // we then have a long stretch of contiguous elements we can skip as one. - let inner_lane_len; - if self_.ndim() > 1 && self_.strides.last_elem() == 1 { - self_.dim.slice_mut().rotate_right(1); - self_.strides.slice_mut().rotate_right(1); - inner_lane_len = self_.dim[0]; - self_.dim[0] = 1; - self_.strides[0] = 1; - } else { - inner_lane_len = 1; - } - - // iter is a raw pointer iterator traversing the array in memory order now with the - // sorted axes. - let mut iter = Baseiter::new(self_.ptr.as_ptr(), self_.dim, self_.strides); - let mut dropped_elements = 0; - - let mut last_ptr = data_ptr; - - while let Some(elem_ptr) = iter.next() { - // The interval from last_ptr up until (not including) elem_ptr - // should now be dropped. This interval may be empty, then we just skip this loop. - while last_ptr != elem_ptr { - debug_assert!(last_ptr < data_end_ptr); - std::ptr::drop_in_place(last_ptr); - last_ptr = last_ptr.add(1); - dropped_elements += 1; - } - // Next interval will continue one past the current lane - last_ptr = elem_ptr.add(inner_lane_len); - } - - while last_ptr < data_end_ptr { - std::ptr::drop_in_place(last_ptr); - last_ptr = last_ptr.add(1); - dropped_elements += 1; - } - - assert_eq!(data_len, dropped_elements + self_len, - "Internal error: inconsistency in move_into"); - } self.data } @@ -594,6 +523,82 @@ impl Array } } +/// This drops all "unreachable" elements in `self_` given the data pointer and data length. +/// +/// # Safety +/// +/// This is an internal function for use by move_into and IntoIter only, safety invariants may need +/// to be upheld across the calls from those implementations. +pub(crate) unsafe fn drop_unreachable_raw(mut self_: RawArrayViewMut, data_ptr: *mut A, data_len: usize) +where + D: Dimension, +{ + let self_len = self_.len(); + + for i in 0..self_.ndim() { + if self_.stride_of(Axis(i)) < 0 { + self_.invert_axis(Axis(i)); + } + } + sort_axes_in_default_order(&mut self_); + // with uninverted axes this is now the element with lowest address + let array_memory_head_ptr = self_.ptr.as_ptr(); + let data_end_ptr = data_ptr.add(data_len); + debug_assert!(data_ptr <= array_memory_head_ptr); + debug_assert!(array_memory_head_ptr <= data_end_ptr); + + // The idea is simply this: the iterator will yield the elements of self_ in + // increasing address order. + // + // The pointers produced by the iterator are those that we *do not* touch. + // The pointers *not mentioned* by the iterator are those we have to drop. + // + // We have to drop elements in the range from `data_ptr` until (not including) + // `data_end_ptr`, except those that are produced by `iter`. + + // As an optimization, the innermost axis is removed if it has stride 1, because + // we then have a long stretch of contiguous elements we can skip as one. + let inner_lane_len; + if self_.ndim() > 1 && self_.strides.last_elem() == 1 { + self_.dim.slice_mut().rotate_right(1); + self_.strides.slice_mut().rotate_right(1); + inner_lane_len = self_.dim[0]; + self_.dim[0] = 1; + self_.strides[0] = 1; + } else { + inner_lane_len = 1; + } + + // iter is a raw pointer iterator traversing the array in memory order now with the + // sorted axes. + let mut iter = Baseiter::new(self_.ptr.as_ptr(), self_.dim, self_.strides); + let mut dropped_elements = 0; + + let mut last_ptr = data_ptr; + + while let Some(elem_ptr) = iter.next() { + // The interval from last_ptr up until (not including) elem_ptr + // should now be dropped. This interval may be empty, then we just skip this loop. + while last_ptr != elem_ptr { + debug_assert!(last_ptr < data_end_ptr); + std::ptr::drop_in_place(last_ptr); + last_ptr = last_ptr.add(1); + dropped_elements += 1; + } + // Next interval will continue one past the current lane + last_ptr = elem_ptr.add(inner_lane_len); + } + + while last_ptr < data_end_ptr { + std::ptr::drop_in_place(last_ptr); + last_ptr = last_ptr.add(1); + dropped_elements += 1; + } + + assert_eq!(data_len, dropped_elements + self_len, + "Internal error: inconsistency in move_into"); +} + /// Sort axes to standard order, i.e Axis(0) has biggest stride and Axis(n - 1) least stride /// /// The axes should have stride >= 0 before calling this method. From 65b46d22bdbd41acc5551a2b989212426b56807e Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 17 Apr 2021 11:35:47 +0200 Subject: [PATCH 331/651] intoiter: Add mut ptr accessor to OwnedRepr --- src/data_repr.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/data_repr.rs b/src/data_repr.rs index 8a52f64c4..307ec2357 100644 --- a/src/data_repr.rs +++ b/src/data_repr.rs @@ -53,6 +53,10 @@ impl OwnedRepr { self.ptr.as_ptr() } + pub(crate) fn as_ptr_mut(&self) -> *mut A { + self.ptr.as_ptr() + } + pub(crate) fn as_nonnull_mut(&mut self) -> NonNull { self.ptr } From 297a5a4d8f8a2953c40b42d0a2c74cdb54b4c08a Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 16 Jan 2021 20:25:13 +0100 Subject: [PATCH 332/651] intoiter: Add release method to OwnedRepr --- src/data_repr.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/data_repr.rs b/src/data_repr.rs index 307ec2357..7047f2014 100644 --- a/src/data_repr.rs +++ b/src/data_repr.rs @@ -92,6 +92,13 @@ impl OwnedRepr { self.len = new_len; } + /// Return the length (number of elements in total) + pub(crate) fn release_all_elements(&mut self) -> usize { + let ret = self.len; + self.len = 0; + ret + } + /// Cast self into equivalent repr of other element type /// /// ## Safety From 5766f4bcc495bc1342ea013df9470b5ce96fe5d6 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 16 Jan 2021 20:25:13 +0100 Subject: [PATCH 333/651] intoiter: Implement by-value iterator for owned arrays --- src/iterators/into_iter.rs | 136 +++++++++++++++++++++++++++++++++++++ src/iterators/mod.rs | 3 + tests/iterators.rs | 92 ++++++++++++++++++++++++- 3 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 src/iterators/into_iter.rs diff --git a/src/iterators/into_iter.rs b/src/iterators/into_iter.rs new file mode 100644 index 000000000..cfa48299a --- /dev/null +++ b/src/iterators/into_iter.rs @@ -0,0 +1,136 @@ +// Copyright 2020-2021 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::mem; +use std::ptr::NonNull; + +use crate::imp_prelude::*; +use crate::OwnedRepr; + +use super::Baseiter; +use crate::impl_owned_array::drop_unreachable_raw; + + +/// By-value iterator for an array +pub struct IntoIter +where + D: Dimension, +{ + array_data: OwnedRepr, + inner: Baseiter, + data_len: usize, + /// first memory address of an array element + array_head_ptr: NonNull, + // if true, the array owns elements that are not reachable by indexing + // through all the indices of the dimension. + has_unreachable_elements: bool, +} + +impl IntoIter +where + D: Dimension, +{ + /// Create a new by-value iterator that consumes `array` + pub(crate) fn new(mut array: Array) -> Self { + unsafe { + let array_head_ptr = array.ptr; + let ptr = array.as_mut_ptr(); + let mut array_data = array.data; + let data_len = array_data.release_all_elements(); + debug_assert!(data_len >= array.dim.size()); + let has_unreachable_elements = array.dim.size() != data_len; + let inner = Baseiter::new(ptr, array.dim, array.strides); + + IntoIter { + array_data, + inner, + data_len, + array_head_ptr, + has_unreachable_elements, + } + } + } +} + +impl Iterator for IntoIter { + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + self.inner.next().map(|p| unsafe { p.read() }) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl ExactSizeIterator for IntoIter { + fn len(&self) -> usize { self.inner.len() } +} + +impl Drop for IntoIter +where + D: Dimension +{ + fn drop(&mut self) { + if !self.has_unreachable_elements || mem::size_of::() == 0 || !mem::needs_drop::() { + return; + } + + // iterate til the end + while let Some(_) = self.next() { } + + unsafe { + let data_ptr = self.array_data.as_ptr_mut(); + let view = RawArrayViewMut::new(self.array_head_ptr, self.inner.dim.clone(), + self.inner.strides.clone()); + debug_assert!(self.inner.dim.size() < self.data_len, "data_len {} and dim size {}", + self.data_len, self.inner.dim.size()); + drop_unreachable_raw(view, data_ptr, self.data_len); + } + } +} + +impl IntoIterator for Array +where + D: Dimension +{ + type Item = A; + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIter::new(self) + } +} + +impl IntoIterator for ArcArray +where + D: Dimension, + A: Clone, +{ + type Item = A; + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIter::new(self.into_owned()) + } +} + +impl IntoIterator for CowArray<'_, A, D> +where + D: Dimension, + A: Clone, +{ + type Item = A; + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIter::new(self.into_owned()) + } +} diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 595f0897d..bb618e5be 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -9,6 +9,7 @@ #[macro_use] mod macros; mod chunks; +mod into_iter; pub mod iter; mod lanes; mod windows; @@ -26,6 +27,7 @@ use super::{Dimension, Ix, Ixs}; pub use self::chunks::{ExactChunks, ExactChunksIter, ExactChunksIterMut, ExactChunksMut}; pub use self::lanes::{Lanes, LanesMut}; pub use self::windows::Windows; +pub use self::into_iter::IntoIter; use std::slice::{self, Iter as SliceIter, IterMut as SliceIterMut}; @@ -1465,6 +1467,7 @@ unsafe impl TrustedIterator for ::std::ops::Range {} // FIXME: These indices iter are dubious -- size needs to be checked up front. unsafe impl TrustedIterator for IndicesIter where D: Dimension {} unsafe impl TrustedIterator for IndicesIterF where D: Dimension {} +unsafe impl TrustedIterator for IntoIter where D: Dimension {} /// Like Iterator::collect, but only for trusted length iterators pub fn to_vec(iter: I) -> Vec diff --git a/tests/iterators.rs b/tests/iterators.rs index 4e4bbc666..fb78c0ccc 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -8,7 +8,9 @@ use ndarray::prelude::*; use ndarray::{arr3, aview1, indices, s, Axis, Slice, Zip}; -use itertools::{assert_equal, enumerate}; +use itertools::assert_equal; +use itertools::enumerate; +use std::cell::Cell; macro_rules! assert_panics { ($body:expr) => { @@ -892,3 +894,91 @@ fn test_rfold() { ); } } + +#[test] +fn test_into_iter() { + let a = Array1::from(vec![1, 2, 3, 4]); + let v = a.into_iter().collect::>(); + assert_eq!(v, [1, 2, 3, 4]); +} + +#[test] +fn test_into_iter_2d() { + let a = Array1::from(vec![1, 2, 3, 4]).into_shape((2, 2)).unwrap(); + let v = a.into_iter().collect::>(); + assert_eq!(v, [1, 2, 3, 4]); + + let a = Array1::from(vec![1, 2, 3, 4]).into_shape((2, 2)).unwrap().reversed_axes(); + let v = a.into_iter().collect::>(); + assert_eq!(v, [1, 3, 2, 4]); +} + +#[test] +fn test_into_iter_sliced() { + let (m, n) = (4, 5); + let drops = Cell::new(0); + + for i in 0..m - 1 { + for j in 0..n - 1 { + for i2 in i + 1 .. m { + for j2 in j + 1 .. n { + for invert in 0..3 { + drops.set(0); + let i = i as isize; + let j = j as isize; + let i2 = i2 as isize; + let j2 = j2 as isize; + let mut a = Array1::from_iter(0..(m * n) as i32) + .mapv(|v| DropCount::new(v, &drops)) + .into_shape((m, n)).unwrap(); + a.slice_collapse(s![i..i2, j..j2]); + if invert < a.ndim() { + a.invert_axis(Axis(invert)); + } + + println!("{:?}, {:?}", i..i2, j..j2); + println!("{:?}", a); + let answer = a.iter().cloned().collect::>(); + let v = a.into_iter().collect::>(); + assert_eq!(v, answer); + + assert_eq!(drops.get(), m * n - v.len()); + drop(v); + assert_eq!(drops.get(), m * n); + } + } + } + } + } +} + +/// Helper struct that counts its drops Asserts that it's not dropped twice. Also global number of +/// drops is counted in the cell. +/// +/// Compares equal by its "represented value". +#[derive(Clone, Debug)] +struct DropCount<'a> { + value: i32, + my_drops: usize, + drops: &'a Cell +} + +impl PartialEq for DropCount<'_> { + fn eq(&self, other: &Self) -> bool { + self.value == other.value + } +} + +impl<'a> DropCount<'a> { + fn new(value: i32, drops: &'a Cell) -> Self { + DropCount { value, my_drops: 0, drops } + } +} + +impl Drop for DropCount<'_> { + fn drop(&mut self) { + assert_eq!(self.my_drops, 0); + self.my_drops += 1; + self.drops.set(self.drops.get() + 1); + } +} From 0386ef37de680ebca856094f3cbb48c6f344bcf9 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 1 May 2021 21:39:56 +0200 Subject: [PATCH 334/651] append: Rename append_row/column to push_row/column --- src/impl_owned_array.rs | 12 +++--- tests/append.rs | 96 ++++++++++++++++++++--------------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 8d8963c1a..d528f779a 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -99,15 +99,15 @@ impl Array { /// /// // create an empty array and append /// let mut a = Array::zeros((0, 4)); - /// a.append_row(ArrayView::from(&[ 1., 2., 3., 4.])).unwrap(); - /// a.append_row(ArrayView::from(&[-1., -2., -3., -4.])).unwrap(); + /// a.push_row(ArrayView::from(&[ 1., 2., 3., 4.])).unwrap(); + /// a.push_row(ArrayView::from(&[-1., -2., -3., -4.])).unwrap(); /// /// assert_eq!( /// a, /// array![[ 1., 2., 3., 4.], /// [-1., -2., -3., -4.]]); /// ``` - pub fn append_row(&mut self, row: ArrayView) -> Result<(), ShapeError> + pub fn push_row(&mut self, row: ArrayView) -> Result<(), ShapeError> where A: Clone, { @@ -136,15 +136,15 @@ impl Array { /// /// // create an empty array and append /// let mut a = Array::zeros((2, 0)); - /// a.append_column(ArrayView::from(&[1., 2.])).unwrap(); - /// a.append_column(ArrayView::from(&[-1., -2.])).unwrap(); + /// a.push_column(ArrayView::from(&[1., 2.])).unwrap(); + /// a.push_column(ArrayView::from(&[-1., -2.])).unwrap(); /// /// assert_eq!( /// a, /// array![[1., -1.], /// [2., -2.]]); /// ``` - pub fn append_column(&mut self, column: ArrayView) -> Result<(), ShapeError> + pub fn push_column(&mut self, column: ArrayView) -> Result<(), ShapeError> where A: Clone, { diff --git a/tests/append.rs b/tests/append.rs index 6306b2ecf..9d5b307c4 100644 --- a/tests/append.rs +++ b/tests/append.rs @@ -3,21 +3,21 @@ use ndarray::prelude::*; use ndarray::{ShapeError, ErrorKind}; #[test] -fn append_row() { +fn push_row() { let mut a = Array::zeros((0, 4)); - a.append_row(aview1(&[0., 1., 2., 3.])).unwrap(); - a.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); + a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[2, 4]); assert_eq!(a, array![[0., 1., 2., 3.], [4., 5., 6., 7.]]); - assert_eq!(a.append_row(aview1(&[1.])), + assert_eq!(a.push_row(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); - assert_eq!(a.append_column(aview1(&[1.])), + assert_eq!(a.push_column(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); - assert_eq!(a.append_column(aview1(&[1., 2.])), + assert_eq!(a.push_column(aview1(&[1., 2.])), Ok(())); assert_eq!(a, array![[0., 1., 2., 3., 1.], @@ -25,10 +25,10 @@ fn append_row() { } #[test] -fn append_row_wrong_layout() { +fn push_row_wrong_layout() { let mut a = Array::zeros((0, 4)); - a.append_row(aview1(&[0., 1., 2., 3.])).unwrap(); - a.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); + a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[2, 4]); assert_eq!(a, @@ -38,7 +38,7 @@ fn append_row_wrong_layout() { // Changing the memory layout to fit the next append let mut a2 = a.clone(); - a2.append_column(aview1(&[1., 2.])).unwrap(); + a2.push_column(aview1(&[1., 2.])).unwrap(); assert_eq!(a2, array![[0., 1., 2., 3., 1.], [4., 5., 6., 7., 2.]]); @@ -51,17 +51,17 @@ fn append_row_wrong_layout() { dim[1] = 0; let mut b = Array::zeros(dim); b.append(Axis(1), a.view()).unwrap(); - assert_eq!(b.append_column(aview1(&[1., 2.])), Ok(())); + assert_eq!(b.push_column(aview1(&[1., 2.])), Ok(())); assert_eq!(b, array![[0., 1., 2., 3., 1.], [4., 5., 6., 7., 2.]]); } #[test] -fn append_row_neg_stride_1() { +fn push_row_neg_stride_1() { let mut a = Array::zeros((0, 4)); - a.append_row(aview1(&[0., 1., 2., 3.])).unwrap(); - a.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); + a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[2, 4]); assert_eq!(a, @@ -75,7 +75,7 @@ fn append_row_neg_stride_1() { let mut a2 = a.clone(); println!("a = {:?}", a); println!("a2 = {:?}", a2); - a2.append_column(aview1(&[1., 2.])).unwrap(); + a2.push_column(aview1(&[1., 2.])).unwrap(); assert_eq!(a2, array![[4., 5., 6., 7., 1.], [0., 1., 2., 3., 2.]]); @@ -83,7 +83,7 @@ fn append_row_neg_stride_1() { a.invert_axis(Axis(1)); let mut a3 = a.clone(); - a3.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + a3.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a3, array![[7., 6., 5., 4.], [3., 2., 1., 0.], @@ -92,7 +92,7 @@ fn append_row_neg_stride_1() { a.invert_axis(Axis(0)); let mut a4 = a.clone(); - a4.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + a4.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a4, array![[3., 2., 1., 0.], [7., 6., 5., 4.], @@ -101,10 +101,10 @@ fn append_row_neg_stride_1() { } #[test] -fn append_row_neg_stride_2() { +fn push_row_neg_stride_2() { let mut a = Array::zeros((0, 4)); - a.append_row(aview1(&[0., 1., 2., 3.])).unwrap(); - a.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); + a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[2, 4]); assert_eq!(a, @@ -118,7 +118,7 @@ fn append_row_neg_stride_2() { let mut a2 = a.clone(); println!("a = {:?}", a); println!("a2 = {:?}", a2); - a2.append_column(aview1(&[1., 2.])).unwrap(); + a2.push_column(aview1(&[1., 2.])).unwrap(); assert_eq!(a2, array![[3., 2., 1., 0., 1.], [7., 6., 5., 4., 2.]]); @@ -126,7 +126,7 @@ fn append_row_neg_stride_2() { a.invert_axis(Axis(0)); let mut a3 = a.clone(); - a3.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + a3.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a3, array![[7., 6., 5., 4.], [3., 2., 1., 0.], @@ -135,7 +135,7 @@ fn append_row_neg_stride_2() { a.invert_axis(Axis(1)); let mut a4 = a.clone(); - a4.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + a4.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a4, array![[4., 5., 6., 7.], [0., 1., 2., 3.], @@ -144,14 +144,14 @@ fn append_row_neg_stride_2() { } #[test] -fn append_row_error() { +fn push_row_error() { let mut a = Array::zeros((3, 4)); - assert_eq!(a.append_row(aview1(&[1.])), + assert_eq!(a.push_row(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); - assert_eq!(a.append_column(aview1(&[1.])), + assert_eq!(a.push_column(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); - assert_eq!(a.append_column(aview1(&[1., 2., 3.])), + assert_eq!(a.push_column(aview1(&[1., 2., 3.])), Ok(())); assert_eq!(a.t(), array![[0., 0., 0.], @@ -162,10 +162,10 @@ fn append_row_error() { } #[test] -fn append_row_existing() { +fn push_row_existing() { let mut a = Array::zeros((1, 4)); - a.append_row(aview1(&[0., 1., 2., 3.])).unwrap(); - a.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); + a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[3, 4]); assert_eq!(a, @@ -173,11 +173,11 @@ fn append_row_existing() { [0., 1., 2., 3.], [4., 5., 6., 7.]]); - assert_eq!(a.append_row(aview1(&[1.])), + assert_eq!(a.push_row(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); - assert_eq!(a.append_column(aview1(&[1.])), + assert_eq!(a.push_column(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); - assert_eq!(a.append_column(aview1(&[1., 2., 3.])), + assert_eq!(a.push_column(aview1(&[1., 2., 3.])), Ok(())); assert_eq!(a, array![[0., 0., 0., 0., 1.], @@ -186,15 +186,15 @@ fn append_row_existing() { } #[test] -fn append_row_col_len_1() { +fn push_row_col_len_1() { // Test appending 1 row and then cols from shape 1 x 1 let mut a = Array::zeros((1, 1)); - a.append_row(aview1(&[1.])).unwrap(); // shape 2 x 1 - a.append_column(aview1(&[2., 3.])).unwrap(); // shape 2 x 2 - assert_eq!(a.append_row(aview1(&[1.])), + a.push_row(aview1(&[1.])).unwrap(); // shape 2 x 1 + a.push_column(aview1(&[2., 3.])).unwrap(); // shape 2 x 2 + assert_eq!(a.push_row(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); - //assert_eq!(a.append_row(aview1(&[1., 2.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); - a.append_column(aview1(&[4., 5.])).unwrap(); // shape 2 x 3 + //assert_eq!(a.push_row(aview1(&[1., 2.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); + a.push_column(aview1(&[4., 5.])).unwrap(); // shape 2 x 3 assert_eq!(a.shape(), &[2, 3]); assert_eq!(a, @@ -203,10 +203,10 @@ fn append_row_col_len_1() { } #[test] -fn append_column() { +fn push_column() { let mut a = Array::zeros((4, 0)); - a.append_column(aview1(&[0., 1., 2., 3.])).unwrap(); - a.append_column(aview1(&[4., 5., 6., 7.])).unwrap(); + a.push_column(aview1(&[0., 1., 2., 3.])).unwrap(); + a.push_column(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[4, 2]); assert_eq!(a.t(), @@ -221,7 +221,7 @@ fn append_array1() { println!("{:?}", a); a.append(Axis(0), aview2(&[[4., 5., 6., 7.]])).unwrap(); println!("{:?}", a); - //a.append_column(aview1(&[4., 5., 6., 7.])).unwrap(); + //a.push_column(aview1(&[4., 5., 6., 7.])).unwrap(); //assert_eq!(a.shape(), &[4, 2]); assert_eq!(a, @@ -347,18 +347,18 @@ fn test_append_zero_size() { } #[test] -fn append_row_neg_stride_3() { +fn push_row_neg_stride_3() { let mut a = Array::zeros((0, 4)); - a.append_row(aview1(&[0., 1., 2., 3.])).unwrap(); + a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); a.invert_axis(Axis(1)); - a.append_row(aview1(&[4., 5., 6., 7.])).unwrap(); + a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[2, 4]); assert_eq!(a, array![[3., 2., 1., 0.], [4., 5., 6., 7.]]); assert_eq!(a.strides(), &[4, -1]); } #[test] -fn append_row_ignore_strides_length_one_axes() { +fn push_row_ignore_strides_length_one_axes() { let strides = &[0, 1, 10, 20]; for invert in &[vec![], vec![0], vec![1], vec![0, 1]] { for &stride0 in strides { @@ -368,7 +368,7 @@ fn append_row_ignore_strides_length_one_axes() { for &ax in invert { a.invert_axis(Axis(ax)); } - a.append_row(aview1(&[1.])).unwrap(); + a.push_row(aview1(&[1.])).unwrap(); assert_eq!(a.shape(), &[2, 1]); assert_eq!(a, array![[0.], [1.]]); assert_eq!(a.stride_of(Axis(0)), 1); From cae2544a10c8b220fa87168cf85d364945e50493 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 1 May 2021 21:49:21 +0200 Subject: [PATCH 335/651] append: Add method .push() for appending an array with a dimension less. --- src/impl_owned_array.rs | 60 ++++++++++++++++++++++++++++++++++++++++- tests/append.rs | 25 +++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index d528f779a..952bc08d8 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -278,8 +278,66 @@ impl Array } } + /// Append an array to the array along an axis + /// + /// Where the item to push to the array has one dimension less than the `self` array. This + /// method is equivalent to `self.append(axis, array.insert_axis(axis))`. + /// + /// The axis-to-append-to `axis` must be the array's "growing axis" for this operation + /// to succeed. The growing axis is the outermost or last-visited when elements are visited in + /// memory order: + /// + /// `axis` must be the growing axis of the current array, an axis with length 0 or 1. + /// + /// - This is the 0th axis for standard layout arrays + /// - This is the *n*-1 th axis for fortran layout arrays + /// - If the array is empty (the axis or any other has length 0) or if `axis` + /// has length 1, then the array can always be appended. + /// + /// ***Errors*** with a shape error if the shape of self does not match the array-to-append; + /// all axes *except* the axis along which it being appended matter for this check. + /// + /// The memory layout of the `self` array matters for ensuring that the append is efficient. + /// Appending automatically changes memory layout of the array so that it is appended to + /// along the "growing axis". + /// + /// Ensure appending is efficient by for example starting from an empty array and/or always + /// appending to an array along the same axis. + /// + /// The amortized average complexity of the append, when appending along its growing axis, is + /// O(*m*) where *m* is the length of the row. + /// + /// The memory layout of the argument `array` does not matter to the same extent. + /// + /// ```rust + /// use ndarray::{Array, ArrayView, array, Axis}; + /// + /// // create an empty array and push rows to it + /// let mut a = Array::zeros((0, 4)); + /// let ones = ArrayView::from(&[1.; 4]); + /// let zeros = ArrayView::from(&[0.; 4]); + /// a.push(Axis(0), ones).unwrap(); + /// a.push(Axis(0), zeros).unwrap(); + /// a.push(Axis(0), ones).unwrap(); + /// + /// assert_eq!( + /// a, + /// array![[1., 1., 1., 1.], + /// [0., 0., 0., 0.], + /// [1., 1., 1., 1.]]); + /// ``` + pub fn push(&mut self, axis: Axis, array: ArrayView) + -> Result<(), ShapeError> + where + A: Clone, + D: RemoveAxis, + { + // same-dimensionality conversion + self.append(axis, array.insert_axis(axis).into_dimensionality::().unwrap()) + } + - /// Append an array to the array + /// Append an array to the array along an axis /// /// The axis-to-append-to `axis` must be the array's "growing axis" for this operation /// to succeed. The growing axis is the outermost or last-visited when elements are visited in diff --git a/tests/append.rs b/tests/append.rs index 9d5b307c4..dcebcf50e 100644 --- a/tests/append.rs +++ b/tests/append.rs @@ -376,3 +376,28 @@ fn push_row_ignore_strides_length_one_axes() { } } } + +#[test] +#[should_panic(expected = "IncompatibleShape")] +fn zero_dimensional_error1() { + let mut a = Array::zeros(()).into_dyn(); + a.append(Axis(0), arr0(0).into_dyn().view()).unwrap(); +} + +#[test] +#[should_panic(expected = "IncompatibleShape")] +fn zero_dimensional_error2() { + let mut a = Array::zeros(()).into_dyn(); + a.push(Axis(0), arr0(0).into_dyn().view()).unwrap(); +} + +#[test] +fn zero_dimensional_ok() { + let mut a = Array::zeros(0); + let one = aview0(&1); + let two = aview0(&2); + a.push(Axis(0), two).unwrap(); + a.push(Axis(0), one).unwrap(); + a.push(Axis(0), one).unwrap(); + assert_eq!(a, array![2, 1, 1]); +} From 68c5be02a710e23d02ac8c81c08d12b89611abef Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 1 May 2021 22:01:31 +0200 Subject: [PATCH 336/651] append: Avoid cloning dim values where possible While this has no effect for low-dimensionality arrays, for dyn dim it can save some time. --- src/impl_owned_array.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 952bc08d8..63af63917 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -396,8 +396,10 @@ impl Array } let current_axis_len = self.len_of(axis); - let remaining_shape = self.raw_dim().remove_axis(axis); - let array_rem_shape = array.raw_dim().remove_axis(axis); + let self_dim = self.raw_dim(); + let array_dim = array.raw_dim(); + let remaining_shape = self_dim.remove_axis(axis); + let array_rem_shape = array_dim.remove_axis(axis); if remaining_shape != array_rem_shape { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); @@ -405,9 +407,8 @@ impl Array let len_to_append = array.len(); - let array_shape = array.raw_dim(); - let mut res_dim = self.raw_dim(); - res_dim[axis.index()] += array_shape[axis.index()]; + let mut res_dim = self_dim; + res_dim[axis.index()] += array_dim[axis.index()]; let new_len = dimension::size_of_shape_checked(&res_dim)?; if len_to_append == 0 { @@ -526,7 +527,7 @@ impl Array // With > 0 strides, the current end of data is the correct base pointer for tail_view let tail_ptr = self.data.as_end_nonnull(); - let mut tail_view = RawArrayViewMut::new(tail_ptr, array_shape, tail_strides); + let mut tail_view = RawArrayViewMut::new(tail_ptr, array_dim, tail_strides); if tail_view.ndim() > 1 { sort_axes_in_default_order_tandem(&mut tail_view, &mut array); From 902bba87a740ffbc209854f5bd257956626953eb Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 21 Apr 2021 23:07:33 +0200 Subject: [PATCH 337/651] move_into: Skip dropping if element doesn't have drop --- src/impl_owned_array.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 63af63917..e91a7f61b 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -1,5 +1,6 @@ use alloc::vec::Vec; +use std::mem; use std::mem::MaybeUninit; use rawpointer::PointerExt; @@ -33,7 +34,7 @@ impl Array { /// assert_eq!(scalar, Foo); /// ``` pub fn into_scalar(self) -> A { - let size = ::std::mem::size_of::(); + let size = mem::size_of::(); if size == 0 { // Any index in the `Vec` is fine since all elements are identical. self.data.into_vec().remove(0) @@ -208,7 +209,7 @@ impl Array let data_len = self.data.len(); let has_unreachable_elements = self_len != data_len; - if !has_unreachable_elements || std::mem::size_of::() == 0 { + if !has_unreachable_elements || mem::size_of::() == 0 || !mem::needs_drop::() { unsafe { self.data.set_len(0); } From 31c71da59472dd7a6f76185a563d35ce77e80b72 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 17 Apr 2021 13:52:01 +0200 Subject: [PATCH 338/651] order: Add enum Order --- src/lib.rs | 2 ++ src/order.rs | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 src/order.rs diff --git a/src/lib.rs b/src/lib.rs index a4c59eb66..d4f721224 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -145,6 +145,7 @@ pub use crate::dimension::IxDynImpl; pub use crate::dimension::NdIndex; pub use crate::error::{ErrorKind, ShapeError}; pub use crate::indexes::{indices, indices_of}; +pub use crate::order::Order; pub use crate::slice::{ MultiSliceArg, NewAxis, Slice, SliceArg, SliceInfo, SliceInfoElem, SliceNextDim, }; @@ -202,6 +203,7 @@ mod linspace; mod logspace; mod math_cell; mod numeric_util; +mod order; mod partial; mod shape_builder; #[macro_use] diff --git a/src/order.rs b/src/order.rs new file mode 100644 index 000000000..e8d9c8db1 --- /dev/null +++ b/src/order.rs @@ -0,0 +1,83 @@ + +/// Array order +/// +/// Order refers to indexing order, or how a linear sequence is translated +/// into a two-dimensional or multi-dimensional array. +/// +/// - `RowMajor` means that the index along the row is the most rapidly changing +/// - `ColumnMajor` means that the index along the column is the most rapidly changing +/// +/// Given a sequence like: 1, 2, 3, 4, 5, 6 +/// +/// If it is laid it out in a 2 x 3 matrix using row major ordering, it results in: +/// +/// ```text +/// 1 2 3 +/// 4 5 6 +/// ``` +/// +/// If it is laid using column major ordering, it results in: +/// +/// ```text +/// 1 3 5 +/// 2 4 6 +/// ``` +/// +/// It can be seen as filling in "rows first" or "columns first". +/// +/// `Order` can be used both to refer to logical ordering as well as memory ordering or memory +/// layout. The orderings have common short names, also seen in other environments, where +/// row major is called "C" order (after the C programming language) and column major is called "F" +/// or "Fortran" order. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum Order { + /// Row major or "C" order + RowMajor, + /// Column major or "F" order + ColumnMajor, +} + +impl Order { + /// "C" is an alias for row major ordering + pub const C: Order = Order::RowMajor; + + /// "F" (for Fortran) is an alias for column major ordering + pub const F: Order = Order::ColumnMajor; + + /// Return true if input is Order::RowMajor, false otherwise + #[inline] + pub fn is_row_major(self) -> bool { + match self { + Order::RowMajor => true, + Order::ColumnMajor => false, + } + } + + /// Return true if input is Order::ColumnMajor, false otherwise + #[inline] + pub fn is_column_major(self) -> bool { + !self.is_row_major() + } + + /// Return Order::RowMajor if the input is true, Order::ColumnMajor otherwise + #[inline] + pub fn row_major(row_major: bool) -> Order { + if row_major { Order::RowMajor } else { Order::ColumnMajor } + } + + /// Return Order::ColumnMajor if the input is true, Order::RowMajor otherwise + #[inline] + pub fn column_major(column_major: bool) -> Order { + Self::row_major(!column_major) + } + + /// Return the transpose: row major becomes column major and vice versa. + #[inline] + pub fn transpose(self) -> Order { + match self { + Order::RowMajor => Order::ColumnMajor, + Order::ColumnMajor => Order::RowMajor, + } + } +} From 0d094bf70c843ed3fa594dd76593ab967f831da6 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 17 Apr 2021 12:26:49 +0200 Subject: [PATCH 339/651] shape: Add trait ShapeArg --- src/impl_methods.rs | 2 +- src/lib.rs | 2 +- src/shape_builder.rs | 31 +++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 9ef4277de..0affd9f0b 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -26,8 +26,8 @@ use crate::dimension::broadcast::co_broadcast; use crate::error::{self, ErrorKind, ShapeError, from_kind}; use crate::math_cell::MathCell; use crate::itertools::zip; -use crate::zip::{IntoNdProducer, Zip}; use crate::AxisDescription; +use crate::zip::{IntoNdProducer, Zip}; use crate::iter::{ AxisChunksIter, AxisChunksIterMut, AxisIter, AxisIterMut, ExactChunks, ExactChunksMut, diff --git a/src/lib.rs b/src/lib.rs index d4f721224..dfce924e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -163,7 +163,7 @@ pub use crate::stacking::{concatenate, stack, stack_new_axis}; pub use crate::math_cell::MathCell; pub use crate::impl_views::IndexLonger; -pub use crate::shape_builder::{Shape, ShapeBuilder, StrideShape}; +pub use crate::shape_builder::{Shape, ShapeBuilder, ShapeArg, StrideShape}; #[macro_use] mod macro_utils; diff --git a/src/shape_builder.rs b/src/shape_builder.rs index dcfddc1b9..470374077 100644 --- a/src/shape_builder.rs +++ b/src/shape_builder.rs @@ -1,5 +1,6 @@ use crate::dimension::IntoDimension; use crate::Dimension; +use crate::order::Order; /// A contiguous array shape of n dimensions. /// @@ -184,3 +185,33 @@ where self.dim.size() } } + + +/// Array shape argument with optional order parameter +/// +/// Shape or array dimension argument, with optional [`Order`] parameter. +/// +/// This is an argument conversion trait that is used to accept an array shape and +/// (optionally) an ordering argument. +/// +/// See for example [`.to_shape()`](crate::ArrayBase::to_shape). +pub trait ShapeArg { + type Dim: Dimension; + fn into_shape_and_order(self) -> (Self::Dim, Option); +} + +impl ShapeArg for T where T: IntoDimension { + type Dim = T::Dim; + + fn into_shape_and_order(self) -> (Self::Dim, Option) { + (self.into_dimension(), None) + } +} + +impl ShapeArg for (T, Order) where T: IntoDimension { + type Dim = T::Dim; + + fn into_shape_and_order(self) -> (Self::Dim, Option) { + (self.0.into_dimension(), Some(self.1)) + } +} From 3794952ce342ad7af866470a202cfedaed74bf49 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 15 Jan 2021 20:58:38 +0100 Subject: [PATCH 340/651] shape: Add method .to_shape() --- src/impl_methods.rs | 87 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 0affd9f0b..cda2cd95e 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -27,6 +27,9 @@ use crate::error::{self, ErrorKind, ShapeError, from_kind}; use crate::math_cell::MathCell; use crate::itertools::zip; use crate::AxisDescription; +use crate::Layout; +use crate::order::Order; +use crate::shape_builder::ShapeArg; use crate::zip::{IntoNdProducer, Zip}; use crate::iter::{ @@ -1577,6 +1580,90 @@ where } } + /// Transform the array into `new_shape`; any shape with the same number of elements is + /// accepted. + /// + /// `order` specifies the *logical* order in which the array is to be read and reshaped. + /// The array is returned as a `CowArray`; a view if possible, otherwise an owned array. + /// + /// For example, when starting from the one-dimensional sequence 1 2 3 4 5 6, it would be + /// understood as a 2 x 3 array in row major ("C") order this way: + /// + /// ```text + /// 1 2 3 + /// 4 5 6 + /// ``` + /// + /// and as 2 x 3 in column major ("F") order this way: + /// + /// ```text + /// 1 3 5 + /// 2 4 6 + /// ``` + /// + /// This example should show that any time we "reflow" the elements in the array to a different + /// number of rows and columns (or more axes if applicable), it is important to pick an index + /// ordering, and that's the reason for the function parameter for `order`. + /// + /// **Errors** if the new shape doesn't have the same number of elements as the array's current + /// shape. + /// + /// ``` + /// use ndarray::array; + /// use ndarray::Order; + /// + /// assert!( + /// array![1., 2., 3., 4., 5., 6.].to_shape(((2, 3), Order::RowMajor)).unwrap() + /// == array![[1., 2., 3.], + /// [4., 5., 6.]] + /// ); + /// + /// assert!( + /// array![1., 2., 3., 4., 5., 6.].to_shape(((2, 3), Order::ColumnMajor)).unwrap() + /// == array![[1., 3., 5.], + /// [2., 4., 6.]] + /// ); + /// ``` + pub fn to_shape(&self, new_shape: E) -> Result, ShapeError> + where + E: ShapeArg, + A: Clone, + S: Data, + { + let (shape, order) = new_shape.into_shape_and_order(); + self.to_shape_order(shape, order.unwrap_or(Order::RowMajor)) + } + + fn to_shape_order(&self, shape: E, order: Order) + -> Result, ShapeError> + where + E: Dimension, + A: Clone, + S: Data, + { + if size_of_shape_checked(&shape) != Ok(self.dim.size()) { + return Err(error::incompatible_shapes(&self.dim, &shape)); + } + let layout = self.layout_impl(); + + unsafe { + if layout.is(Layout::CORDER) && order == Order::RowMajor { + let strides = shape.default_strides(); + Ok(CowArray::from(ArrayView::new(self.ptr, shape, strides))) + } else if layout.is(Layout::FORDER) && order == Order::ColumnMajor { + let strides = shape.fortran_strides(); + Ok(CowArray::from(ArrayView::new(self.ptr, shape, strides))) + } else { + let (shape, view) = match order { + Order::RowMajor => (shape.set_f(false), self.view()), + Order::ColumnMajor => (shape.set_f(true), self.t()), + }; + Ok(CowArray::from(Array::from_shape_trusted_iter_unchecked( + shape, view.into_iter(), A::clone))) + } + } + } + /// Transform the array into `shape`; any shape with the same number of /// elements is accepted, but the source array or view must be in standard /// or column-major (Fortran) layout. From 8032aec1b742c36a985cd69f0ef53ec6adad6f28 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 17 Apr 2021 12:26:49 +0200 Subject: [PATCH 341/651] shape: Add tests for .to_shape() --- tests/array.rs | 60 +----------------- tests/reshape.rs | 159 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 59 deletions(-) create mode 100644 tests/reshape.rs diff --git a/tests/array.rs b/tests/array.rs index 976824dfe..edd58adbc 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -8,7 +8,7 @@ )] use defmac::defmac; -use itertools::{enumerate, zip, Itertools}; +use itertools::{zip, Itertools}; use ndarray::prelude::*; use ndarray::{arr3, rcarr2}; use ndarray::indices; @@ -1370,64 +1370,6 @@ fn transpose_view_mut() { assert_eq!(at, arr2(&[[1, 4], [2, 5], [3, 7]])); } -#[test] -fn reshape() { - let data = [1, 2, 3, 4, 5, 6, 7, 8]; - let v = aview1(&data); - let u = v.into_shape((3, 3)); - assert!(u.is_err()); - let u = v.into_shape((2, 2, 2)); - assert!(u.is_ok()); - let u = u.unwrap(); - assert_eq!(u.shape(), &[2, 2, 2]); - let s = u.into_shape((4, 2)).unwrap(); - assert_eq!(s.shape(), &[4, 2]); - assert_eq!(s, aview2(&[[1, 2], [3, 4], [5, 6], [7, 8]])); -} - -#[test] -#[should_panic(expected = "IncompatibleShape")] -fn reshape_error1() { - let data = [1, 2, 3, 4, 5, 6, 7, 8]; - let v = aview1(&data); - let _u = v.into_shape((2, 5)).unwrap(); -} - -#[test] -#[should_panic(expected = "IncompatibleLayout")] -fn reshape_error2() { - let data = [1, 2, 3, 4, 5, 6, 7, 8]; - let v = aview1(&data); - let mut u = v.into_shape((2, 2, 2)).unwrap(); - u.swap_axes(0, 1); - let _s = u.into_shape((2, 4)).unwrap(); -} - -#[test] -fn reshape_f() { - let mut u = Array::zeros((3, 4).f()); - for (i, elt) in enumerate(u.as_slice_memory_order_mut().unwrap()) { - *elt = i as i32; - } - let v = u.view(); - println!("{:?}", v); - - // noop ok - let v2 = v.into_shape((3, 4)); - assert!(v2.is_ok()); - assert_eq!(v, v2.unwrap()); - - let u = v.into_shape((3, 2, 2)); - assert!(u.is_ok()); - let u = u.unwrap(); - println!("{:?}", u); - assert_eq!(u.shape(), &[3, 2, 2]); - let s = u.into_shape((4, 3)).unwrap(); - println!("{:?}", s); - assert_eq!(s.shape(), &[4, 3]); - assert_eq!(s, aview2(&[[0, 4, 8], [1, 5, 9], [2, 6, 10], [3, 7, 11]])); -} - #[test] #[allow(clippy::cognitive_complexity)] fn insert_axis() { diff --git a/tests/reshape.rs b/tests/reshape.rs new file mode 100644 index 000000000..f03f4ccf1 --- /dev/null +++ b/tests/reshape.rs @@ -0,0 +1,159 @@ +use ndarray::prelude::*; + +use itertools::enumerate; + +use ndarray::Order; + +#[test] +fn reshape() { + let data = [1, 2, 3, 4, 5, 6, 7, 8]; + let v = aview1(&data); + let u = v.into_shape((3, 3)); + assert!(u.is_err()); + let u = v.into_shape((2, 2, 2)); + assert!(u.is_ok()); + let u = u.unwrap(); + assert_eq!(u.shape(), &[2, 2, 2]); + let s = u.into_shape((4, 2)).unwrap(); + assert_eq!(s.shape(), &[4, 2]); + assert_eq!(s, aview2(&[[1, 2], [3, 4], [5, 6], [7, 8]])); +} + +#[test] +#[should_panic(expected = "IncompatibleShape")] +fn reshape_error1() { + let data = [1, 2, 3, 4, 5, 6, 7, 8]; + let v = aview1(&data); + let _u = v.into_shape((2, 5)).unwrap(); +} + +#[test] +#[should_panic(expected = "IncompatibleLayout")] +fn reshape_error2() { + let data = [1, 2, 3, 4, 5, 6, 7, 8]; + let v = aview1(&data); + let mut u = v.into_shape((2, 2, 2)).unwrap(); + u.swap_axes(0, 1); + let _s = u.into_shape((2, 4)).unwrap(); +} + +#[test] +fn reshape_f() { + let mut u = Array::zeros((3, 4).f()); + for (i, elt) in enumerate(u.as_slice_memory_order_mut().unwrap()) { + *elt = i as i32; + } + let v = u.view(); + println!("{:?}", v); + + // noop ok + let v2 = v.into_shape((3, 4)); + assert!(v2.is_ok()); + assert_eq!(v, v2.unwrap()); + + let u = v.into_shape((3, 2, 2)); + assert!(u.is_ok()); + let u = u.unwrap(); + println!("{:?}", u); + assert_eq!(u.shape(), &[3, 2, 2]); + let s = u.into_shape((4, 3)).unwrap(); + println!("{:?}", s); + assert_eq!(s.shape(), &[4, 3]); + assert_eq!(s, aview2(&[[0, 4, 8], [1, 5, 9], [2, 6, 10], [3, 7, 11]])); +} + + +#[test] +fn to_shape_easy() { + // 1D -> C -> C + let data = [1, 2, 3, 4, 5, 6, 7, 8]; + let v = aview1(&data); + let u = v.to_shape(((3, 3), Order::RowMajor)); + assert!(u.is_err()); + + let u = v.to_shape(((2, 2, 2), Order::C)); + assert!(u.is_ok()); + + let u = u.unwrap(); + assert!(u.is_view()); + assert_eq!(u.shape(), &[2, 2, 2]); + assert_eq!(u, array![[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); + + let s = u.to_shape((4, 2)).unwrap(); + assert_eq!(s.shape(), &[4, 2]); + assert_eq!(s, aview2(&[[1, 2], [3, 4], [5, 6], [7, 8]])); + + // 1D -> F -> F + let data = [1, 2, 3, 4, 5, 6, 7, 8]; + let v = aview1(&data); + let u = v.to_shape(((3, 3), Order::ColumnMajor)); + assert!(u.is_err()); + + let u = v.to_shape(((2, 2, 2), Order::ColumnMajor)); + assert!(u.is_ok()); + + let u = u.unwrap(); + assert!(u.is_view()); + assert_eq!(u.shape(), &[2, 2, 2]); + assert_eq!(u, array![[[1, 5], [3, 7]], [[2, 6], [4, 8]]]); + + let s = u.to_shape(((4, 2), Order::ColumnMajor)).unwrap(); + assert_eq!(s.shape(), &[4, 2]); + assert_eq!(s, array![[1, 5], [2, 6], [3, 7], [4, 8]]); +} + +#[test] +fn to_shape_copy() { + // 1D -> C -> F + let v = ArrayView::from(&[1, 2, 3, 4, 5, 6, 7, 8]); + let u = v.to_shape(((4, 2), Order::RowMajor)).unwrap(); + assert_eq!(u.shape(), &[4, 2]); + assert_eq!(u, array![[1, 2], [3, 4], [5, 6], [7, 8]]); + + let u = u.to_shape(((2, 4), Order::ColumnMajor)).unwrap(); + assert_eq!(u.shape(), &[2, 4]); + assert_eq!(u, array![[1, 5, 2, 6], [3, 7, 4, 8]]); + + // 1D -> F -> C + let v = ArrayView::from(&[1, 2, 3, 4, 5, 6, 7, 8]); + let u = v.to_shape(((4, 2), Order::ColumnMajor)).unwrap(); + assert_eq!(u.shape(), &[4, 2]); + assert_eq!(u, array![[1, 5], [2, 6], [3, 7], [4, 8]]); + + let u = u.to_shape((2, 4)).unwrap(); + assert_eq!(u.shape(), &[2, 4]); + assert_eq!(u, array![[1, 5, 2, 6], [3, 7, 4, 8]]); +} + +#[test] +fn to_shape_add_axis() { + // 1D -> C -> C + let data = [1, 2, 3, 4, 5, 6, 7, 8]; + let v = aview1(&data); + let u = v.to_shape(((4, 2), Order::RowMajor)).unwrap(); + + assert!(u.to_shape(((1, 4, 2), Order::RowMajor)).unwrap().is_view()); + assert!(u.to_shape(((1, 4, 2), Order::ColumnMajor)).unwrap().is_owned()); +} + + +#[test] +fn to_shape_copy_stride() { + let v = array![[1, 2, 3, 4], [5, 6, 7, 8]]; + let vs = v.slice(s![.., ..3]); + let lin1 = vs.to_shape(6).unwrap(); + assert_eq!(lin1, array![1, 2, 3, 5, 6, 7]); + assert!(lin1.is_owned()); + + let lin2 = vs.to_shape((6, Order::ColumnMajor)).unwrap(); + assert_eq!(lin2, array![1, 5, 2, 6, 3, 7]); + assert!(lin2.is_owned()); +} + +#[test] +#[should_panic(expected = "IncompatibleShape")] +fn to_shape_error1() { + let data = [1, 2, 3, 4, 5, 6, 7, 8]; + let v = aview1(&data); + let _u = v.to_shape((2, 5)).unwrap(); +} From a26bf94afcea540676b715f0b5ff4355317f2ed6 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 9 May 2021 20:01:26 +0200 Subject: [PATCH 342/651] shape: Add benchmarks for .to_shape (to view) --- benches/to_shape.rs | 106 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 benches/to_shape.rs diff --git a/benches/to_shape.rs b/benches/to_shape.rs new file mode 100644 index 000000000..a048eb774 --- /dev/null +++ b/benches/to_shape.rs @@ -0,0 +1,106 @@ +#![feature(test)] + +extern crate test; +use test::Bencher; + +use ndarray::prelude::*; +use ndarray::Order; + +#[bench] +fn to_shape2_1(bench: &mut Bencher) { + let a = Array::::zeros((4, 5)); + let view = a.view(); + bench.iter(|| { + view.to_shape(4 * 5).unwrap() + }); +} + +#[bench] +fn to_shape2_2_same(bench: &mut Bencher) { + let a = Array::::zeros((4, 5)); + let view = a.view(); + bench.iter(|| { + view.to_shape((4, 5)).unwrap() + }); +} + +#[bench] +fn to_shape2_2_flip(bench: &mut Bencher) { + let a = Array::::zeros((4, 5)); + let view = a.view(); + bench.iter(|| { + view.to_shape((5, 4)).unwrap() + }); +} + +#[bench] +fn to_shape2_3(bench: &mut Bencher) { + let a = Array::::zeros((4, 5)); + let view = a.view(); + bench.iter(|| { + view.to_shape((2, 5, 2)).unwrap() + }); +} + +#[bench] +fn to_shape3_1(bench: &mut Bencher) { + let a = Array::::zeros((3, 4, 5)); + let view = a.view(); + bench.iter(|| { + view.to_shape(3 * 4 * 5).unwrap() + }); +} + +#[bench] +fn to_shape3_2_order(bench: &mut Bencher) { + let a = Array::::zeros((3, 4, 5)); + let view = a.view(); + bench.iter(|| { + view.to_shape((12, 5)).unwrap() + }); +} + +#[bench] +fn to_shape3_2_outoforder(bench: &mut Bencher) { + let a = Array::::zeros((3, 4, 5)); + let view = a.view(); + bench.iter(|| { + view.to_shape((4, 15)).unwrap() + }); +} + +#[bench] +fn to_shape3_3c(bench: &mut Bencher) { + let a = Array::::zeros((3, 4, 5)); + let view = a.view(); + bench.iter(|| { + view.to_shape((3, 4, 5)).unwrap() + }); +} + +#[bench] +fn to_shape3_3f(bench: &mut Bencher) { + let a = Array::::zeros((3, 4, 5).f()); + let view = a.view(); + bench.iter(|| { + view.to_shape(((3, 4, 5), Order::F)).unwrap() + }); +} + +#[bench] +fn to_shape3_4c(bench: &mut Bencher) { + let a = Array::::zeros((3, 4, 5)); + let view = a.view(); + bench.iter(|| { + view.to_shape(((2, 3, 2, 5), Order::C)).unwrap() + }); +} + +#[bench] +fn to_shape3_4f(bench: &mut Bencher) { + let a = Array::::zeros((3, 4, 5).f()); + let view = a.view(); + bench.iter(|| { + view.to_shape(((2, 3, 2, 5), Order::F)).unwrap() + }); +} From b157cc68b75f23ce7adce2910709215c468dae82 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 9 May 2021 20:01:26 +0200 Subject: [PATCH 343/651] shape: Add trait Sequence To generalize over Forward/Reverse indexing of dimensions, add simple traits Sequence/Mut that allow indexing forwards and reversed references of dimension values. In a future change, they will support strides (with isize values) too. --- src/dimension/mod.rs | 1 + src/dimension/sequence.rs | 109 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 src/dimension/sequence.rs diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index f4f46e764..e883933b2 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -40,6 +40,7 @@ mod dynindeximpl; mod ndindex; mod ops; mod remove_axis; +mod sequence; /// Calculate offset from `Ix` stride converting sign properly #[inline(always)] diff --git a/src/dimension/sequence.rs b/src/dimension/sequence.rs new file mode 100644 index 000000000..835e00d18 --- /dev/null +++ b/src/dimension/sequence.rs @@ -0,0 +1,109 @@ +use std::ops::Index; +use std::ops::IndexMut; + +use crate::dimension::Dimension; + +pub(in crate::dimension) struct Forward(pub(crate) D); +pub(in crate::dimension) struct Reverse(pub(crate) D); + +impl Index for Forward<&D> +where + D: Dimension, +{ + type Output = usize; + + #[inline] + fn index(&self, index: usize) -> &usize { + &self.0[index] + } +} + +impl Index for Forward<&mut D> +where + D: Dimension, +{ + type Output = usize; + + #[inline] + fn index(&self, index: usize) -> &usize { + &self.0[index] + } +} + +impl IndexMut for Forward<&mut D> +where + D: Dimension, +{ + #[inline] + fn index_mut(&mut self, index: usize) -> &mut usize { + &mut self.0[index] + } +} + +impl Index for Reverse<&D> +where + D: Dimension, +{ + type Output = usize; + + #[inline] + fn index(&self, index: usize) -> &usize { + &self.0[self.len() - index - 1] + } +} + +impl Index for Reverse<&mut D> +where + D: Dimension, +{ + type Output = usize; + + #[inline] + fn index(&self, index: usize) -> &usize { + &self.0[self.len() - index - 1] + } +} + +impl IndexMut for Reverse<&mut D> +where + D: Dimension, +{ + #[inline] + fn index_mut(&mut self, index: usize) -> &mut usize { + let len = self.len(); + &mut self.0[len - index - 1] + } +} + +/// Indexable sequence with length +pub(in crate::dimension) trait Sequence: Index { + fn len(&self) -> usize; +} + +/// Indexable sequence with length (mut) +pub(in crate::dimension) trait SequenceMut: Sequence + IndexMut { } + +impl Sequence for Forward<&D> where D: Dimension { + #[inline] + fn len(&self) -> usize { self.0.ndim() } +} + +impl Sequence for Forward<&mut D> where D: Dimension { + #[inline] + fn len(&self) -> usize { self.0.ndim() } +} + +impl SequenceMut for Forward<&mut D> where D: Dimension { } + +impl Sequence for Reverse<&D> where D: Dimension { + #[inline] + fn len(&self) -> usize { self.0.ndim() } +} + +impl Sequence for Reverse<&mut D> where D: Dimension { + #[inline] + fn len(&self) -> usize { self.0.ndim() } +} + +impl SequenceMut for Reverse<&mut D> where D: Dimension { } + From 2d42e5f0ff058abaca79f9eb734975a3967d7fa5 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 9 May 2021 20:01:26 +0200 Subject: [PATCH 344/651] shape: Add function for reshape while preserving memory layout This function can compute the strides needed and if it's possible to reshape an array with given strides into a different shape. This happens using a given Order::{C, F}. --- src/dimension/mod.rs | 6 +- src/dimension/reshape.rs | 241 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 src/dimension/reshape.rs diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index e883933b2..4aa7c6641 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -9,10 +9,10 @@ use crate::error::{from_kind, ErrorKind, ShapeError}; use crate::slice::SliceArg; use crate::{Ix, Ixs, Slice, SliceInfoElem}; +use crate::shape_builder::Strides; use num_integer::div_floor; pub use self::axes::{Axes, AxisDescription}; -pub(crate) use self::axes::axes_of; pub use self::axis::Axis; pub use self::broadcast::DimMax; pub use self::conversion::IntoDimension; @@ -23,7 +23,8 @@ pub use self::ndindex::NdIndex; pub use self::ops::DimAdd; pub use self::remove_axis::RemoveAxis; -use crate::shape_builder::Strides; +pub(crate) use self::axes::axes_of; +pub(crate) use self::reshape::reshape_dim; use std::isize; use std::mem; @@ -40,6 +41,7 @@ mod dynindeximpl; mod ndindex; mod ops; mod remove_axis; +pub(crate) mod reshape; mod sequence; /// Calculate offset from `Ix` stride converting sign properly diff --git a/src/dimension/reshape.rs b/src/dimension/reshape.rs new file mode 100644 index 000000000..c6e08848d --- /dev/null +++ b/src/dimension/reshape.rs @@ -0,0 +1,241 @@ + +use crate::{Dimension, Order, ShapeError, ErrorKind}; +use crate::dimension::sequence::{Sequence, SequenceMut, Forward, Reverse}; + +#[inline] +pub(crate) fn reshape_dim(from: &D, strides: &D, to: &E, order: Order) + -> Result +where + D: Dimension, + E: Dimension, +{ + debug_assert_eq!(from.ndim(), strides.ndim()); + let mut to_strides = E::zeros(to.ndim()); + match order { + Order::RowMajor => { + reshape_dim_c(&Forward(from), &Forward(strides), + &Forward(to), Forward(&mut to_strides))?; + } + Order::ColumnMajor => { + reshape_dim_c(&Reverse(from), &Reverse(strides), + &Reverse(to), Reverse(&mut to_strides))?; + } + } + Ok(to_strides) +} + +/// Try to reshape an array with dimensions `from_dim` and strides `from_strides` to the new +/// dimension `to_dim`, while keeping the same layout of elements in memory. The strides needed +/// if this is possible are stored into `to_strides`. +/// +/// This function uses RowMajor index ordering if the inputs are read in the forward direction +/// (index 0 is axis 0 etc) and ColumnMajor index ordering if the inputs are read in reversed +/// direction (as made possible with the Sequence trait). +/// +/// Preconditions: +/// +/// 1. from_dim and to_dim are valid dimensions (product of all non-zero axes +/// fits in isize::MAX). +/// 2. from_dim and to_dim are don't have any axes that are zero (that should be handled before +/// this function). +/// 3. `to_strides` should be an all-zeros or all-ones dimension of the right dimensionality +/// (but it will be overwritten after successful exit of this function). +/// +/// This function returns: +/// +/// - IncompatibleShape if the two shapes are not of matching number of elements +/// - IncompatibleLayout if the input shape and stride can not be remapped to the output shape +/// without moving the array data into a new memory layout. +/// - Ok if the from dim could be mapped to the new to dim. +fn reshape_dim_c(from_dim: &D, from_strides: &D, to_dim: &E, mut to_strides: E2) + -> Result<(), ShapeError> +where + D: Sequence, + E: Sequence, + E2: SequenceMut, +{ + // cursor indexes into the from and to dimensions + let mut fi = 0; // index into `from_dim` + let mut ti = 0; // index into `to_dim`. + + while fi < from_dim.len() && ti < to_dim.len() { + let mut fd = from_dim[fi]; + let mut fs = from_strides[fi] as isize; + let mut td = to_dim[ti]; + + if fd == td { + to_strides[ti] = from_strides[fi]; + fi += 1; + ti += 1; + continue + } + + if fd == 1 { + fi += 1; + continue; + } + + if td == 1 { + to_strides[ti] = 1; + ti += 1; + continue; + } + + if fd == 0 || td == 0 { + debug_assert!(false, "zero dim not handled by this function"); + return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); + } + + // stride times element count is to be distributed out over a combination of axes. + let mut fstride_whole = fs * (fd as isize); + let mut fd_product = fd; // cumulative product of axis lengths in the combination (from) + let mut td_product = td; // cumulative product of axis lengths in the combination (to) + + // The two axis lengths are not a match, so try to combine multiple axes + // to get it to match up. + while fd_product != td_product { + if fd_product < td_product { + // Take another axis on the from side + fi += 1; + if fi >= from_dim.len() { + return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); + } + fd = from_dim[fi]; + fd_product *= fd; + if fd > 1 { + let fs_old = fs; + fs = from_strides[fi] as isize; + // check if this axis and the next are contiguous together + if fs_old != fd as isize * fs { + return Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)); + } + } + } else { + // Take another axis on the `to` side + // First assign the stride to the axis we leave behind + fstride_whole /= td as isize; + to_strides[ti] = fstride_whole as usize; + ti += 1; + if ti >= to_dim.len() { + return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); + } + + td = to_dim[ti]; + td_product *= td; + } + } + + fstride_whole /= td as isize; + to_strides[ti] = fstride_whole as usize; + + fi += 1; + ti += 1; + } + + // skip past 1-dims at the end + while fi < from_dim.len() && from_dim[fi] == 1 { + fi += 1; + } + + while ti < to_dim.len() && to_dim[ti] == 1 { + to_strides[ti] = 1; + ti += 1; + } + + if fi < from_dim.len() || ti < to_dim.len() { + return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); + } + + Ok(()) +} + +#[cfg(feature = "std")] +#[test] +fn test_reshape() { + use crate::Dim; + + macro_rules! test_reshape { + (fail $order:ident from $from:expr, $stride:expr, to $to:expr) => { + let res = reshape_dim(&Dim($from), &Dim($stride), &Dim($to), Order::$order); + println!("Reshape {:?} {:?} to {:?}, order {:?}\n => {:?}", + $from, $stride, $to, Order::$order, res); + let _res = res.expect_err("Expected failed reshape"); + }; + (ok $order:ident from $from:expr, $stride:expr, to $to:expr, $to_stride:expr) => {{ + let res = reshape_dim(&Dim($from), &Dim($stride), &Dim($to), Order::$order); + println!("Reshape {:?} {:?} to {:?}, order {:?}\n => {:?}", + $from, $stride, $to, Order::$order, res); + println!("default stride for from dim: {:?}", Dim($from).default_strides()); + println!("default stride for to dim: {:?}", Dim($to).default_strides()); + let res = res.expect("Expected successful reshape"); + assert_eq!(res, Dim($to_stride), "mismatch in strides"); + }}; + } + + test_reshape!(ok C from [1, 2, 3], [6, 3, 1], to [1, 2, 3], [6, 3, 1]); + test_reshape!(ok C from [1, 2, 3], [6, 3, 1], to [2, 3], [3, 1]); + test_reshape!(ok C from [1, 2, 3], [6, 3, 1], to [6], [1]); + test_reshape!(fail C from [1, 2, 3], [6, 3, 1], to [1]); + test_reshape!(fail F from [1, 2, 3], [6, 3, 1], to [1]); + + test_reshape!(ok C from [6], [1], to [3, 2], [2, 1]); + test_reshape!(ok C from [3, 4, 5], [20, 5, 1], to [4, 15], [15, 1]); + + test_reshape!(ok C from [4, 4, 4], [16, 4, 1], to [16, 4], [4, 1]); + + test_reshape!(ok C from [4, 4], [4, 1], to [2, 2, 4, 1], [8, 4, 1, 1]); + test_reshape!(ok C from [4, 4], [4, 1], to [2, 2, 4], [8, 4, 1]); + test_reshape!(ok C from [4, 4], [4, 1], to [2, 2, 2, 2], [8, 4, 2, 1]); + + test_reshape!(ok C from [4, 4], [4, 1], to [2, 2, 1, 4], [8, 4, 1, 1]); + + test_reshape!(ok C from [4, 4, 4], [16, 4, 1], to [16, 4], [4, 1]); + test_reshape!(ok C from [3, 4, 4], [16, 4, 1], to [3, 16], [16, 1]); + + test_reshape!(ok C from [4, 4], [8, 1], to [2, 2, 2, 2], [16, 8, 2, 1]); + + test_reshape!(fail C from [4, 4], [8, 1], to [2, 1, 4, 2]); + + test_reshape!(ok C from [16], [4], to [2, 2, 4], [32, 16, 4]); + test_reshape!(ok C from [16], [-4isize as usize], to [2, 2, 4], + [-32isize as usize, -16isize as usize, -4isize as usize]); + test_reshape!(ok F from [16], [4], to [2, 2, 4], [4, 8, 16]); + test_reshape!(ok F from [16], [-4isize as usize], to [2, 2, 4], + [-4isize as usize, -8isize as usize, -16isize as usize]); + + test_reshape!(ok C from [3, 4, 5], [20, 5, 1], to [12, 5], [5, 1]); + test_reshape!(ok C from [3, 4, 5], [20, 5, 1], to [4, 15], [15, 1]); + test_reshape!(fail F from [3, 4, 5], [20, 5, 1], to [4, 15]); + test_reshape!(ok C from [3, 4, 5, 7], [140, 35, 7, 1], to [28, 15], [15, 1]); + + // preserve stride if shape matches + test_reshape!(ok C from [10], [2], to [10], [2]); + test_reshape!(ok F from [10], [2], to [10], [2]); + test_reshape!(ok C from [2, 10], [1, 2], to [2, 10], [1, 2]); + test_reshape!(ok F from [2, 10], [1, 2], to [2, 10], [1, 2]); + test_reshape!(ok C from [3, 4, 5], [20, 5, 1], to [3, 4, 5], [20, 5, 1]); + test_reshape!(ok F from [3, 4, 5], [20, 5, 1], to [3, 4, 5], [20, 5, 1]); + + test_reshape!(ok C from [3, 4, 5], [4, 1, 1], to [12, 5], [1, 1]); + test_reshape!(ok F from [3, 4, 5], [1, 3, 12], to [12, 5], [1, 12]); + test_reshape!(ok F from [3, 4, 5], [1, 3, 1], to [12, 5], [1, 1]); + + // broadcast shapes + test_reshape!(ok C from [3, 4, 5, 7], [0, 0, 7, 1], to [12, 35], [0, 1]); + test_reshape!(fail C from [3, 4, 5, 7], [0, 0, 7, 1], to [28, 15]); + + // one-filled shapes + test_reshape!(ok C from [10], [1], to [1, 10, 1, 1, 1], [1, 1, 1, 1, 1]); + test_reshape!(ok F from [10], [1], to [1, 10, 1, 1, 1], [1, 1, 1, 1, 1]); + test_reshape!(ok C from [1, 10], [10, 1], to [1, 10, 1, 1, 1], [10, 1, 1, 1, 1]); + test_reshape!(ok F from [1, 10], [10, 1], to [1, 10, 1, 1, 1], [10, 1, 1, 1, 1]); + test_reshape!(ok C from [1, 10], [1, 1], to [1, 5, 1, 1, 2], [1, 2, 2, 2, 1]); + test_reshape!(ok F from [1, 10], [1, 1], to [1, 5, 1, 1, 2], [1, 1, 5, 5, 5]); + test_reshape!(ok C from [10, 1, 1, 1, 1], [1, 1, 1, 1, 1], to [10], [1]); + test_reshape!(ok F from [10, 1, 1, 1, 1], [1, 1, 1, 1, 1], to [10], [1]); + test_reshape!(ok C from [1, 5, 1, 2, 1], [1, 2, 1, 1, 1], to [10], [1]); + test_reshape!(fail F from [1, 5, 1, 2, 1], [1, 2, 1, 1, 1], to [10]); + test_reshape!(ok F from [1, 5, 1, 2, 1], [1, 1, 1, 5, 1], to [10], [1]); + test_reshape!(fail C from [1, 5, 1, 2, 1], [1, 1, 1, 5, 1], to [10]); +} + From c9af569acbadd91224047a957fc79a0df99f5719 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 9 May 2021 20:01:26 +0200 Subject: [PATCH 345/651] shape: Use reshape_dim function in .to_shape() --- src/impl_methods.rs | 45 ++++++++++++++++++++++++++++----------------- tests/reshape.rs | 21 ++++++++++++++++++++- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index cda2cd95e..6c51b4515 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -23,11 +23,11 @@ use crate::dimension::{ offset_from_ptr_to_memory, size_of_shape_checked, stride_offset, Axes, }; use crate::dimension::broadcast::co_broadcast; +use crate::dimension::reshape_dim; use crate::error::{self, ErrorKind, ShapeError, from_kind}; use crate::math_cell::MathCell; use crate::itertools::zip; use crate::AxisDescription; -use crate::Layout; use crate::order::Order; use crate::shape_builder::ShapeArg; use crate::zip::{IntoNdProducer, Zip}; @@ -1641,27 +1641,38 @@ where A: Clone, S: Data, { - if size_of_shape_checked(&shape) != Ok(self.dim.size()) { + let len = self.dim.size(); + if size_of_shape_checked(&shape) != Ok(len) { return Err(error::incompatible_shapes(&self.dim, &shape)); } - let layout = self.layout_impl(); - unsafe { - if layout.is(Layout::CORDER) && order == Order::RowMajor { - let strides = shape.default_strides(); - Ok(CowArray::from(ArrayView::new(self.ptr, shape, strides))) - } else if layout.is(Layout::FORDER) && order == Order::ColumnMajor { - let strides = shape.fortran_strides(); - Ok(CowArray::from(ArrayView::new(self.ptr, shape, strides))) - } else { - let (shape, view) = match order { - Order::RowMajor => (shape.set_f(false), self.view()), - Order::ColumnMajor => (shape.set_f(true), self.t()), - }; - Ok(CowArray::from(Array::from_shape_trusted_iter_unchecked( - shape, view.into_iter(), A::clone))) + // Create a view if the length is 0, safe because the array and new shape is empty. + if len == 0 { + unsafe { + return Ok(CowArray::from(ArrayView::from_shape_ptr(shape, self.as_ptr()))); } } + + // Try to reshape the array as a view into the existing data + match reshape_dim(&self.dim, &self.strides, &shape, order) { + Ok(to_strides) => unsafe { + return Ok(CowArray::from(ArrayView::new(self.ptr, shape, to_strides))); + } + Err(err) if err.kind() == ErrorKind::IncompatibleShape => { + return Err(error::incompatible_shapes(&self.dim, &shape)); + } + _otherwise => { } + } + + // otherwise create a new array and copy the elements + unsafe { + let (shape, view) = match order { + Order::RowMajor => (shape.set_f(false), self.view()), + Order::ColumnMajor => (shape.set_f(true), self.t()), + }; + Ok(CowArray::from(Array::from_shape_trusted_iter_unchecked( + shape, view.into_iter(), A::clone))) + } } /// Transform the array into `shape`; any shape with the same number of diff --git a/tests/reshape.rs b/tests/reshape.rs index f03f4ccf1..19f5b4ae1 100644 --- a/tests/reshape.rs +++ b/tests/reshape.rs @@ -133,7 +133,7 @@ fn to_shape_add_axis() { let u = v.to_shape(((4, 2), Order::RowMajor)).unwrap(); assert!(u.to_shape(((1, 4, 2), Order::RowMajor)).unwrap().is_view()); - assert!(u.to_shape(((1, 4, 2), Order::ColumnMajor)).unwrap().is_owned()); + assert!(u.to_shape(((1, 4, 2), Order::ColumnMajor)).unwrap().is_view()); } @@ -150,6 +150,16 @@ fn to_shape_copy_stride() { assert!(lin2.is_owned()); } + +#[test] +fn to_shape_zero_len() { + let v = array![[1, 2, 3, 4], [5, 6, 7, 8]]; + let vs = v.slice(s![.., ..0]); + let lin1 = vs.to_shape(0).unwrap(); + assert_eq!(lin1, array![]); + assert!(lin1.is_view()); +} + #[test] #[should_panic(expected = "IncompatibleShape")] fn to_shape_error1() { @@ -157,3 +167,12 @@ fn to_shape_error1() { let v = aview1(&data); let _u = v.to_shape((2, 5)).unwrap(); } + +#[test] +#[should_panic(expected = "IncompatibleShape")] +fn to_shape_error2() { + // overflow + let data = [3, 4, 5, 6, 7, 8]; + let v = aview1(&data); + let _u = v.to_shape((2, usize::MAX)).unwrap(); +} From 4467cd8b51e2b24229319aa27ede2c2483476354 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 10 May 2021 19:50:47 +0200 Subject: [PATCH 346/651] shape: Add more .to_shape() tests --- tests/reshape.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/reshape.rs b/tests/reshape.rs index 19f5b4ae1..21fe407ea 100644 --- a/tests/reshape.rs +++ b/tests/reshape.rs @@ -176,3 +176,57 @@ fn to_shape_error2() { let v = aview1(&data); let _u = v.to_shape((2, usize::MAX)).unwrap(); } + +#[test] +fn to_shape_discontig() { + for &create_order in &[Order::C, Order::F] { + let a = Array::from_iter(0..64); + let mut a1 = a.to_shape(((4, 4, 4), create_order)).unwrap(); + a1.slice_collapse(s![.., ..;2, ..]); // now shape (4, 2, 4) + assert!(a1.as_slice_memory_order().is_none()); + + for &order in &[Order::C, Order::F] { + let v1 = a1.to_shape(((2, 2, 2, 2, 2), order)).unwrap(); + assert!(v1.is_view()); + let v1 = a1.to_shape(((4, 1, 2, 1, 2, 2), order)).unwrap(); + assert!(v1.is_view()); + let v1 = a1.to_shape(((4, 2, 4), order)).unwrap(); + assert!(v1.is_view()); + let v1 = a1.to_shape(((8, 4), order)).unwrap(); + assert_eq!(v1.is_view(), order == create_order && create_order == Order::C, + "failed for {:?}, {:?}", create_order, order); + let v1 = a1.to_shape(((4, 8), order)).unwrap(); + assert_eq!(v1.is_view(), order == create_order && create_order == Order::F, + "failed for {:?}, {:?}", create_order, order); + let v1 = a1.to_shape((32, order)).unwrap(); + assert!(!v1.is_view()); + } + } +} + +#[test] +fn to_shape_broadcast() { + for &create_order in &[Order::C, Order::F] { + let a = Array::from_iter(0..64); + let mut a1 = a.to_shape(((4, 4, 4), create_order)).unwrap(); + a1.slice_collapse(s![.., ..1, ..]); // now shape (4, 1, 4) + let v1 = a1.broadcast((4, 4, 4)).unwrap(); // Now shape (4, 4, 4) + assert!(v1.as_slice_memory_order().is_none()); + + for &order in &[Order::C, Order::F] { + let v2 = v1.to_shape(((2, 2, 2, 2, 2, 2), order)).unwrap(); + assert_eq!(v2.strides(), match (create_order, order) { + (Order::C, Order::C) => { &[32, 16, 0, 0, 2, 1] } + (Order::C, Order::F) => { &[16, 32, 0, 0, 1, 2] } + (Order::F, Order::C) => { &[2, 1, 0, 0, 32, 16] } + (Order::F, Order::F) => { &[1, 2, 0, 0, 16, 32] } + _other => unreachable!() + }); + + let v2 = v1.to_shape(((4, 4, 4), order)).unwrap(); + assert!(v2.is_view()); + let v2 = v1.to_shape(((8, 8), order)).unwrap(); + assert!(v2.is_owned()); + } + } +} From d155d8423e4bf00ca93011bb306afa7c1ef19964 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 12 May 2021 20:18:51 -0400 Subject: [PATCH 347/651] Fix/suppress clippy warnings (#999) --- src/arrayformat.rs | 4 +--- src/data_traits.rs | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/arrayformat.rs b/src/arrayformat.rs index c1ca352e3..5998a0c1e 100644 --- a/src/arrayformat.rs +++ b/src/arrayformat.rs @@ -50,10 +50,8 @@ impl FormatOptions { self.axis_collapse_limit = std::usize::MAX; self.axis_collapse_limit_next_last = std::usize::MAX; self.axis_collapse_limit_last = std::usize::MAX; - self - } else { - self } + self } /// Axis length collapse limit before ellipsizing, where `axis_rindex` is diff --git a/src/data_traits.rs b/src/data_traits.rs index 7ac63d54e..7d9f15b0a 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -252,6 +252,7 @@ unsafe impl Data for OwnedArcRepr { } } + #[allow(clippy::wrong_self_convention)] fn to_shared(self_: &ArrayBase) -> ArrayBase, D> where Self::Elem: Clone, From b2873dbb1d84dcd3f47ca718c5b0aa69318dd25c Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 13 May 2021 08:56:09 +0200 Subject: [PATCH 348/651] Add test for offset computation in empty array The first of the added tests failed because of bug #998, and the fix is verified by the test. The second and third testcases did not have an error, but were added for completeness. The first testcase is by SparrowLii, from the initial mention of the bug. --- tests/array-construct.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/array-construct.rs b/tests/array-construct.rs index 2b72ab039..c8948d1a3 100644 --- a/tests/array-construct.rs +++ b/tests/array-construct.rs @@ -7,6 +7,7 @@ use defmac::defmac; use ndarray::prelude::*; +use ndarray::arr3; use ndarray::Zip; #[test] @@ -164,6 +165,43 @@ fn test_ones() { assert_eq!(a, b); } +#[test] +fn test_from_shape_empty_with_neg_stride() { + // Issue #998, negative strides for an axis where it doesn't matter. + let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; + let v = s[..12].to_vec(); + let v_ptr = v.as_ptr(); + let a = Array::from_shape_vec((2, 0, 2).strides((1, -4isize as usize, 2)), v).unwrap(); + assert_eq!(a, arr3(&[[[0; 2]; 0]; 2])); + assert_eq!(a.as_ptr(), v_ptr); +} + +#[test] +fn test_from_shape_with_neg_stride() { + // Issue #998, negative strides for an axis where it doesn't matter. + let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; + let v = s[..12].to_vec(); + let v_ptr = v.as_ptr(); + let a = Array::from_shape_vec((2, 1, 2).strides((1, -4isize as usize, 2)), v).unwrap(); + assert_eq!(a, arr3(&[[[0, 2]], + [[1, 3]]])); + assert_eq!(a.as_ptr(), v_ptr); +} + +#[test] +fn test_from_shape_2_2_2_with_neg_stride() { + // Issue #998, negative strides for an axis where it doesn't matter. + let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; + let v = s[..12].to_vec(); + let v_ptr = v.as_ptr(); + let a = Array::from_shape_vec((2, 2, 2).strides((1, -4isize as usize, 2)), v).unwrap(); + assert_eq!(a, arr3(&[[[4, 6], + [0, 2]], + [[5, 7], + [1, 3]]])); + assert_eq!(a.as_ptr(), v_ptr.wrapping_add(4)); +} + #[should_panic] #[test] fn deny_wraparound_zeros() { From 664ce2db7502eeff390de4198320a815d49f2f1d Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 12 May 2021 19:55:00 -0400 Subject: [PATCH 349/651] Fix offset_from_ptr_to_memory for axis length 0 --- src/dimension/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 4aa7c6641..17e5a30b8 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -410,9 +410,10 @@ fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize) { /// This function computes the offset from the logically first element to the first element in /// memory of the array. The result is always <= 0. pub fn offset_from_ptr_to_memory(dim: &D, strides: &D) -> isize { - let offset = izip!(dim.slice(), strides.slice()).fold(0, |_offset, (d, s)| { - if (*s as isize) < 0 { - _offset + *s as isize * (*d as isize - 1) + let offset = izip!(dim.slice(), strides.slice()).fold(0, |_offset, (&d, &s)| { + let s = s as isize; + if s < 0 && d > 1 { + _offset + s * (d as isize - 1) } else { _offset } From 4ca0f498cd0094fc248aefe7f3740f55004a2f97 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 9 May 2021 14:57:13 -0400 Subject: [PATCH 350/651] Change offset_from_ptr_to_memory to offset_from_low_addr_ptr_to_logical_ptr --- src/dimension/mod.rs | 22 ++++++++++++---------- src/impl_constructors.rs | 4 ++-- src/impl_methods.rs | 10 +++++----- src/impl_views/constructors.rs | 6 +++--- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 17e5a30b8..732006e2b 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -235,11 +235,12 @@ where /// units of `A` and in units of bytes between the least address and greatest /// address accessible by moving along all axes does not exceed `isize::MAX`. /// -/// Warning: This function is sufficient to check the invariants of ArrayBase only -/// if the pointer to the first element of the array is chosen such that the element -/// with the smallest memory address is at the start of data. (In other words, the -/// pointer to the first element of the array must be computed using offset_from_ptr_to_memory -/// so that negative strides are correctly handled.) +/// Warning: This function is sufficient to check the invariants of ArrayBase +/// only if the pointer to the first element of the array is chosen such that +/// the element with the smallest memory address is at the start of the +/// allocation. (In other words, the pointer to the first element of the array +/// must be computed using `offset_from_low_addr_ptr_to_logical_ptr` so that +/// negative strides are correctly handled.) pub(crate) fn can_index_slice( data: &[A], dim: &D, @@ -407,18 +408,19 @@ fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize) { (start, end, step) } -/// This function computes the offset from the logically first element to the first element in -/// memory of the array. The result is always <= 0. -pub fn offset_from_ptr_to_memory(dim: &D, strides: &D) -> isize { +/// Returns the offset from the lowest-address element to the logically first +/// element. +pub fn offset_from_low_addr_ptr_to_logical_ptr(dim: &D, strides: &D) -> usize { let offset = izip!(dim.slice(), strides.slice()).fold(0, |_offset, (&d, &s)| { let s = s as isize; if s < 0 && d > 1 { - _offset + s * (d as isize - 1) + _offset - s * (d as isize - 1) } else { _offset } }); - offset + debug_assert!(offset >= 0); + offset as usize } /// Modify dimension, stride and return data pointer offset diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 3336ec336..40a54d1da 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -20,7 +20,7 @@ use alloc::vec; use alloc::vec::Vec; use crate::dimension; -use crate::dimension::offset_from_ptr_to_memory; +use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr; use crate::error::{self, ShapeError}; use crate::extension::nonnull::nonnull_from_vec_data; use crate::imp_prelude::*; @@ -492,7 +492,7 @@ where // debug check for issues that indicates wrong use of this constructor debug_assert!(dimension::can_index_slice(&v, &dim, &strides).is_ok()); - let ptr = nonnull_from_vec_data(&mut v).offset(-offset_from_ptr_to_memory(&dim, &strides)); + let ptr = nonnull_from_vec_data(&mut v).add(offset_from_low_addr_ptr_to_logical_ptr(&dim, &strides)); ArrayBase::from_data_ptr(DataOwned::new(v), ptr).with_strides_dim(strides, dim) } diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 6c51b4515..c441efccd 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -20,7 +20,7 @@ use crate::dimension; use crate::dimension::IntoDimension; use crate::dimension::{ abs_index, axes_of, do_slice, merge_axes, move_min_stride_axis_to_last, - offset_from_ptr_to_memory, size_of_shape_checked, stride_offset, Axes, + offset_from_low_addr_ptr_to_logical_ptr, size_of_shape_checked, stride_offset, Axes, }; use crate::dimension::broadcast::co_broadcast; use crate::dimension::reshape_dim; @@ -1539,10 +1539,10 @@ where S: Data, { if self.is_contiguous() { - let offset = offset_from_ptr_to_memory(&self.dim, &self.strides); + let offset = offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides); unsafe { Some(slice::from_raw_parts( - self.ptr.offset(offset).as_ptr(), + self.ptr.sub(offset).as_ptr(), self.len(), )) } @@ -1568,10 +1568,10 @@ where { if self.is_contiguous() { self.ensure_unique(); - let offset = offset_from_ptr_to_memory(&self.dim, &self.strides); + let offset = offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides); unsafe { Ok(slice::from_raw_parts_mut( - self.ptr.offset(offset).as_ptr(), + self.ptr.sub(offset).as_ptr(), self.len(), )) } diff --git a/src/impl_views/constructors.rs b/src/impl_views/constructors.rs index fd9457fa1..98cb81fcc 100644 --- a/src/impl_views/constructors.rs +++ b/src/impl_views/constructors.rs @@ -13,7 +13,7 @@ use crate::error::ShapeError; use crate::extension::nonnull::nonnull_debug_checked_from_ptr; use crate::imp_prelude::*; use crate::{is_aligned, StrideShape}; -use crate::dimension::offset_from_ptr_to_memory; +use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr; /// Methods for read-only array views. impl<'a, A, D> ArrayView<'a, A, D> @@ -57,7 +57,7 @@ where let dim = shape.dim; dimension::can_index_slice_with_strides(xs, &dim, &shape.strides)?; let strides = shape.strides.strides_for_dim(&dim); - unsafe { Ok(Self::new_(xs.as_ptr().offset(-offset_from_ptr_to_memory(&dim, &strides)), dim, strides)) } + unsafe { Ok(Self::new_(xs.as_ptr().add(offset_from_low_addr_ptr_to_logical_ptr(&dim, &strides)), dim, strides)) } } /// Create an `ArrayView` from shape information and a raw pointer to @@ -154,7 +154,7 @@ where let dim = shape.dim; dimension::can_index_slice_with_strides(xs, &dim, &shape.strides)?; let strides = shape.strides.strides_for_dim(&dim); - unsafe { Ok(Self::new_(xs.as_mut_ptr().offset(-offset_from_ptr_to_memory(&dim, &strides)), dim, strides)) } + unsafe { Ok(Self::new_(xs.as_mut_ptr().add(offset_from_low_addr_ptr_to_logical_ptr(&dim, &strides)), dim, strides)) } } /// Create an `ArrayViewMut` from shape information and a From a02383292d304c50eaefc2aa13035e3677db759d Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 13 May 2021 11:07:50 +0200 Subject: [PATCH 351/651] uninit: Make build_uninit public This builder method is needed - `uninit` on itself is very hard to use while generalizing over owned arrays (we have some expression problems with Rust generics here). This functionality is needed by ndarray-linalg. --- src/impl_constructors.rs | 19 ++++++++++--------- src/zip/mod.rs | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 3336ec336..9f0174f48 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -530,7 +530,7 @@ where /// ### Safety /// /// The whole of the array must be initialized before it is converted - /// using [`.assume_init()`] or otherwise traversed. + /// using [`.assume_init()`] or otherwise traversed/read with the element type `A`. /// /// ### Examples /// @@ -580,10 +580,10 @@ where /// The uninitialized elements of type `A` are represented by the type `MaybeUninit`, /// an easier way to handle uninit values correctly. /// - /// The `builder` closure gets unshared access to the array through a raw view - /// and can use it to modify the array before it is returned. This allows initializing - /// the array for any owned array type (avoiding clone requirements for copy-on-write, - /// because the array is unshared when initially created). + /// The `builder` closure gets unshared access to the array through a view and can use it to + /// modify the array before it is returned. This allows initializing the array for any owned + /// array type (avoiding clone requirements for copy-on-write, because the array is unshared + /// when initially created). /// /// Only *when* the array is completely initialized with valid elements, can it be /// converted to an array of `A` elements using [`.assume_init()`]. @@ -593,17 +593,18 @@ where /// ### Safety /// /// The whole of the array must be initialized before it is converted - /// using [`.assume_init()`] or otherwise traversed. + /// using [`.assume_init()`] or otherwise traversed/read with the element type `A`. /// - pub(crate) fn build_uninit(shape: Sh, builder: F) -> ArrayBase + /// [`.assume_init()`]: ArrayBase::assume_init + pub fn build_uninit(shape: Sh, builder: F) -> ArrayBase where Sh: ShapeBuilder, - F: FnOnce(RawArrayViewMut, D>), + F: FnOnce(ArrayViewMut, D>), { let mut array = Self::uninit(shape); // Safe because: the array is unshared here unsafe { - builder(array.raw_view_mut_unchecked()); + builder(array.raw_view_mut_unchecked().deref_into_view_mut()); } array } diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 07fcbb062..7277f99f9 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -745,7 +745,7 @@ macro_rules! map_impl { // Use partial to count the number of filled elements, and can drop the right // number of elements on unwinding (if it happens during apply/collect). unsafe { - let output_view = output.cast::(); + let output_view = output.into_raw_view_mut().cast::(); self.and(output_view) .collect_with_partial(f) .release_ownership(); From dd49b77e65fca4d9844df7dc109e181060a49549 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 21 Apr 2021 23:09:03 +0200 Subject: [PATCH 352/651] move_into: Split into .move_into() and .move_into_unit() --- src/impl_owned_array.rs | 68 ++++++++++++++++++++++++++++++++--- src/impl_views/conversions.rs | 22 ++++++++++++ tests/assign.rs | 51 ++++++++++++++++++++------ 3 files changed, 126 insertions(+), 15 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index e91a7f61b..d148a115b 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -156,6 +156,51 @@ impl Array { impl Array where D: Dimension { + /// Move all elements from self into `new_array`, which must be of the same shape but + /// can have a different memory layout. The destination is overwritten completely. + /// + /// The destination should be a mut reference to an array or an `ArrayViewMut` with + /// `A` elements. + /// + /// ***Panics*** if the shapes don't agree. + /// + /// ## Example + /// + /// ``` + /// use ndarray::Array; + /// + /// // Usage example of move_into in safe code + /// let mut a = Array::default((10, 10)); + /// let b = Array::from_shape_fn((10, 10), |(i, j)| (i + j).to_string()); + /// b.move_into(&mut a); + /// ``` + pub fn move_into<'a, AM>(self, new_array: AM) + where + AM: Into>, + A: 'a, + { + // Remove generic parameter P and call the implementation + let new_array = new_array.into(); + if mem::needs_drop::() { + self.move_into_needs_drop(new_array); + } else { + // If `A` doesn't need drop, we can overwrite the destination. + // Safe because: move_into_uninit only writes initialized values + unsafe { + self.move_into_uninit(new_array.into_maybe_uninit()) + } + } + } + + fn move_into_needs_drop(mut self, new_array: ArrayViewMut) { + // Simple case where `A` has a destructor: just swap values between self and new_array. + // Afterwards, `self` drops full of initialized values and dropping works as usual. + // This avoids moving out of owned values in `self` while at the same time managing + // the dropping if the values being overwritten in `new_array`. + Zip::from(&mut self).and(new_array) + .for_each(|src, dst| mem::swap(src, dst)); + } + /// Move all elements from self into `new_array`, which must be of the same shape but /// can have a different memory layout. The destination is overwritten completely. /// @@ -168,12 +213,26 @@ impl Array /// drop of any such element, other elements may be leaked. /// /// ***Panics*** if the shapes don't agree. - pub fn move_into<'a, AM>(self, new_array: AM) + /// + /// ## Example + /// + /// ``` + /// use ndarray::Array; + /// + /// let a = Array::from_iter(0..100).into_shape((10, 10)).unwrap(); + /// let mut b = Array::uninit((10, 10)); + /// a.move_into_uninit(&mut b); + /// unsafe { + /// // we can now promise we have fully initialized `b`. + /// let b = b.assume_init(); + /// } + /// ``` + pub fn move_into_uninit<'a, AM>(self, new_array: AM) where AM: Into, D>>, A: 'a, { - // Remove generic parameter P and call the implementation + // Remove generic parameter AM and call the implementation self.move_into_impl(new_array.into()) } @@ -181,7 +240,8 @@ impl Array unsafe { // Safety: copy_to_nonoverlapping cannot panic let guard = AbortIfPanic(&"move_into: moving out of owned value"); - // Move all reachable elements + // Move all reachable elements; we move elements out of `self`. + // and thus must not panic for the whole section until we call `self.data.set_len(0)`. Zip::from(self.raw_view_mut()) .and(new_array) .for_each(|src, dst| { @@ -271,7 +331,7 @@ impl Array // dummy array -> self. // old_self elements are moved -> new_array. let old_self = std::mem::replace(self, Self::empty()); - old_self.move_into(new_array.view_mut()); + old_self.move_into_uninit(new_array.view_mut()); // new_array -> self. unsafe { diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs index cfd7f9aa0..5dba16d98 100644 --- a/src/impl_views/conversions.rs +++ b/src/impl_views/conversions.rs @@ -7,6 +7,7 @@ // except according to those terms. use alloc::slice; +use std::mem::MaybeUninit; use crate::imp_prelude::*; @@ -133,6 +134,27 @@ where self.into_raw_view_mut().cast::>().deref_into_view() } } + + /// Return the array view as a view of `MaybeUninit` elements + /// + /// This conversion leaves the elements as they were (presumably initialized), but + /// they are represented with the `MaybeUninit` type. Effectively this means that + /// the elements can be overwritten without dropping the old element in its place. + /// (In some situations this is not what you want, while for `Copy` elements it makes + /// no difference at all.) + /// + /// # Safety + /// + /// This method allows writing uninitialized data into the view, which could leave any + /// original array that we borrow from in an inconsistent state. This is not allowed + /// when using the resulting array view. + pub(crate) unsafe fn into_maybe_uninit(self) -> ArrayViewMut<'a, MaybeUninit, D> { + // Safe because: A and MaybeUninit have the same representation; + // and we can go from initialized to (maybe) not unconditionally in terms of + // representation. However, the user must be careful to not write uninit elements + // through the view. + self.into_raw_view_mut().cast::>().deref_into_view_mut() + } } /// Private array view methods diff --git a/tests/assign.rs b/tests/assign.rs index 19156cce8..5c300943d 100644 --- a/tests/assign.rs +++ b/tests/assign.rs @@ -41,14 +41,14 @@ fn move_into_copy() { let a = arr2(&[[1., 2.], [3., 4.]]); let acopy = a.clone(); let mut b = Array::uninit(a.dim()); - a.move_into(b.view_mut()); + a.move_into_uninit(b.view_mut()); let b = unsafe { b.assume_init() }; assert_eq!(acopy, b); let a = arr2(&[[1., 2.], [3., 4.]]).reversed_axes(); let acopy = a.clone(); let mut b = Array::uninit(a.dim()); - a.move_into(b.view_mut()); + a.move_into_uninit(b.view_mut()); let b = unsafe { b.assume_init() }; assert_eq!(acopy, b); } @@ -74,7 +74,7 @@ fn move_into_owned() { let acopy = a.clone(); let mut b = Array::uninit(a.dim()); - a.move_into(b.view_mut()); + a.move_into_uninit(b.view_mut()); let b = unsafe { b.assume_init() }; assert_eq!(acopy, b); @@ -85,7 +85,7 @@ fn move_into_owned() { #[test] fn move_into_slicing() { - // Count correct number of drops when using move_into and discontiguous arrays (with holes). + // Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes). for &use_f_order in &[false, true] { for &invert_axis in &[0b00, 0b01, 0b10, 0b11] { // bitmask for axis to invert let counter = DropCounter::default(); @@ -102,7 +102,7 @@ fn move_into_slicing() { } let mut b = Array::uninit(a.dim()); - a.move_into(b.view_mut()); + a.move_into_uninit(b.view_mut()); let b = unsafe { b.assume_init() }; let total = m * n; @@ -118,7 +118,7 @@ fn move_into_slicing() { #[test] fn move_into_diag() { - // Count correct number of drops when using move_into and discontiguous arrays (with holes). + // Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes). for &use_f_order in &[false, true] { let counter = DropCounter::default(); { @@ -128,7 +128,7 @@ fn move_into_diag() { let a = a.into_diag(); let mut b = Array::uninit(a.dim()); - a.move_into(b.view_mut()); + a.move_into_uninit(b.view_mut()); let b = unsafe { b.assume_init() }; let total = m * n; @@ -143,7 +143,7 @@ fn move_into_diag() { #[test] fn move_into_0dim() { - // Count correct number of drops when using move_into and discontiguous arrays (with holes). + // Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes). for &use_f_order in &[false, true] { let counter = DropCounter::default(); { @@ -155,7 +155,7 @@ fn move_into_0dim() { assert_eq!(a.ndim(), 0); let mut b = Array::uninit(a.dim()); - a.move_into(b.view_mut()); + a.move_into_uninit(b.view_mut()); let b = unsafe { b.assume_init() }; let total = m * n; @@ -170,7 +170,7 @@ fn move_into_0dim() { #[test] fn move_into_empty() { - // Count correct number of drops when using move_into and discontiguous arrays (with holes). + // Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes). for &use_f_order in &[false, true] { let counter = DropCounter::default(); { @@ -181,7 +181,7 @@ fn move_into_empty() { let a = a.slice_move(s![..0, 1..1]); assert!(a.is_empty()); let mut b = Array::uninit(a.dim()); - a.move_into(b.view_mut()); + a.move_into_uninit(b.view_mut()); let b = unsafe { b.assume_init() }; let total = m * n; @@ -194,6 +194,35 @@ fn move_into_empty() { } } +#[test] +fn move_into() { + // Test various memory layouts and holes while moving String elements with move_into + for &use_f_order in &[false, true] { + for &invert_axis in &[0b00, 0b01, 0b10, 0b11] { // bitmask for axis to invert + for &slice in &[false, true] { + let mut a = Array::from_shape_fn((5, 4).set_f(use_f_order), + |idx| format!("{:?}", idx)); + if slice { + a.slice_collapse(s![1..-1, ..;2]); + } + + if invert_axis & 0b01 != 0 { + a.invert_axis(Axis(0)); + } + if invert_axis & 0b10 != 0 { + a.invert_axis(Axis(1)); + } + + let acopy = a.clone(); + let mut b = Array::default(a.dim().set_f(!use_f_order ^ !slice)); + a.move_into(&mut b); + + assert_eq!(acopy, b); + } + } + } +} + /// This counter can create elements, and then count and verify /// the number of which have actually been dropped again. From 7172504f0b6502fcfc196191b01525655c0acbaa Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 13 May 2021 09:49:47 +0200 Subject: [PATCH 353/651] DOC: Update changelog for 0.15.2 --- Cargo.toml | 2 +- RELEASES.md | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b2c53ca29..5e241f398 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ndarray" -version = "0.15.1" +version = "0.15.2" edition = "2018" authors = [ "bluss", diff --git a/RELEASES.md b/RELEASES.md index be479c874..386ed24d9 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,87 @@ +Version 0.15.2 (2021-xx-xx Not released yet) +=========================== + +New features +------------ + +- New methods for growing/appending to owned `Array`s. These methods allow + building an array efficiently chunk by chunk. By [@bluss]. + + - `.push_row()`, `.push_column()` + - `.push(axis, array)`, `.append(axis, array)` + + https://github.com/rust-ndarray/ndarray/pull/932
+ https://github.com/rust-ndarray/ndarray/pull/990 + +- New reshaping method `.to_shape(...)`, called with new shape and optional + ordering parameter, this is the first improvement for reshaping in terms of + added features and increased consistency. By [@bluss]. + + https://github.com/rust-ndarray/ndarray/pull/982 + +- `Array` now implements a by-value iterator, by [@bluss]. + + https://github.com/rust-ndarray/ndarray/pull/986 + +- New methods `.move_into()` and `.move_into_uninit()` which allow assigning + into an array by moving values into them, by [@bluss]. + + https://github.com/rust-ndarray/ndarray/pull/932
+ https://github.com/rust-ndarray/ndarray/pull/997 + +- New method `.remove_index()` for owned arrays by [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/967 + +- New constructor `build_uninit` which makes it easier to initialize + uninitialized arrays in a way that's generic over all owned array kinds. + By [@bluss]. + + https://github.com/rust-ndarray/ndarray/pull/1001 + +Enhancements +------------ + +- Preserve the allocation of the input array in some more cases for arithmetic ops by [@SparrowLii] + + https://github.com/rust-ndarray/ndarray/pull/963 + +- Improve broadcasting performance for &array + &array arithmetic ops by [@SparrowLii] + + https://github.com/rust-ndarray/ndarray/pull/965 + +Bug fixes +--------- + +- Fix an error in construction of empty array with negative strides, by [@jturner314]. + + https://github.com/rust-ndarray/ndarray/pull/998 + +- Fix minor performance bug with loop order selection in Zip by [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/977 + +API changes +----------- + +- Add dimension getters to `Shape` and `StrideShape` by [@stokhos] + + https://github.com/rust-ndarray/ndarray/pull/978 + +Other changes +------------- + +- Rustdoc now uses the ndarray logo that [@jturner314] created previously + + https://github.com/rust-ndarray/ndarray/pull/981 + +- Minor doc changes by [@stokhos], [@cassiersg] and [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/968
+ https://github.com/rust-ndarray/ndarray/pull/971
+ https://github.com/rust-ndarray/ndarray/pull/974 + + Version 0.15.1 (2021-03-29) =========================== @@ -1286,6 +1370,7 @@ Earlier releases [@LukeMathWalker]: https://github.com/LukeMathWalker [@acj]: https://github.com/acj [@andrei-papou]: https://github.com/andrei-papou +[@cassiersg]: https://github.com/cassiersg [@dam5h]: https://github.com/dam5h [@d-dorazio]: https://github.com/d-dorazio [@Eijebong]: https://github.com/Eijebong @@ -1301,6 +1386,7 @@ Earlier releases [@rth]: https://github.com/rth [@sebasv]: https://github.com/sebasv [@SparrowLii]: https://github.com/SparrowLii +[@stokhos]: https://github.com/stokhos [@termoshtt]: https://github.com/termoshtt [@TheLortex]: https://github.com/TheLortex [@viniciusd]: https://github.com/viniciusd From 2a8b58a28980722a7198abb4d953fe1da3bcaa31 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 14 May 2021 12:32:44 +0200 Subject: [PATCH 354/651] debloat: Factor out layout c/f check from layout computation Factor out the code that checks C/F memory layout into standalone functions. These functions will be more useful on their own. Also factor out the NdProducer Layout computation function, so that more of that code is shared between generic instatiations. Tested using cargo llvm-lines. --- src/dimension/mod.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++ src/impl_methods.rs | 18 +--------------- src/zip/mod.rs | 48 +++++++++++++++++++++++------------------- 3 files changed, 78 insertions(+), 38 deletions(-) diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 732006e2b..7bb92892c 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -668,6 +668,56 @@ pub fn slices_intersect( true } +pub(crate) fn is_layout_c(dim: &D, strides: &D) -> bool { + if let Some(1) = D::NDIM { + return strides[0] == 1 || dim[0] <= 1; + } + + for &d in dim.slice() { + if d == 0 { + return true; + } + } + + let mut contig_stride = 1_isize; + // check all dimensions -- a dimension of length 1 can have unequal strides + for (&dim, &s) in izip!(dim.slice().iter().rev(), strides.slice().iter().rev()) { + if dim != 1 { + let s = s as isize; + if s != contig_stride { + return false; + } + contig_stride *= dim as isize; + } + } + true +} + +pub(crate) fn is_layout_f(dim: &D, strides: &D) -> bool { + if let Some(1) = D::NDIM { + return strides[0] == 1 || dim[0] <= 1; + } + + for &d in dim.slice() { + if d == 0 { + return true; + } + } + + let mut contig_stride = 1_isize; + // check all dimensions -- a dimension of length 1 can have unequal strides + for (&dim, &s) in izip!(dim.slice(), strides.slice()) { + if dim != 1 { + let s = s as isize; + if s != contig_stride { + return false; + } + contig_stride *= dim as isize; + } + } + true +} + pub fn merge_axes(dim: &mut D, strides: &mut D, take: Axis, into: Axis) -> bool where D: Dimension, diff --git a/src/impl_methods.rs b/src/impl_methods.rs index c441efccd..d238f0245 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1382,23 +1382,7 @@ where /// Return `false` otherwise, i.e. the array is possibly not /// contiguous in memory, it has custom strides, etc. pub fn is_standard_layout(&self) -> bool { - fn is_standard_layout(dim: &D, strides: &D) -> bool { - if let Some(1) = D::NDIM { - return strides[0] == 1 || dim[0] <= 1; - } - if dim.slice().iter().any(|&d| d == 0) { - return true; - } - let defaults = dim.default_strides(); - // check all dimensions -- a dimension of length 1 can have unequal strides - for (&dim, &s, &ds) in izip!(dim.slice(), strides.slice(), defaults.slice()) { - if dim != 1 && s != ds { - return false; - } - } - true - } - is_standard_layout(&self.dim, &self.strides) + dimension::is_layout_c(&self.dim, &self.strides) } /// Return true if the array is known to be contiguous. diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 7277f99f9..1ba0181c0 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -21,6 +21,7 @@ use crate::partial::Partial; use crate::indexes::{indices, Indices}; use crate::split_at::{SplitPreference, SplitAt}; +use crate::dimension; pub use self::ndproducer::{NdProducer, IntoNdProducer, Offset}; @@ -51,33 +52,38 @@ where private_decl! {} } +/// Compute `Layout` hints for array shape dim, strides +fn array_layout(dim: &D, strides: &D) -> Layout { + let n = dim.ndim(); + if dimension::is_layout_c(dim, strides) { + // effectively one-dimensional => C and F layout compatible + if n <= 1 || dim.slice().iter().filter(|&&len| len > 1).count() <= 1 { + Layout::one_dimensional() + } else { + Layout::c() + } + } else if n > 1 && dimension::is_layout_f(dim, strides) { + Layout::f() + } else if n > 1 { + if dim[0] > 1 && strides[0] == 1 { + Layout::fpref() + } else if dim[n - 1] > 1 && strides[n - 1] == 1 { + Layout::cpref() + } else { + Layout::none() + } + } else { + Layout::none() + } +} + impl ArrayBase where S: RawData, D: Dimension, { pub(crate) fn layout_impl(&self) -> Layout { - let n = self.ndim(); - if self.is_standard_layout() { - // effectively one-dimensional => C and F layout compatible - if n <= 1 || self.shape().iter().filter(|&&len| len > 1).count() <= 1 { - Layout::one_dimensional() - } else { - Layout::c() - } - } else if n > 1 && self.raw_view().reversed_axes().is_standard_layout() { - Layout::f() - } else if n > 1 { - if self.len_of(Axis(0)) > 1 && self.stride_of(Axis(0)) == 1 { - Layout::fpref() - } else if self.len_of(Axis(n - 1)) > 1 && self.stride_of(Axis(n - 1)) == 1 { - Layout::cpref() - } else { - Layout::none() - } - } else { - Layout::none() - } + array_layout(&self.dim, &self.strides) } } From 2ddc9dbd0234c1f4b03f759ed32fdf0fec8f6aaf Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 14 May 2021 13:07:40 +0200 Subject: [PATCH 355/651] debloat: Debloat pointer inbounds assert Debloat by pushing the code down into the DataReprs, this avoids instantiating the pointer check code for the views (where it can't do anything anyway). --- src/data_traits.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++ src/impl_methods.rs | 12 +--------- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index 7d9f15b0a..bd540b1b8 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -33,8 +33,12 @@ pub unsafe trait RawData: Sized { #[doc(hidden)] // This method is only used for debugging + #[deprecated(note="Unused", since="0.15.2")] fn _data_slice(&self) -> Option<&[Self::Elem]>; + #[doc(hidden)] + fn _is_pointer_inbounds(&self, ptr: *const Self::Elem) -> bool; + private_decl! {} } @@ -146,9 +150,15 @@ pub unsafe trait DataMut: Data + RawDataMut { unsafe impl
RawData for RawViewRepr<*const A> { type Elem = A; + + #[inline] fn _data_slice(&self) -> Option<&[A]> { None } + + #[inline(always)] + fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true } + private_impl! {} } @@ -160,9 +170,15 @@ unsafe impl RawDataClone for RawViewRepr<*const A> { unsafe impl RawData for RawViewRepr<*mut A> { type Elem = A; + + #[inline] fn _data_slice(&self) -> Option<&[A]> { None } + + #[inline(always)] + fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true } + private_impl! {} } @@ -192,6 +208,11 @@ unsafe impl RawData for OwnedArcRepr { fn _data_slice(&self) -> Option<&[A]> { Some(self.0.as_slice()) } + + fn _is_pointer_inbounds(&self, self_ptr: *const Self::Elem) -> bool { + self.0._is_pointer_inbounds(self_ptr) + } + private_impl! {} } @@ -274,9 +295,18 @@ unsafe impl RawDataClone for OwnedArcRepr { unsafe impl RawData for OwnedRepr { type Elem = A; + fn _data_slice(&self) -> Option<&[A]> { Some(self.as_slice()) } + + fn _is_pointer_inbounds(&self, self_ptr: *const Self::Elem) -> bool { + let slc = self.as_slice(); + let ptr = slc.as_ptr() as *mut A; + let end = unsafe { ptr.add(slc.len()) }; + self_ptr >= ptr && self_ptr <= end + } + private_impl! {} } @@ -340,9 +370,15 @@ where unsafe impl<'a, A> RawData for ViewRepr<&'a A> { type Elem = A; + + #[inline] fn _data_slice(&self) -> Option<&[A]> { None } + + #[inline(always)] + fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true } + private_impl! {} } @@ -364,9 +400,15 @@ unsafe impl<'a, A> RawDataClone for ViewRepr<&'a A> { unsafe impl<'a, A> RawData for ViewRepr<&'a mut A> { type Elem = A; + + #[inline] fn _data_slice(&self) -> Option<&[A]> { None } + + #[inline(always)] + fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true } + private_impl! {} } @@ -458,12 +500,23 @@ unsafe impl DataOwned for OwnedArcRepr { unsafe impl<'a, A> RawData for CowRepr<'a, A> { type Elem = A; + fn _data_slice(&self) -> Option<&[A]> { + #[allow(deprecated)] match self { CowRepr::View(view) => view._data_slice(), CowRepr::Owned(data) => data._data_slice(), } } + + #[inline] + fn _is_pointer_inbounds(&self, ptr: *const Self::Elem) -> bool { + match self { + CowRepr::View(view) => view._is_pointer_inbounds(ptr), + CowRepr::Owned(data) => data._is_pointer_inbounds(ptr), + } + } + private_impl! {} } diff --git a/src/impl_methods.rs b/src/impl_methods.rs index d238f0245..21470ec0f 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2135,17 +2135,7 @@ where } pub(crate) fn pointer_is_inbounds(&self) -> bool { - match self.data._data_slice() { - None => { - // special case for non-owned views - true - } - Some(slc) => { - let ptr = slc.as_ptr() as *mut A; - let end = unsafe { ptr.add(slc.len()) }; - self.ptr.as_ptr() >= ptr && self.ptr.as_ptr() <= end - } - } + self.data._is_pointer_inbounds(self.as_ptr()) } /// Perform an elementwise assigment to `self` from `rhs`. From 864cccf90bbc54e5d4466db05b7f16f386d1cbaf Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 17 May 2021 21:05:32 +0200 Subject: [PATCH 356/651] 0.15.2 --- Cargo.toml | 2 +- LICENSE-MIT | 2 +- RELEASES.md | 15 +++++++++++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5e241f398..31bab8d33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ name = "ndarray" version = "0.15.2" edition = "2018" authors = [ - "bluss", + "Ulrik Sverdrup \"bluss\"", "Jim Turner" ] license = "MIT OR Apache-2.0" diff --git a/LICENSE-MIT b/LICENSE-MIT index c87e92dc4..d0af99b04 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2015 - 2018 Ulrik Sverdrup "bluss", +Copyright (c) 2015 - 2021 Ulrik Sverdrup "bluss", Jim Turner, and ndarray developers diff --git a/RELEASES.md b/RELEASES.md index 386ed24d9..da895d94b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,5 @@ -Version 0.15.2 (2021-xx-xx Not released yet) -=========================== +Version 0.15.2 (2021-05-17 🇳🇴) +================================ New features ------------ @@ -10,12 +10,15 @@ New features - `.push_row()`, `.push_column()` - `.push(axis, array)`, `.append(axis, array)` + `stack`, `concatenate` and `.select()` now support all `Clone`-able elements + as a result. + https://github.com/rust-ndarray/ndarray/pull/932
https://github.com/rust-ndarray/ndarray/pull/990 - New reshaping method `.to_shape(...)`, called with new shape and optional ordering parameter, this is the first improvement for reshaping in terms of - added features and increased consistency. By [@bluss]. + added features and increased consistency, with more to come. By [@bluss]. https://github.com/rust-ndarray/ndarray/pull/982 @@ -24,7 +27,7 @@ New features https://github.com/rust-ndarray/ndarray/pull/986 - New methods `.move_into()` and `.move_into_uninit()` which allow assigning - into an array by moving values into them, by [@bluss]. + into an array by moving values from an array into another, by [@bluss]. https://github.com/rust-ndarray/ndarray/pull/932
https://github.com/rust-ndarray/ndarray/pull/997 @@ -81,6 +84,10 @@ Other changes https://github.com/rust-ndarray/ndarray/pull/971
https://github.com/rust-ndarray/ndarray/pull/974 +- A little refactoring to reduce generics bloat in a few places by [@bluss]. + + https://github.com/rust-ndarray/ndarray/pull/1004 + Version 0.15.1 (2021-03-29) =========================== From f21c668af9641e596eb54d0e89fc8ee52fc3e85c Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 18 May 2021 20:59:52 +0200 Subject: [PATCH 357/651] append: Update docs for push_row/column, push, append A few things stuck around in the doc that came from the earlier versions of these methods. Correct the doc so that it's easier to understand and remove the part about where it can fail when it can not. --- src/impl_owned_array.rs | 110 ++++++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 44 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index d148a115b..efe67a046 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -80,20 +80,29 @@ where impl
Array { /// Append a row to an array /// + /// The elements from `row` are cloned and added as a new row in the array. + /// /// ***Errors*** with a shape error if the length of the row does not match the length of the - /// rows in the array.
+ /// rows in the array. /// /// The memory layout of the `self` array matters for ensuring that the append is efficient. /// Appending automatically changes memory layout of the array so that it is appended to - /// along the "growing axis". + /// along the "growing axis". However, if the memory layout needs adjusting, the array must + /// reallocate and move memory. + /// + /// The operation leaves the existing data in place and is most efficent if one of these is + /// true: /// - /// Ensure appending is efficient by, for example, appending to an empty array and then - /// always appending along the same axis. For rows, ndarray's default layout is efficient for - /// appending. + /// - The axis being appended to is the longest stride axis, i.e the array is in row major + /// ("C") layout. + /// - The array has 0 or 1 rows (It is converted to row major) /// - /// Notice that an empty array (where it has an axis of length zero) is the simplest starting - /// point. When repeatedly appending to a single axis, the amortized average complexity of each append is O(m), where *m* is the length of - /// the row. + /// Ensure appending is efficient by, for example, appending to an empty array and then always + /// pushing/appending along the same axis. For pushing rows, ndarray's default layout (C order) + /// is efficient. + /// + /// When repeatedly appending to a single axis, the amortized average complexity of each + /// append is O(m), where *m* is the length of the row. /// /// ```rust /// use ndarray::{Array, ArrayView, array}; @@ -117,20 +126,29 @@ impl
Array { /// Append a column to an array /// + /// The elements from `column` are cloned and added as a new row in the array. + /// /// ***Errors*** with a shape error if the length of the column does not match the length of - /// the columns in the array.
+ /// the columns in the array. /// /// The memory layout of the `self` array matters for ensuring that the append is efficient. /// Appending automatically changes memory layout of the array so that it is appended to - /// along the "growing axis". + /// along the "growing axis". However, if the memory layout needs adjusting, the array must + /// reallocate and move memory. /// - /// Ensure appending is efficient by, for example, appending to an empty array and then - /// always appending along the same axis. For columns, column major ("F") memory layout is - /// efficient for appending. + /// The operation leaves the existing data in place and is most efficent if one of these is + /// true: /// - /// Notice that an empty array (where it has an axis of length zero) is the simplest starting - /// point. When repeatedly appending to a single axis, the amortized average complexity of each append is O(m), where *m* is the length of - /// the row. + /// - The axis being appended to is the longest stride axis, i.e the array is in column major + /// ("F") layout. + /// - The array has 0 or 1 columns (It is converted to column major) + /// + /// Ensure appending is efficient by, for example, appending to an empty array and then always + /// pushing/appending along the same axis. For pushing columns, column major layout (F order) + /// is efficient. + /// + /// When repeatedly appending to a single axis, the amortized average complexity of each append + /// is O(m), where *m* is the length of the column. /// /// ```rust /// use ndarray::{Array, ArrayView, array}; @@ -339,28 +357,30 @@ impl Array } } - /// Append an array to the array along an axis - /// - /// Where the item to push to the array has one dimension less than the `self` array. This - /// method is equivalent to `self.append(axis, array.insert_axis(axis))`. + /// Append an array to the array along an axis. /// - /// The axis-to-append-to `axis` must be the array's "growing axis" for this operation - /// to succeed. The growing axis is the outermost or last-visited when elements are visited in - /// memory order: + /// The elements of `array` are cloned and extend the axis `axis` in the present array; + /// `self` will grow in size by 1 along `axis`. /// - /// `axis` must be the growing axis of the current array, an axis with length 0 or 1. - /// - /// - This is the 0th axis for standard layout arrays - /// - This is the *n*-1 th axis for fortran layout arrays - /// - If the array is empty (the axis or any other has length 0) or if `axis` - /// has length 1, then the array can always be appended. + /// Append to the array, where the array being pushed to the array has one dimension less than + /// the `self` array. This method is equivalent to [append](ArrayBase::append) in this way: + /// `self.append(axis, array.insert_axis(axis))`. /// /// ***Errors*** with a shape error if the shape of self does not match the array-to-append; - /// all axes *except* the axis along which it being appended matter for this check. + /// all axes *except* the axis along which it being appended matter for this check: + /// the shape of `self` with `axis` removed must be the same as the shape of `array`. /// /// The memory layout of the `self` array matters for ensuring that the append is efficient. /// Appending automatically changes memory layout of the array so that it is appended to - /// along the "growing axis". + /// along the "growing axis". However, if the memory layout needs adjusting, the array must + /// reallocate and move memory. + /// + /// The operation leaves the existing data in place and is most efficent if `axis` is a + /// "growing axis" for the array, i.e. one of these is true: + /// + /// - The axis is the longest stride axis, for example the 0th axis in a C-layout or the + /// *n-1*th axis in an F-layout array. + /// - The axis has length 0 or 1 (It is converted to the new growing axis) /// /// Ensure appending is efficient by for example starting from an empty array and/or always /// appending to an array along the same axis. @@ -398,25 +418,27 @@ impl Array } - /// Append an array to the array along an axis - /// - /// The axis-to-append-to `axis` must be the array's "growing axis" for this operation - /// to succeed. The growing axis is the outermost or last-visited when elements are visited in - /// memory order: + /// Append an array to the array along an axis. /// - /// `axis` must be the growing axis of the current array, an axis with length 0 or 1. - /// - /// - This is the 0th axis for standard layout arrays - /// - This is the *n*-1 th axis for fortran layout arrays - /// - If the array is empty (the axis or any other has length 0) or if `axis` - /// has length 1, then the array can always be appended. + /// The elements of `array` are cloned and extend the axis `axis` in the present array; + /// `self` will grow in size by `array.len_of(axis)` along `axis`. /// /// ***Errors*** with a shape error if the shape of self does not match the array-to-append; - /// all axes *except* the axis along which it being appended matter for this check. + /// all axes *except* the axis along which it being appended matter for this check: + /// the shape of `self` with `axis` removed must be the same as the shape of `array` with + /// `axis` removed. /// /// The memory layout of the `self` array matters for ensuring that the append is efficient. /// Appending automatically changes memory layout of the array so that it is appended to - /// along the "growing axis". + /// along the "growing axis". However, if the memory layout needs adjusting, the array must + /// reallocate and move memory. + /// + /// The operation leaves the existing data in place and is most efficent if `axis` is a + /// "growing axis" for the array, i.e. one of these is true: + /// + /// - The axis is the longest stride axis, for example the 0th axis in a C-layout or the + /// *n-1*th axis in an F-layout array. + /// - The axis has length 0 or 1 (It is converted to the new growing axis) /// /// Ensure appending is efficient by for example starting from an empty array and/or always /// appending to an array along the same axis. From b326ebe34ed3b8c327aeeb5ddd6e6bb25dc6c586 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 19 May 2021 19:51:06 +0200 Subject: [PATCH 358/651] MAINT: Fix warnings in examples Add attributes to fail example tests on warnings. Exempt a few warnings, for example unused variables, which are not worth fixing (would make the examples worse in many cases). --- src/arraytraits.rs | 1 - src/doc/ndarray_for_numpy_users/coord_transform.rs | 2 +- src/doc/ndarray_for_numpy_users/mod.rs | 2 +- src/doc/ndarray_for_numpy_users/rk_step.rs | 4 ++-- src/impl_constructors.rs | 10 +++++----- src/impl_methods.rs | 7 +++---- src/impl_views/conversions.rs | 2 +- src/lib.rs | 3 +++ src/prelude.rs | 3 ++- src/slice.rs | 2 +- src/zip/mod.rs | 2 +- 11 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 0d273ec31..421fc4713 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -176,7 +176,6 @@ where /// /// ```rust /// use ndarray::{Array, arr1}; - /// use std::iter::FromIterator; /// /// // Either use `from_iter` directly or use `Iterator::collect`. /// let array = Array::from_iter((0..5).map(|x| x * x)); diff --git a/src/doc/ndarray_for_numpy_users/coord_transform.rs b/src/doc/ndarray_for_numpy_users/coord_transform.rs index e019fd1b1..09295be9d 100644 --- a/src/doc/ndarray_for_numpy_users/coord_transform.rs +++ b/src/doc/ndarray_for_numpy_users/coord_transform.rs @@ -83,7 +83,7 @@ //! for i in 0..nelems { //! rotated //! .slice_mut(s![.., .., i]) -//! .assign({ &rmat.slice(s![.., .., i]).dot(&eye2d) }); +//! .assign(&rmat.slice(s![.., .., i]).dot(&eye2d)); //! } //! } //! ``` diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index 478cc2cec..bedd1c8b5 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -179,7 +179,7 @@ //! ``` //! use ndarray::prelude::*; //! # -//! # fn main() {} +//! # fn main() { let _ = arr0(1); } //! ``` //! //! ## Array creation diff --git a/src/doc/ndarray_for_numpy_users/rk_step.rs b/src/doc/ndarray_for_numpy_users/rk_step.rs index 2c5ce2362..92935f3e7 100644 --- a/src/doc/ndarray_for_numpy_users/rk_step.rs +++ b/src/doc/ndarray_for_numpy_users/rk_step.rs @@ -104,7 +104,7 @@ //! (y_new, f_new, error) //! } //! # -//! # fn main() {} +//! # fn main() { let _ = rk_step::) -> _>; } //! ``` //! //! It's possible to improve the efficiency by doing the following: @@ -165,7 +165,7 @@ //! (y_new, error) //! } //! # -//! # fn main() {} +//! # fn main() { let _ = rk_step::, ArrayViewMut1<'_, f64>)>; } //! ``` //! //! [f64.mul_add()]: https://doc.rust-lang.org/std/primitive.f64.html#method.mul_add diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 62b951695..46d0c76c5 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -133,10 +133,10 @@ where /// to type `A` fails. /// /// ```rust + /// # #[cfg(feature = "approx")] { /// use approx::assert_abs_diff_eq; /// use ndarray::{Array, arr1}; /// - /// # #[cfg(feature = "approx")] { /// let array = Array::logspace(10.0, 0.0, 3.0, 4); /// assert_abs_diff_eq!(array, arr1(&[1e0, 1e1, 1e2, 1e3])); /// @@ -163,11 +163,11 @@ where /// to type `A` fails. /// /// ```rust + /// # fn example() -> Option<()> { + /// # #[cfg(feature = "approx")] { /// use approx::assert_abs_diff_eq; /// use ndarray::{Array, arr1}; /// - /// # fn example() -> Option<()> { - /// # #[cfg(feature = "approx")] { /// let array = Array::geomspace(1e0, 1e3, 4)?; /// assert_abs_diff_eq!(array, arr1(&[1e0, 1e1, 1e2, 1e3]), epsilon = 1e-12); /// @@ -541,8 +541,6 @@ where /// /// ``` /// use ndarray::{s, Array2}; - /// use ndarray::Zip; - /// use ndarray::Axis; /// /// // Example Task: Let's create a column shifted copy of the input /// @@ -561,6 +559,8 @@ where /// b.assume_init() /// } /// } + /// + /// # let _ = shift_by_two; /// ``` pub fn uninit(shape: Sh) -> ArrayBase where diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 21470ec0f..28098f68f 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -928,7 +928,7 @@ where /// Iterator element is `ArrayView1
` (1D array view). /// /// ``` - /// use ndarray::{arr3, Axis, arr1}; + /// use ndarray::arr3; /// /// let a = arr3(&[[[ 0, 1, 2], // -- row 0, 0 /// [ 3, 4, 5]], // -- row 0, 1 @@ -996,7 +996,7 @@ where /// Iterator element is `ArrayView1` (1D array view). /// /// ``` - /// use ndarray::{arr3, Axis, arr1}; + /// use ndarray::arr3; /// /// // The generalized columns of a 3D array: /// // are directed along the 0th axis: 0 and 6, 1 and 7 and so on... @@ -1177,7 +1177,6 @@ where /// ``` /// use ndarray::Array; /// use ndarray::{arr3, Axis}; - /// use std::iter::FromIterator; /// /// let a = Array::from_iter(0..28).into_shape((2, 7, 2)).unwrap(); /// let mut iter = a.axis_chunks_iter(Axis(1), 2); @@ -2397,10 +2396,10 @@ where /// Elements are visited in arbitrary order. /// /// ``` + /// # #[cfg(feature = "approx")] { /// use approx::assert_abs_diff_eq; /// use ndarray::arr2; /// - /// # #[cfg(feature = "approx")] { /// let mut a = arr2(&[[ 0., 1.], /// [-1., 2.]]); /// a.mapv_inplace(f32::exp); diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs index 5dba16d98..d0f91b22b 100644 --- a/src/impl_views/conversions.rs +++ b/src/impl_views/conversions.rs @@ -96,7 +96,7 @@ impl<'a, A> ArrayViewMut<'a, A, Ix0> { /// /// let mut array: Array0 = arr0(5.); /// let view = array.view_mut(); - /// let mut scalar = view.into_scalar(); + /// let scalar = view.into_scalar(); /// *scalar = 7.; /// assert_eq!(scalar, &7.); /// assert_eq!(array[()], 7.); diff --git a/src/lib.rs b/src/lib.rs index dfce924e3..8cf1a495f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,9 @@ clippy::while_let_on_iterator, // is not an error clippy::from_iter_instead_of_collect, // using from_iter is good style )] +#![doc(test(attr(deny(warnings))))] +#![doc(test(attr(allow(unused_variables))))] +#![doc(test(attr(allow(deprecated))))] #![cfg_attr(not(feature = "std"), no_std)] //! The `ndarray` crate provides an *n*-dimensional container for general elements diff --git a/src/prelude.rs b/src/prelude.rs index ea6dfb08f..a25fc8780 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -12,8 +12,9 @@ //! and macros that you can import easily as a group. //! //! ``` -//! //! use ndarray::prelude::*; +//! +//! # let _ = arr0(1); // use the import //! ``` #[doc(no_inline)] diff --git a/src/slice.rs b/src/slice.rs index 3cbdf2ee9..113b82d05 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -741,7 +741,7 @@ impl_slicenextdim!((), NewAxis, Ix0, Ix1); /// + v.slice(s![1..-1, 2.. ]) /// + v.slice(s![2.. , 1..-1]) /// } -/// # fn main() { } +/// # fn main() { let _ = laplacian; } /// ``` /// /// # Negative *step* diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 1ba0181c0..d611fabbc 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -184,7 +184,7 @@ trait ZippableTuple: Sized { /// /// // Example 3: Recreate Example 2 using map_collect to make a new array /// -/// let mut totals2 = Zip::from(a.rows()).map_collect(|row| row.sum()); +/// let totals2 = Zip::from(a.rows()).map_collect(|row| row.sum()); /// /// // Check the result against the previous example. /// assert_eq!(totals, totals2); From 516294b7d95baffa1508392dfc4b240264e254bf Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 26 May 2021 22:20:08 -0400 Subject: [PATCH 359/651] Add .last() and .last_mut() methods to ArrayBase --- src/impl_methods.rs | 60 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 28098f68f..f556f9b4e 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -280,6 +280,66 @@ where } } + /// Returns a reference to the last element of the array, or `None` if it + /// is empty. + /// + /// # Example + /// + /// ```rust + /// use ndarray::Array3; + /// + /// let mut a = Array3::::zeros([3, 4, 2]); + /// a[[2, 3, 1]] = 42.; + /// assert_eq!(a.last(), Some(&42.)); + /// + /// let b = Array3::::zeros([3, 0, 5]); + /// assert_eq!(b.last(), None); + /// ``` + pub fn last(&self) -> Option<&A> + where + S: Data, + { + if self.is_empty() { + None + } else { + let mut index = self.raw_dim(); + for ax in 0..index.ndim() { + index[ax] -= 1; + } + Some(unsafe { self.uget(index) }) + } + } + + /// Returns a mutable reference to the last element of the array, or `None` + /// if it is empty. + /// + /// # Example + /// + /// ```rust + /// use ndarray::Array3; + /// + /// let mut a = Array3::::zeros([3, 4, 2]); + /// *a.last_mut().unwrap() = 42.; + /// assert_eq!(a[[2, 3, 1]], 42.); + /// + /// let mut b = Array3::::zeros([3, 0, 5]); + /// assert_eq!(b.last_mut(), None); + /// ``` + pub fn last_mut(&mut self) -> Option<&mut A> + where + S: DataMut, + { + if self.is_empty() { + None + } else { + let mut index = self.raw_dim(); + for ax in 0..index.ndim() { + index[ax] -= 1; + } + Some(unsafe { self.uget_mut(index) }) + } + } + /// Return an iterator of references to the elements of the array. /// /// Elements are visited in the *logical order* of the array, which From 68f258c0d82d1f78f5428991246a33d279b58565 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 26 May 2021 22:24:04 -0400 Subject: [PATCH 360/651] Add examples to docs for .first() and .first_mut() --- src/impl_methods.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index f556f9b4e..8b1ace803 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -256,6 +256,19 @@ where /// Returns a reference to the first element of the array, or `None` if it /// is empty. + /// + /// # Example + /// + /// ```rust + /// use ndarray::Array3; + /// + /// let mut a = Array3::::zeros([3, 4, 2]); + /// a[[0, 0, 0]] = 42.; + /// assert_eq!(a.first(), Some(&42.)); + /// + /// let b = Array3::::zeros([3, 0, 5]); + /// assert_eq!(b.first(), None); + /// ``` pub fn first(&self) -> Option<&A> where S: Data, @@ -269,6 +282,19 @@ where /// Returns a mutable reference to the first element of the array, or /// `None` if it is empty. + /// + /// # Example + /// + /// ```rust + /// use ndarray::Array3; + /// + /// let mut a = Array3::::zeros([3, 4, 2]); + /// *a.first_mut().unwrap() = 42.; + /// assert_eq!(a[[0, 0, 0]], 42.); + /// + /// let mut b = Array3::::zeros([3, 0, 5]); + /// assert_eq!(b.first_mut(), None); + /// ``` pub fn first_mut(&mut self) -> Option<&mut A> where S: DataMut, From 0f15056e9dd7b74e905dac5cda4cb5f3098599bb Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 30 May 2021 00:36:44 -0400 Subject: [PATCH 361/651] Add From> for 1-D owned arrays --- src/arraytraits.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 421fc4713..07a82e2b7 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -11,6 +11,7 @@ use std::iter::FromIterator; use std::iter::IntoIterator; use std::mem; use std::ops::{Index, IndexMut}; +use alloc::boxed::Box; use alloc::vec::Vec; use crate::imp_prelude::*; @@ -148,6 +149,18 @@ where { } +impl From> for ArrayBase +where + S: DataOwned, +{ + /// Create a one-dimensional array from a boxed slice (no copying needed). + /// + /// **Panics** if the length is greater than `isize::MAX`. + fn from(b: Box<[A]>) -> Self { + Self::from_vec(b.into_vec()) + } +} + impl From> for ArrayBase where S: DataOwned, From b956dbc52fa4ad37f291ceeb01fab4e410bc0337 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 30 May 2021 21:25:32 -0400 Subject: [PATCH 362/651] Document 'unsharing' in as_mut_ptr and raw_view_mut --- src/impl_methods.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 28098f68f..53e8e4896 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1446,6 +1446,15 @@ where } /// Return a mutable pointer to the first element in the array. + /// + /// This method attempts to unshare the data. If `S: DataMut`, then the + /// data is guaranteed to be uniquely held on return. + /// + /// # Warning + /// + /// When accessing elements through this pointer, make sure to use strides + /// obtained *after* calling this method, since the process of unsharing + /// the data may change the strides. #[inline(always)] pub fn as_mut_ptr(&mut self) -> *mut A where @@ -1462,6 +1471,9 @@ where } /// Return a raw mutable view of the array. + /// + /// This method attempts to unshare the data. If `S: DataMut`, then the + /// data is guaranteed to be uniquely held on return. #[inline] pub fn raw_view_mut(&mut self) -> RawArrayViewMut where From cadee2b59a99311db11a29bbb9d2e13242b35820 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 31 May 2021 18:30:23 -0400 Subject: [PATCH 363/651] Remove unnecessary closure --- src/impl_methods.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 28098f68f..106e9280b 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -217,7 +217,7 @@ where ) } } else { - self.map(|x| x.clone()) + self.map(A::clone) } } From 15b0808503a403299503737a1174f89c163ec6f3 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 31 May 2021 18:51:16 -0400 Subject: [PATCH 364/651] Add test for issue #1018 --- tests/array.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/array.rs b/tests/array.rs index edd58adbc..d1027665f 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1788,6 +1788,29 @@ fn map_memory_order() { assert_eq!(amap.strides(), v.strides()); } +#[test] +fn map_mut_with_unsharing() { + // Fortran-layout `ArcArray`. + let a = rcarr2(&[[0, 5], [1, 6], [2, 7], [3, 8], [4, 9]]).reversed_axes(); + assert_eq!(a.shape(), &[2, 5]); + assert_eq!(a.strides(), &[1, 2]); + assert_eq!( + a.as_slice_memory_order(), + Some(&[0, 5, 1, 6, 2, 7, 3, 8, 4, 9][..]) + ); + + // Shared reference of a portion of `a`. + let mut b = a.clone().slice_move(s![.., ..2]); + assert_eq!(b.shape(), &[2, 2]); + assert_eq!(b.strides(), &[1, 2]); + assert_eq!(b.as_slice_memory_order(), Some(&[0, 5, 1, 6][..])); + assert_eq!(b, array![[0, 1], [5, 6]]); + + // `.map_mut()` unshares the data. Earlier versions of `ndarray` failed + // this assertion. See #1018. + assert_eq!(b.map_mut(|&mut x| x + 10), array![[10, 11], [15, 16]]); +} + #[test] fn test_view_from_shape() { let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; From 37645bdd588ebe11721573ac854d1c019da891ed Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 31 May 2021 19:00:48 -0400 Subject: [PATCH 365/651] Guarantee that as_slice_memory_order_mut preserves strides This fixes bugs in `.map_mut()` and `.zip_mut_with_same_shape()`. Before this commit, strides obtained before calling `.as_slice_memory_order_mut()` could not be used to correctly interpret the data in the returned slice. Now, the strides are preserved, so the implementations of `.map_mut()` and `.zip_mut_with_same_shape()` work correctly. This also makes it much easier for users of the crate to use `.as_slice_memory_order_mut()` correctly in generic code. Fixes #1018. --- src/data_traits.rs | 18 ++++++++---------- src/impl_methods.rs | 4 ++++ tests/array.rs | 40 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index bd540b1b8..858dc7c86 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -50,8 +50,11 @@ pub unsafe trait RawData: Sized { pub unsafe trait RawDataMut: RawData { /// If possible, ensures that the array has unique access to its data. /// - /// If `Self` provides safe mutable access to array elements, then it - /// **must** panic or ensure that the data is unique. + /// The implementer must ensure that if the input is contiguous, then the + /// output has the same strides as input. + /// + /// Additionally, if `Self` provides safe mutable access to array elements, + /// then this method **must** panic or ensure that the data is unique. #[doc(hidden)] fn try_ensure_unique(_: &mut ArrayBase) where @@ -230,14 +233,9 @@ where return; } if self_.dim.size() <= self_.data.0.len() / 2 { - // Create a new vec if the current view is less than half of - // backing data. - unsafe { - *self_ = ArrayBase::from_shape_vec_unchecked( - self_.dim.clone(), - self_.iter().cloned().collect(), - ); - } + // Clone only the visible elements if the current view is less than + // half of backing data. + *self_ = self_.to_owned().into_shared(); return; } let rcvec = &mut self_.data.0; diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 106e9280b..30776ef53 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1536,6 +1536,10 @@ where /// Return the array’s data as a slice if it is contiguous, /// return `None` otherwise. + /// + /// In the contiguous case, in order to return a unique reference, this + /// method unshares the data if necessary, but it preserves the existing + /// strides. pub fn as_slice_memory_order_mut(&mut self) -> Option<&mut [A]> where S: DataMut, diff --git a/tests/array.rs b/tests/array.rs index d1027665f..37065ba1b 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -990,8 +990,8 @@ fn map1() { } #[test] -fn as_slice_memory_order() { - // test that mutation breaks sharing +fn as_slice_memory_order_mut_arcarray() { + // Test that mutation breaks sharing for `ArcArray`. let a = rcarr2(&[[1., 2.], [3., 4.0f32]]); let mut b = a.clone(); for elt in b.as_slice_memory_order_mut().unwrap() { @@ -1000,6 +1000,38 @@ fn as_slice_memory_order() { assert!(a != b, "{:?} != {:?}", a, b); } +#[test] +fn as_slice_memory_order_mut_cowarray() { + // Test that mutation breaks sharing for `CowArray`. + let a = arr2(&[[1., 2.], [3., 4.0f32]]); + let mut b = CowArray::from(a.view()); + for elt in b.as_slice_memory_order_mut().unwrap() { + *elt = 0.; + } + assert!(a != b, "{:?} != {:?}", a, b); +} + +#[test] +fn as_slice_memory_order_mut_contiguous_arcarray() { + // Test that unsharing preserves the strides in the contiguous case for `ArcArray`. + let a = rcarr2(&[[0, 5], [1, 6], [2, 7], [3, 8], [4, 9]]).reversed_axes(); + let mut b = a.clone().slice_move(s![.., ..2]); + assert_eq!(b.strides(), &[1, 2]); + b.as_slice_memory_order_mut().unwrap(); + assert_eq!(b.strides(), &[1, 2]); +} + +#[test] +fn as_slice_memory_order_mut_contiguous_cowarray() { + // Test that unsharing preserves the strides in the contiguous case for `CowArray`. + let a = arr2(&[[0, 5], [1, 6], [2, 7], [3, 8], [4, 9]]).reversed_axes(); + let mut b = CowArray::from(a.slice(s![.., ..2])); + assert!(b.is_view()); + assert_eq!(b.strides(), &[1, 2]); + b.as_slice_memory_order_mut().unwrap(); + assert_eq!(b.strides(), &[1, 2]); +} + #[test] fn array0_into_scalar() { // With this kind of setup, the `Array`'s pointer is not the same as the @@ -1809,6 +1841,10 @@ fn map_mut_with_unsharing() { // `.map_mut()` unshares the data. Earlier versions of `ndarray` failed // this assertion. See #1018. assert_eq!(b.map_mut(|&mut x| x + 10), array![[10, 11], [15, 16]]); + + // The strides should be preserved. + assert_eq!(b.shape(), &[2, 2]); + assert_eq!(b.strides(), &[1, 2]); } #[test] From 01d67134adb930b3b27c32d59115707311ef1c02 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 31 May 2021 21:59:10 -0400 Subject: [PATCH 366/651] Implement RawDataSubst for CowRepr --- src/data_traits.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/data_traits.rs b/src/data_traits.rs index bd540b1b8..0f690a90e 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -675,3 +675,13 @@ impl<'a, A: 'a, B: 'a> RawDataSubst for ViewRepr<&'a mut A> { } } +impl<'a, A: 'a, B: 'a> RawDataSubst for CowRepr<'a, A> { + type Output = CowRepr<'a, B>; + + unsafe fn data_subst(self) -> Self::Output { + match self { + CowRepr::View(view) => CowRepr::View(view.data_subst()), + CowRepr::Owned(owned) => CowRepr::Owned(owned.data_subst()), + } + } +} From 0d9d382d5186b4f72cf7f85acb39c90c11d1997b Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 31 May 2021 21:48:32 -0400 Subject: [PATCH 367/651] Implement From> for ArcArray --- src/arraytraits.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 421fc4713..8630a189d 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -354,6 +354,15 @@ where } } +impl From> for ArcArray +where + D: Dimension, +{ + fn from(arr: Array) -> ArcArray { + arr.into_shared() + } +} + /// Argument conversion into an array view /// /// The trait is parameterized over `A`, the element type, and `D`, the From 307234e71dac87d72d7c1d955ed9f68e5e902623 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 5 Jun 2021 15:28:27 +0200 Subject: [PATCH 368/651] 0.15.3 --- Cargo.toml | 2 +- RELEASES.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 31bab8d33..75875d619 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ndarray" -version = "0.15.2" +version = "0.15.3" edition = "2018" authors = [ "Ulrik Sverdrup \"bluss\"", diff --git a/RELEASES.md b/RELEASES.md index da895d94b..cfb5e7d31 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,54 @@ +Version 0.15.3 (2021-06-05) +=========================== + +New features +------------ + +- New methods `.last/_mut()` for arrays and array views by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/1013 + +Bug fixes +--------- + +- Fix `as_slice_memory_order_mut()` so that it never changes strides (the + memory layout) of the array when called. + + This was a bug that impacted `ArcArray` (and for example not `Array` or `ArrayView/Mut`), + and multiple methods on `ArcArray` that use `as_slice_memory_order_mut` (for example `map_mut`). + Fix by [@jturner314]. + + https://github.com/rust-ndarray/ndarray/pull/1019 + +API changes +----------- + +- Array1 now implements `From>` by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/1016 + +- ArcArray now implements `From>` by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/1021 + +- CowArray now implements RawDataSubst by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/1020 + +Other changes +------------- + +- Mention unsharing in `.as_mut_ptr` docs by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/1017 + +- Clarify and fix minor errors in push/append method docs by [@bluss] f21c668a + +- Fix several warnings in doc example code by [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/1009 + + Version 0.15.2 (2021-05-17 🇳🇴) ================================ From 0968a710ad343930b7b24a5f632367062ffa2cd1 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 7 Jun 2021 00:53:58 -0400 Subject: [PATCH 369/651] Add view_re_im and view_mut_re_im methods These methods make it possible to obtain views of the real and imaginary components of elements in an array of complex elements. --- src/impl_methods.rs | 2 +- src/numeric/mod.rs | 158 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+), 1 deletion(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 51c1403a5..e597881d7 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1452,7 +1452,7 @@ where /// Make the array unshared. /// /// This method is mostly only useful with unsafe code. - fn ensure_unique(&mut self) + pub(crate) fn ensure_unique(&mut self) where S: DataMut, { diff --git a/src/numeric/mod.rs b/src/numeric/mod.rs index b3da06746..7d3127532 100644 --- a/src/numeric/mod.rs +++ b/src/numeric/mod.rs @@ -1 +1,159 @@ +use crate::imp_prelude::*; +use num_complex::Complex; +use rawpointer::PointerExt; +use std::mem; +use std::ptr::NonNull; + mod impl_numeric; + +impl ArrayBase +where + S: Data>, + D: Dimension, +{ + /// Returns views of the real and imaginary components of the elements. + /// + /// ``` + /// use ndarray::prelude::*; + /// use num_complex::{Complex, Complex64}; + /// + /// let arr = array![ + /// [Complex64::new(1., 2.), Complex64::new(3., 4.)], + /// [Complex64::new(5., 6.), Complex64::new(7., 8.)], + /// [Complex64::new(9., 10.), Complex64::new(11., 12.)], + /// ]; + /// let Complex { re, im } = arr.view_re_im(); + /// assert_eq!(re, array![[1., 3.], [5., 7.], [9., 11.]]); + /// assert_eq!(im, array![[2., 4.], [6., 8.], [10., 12.]]); + /// ``` + pub fn view_re_im(&self) -> Complex> { + debug_assert!(self.pointer_is_inbounds()); + + let dim = self.dim.clone(); + + // Double the strides. In the zero-sized element case and for axes of + // length <= 1, we leave the strides as-is to avoid possible overflow. + let mut strides = self.strides.clone(); + if mem::size_of::() != 0 { + for ax in 0..strides.ndim() { + if dim[ax] > 1 { + strides[ax] *= 2; + } + } + } + + let ptr_re: NonNull = self.ptr.cast(); + let ptr_im: NonNull = if self.is_empty() { + // In the empty case, we can just reuse the existing pointer since + // it won't be dereferenced anyway. It is not safe to offset by one + // since the allocation may be empty. + self.ptr.cast() + } else { + // Safe because `self` is nonempty, so we can safely offset into + // the first element. + unsafe { self.ptr.cast().add(1) } + }; + + // `Complex` is `repr(C)` with only fields `re: T` and `im: T`. So, the + // real components of the elements start at the same pointer, and the + // imaginary components start at the pointer offset by one, with + // exactly double the strides. The new, doubled strides still meet the + // overflow constraints: + // + // - For the zero-sized element case, the strides are unchanged in + // units of bytes and in units of the element type. + // + // - For the nonzero-sized element case: + // + // - In units of bytes, the strides are unchanged. + // + // - Since `Complex` for nonzero `T` is always at least 2 bytes, + // and the original strides did not overflow in units of bytes, we + // know that the new doubled strides will not overflow in units of + // `T`. + unsafe { + Complex { + re: ArrayView::new(ptr_re, dim.clone(), strides.clone()), + im: ArrayView::new(ptr_im, dim, strides), + } + } + } + + /// Returns mutable views of the real and imaginary components of the elements. + /// + /// ``` + /// use ndarray::prelude::*; + /// use num_complex::{Complex, Complex64}; + /// + /// let mut arr = array![ + /// [Complex64::new(1., 2.), Complex64::new(3., 4.)], + /// [Complex64::new(5., 6.), Complex64::new(7., 8.)], + /// [Complex64::new(9., 10.), Complex64::new(11., 12.)], + /// ]; + /// + /// let Complex { mut re, mut im } = arr.view_mut_re_im(); + /// assert_eq!(re, array![[1., 3.], [5., 7.], [9., 11.]]); + /// assert_eq!(im, array![[2., 4.], [6., 8.], [10., 12.]]); + /// + /// re[[0, 1]] = 13.; + /// im[[2, 0]] = 14.; + /// + /// assert_eq!(arr[[0, 1]], Complex64::new(13., 4.)); + /// assert_eq!(arr[[2, 0]], Complex64::new(9., 14.)); + /// ``` + pub fn view_mut_re_im(&mut self) -> Complex> + where + S: DataMut, + { + self.ensure_unique(); + + let dim = self.dim.clone(); + + // Double the strides. In the zero-sized element case and for axes of + // length <= 1, we leave the strides as-is to avoid possible overflow. + let mut strides = self.strides.clone(); + if mem::size_of::() != 0 { + for ax in 0..strides.ndim() { + if dim[ax] > 1 { + strides[ax] *= 2; + } + } + } + + let ptr_re: NonNull = self.ptr.cast(); + let ptr_im: NonNull = if self.is_empty() { + // In the empty case, we can just reuse the existing pointer since + // it won't be dereferenced anyway. It is not safe to offset by one + // since the allocation may be empty. + self.ptr.cast() + } else { + // Safe because `self` is nonempty, so we can safely offset into + // the first element. + unsafe { self.ptr.cast().add(1) } + }; + + // `Complex` is `repr(C)` with only fields `re: T` and `im: T`. So, the + // real components of the elements start at the same pointer, and the + // imaginary components start at the pointer offset by one, with + // exactly double the strides. The new, doubled strides still meet the + // overflow constraints: + // + // - For the zero-sized element case, the strides are unchanged in + // units of bytes and in units of the element type. + // + // - For the nonzero-sized element case: + // + // - In units of bytes, the strides are unchanged. + // + // - Since `Complex` for nonzero `T` is always at least 2 bytes, + // and the original strides did not overflow in units of bytes, we + // know that the new doubled strides will not overflow in units of + // `T`. + unsafe { + Complex { + re: ArrayViewMut::new(ptr_re, dim.clone(), strides.clone()), + im: ArrayViewMut::new(ptr_im, dim, strides), + } + } + } +} From 9cbc9755a256e697c691bfbbcdeccfcf352b3897 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 7 Jun 2021 03:42:43 -0400 Subject: [PATCH 370/651] Improve code style and clarity of comments --- src/numeric/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/numeric/mod.rs b/src/numeric/mod.rs index 7d3127532..c230007d8 100644 --- a/src/numeric/mod.rs +++ b/src/numeric/mod.rs @@ -47,11 +47,11 @@ where // In the empty case, we can just reuse the existing pointer since // it won't be dereferenced anyway. It is not safe to offset by one // since the allocation may be empty. - self.ptr.cast() + ptr_re } else { - // Safe because `self` is nonempty, so we can safely offset into - // the first element. - unsafe { self.ptr.cast().add(1) } + // In the nonempty case, we can safely offset into the first + // (complex) element. + unsafe { ptr_re.add(1) } }; // `Complex` is `repr(C)` with only fields `re: T` and `im: T`. So, the @@ -125,11 +125,11 @@ where // In the empty case, we can just reuse the existing pointer since // it won't be dereferenced anyway. It is not safe to offset by one // since the allocation may be empty. - self.ptr.cast() + ptr_re } else { - // Safe because `self` is nonempty, so we can safely offset into - // the first element. - unsafe { self.ptr.cast().add(1) } + // In the nonempty case, we can safely offset into the first + // (complex) element. + unsafe { ptr_re.add(1) } }; // `Complex` is `repr(C)` with only fields `re: T` and `im: T`. So, the From cd08a06b3a834838b53754954421c64b24dc1ec9 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 13 Jun 2021 17:20:03 -0400 Subject: [PATCH 371/651] Replace view_re_im with split_re_im --- src/impl_raw_views.rs | 85 +++++++++++++++++++ src/impl_views/splitting.rs | 70 ++++++++++++++++ src/numeric/mod.rs | 158 ------------------------------------ 3 files changed, 155 insertions(+), 158 deletions(-) diff --git a/src/impl_raw_views.rs b/src/impl_raw_views.rs index 2ac5c08c7..b12b2a727 100644 --- a/src/impl_raw_views.rs +++ b/src/impl_raw_views.rs @@ -1,3 +1,4 @@ +use num_complex::Complex; use std::mem; use std::ptr::NonNull; @@ -149,6 +150,73 @@ where } } +impl RawArrayView, D> +where + D: Dimension, +{ + /// Splits the view into views of the real and imaginary components of the + /// elements. + pub fn split_re_im(self) -> Complex> { + // Check that the size and alignment of `Complex` are as expected. + // These assertions should always pass, for arbitrary `T`. + assert_eq!( + mem::size_of::>(), + mem::size_of::().checked_mul(2).unwrap() + ); + assert_eq!(mem::align_of::>(), mem::align_of::()); + + let dim = self.dim.clone(); + + // Double the strides. In the zero-sized element case and for axes of + // length <= 1, we leave the strides as-is to avoid possible overflow. + let mut strides = self.strides.clone(); + if mem::size_of::() != 0 { + for ax in 0..strides.ndim() { + if dim[ax] > 1 { + strides[ax] *= 2; + } + } + } + + let ptr_re: *mut T = self.ptr.as_ptr().cast(); + let ptr_im: *mut T = if self.is_empty() { + // In the empty case, we can just reuse the existing pointer since + // it won't be dereferenced anyway. It is not safe to offset by + // one, since the allocation may be empty. + ptr_re + } else { + // In the nonempty case, we can safely offset into the first + // (complex) element. + unsafe { ptr_re.add(1) } + }; + + // `Complex` is `repr(C)` with only fields `re: T` and `im: T`. So, the + // real components of the elements start at the same pointer, and the + // imaginary components start at the pointer offset by one, with + // exactly double the strides. The new, doubled strides still meet the + // overflow constraints: + // + // - For the zero-sized element case, the strides are unchanged in + // units of bytes and in units of the element type. + // + // - For the nonzero-sized element case: + // + // - In units of bytes, the strides are unchanged. The only exception + // is axes of length <= 1, but those strides are irrelevant anyway. + // + // - Since `Complex` for nonzero `T` is always at least 2 bytes, + // and the original strides did not overflow in units of bytes, we + // know that the new, doubled strides will not overflow in units of + // `T`. + unsafe { + Complex { + re: RawArrayView::new_(ptr_re, dim.clone(), strides.clone()), + im: RawArrayView::new_(ptr_im, dim, strides), + } + } + } +} + impl RawArrayViewMut where D: Dimension, @@ -300,3 +368,20 @@ where unsafe { RawArrayViewMut::new(ptr, self.dim, self.strides) } } } + +impl RawArrayViewMut, D> +where + D: Dimension, +{ + /// Splits the view into views of the real and imaginary components of the + /// elements. + pub fn split_re_im(self) -> Complex> { + let Complex { re, im } = self.into_raw_view().split_re_im(); + unsafe { + Complex { + re: RawArrayViewMut::new(re.ptr, re.dim, re.strides), + im: RawArrayViewMut::new(im.ptr, im.dim, im.strides), + } + } + } +} diff --git a/src/impl_views/splitting.rs b/src/impl_views/splitting.rs index a36ae4ddb..5ea554d8a 100644 --- a/src/impl_views/splitting.rs +++ b/src/impl_views/splitting.rs @@ -8,6 +8,7 @@ use crate::imp_prelude::*; use crate::slice::MultiSliceArg; +use num_complex::Complex; /// Methods for read-only array views. impl<'a, A, D> ArrayView<'a, A, D> @@ -95,6 +96,37 @@ where } } +impl<'a, T, D> ArrayView<'a, Complex, D> +where + D: Dimension, +{ + /// Splits the view into views of the real and imaginary components of the + /// elements. + /// + /// ``` + /// use ndarray::prelude::*; + /// use num_complex::{Complex, Complex64}; + /// + /// let arr = array![ + /// [Complex64::new(1., 2.), Complex64::new(3., 4.)], + /// [Complex64::new(5., 6.), Complex64::new(7., 8.)], + /// [Complex64::new(9., 10.), Complex64::new(11., 12.)], + /// ]; + /// let Complex { re, im } = arr.view().split_re_im(); + /// assert_eq!(re, array![[1., 3.], [5., 7.], [9., 11.]]); + /// assert_eq!(im, array![[2., 4.], [6., 8.], [10., 12.]]); + /// ``` + pub fn split_re_im(self) -> Complex> { + unsafe { + let Complex { re, im } = self.into_raw_view().split_re_im(); + Complex { + re: re.deref_into_view(), + im: im.deref_into_view(), + } + } + } +} + /// Methods for read-write array views. impl<'a, A, D> ArrayViewMut<'a, A, D> where @@ -135,3 +167,41 @@ where info.multi_slice_move(self) } } + +impl<'a, T, D> ArrayViewMut<'a, Complex, D> +where + D: Dimension, +{ + /// Splits the view into views of the real and imaginary components of the + /// elements. + /// + /// ``` + /// use ndarray::prelude::*; + /// use num_complex::{Complex, Complex64}; + /// + /// let mut arr = array![ + /// [Complex64::new(1., 2.), Complex64::new(3., 4.)], + /// [Complex64::new(5., 6.), Complex64::new(7., 8.)], + /// [Complex64::new(9., 10.), Complex64::new(11., 12.)], + /// ]; + /// + /// let Complex { mut re, mut im } = arr.view_mut().split_re_im(); + /// assert_eq!(re, array![[1., 3.], [5., 7.], [9., 11.]]); + /// assert_eq!(im, array![[2., 4.], [6., 8.], [10., 12.]]); + /// + /// re[[0, 1]] = 13.; + /// im[[2, 0]] = 14.; + /// + /// assert_eq!(arr[[0, 1]], Complex64::new(13., 4.)); + /// assert_eq!(arr[[2, 0]], Complex64::new(9., 14.)); + /// ``` + pub fn split_re_im(self) -> Complex> { + unsafe { + let Complex { re, im } = self.into_raw_view_mut().split_re_im(); + Complex { + re: re.deref_into_view_mut(), + im: im.deref_into_view_mut(), + } + } + } +} diff --git a/src/numeric/mod.rs b/src/numeric/mod.rs index c230007d8..b3da06746 100644 --- a/src/numeric/mod.rs +++ b/src/numeric/mod.rs @@ -1,159 +1 @@ -use crate::imp_prelude::*; -use num_complex::Complex; -use rawpointer::PointerExt; -use std::mem; -use std::ptr::NonNull; - mod impl_numeric; - -impl ArrayBase -where - S: Data>, - D: Dimension, -{ - /// Returns views of the real and imaginary components of the elements. - /// - /// ``` - /// use ndarray::prelude::*; - /// use num_complex::{Complex, Complex64}; - /// - /// let arr = array![ - /// [Complex64::new(1., 2.), Complex64::new(3., 4.)], - /// [Complex64::new(5., 6.), Complex64::new(7., 8.)], - /// [Complex64::new(9., 10.), Complex64::new(11., 12.)], - /// ]; - /// let Complex { re, im } = arr.view_re_im(); - /// assert_eq!(re, array![[1., 3.], [5., 7.], [9., 11.]]); - /// assert_eq!(im, array![[2., 4.], [6., 8.], [10., 12.]]); - /// ``` - pub fn view_re_im(&self) -> Complex> { - debug_assert!(self.pointer_is_inbounds()); - - let dim = self.dim.clone(); - - // Double the strides. In the zero-sized element case and for axes of - // length <= 1, we leave the strides as-is to avoid possible overflow. - let mut strides = self.strides.clone(); - if mem::size_of::() != 0 { - for ax in 0..strides.ndim() { - if dim[ax] > 1 { - strides[ax] *= 2; - } - } - } - - let ptr_re: NonNull = self.ptr.cast(); - let ptr_im: NonNull = if self.is_empty() { - // In the empty case, we can just reuse the existing pointer since - // it won't be dereferenced anyway. It is not safe to offset by one - // since the allocation may be empty. - ptr_re - } else { - // In the nonempty case, we can safely offset into the first - // (complex) element. - unsafe { ptr_re.add(1) } - }; - - // `Complex` is `repr(C)` with only fields `re: T` and `im: T`. So, the - // real components of the elements start at the same pointer, and the - // imaginary components start at the pointer offset by one, with - // exactly double the strides. The new, doubled strides still meet the - // overflow constraints: - // - // - For the zero-sized element case, the strides are unchanged in - // units of bytes and in units of the element type. - // - // - For the nonzero-sized element case: - // - // - In units of bytes, the strides are unchanged. - // - // - Since `Complex` for nonzero `T` is always at least 2 bytes, - // and the original strides did not overflow in units of bytes, we - // know that the new doubled strides will not overflow in units of - // `T`. - unsafe { - Complex { - re: ArrayView::new(ptr_re, dim.clone(), strides.clone()), - im: ArrayView::new(ptr_im, dim, strides), - } - } - } - - /// Returns mutable views of the real and imaginary components of the elements. - /// - /// ``` - /// use ndarray::prelude::*; - /// use num_complex::{Complex, Complex64}; - /// - /// let mut arr = array![ - /// [Complex64::new(1., 2.), Complex64::new(3., 4.)], - /// [Complex64::new(5., 6.), Complex64::new(7., 8.)], - /// [Complex64::new(9., 10.), Complex64::new(11., 12.)], - /// ]; - /// - /// let Complex { mut re, mut im } = arr.view_mut_re_im(); - /// assert_eq!(re, array![[1., 3.], [5., 7.], [9., 11.]]); - /// assert_eq!(im, array![[2., 4.], [6., 8.], [10., 12.]]); - /// - /// re[[0, 1]] = 13.; - /// im[[2, 0]] = 14.; - /// - /// assert_eq!(arr[[0, 1]], Complex64::new(13., 4.)); - /// assert_eq!(arr[[2, 0]], Complex64::new(9., 14.)); - /// ``` - pub fn view_mut_re_im(&mut self) -> Complex> - where - S: DataMut, - { - self.ensure_unique(); - - let dim = self.dim.clone(); - - // Double the strides. In the zero-sized element case and for axes of - // length <= 1, we leave the strides as-is to avoid possible overflow. - let mut strides = self.strides.clone(); - if mem::size_of::() != 0 { - for ax in 0..strides.ndim() { - if dim[ax] > 1 { - strides[ax] *= 2; - } - } - } - - let ptr_re: NonNull = self.ptr.cast(); - let ptr_im: NonNull = if self.is_empty() { - // In the empty case, we can just reuse the existing pointer since - // it won't be dereferenced anyway. It is not safe to offset by one - // since the allocation may be empty. - ptr_re - } else { - // In the nonempty case, we can safely offset into the first - // (complex) element. - unsafe { ptr_re.add(1) } - }; - - // `Complex` is `repr(C)` with only fields `re: T` and `im: T`. So, the - // real components of the elements start at the same pointer, and the - // imaginary components start at the pointer offset by one, with - // exactly double the strides. The new, doubled strides still meet the - // overflow constraints: - // - // - For the zero-sized element case, the strides are unchanged in - // units of bytes and in units of the element type. - // - // - For the nonzero-sized element case: - // - // - In units of bytes, the strides are unchanged. - // - // - Since `Complex` for nonzero `T` is always at least 2 bytes, - // and the original strides did not overflow in units of bytes, we - // know that the new doubled strides will not overflow in units of - // `T`. - unsafe { - Complex { - re: ArrayViewMut::new(ptr_re, dim.clone(), strides.clone()), - im: ArrayViewMut::new(ptr_im, dim, strides), - } - } - } -} From 94ffa0f36b3da3c6831ea7d7d76731aaf070fa2f Mon Sep 17 00:00:00 2001 From: Benjamin Kay Date: Fri, 25 Jun 2021 13:13:37 -0500 Subject: [PATCH 372/651] add mapv_into_any(), resolved #1031 --- src/impl_methods.rs | 39 +++++++++++++++++++++++++++++++++++++++ tests/array.rs | 14 ++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 51c1403a5..1042574c7 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2473,6 +2473,45 @@ where self } + /// Consume the array, call `f` by **v**alue on each element, and return an + /// owned array with the new values. Works for **any** `F: FnMut(A)->B`. + /// + /// If `A` and `B` are the same type then the map is performed by delegating + /// to [`mapv_into()`] and then converting into an owned array. This avoids + /// unnecessary memory allocations in [`mapv()`]. + /// + /// If `A` and `B` are different types then a new array is allocated and the + /// map is performed as in [`mapv()`]. + /// + /// Elements are visited in arbitrary order. + pub fn mapv_into_any(self, mut f: F) -> Array + where + S: DataMut, + F: FnMut(A) -> B, + A: Clone + 'static, + B: 'static, + { + if core::any::TypeId::of::() == core::any::TypeId::of::() { + // A and B are the same type. + // Wrap f in a closure of type FnMut(A) -> A . + let f = |a| { + let b = f(a); + // Safe because A and B are the same type. + unsafe { unlimited_transmute::(b) } + }; + // Delegate to mapv_into() using the wrapped closure. + // Convert output to a uniquely owned array of type Array. + let output = self.mapv_into(f).into_owned(); + // Change the return type from Array to Array. + // Again, safe because A and B are the same type. + unsafe { unlimited_transmute::, Array>(output) } + } else { + // A and B are not the same type. + // Fallback to mapv(). + self.mapv(f) + } + } + /// Modify the array in place by calling `f` by mutable reference on each element. /// /// Elements are visited in arbitrary order. diff --git a/tests/array.rs b/tests/array.rs index 37065ba1b..d0fc67def 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -989,6 +989,20 @@ fn map1() { assert_eq!(a[(0, 0)], *c[(0, 0)]); } +#[test] +fn mapv_into_any_same_type() { + let a: Array = array![[1., 2., 3.], [4., 5., 6.]]; + let a_plus_one: Array = array![[2., 3., 4.], [5., 6., 7.]]; + assert_eq!(a.mapv_into_any(|a| a + 1.), a_plus_one); +} + +#[test] +fn mapv_into_any_diff_types() { + let a: Array = array![[1., 2., 3.], [4., 5., 6.]]; + let a_even: Array = array![[false, true, false], [true, false, true]]; + assert_eq!(a.mapv_into_any(|a| a.round() as i32 % 2 == 0), a_even); +} + #[test] fn as_slice_memory_order_mut_arcarray() { // Test that mutation breaks sharing for `ArcArray`. From 566177b2e4d4556f841eebe78cafdfb1632235bf Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 9 Jul 2021 21:29:46 +0200 Subject: [PATCH 373/651] MAINT: Fix clippy warnings about double references These were instances of writing &&T where &T was enough (deref coercion adjusts trivially, but it's better to avoid it). --- src/data_traits.rs | 2 +- src/impl_constructors.rs | 2 +- src/impl_methods.rs | 2 +- src/impl_raw_views.rs | 4 ++-- src/zip/mod.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index 98fc88e27..3785f79eb 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -361,7 +361,7 @@ where } else { 0 }; - self.clone_from(&other); + self.clone_from(other); self.as_nonnull_mut().offset(our_off) } } diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 46d0c76c5..99c393e9d 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -227,7 +227,7 @@ where { let n = diag.len(); let mut arr = Self::zeros((n, n)); - arr.diag_mut().assign(&diag); + arr.diag_mut().assign(diag); arr } } diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 1042574c7..bbe17d34b 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2292,7 +2292,7 @@ where if let Some(self_s) = self.as_slice_memory_order_mut() { if let Some(rhs_s) = rhs.as_slice_memory_order() { for (s, r) in self_s.iter_mut().zip(rhs_s) { - f(s, &r); + f(s, r); } return; } diff --git a/src/impl_raw_views.rs b/src/impl_raw_views.rs index 2ac5c08c7..90cfa6376 100644 --- a/src/impl_raw_views.rs +++ b/src/impl_raw_views.rs @@ -75,7 +75,7 @@ where assert!(!ptr.is_null(), "The pointer must be non-null."); if let Strides::Custom(strides) = &shape.strides { dimension::strides_non_negative(strides).unwrap(); - dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); + dimension::max_abs_offset_check_overflow::(&dim, strides).unwrap(); } else { dimension::size_of_shape_checked(&dim).unwrap(); } @@ -217,7 +217,7 @@ where assert!(!ptr.is_null(), "The pointer must be non-null."); if let Strides::Custom(strides) = &shape.strides { dimension::strides_non_negative(strides).unwrap(); - dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); + dimension::max_abs_offset_check_overflow::(&dim, strides).unwrap(); } else { dimension::size_of_shape_checked(&dim).unwrap(); } diff --git a/src/zip/mod.rs b/src/zip/mod.rs index d611fabbc..2fdc52528 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -252,7 +252,7 @@ where P: NdProducer, { ndassert!( - part.equal_dim(&dimension), + part.equal_dim(dimension), "Zip: Producer dimension mismatch, expected: {:?}, got: {:?}", dimension, part.raw_dim() From d03a55a65f6a37be62b49e79ed4f6c0bed57a88a Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 2 Aug 2021 21:38:59 -0400 Subject: [PATCH 374/651] Fix inline attributes (#1054) Before, the nightly compiler warned that the `inline` attributes were unused. Now, it has no warnings. --- src/dimension/dimension_trait.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 1eace34a7..a4d73b1b1 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -379,6 +379,7 @@ pub trait Dimension: macro_rules! impl_insert_axis_array( ($n:expr) => ( + #[inline] fn insert_axis(&self, axis: Axis) -> Self::Larger { debug_assert!(axis.index() <= $n); let mut out = [1; $n + 1]; @@ -422,7 +423,6 @@ impl Dimension for Dim<[Ix; 0]> { fn next_for(&self, _index: Self) -> Option { None } - #[inline] impl_insert_axis_array!(0); #[inline] fn try_remove_axis(&self, _ignore: Axis) -> Self::Smaller { @@ -530,7 +530,6 @@ impl Dimension for Dim<[Ix; 1]> { None } } - #[inline] impl_insert_axis_array!(1); #[inline] fn try_remove_axis(&self, axis: Axis) -> Self::Smaller { @@ -694,7 +693,6 @@ impl Dimension for Dim<[Ix; 2]> { None } } - #[inline] impl_insert_axis_array!(2); #[inline] fn try_remove_axis(&self, axis: Axis) -> Self::Smaller { @@ -814,7 +812,6 @@ impl Dimension for Dim<[Ix; 3]> { } order } - #[inline] impl_insert_axis_array!(3); #[inline] fn try_remove_axis(&self, axis: Axis) -> Self::Smaller { @@ -845,7 +842,6 @@ macro_rules! large_dim { assert_eq!(ndim, $n); Self::default() } - #[inline] $($insert_axis)* #[inline] fn try_remove_axis(&self, axis: Axis) -> Self::Smaller { From f9b8d78473bf80d82d65fb6856f386f94cc1f82d Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 25 Jul 2021 19:42:16 -0400 Subject: [PATCH 375/651] Allow trailing comma in stack and concatenate macros --- src/stacking.rs | 61 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/src/stacking.rs b/src/stacking.rs index fb05c1963..f8df7b5f8 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -192,22 +192,39 @@ where /// /// # fn main() { /// -/// let a = arr2(&[[2., 2.], -/// [3., 3.]]); -/// assert!( -/// stack![Axis(0), a, a] -/// == arr3(&[[[2., 2.], -/// [3., 3.]], -/// [[2., 2.], -/// [3., 3.]]]) +/// let a = arr2(&[[1., 2.], +/// [3., 4.]]); +/// assert_eq!( +/// stack![Axis(0), a, a], +/// arr3(&[[[1., 2.], +/// [3., 4.]], +/// [[1., 2.], +/// [3., 4.]]]), +/// ); +/// assert_eq!( +/// stack![Axis(1), a, a,], +/// arr3(&[[[1., 2.], +/// [1., 2.]], +/// [[3., 4.], +/// [3., 4.]]]), +/// ); +/// assert_eq!( +/// stack![Axis(2), a, a], +/// arr3(&[[[1., 1.], +/// [2., 2.]], +/// [[3., 3.], +/// [4., 4.]]]), /// ); /// # } /// ``` #[macro_export] macro_rules! stack { + ($axis:expr, $( $array:expr ),+ ,) => { + $crate::stack!($axis, $($array),+) + }; ($axis:expr, $( $array:expr ),+ ) => { $crate::stack($axis, &[ $($crate::ArrayView::from(&$array) ),* ]).unwrap() - } + }; } /// Concatenate arrays along the given axis. @@ -226,22 +243,30 @@ macro_rules! stack { /// /// # fn main() { /// -/// let a = arr2(&[[2., 2.], -/// [3., 3.]]); -/// assert!( -/// concatenate![Axis(0), a, a] -/// == arr2(&[[2., 2.], -/// [3., 3.], -/// [2., 2.], -/// [3., 3.]]) +/// let a = arr2(&[[1., 2.], +/// [3., 4.]]); +/// assert_eq!( +/// concatenate![Axis(0), a, a], +/// arr2(&[[1., 2.], +/// [3., 4.], +/// [1., 2.], +/// [3., 4.]]), +/// ); +/// assert_eq!( +/// concatenate![Axis(1), a, a,], +/// arr2(&[[1., 2., 1., 2.], +/// [3., 4., 3., 4.]]), /// ); /// # } /// ``` #[macro_export] macro_rules! concatenate { + ($axis:expr, $( $array:expr ),+ ,) => { + $crate::concatenate!($axis, $($array),+) + }; ($axis:expr, $( $array:expr ),+ ) => { $crate::concatenate($axis, &[ $($crate::ArrayView::from(&$array) ),* ]).unwrap() - } + }; } /// Stack arrays along the new axis. From 25516c28bc4ac96a62fdf8b2d121f6693cbf9fe5 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Thu, 12 Aug 2021 17:40:28 -0700 Subject: [PATCH 376/651] Minor doc fixes: broken links, commas. --- src/impl_methods.rs | 9 ++++++--- src/lib.rs | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index bbe17d34b..e4d238f11 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2477,13 +2477,16 @@ where /// owned array with the new values. Works for **any** `F: FnMut(A)->B`. /// /// If `A` and `B` are the same type then the map is performed by delegating - /// to [`mapv_into()`] and then converting into an owned array. This avoids - /// unnecessary memory allocations in [`mapv()`]. + /// to [`mapv_into`] and then converting into an owned array. This avoids + /// unnecessary memory allocations in [`mapv`]. /// /// If `A` and `B` are different types then a new array is allocated and the - /// map is performed as in [`mapv()`]. + /// map is performed as in [`mapv`]. /// /// Elements are visited in arbitrary order. + /// + /// [`mapv_into`]: ArrayBase::mapv_into + /// [`mapv`]: ArrayBase::mapv pub fn mapv_into_any(self, mut f: F) -> Array where S: DataMut, diff --git a/src/lib.rs b/src/lib.rs index 8cf1a495f..63e6316c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,7 +24,7 @@ //! The `ndarray` crate provides an *n*-dimensional container for general elements //! and for numerics. //! -//! In *n*-dimensional we include for example 1-dimensional rows or columns, +//! In *n*-dimensional we include, for example, 1-dimensional rows or columns, //! 2-dimensional matrices, and higher dimensional arrays. If the array has *n* //! dimensions, then an element in the array is accessed by using that many indices. //! Each dimension is also called an *axis*. From acc3bfd7032510ca13c16650d6fc6ef294bea2ab Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Thu, 14 Oct 2021 18:54:57 +0200 Subject: [PATCH 377/651] Mark Zip as must_use as they are lazy similar to iterators. --- src/zip/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 2fdc52528..cdc190a40 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -190,6 +190,7 @@ trait ZippableTuple: Sized { /// assert_eq!(totals, totals2); /// ``` #[derive(Debug, Clone)] +#[must_use = "zipping producers is lazy and does nothing unless consumed"] pub struct Zip { parts: Parts, dimension: D, From abfdf4dd30196e70c56a0f54eb46d01d3377ba8f Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 1 Sep 2021 20:49:12 -0400 Subject: [PATCH 378/651] Improve layout heuristic for sum_axis --- src/numeric/impl_numeric.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index da170c9e7..afcd7e896 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -12,7 +12,6 @@ use num_traits::{self, FromPrimitive, Zero}; use std::ops::{Add, Div, Mul}; use crate::imp_prelude::*; -use crate::itertools::enumerate; use crate::numeric_util; /// # Numerical Methods for Arrays @@ -246,22 +245,16 @@ where A: Clone + Zero + Add, D: RemoveAxis, { - let n = self.len_of(axis); - let mut res = Array::zeros(self.raw_dim().remove_axis(axis)); - let stride = self.strides()[axis.index()]; - if self.ndim() == 2 && stride == 1 { - // contiguous along the axis we are summing - let ax = axis.index(); - for (i, elt) in enumerate(&mut res) { - *elt = self.index_axis(Axis(1 - ax), i).sum(); - } + let min_stride_axis = self.dim.min_stride_axis(&self.strides); + if axis == min_stride_axis { + crate::Zip::from(self.lanes(axis)).map_collect(|lane| lane.sum()) } else { - for i in 0..n { - let view = self.index_axis(axis, i); - res = res + &view; + let mut res = Array::zeros(self.raw_dim().remove_axis(axis)); + for subview in self.axis_iter(axis) { + res = res + &subview; } + res } - res } /// Return mean along `axis`. From 267063b6113bf7d8aab36ec360579b86bca67ba6 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 23 Oct 2021 15:21:15 -0400 Subject: [PATCH 379/651] Fix blas usage with vector of stride <= 0 Stride == 0 is unsuppored for vector increments; Stride < 0 would be supported but the code needs to be adapted to pass the right pointer for this case (lowest in memory pointer). Co-authored-by: bluss --- src/linalg/impl_linalg.rs | 19 +++++++++++++++---- xtest-blas/tests/oper.rs | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index 61b91eaed..d851f2f08 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -8,6 +8,8 @@ use crate::imp_prelude::*; use crate::numeric_util; +#[cfg(feature = "blas")] +use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr; use crate::{LinalgScalar, Zip}; @@ -649,6 +651,12 @@ unsafe fn general_mat_vec_mul_impl( } }; + // Low addr in memory pointers required for x, y + let x_offset = offset_from_low_addr_ptr_to_logical_ptr(&x.dim, &x.strides); + let x_ptr = x.ptr.as_ptr().sub(x_offset); + let y_offset = offset_from_low_addr_ptr_to_logical_ptr(&y.dim, &y.strides); + let y_ptr = y.ptr.as_ptr().sub(y_offset); + let x_stride = x.strides()[0] as blas_index; let y_stride = y.strides()[0] as blas_index; @@ -660,10 +668,10 @@ unsafe fn general_mat_vec_mul_impl( cast_as(&alpha), // alpha a.ptr.as_ptr() as *const _, // a a_stride, // lda - x.ptr.as_ptr() as *const _, // x + x_ptr as *const _, // x x_stride, - cast_as(&beta), // beta - y.ptr.as_ptr() as *mut _, // x + cast_as(&beta), // beta + y_ptr as *mut _, // y y_stride, ); return; @@ -719,7 +727,10 @@ where return false; } let stride = a.strides()[0]; - if stride > blas_index::max_value() as isize || stride < blas_index::min_value() as isize { + if stride == 0 + || stride > blas_index::max_value() as isize + || stride < blas_index::min_value() as isize + { return false; } true diff --git a/xtest-blas/tests/oper.rs b/xtest-blas/tests/oper.rs index 0aeb47680..95ab4ebb2 100644 --- a/xtest-blas/tests/oper.rs +++ b/xtest-blas/tests/oper.rs @@ -21,6 +21,25 @@ fn mat_vec_product_1d() { assert_eq!(a.t().dot(&b), ans); } +#[test] +fn mat_vec_product_1d_broadcast() { + let a = arr2(&[[1.], [2.], [3.]]); + let b = arr1(&[1.]); + let b = b.broadcast(3).unwrap(); + let ans = arr1(&[6.]); + assert_eq!(a.t().dot(&b), ans); +} + +#[test] +fn mat_vec_product_1d_inverted_axis() { + let a = arr2(&[[1.], [2.], [3.]]); + let mut b = arr1(&[1., 2., 3.]); + b.invert_axis(Axis(0)); + + let ans = arr1(&[3. + 4. + 3.]); + assert_eq!(a.t().dot(&b), ans); +} + fn range_mat(m: Ix, n: Ix) -> Array2 { Array::linspace(0., (m * n) as f32 - 1., m * n) .into_shape((m, n)) From 55f327611e8105a909c45b1308d5da06bd522103 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 24 Oct 2021 14:03:51 +0200 Subject: [PATCH 380/651] MAINT: Silence clippy lint about if_then_panic (style issue) --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 63e6316c5..1448b89a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,8 @@ clippy::manual_map, // is not an error clippy::while_let_on_iterator, // is not an error clippy::from_iter_instead_of_collect, // using from_iter is good style + clippy::if_then_panic, // is not an error + clippy::redundant_closure, // false positives clippy #7812 )] #![doc(test(attr(deny(warnings))))] #![doc(test(attr(allow(unused_variables))))] From ee5880bda8bb2b1c0e223bc58f2c0e9a673497c7 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Wed, 6 Oct 2021 22:30:29 +0200 Subject: [PATCH 381/651] Resolve needless_doctest_main Clippy lint in zipmacro module. --- src/zip/zipmacro.rs | 81 ++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/src/zip/zipmacro.rs b/src/zip/zipmacro.rs index f25e33d31..dbd3a56a4 100644 --- a/src/zip/zipmacro.rs +++ b/src/zip/zipmacro.rs @@ -42,60 +42,57 @@ /// /// type M = Array2; /// -/// fn main() { -/// // Setup example arrays -/// let mut a = M::zeros((16, 16)); -/// let mut b = M::zeros(a.dim()); -/// let mut c = M::zeros(a.dim()); -/// -/// // assign values -/// b.fill(1.); -/// for ((i, j), elt) in c.indexed_iter_mut() { -/// *elt = (i + 10 * j) as f32; -/// } -/// -/// // Example 1: Compute a simple ternary operation: -/// // elementwise addition of b and c, stored in a -/// azip!((a in &mut a, &b in &b, &c in &c) *a = b + c); +/// // Setup example arrays +/// let mut a = M::zeros((16, 16)); +/// let mut b = M::zeros(a.dim()); +/// let mut c = M::zeros(a.dim()); +/// +/// // assign values +/// b.fill(1.); +/// for ((i, j), elt) in c.indexed_iter_mut() { +/// *elt = (i + 10 * j) as f32; +/// } /// -/// assert_eq!(a, &b + &c); +/// // Example 1: Compute a simple ternary operation: +/// // elementwise addition of b and c, stored in a +/// azip!((a in &mut a, &b in &b, &c in &c) *a = b + c); /// -/// // Example 2: azip!() with index -/// azip!((index (i, j), &b in &b, &c in &c) { -/// a[[i, j]] = b - c; -/// }); +/// assert_eq!(a, &b + &c); /// -/// assert_eq!(a, &b - &c); +/// // Example 2: azip!() with index +/// azip!((index (i, j), &b in &b, &c in &c) { +/// a[[i, j]] = b - c; +/// }); /// +/// assert_eq!(a, &b - &c); /// -/// // Example 3: azip!() on references -/// // See the definition of the function below -/// borrow_multiply(&mut a, &b, &c); /// -/// assert_eq!(a, &b * &c); +/// // Example 3: azip!() on references +/// // See the definition of the function below +/// borrow_multiply(&mut a, &b, &c); /// +/// assert_eq!(a, &b * &c); /// -/// // Since this function borrows its inputs, the `IntoNdProducer` -/// // expressions don't need to explicitly include `&mut` or `&`. -/// fn borrow_multiply(a: &mut M, b: &M, c: &M) { -/// azip!((a in a, &b in b, &c in c) *a = b * c); -/// } /// +/// // Since this function borrows its inputs, the `IntoNdProducer` +/// // expressions don't need to explicitly include `&mut` or `&`. +/// fn borrow_multiply(a: &mut M, b: &M, c: &M) { +/// azip!((a in a, &b in b, &c in c) *a = b * c); +/// } /// -/// // Example 4: using azip!() without dereference in pattern. -/// // -/// // Create a new array `totals` with one entry per row of `a`. -/// // Use azip to traverse the rows of `a` and assign to the corresponding -/// // entry in `totals` with the sum across each row. -/// // -/// // The row is an array view; it doesn't need to be dereferenced. -/// let mut totals = Array1::zeros(a.nrows()); -/// azip!((totals in &mut totals, row in a.rows()) *totals = row.sum()); /// -/// // Check the result against the built in `.sum_axis()` along axis 1. -/// assert_eq!(totals, a.sum_axis(Axis(1))); -/// } +/// // Example 4: using azip!() without dereference in pattern. +/// // +/// // Create a new array `totals` with one entry per row of `a`. +/// // Use azip to traverse the rows of `a` and assign to the corresponding +/// // entry in `totals` with the sum across each row. +/// // +/// // The row is an array view; it doesn't need to be dereferenced. +/// let mut totals = Array1::zeros(a.nrows()); +/// azip!((totals in &mut totals, row in a.rows()) *totals = row.sum()); /// +/// // Check the result against the built in `.sum_axis()` along axis 1. +/// assert_eq!(totals, a.sum_axis(Axis(1))); /// ``` #[macro_export] macro_rules! azip { From 25b7eff10b1380cc5a7d6f6a1d32793c57e63841 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Wed, 6 Oct 2021 09:03:01 +0200 Subject: [PATCH 382/651] Add Parallel::with_min_len to limit splitting Splitting of parallel iterators working `ArrayView` and `Zip` is currently not limited and continues until only one element is left which can lead to excessive overhead for algorithms formulated in terms of these iterators. Since these iterators are also not indexed, Rayon's generic `IndexedParallelIterator::with_min_len` does not apply. However, since the number of elements is known and currently checked against to one to determine if another split is possible, it appears straight-forward to replace this constant by a parameter and make it available to the user via a `Parallel::with_min_len` inherent method. --- src/parallel/par.rs | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/parallel/par.rs b/src/parallel/par.rs index d9d592af6..7db216c53 100644 --- a/src/parallel/par.rs +++ b/src/parallel/par.rs @@ -21,11 +21,24 @@ use crate::split_at::SplitPreference; #[derive(Copy, Clone, Debug)] pub struct Parallel { iter: I, + min_len: usize, } +impl Parallel { + /// Sets the minimum number of elements desired to process in each job. This will not be split any smaller than this length, but of course a producer could already be smaller to begin with. + pub fn with_min_len(self, min_len: usize) -> Self { + Self { + min_len, + ..self + } + } +} + +const DEFAULT_MIN_LEN: usize = 1; + /// Parallel producer wrapper. #[derive(Copy, Clone, Debug)] -struct ParallelProducer(I); +struct ParallelProducer(I, usize); macro_rules! par_iter_wrapper { // thread_bounds are either Sync or Send + Sync @@ -40,6 +53,7 @@ macro_rules! par_iter_wrapper { fn into_par_iter(self) -> Self::Iter { Parallel { iter: self, + min_len: DEFAULT_MIN_LEN, } } } @@ -67,7 +81,7 @@ macro_rules! par_iter_wrapper { fn with_producer(self, callback: Cb) -> Cb::Output where Cb: ProducerCallback { - callback.callback(ParallelProducer(self.iter)) + callback.callback(ParallelProducer(self.iter, self.min_len)) } fn len(&self) -> usize { @@ -106,7 +120,7 @@ macro_rules! par_iter_wrapper { fn split_at(self, i: usize) -> (Self, Self) { let (a, b) = self.0.split_at(i); - (ParallelProducer(a), ParallelProducer(b)) + (ParallelProducer(a, self.1), ParallelProducer(b, self.1)) } } @@ -131,6 +145,7 @@ macro_rules! par_iter_view_wrapper { fn into_par_iter(self) -> Self::Iter { Parallel { iter: self, + min_len: DEFAULT_MIN_LEN, } } } @@ -144,7 +159,7 @@ macro_rules! par_iter_view_wrapper { fn drive_unindexed(self, consumer: C) -> C::Result where C: UnindexedConsumer { - bridge_unindexed(ParallelProducer(self.iter), consumer) + bridge_unindexed(ParallelProducer(self.iter, self.min_len), consumer) } fn opt_len(&self) -> Option { @@ -158,14 +173,14 @@ macro_rules! par_iter_view_wrapper { { type Item = <$view_name<'a, A, D> as IntoIterator>::Item; fn split(self) -> (Self, Option) { - if self.0.len() <= 1 { + if self.0.len() <= self.1 { return (self, None) } let array = self.0; let max_axis = array.max_stride_axis(); let mid = array.len_of(max_axis) / 2; let (a, b) = array.split_at(max_axis, mid); - (ParallelProducer(a), Some(ParallelProducer(b))) + (ParallelProducer(a, self.1), Some(ParallelProducer(b, self.1))) } fn fold_with(self, folder: F) -> F @@ -217,6 +232,7 @@ macro_rules! zip_impl { fn into_par_iter(self) -> Self::Iter { Parallel { iter: self, + min_len: DEFAULT_MIN_LEN, } } } @@ -233,7 +249,7 @@ macro_rules! zip_impl { fn drive_unindexed(self, consumer: Cons) -> Cons::Result where Cons: UnindexedConsumer { - bridge_unindexed(ParallelProducer(self.iter), consumer) + bridge_unindexed(ParallelProducer(self.iter, self.min_len), consumer) } fn opt_len(&self) -> Option { @@ -251,11 +267,11 @@ macro_rules! zip_impl { type Item = ($($p::Item ,)*); fn split(self) -> (Self, Option) { - if !self.0.can_split() { + if self.0.size() <= self.1 { return (self, None) } let (a, b) = self.0.split(); - (ParallelProducer(a), Some(ParallelProducer(b))) + (ParallelProducer(a, self.1), Some(ParallelProducer(b, self.1))) } fn fold_with(self, folder: Fold) -> Fold From f596c9e128fa35fb8d6ea4c580a891c7911646bd Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Mon, 25 Oct 2021 19:56:34 +0200 Subject: [PATCH 383/651] Limit the with_min_len method to those parallel wrappers where it would not shadow a trait method Additionally, the min_len field does not affect those parallel iterators which are indexed as the splitting positions are chosen by Rayon which has its own `with_min_len` method to achieve the same effect. --- src/parallel/par.rs | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/parallel/par.rs b/src/parallel/par.rs index 7db216c53..eb53f96cb 100644 --- a/src/parallel/par.rs +++ b/src/parallel/par.rs @@ -24,16 +24,6 @@ pub struct Parallel { min_len: usize, } -impl Parallel { - /// Sets the minimum number of elements desired to process in each job. This will not be split any smaller than this length, but of course a producer could already be smaller to begin with. - pub fn with_min_len(self, min_len: usize) -> Self { - Self { - min_len, - ..self - } - } -} - const DEFAULT_MIN_LEN: usize = 1; /// Parallel producer wrapper. @@ -150,7 +140,6 @@ macro_rules! par_iter_view_wrapper { } } - impl<'a, A, D> ParallelIterator for Parallel<$view_name<'a, A, D>> where D: Dimension, A: $($thread_bounds)*, @@ -167,6 +156,19 @@ macro_rules! par_iter_view_wrapper { } } + impl<'a, A, D> Parallel<$view_name<'a, A, D>> + where D: Dimension, + A: $($thread_bounds)*, + { + /// Sets the minimum number of elements desired to process in each job. This will not be split any smaller than this length, but of course a producer could already be smaller to begin with. + pub fn with_min_len(self, min_len: usize) -> Self { + Self { + min_len, + ..self + } + } + } + impl<'a, A, D> UnindexedProducer for ParallelProducer<$view_name<'a, A, D>> where D: Dimension, A: $($thread_bounds)*, @@ -300,6 +302,19 @@ zip_impl! { [P1 P2 P3 P4 P5 P6], } +impl Parallel> +where + D: Dimension, +{ + /// Sets the minimum number of elements desired to process in each job. This will not be split any smaller than this length, but of course a producer could already be smaller to begin with. + pub fn with_min_len(self, min_len: usize) -> Self { + Self { + min_len, + ..self + } + } +} + /// A parallel iterator (unindexed) that produces the splits of the array /// or producer `P`. pub(crate) struct ParallelSplits

{ From f71ec6b501693c9a2264757447194367c5aa30c6 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Mon, 25 Oct 2021 20:19:44 +0200 Subject: [PATCH 384/651] Explain the relation of Parallel::with_min_len with Rayon's IndexedParallelIterator::with_min_len in the module-level docs. --- src/parallel/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/parallel/mod.rs b/src/parallel/mod.rs index dd3b89341..045aeb13a 100644 --- a/src/parallel/mod.rs +++ b/src/parallel/mod.rs @@ -32,6 +32,10 @@ //! “unindexed”. Use ndarray’s [Zip] for lock step parallel iteration of //! multiple arrays or producers at a time. //! +//! For the unindexed parallel iterators, an inherent method [`with_min_len`](Parallel::with_min_len) +//! is provided to limit the number of elements each parallel task processes in way that is +//! similar to Rayon's [`IndexedParallelIterator::with_min_len`](rayon::prelude::IndexedParallelIterator::with_min_len). +//! //! # Examples //! //! ## Arrays and array views From 4efc563e1bb1ae4f4d820ecae9180f9cc5ebcd49 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Mon, 25 Oct 2021 20:33:52 +0200 Subject: [PATCH 385/651] Assert non-zero parallel producer mininum length to avoid splittinf off empty tasks. --- src/parallel/par.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/parallel/par.rs b/src/parallel/par.rs index eb53f96cb..cc905b5cf 100644 --- a/src/parallel/par.rs +++ b/src/parallel/par.rs @@ -160,8 +160,14 @@ macro_rules! par_iter_view_wrapper { where D: Dimension, A: $($thread_bounds)*, { - /// Sets the minimum number of elements desired to process in each job. This will not be split any smaller than this length, but of course a producer could already be smaller to begin with. + /// Sets the minimum number of elements desired to process in each job. This will not be + /// split any smaller than this length, but of course a producer could already be smaller + /// to begin with. + /// + /// ***Panics*** if `min_len` is zero. pub fn with_min_len(self, min_len: usize) -> Self { + assert_ne!(min_len, 0, "Minimum number of elements must at least be one to avoid splitting off empty tasks."); + Self { min_len, ..self @@ -306,8 +312,14 @@ impl Parallel> where D: Dimension, { - /// Sets the minimum number of elements desired to process in each job. This will not be split any smaller than this length, but of course a producer could already be smaller to begin with. + /// Sets the minimum number of elements desired to process in each job. This will not be + /// split any smaller than this length, but of course a producer could already be smaller + /// to begin with. + /// + /// ***Panics*** if `min_len` is zero. pub fn with_min_len(self, min_len: usize) -> Self { + assert_ne!(min_len, 0, "Minimum number of elements must at least be one to avoid splitting off empty tasks."); + Self { min_len, ..self From 4fcb8ac38cd9dcbffd944d32868adb5939224525 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Mon, 25 Oct 2021 20:37:00 +0200 Subject: [PATCH 386/651] Expand Clippy coverage to include the features enabled for the docs. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 42e5e6584..a44568ae8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,5 +75,5 @@ jobs: toolchain: ${{ matrix.rust }} override: true components: clippy - - run: cargo clippy + - run: cargo clippy --features docs From 6ff8cfc603570da406b8b5bd0b48635b9a15edf9 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Mon, 25 Oct 2021 20:42:07 +0200 Subject: [PATCH 387/651] Resolve more instances of needless_doctest_main exposed by building with more features enabled. --- .../coord_transform.rs | 130 +++++++++--------- src/parallel/mod.rs | 70 +++++----- src/parallel/zipmacro.rs | 16 +-- 3 files changed, 101 insertions(+), 115 deletions(-) diff --git a/src/doc/ndarray_for_numpy_users/coord_transform.rs b/src/doc/ndarray_for_numpy_users/coord_transform.rs index 09295be9d..1529e8746 100644 --- a/src/doc/ndarray_for_numpy_users/coord_transform.rs +++ b/src/doc/ndarray_for_numpy_users/coord_transform.rs @@ -51,40 +51,38 @@ //! ``` //! use ndarray::prelude::*; //! -//! fn main() { -//! let nelems = 4; -//! let bunge = Array::ones((3, nelems)); -//! -//! let s1 = bunge.slice(s![0, ..]).mapv(f64::sin); -//! let c1 = bunge.slice(s![0, ..]).mapv(f64::cos); -//! let s2 = bunge.slice(s![1, ..]).mapv(f64::sin); -//! let c2 = bunge.slice(s![1, ..]).mapv(f64::cos); -//! let s3 = bunge.slice(s![2, ..]).mapv(f64::sin); -//! let c3 = bunge.slice(s![2, ..]).mapv(f64::cos); -//! -//! let mut rmat = Array::zeros((3, 3, nelems).f()); -//! for i in 0..nelems { -//! rmat[[0, 0, i]] = c1[i] * c3[i] - s1[i] * s3[i] * c2[i]; -//! rmat[[0, 1, i]] = -c1[i] * s3[i] - s1[i] * c2[i] * c3[i]; -//! rmat[[0, 2, i]] = s1[i] * s2[i]; -//! -//! rmat[[1, 0, i]] = s1[i] * c3[i] + c1[i] * c2[i] * s3[i]; -//! rmat[[1, 1, i]] = -s1[i] * s3[i] + c1[i] * c2[i] * c3[i]; -//! rmat[[1, 2, i]] = -c1[i] * s2[i]; -//! -//! rmat[[2, 0, i]] = s2[i] * s3[i]; -//! rmat[[2, 1, i]] = s2[i] * c3[i]; -//! rmat[[2, 2, i]] = c2[i]; -//! } -//! -//! let eye2d = Array::eye(3); -//! -//! let mut rotated = Array::zeros((3, 3, nelems).f()); -//! for i in 0..nelems { -//! rotated -//! .slice_mut(s![.., .., i]) -//! .assign(&rmat.slice(s![.., .., i]).dot(&eye2d)); -//! } +//! let nelems = 4; +//! let bunge = Array::ones((3, nelems)); +//! +//! let s1 = bunge.slice(s![0, ..]).mapv(f64::sin); +//! let c1 = bunge.slice(s![0, ..]).mapv(f64::cos); +//! let s2 = bunge.slice(s![1, ..]).mapv(f64::sin); +//! let c2 = bunge.slice(s![1, ..]).mapv(f64::cos); +//! let s3 = bunge.slice(s![2, ..]).mapv(f64::sin); +//! let c3 = bunge.slice(s![2, ..]).mapv(f64::cos); +//! +//! let mut rmat = Array::zeros((3, 3, nelems).f()); +//! for i in 0..nelems { +//! rmat[[0, 0, i]] = c1[i] * c3[i] - s1[i] * s3[i] * c2[i]; +//! rmat[[0, 1, i]] = -c1[i] * s3[i] - s1[i] * c2[i] * c3[i]; +//! rmat[[0, 2, i]] = s1[i] * s2[i]; +//! +//! rmat[[1, 0, i]] = s1[i] * c3[i] + c1[i] * c2[i] * s3[i]; +//! rmat[[1, 1, i]] = -s1[i] * s3[i] + c1[i] * c2[i] * c3[i]; +//! rmat[[1, 2, i]] = -c1[i] * s2[i]; +//! +//! rmat[[2, 0, i]] = s2[i] * s3[i]; +//! rmat[[2, 1, i]] = s2[i] * c3[i]; +//! rmat[[2, 2, i]] = c2[i]; +//! } +//! +//! let eye2d = Array::eye(3); +//! +//! let mut rotated = Array::zeros((3, 3, nelems).f()); +//! for i in 0..nelems { +//! rotated +//! .slice_mut(s![.., .., i]) +//! .assign(&rmat.slice(s![.., .., i]).dot(&eye2d)); //! } //! ``` //! @@ -96,37 +94,35 @@ //! ``` //! use ndarray::prelude::*; //! -//! fn main() { -//! let nelems = 4; -//! let bunge = Array2::::ones((3, nelems)); -//! -//! let mut rmat = Array::zeros((3, 3, nelems).f()); -//! azip!((mut rmat in rmat.axis_iter_mut(Axis(2)), bunge in bunge.axis_iter(Axis(1))) { -//! let s1 = bunge[0].sin(); -//! let c1 = bunge[0].cos(); -//! let s2 = bunge[1].sin(); -//! let c2 = bunge[1].cos(); -//! let s3 = bunge[2].sin(); -//! let c3 = bunge[2].cos(); -//! -//! rmat[[0, 0]] = c1 * c3 - s1 * s3 * c2; -//! rmat[[0, 1]] = -c1 * s3 - s1 * c2 * c3; -//! rmat[[0, 2]] = s1 * s2; -//! -//! rmat[[1, 0]] = s1 * c3 + c1 * c2 * s3; -//! rmat[[1, 1]] = -s1 * s3 + c1 * c2 * c3; -//! rmat[[1, 2]] = -c1 * s2; -//! -//! rmat[[2, 0]] = s2 * s3; -//! rmat[[2, 1]] = s2 * c3; -//! rmat[[2, 2]] = c2; -//! }); -//! -//! let eye2d = Array2::::eye(3); -//! -//! let mut rotated = Array3::::zeros((3, 3, nelems).f()); -//! azip!((mut rotated in rotated.axis_iter_mut(Axis(2)), rmat in rmat.axis_iter(Axis(2))) { -//! rotated.assign(&rmat.dot(&eye2d)); -//! }); -//! } +//! let nelems = 4; +//! let bunge = Array2::::ones((3, nelems)); +//! +//! let mut rmat = Array::zeros((3, 3, nelems).f()); +//! azip!((mut rmat in rmat.axis_iter_mut(Axis(2)), bunge in bunge.axis_iter(Axis(1))) { +//! let s1 = bunge[0].sin(); +//! let c1 = bunge[0].cos(); +//! let s2 = bunge[1].sin(); +//! let c2 = bunge[1].cos(); +//! let s3 = bunge[2].sin(); +//! let c3 = bunge[2].cos(); +//! +//! rmat[[0, 0]] = c1 * c3 - s1 * s3 * c2; +//! rmat[[0, 1]] = -c1 * s3 - s1 * c2 * c3; +//! rmat[[0, 2]] = s1 * s2; +//! +//! rmat[[1, 0]] = s1 * c3 + c1 * c2 * s3; +//! rmat[[1, 1]] = -s1 * s3 + c1 * c2 * c3; +//! rmat[[1, 2]] = -c1 * s2; +//! +//! rmat[[2, 0]] = s2 * s3; +//! rmat[[2, 1]] = s2 * c3; +//! rmat[[2, 2]] = c2; +//! }); +//! +//! let eye2d = Array2::::eye(3); +//! +//! let mut rotated = Array3::::zeros((3, 3, nelems).f()); +//! azip!((mut rotated in rotated.axis_iter_mut(Axis(2)), rmat in rmat.axis_iter(Axis(2))) { +//! rotated.assign(&rmat.dot(&eye2d)); +//! }); //! ``` diff --git a/src/parallel/mod.rs b/src/parallel/mod.rs index 045aeb13a..48e97be47 100644 --- a/src/parallel/mod.rs +++ b/src/parallel/mod.rs @@ -46,16 +46,14 @@ //! use ndarray::Array2; //! use ndarray::parallel::prelude::*; //! -//! fn main() { -//! let mut a = Array2::::zeros((128, 128)); +//! let mut a = Array2::::zeros((128, 128)); //! -//! // Parallel versions of regular array methods -//! a.par_map_inplace(|x| *x = x.exp()); -//! a.par_mapv_inplace(f64::exp); +//! // Parallel versions of regular array methods +//! a.par_map_inplace(|x| *x = x.exp()); +//! a.par_mapv_inplace(f64::exp); //! -//! // You can also use the parallel iterator directly -//! a.par_iter_mut().for_each(|x| *x = x.exp()); -//! } +//! // You can also use the parallel iterator directly +//! a.par_iter_mut().for_each(|x| *x = x.exp()); //! ``` //! //! ## Axis iterators @@ -67,16 +65,14 @@ //! use ndarray::Axis; //! use ndarray::parallel::prelude::*; //! -//! fn main() { -//! let a = Array::linspace(0., 63., 64).into_shape((4, 16)).unwrap(); -//! let mut sums = Vec::new(); -//! a.axis_iter(Axis(0)) -//! .into_par_iter() -//! .map(|row| row.sum()) -//! .collect_into_vec(&mut sums); +//! let a = Array::linspace(0., 63., 64).into_shape((4, 16)).unwrap(); +//! let mut sums = Vec::new(); +//! a.axis_iter(Axis(0)) +//! .into_par_iter() +//! .map(|row| row.sum()) +//! .collect_into_vec(&mut sums); //! -//! assert_eq!(sums, [120., 376., 632., 888.]); -//! } +//! assert_eq!(sums, [120., 376., 632., 888.]); //! ``` //! //! ## Axis chunks iterators @@ -88,16 +84,14 @@ //! use ndarray::Axis; //! use ndarray::parallel::prelude::*; //! -//! fn main() { -//! let a = Array::linspace(0., 63., 64).into_shape((4, 16)).unwrap(); -//! let mut shapes = Vec::new(); -//! a.axis_chunks_iter(Axis(0), 3) -//! .into_par_iter() -//! .map(|chunk| chunk.shape().to_owned()) -//! .collect_into_vec(&mut shapes); +//! let a = Array::linspace(0., 63., 64).into_shape((4, 16)).unwrap(); +//! let mut shapes = Vec::new(); +//! a.axis_chunks_iter(Axis(0), 3) +//! .into_par_iter() +//! .map(|chunk| chunk.shape().to_owned()) +//! .collect_into_vec(&mut shapes); //! -//! assert_eq!(shapes, [vec![3, 16], vec![1, 16]]); -//! } +//! assert_eq!(shapes, [vec![3, 16], vec![1, 16]]); //! ``` //! //! ## Zip @@ -110,19 +104,17 @@ //! //! type Array3f64 = Array3; //! -//! fn main() { -//! const N: usize = 128; -//! let a = Array3f64::from_elem((N, N, N), 1.); -//! let b = Array3f64::from_elem(a.dim(), 2.); -//! let mut c = Array3f64::zeros(a.dim()); -//! -//! Zip::from(&mut c) -//! .and(&a) -//! .and(&b) -//! .par_for_each(|c, &a, &b| { -//! *c += a - b; -//! }); -//! } +//! const N: usize = 128; +//! let a = Array3f64::from_elem((N, N, N), 1.); +//! let b = Array3f64::from_elem(a.dim(), 2.); +//! let mut c = Array3f64::zeros(a.dim()); +//! +//! Zip::from(&mut c) +//! .and(&a) +//! .and(&b) +//! .par_for_each(|c, &a, &b| { +//! *c += a - b; +//! }); //! ``` #[allow(unused_imports)] // used by rustdoc links diff --git a/src/parallel/zipmacro.rs b/src/parallel/zipmacro.rs index 95111c933..28188542f 100644 --- a/src/parallel/zipmacro.rs +++ b/src/parallel/zipmacro.rs @@ -39,18 +39,16 @@ /// /// type M = Array2; /// -/// fn main() { -/// let mut a = M::zeros((16, 16)); -/// let b = M::from_elem(a.dim(), 1.); -/// let c = M::from_elem(a.dim(), 2.); +/// let mut a = M::zeros((16, 16)); +/// let b = M::from_elem(a.dim(), 1.); +/// let c = M::from_elem(a.dim(), 2.); /// -/// // Compute a simple ternary operation: -/// // elementwise addition of b and c, stored in a +/// // Compute a simple ternary operation: +/// // elementwise addition of b and c, stored in a /// -/// par_azip!((a in &mut a, &b in &b, &c in &c) *a = b + c); +/// par_azip!((a in &mut a, &b in &b, &c in &c) *a = b + c); /// -/// assert_eq!(a, &b + &c); -/// } +/// assert_eq!(a, &b + &c); /// ``` macro_rules! par_azip { ($($t:tt)*) => { From b077676250a7cca456ae8e47f53e81d3b8a2412a Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Mon, 25 Oct 2021 20:43:52 +0200 Subject: [PATCH 388/651] Resolve instances of Clippy's try_err lint in the Serde integration. --- src/array_serde.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/array_serde.rs b/src/array_serde.rs index 41f7d60c1..f1c9e1219 100644 --- a/src/array_serde.rs +++ b/src/array_serde.rs @@ -276,17 +276,17 @@ where let _v = match v { Some(v) => v, - None => Err(de::Error::missing_field("v"))?, + None => return Err(de::Error::missing_field("v")), }; let data = match data { Some(data) => data, - None => Err(de::Error::missing_field("data"))?, + None => return Err(de::Error::missing_field("data")), }; let dim = match dim { Some(dim) => dim, - None => Err(de::Error::missing_field("dim"))?, + None => return Err(de::Error::missing_field("dim")), }; if let Ok(array) = ArrayBase::from_shape_vec(dim, data) { From 89460c0a02b69666f4e5f026226952bc48e28448 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Mon, 25 Oct 2021 20:45:13 +0200 Subject: [PATCH 389/651] Resolve instance of Clippy's zero_ptr lint in the Partial helper type. --- src/partial.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/partial.rs b/src/partial.rs index a8146aff0..a835081c4 100644 --- a/src/partial.rs +++ b/src/partial.rs @@ -38,7 +38,7 @@ impl Partial { #[cfg(feature = "rayon")] pub(crate) fn stub() -> Self { - Self { len: 0, ptr: 0 as *mut _ } + Self { len: 0, ptr: ptr::null_mut() } } #[cfg(feature = "rayon")] From 6169d49cf8dba8f871ae3e81a8b0c1ba64c6ddb6 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Mon, 25 Oct 2021 20:58:53 +0200 Subject: [PATCH 390/651] Locally suppress Clippy's missing-safety-doc lint for traits which are not implementable downstream. --- src/data_traits.rs | 7 +++++++ src/iterators/mod.rs | 1 + src/slice.rs | 1 + 3 files changed, 9 insertions(+) diff --git a/src/data_traits.rs b/src/data_traits.rs index 3785f79eb..bcf196597 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -27,6 +27,7 @@ use crate::{ArrayBase, CowRepr, Dimension, OwnedArcRepr, OwnedRepr, RawViewRepr, /// ***Note:*** `RawData` is not an extension interface at this point. /// Traits in Rust can serve many different roles. This trait is public because /// it is used as a bound on public methods. +#[allow(clippy::missing_safety_doc)] // not implementable downstream pub unsafe trait RawData: Sized { /// The array element type. type Elem; @@ -47,6 +48,7 @@ pub unsafe trait RawData: Sized { /// For an array with writable elements. /// /// ***Internal trait, see `RawData`.*** +#[allow(clippy::missing_safety_doc)] // not implementable downstream pub unsafe trait RawDataMut: RawData { /// If possible, ensures that the array has unique access to its data. /// @@ -74,6 +76,7 @@ pub unsafe trait RawDataMut: RawData { /// An array representation that can be cloned. /// /// ***Internal trait, see `RawData`.*** +#[allow(clippy::missing_safety_doc)] // not implementable downstream pub unsafe trait RawDataClone: RawData { #[doc(hidden)] /// Unsafe because, `ptr` must point inside the current storage. @@ -96,6 +99,7 @@ pub unsafe trait RawDataClone: RawData { /// For an array with elements that can be accessed with safe code. /// /// ***Internal trait, see `RawData`.*** +#[allow(clippy::missing_safety_doc)] // not implementable downstream pub unsafe trait Data: RawData { /// Converts the array to a uniquely owned array, cloning elements if necessary. #[doc(hidden)] @@ -131,6 +135,7 @@ pub unsafe trait Data: RawData { // `RawDataMut::try_ensure_unique` implementation always panics or ensures that // the data is unique. You are also guaranteeing that `try_is_unique` always // returns `Some(_)`. +#[allow(clippy::missing_safety_doc)] // not implementable downstream pub unsafe trait DataMut: Data + RawDataMut { /// Ensures that the array has unique access to its data. #[doc(hidden)] @@ -449,6 +454,7 @@ unsafe impl<'a, A> DataMut for ViewRepr<&'a mut A> {} // The array storage must be initially mutable - copy on write arrays may require copying for // unsharing storage before mutating it. The initially allocated storage must be mutable so // that it can be mutated directly - through .raw_view_mut_unchecked() - for initialization. +#[allow(clippy::missing_safety_doc)] // not implementable downstream pub unsafe trait DataOwned: Data { /// Corresponding owned data with MaybeUninit elements type MaybeUninit: DataOwned> @@ -467,6 +473,7 @@ pub unsafe trait DataOwned: Data { /// A representation that is a lightweight view. /// /// ***Internal trait, see `Data`.*** +#[allow(clippy::missing_safety_doc)] // not implementable downstream pub unsafe trait DataShared: Clone + Data + RawDataClone {} unsafe impl DataShared for OwnedArcRepr {} diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index bb618e5be..14ba342f4 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -1445,6 +1445,7 @@ send_sync_read_write!(ElementsBaseMut); /// /// The iterator must produce exactly the number of elements it reported or /// diverge before reaching the end. +#[allow(clippy::missing_safety_doc)] // not nameable downstream pub unsafe trait TrustedIterator {} use crate::indexes::IndicesIterF; diff --git a/src/slice.rs b/src/slice.rs index 113b82d05..44e768396 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -295,6 +295,7 @@ impl From for SliceInfoElem { /// that `D`, `Self::OutDim`, `self.in_dim()`, and `self.out_ndim()` are /// consistent with the `&[SliceInfoElem]` returned by `self.as_ref()` and that /// `self.as_ref()` always returns the same value when called multiple times. +#[allow(clippy::missing_safety_doc)] // not implementable downstream pub unsafe trait SliceArg: AsRef<[SliceInfoElem]> { /// Dimensionality of the output array. type OutDim: Dimension; From b02a7cb57e5cb4dd473e213960c0007090d595ff Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 28 Oct 2021 18:43:52 +0200 Subject: [PATCH 391/651] Allow Clippy safety doc lints for NdIndex and FixedInitializer --- src/dimension/ndindex.rs | 1 + src/free_functions.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/dimension/ndindex.rs b/src/dimension/ndindex.rs index d9bac1d94..810a5b097 100644 --- a/src/dimension/ndindex.rs +++ b/src/dimension/ndindex.rs @@ -18,6 +18,7 @@ use crate::{ /// a[[1, 1]] += 1; /// assert_eq!(a[(1, 1)], 4); /// ``` +#[allow(clippy::missing_safety_doc)] // TODO: Add doc pub unsafe trait NdIndex: Debug { #[doc(hidden)] fn index_checked(&self, dim: &E, strides: &E) -> Option; diff --git a/src/free_functions.rs b/src/free_functions.rs index 95eb7dc81..4a9d66810 100644 --- a/src/free_functions.rs +++ b/src/free_functions.rs @@ -169,6 +169,7 @@ pub fn aview_mut2>(xs: &mut [V]) -> ArrayViewMu } /// Fixed-size array used for array initialization +#[allow(clippy::missing_safety_doc)] // Should not be implemented downstream and to be deprecated. pub unsafe trait FixedInitializer { type Elem; fn as_init_slice(&self) -> &[Self::Elem]; From 562104a5326acdefbd0235599b91a59bcc8d73d4 Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Tue, 15 Jun 2021 12:15:04 +0200 Subject: [PATCH 392/651] Replace some links with intra-doc links --- ndarray-rand/src/lib.rs | 12 +- src/data_repr.rs | 2 +- src/dimension/dim.rs | 2 +- src/doc/ndarray_for_numpy_users/mod.rs | 177 ++++++++-------- src/doc/ndarray_for_numpy_users/rk_step.rs | 10 +- src/free_functions.rs | 2 +- src/impl_constructors.rs | 2 +- src/impl_cow.rs | 2 - src/impl_methods.rs | 12 +- src/impl_owned_array.rs | 6 - src/impl_special_element_types.rs | 2 - src/impl_views/conversions.rs | 6 - src/impl_views/indexing.rs | 18 +- src/impl_views/splitting.rs | 2 +- src/iterators/chunks.rs | 8 +- src/iterators/iter.rs | 2 +- src/iterators/lanes.rs | 4 +- src/iterators/mod.rs | 24 +-- src/iterators/windows.rs | 4 +- src/itertools.rs | 4 +- src/lib.rs | 234 ++++++++++----------- src/slice.rs | 25 +-- src/stacking.rs | 12 +- src/zip/mod.rs | 10 +- src/zip/ndproducer.rs | 6 +- src/zip/zipmacro.rs | 2 +- 26 files changed, 262 insertions(+), 328 deletions(-) diff --git a/ndarray-rand/src/lib.rs b/ndarray-rand/src/lib.rs index e293eb561..d9edabfdb 100644 --- a/ndarray-rand/src/lib.rs +++ b/ndarray-rand/src/lib.rs @@ -8,15 +8,15 @@ //! Constructors for randomized arrays: `rand` integration for `ndarray`. //! -//! See [**`RandomExt`**](trait.RandomExt.html) for usage examples. +//! See **[`RandomExt`]** for usage examples. //! //! ## Note //! //! `ndarray-rand` depends on [`rand` 0.8][rand]. //! //! [`rand`][rand] and [`rand_distr`][rand_distr] -//! are re-exported as sub-modules, [`ndarray_rand::rand`](rand/index.html) -//! and [`ndarray_rand::rand_distr`](rand_distr/index.html) respectively. +//! are re-exported as sub-modules, [`ndarray_rand::rand`](rand) +//! and [`ndarray_rand::rand_distr`](rand_distr) respectively. //! You can use these submodules for guaranteed version compatibility or //! convenience. //! @@ -60,7 +60,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()`](#tymethod.random_using). +/// [`.random_using()`](Self::random_using). pub trait RandomExt where S: RawData, @@ -293,8 +293,8 @@ where /// if lanes from the original array should only be sampled once (*without replacement*) or /// multiple times (*with replacement*). /// -/// [`sample_axis`]: trait.RandomExt.html#tymethod.sample_axis -/// [`sample_axis_using`]: trait.RandomExt.html#tymethod.sample_axis_using +/// [`sample_axis`]: RandomExt::sample_axis +/// [`sample_axis_using`]: RandomExt::sample_axis_using #[derive(Debug, Clone)] pub enum SamplingStrategy { WithReplacement, diff --git a/src/data_repr.rs b/src/data_repr.rs index 7047f2014..6630f9ddf 100644 --- a/src/data_repr.rs +++ b/src/data_repr.rs @@ -11,7 +11,7 @@ use rawpointer::PointerExt; /// Array's representation. /// /// *Don’t use this type directly—use the type alias -/// [`Array`](type.Array.html) for the array type!* +/// [`Array`](crate::Array) for the array type!* // Like a Vec, but with non-unique ownership semantics // // repr(C) to make it transmutable OwnedRepr -> OwnedRepr if diff --git a/src/dimension/dim.rs b/src/dimension/dim.rs index 97de6d842..19075f943 100644 --- a/src/dimension/dim.rs +++ b/src/dimension/dim.rs @@ -18,7 +18,7 @@ use crate::Ix; /// `Dim` describes the number of axes and the length of each axis /// in an array. It is also used as an index type. /// -/// See also the [`Dimension` trait](trait.Dimension.html) for its methods and +/// See also the [`Dimension`] trait for its methods and /// operations. /// /// # Examples diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index bedd1c8b5..f9c05c612 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -23,7 +23,7 @@ //! //! # Similarities //! -//! `ndarray`'s array type ([`ArrayBase`][ArrayBase]), is very similar to +//! `ndarray`'s array type ([`ArrayBase`]), is very similar to //! NumPy's array type (`numpy.ndarray`): //! //! * Arrays have a single element type. @@ -70,12 +70,12 @@ //! //!

IntoNdProducer for P -where - P: NdProducer, -{ - type Item = P::Item; - type Dim = P::Dim; - type Output = Self; - fn into_producer(self) -> Self::Output { - self - } -} - -/// A producer of an n-dimensional set of elements; -/// for example an array view, mutable array view or an iterator -/// that yields chunks. -/// -/// Producers are used as a arguments to [`Zip`](struct.Zip.html) and -/// [`azip!()`](macro.azip.html). -/// -/// # Comparison to `IntoIterator` -/// -/// Most `NdProducers` are *iterable* (implement `IntoIterator`) but not directly -/// iterators. This separation is needed because the producer represents -/// a multidimensional set of items, it can be split along a particular axis for -/// parallelization, and it has no fixed correspondance to a sequence. -/// -/// The natural exception is one dimensional producers, like `AxisIter`, which -/// implement `Iterator` directly -/// (`AxisIter` traverses a one dimensional sequence, along an axis, while -/// *producing* multidimensional items). -/// -/// See also [`IntoNdProducer`](trait.IntoNdProducer.html) -pub trait NdProducer { - /// The element produced per iteration. - type Item; - // Internal use / Pointee type - /// Dimension type - type Dim: Dimension; - - // The pointer Ptr is used by an array view to simply point to the - // current element. It doesn't have to be a pointer (see Indices). - // Its main function is that it can be incremented with a particular - // stride (= along a particular axis) - #[doc(hidden)] - /// Pointer or stand-in for pointer - type Ptr: Offset; - #[doc(hidden)] - /// Pointer stride - type Stride: Copy; - - #[doc(hidden)] - fn layout(&self) -> Layout; - #[doc(hidden)] - fn raw_dim(&self) -> Self::Dim; - #[doc(hidden)] - fn equal_dim(&self, dim: &Self::Dim) -> bool { - self.raw_dim() == *dim - } - #[doc(hidden)] - fn as_ptr(&self) -> Self::Ptr; - #[doc(hidden)] - unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item; - #[doc(hidden)] - unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr; - #[doc(hidden)] - fn stride_of(&self, axis: Axis) -> ::Stride; - #[doc(hidden)] - fn contiguous_stride(&self) -> Self::Stride; - #[doc(hidden)] - fn split_at(self, axis: Axis, index: usize) -> (Self, Self) - where - Self: Sized; - - private_decl! {} -} - -pub trait Offset: Copy { - type Stride: Copy; - unsafe fn stride_offset(self, s: Self::Stride, index: usize) -> Self; - private_decl! {} -} - -impl Offset for *const T { - type Stride = isize; - unsafe fn stride_offset(self, s: Self::Stride, index: usize) -> Self { - self.offset(s * (index as isize)) - } - private_impl! {} -} - -impl Offset for *mut T { - type Stride = isize; - unsafe fn stride_offset(self, s: Self::Stride, index: usize) -> Self { - self.offset(s * (index as isize)) - } - private_impl! {} -} - trait ZippableTuple: Sized { type Item; type Ptr: OffsetTuple + Copy; @@ -220,288 +108,6 @@ trait ZippableTuple: Sized { fn split_at(self, axis: Axis, index: usize) -> (Self, Self); } -/// An array reference is an n-dimensional producer of element references -/// (like ArrayView). -impl<'a, A: 'a, S, D> IntoNdProducer for &'a ArrayBase -where - D: Dimension, - S: Data, -{ - type Item = &'a A; - type Dim = D; - type Output = ArrayView<'a, A, D>; - fn into_producer(self) -> Self::Output { - self.view() - } -} - -/// A mutable array reference is an n-dimensional producer of mutable element -/// references (like ArrayViewMut). -impl<'a, A: 'a, S, D> IntoNdProducer for &'a mut ArrayBase -where - D: Dimension, - S: DataMut, -{ - type Item = &'a mut A; - type Dim = D; - type Output = ArrayViewMut<'a, A, D>; - fn into_producer(self) -> Self::Output { - self.view_mut() - } -} - -/// A slice is a one-dimensional producer -impl<'a, A: 'a> IntoNdProducer for &'a [A] { - type Item = ::Item; - type Dim = Ix1; - type Output = ArrayView1<'a, A>; - fn into_producer(self) -> Self::Output { - <_>::from(self) - } -} - -/// A mutable slice is a mutable one-dimensional producer -impl<'a, A: 'a> IntoNdProducer for &'a mut [A] { - type Item = ::Item; - type Dim = Ix1; - type Output = ArrayViewMut1<'a, A>; - fn into_producer(self) -> Self::Output { - <_>::from(self) - } -} - -/// A Vec is a one-dimensional producer -impl<'a, A: 'a> IntoNdProducer for &'a Vec { - type Item = ::Item; - type Dim = Ix1; - type Output = ArrayView1<'a, A>; - fn into_producer(self) -> Self::Output { - <_>::from(self) - } -} - -/// A mutable Vec is a mutable one-dimensional producer -impl<'a, A: 'a> IntoNdProducer for &'a mut Vec { - type Item = ::Item; - type Dim = Ix1; - type Output = ArrayViewMut1<'a, A>; - fn into_producer(self) -> Self::Output { - <_>::from(self) - } -} - -impl<'a, A, D: Dimension> NdProducer for ArrayView<'a, A, D> { - type Item = &'a A; - type Dim = D; - type Ptr = *mut A; - type Stride = isize; - - private_impl! {} - #[doc(hidden)] - fn raw_dim(&self) -> Self::Dim { - self.raw_dim() - } - - #[doc(hidden)] - fn equal_dim(&self, dim: &Self::Dim) -> bool { - self.dim.equal(dim) - } - - #[doc(hidden)] - fn as_ptr(&self) -> *mut A { - self.as_ptr() as _ - } - - #[doc(hidden)] - fn layout(&self) -> Layout { - self.layout_impl() - } - - #[doc(hidden)] - unsafe fn as_ref(&self, ptr: *mut A) -> Self::Item { - &*ptr - } - - #[doc(hidden)] - unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { - self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) - } - - #[doc(hidden)] - fn stride_of(&self, axis: Axis) -> isize { - self.stride_of(axis) - } - - #[inline(always)] - fn contiguous_stride(&self) -> Self::Stride { - 1 - } - - #[doc(hidden)] - fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { - self.split_at(axis, index) - } -} - -impl<'a, A, D: Dimension> NdProducer for ArrayViewMut<'a, A, D> { - type Item = &'a mut A; - type Dim = D; - type Ptr = *mut A; - type Stride = isize; - - private_impl! {} - #[doc(hidden)] - fn raw_dim(&self) -> Self::Dim { - self.raw_dim() - } - - #[doc(hidden)] - fn equal_dim(&self, dim: &Self::Dim) -> bool { - self.dim.equal(dim) - } - - #[doc(hidden)] - fn as_ptr(&self) -> *mut A { - self.as_ptr() as _ - } - - #[doc(hidden)] - fn layout(&self) -> Layout { - self.layout_impl() - } - - #[doc(hidden)] - unsafe fn as_ref(&self, ptr: *mut A) -> Self::Item { - &mut *ptr - } - - #[doc(hidden)] - unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { - self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) - } - - #[doc(hidden)] - fn stride_of(&self, axis: Axis) -> isize { - self.stride_of(axis) - } - - #[inline(always)] - fn contiguous_stride(&self) -> Self::Stride { - 1 - } - - #[doc(hidden)] - fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { - self.split_at(axis, index) - } -} - -impl NdProducer for RawArrayView { - type Item = *const A; - type Dim = D; - type Ptr = *const A; - type Stride = isize; - - private_impl! {} - #[doc(hidden)] - fn raw_dim(&self) -> Self::Dim { - self.raw_dim() - } - - #[doc(hidden)] - fn equal_dim(&self, dim: &Self::Dim) -> bool { - self.dim.equal(dim) - } - - #[doc(hidden)] - fn as_ptr(&self) -> *const A { - self.as_ptr() - } - - #[doc(hidden)] - fn layout(&self) -> Layout { - self.layout_impl() - } - - #[doc(hidden)] - unsafe fn as_ref(&self, ptr: *const A) -> *const A { - ptr - } - - #[doc(hidden)] - unsafe fn uget_ptr(&self, i: &Self::Dim) -> *const A { - self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) - } - - #[doc(hidden)] - fn stride_of(&self, axis: Axis) -> isize { - self.stride_of(axis) - } - - #[inline(always)] - fn contiguous_stride(&self) -> Self::Stride { - 1 - } - - #[doc(hidden)] - fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { - self.split_at(axis, index) - } -} - -impl NdProducer for RawArrayViewMut { - type Item = *mut A; - type Dim = D; - type Ptr = *mut A; - type Stride = isize; - - private_impl! {} - #[doc(hidden)] - fn raw_dim(&self) -> Self::Dim { - self.raw_dim() - } - - #[doc(hidden)] - fn equal_dim(&self, dim: &Self::Dim) -> bool { - self.dim.equal(dim) - } - - #[doc(hidden)] - fn as_ptr(&self) -> *mut A { - self.as_ptr() as _ - } - - #[doc(hidden)] - fn layout(&self) -> Layout { - self.layout_impl() - } - - #[doc(hidden)] - unsafe fn as_ref(&self, ptr: *mut A) -> *mut A { - ptr - } - - #[doc(hidden)] - unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { - self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) - } - - #[doc(hidden)] - fn stride_of(&self, axis: Axis) -> isize { - self.stride_of(axis) - } - - #[inline(always)] - fn contiguous_stride(&self) -> Self::Stride { - 1 - } - - #[doc(hidden)] - fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { - self.split_at(axis, index) - } -} - /// Lock step function application across several arrays or other producers. /// /// Zip allows matching several producers to each other elementwise and applying diff --git a/src/zip/ndproducer.rs b/src/zip/ndproducer.rs new file mode 100644 index 000000000..d8ec6f826 --- /dev/null +++ b/src/zip/ndproducer.rs @@ -0,0 +1,400 @@ + +use crate::imp_prelude::*; +use crate::Layout; +use crate::NdIndex; + +/// Argument conversion into a producer. +/// +/// Slices and vectors can be used (equivalent to 1-dimensional array views). +/// +/// This trait is like `IntoIterator` for `NdProducers` instead of iterators. +pub trait IntoNdProducer { + /// The element produced per iteration. + type Item; + /// Dimension type of the producer + type Dim: Dimension; + type Output: NdProducer; + /// Convert the value into an `NdProducer`. + fn into_producer(self) -> Self::Output; +} + +impl

/// /// /// /// /// /// @@ -1043,19 +1035,19 @@ pub type Ixs = isize; /// /// Input | Output | Methods /// ------|--------|-------- -/// `Vec` | `ArrayBase` | [`::from_vec()`](#method.from_vec) -/// `Vec` | `ArrayBase` | [`::from_shape_vec()`](#method.from_shape_vec) -/// `&[A]` | `ArrayView1` | [`::from()`](type.ArrayView.html#method.from) -/// `&[A]` | `ArrayView` | [`::from_shape()`](type.ArrayView.html#method.from_shape) -/// `&mut [A]` | `ArrayViewMut1` | [`::from()`](type.ArrayViewMut.html#method.from) -/// `&mut [A]` | `ArrayViewMut` | [`::from_shape()`](type.ArrayViewMut.html#method.from_shape) -/// `&ArrayBase` | `Vec` | [`.to_vec()`](#method.to_vec) -/// `Array` | `Vec` | [`.into_raw_vec()`](type.Array.html#method.into_raw_vec)[1](#into_raw_vec) -/// `&ArrayBase` | `&[A]` | [`.as_slice()`](#method.as_slice)[2](#req_contig_std), [`.as_slice_memory_order()`](#method.as_slice_memory_order)[3](#req_contig) -/// `&mut ArrayBase` | `&mut [A]` | [`.as_slice_mut()`](#method.as_slice_mut)[2](#req_contig_std), [`.as_slice_memory_order_mut()`](#method.as_slice_memory_order_mut)[3](#req_contig) -/// `ArrayView` | `&[A]` | [`.to_slice()`](type.ArrayView.html#method.to_slice)[2](#req_contig_std) -/// `ArrayViewMut` | `&mut [A]` | [`.into_slice()`](type.ArrayViewMut.html#method.into_slice)[2](#req_contig_std) -/// `Array0` | `A` | [`.into_scalar()`](type.Array.html#method.into_scalar) +/// `Vec` | `ArrayBase` | [`::from_vec()`](Self::from_vec) +/// `Vec` | `ArrayBase` | [`::from_shape_vec()`](Self::from_shape_vec) +/// `&[A]` | `ArrayView1` | [`::from()`](ArrayView#method.from) +/// `&[A]` | `ArrayView` | [`::from_shape()`](ArrayView#method.from_shape) +/// `&mut [A]` | `ArrayViewMut1` | [`::from()`](ArrayViewMut#method.from) +/// `&mut [A]` | `ArrayViewMut` | [`::from_shape()`](ArrayViewMut#method.from_shape) +/// `&ArrayBase` | `Vec` | [`.to_vec()`](Self::to_vec) +/// `Array` | `Vec` | [`.into_raw_vec()`](Array#method.into_raw_vec)[1](#into_raw_vec) +/// `&ArrayBase` | `&[A]` | [`.as_slice()`](Self::as_slice)[2](#req_contig_std), [`.as_slice_memory_order()`](Self::as_slice_memory_order)[3](#req_contig) +/// `&mut ArrayBase` | `&mut [A]` | [`.as_slice_mut()`](Self::as_slice_mut)[2](#req_contig_std), [`.as_slice_memory_order_mut()`](Self::as_slice_memory_order_mut)[3](#req_contig) +/// `ArrayView` | `&[A]` | [`.to_slice()`](ArrayView#method.to_slice)[2](#req_contig_std) +/// `ArrayViewMut` | `&mut [A]` | [`.into_slice()`](ArrayViewMut#method.into_slice)[2](#req_contig_std) +/// `Array0` | `A` | [`.into_scalar()`](Array#method.into_scalar) /// /// 1Returns the data in memory order. /// @@ -1068,16 +1060,16 @@ pub type Ixs = isize; /// conversions to/from `Vec`s/slices. See /// [below](#constructor-methods-for-owned-arrays) for more constructors. /// -/// [ArrayView::reborrow()]: type.ArrayView.html#method.reborrow -/// [ArrayViewMut::reborrow()]: type.ArrayViewMut.html#method.reborrow -/// [.into_dimensionality()]: #method.into_dimensionality -/// [.into_dyn()]: #method.into_dyn -/// [.into_owned()]: #method.into_owned -/// [.into_shared()]: #method.into_shared -/// [.to_owned()]: #method.to_owned -/// [.map()]: #method.map -/// [.view()]: #method.view -/// [.view_mut()]: #method.view_mut +/// [ArrayView::reborrow()]: ArrayView#method.reborrow +/// [ArrayViewMut::reborrow()]: ArrayViewMut#method.reborrow +/// [.into_dimensionality()]: Self::into_dimensionality +/// [.into_dyn()]: Self::into_dyn +/// [.into_owned()]: Self::into_owned +/// [.into_shared()]: Self::into_shared +/// [.to_owned()]: Self::to_owned +/// [.map()]: Self::map +/// [.view()]: Self::view +/// [.view_mut()]: Self::view_mut /// /// ### Conversions from Nested `Vec`s/`Array`s /// @@ -1119,7 +1111,7 @@ pub type Ixs = isize; /// If you don't know ahead-of-time the shape of the final array, then the /// cleanest solution is generally to append the data to a flat `Vec`, and then /// convert it to an `Array` at the end with -/// [`::from_shape_vec()`](#method.from_shape_vec). You just have to be careful +/// [`::from_shape_vec()`](Self::from_shape_vec). You just have to be careful /// that the layout of the data (the order of the elements in the flat `Vec`) /// is correct. /// @@ -1142,10 +1134,9 @@ pub type Ixs = isize; /// /// If neither of these options works for you, and you really need to convert /// nested `Vec`/`Array` instances to an `Array`, the cleanest solution is -/// generally to use -/// [`Iterator::flatten()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.flatten) +/// generally to use [`Iterator::flatten()`] /// to get a flat `Vec`, and then convert the `Vec` to an `Array` with -/// [`::from_shape_vec()`](#method.from_shape_vec), like this: +/// [`::from_shape_vec()`](Self::from_shape_vec), like this: /// /// ```rust /// use ndarray::{array, Array2, Array3}; @@ -1308,20 +1299,20 @@ where /// It can act as both an owner as the data as well as a shared reference (view /// like). /// Calling a method for mutating elements on `ArcArray`, for example -/// [`view_mut()`](struct.ArrayBase.html#method.view_mut) or -/// [`get_mut()`](struct.ArrayBase.html#method.get_mut), will break sharing and +/// [`view_mut()`](ArrayBase::view_mut) or +/// [`get_mut()`](ArrayBase::get_mut), will break sharing and /// require a clone of the data (if it is not uniquely held). /// /// `ArcArray` uses atomic reference counting like `Arc`, so it is `Send` and /// `Sync` (when allowed by the element type of the array too). /// -/// [**`ArrayBase`**](struct.ArrayBase.html) is used to implement both the owned +/// **[`ArrayBase`]** is used to implement both the owned /// arrays and the views; see its docs for an overview of all array features. /// /// See also: /// -/// + [Constructor Methods for Owned Arrays](struct.ArrayBase.html#constructor-methods-for-owned-arrays) -/// + [Methods For All Array Types](struct.ArrayBase.html#methods-for-all-array-types) +/// + [Constructor Methods for Owned Arrays](ArrayBase#constructor-methods-for-owned-arrays) +/// + [Methods For All Array Types](ArrayBase#methods-for-all-array-types) pub type ArcArray = ArrayBase, D>; /// An array that owns its data uniquely. @@ -1332,18 +1323,18 @@ pub type ArcArray = ArrayBase, D>; /// The `Array` is parameterized by `A` for the element type and `D` for /// the dimensionality. /// -/// [**`ArrayBase`**](struct.ArrayBase.html) is used to implement both the owned +/// **[`ArrayBase`]** is used to implement both the owned /// arrays and the views; see its docs for an overview of all array features. /// /// See also: /// -/// + [Constructor Methods for Owned Arrays](struct.ArrayBase.html#constructor-methods-for-owned-arrays) -/// + [Methods For All Array Types](struct.ArrayBase.html#methods-for-all-array-types) +/// + [Constructor Methods for Owned Arrays](ArrayBase#constructor-methods-for-owned-arrays) +/// + [Methods For All Array Types](ArrayBase#methods-for-all-array-types) /// + Dimensionality-specific type alises -/// [`Array1`](type.Array1.html), -/// [`Array2`](type.Array2.html), -/// [`Array3`](type.Array3.html), ..., -/// [`ArrayD`](type.ArrayD.html), +/// [`Array1`], +/// [`Array2`], +/// [`Array3`], ..., +/// [`ArrayD`], /// and so on. pub type Array = ArrayBase, D>; @@ -1352,20 +1343,17 @@ pub type Array = ArrayBase, D>; /// An `CowArray` represents either a uniquely owned array or a view of an /// array. The `'a` corresponds to the lifetime of the view variant. /// -/// This type is analogous to -/// [`std::borrow::Cow`](https://doc.rust-lang.org/std/borrow/enum.Cow.html). +/// This type is analogous to [`std::borrow::Cow`]. /// If a `CowArray` instance is the immutable view variant, then calling a /// method for mutating elements in the array will cause it to be converted /// into the owned variant (by cloning all the elements) before the /// modification is performed. /// -/// Array views have all the methods of an array (see [`ArrayBase`][ab]). +/// Array views have all the methods of an array (see [`ArrayBase`]). /// -/// See also [`ArcArray`](type.ArcArray.html), which also provides +/// See also [`ArcArray`], which also provides /// copy-on-write behavior but has a reference-counted pointer to the data /// instead of either a view or a uniquely owned copy. -/// -/// [ab]: struct.ArrayBase.html pub type CowArray<'a, A, D> = ArrayBase, D>; /// A read-only array view. @@ -1376,11 +1364,9 @@ pub type CowArray<'a, A, D> = ArrayBase, D>; /// The `ArrayView<'a, A, D>` is parameterized by `'a` for the scope of the /// borrow, `A` for the element type and `D` for the dimensionality. /// -/// Array views have all the methods of an array (see [`ArrayBase`][ab]). -/// -/// See also [`ArrayViewMut`](type.ArrayViewMut.html). +/// Array views have all the methods of an array (see [`ArrayBase`]). /// -/// [ab]: struct.ArrayBase.html +/// See also [`ArrayViewMut`]. pub type ArrayView<'a, A, D> = ArrayBase, D>; /// A read-write array view. @@ -1391,11 +1377,9 @@ pub type ArrayView<'a, A, D> = ArrayBase, D>; /// The `ArrayViewMut<'a, A, D>` is parameterized by `'a` for the scope of the /// borrow, `A` for the element type and `D` for the dimensionality. /// -/// Array views have all the methods of an array (see [`ArrayBase`][ab]). +/// Array views have all the methods of an array (see [`ArrayBase`]). /// -/// See also [`ArrayView`](type.ArrayView.html). -/// -/// [ab]: struct.ArrayBase.html +/// See also [`ArrayView`]. pub type ArrayViewMut<'a, A, D> = ArrayBase, D>; /// A read-only array view without a lifetime. @@ -1407,15 +1391,13 @@ pub type ArrayViewMut<'a, A, D> = ArrayBase, D>; /// T` and `&T`, but `RawArrayView` has additional requirements that `*const T` /// does not, such as non-nullness. /// -/// [`ArrayView`]: type.ArrayView.html -/// /// The `RawArrayView` is parameterized by `A` for the element type and /// `D` for the dimensionality. /// /// Raw array views have all the methods of an array (see -/// [`ArrayBase`](struct.ArrayBase.html)). +/// [`ArrayBase`]). /// -/// See also [`RawArrayViewMut`](type.RawArrayViewMut.html). +/// See also [`RawArrayViewMut`]. /// /// # Warning /// @@ -1432,15 +1414,13 @@ pub type RawArrayView = ArrayBase, D>; /// relationship between `*mut T` and `&mut T`, but `RawArrayViewMut` has /// additional requirements that `*mut T` does not, such as non-nullness. /// -/// [`ArrayViewMut`]: type.ArrayViewMut.html -/// /// The `RawArrayViewMut` is parameterized by `A` for the element type /// and `D` for the dimensionality. /// /// Raw array views have all the methods of an array (see -/// [`ArrayBase`](struct.ArrayBase.html)). +/// [`ArrayBase`]). /// -/// See also [`RawArrayView`](type.RawArrayView.html). +/// See also [`RawArrayView`]. /// /// # Warning /// @@ -1453,7 +1433,7 @@ pub use data_repr::OwnedRepr; /// ArcArray's representation. /// /// *Don’t use this type directly—use the type alias -/// [`ArcArray`](type.ArcArray.html) for the array type!* +/// [`ArcArray`] for the array type!* #[derive(Debug)] pub struct OwnedArcRepr(Arc>); @@ -1466,8 +1446,7 @@ impl Clone for OwnedArcRepr { /// Array pointer’s representation. /// /// *Don’t use this type directly—use the type aliases -/// [`RawArrayView`](type.RawArrayView.html) / -/// [`RawArrayViewMut`](type.RawArrayViewMut.html) for the array type!* +/// [`RawArrayView`] / [`RawArrayViewMut`] for the array type!* #[derive(Copy, Clone)] // This is just a marker type, to carry the mutability and element type. pub struct RawViewRepr { @@ -1484,8 +1463,7 @@ impl RawViewRepr { /// Array view’s representation. /// /// *Don’t use this type directly—use the type aliases -/// [`ArrayView`](type.ArrayView.html) -/// / [`ArrayViewMut`](type.ArrayViewMut.html) for the array type!* +/// [`ArrayView`] / [`ArrayViewMut`] for the array type!* #[derive(Copy, Clone)] // This is just a marker type, to carry the lifetime parameter. pub struct ViewRepr { @@ -1502,7 +1480,7 @@ impl ViewRepr { /// CowArray's representation. /// /// *Don't use this type directly—use the type alias -/// [`CowArray`](type.CowArray.html) for the array type!* +/// [`CowArray`] for the array type!* pub enum CowRepr<'a, A> { /// Borrowed data. View(ViewRepr<&'a A>), diff --git a/src/slice.rs b/src/slice.rs index 44e768396..dd0d162e2 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -20,7 +20,7 @@ use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, Rang /// from the back of the axis. If `end` is `None`, the slice extends to the end /// of the axis. /// -/// See also the [`s![]`](macro.s.html) macro. +/// See also the [`s![]`](s!) macro. /// /// ## Examples /// @@ -75,13 +75,13 @@ impl Slice { /// Token to represent a new axis in a slice description. /// -/// See also the [`s![]`](macro.s!.html) macro. +/// See also the [`s![]`](s!) macro. #[derive(Clone, Copy, Debug)] pub struct NewAxis; /// A slice (range with step), an index, or a new axis token. /// -/// See also the [`s![]`](macro.s!.html) macro for a convenient way to create a +/// See also the [`s![]`](s!) macro for a convenient way to create a /// `SliceInfo<[SliceInfoElem; n], Din, Dout>`. /// /// ## Examples @@ -398,7 +398,7 @@ unsafe impl SliceArg for [SliceInfoElem] { /// `SliceInfo` instance can still be used to slice an array with dimension /// `IxDyn` as long as the number of axes matches. /// -/// [`.slice()`]: struct.ArrayBase.html#method.slice +/// [`.slice()`]: crate::ArrayBase::slice #[derive(Debug)] pub struct SliceInfo { in_dim: PhantomData, @@ -487,7 +487,7 @@ where } /// Returns the number of dimensions of the input array for - /// [`.slice()`](struct.ArrayBase.html#method.slice). + /// [`.slice()`](crate::ArrayBase::slice). /// /// If `Din` is a fixed-size dimension type, then this is equivalent to /// `Din::NDIM.unwrap()`. Otherwise, the value is calculated by iterating @@ -501,7 +501,7 @@ where } /// Returns the number of dimensions after calling - /// [`.slice()`](struct.ArrayBase.html#method.slice) (including taking + /// [`.slice()`](crate::ArrayBase::slice) (including taking /// subviews). /// /// If `Dout` is a fixed-size dimension type, then this is equivalent to @@ -704,9 +704,6 @@ impl_slicenextdim!((), NewAxis, Ix0, Ix1); /// * *new-axis*: a [`NewAxis`] instance that represents the creation of a new axis. /// (Except for [`.slice_collapse()`], which panics on [`NewAxis`] elements.) /// -/// [`Slice`]: struct.Slice.html -/// [`NewAxis`]: struct.NewAxis.html -/// /// The number of *elem*, not including *new-axis*, must match the /// number of axes in the array. *index*, *range*, *slice*, *step*, and /// *new-axis* can be expressions. *index* must be of type `isize`, `usize`, or @@ -723,12 +720,12 @@ impl_slicenextdim!((), NewAxis, Ix0, Ix1); /// panic. Without the `NewAxis`, i.e. `s![0..4;2, 6, 1..5]`, /// [`.slice_collapse()`] would result in an array of shape `[2, 1, 4]`. /// -/// [`.slice()`]: struct.ArrayBase.html#method.slice -/// [`.slice_mut()`]: struct.ArrayBase.html#method.slice_mut -/// [`.slice_move()`]: struct.ArrayBase.html#method.slice_move -/// [`.slice_collapse()`]: struct.ArrayBase.html#method.slice_collapse +/// [`.slice()`]: crate::ArrayBase::slice +/// [`.slice_mut()`]: crate::ArrayBase::slice_mut +/// [`.slice_move()`]: crate::ArrayBase::slice_move +/// [`.slice_collapse()`]: crate::ArrayBase::slice_collapse /// -/// See also [*Slicing*](struct.ArrayBase.html#slicing). +/// See also [*Slicing*](crate::ArrayBase#slicing). /// /// # Example /// diff --git a/src/stacking.rs b/src/stacking.rs index f8df7b5f8..5a47589d5 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -178,11 +178,9 @@ where /// Stack arrays along the new axis. /// -/// Uses the [`stack`][1] function, calling `ArrayView::from(&a)` on each +/// Uses the [`stack()`] function, calling `ArrayView::from(&a)` on each /// argument `a`. /// -/// [1]: fn.stack.html -/// /// ***Panics*** if the `stack` function would return an error. /// /// ``` @@ -229,11 +227,9 @@ macro_rules! stack { /// Concatenate arrays along the given axis. /// -/// Uses the [`concatenate`][1] function, calling `ArrayView::from(&a)` on each +/// Uses the [`concatenate()`] function, calling `ArrayView::from(&a)` on each /// argument `a`. /// -/// [1]: fn.concatenate.html -/// /// ***Panics*** if the `concatenate` function would return an error. /// /// ``` @@ -271,11 +267,9 @@ macro_rules! concatenate { /// Stack arrays along the new axis. /// -/// Uses the [`stack_new_axis`][1] function, calling `ArrayView::from(&a)` on each +/// Uses the [`stack_new_axis()`] function, calling `ArrayView::from(&a)` on each /// argument `a`. /// -/// [1]: fn.stack_new_axis.html -/// /// ***Panics*** if the `stack` function would return an error. /// /// ``` diff --git a/src/zip/mod.rs b/src/zip/mod.rs index cdc190a40..a24adc2c1 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -37,9 +37,7 @@ macro_rules! fold_while { /// Broadcast an array so that it acts like a larger size and/or shape array. /// -/// See [broadcasting][1] for more information. -/// -/// [1]: struct.ArrayBase.html#broadcasting +/// See [broadcasting](ArrayBase#broadcasting) for more information. trait Broadcast where E: IntoDimension, @@ -120,7 +118,7 @@ trait ZippableTuple: Sized { /// a time). /// /// In general, the zip uses a tuple of producers -/// ([`NdProducer`](trait.NdProducer.html) trait) that all have to be of the +/// ([`NdProducer`] trait) that all have to be of the /// same shape. The NdProducer implementation defines what its item type is /// (for example if it's a shared reference, mutable reference or an array /// view etc). @@ -135,11 +133,9 @@ trait ZippableTuple: Sized { /// `fold_while`. The zip object can be split, which allows parallelization. /// A read-only zip object (no mutable producers) can be cloned. /// -/// See also the [`azip!()` macro][az] which offers a convenient shorthand +/// See also the [`azip!()`] which offers a convenient shorthand /// to common ways to use `Zip`. /// -/// [az]: macro.azip.html -/// /// ``` /// use ndarray::Zip; /// use ndarray::Array2; diff --git a/src/zip/ndproducer.rs b/src/zip/ndproducer.rs index 7c201741c..619fadcc3 100644 --- a/src/zip/ndproducer.rs +++ b/src/zip/ndproducer.rs @@ -36,8 +36,8 @@ where /// for example an array view, mutable array view or an iterator /// that yields chunks. /// -/// Producers are used as a arguments to [`Zip`](struct.Zip.html) and -/// [`azip!()`](macro.azip.html). +/// Producers are used as a arguments to [`Zip`](crate::Zip) and +/// [`azip!()`]. /// /// # Comparison to `IntoIterator` /// @@ -51,7 +51,7 @@ where /// (`AxisIter` traverses a one dimensional sequence, along an axis, while /// *producing* multidimensional items). /// -/// See also [`IntoNdProducer`](trait.IntoNdProducer.html) +/// See also [`IntoNdProducer`] pub trait NdProducer { /// The element produced per iteration. type Item; diff --git a/src/zip/zipmacro.rs b/src/zip/zipmacro.rs index dbd3a56a4..0bbe956b3 100644 --- a/src/zip/zipmacro.rs +++ b/src/zip/zipmacro.rs @@ -1,7 +1,7 @@ /// Array zip macro: lock step function application across several arrays and /// producers. /// -/// This is a shorthand for [`Zip`](struct.Zip.html). +/// This is a shorthand for [`Zip`](crate::Zip). /// /// This example: /// From 6e19f048700be1a5acbf6785fb93cb55be311cb4 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Thu, 30 Sep 2021 17:27:58 -0400 Subject: [PATCH 393/651] Add from_diag_elem method --- src/impl_constructors.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index e800a17ea..df5e295c6 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -230,6 +230,29 @@ where arr.diag_mut().assign(diag); arr } + + /// Create a square 2D matrix of the specified size, with the specified + /// element along the diagonal and zeros elsewhere. + /// + /// **Panics** if `n * n` would overflow `isize`. + /// + /// ```rust + /// use ndarray::{array, Array2}; + /// + /// let array = Array2::from_diag_elem(2, 5.); + /// assert_eq!(array, array![[5., 0.], [0., 5.]]); + /// ``` + pub fn from_diag_elem(n: usize, elem: A) -> Self + where + S: DataMut, + A: Clone + Zero, + { + let mut eye = Self::zeros((n, n)); + for a_ii in eye.diag_mut() { + *a_ii = elem.clone(); + } + eye + } } #[cfg(not(debug_assertions))] From 69993f2621acde5f825e7365ce1e0ee6c75898f0 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Fri, 5 Nov 2021 17:35:59 -0700 Subject: [PATCH 394/651] Implement Kronecker product --- src/linalg/impl_linalg.rs | 28 +++++++++++++++++ src/linalg/mod.rs | 1 + tests/oper.rs | 63 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index d851f2f08..b710138df 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use crate::OwnedRepr; use crate::imp_prelude::*; use crate::numeric_util; #[cfg(feature = "blas")] @@ -14,6 +15,7 @@ use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr; use crate::{LinalgScalar, Zip}; use std::any::TypeId; +use std::mem::MaybeUninit; use alloc::vec::Vec; #[cfg(feature = "blas")] @@ -699,6 +701,32 @@ unsafe fn general_mat_vec_mul_impl( } } + +/// Kronecker product of 2D matrices. +/// +/// The kronecker product of a LxN matrix A and a MxR matrix B is a (L*M)x(N*R) +/// matrix K formed by the block multiplication A_ij * B. +pub fn kron<'a, A, S1, S2>(a: &ArrayBase, b: &'a ArrayBase) -> ArrayBase, Ix2> +where + S1: Data, + S2: Data, + A: LinalgScalar, + A: std::ops::Mul<&'a ArrayBase, Output = ArrayBase, Ix2>>, +{ + let dimar = a.shape()[0]; + let dimac = a.shape()[1]; + let dimbr = b.shape()[0]; + let dimbc = b.shape()[1]; + let mut out: Array2> = Array2::uninit((dimar * dimbr, dimac * dimbc)); + Zip::from(out.exact_chunks_mut((dimbr, dimbc))) + .and(a) + .for_each(|out, a| { + (*a * b).assign_to(out); + }); + unsafe { out.assume_init() } +} + + #[inline(always)] /// Return `true` if `A` and `B` are the same type fn same_type() -> bool { diff --git a/src/linalg/mod.rs b/src/linalg/mod.rs index 8575905cd..abd7b2b9d 100644 --- a/src/linalg/mod.rs +++ b/src/linalg/mod.rs @@ -11,5 +11,6 @@ pub use self::impl_linalg::general_mat_mul; pub use self::impl_linalg::general_mat_vec_mul; pub use self::impl_linalg::Dot; +pub use self::impl_linalg::kron; mod impl_linalg; diff --git a/tests/oper.rs b/tests/oper.rs index ed612bad2..051728680 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -6,6 +6,7 @@ )] #![cfg(feature = "std")] use ndarray::linalg::general_mat_mul; +use ndarray::linalg::kron; use ndarray::prelude::*; use ndarray::{rcarr1, rcarr2}; use ndarray::{Data, LinalgScalar}; @@ -820,3 +821,65 @@ fn vec_mat_mul() { } } } + +#[test] +fn kron_square_f64() { + let a = arr2(&[[1.0, 0.0], [0.0, 1.0]]); + let b = arr2(&[[0.0, 1.0], [1.0, 0.0]]); + + assert_eq!( + kron(&a, &b), + arr2(&[ + [0.0, 1.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + [0.0, 0.0, 1.0, 0.0] + ]), + ); + + assert_eq!( + kron(&b, &a), + arr2(&[ + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + [1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0] + ]), + ) +} + +#[test] +fn kron_square_i64() { + let a = arr2(&[[1, 0], [0, 1]]); + let b = arr2(&[[0, 1], [1, 0]]); + + assert_eq!( + kron(&a, &b), + arr2(&[[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]), + ); + + assert_eq!( + kron(&b, &a), + arr2(&[[0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0], [0, 1, 0, 0]]), + ) +} + +#[test] +fn kron_i64() { + let a = arr2(&[[1, 0]]); + let b = arr2(&[[0, 1], [1, 0]]); + let r = arr2(&[[0, 1, 0, 0], [1, 0, 0, 0]]); + assert_eq!(kron(&a, &b), r); + + let a = arr2(&[[1, 0], [0, 0], [0, 1]]); + let b = arr2(&[[0, 1], [1, 0]]); + let r = arr2(&[ + [0, 1, 0, 0], + [1, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 1], + [0, 0, 1, 0], + ]); + assert_eq!(kron(&a, &b), r); +} From 155f941f8cce0ac751f436df5734717110993cc7 Mon Sep 17 00:00:00 2001 From: Alex Touchet Date: Fri, 5 Nov 2021 16:09:31 -0700 Subject: [PATCH 395/651] Fix crates.io badge --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 503024886..a6ed9e90f 100644 --- a/README.rst +++ b/README.rst @@ -15,7 +15,7 @@ __ https://docs.rs/ndarray/ :alt: CI build status .. _build_status: https://github.com/rust-ndarray/ndarray/actions -.. |crates| image:: http://meritbadge.herokuapp.com/ndarray +.. |crates| image:: https://img.shields.io/crates/v/ndarray.svg :alt: ndarray at crates.io .. _crates: https://crates.io/crates/ndarray From 187cea974ed0dfac2c34f83bd49f510ea1d3b95e Mon Sep 17 00:00:00 2001 From: VasanthakumarV Date: Tue, 14 Sep 2021 19:23:47 +0530 Subject: [PATCH 396/651] Add tests for `axis_windows` iterator --- tests/windows.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/tests/windows.rs b/tests/windows.rs index b0482e4bd..664616f67 100644 --- a/tests/windows.rs +++ b/tests/windows.rs @@ -6,7 +6,7 @@ )] use ndarray::prelude::*; -use ndarray::Zip; +use ndarray::{arr3, Zip}; // Edge Cases for Windows iterator: // @@ -79,7 +79,6 @@ fn windows_iterator_2d() { /// Simple test for iterating 3d-arrays via `Windows`. #[test] fn windows_iterator_3d() { - use ndarray::arr3; let a = Array::from_iter(10..37).into_shape((3, 3, 3)).unwrap(); itertools::assert_equal( a.windows(Dim((2, 2, 2))), @@ -117,6 +116,87 @@ fn test_window_zip() { } } +/// Test verifies that non existant Axis results in panic +#[test] +#[should_panic] +fn axis_windows_outofbound() { + let a = Array::from_iter(10..37).into_shape((3, 3, 3)).unwrap(); + a.axis_windows(Axis(4), 2); +} + +/// Test verifies that zero sizes results in panic +#[test] +#[should_panic] +fn axis_windows_zero_size() { + let a = Array::from_iter(10..37).into_shape((3, 3, 3)).unwrap(); + a.axis_windows(Axis(0), 0); +} + +/// Test verifies that over sized windows yield nothing +#[test] +fn axis_windows_oversized() { + let a = Array::from_iter(10..37).into_shape((3, 3, 3)).unwrap(); + let mut iter = a.axis_windows(Axis(2), 4).into_iter(); + assert_eq!(iter.next(), None); +} + +/// Simple test for iterating 1d-arrays via `Axis Windows`. +#[test] +fn test_axis_windows_1d() { + let a = Array::from_iter(10..20).into_shape(10).unwrap(); + + itertools::assert_equal( + a.axis_windows(Axis(0), 5), + vec![ + arr1(&[10, 11, 12, 13, 14]), + arr1(&[11, 12, 13, 14, 15]), + arr1(&[12, 13, 14, 15, 16]), + arr1(&[13, 14, 15, 16, 17]), + arr1(&[14, 15, 16, 17, 18]), + arr1(&[15, 16, 17, 18, 19]), + ], + ); +} + +/// Simple test for iterating 2d-arrays via `Axis Windows`. +#[test] +fn test_axis_windows_2d() { + let a = Array::from_iter(10..30).into_shape((5, 4)).unwrap(); + + itertools::assert_equal( + a.axis_windows(Axis(0), 2), + vec![ + arr2(&[[10, 11, 12, 13], [14, 15, 16, 17]]), + arr2(&[[14, 15, 16, 17], [18, 19, 20, 21]]), + arr2(&[[18, 19, 20, 21], [22, 23, 24, 25]]), + arr2(&[[22, 23, 24, 25], [26, 27, 28, 29]]), + ], + ); +} + +/// Simple test for iterating 3d-arrays via `Axis Windows`. +#[test] +fn test_axis_windows_3d() { + let a = Array::from_iter(0..27).into_shape((3, 3, 3)).unwrap(); + + itertools::assert_equal( + a.axis_windows(Axis(1), 2), + vec![ + arr3(&[ + [[0, 1, 2], [3, 4, 5]], + [[9, 10, 11], [12, 13, 14]], + [[18, 19, 20], [21, 22, 23]], + ]), + arr3(&[ + [[3, 4, 5], [6, 7, 8]], + [[12, 13, 14], [15, 16, 17]], + [[21, 22, 23], [24, 25, 26]], + ]), + ], + ); +} + + #[test] fn test_window_neg_stride() { let array = Array::from_iter(1..10).into_shape((3, 3)).unwrap(); From b10b09cf100af73d3233f1413ee664e013be0f75 Mon Sep 17 00:00:00 2001 From: VasanthakumarV Date: Tue, 14 Sep 2021 19:24:13 +0530 Subject: [PATCH 397/651] Add `axis windows` method Co-authored-by: Jim Turner --- src/impl_methods.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 27abe6893..cdba17918 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1399,6 +1399,50 @@ where Windows::new(self.view(), window_size) } + /// Returns a producer which traverses over all windows of a given length along an axis. + /// + /// The windows are all distinct, possibly-overlapping views. The shape of each window + /// is the shape of `self`, with the length of `axis` replaced with `window_size`. + /// + /// **Panics** if `axis` is out-of-bounds or if `window_size` is zero. + /// + /// ``` + /// use ndarray::{Array3, Axis, s}; + /// + /// let arr = Array3::from_shape_fn([4, 5, 2], |(i, j, k)| i * 100 + j * 10 + k); + /// let correct = vec![ + /// arr.slice(s![.., 0..3, ..]), + /// arr.slice(s![.., 1..4, ..]), + /// arr.slice(s![.., 2..5, ..]), + /// ]; + /// for (window, correct) in arr.axis_windows(Axis(1), 3).into_iter().zip(&correct) { + /// assert_eq!(window, correct); + /// assert_eq!(window.shape(), &[4, 3, 2]); + /// } + /// ``` + pub fn axis_windows(&self, axis: Axis, window_size: usize) -> Windows<'_, A, D> + where + S: Data, + { + let axis_index = axis.index(); + + ndassert!( + axis_index < self.ndim(), + concat!( + "Window axis {} does not match array dimension {} ", + "(with array of shape {:?})" + ), + axis_index, + self.ndim(), + self.shape() + ); + + let mut size = self.raw_dim(); + size[axis_index] = window_size; + + Windows::new(self.view(), size) + } + // Return (length, stride) for diagonal fn diag_params(&self) -> (Ix, Ixs) { /* empty shape has len 1 */ From 493674d3eae18bf76f0e6d5c605627857c345dcb Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Wed, 27 Oct 2021 12:00:47 +0200 Subject: [PATCH 398/651] Add a par_fold method to Zip to improve the discoverability of Rayon's fold-reduce idiom. --- src/parallel/impl_par_methods.rs | 50 ++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/parallel/impl_par_methods.rs b/src/parallel/impl_par_methods.rs index 33d393345..381b86b52 100644 --- a/src/parallel/impl_par_methods.rs +++ b/src/parallel/impl_par_methods.rs @@ -177,6 +177,56 @@ macro_rules! zip_impl { self.par_map_assign_into(into, f) } + /// Parallel version of `fold`. + /// + /// Splits the producer in multiple tasks which each accumulate a single value + /// using the `fold` closure. Those tasks are executed in parallel and their results + /// are then combined to a single value using the `reduce` closure. + /// + /// The `identity` closure provides the initial values for each of the tasks and + /// for the final reduction. + /// + /// This is a shorthand for calling `self.into_par_iter().fold(...).reduce(...)`. + /// + /// Note that it is often more efficient to parallelize not per-element but rather + /// based on larger chunks of an array like generalized rows and operating on each chunk + /// using a sequential variant of the accumulation. + /// For example, sum each row sequentially and in parallel, taking advatange of locality + /// and vectorization within each task, and then reduce their sums to the sum of the matrix. + /// + /// Also note that the splitting of the producer into multiple tasks is _not_ deterministic + /// which needs to be considered when the accuracy of such an operation is analyzed. + /// + /// ## Examples + /// + /// ```rust + /// use ndarray::{Array, Zip}; + /// + /// let a = Array::::ones((128, 1024)); + /// let b = Array::::ones(128); + /// + /// let weighted_sum = Zip::from(a.rows()).and(&b).par_fold( + /// || 0, + /// |sum, row, factor| sum + row.sum() * factor, + /// |sum, other_sum| sum + other_sum, + /// ); + /// + /// assert_eq!(weighted_sum, a.len()); + /// ``` + pub fn par_fold(self, identity: ID, fold: F, reduce: R) -> T + where + ID: Fn() -> T + Send + Sync + Clone, + F: Fn(T, $($p::Item),*) -> T + Send + Sync, + R: Fn(T, T) -> T + Send + Sync, + T: Send + { + self.into_par_iter() + .fold(identity.clone(), move |accumulator, ($($p,)*)| { + fold(accumulator, $($p),*) + }) + .reduce(identity, reduce) + } + ); } )+ From f2f3a44d2c0da5caedccc0c54d695cc2e3949720 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Sun, 7 Nov 2021 03:13:16 -0800 Subject: [PATCH 399/651] Add a few tests for split_re_im --- tests/array.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/array.rs b/tests/array.rs index d0fc67def..d702b221e 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -7,12 +7,14 @@ clippy::float_cmp )] +use approx::assert_relative_eq; use defmac::defmac; use itertools::{zip, Itertools}; use ndarray::prelude::*; use ndarray::{arr3, rcarr2}; use ndarray::indices; use ndarray::{Slice, SliceInfo, SliceInfoElem}; +use num_complex::Complex; use std::convert::TryFrom; macro_rules! assert_panics { @@ -2501,3 +2503,40 @@ fn test_remove_index_oob3() { let mut a = array![[10], [4], [1]]; a.remove_index(Axis(2), 0); } + +#[test] +fn test_split_re_im_view() { + let a = Array3::from_shape_fn((3, 4, 5), |(i, j, k)| { + Complex::::new(i as f32 * j as f32, k as f32) + }); + let Complex { re, im } = a.view().split_re_im(); + assert_relative_eq!(re.sum(), 90.); + assert_relative_eq!(im.sum(), 120.); +} + +#[test] +fn test_split_re_im_view_roundtrip() { + let a_re = Array3::from_shape_fn((3,4,5), |(i, j, _k)| { + i * j + }); + let a_im = Array3::from_shape_fn((3,4,5), |(_i, _j, k)| { + k + }); + let a = Array3::from_shape_fn((3,4,5), |(i,j,k)| { + Complex::new(a_re[[i,j,k]], a_im[[i,j,k]]) + }); + let Complex { re, im } = a.view().split_re_im(); + assert_eq!(a_re, re); + assert_eq!(a_im, im); +} + +#[test] +fn test_split_re_im_view_mut() { + let eye_scalar = Array2::::eye(4); + let eye_complex = Array2::>::eye(4); + let mut a = Array2::>::zeros((4, 4)); + let Complex { mut re, im } = a.view_mut().split_re_im(); + re.assign(&eye_scalar); + assert_eq!(im.sum(), 0); + assert_eq!(a, eye_complex); +} From c37b309a278296ef051a3cfb7409ceb43fe894de Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Sun, 7 Nov 2021 03:46:09 -0800 Subject: [PATCH 400/651] Remove unused pub(crate) --- src/impl_methods.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index bf68771e6..27abe6893 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1452,7 +1452,7 @@ where /// Make the array unshared. /// /// This method is mostly only useful with unsafe code. - pub(crate) fn ensure_unique(&mut self) + fn ensure_unique(&mut self) where S: DataMut, { From 36aa2be0f23c452a1ffc7ba771e65988dd530efb Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 1 Jun 2021 17:46:16 -0400 Subject: [PATCH 401/651] Add try_into_owned_nocopy method --- src/data_traits.rs | 74 +++++++++++++++++++++++++++++++++++++++++++++ src/impl_methods.rs | 28 +++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/src/data_traits.rs b/src/data_traits.rs index bcf196597..103d864b9 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -109,6 +109,15 @@ pub unsafe trait Data: RawData { Self::Elem: Clone, D: Dimension; + /// Converts the array into `Array` if this is possible without + /// cloning the array elements. Otherwise, returns `self_` unchanged. + #[doc(hidden)] + fn try_into_owned_nocopy( + self_: ArrayBase, + ) -> Result, D>, ArrayBase> + where + D: Dimension; + /// Return a shared ownership (copy on write) array based on the existing one, /// cloning elements if necessary. #[doc(hidden)] @@ -276,6 +285,27 @@ unsafe impl Data for OwnedArcRepr { } } + fn try_into_owned_nocopy( + self_: ArrayBase, + ) -> Result, D>, ArrayBase> + where + D: Dimension, + { + match Arc::try_unwrap(self_.data.0) { + Ok(owned_data) => unsafe { + // Safe because the data is equivalent. + Ok(ArrayBase::from_data_ptr(owned_data, self_.ptr) + .with_strides_dim(self_.strides, self_.dim)) + }, + Err(arc_data) => unsafe { + // Safe because the data is equivalent; we're just + // reconstructing `self_`. + Err(ArrayBase::from_data_ptr(OwnedArcRepr(arc_data), self_.ptr) + .with_strides_dim(self_.strides, self_.dim)) + }, + } + } + #[allow(clippy::wrong_self_convention)] fn to_shared(self_: &ArrayBase) -> ArrayBase, D> where @@ -337,6 +367,16 @@ unsafe impl Data for OwnedRepr { { self_ } + + #[inline] + fn try_into_owned_nocopy( + self_: ArrayBase, + ) -> Result, ArrayBase> + where + D: Dimension, + { + Ok(self_) + } } unsafe impl DataMut for OwnedRepr {} @@ -393,6 +433,15 @@ unsafe impl<'a, A> Data for ViewRepr<&'a A> { { self_.to_owned() } + + fn try_into_owned_nocopy( + self_: ArrayBase, + ) -> Result, D>, ArrayBase> + where + D: Dimension, + { + Err(self_) + } } unsafe impl<'a, A> RawDataClone for ViewRepr<&'a A> { @@ -438,6 +487,15 @@ unsafe impl<'a, A> Data for ViewRepr<&'a mut A> { { self_.to_owned() } + + fn try_into_owned_nocopy( + self_: ArrayBase, + ) -> Result, D>, ArrayBase> + where + D: Dimension, + { + Err(self_) + } } unsafe impl<'a, A> DataMut for ViewRepr<&'a mut A> {} @@ -609,6 +667,22 @@ unsafe impl<'a, A> Data for CowRepr<'a, A> { }, } } + + fn try_into_owned_nocopy( + self_: ArrayBase, + ) -> Result, D>, ArrayBase> + where + D: Dimension, + { + match self_.data { + CowRepr::View(_) => Err(self_), + CowRepr::Owned(data) => unsafe { + // safe because the data is equivalent so ptr, dims remain valid + Ok(ArrayBase::from_data_ptr(data, self_.ptr) + .with_strides_dim(self_.strides, self_.dim)) + }, + } + } } unsafe impl<'a, A> DataMut for CowRepr<'a, A> where A: Clone {} diff --git a/src/impl_methods.rs b/src/impl_methods.rs index cdba17918..745a8e60b 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -241,6 +241,34 @@ where S::into_owned(self) } + /// Converts the array into `Array` if this is possible without + /// cloning the array elements. Otherwise, returns `self` unchanged. + /// + /// ``` + /// use ndarray::{array, rcarr2, ArcArray2, Array2}; + /// + /// // Reference-counted, clone-on-write `ArcArray`. + /// let a: ArcArray2<_> = rcarr2(&[[1., 2.], [3., 4.]]); + /// { + /// // Another reference to the same data. + /// let b: ArcArray2<_> = a.clone(); + /// // Since there are two references to the same data, `.into_owned()` + /// // would require cloning the data, so `.try_into_owned_nocopy()` + /// // returns `Err`. + /// assert!(b.try_into_owned_nocopy().is_err()); + /// } + /// // Here, since the second reference has been dropped, the `ArcArray` + /// // can be converted into an `Array` without cloning the data. + /// let unique: Array2<_> = a.try_into_owned_nocopy().unwrap(); + /// assert_eq!(unique, array![[1., 2.], [3., 4.]]); + /// ``` + pub fn try_into_owned_nocopy(self) -> Result, Self> + where + S: Data, + { + S::try_into_owned_nocopy(self) + } + /// Turn the array into a shared ownership (copy on write) array, /// without any copying. pub fn into_shared(self) -> ArcArray From daaf6258f6a405d9cf9af31e30976b2b016f82bb Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 1 Jun 2021 19:54:24 -0400 Subject: [PATCH 402/651] Use type aliases to simplify type signatures Clippy warned that some of the types were too complex, so this commit simplifies those lines and a few others. --- src/data_traits.rs | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index 103d864b9..b117792c4 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -16,7 +16,9 @@ use std::ptr::NonNull; use alloc::sync::Arc; use alloc::vec::Vec; -use crate::{ArrayBase, CowRepr, Dimension, OwnedArcRepr, OwnedRepr, RawViewRepr, ViewRepr}; +use crate::{ + ArcArray, Array, ArrayBase, CowRepr, Dimension, OwnedArcRepr, OwnedRepr, RawViewRepr, ViewRepr, +}; /// Array representation trait. /// @@ -104,7 +106,7 @@ pub unsafe trait Data: RawData { /// Converts the array to a uniquely owned array, cloning elements if necessary. #[doc(hidden)] #[allow(clippy::wrong_self_convention)] - fn into_owned(self_: ArrayBase) -> ArrayBase, D> + fn into_owned(self_: ArrayBase) -> Array where Self::Elem: Clone, D: Dimension; @@ -114,7 +116,7 @@ pub unsafe trait Data: RawData { #[doc(hidden)] fn try_into_owned_nocopy( self_: ArrayBase, - ) -> Result, D>, ArrayBase> + ) -> Result, ArrayBase> where D: Dimension; @@ -122,7 +124,7 @@ pub unsafe trait Data: RawData { /// cloning elements if necessary. #[doc(hidden)] #[allow(clippy::wrong_self_convention)] - fn to_shared(self_: &ArrayBase) -> ArrayBase, D> + fn to_shared(self_: &ArrayBase) -> ArcArray where Self::Elem: Clone, D: Dimension, @@ -271,7 +273,7 @@ where } unsafe impl Data for OwnedArcRepr { - fn into_owned(mut self_: ArrayBase) -> ArrayBase, D> + fn into_owned(mut self_: ArrayBase) -> Array where A: Clone, D: Dimension, @@ -287,7 +289,7 @@ unsafe impl Data for OwnedArcRepr { fn try_into_owned_nocopy( self_: ArrayBase, - ) -> Result, D>, ArrayBase> + ) -> Result, ArrayBase> where D: Dimension, { @@ -307,7 +309,7 @@ unsafe impl Data for OwnedArcRepr { } #[allow(clippy::wrong_self_convention)] - fn to_shared(self_: &ArrayBase) -> ArrayBase, D> + fn to_shared(self_: &ArrayBase) -> ArcArray where Self::Elem: Clone, D: Dimension, @@ -360,7 +362,7 @@ unsafe impl RawDataMut for OwnedRepr { unsafe impl Data for OwnedRepr { #[inline] - fn into_owned(self_: ArrayBase) -> ArrayBase, D> + fn into_owned(self_: ArrayBase) -> Array where A: Clone, D: Dimension, @@ -371,7 +373,7 @@ unsafe impl Data for OwnedRepr { #[inline] fn try_into_owned_nocopy( self_: ArrayBase, - ) -> Result, ArrayBase> + ) -> Result, ArrayBase> where D: Dimension, { @@ -426,7 +428,7 @@ unsafe impl<'a, A> RawData for ViewRepr<&'a A> { } unsafe impl<'a, A> Data for ViewRepr<&'a A> { - fn into_owned(self_: ArrayBase) -> ArrayBase, D> + fn into_owned(self_: ArrayBase) -> Array where Self::Elem: Clone, D: Dimension, @@ -436,7 +438,7 @@ unsafe impl<'a, A> Data for ViewRepr<&'a A> { fn try_into_owned_nocopy( self_: ArrayBase, - ) -> Result, D>, ArrayBase> + ) -> Result, ArrayBase> where D: Dimension, { @@ -480,7 +482,7 @@ unsafe impl<'a, A> RawDataMut for ViewRepr<&'a mut A> { } unsafe impl<'a, A> Data for ViewRepr<&'a mut A> { - fn into_owned(self_: ArrayBase) -> ArrayBase, D> + fn into_owned(self_: ArrayBase) -> Array where Self::Elem: Clone, D: Dimension, @@ -490,7 +492,7 @@ unsafe impl<'a, A> Data for ViewRepr<&'a mut A> { fn try_into_owned_nocopy( self_: ArrayBase, - ) -> Result, D>, ArrayBase> + ) -> Result, ArrayBase> where D: Dimension, { @@ -653,7 +655,7 @@ where unsafe impl<'a, A> Data for CowRepr<'a, A> { #[inline] - fn into_owned(self_: ArrayBase, D>) -> ArrayBase, D> + fn into_owned(self_: ArrayBase, D>) -> Array where A: Clone, D: Dimension, @@ -670,7 +672,7 @@ unsafe impl<'a, A> Data for CowRepr<'a, A> { fn try_into_owned_nocopy( self_: ArrayBase, - ) -> Result, D>, ArrayBase> + ) -> Result, ArrayBase> where D: Dimension, { From 4a9752598645881450b2ab40677548328a8203d4 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Fri, 18 Jun 2021 12:24:27 -0400 Subject: [PATCH 403/651] Implement From<&'a Slice> for CowArray<'a, A, Ix1> --- src/impl_cow.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/impl_cow.rs b/src/impl_cow.rs index 5cd00077e..b3f560bfe 100644 --- a/src/impl_cow.rs +++ b/src/impl_cow.rs @@ -51,3 +51,23 @@ where } } } + +impl<'a, A, Slice: ?Sized> From<&'a Slice> for CowArray<'a, A, Ix1> +where + Slice: AsRef<[A]>, +{ + /// Create a one-dimensional clone-on-write view of the data in `slice`. + /// + /// **Panics** if the slice length is greater than [`isize::MAX`]. + /// + /// ``` + /// use ndarray::{array, CowArray}; + /// + /// let array = CowArray::from(&[1., 2., 3., 4.]); + /// assert!(array.is_view()); + /// assert_eq!(array, array![1., 2., 3., 4.]); + /// ``` + fn from(slice: &'a Slice) -> Self { + Self::from(ArrayView1::from(slice)) + } +} From c5e3ba8b389fe2df7d1cc4ad1bf3e1f5dd8dbbf9 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Fri, 18 Jun 2021 12:39:00 -0400 Subject: [PATCH 404/651] Implement From<&'a ArrayBase> for CowArray --- src/impl_cow.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/impl_cow.rs b/src/impl_cow.rs index b3f560bfe..22d5c78b2 100644 --- a/src/impl_cow.rs +++ b/src/impl_cow.rs @@ -71,3 +71,14 @@ where Self::from(ArrayView1::from(slice)) } } + +impl<'a, A, S, D> From<&'a ArrayBase> for CowArray<'a, A, D> +where + S: Data, + D: Dimension, +{ + /// Create a read-only clone-on-write view of the array. + fn from(array: &'a ArrayBase) -> Self { + Self::from(array.view()) + } +} From 533466f0d9ff4db36b6858bc00b803471da551a1 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Sun, 7 Nov 2021 05:48:34 -0800 Subject: [PATCH 405/651] Add test that fails on negative stride --- tests/array.rs | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/tests/array.rs b/tests/array.rs index d702b221e..16d901568 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -2516,13 +2516,13 @@ fn test_split_re_im_view() { #[test] fn test_split_re_im_view_roundtrip() { - let a_re = Array3::from_shape_fn((3,4,5), |(i, j, _k)| { + let a_re = Array3::from_shape_fn((3,1,5), |(i, j, _k)| { i * j }); - let a_im = Array3::from_shape_fn((3,4,5), |(_i, _j, k)| { + let a_im = Array3::from_shape_fn((3,1,5), |(_i, _j, k)| { k }); - let a = Array3::from_shape_fn((3,4,5), |(i,j,k)| { + let a = Array3::from_shape_fn((3,1,5), |(i,j,k)| { Complex::new(a_re[[i,j,k]], a_im[[i,j,k]]) }); let Complex { re, im } = a.view().split_re_im(); @@ -2540,3 +2540,34 @@ fn test_split_re_im_view_mut() { assert_eq!(im.sum(), 0); assert_eq!(a, eye_complex); } + +#[test] +fn test_split_re_im_zerod() { + let mut a = Array0::from_elem((), Complex::new(42, 32)); + let Complex { re, im } = a.view().split_re_im(); + assert_eq!(re.get(()), Some(&42)); + assert_eq!(im.get(()), Some(&32)); + let cmplx = a.view_mut().split_re_im(); + cmplx.re.assign_to(cmplx.im); + assert_eq!(a.get(()).unwrap().im, 42); +} + +#[test] +fn test_split_re_im_permuted() { + let a = Array3::from_shape_fn((3, 4, 5), |(i, j, k)| { + Complex::new(i * k + j, k) + }); + let permuted = a.view().permuted_axes([1,0,2]); + let Complex { re, im } = permuted.split_re_im(); + assert_eq!(re.get((3,2,4)).unwrap(), &11); + assert_eq!(im.get((3,2,4)).unwrap(), &4); +} + +#[test] +fn test_split_re_im_invert_axis() { + let mut a = Array::from_shape_fn((2, 3, 2), |(i, j, k)| Complex::new(i as f64 + j as f64, i as f64 + k as f64)); + a.invert_axis(Axis(1)); + let cmplx = a.view().split_re_im(); + assert_eq!(cmplx.re, a.mapv(|z| z.re)); + assert_eq!(cmplx.im, a.mapv(|z| z.im)); +} From 6937be5b699b1c0469db8f847a6d491fac398ee4 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Sun, 7 Nov 2021 05:49:07 -0800 Subject: [PATCH 406/651] Use isize math for stride doubling --- src/impl_raw_views.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impl_raw_views.rs b/src/impl_raw_views.rs index ef3df735a..425af96c2 100644 --- a/src/impl_raw_views.rs +++ b/src/impl_raw_views.rs @@ -173,7 +173,7 @@ where if mem::size_of::() != 0 { for ax in 0..strides.ndim() { if dim[ax] > 1 { - strides[ax] *= 2; + strides[ax] = (strides[ax] as isize * 2) as usize; } } } From 228f853e6f312ca6c5c5e04b962f1e2fcf2eb6e9 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Mon, 8 Nov 2021 20:07:32 -0800 Subject: [PATCH 407/651] Change return type of kron Co-authored-by: bluss --- src/linalg/impl_linalg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index b710138df..df6e02a2a 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -706,7 +706,7 @@ unsafe fn general_mat_vec_mul_impl( /// /// The kronecker product of a LxN matrix A and a MxR matrix B is a (L*M)x(N*R) /// matrix K formed by the block multiplication A_ij * B. -pub fn kron<'a, A, S1, S2>(a: &ArrayBase, b: &'a ArrayBase) -> ArrayBase, Ix2> +pub fn kron<'a, A, S1, S2>(a: &ArrayBase, b: &'a ArrayBase) -> Array where S1: Data, S2: Data, From 455d5e01ddd96774acb0d8a0dac4bf8939608617 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Mon, 8 Nov 2021 20:07:53 -0800 Subject: [PATCH 408/651] Change return type of Mul output Co-authored-by: bluss --- src/linalg/impl_linalg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index df6e02a2a..483ccef84 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -711,7 +711,7 @@ where S1: Data, S2: Data, A: LinalgScalar, - A: std::ops::Mul<&'a ArrayBase, Output = ArrayBase, Ix2>>, + A: std::ops::Mul<&'a ArrayBase, Output = Array>, { let dimar = a.shape()[0]; let dimac = a.shape()[1]; From 7733b94724299c69b37f065a5fe7d03330dcee65 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Mon, 8 Nov 2021 20:52:53 -0800 Subject: [PATCH 409/651] Check multiplication when making out array in kron --- src/linalg/impl_linalg.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index 483ccef84..e18026b07 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -6,7 +6,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::OwnedRepr; use crate::imp_prelude::*; use crate::numeric_util; #[cfg(feature = "blas")] @@ -717,16 +716,22 @@ where let dimac = a.shape()[1]; let dimbr = b.shape()[0]; let dimbc = b.shape()[1]; - let mut out: Array2> = Array2::uninit((dimar * dimbr, dimac * dimbc)); + let mut out: Array2> = Array2::uninit(( + dimar + .checked_mul(dimbr) + .expect("Dimensions of kronecker product output array overflows usize."), + dimac + .checked_mul(dimbc) + .expect("Dimensions of kronecker product output array overflows usize."), + )); Zip::from(out.exact_chunks_mut((dimbr, dimbc))) .and(a) - .for_each(|out, a| { - (*a * b).assign_to(out); + .for_each(|out, &a| { + (a * b).assign_to(out); }); unsafe { out.assume_init() } } - #[inline(always)] /// Return `true` if `A` and `B` are the same type fn same_type() -> bool { From 6d7dd683f519c550a501bfdd8cb9a6e0be6d2b43 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Tue, 9 Nov 2021 00:35:19 -0800 Subject: [PATCH 410/651] Use nested Zip to not create temporary array Co-authored-by: Adam Reichold --- src/linalg/impl_linalg.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index e18026b07..918261305 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -727,7 +727,9 @@ where Zip::from(out.exact_chunks_mut((dimbr, dimbc))) .and(a) .for_each(|out, &a| { - (a * b).assign_to(out); + Zip::from(out).and(b).for_each(|out, &b| { + *out = MaybeUninit::new(a * b); + }) }); unsafe { out.assume_init() } } From a07778efce0016885dcc4405fa78a097b15d6292 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Tue, 9 Nov 2021 15:15:41 -0800 Subject: [PATCH 411/651] Remove bound that is no longer needed Co-authored-by: bluss --- src/linalg/impl_linalg.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index 918261305..68815f021 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -710,7 +710,6 @@ where S1: Data, S2: Data, A: LinalgScalar, - A: std::ops::Mul<&'a ArrayBase, Output = Array>, { let dimar = a.shape()[0]; let dimac = a.shape()[1]; From 7d6fd72fb986754ca0c12b8897285727027d001c Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Tue, 9 Nov 2021 22:41:52 -0800 Subject: [PATCH 412/651] Fix clippy --- src/linalg/impl_linalg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index 68815f021..fe87567f0 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -705,7 +705,7 @@ unsafe fn general_mat_vec_mul_impl( /// /// The kronecker product of a LxN matrix A and a MxR matrix B is a (L*M)x(N*R) /// matrix K formed by the block multiplication A_ij * B. -pub fn kron<'a, A, S1, S2>(a: &ArrayBase, b: &'a ArrayBase) -> Array +pub fn kron(a: &ArrayBase, b: &ArrayBase) -> Array where S1: Data, S2: Data, From 1175edbc4a458cdfe9ab2db5891a10bf09cd5459 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Sat, 6 Nov 2021 00:53:05 -0700 Subject: [PATCH 413/651] Add complex GEMM and simple test --- src/linalg/impl_linalg.rs | 6 ++++++ xtest-blas/Cargo.toml | 1 + xtest-blas/tests/oper.rs | 41 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index fe87567f0..10f258a6f 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -29,6 +29,9 @@ use cblas_sys as blas_sys; #[cfg(feature = "blas")] use cblas_sys::{CblasNoTrans, CblasRowMajor, CblasTrans, CBLAS_LAYOUT}; +#[cfg(feature = "blas")] +use num_complex::{Complex32 as c32, Complex64 as c64}; + /// len of vector before we use blas #[cfg(feature = "blas")] const DOT_BLAS_CUTOFF: usize = 32; @@ -374,6 +377,7 @@ fn mat_mul_impl( ) where A: LinalgScalar, { + // size cutoff for using BLAS let cut = GEMM_BLAS_CUTOFF; let ((mut m, a), (_, mut n)) = (lhs.dim(), rhs.dim()); @@ -455,6 +459,8 @@ fn mat_mul_impl( } gemm!(f32, cblas_sgemm); gemm!(f64, cblas_dgemm); + gemm!(c32, cblas_cgemm); + gemm!(c64, cblas_zgemm); } mat_mul_general(alpha, lhs, rhs, beta, c) } diff --git a/xtest-blas/Cargo.toml b/xtest-blas/Cargo.toml index 463be6863..aee8c3ce4 100644 --- a/xtest-blas/Cargo.toml +++ b/xtest-blas/Cargo.toml @@ -11,6 +11,7 @@ test = false approx = "0.4" defmac = "0.2" num-traits = "0.2" +num-complex = { version = "0.4", default-features = false } [dependencies] ndarray = { path = "../", features = ["approx", "blas"] } diff --git a/xtest-blas/tests/oper.rs b/xtest-blas/tests/oper.rs index 95ab4ebb2..e0d0dcd81 100644 --- a/xtest-blas/tests/oper.rs +++ b/xtest-blas/tests/oper.rs @@ -3,6 +3,7 @@ extern crate defmac; extern crate ndarray; extern crate num_traits; extern crate blas_src; +extern crate num_complex; use ndarray::prelude::*; @@ -12,6 +13,8 @@ use ndarray::{Data, Ix, LinalgScalar}; use approx::assert_relative_eq; use defmac::defmac; +use num_complex::Complex32; +use num_complex::Complex64; #[test] fn mat_vec_product_1d() { @@ -52,6 +55,20 @@ fn range_mat64(m: Ix, n: Ix) -> Array2 { .unwrap() } +fn range_mat_complex(m: Ix, n: Ix) -> Array2 { + Array::linspace(0., (m * n) as f32 - 1., m * n) + .into_shape((m, n)) + .unwrap() + .map_mut(|&mut f| Complex32::new(f, 0.)) +} + +fn range_mat_complex64(m: Ix, n: Ix) -> Array2 { + Array::linspace(0., (m * n) as f64 - 1., m * n) + .into_shape((m, n)) + .unwrap() + .map_mut(|&mut f| Complex64::new(f, 0.)) +} + fn range1_mat64(m: Ix) -> Array1 { Array::linspace(0., m as f64 - 1., m) } @@ -250,6 +267,30 @@ fn gemm_64_1_f() { assert_relative_eq!(y, answer, epsilon = 1e-12, max_relative = 1e-7); } +#[test] +fn gemm_c64_1_f() { + let a = range_mat_complex64(64, 64).reversed_axes(); + let (m, n) = a.dim(); + // m x n times n x 1 == m x 1 + let x = range_mat_complex64(n, 1); + let mut y = range_mat_complex64(m, 1); + let answer = reference_mat_mul(&a, &x) + &y; + general_mat_mul(Complex64::new(1.0, 0.), &a, &x, Complex64::new(1.0, 0.), &mut y); + assert_relative_eq!(y.mapv(|i| i.norm_sqr()), answer.mapv(|i| i.norm_sqr()), epsilon = 1e-12, max_relative = 1e-7); +} + +#[test] +fn gemm_c32_1_f() { + let a = range_mat_complex(64, 64).reversed_axes(); + let (m, n) = a.dim(); + // m x n times n x 1 == m x 1 + let x = range_mat_complex(n, 1); + let mut y = range_mat_complex(m, 1); + let answer = reference_mat_mul(&a, &x) + &y; + general_mat_mul(Complex32::new(1.0, 0.), &a, &x, Complex32::new(1.0, 0.), &mut y); + assert_relative_eq!(y.mapv(|i| i.norm_sqr()), answer.mapv(|i| i.norm_sqr()), epsilon = 1e-12, max_relative = 1e-7); +} + #[test] fn gen_mat_vec_mul() { let alpha = -2.3; From 67ec3ade42eb17b74f66e2c080298ff818540d3b Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Sat, 6 Nov 2021 01:09:19 -0700 Subject: [PATCH 414/651] Actually use blas with complex numbers --- src/linalg/impl_linalg.rs | 66 ++++++++++++++++++++++++++++++++++----- xtest-blas/tests/oper.rs | 34 ++++++++++++++++---- 2 files changed, 86 insertions(+), 14 deletions(-) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index 10f258a6f..c9346ca04 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -7,9 +7,10 @@ // except according to those terms. use crate::imp_prelude::*; -use crate::numeric_util; + #[cfg(feature = "blas")] use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr; +use crate::numeric_util; use crate::{LinalgScalar, Zip}; @@ -17,12 +18,12 @@ use std::any::TypeId; use std::mem::MaybeUninit; use alloc::vec::Vec; +#[cfg(feature = "blas")] +use libc::c_int; #[cfg(feature = "blas")] use std::cmp; #[cfg(feature = "blas")] use std::mem::swap; -#[cfg(feature = "blas")] -use libc::c_int; #[cfg(feature = "blas")] use cblas_sys as blas_sys; @@ -377,11 +378,15 @@ fn mat_mul_impl( ) where A: LinalgScalar, { - // size cutoff for using BLAS let cut = GEMM_BLAS_CUTOFF; let ((mut m, a), (_, mut n)) = (lhs.dim(), rhs.dim()); - if !(m > cut || n > cut || a > cut) || !(same_type::() || same_type::()) { + if !(m > cut || n > cut || a > cut) + || !(same_type::() + || same_type::() + || same_type::() + || same_type::()) + { return mat_mul_general(alpha, lhs, rhs, beta, c); } { @@ -459,6 +464,53 @@ fn mat_mul_impl( } gemm!(f32, cblas_sgemm); gemm!(f64, cblas_dgemm); + + macro_rules! gemm { + ($ty:ty, $gemm:ident) => { + if blas_row_major_2d::<$ty, _>(&lhs_) + && blas_row_major_2d::<$ty, _>(&rhs_) + && blas_row_major_2d::<$ty, _>(&c_) + { + let (m, k) = match lhs_trans { + CblasNoTrans => lhs_.dim(), + _ => { + let (rows, cols) = lhs_.dim(); + (cols, rows) + } + }; + let n = match rhs_trans { + CblasNoTrans => rhs_.raw_dim()[1], + _ => rhs_.raw_dim()[0], + }; + // adjust strides, these may [1, 1] for column matrices + let lhs_stride = cmp::max(lhs_.strides()[0] as blas_index, k as blas_index); + let rhs_stride = cmp::max(rhs_.strides()[0] as blas_index, n as blas_index); + let c_stride = cmp::max(c_.strides()[0] as blas_index, n as blas_index); + + // gemm is C ← αA^Op B^Op + βC + // Where Op is notrans/trans/conjtrans + unsafe { + blas_sys::$gemm( + CblasRowMajor, + lhs_trans, + rhs_trans, + m as blas_index, // m, rows of Op(a) + n as blas_index, // n, cols of Op(b) + k as blas_index, // k, cols of Op(a) + &alpha as *const A as *const _, // alpha + lhs_.ptr.as_ptr() as *const _, // a + lhs_stride, // lda + rhs_.ptr.as_ptr() as *const _, // b + rhs_stride, // ldb + &beta as *const A as *const _, // beta + c_.ptr.as_ptr() as *mut _, // c + c_stride, // ldc + ); + } + return; + } + }; + } gemm!(c32, cblas_cgemm); gemm!(c64, cblas_zgemm); } @@ -609,9 +661,7 @@ pub fn general_mat_vec_mul( S3: DataMut, A: LinalgScalar, { - unsafe { - general_mat_vec_mul_impl(alpha, a, x, beta, y.raw_view_mut()) - } + unsafe { general_mat_vec_mul_impl(alpha, a, x, beta, y.raw_view_mut()) } } /// General matrix-vector multiplication diff --git a/xtest-blas/tests/oper.rs b/xtest-blas/tests/oper.rs index e0d0dcd81..c3e84b445 100644 --- a/xtest-blas/tests/oper.rs +++ b/xtest-blas/tests/oper.rs @@ -1,9 +1,9 @@ extern crate approx; +extern crate blas_src; extern crate defmac; extern crate ndarray; -extern crate num_traits; -extern crate blas_src; extern crate num_complex; +extern crate num_traits; use ndarray::prelude::*; @@ -275,8 +275,19 @@ fn gemm_c64_1_f() { let x = range_mat_complex64(n, 1); let mut y = range_mat_complex64(m, 1); let answer = reference_mat_mul(&a, &x) + &y; - general_mat_mul(Complex64::new(1.0, 0.), &a, &x, Complex64::new(1.0, 0.), &mut y); - assert_relative_eq!(y.mapv(|i| i.norm_sqr()), answer.mapv(|i| i.norm_sqr()), epsilon = 1e-12, max_relative = 1e-7); + general_mat_mul( + Complex64::new(1.0, 0.), + &a, + &x, + Complex64::new(1.0, 0.), + &mut y, + ); + assert_relative_eq!( + y.mapv(|i| i.norm_sqr()), + answer.mapv(|i| i.norm_sqr()), + epsilon = 1e-12, + max_relative = 1e-7 + ); } #[test] @@ -287,8 +298,19 @@ fn gemm_c32_1_f() { let x = range_mat_complex(n, 1); let mut y = range_mat_complex(m, 1); let answer = reference_mat_mul(&a, &x) + &y; - general_mat_mul(Complex32::new(1.0, 0.), &a, &x, Complex32::new(1.0, 0.), &mut y); - assert_relative_eq!(y.mapv(|i| i.norm_sqr()), answer.mapv(|i| i.norm_sqr()), epsilon = 1e-12, max_relative = 1e-7); + general_mat_mul( + Complex32::new(1.0, 0.), + &a, + &x, + Complex32::new(1.0, 0.), + &mut y, + ); + assert_relative_eq!( + y.mapv(|i| i.norm_sqr()), + answer.mapv(|i| i.norm_sqr()), + epsilon = 1e-12, + max_relative = 1e-7 + ); } #[test] From 8712bac5379a72966c95c2e2a70c5bbef5753c02 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Sat, 6 Nov 2021 21:28:02 -0700 Subject: [PATCH 415/651] Merge complex and real gemm calls --- src/linalg/impl_linalg.rs | 69 +++++++++++---------------------------- 1 file changed, 19 insertions(+), 50 deletions(-) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index c9346ca04..eeccfbc01 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -416,8 +416,23 @@ fn mat_mul_impl( rhs_trans = CblasTrans; } + macro_rules! cast_ty { + (f32, $var:ident) => { + cast_as(&$var) + }; + (f64, $var:ident) => { + cast_as(&$var) + }; + (c32, $var:ident) => { + &$var as *const A as *const _ + }; + (c64, $var:ident) => { + &$var as *const A as *const _ + }; + } + macro_rules! gemm { - ($ty:ty, $gemm:ident) => { + ($ty:tt, $gemm:ident) => { if blas_row_major_2d::<$ty, _>(&lhs_) && blas_row_major_2d::<$ty, _>(&rhs_) && blas_row_major_2d::<$ty, _>(&c_) @@ -437,9 +452,9 @@ fn mat_mul_impl( let lhs_stride = cmp::max(lhs_.strides()[0] as blas_index, k as blas_index); let rhs_stride = cmp::max(rhs_.strides()[0] as blas_index, n as blas_index); let c_stride = cmp::max(c_.strides()[0] as blas_index, n as blas_index); - // gemm is C ← αA^Op B^Op + βC // Where Op is notrans/trans/conjtrans + unsafe { blas_sys::$gemm( CblasRowMajor, @@ -448,12 +463,12 @@ fn mat_mul_impl( m as blas_index, // m, rows of Op(a) n as blas_index, // n, cols of Op(b) k as blas_index, // k, cols of Op(a) - cast_as(&alpha), // alpha + cast_ty!($ty, alpha), // alpha lhs_.ptr.as_ptr() as *const _, // a lhs_stride, // lda rhs_.ptr.as_ptr() as *const _, // b rhs_stride, // ldb - cast_as(&beta), // beta + cast_ty!($ty, beta), // beta c_.ptr.as_ptr() as *mut _, // c c_stride, // ldc ); @@ -465,52 +480,6 @@ fn mat_mul_impl( gemm!(f32, cblas_sgemm); gemm!(f64, cblas_dgemm); - macro_rules! gemm { - ($ty:ty, $gemm:ident) => { - if blas_row_major_2d::<$ty, _>(&lhs_) - && blas_row_major_2d::<$ty, _>(&rhs_) - && blas_row_major_2d::<$ty, _>(&c_) - { - let (m, k) = match lhs_trans { - CblasNoTrans => lhs_.dim(), - _ => { - let (rows, cols) = lhs_.dim(); - (cols, rows) - } - }; - let n = match rhs_trans { - CblasNoTrans => rhs_.raw_dim()[1], - _ => rhs_.raw_dim()[0], - }; - // adjust strides, these may [1, 1] for column matrices - let lhs_stride = cmp::max(lhs_.strides()[0] as blas_index, k as blas_index); - let rhs_stride = cmp::max(rhs_.strides()[0] as blas_index, n as blas_index); - let c_stride = cmp::max(c_.strides()[0] as blas_index, n as blas_index); - - // gemm is C ← αA^Op B^Op + βC - // Where Op is notrans/trans/conjtrans - unsafe { - blas_sys::$gemm( - CblasRowMajor, - lhs_trans, - rhs_trans, - m as blas_index, // m, rows of Op(a) - n as blas_index, // n, cols of Op(b) - k as blas_index, // k, cols of Op(a) - &alpha as *const A as *const _, // alpha - lhs_.ptr.as_ptr() as *const _, // a - lhs_stride, // lda - rhs_.ptr.as_ptr() as *const _, // b - rhs_stride, // ldb - &beta as *const A as *const _, // beta - c_.ptr.as_ptr() as *mut _, // c - c_stride, // ldc - ); - } - return; - } - }; - } gemm!(c32, cblas_cgemm); gemm!(c64, cblas_zgemm); } From 67cbfe18758a7eb2165ed182a38862fb1cacb677 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Mon, 8 Nov 2021 20:54:54 -0800 Subject: [PATCH 416/651] Small realignment of comment --- src/linalg/impl_linalg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index eeccfbc01..d6c90b966 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -452,9 +452,9 @@ fn mat_mul_impl( let lhs_stride = cmp::max(lhs_.strides()[0] as blas_index, k as blas_index); let rhs_stride = cmp::max(rhs_.strides()[0] as blas_index, n as blas_index); let c_stride = cmp::max(c_.strides()[0] as blas_index, n as blas_index); + // gemm is C ← αA^Op B^Op + βC // Where Op is notrans/trans/conjtrans - unsafe { blas_sys::$gemm( CblasRowMajor, From 7bf4b62751138811b8850c41871f80fae014d014 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Mon, 8 Nov 2021 20:55:56 -0800 Subject: [PATCH 417/651] Don't make map mutable --- xtest-blas/tests/oper.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xtest-blas/tests/oper.rs b/xtest-blas/tests/oper.rs index c3e84b445..7ebe6822e 100644 --- a/xtest-blas/tests/oper.rs +++ b/xtest-blas/tests/oper.rs @@ -59,14 +59,14 @@ fn range_mat_complex(m: Ix, n: Ix) -> Array2 { Array::linspace(0., (m * n) as f32 - 1., m * n) .into_shape((m, n)) .unwrap() - .map_mut(|&mut f| Complex32::new(f, 0.)) + .map(|&f| Complex32::new(f, 0.)) } fn range_mat_complex64(m: Ix, n: Ix) -> Array2 { Array::linspace(0., (m * n) as f64 - 1., m * n) .into_shape((m, n)) .unwrap() - .map_mut(|&mut f| Complex64::new(f, 0.)) + .map(|&f| Complex64::new(f, 0.)) } fn range1_mat64(m: Ix) -> Array1 { From 84cc03837158956465421d49af87bbdacfb809d0 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Tue, 9 Nov 2021 15:13:49 -0800 Subject: [PATCH 418/651] Add complex matmul test --- src/linalg/impl_linalg.rs | 24 ++++++++++++------------ xtest-blas/tests/oper.rs | 25 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index d6c90b966..0e6700d65 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -416,7 +416,7 @@ fn mat_mul_impl( rhs_trans = CblasTrans; } - macro_rules! cast_ty { + macro_rules! gemm_scalar_cast { (f32, $var:ident) => { cast_as(&$var) }; @@ -460,17 +460,17 @@ fn mat_mul_impl( CblasRowMajor, lhs_trans, rhs_trans, - m as blas_index, // m, rows of Op(a) - n as blas_index, // n, cols of Op(b) - k as blas_index, // k, cols of Op(a) - cast_ty!($ty, alpha), // alpha - lhs_.ptr.as_ptr() as *const _, // a - lhs_stride, // lda - rhs_.ptr.as_ptr() as *const _, // b - rhs_stride, // ldb - cast_ty!($ty, beta), // beta - c_.ptr.as_ptr() as *mut _, // c - c_stride, // ldc + m as blas_index, // m, rows of Op(a) + n as blas_index, // n, cols of Op(b) + k as blas_index, // k, cols of Op(a) + gemm_scalar_cast!($ty, alpha), // alpha + lhs_.ptr.as_ptr() as *const _, // a + lhs_stride, // lda + rhs_.ptr.as_ptr() as *const _, // b + rhs_stride, // ldb + gemm_scalar_cast!($ty, beta), // beta + c_.ptr.as_ptr() as *mut _, // c + c_stride, // ldc ); } return; diff --git a/xtest-blas/tests/oper.rs b/xtest-blas/tests/oper.rs index 7ebe6822e..120626d0b 100644 --- a/xtest-blas/tests/oper.rs +++ b/xtest-blas/tests/oper.rs @@ -313,6 +313,31 @@ fn gemm_c32_1_f() { ); } +#[test] +fn gemm_c64_actually_complex() { + let mut a = range_mat_complex64(4,4); + a = a.map(|&i| if i.re > 8. { i.conj() } else { i }); + let mut b = range_mat_complex64(4,6); + b = b.map(|&i| if i.re > 4. { i.conj() } else {i}); + let mut y = range_mat_complex64(4,6); + let alpha = Complex64::new(0., 1.0); + let beta = Complex64::new(1.0, 1.0); + let answer = alpha * reference_mat_mul(&a, &b) + beta * &y; + general_mat_mul( + alpha.clone(), + &a, + &b, + beta.clone(), + &mut y, + ); + assert_relative_eq!( + y.mapv(|i| i.norm_sqr()), + answer.mapv(|i| i.norm_sqr()), + epsilon = 1e-12, + max_relative = 1e-7 + ); +} + #[test] fn gen_mat_vec_mul() { let alpha = -2.3; From a2b1f18532d3bee791d19dc0227e554d5b77a496 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 17 Nov 2021 19:19:18 +0100 Subject: [PATCH 419/651] Update to quickcheck 1.0 New in 1.0 is that integers now generate values in their whole range. That creates a different situation for the tests taking isize than before. Shrink their accepted values for now, and leave as fixmes to investigate and improve. In some cases, the tests are slow to execute. In other cases, the tests fail with large values (probably overflow), and following that, the case shrinking is extremely slow. --- Cargo.toml | 2 +- ndarray-rand/Cargo.toml | 4 ++-- ndarray-rand/src/lib.rs | 2 +- ndarray-rand/tests/tests.rs | 29 +++++++++++++++++++---------- src/dimension/mod.rs | 28 ++++++++++++++++++++++------ 5 files changed, 45 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 75875d619..44dd52fb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ rawpointer = { version = "0.2" } [dev-dependencies] defmac = "0.2" -quickcheck = { version = "0.9", default-features = false } +quickcheck = { version = "1.0", default-features = false } approx = "0.4" itertools = { version = "0.10.0", default-features = false, features = ["use_std"] } diff --git a/ndarray-rand/Cargo.toml b/ndarray-rand/Cargo.toml index 4127d5631..4e9bf265f 100644 --- a/ndarray-rand/Cargo.toml +++ b/ndarray-rand/Cargo.toml @@ -16,7 +16,7 @@ keywords = ["multidimensional", "matrix", "rand", "ndarray"] [dependencies] ndarray = { version = "0.15", path = ".." } rand_distr = "0.4.0" -quickcheck = { version = "0.9", default-features = false, optional = true } +quickcheck = { version = "1.0", default-features = false, optional = true } [dependencies.rand] version = "0.8.0" @@ -24,7 +24,7 @@ features = ["small_rng"] [dev-dependencies] rand_isaac = "0.3.0" -quickcheck = { version = "0.9", default-features = false } +quickcheck = { version = "1.0", default-features = false } [package.metadata.release] no-dev-version = true diff --git a/ndarray-rand/src/lib.rs b/ndarray-rand/src/lib.rs index d9edabfdb..448eb10d9 100644 --- a/ndarray-rand/src/lib.rs +++ b/ndarray-rand/src/lib.rs @@ -304,7 +304,7 @@ pub enum SamplingStrategy { // `Arbitrary` enables `quickcheck` to generate random `SamplingStrategy` values for testing. #[cfg(feature = "quickcheck")] impl Arbitrary for SamplingStrategy { - fn arbitrary(g: &mut G) -> Self { + fn arbitrary(g: &mut Gen) -> Self { if bool::arbitrary(g) { SamplingStrategy::WithReplacement } else { diff --git a/ndarray-rand/tests/tests.rs b/ndarray-rand/tests/tests.rs index d8b18fe56..5a0cd6c1b 100644 --- a/ndarray-rand/tests/tests.rs +++ b/ndarray-rand/tests/tests.rs @@ -5,7 +5,7 @@ use ndarray_rand::rand::{distributions::Distribution, thread_rng}; use ndarray::ShapeBuilder; use ndarray_rand::rand_distr::Uniform; use ndarray_rand::{RandomExt, SamplingStrategy}; -use quickcheck::quickcheck; +use quickcheck::{quickcheck, TestResult}; #[test] fn test_dim() { @@ -51,7 +51,8 @@ fn oversampling_without_replacement_should_panic() { } quickcheck! { - fn oversampling_with_replacement_is_fine(m: usize, n: usize) -> bool { + 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.)); // Higher than the length of both axes let n_samples = m + n + 1; @@ -59,24 +60,28 @@ quickcheck! { // We don't want to deal with sampling from 0-length axes in this test if m != 0 { if !sampling_works(&a, SamplingStrategy::WithReplacement, Axis(0), n_samples) { - return false; + return TestResult::failed(); } + } else { + return TestResult::discard(); } // We don't want to deal with sampling from 0-length axes in this test if n != 0 { if !sampling_works(&a, SamplingStrategy::WithReplacement, Axis(1), n_samples) { - return false; + return TestResult::failed(); } + } else { + return TestResult::discard(); } - - true + TestResult::passed() } } #[cfg(feature = "quickcheck")] quickcheck! { - fn sampling_behaves_as_expected(m: usize, n: usize, strategy: SamplingStrategy) -> bool { + 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(); @@ -84,19 +89,23 @@ quickcheck! { if m != 0 { let n_row_samples = Uniform::from(1..m+1).sample(&mut rng); if !sampling_works(&a, strategy.clone(), Axis(0), n_row_samples) { - return false; + return TestResult::failed(); } + } else { + return TestResult::discard(); } // 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); if !sampling_works(&a, strategy, Axis(1), n_col_samples) { - return false; + return TestResult::failed(); } + } else { + return TestResult::discard(); } - true + TestResult::passed() } } diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 7bb92892c..f0752142b 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -920,12 +920,16 @@ mod test { } quickcheck! { - fn extended_gcd_solves_eq(a: isize, b: isize) -> bool { + // FIXME: This test can't handle larger values at the moment + fn extended_gcd_solves_eq(a: i16, b: i16) -> bool { + let (a, b) = (a as isize, b as isize); let (g, (x, y)) = extended_gcd(a, b); a * x + b * y == g } - fn extended_gcd_correct_gcd(a: isize, b: isize) -> bool { + // FIXME: This test can't handle larger values at the moment + fn extended_gcd_correct_gcd(a: i16, b: i16) -> bool { + let (a, b) = (a as isize, b as isize); let (g, _) = extended_gcd(a, b); g == gcd(a, b) } @@ -941,9 +945,12 @@ mod test { } quickcheck! { + // FIXME: This test can't handle larger values at the moment fn solve_linear_diophantine_eq_solution_existence( - a: isize, b: isize, c: isize + a: i16, b: i16, c: i16 ) -> TestResult { + let (a, b, c) = (a as isize, b as isize, c as isize); + if a == 0 || b == 0 { TestResult::discard() } else { @@ -953,9 +960,12 @@ mod test { } } + // FIXME: This test can't handle larger values at the moment fn solve_linear_diophantine_eq_correct_solution( - a: isize, b: isize, c: isize, t: isize + a: i8, b: i8, c: i8, t: i8 ) -> TestResult { + let (a, b, c, t) = (a as isize, b as isize, c as isize, t as isize); + if a == 0 || b == 0 { TestResult::discard() } else { @@ -972,17 +982,23 @@ mod test { } quickcheck! { + // FIXME: This test is extremely slow, even with i16 values, investigate fn arith_seq_intersect_correct( - first1: isize, len1: isize, step1: isize, - first2: isize, len2: isize, step2: isize + first1: i8, len1: i8, step1: i8, + first2: i8, len2: i8, step2: i8 ) -> TestResult { use std::cmp; + let (len1, len2) = (len1 as isize, len2 as isize); + let (first1, step1) = (first1 as isize, step1 as isize); + let (first2, step2) = (first2 as isize, step2 as isize); + if len1 == 0 || len2 == 0 { // This case is impossible to reach in `arith_seq_intersect()` // because the `min*` and `max*` arguments are inclusive. return TestResult::discard(); } + let len1 = len1.abs(); let len2 = len2.abs(); From 6a50e9d4c438d023de4536963c202f10ff0c5147 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Fri, 4 Jun 2021 20:58:59 -0400 Subject: [PATCH 420/651] Add approx-0p5 feature for approx 0.5 support `approx` 0.5 updates its `num-complex` dependency to 0.4, which is the same version of `num-complex` which `ndarray` depends on. So, it's very useful for `ndarray` to support `approx` 0.5. --- Cargo.toml | 3 +- src/array_approx.rs | 361 +++++++++++++++++++++++--------------------- src/lib.rs | 6 +- 3 files changed, 197 insertions(+), 173 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 44dd52fb5..48c7e007e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ num-complex = { version = "0.4", default-features = false } rayon_ = { version = "1.0.3", optional = true, package = "rayon" } approx = { version = "0.4", optional = true , default-features = false } +approx-0p5 = { package = "approx", version = "0.5", optional = true , default-features = false } # Use via the `blas` crate feature! cblas-sys = { version = "0.1.4", optional = true, default-features = false } @@ -64,7 +65,7 @@ serde-1 = ["serde"] test = [] # This feature is used for docs -docs = ["approx", "serde", "rayon"] +docs = ["approx", "approx-0p5", "serde", "rayon"] std = ["num-traits/std", "matrixmultiply/std"] rayon = ["rayon_", "std"] diff --git a/src/array_approx.rs b/src/array_approx.rs index bb6804a27..37957c384 100644 --- a/src/array_approx.rs +++ b/src/array_approx.rs @@ -1,181 +1,202 @@ -use crate::imp_prelude::*; -use crate::Zip; -use approx::{AbsDiffEq, RelativeEq, UlpsEq}; - -/// **Requires crate feature `"approx"`** -impl AbsDiffEq> for ArrayBase -where - A: AbsDiffEq, - A::Epsilon: Clone, - S: Data, - S2: Data, - D: Dimension, -{ - type Epsilon = A::Epsilon; - - fn default_epsilon() -> A::Epsilon { - A::default_epsilon() - } +#[cfg(feature = "approx")] +mod approx_methods { + use crate::imp_prelude::*; - fn abs_diff_eq(&self, other: &ArrayBase, epsilon: A::Epsilon) -> bool { - if self.shape() != other.shape() { - return false; + impl ArrayBase + where + S: Data, + D: Dimension, + { + /// A test for equality that uses the elementwise absolute difference to compute the + /// approximate equality of two arrays. + /// + /// **Requires crate feature `"approx"`** + pub fn abs_diff_eq(&self, other: &ArrayBase, epsilon: A::Epsilon) -> bool + where + A: ::approx::AbsDiffEq, + A::Epsilon: Clone, + S2: Data, + { + >::abs_diff_eq(self, other, epsilon) } - Zip::from(self) - .and(other) - .all(move |a, b| A::abs_diff_eq(a, b, epsilon.clone())) - } -} - -/// **Requires crate feature `"approx"`** -impl RelativeEq> for ArrayBase -where - A: RelativeEq, - A::Epsilon: Clone, - S: Data, - S2: Data, - D: Dimension, -{ - fn default_max_relative() -> A::Epsilon { - A::default_max_relative() - } - - fn relative_eq( - &self, - other: &ArrayBase, - epsilon: A::Epsilon, - max_relative: A::Epsilon, - ) -> bool { - if self.shape() != other.shape() { - return false; + /// A test for equality that uses an elementwise relative comparison if the values are far + /// apart; and the absolute difference otherwise. + /// + /// **Requires crate feature `"approx"`** + pub fn relative_eq( + &self, + other: &ArrayBase, + epsilon: A::Epsilon, + max_relative: A::Epsilon, + ) -> bool + where + A: ::approx::RelativeEq, + A::Epsilon: Clone, + S2: Data, + { + >::relative_eq(self, other, epsilon, max_relative) } - - Zip::from(self) - .and(other) - .all(move |a, b| A::relative_eq(a, b, epsilon.clone(), max_relative.clone())) } } -/// **Requires crate feature `"approx"`** -impl UlpsEq> for ArrayBase -where - A: UlpsEq, - A::Epsilon: Clone, - S: Data, - S2: Data, - D: Dimension, -{ - fn default_max_ulps() -> u32 { - A::default_max_ulps() - } - - fn ulps_eq(&self, other: &ArrayBase, epsilon: A::Epsilon, max_ulps: u32) -> bool { - if self.shape() != other.shape() { - return false; +macro_rules! impl_approx_traits { + ($approx:ident, $doc:expr) => { + mod $approx { + use crate::imp_prelude::*; + use crate::Zip; + use $approx::{AbsDiffEq, RelativeEq, UlpsEq}; + + #[doc = $doc] + impl AbsDiffEq> for ArrayBase + where + A: AbsDiffEq, + A::Epsilon: Clone, + S: Data, + S2: Data, + D: Dimension, + { + type Epsilon = A::Epsilon; + + fn default_epsilon() -> A::Epsilon { + A::default_epsilon() + } + + fn abs_diff_eq(&self, other: &ArrayBase, epsilon: A::Epsilon) -> bool { + if self.shape() != other.shape() { + return false; + } + + Zip::from(self) + .and(other) + .all(move |a, b| A::abs_diff_eq(a, b, epsilon.clone())) + } + } + + #[doc = $doc] + impl RelativeEq> for ArrayBase + where + A: RelativeEq, + A::Epsilon: Clone, + S: Data, + S2: Data, + D: Dimension, + { + fn default_max_relative() -> A::Epsilon { + A::default_max_relative() + } + + fn relative_eq( + &self, + other: &ArrayBase, + epsilon: A::Epsilon, + max_relative: A::Epsilon, + ) -> bool { + if self.shape() != other.shape() { + return false; + } + + Zip::from(self).and(other).all(move |a, b| { + A::relative_eq(a, b, epsilon.clone(), max_relative.clone()) + }) + } + } + + #[doc = $doc] + impl UlpsEq> for ArrayBase + where + A: UlpsEq, + A::Epsilon: Clone, + S: Data, + S2: Data, + D: Dimension, + { + fn default_max_ulps() -> u32 { + A::default_max_ulps() + } + + fn ulps_eq( + &self, + other: &ArrayBase, + epsilon: A::Epsilon, + max_ulps: u32, + ) -> bool { + if self.shape() != other.shape() { + return false; + } + + Zip::from(self) + .and(other) + .all(move |a, b| A::ulps_eq(a, b, epsilon.clone(), max_ulps)) + } + } + + #[cfg(test)] + mod tests { + use crate::prelude::*; + use alloc::vec; + use $approx::{ + assert_abs_diff_eq, assert_abs_diff_ne, assert_relative_eq, assert_relative_ne, + assert_ulps_eq, assert_ulps_ne, + }; + + #[test] + fn abs_diff_eq() { + let a: Array2 = array![[0., 2.], [-0.000010001, 100000000.]]; + let mut b: Array2 = array![[0., 1.], [-0.000010002, 100000001.]]; + assert_abs_diff_ne!(a, b); + b[(0, 1)] = 2.; + assert_abs_diff_eq!(a, b); + + // Check epsilon. + assert_abs_diff_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32); + assert_abs_diff_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32); + + // Make sure we can compare different shapes without failure. + let c = array![[1., 2.]]; + assert_abs_diff_ne!(a, c); + } + + #[test] + fn relative_eq() { + let a: Array2 = array![[1., 2.], [-0.000010001, 100000000.]]; + let mut b: Array2 = array![[1., 1.], [-0.000010002, 100000001.]]; + assert_relative_ne!(a, b); + b[(0, 1)] = 2.; + assert_relative_eq!(a, b); + + // Check epsilon. + assert_relative_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32); + assert_relative_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32); + + // Make sure we can compare different shapes without failure. + let c = array![[1., 2.]]; + assert_relative_ne!(a, c); + } + + #[test] + fn ulps_eq() { + let a: Array2 = array![[1., 2.], [-0.000010001, 100000000.]]; + let mut b: Array2 = array![[1., 1.], [-0.000010002, 100000001.]]; + assert_ulps_ne!(a, b); + b[(0, 1)] = 2.; + assert_ulps_eq!(a, b); + + // Check epsilon. + assert_ulps_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32); + assert_ulps_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32); + + // Make sure we can compare different shapes without failure. + let c = array![[1., 2.]]; + assert_ulps_ne!(a, c); + } + } } - - Zip::from(self) - .and(other) - .all(move |a, b| A::ulps_eq(a, b, epsilon.clone(), max_ulps)) - } -} - -impl ArrayBase -where - S: Data, - D: Dimension, -{ - /// A test for equality that uses the elementwise absolute difference to compute the - /// approximate equality of two arrays. - /// - /// **Requires crate feature `"approx"`** - pub fn abs_diff_eq(&self, other: &ArrayBase, epsilon: A::Epsilon) -> bool - where - A: AbsDiffEq, - A::Epsilon: Clone, - S2: Data, - { - >::abs_diff_eq(self, other, epsilon) - } - - /// A test for equality that uses an elementwise relative comparison if the values are far - /// apart; and the absolute difference otherwise. - /// - /// **Requires crate feature `"approx"`** - pub fn relative_eq( - &self, - other: &ArrayBase, - epsilon: A::Epsilon, - max_relative: A::Epsilon, - ) -> bool - where - A: RelativeEq, - A::Epsilon: Clone, - S2: Data - { - >::relative_eq(self, other, epsilon, max_relative) - } -} - - -#[cfg(test)] -mod tests { - use crate::prelude::*; - use alloc::vec; - use approx::{ - assert_abs_diff_eq, assert_abs_diff_ne, assert_relative_eq, assert_relative_ne, - assert_ulps_eq, assert_ulps_ne, }; +} - #[test] - fn abs_diff_eq() { - let a: Array2 = array![[0., 2.], [-0.000010001, 100000000.]]; - let mut b: Array2 = array![[0., 1.], [-0.000010002, 100000001.]]; - assert_abs_diff_ne!(a, b); - b[(0, 1)] = 2.; - assert_abs_diff_eq!(a, b); - - // Check epsilon. - assert_abs_diff_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32); - assert_abs_diff_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32); - - // Make sure we can compare different shapes without failure. - let c = array![[1., 2.]]; - assert_abs_diff_ne!(a, c); - } +#[cfg(feature = "approx")] +impl_approx_traits!(approx, "**Requires crate feature `\"approx\"`.**"); - #[test] - fn relative_eq() { - let a: Array2 = array![[1., 2.], [-0.000010001, 100000000.]]; - let mut b: Array2 = array![[1., 1.], [-0.000010002, 100000001.]]; - assert_relative_ne!(a, b); - b[(0, 1)] = 2.; - assert_relative_eq!(a, b); - - // Check epsilon. - assert_relative_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32); - assert_relative_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32); - - // Make sure we can compare different shapes without failure. - let c = array![[1., 2.]]; - assert_relative_ne!(a, c); - } - - #[test] - fn ulps_eq() { - let a: Array2 = array![[1., 2.], [-0.000010001, 100000000.]]; - let mut b: Array2 = array![[1., 1.], [-0.000010002, 100000001.]]; - assert_ulps_ne!(a, b); - b[(0, 1)] = 2.; - assert_ulps_eq!(a, b); - - // Check epsilon. - assert_ulps_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32); - assert_ulps_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32); - - // Make sure we can compare different shapes without failure. - let c = array![[1., 2.]]; - assert_ulps_ne!(a, c); - } -} +#[cfg(feature = "approx-0p5")] +impl_approx_traits!(approx_0p5, "**Requires crate feature `\"approx-0p5\"`.**"); diff --git a/src/lib.rs b/src/lib.rs index f773226f5..8d38a8d04 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,7 +91,9 @@ //! - Enables parallel iterators, parallelized methods, the [`parallel`] module and [`par_azip!`]. //! - Implies std //! - `approx` -//! - Enables implementations of traits from the [`approx`] crate. +//! - Enables implementations of traits from version 0.4 of the [`approx`] crate. +//! - `approx-0p5` +//! - Enables implementations of traits from version 0.5 of the [`approx`] crate. //! - `blas` //! - Enable transparent BLAS support for matrix multiplication. //! Uses ``blas-src`` for pluggable backend, which needs to be configured @@ -1596,7 +1598,7 @@ pub mod linalg; mod impl_ops; pub use crate::impl_ops::ScalarOperand; -#[cfg(feature = "approx")] +#[cfg(any(feature = "approx", feature = "approx-0p5"))] mod array_approx; // Array view methods From 99a5cb15b88950a014c6428e47fd874d3ce38269 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 5 Jun 2021 16:50:15 -0400 Subject: [PATCH 421/651] Rename approx-0p5 feature to approx-0_5 --- Cargo.toml | 4 ++-- src/array_approx.rs | 4 ++-- src/lib.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 48c7e007e..56de806b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ num-complex = { version = "0.4", default-features = false } rayon_ = { version = "1.0.3", optional = true, package = "rayon" } approx = { version = "0.4", optional = true , default-features = false } -approx-0p5 = { package = "approx", version = "0.5", optional = true , default-features = false } +approx-0_5 = { package = "approx", version = "0.5", optional = true , default-features = false } # Use via the `blas` crate feature! cblas-sys = { version = "0.1.4", optional = true, default-features = false } @@ -65,7 +65,7 @@ serde-1 = ["serde"] test = [] # This feature is used for docs -docs = ["approx", "approx-0p5", "serde", "rayon"] +docs = ["approx", "approx-0_5", "serde", "rayon"] std = ["num-traits/std", "matrixmultiply/std"] rayon = ["rayon_", "std"] diff --git a/src/array_approx.rs b/src/array_approx.rs index 37957c384..a40982a56 100644 --- a/src/array_approx.rs +++ b/src/array_approx.rs @@ -198,5 +198,5 @@ macro_rules! impl_approx_traits { #[cfg(feature = "approx")] impl_approx_traits!(approx, "**Requires crate feature `\"approx\"`.**"); -#[cfg(feature = "approx-0p5")] -impl_approx_traits!(approx_0p5, "**Requires crate feature `\"approx-0p5\"`.**"); +#[cfg(feature = "approx-0_5")] +impl_approx_traits!(approx_0_5, "**Requires crate feature `\"approx-0_5\"`.**"); diff --git a/src/lib.rs b/src/lib.rs index 8d38a8d04..8cf23e915 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,7 +92,7 @@ //! - Implies std //! - `approx` //! - Enables implementations of traits from version 0.4 of the [`approx`] crate. -//! - `approx-0p5` +//! - `approx-0_5` //! - Enables implementations of traits from version 0.5 of the [`approx`] crate. //! - `blas` //! - Enable transparent BLAS support for matrix multiplication. @@ -1598,7 +1598,7 @@ pub mod linalg; mod impl_ops; pub use crate::impl_ops::ScalarOperand; -#[cfg(any(feature = "approx", feature = "approx-0p5"))] +#[cfg(any(feature = "approx", feature = "approx-0_5"))] mod array_approx; // Array view methods From 0fe046df5dce194edfd5349181d603ad8d39551e Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sun, 21 Nov 2021 12:52:23 +0100 Subject: [PATCH 422/651] Use matrixmultiply cgemm and improve tests Refactor tests to use generics in a bit smarter way. We test both .dot() and general_mat_mul. Tests now use relative accuracy more explicitly (this works better with generics, instead of using approx). --- Cargo.toml | 2 +- benches/{gemv.rs => gemv_gemm.rs} | 29 ++++ src/linalg/impl_linalg.rs | 61 +++++++- xtest-numeric/Cargo.toml | 5 + xtest-numeric/tests/accuracy.rs | 240 +++++++++++++++++------------- 5 files changed, 226 insertions(+), 111 deletions(-) rename benches/{gemv.rs => gemv_gemm.rs} (61%) diff --git a/Cargo.toml b/Cargo.toml index 56de806b0..65f3d3693 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,7 @@ approx-0_5 = { package = "approx", version = "0.5", optional = true , default-fe cblas-sys = { version = "0.1.4", optional = true, default-features = false } libc = { version = "0.2.82", optional = true } -matrixmultiply = { version = "0.3.0", default-features = false} +matrixmultiply = { version = "0.3.2", default-features = false, features=["cgemm"] } serde = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } rawpointer = { version = "0.2" } diff --git a/benches/gemv.rs b/benches/gemv_gemm.rs similarity index 61% rename from benches/gemv.rs rename to benches/gemv_gemm.rs index 4bca08319..cfa14beac 100644 --- a/benches/gemv.rs +++ b/benches/gemv_gemm.rs @@ -9,8 +9,13 @@ extern crate test; use test::Bencher; +use num_complex::Complex; +use num_traits::{Float, One, Zero}; + use ndarray::prelude::*; +use ndarray::LinalgScalar; +use ndarray::linalg::general_mat_mul; use ndarray::linalg::general_mat_vec_mul; #[bench] @@ -45,3 +50,27 @@ fn gemv_64_32(bench: &mut Bencher) { general_mat_vec_mul(1.0, &a, &x, 1.0, &mut y); }); } + +#[bench] +fn cgemm_100(bench: &mut Bencher) { + cgemm_bench::(100, bench); +} + +#[bench] +fn zgemm_100(bench: &mut Bencher) { + cgemm_bench::(100, bench); +} + +fn cgemm_bench(size: usize, bench: &mut Bencher) +where + A: LinalgScalar + Float, +{ + let (m, k, n) = (size, size, size); + let a = Array::, _>::zeros((m, k)); + + let x = Array::zeros((k, n)); + let mut y = Array::zeros((m, n)); + bench.iter(|| { + general_mat_mul(Complex::one(), &a, &x, Complex::zero(), &mut y); + }); +} diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index 0e6700d65..d953dfdb4 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -18,6 +18,9 @@ use std::any::TypeId; use std::mem::MaybeUninit; use alloc::vec::Vec; +use num_complex::Complex; +use num_complex::{Complex32 as c32, Complex64 as c64}; + #[cfg(feature = "blas")] use libc::c_int; #[cfg(feature = "blas")] @@ -30,9 +33,6 @@ use cblas_sys as blas_sys; #[cfg(feature = "blas")] use cblas_sys::{CblasNoTrans, CblasRowMajor, CblasTrans, CBLAS_LAYOUT}; -#[cfg(feature = "blas")] -use num_complex::{Complex32 as c32, Complex64 as c64}; - /// len of vector before we use blas #[cfg(feature = "blas")] const DOT_BLAS_CUTOFF: usize = 32; @@ -505,7 +505,7 @@ fn mat_mul_general( let (rsc, csc) = (c.strides()[0], c.strides()[1]); if same_type::() { unsafe { - ::matrixmultiply::sgemm( + matrixmultiply::sgemm( m, k, n, @@ -524,7 +524,7 @@ fn mat_mul_general( } } else if same_type::() { unsafe { - ::matrixmultiply::dgemm( + matrixmultiply::dgemm( m, k, n, @@ -541,6 +541,48 @@ fn mat_mul_general( csc, ); } + } else if same_type::() { + unsafe { + matrixmultiply::cgemm( + matrixmultiply::CGemmOption::Standard, + matrixmultiply::CGemmOption::Standard, + m, + k, + n, + complex_array(cast_as(&alpha)), + ap as *const _, + lhs.strides()[0], + lhs.strides()[1], + bp as *const _, + rhs.strides()[0], + rhs.strides()[1], + complex_array(cast_as(&beta)), + cp as *mut _, + rsc, + csc, + ); + } + } else if same_type::() { + unsafe { + matrixmultiply::zgemm( + matrixmultiply::CGemmOption::Standard, + matrixmultiply::CGemmOption::Standard, + m, + k, + n, + complex_array(cast_as(&alpha)), + ap as *const _, + lhs.strides()[0], + lhs.strides()[1], + bp as *const _, + rhs.strides()[0], + rhs.strides()[1], + complex_array(cast_as(&beta)), + cp as *mut _, + rsc, + csc, + ); + } } else { // It's a no-op if `c` has zero length. if c.is_empty() { @@ -768,10 +810,17 @@ fn same_type() -> bool { // // **Panics** if `A` and `B` are not the same type fn cast_as(a: &A) -> B { - assert!(same_type::()); + assert!(same_type::(), "expect type {} and {} to match", + std::any::type_name::(), std::any::type_name::()); unsafe { ::std::ptr::read(a as *const _ as *const B) } } +/// Return the complex in the form of an array [re, im] +#[inline] +fn complex_array(z: Complex) -> [A; 2] { + [z.re, z.im] +} + #[cfg(feature = "blas")] fn blas_compat_1d(a: &ArrayBase) -> bool where diff --git a/xtest-numeric/Cargo.toml b/xtest-numeric/Cargo.toml index 14bbd16ee..7a6c3cedd 100644 --- a/xtest-numeric/Cargo.toml +++ b/xtest-numeric/Cargo.toml @@ -3,6 +3,7 @@ name = "numeric-tests" version = "0.1.0" authors = ["bluss"] publish = false +edition = "2018" [dependencies] approx = "0.4" @@ -17,6 +18,10 @@ openblas-src = { optional = true, version = "0.10", default-features = false, fe version = "0.8.0" features = ["small_rng"] +[dev-dependencies] +num-traits = { version = "0.2.14", default-features = false } +num-complex = { version = "0.4", default-features = false } + [lib] test = false diff --git a/xtest-numeric/tests/accuracy.rs b/xtest-numeric/tests/accuracy.rs index 438b73705..1f8e9a82c 100644 --- a/xtest-numeric/tests/accuracy.rs +++ b/xtest-numeric/tests/accuracy.rs @@ -6,6 +6,8 @@ extern crate rand; extern crate numeric_tests; +use std::fmt; + use ndarray_rand::RandomExt; use rand::{Rng, SeedableRng}; use rand::rngs::SmallRng; @@ -17,10 +19,28 @@ use ndarray::{ }; use ndarray::linalg::general_mat_mul; -use rand_distr::Normal; +use rand_distr::{Normal, StandardNormal, Distribution}; +use num_traits::{Float, AsPrimitive}; +use num_complex::Complex; use approx::{assert_abs_diff_eq, assert_relative_eq}; +fn kahan_sum(iter: impl Iterator) -> A + where A: LinalgScalar +{ + let mut sum = A::zero(); + let mut compensation = A::zero(); + + for elt in iter { + let y = elt - compensation; + let t = sum + y; + compensation = (t - sum) - y; + sum = t; + } + + sum +} + // simple, slow, correct (hopefully) mat mul fn reference_mat_mul(lhs: &ArrayBase, rhs: &ArrayBase) -> Array @@ -29,46 +49,48 @@ fn reference_mat_mul(lhs: &ArrayBase, rhs: &ArrayBase S2: Data, { let ((m, k), (_, n)) = (lhs.dim(), rhs.dim()); - let mut res_elems = Vec::::with_capacity(m * n); - unsafe { - res_elems.set_len(m * n); - } + let mut res_elems = Array::zeros(m * n); let mut i = 0; let mut j = 0; for rr in &mut res_elems { - unsafe { - *rr = (0..k).fold(A::zero(), - move |s, x| s + *lhs.uget((i, x)) * *rhs.uget((x, j))); - } + let lhs_i = lhs.row(i); + let rhs_j = rhs.column(j); + *rr = kahan_sum((0..k).map(move |x| lhs_i[x] * rhs_j[x])); + j += 1; if j == n { j = 0; i += 1; } } - unsafe { - ArrayBase::from_shape_vec_unchecked((m, n), res_elems) - } + + res_elems.into_shape((m, n)).unwrap() } -fn gen(d: D) -> Array +fn gen(d: D, rng: &mut SmallRng) -> Array where D: Dimension, + A: Float, + StandardNormal: Distribution, { - Array::random(d, Normal::new(0., 1.).unwrap()) + Array::random_using(d, Normal::new(A::zero(), A::one()).unwrap(), rng) } -fn gen_f64(d: D) -> Array + +fn gen_complex(d: D, rng: &mut SmallRng) -> Array, D> where D: Dimension, + A: Float, + StandardNormal: Distribution, { - Array::random(d, Normal::new(0., 1.).unwrap()) + gen(d.clone(), rng).mapv(Complex::from) + gen(d, rng).mapv(|x| Complex::new(A::zero(), x)) } #[test] fn accurate_eye_f32() { + let rng = &mut SmallRng::from_entropy(); for i in 0..20 { let eye = Array::eye(i); for j in 0..20 { - let a = gen(Ix2(i, j)); + let a = gen::(Ix2(i, j), rng); let a2 = eye.dot(&a); assert_abs_diff_eq!(a, a2, epsilon = 1e-6); let a3 = a.t().dot(&eye); @@ -76,12 +98,11 @@ fn accurate_eye_f32() { } } // pick a few random sizes - let mut rng = SmallRng::from_entropy(); for _ in 0..10 { let i = rng.gen_range(15..512); let j = rng.gen_range(15..512); println!("Testing size {} by {}", i, j); - let a = gen(Ix2(i, j)); + let a = gen::(Ix2(i, j), rng); let eye = Array::eye(i); let a2 = eye.dot(&a); assert_abs_diff_eq!(a, a2, epsilon = 1e-6); @@ -92,11 +113,12 @@ fn accurate_eye_f32() { #[test] fn accurate_eye_f64() { + let rng = &mut SmallRng::from_entropy(); let abs_tol = 1e-15; for i in 0..20 { let eye = Array::eye(i); for j in 0..20 { - let a = gen_f64(Ix2(i, j)); + let a = gen::(Ix2(i, j), rng); let a2 = eye.dot(&a); assert_abs_diff_eq!(a, a2, epsilon = abs_tol); let a3 = a.t().dot(&eye); @@ -104,12 +126,11 @@ fn accurate_eye_f64() { } } // pick a few random sizes - let mut rng = SmallRng::from_entropy(); for _ in 0..10 { let i = rng.gen_range(15..512); let j = rng.gen_range(15..512); println!("Testing size {} by {}", i, j); - let a = gen_f64(Ix2(i, j)); + let a = gen::(Ix2(i, j), rng); let eye = Array::eye(i); let a2 = eye.dot(&a); assert_abs_diff_eq!(a, a2, epsilon = 1e-6); @@ -119,114 +140,125 @@ fn accurate_eye_f64() { } #[test] -fn accurate_mul_f32() { - // pick a few random sizes - let mut rng = SmallRng::from_entropy(); - for i in 0..20 { - let m = rng.gen_range(15..512); - let k = rng.gen_range(15..512); - let n = rng.gen_range(15..1560); - let a = gen(Ix2(m, k)); - let b = gen(Ix2(n, k)); - let b = b.t(); - let (a, b) = if i > 10 { - (a.slice(s![..;2, ..;2]), - b.slice(s![..;2, ..;2])) - } else { (a.view(), b) }; - - println!("Testing size {} by {} by {}", a.shape()[0], a.shape()[1], b.shape()[1]); - let c = a.dot(&b); - let reference = reference_mat_mul(&a, &b); - - assert_relative_eq!(c, reference, epsilon = 1e-4, max_relative = 1e-3); - } +fn accurate_mul_f32_dot() { + accurate_mul_float_general::(1e-5, false); } #[test] fn accurate_mul_f32_general() { - // pick a few random sizes - let mut rng = SmallRng::from_entropy(); - for i in 0..20 { - let m = rng.gen_range(15..512); - let k = rng.gen_range(15..512); - let n = rng.gen_range(15..1560); - let a = gen(Ix2(m, k)); - let b = gen(Ix2(n, k)); - let mut c = gen(Ix2(m, n)); - let b = b.t(); - let (a, b, mut c) = if i > 10 { - (a.slice(s![..;2, ..;2]), - b.slice(s![..;2, ..;2]), - c.slice_mut(s![..;2, ..;2])) - } else { (a.view(), b, c.view_mut()) }; - - println!("Testing size {} by {} by {}", a.shape()[0], a.shape()[1], b.shape()[1]); - general_mat_mul(1., &a, &b, 0., &mut c); - let reference = reference_mat_mul(&a, &b); - - assert_relative_eq!(c, reference, epsilon = 1e-4, max_relative = 1e-3); - } + accurate_mul_float_general::(1e-5, true); +} + +#[test] +fn accurate_mul_f64_dot() { + accurate_mul_float_general::(1e-14, false); } #[test] -fn accurate_mul_f64() { +fn accurate_mul_f64_general() { + accurate_mul_float_general::(1e-14, true); +} + +/// Generate random sized matrices using the given generator function. +/// Compute gemm using either .dot() (if use_general is false) otherwise general_mat_mul. +/// Return tuple of actual result matrix and reference matrix, which should be equal. +fn random_matrix_mul(rng: &mut SmallRng, use_stride: bool, use_general: bool, + generator: fn(Ix2, &mut SmallRng) -> Array2) + -> (Array2, Array2) + where A: LinalgScalar, +{ + let m = rng.gen_range(15..512); + let k = rng.gen_range(15..512); + let n = rng.gen_range(15..1560); + let a = generator(Ix2(m, k), rng); + let b = generator(Ix2(n, k), rng); + let c = if use_general { + Some(generator(Ix2(m, n), rng)) + } else { + None + }; + + let b = b.t(); + let (a, b, mut c) = if use_stride { + (a.slice(s![..;2, ..;2]), + b.slice(s![..;2, ..;2]), + c.map(|c_| c_.slice_move(s![..;2, ..;2]))) + } else { + (a.view(), + b, + c) + }; + + println!("Testing size {} by {} by {}", a.shape()[0], a.shape()[1], b.shape()[1]); + if let Some(c) = &mut c { + general_mat_mul(A::one(), &a, &b, A::zero(), c); + } else { + c = Some(a.dot(&b)); + } + let c = c.unwrap(); + let reference = reference_mat_mul(&a, &b); + + (c, reference) +} + +fn accurate_mul_float_general(limit: f64, use_general: bool) + where A: Float + Copy + 'static + AsPrimitive, + StandardNormal: Distribution, + A: fmt::Debug, +{ // pick a few random sizes let mut rng = SmallRng::from_entropy(); for i in 0..20 { - let m = rng.gen_range(15..512); - let k = rng.gen_range(15..512); - let n = rng.gen_range(15..1560); - let a = gen_f64(Ix2(m, k)); - let b = gen_f64(Ix2(n, k)); - let b = b.t(); - let (a, b) = if i > 10 { - (a.slice(s![..;2, ..;2]), - b.slice(s![..;2, ..;2])) - } else { (a.view(), b) }; - - println!("Testing size {} by {} by {}", a.shape()[0], a.shape()[1], b.shape()[1]); - let c = a.dot(&b); - let reference = reference_mat_mul(&a, &b); - - assert_relative_eq!(c, reference, epsilon = 1e-12, max_relative = 1e-7); + let (c, reference) = random_matrix_mul(&mut rng, i > 10, use_general, gen::); + + let diff = &c - &reference; + let max_diff = diff.iter().copied().fold(A::zero(), A::max); + let max_elt = reference.iter().copied().fold(A::zero(), A::max); + println!("Max elt diff={:?}, max={:?}, ratio={:.4e}", max_diff, max_elt, (max_diff/max_elt).as_()); + assert!((max_diff / max_elt).as_() < limit, + "Expected relative norm diff < {:e}, found {:?} / {:?}", limit, max_diff, max_elt); } } #[test] -fn accurate_mul_f64_general() { +fn accurate_mul_complex32() { + accurate_mul_complex_general::(1e-5); +} + +#[test] +fn accurate_mul_complex64() { + accurate_mul_complex_general::(1e-14); +} + +fn accurate_mul_complex_general(limit: f64) + where A: Float + Copy + 'static + AsPrimitive, + StandardNormal: Distribution, + A: fmt::Debug, +{ // pick a few random sizes let mut rng = SmallRng::from_entropy(); for i in 0..20 { - let m = rng.gen_range(15..512); - let k = rng.gen_range(15..512); - let n = rng.gen_range(15..1560); - let a = gen_f64(Ix2(m, k)); - let b = gen_f64(Ix2(n, k)); - let mut c = gen_f64(Ix2(m, n)); - let b = b.t(); - let (a, b, mut c) = if i > 10 { - (a.slice(s![..;2, ..;2]), - b.slice(s![..;2, ..;2]), - c.slice_mut(s![..;2, ..;2])) - } else { (a.view(), b, c.view_mut()) }; - - println!("Testing size {} by {} by {}", a.shape()[0], a.shape()[1], b.shape()[1]); - general_mat_mul(1., &a, &b, 0., &mut c); - let reference = reference_mat_mul(&a, &b); - - assert_relative_eq!(c, reference, epsilon = 1e-12, max_relative = 1e-7); + let (c, reference) = random_matrix_mul(&mut rng, i > 10, true, gen_complex::); + + let diff = &c - &reference; + let max_elt = |elt: &Complex<_>| A::max(A::abs(elt.re), A::abs(elt.im)); + let max_diff = diff.iter().map(max_elt).fold(A::zero(), A::max); + let max_elt = reference.iter().map(max_elt).fold(A::zero(), A::max); + println!("Max elt diff={:?}, max={:?}, ratio={:.4e}", max_diff, max_elt, (max_diff/max_elt).as_()); + assert!((max_diff / max_elt).as_() < limit, + "Expected relative norm diff < {:e}, found {:?} / {:?}", limit, max_diff, max_elt); } } #[test] fn accurate_mul_with_column_f64() { // pick a few random sizes - let mut rng = SmallRng::from_entropy(); + let rng = &mut SmallRng::from_entropy(); for i in 0..10 { let m = rng.gen_range(1..350); let k = rng.gen_range(1..350); - let a = gen_f64(Ix2(m, k)); - let b_owner = gen_f64(Ix2(k, k)); + let a = gen::(Ix2(m, k), rng); + let b_owner = gen::(Ix2(k, k), rng); let b_row_col; let b_sq; From 5e6ebf5296c372a7be9cbe4ce0faad43a87dc31d Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sun, 21 Nov 2021 13:11:58 +0100 Subject: [PATCH 423/651] clippy: if_then_panic is no longer enabled by default (also changed name) --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 8cf23e915..aadef4c9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,6 @@ clippy::manual_map, // is not an error clippy::while_let_on_iterator, // is not an error clippy::from_iter_instead_of_collect, // using from_iter is good style - clippy::if_then_panic, // is not an error clippy::redundant_closure, // false positives clippy #7812 )] #![doc(test(attr(deny(warnings))))] From d281b55a547337427e95e82abcc209d42b5459ea Mon Sep 17 00:00:00 2001 From: chris Date: Sun, 21 Nov 2021 18:42:43 +0100 Subject: [PATCH 424/651] Fix typo in docs --- src/impl_views/conversions.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs index 7498add48..918b131a1 100644 --- a/src/impl_views/conversions.rs +++ b/src/impl_views/conversions.rs @@ -34,7 +34,7 @@ where /// Return the array’s data as a slice, if it is contiguous and in standard order. /// Return `None` otherwise. /// - /// Note that while the method is similar to [`ArrayBase::as_slice()`], this method tranfers + /// Note that while the method is similar to [`ArrayBase::as_slice()`], this method transfers /// the view's lifetime to the slice, so it is a bit more powerful. pub fn to_slice(&self) -> Option<&'a [A]> { if self.is_standard_layout() { @@ -108,7 +108,7 @@ where /// Return the array’s data as a slice, if it is contiguous and in standard order. /// Return `None` otherwise. /// - /// Note that while this is similar to [`ArrayBase::as_slice_mut()`], this method tranfers the + /// Note that while this is similar to [`ArrayBase::as_slice_mut()`], this method transfers the /// view's lifetime to the slice. pub fn into_slice(self) -> Option<&'a mut [A]> { self.try_into_slice().ok() From 7e70a901f86350c50ad6acb4b720e4a2ab824b3b Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 22 Nov 2021 20:58:40 +0100 Subject: [PATCH 425/651] Add in/to_slice_memory_order for ArrayView/Mut --- src/impl_views/conversions.rs | 48 +++++++++++++++++++++++++++++++++++ tests/array.rs | 36 ++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs index 918b131a1..1e9a1499f 100644 --- a/src/impl_views/conversions.rs +++ b/src/impl_views/conversions.rs @@ -7,12 +7,14 @@ // except according to those terms. use alloc::slice; +use rawpointer::PointerExt; use std::mem::MaybeUninit; use crate::imp_prelude::*; use crate::{Baseiter, ElementsBase, ElementsBaseMut, Iter, IterMut}; +use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr; use crate::iter::{self, AxisIter, AxisIterMut}; use crate::math_cell::MathCell; use crate::IndexLonger; @@ -44,6 +46,26 @@ where } } + /// Return the array’s data as a slice, if it is contiguous. + /// Return `None` otherwise. + /// + /// Note that while the method is similar to + /// [`ArrayBase::as_slice_memory_order()`], this method transfers the view's + /// lifetime to the slice, so it is a bit more powerful. + pub fn to_slice_memory_order(&self) -> Option<&'a [A]> { + if self.is_contiguous() { + let offset = offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides); + unsafe { + Some(slice::from_raw_parts( + self.ptr.sub(offset).as_ptr(), + self.len(), + )) + } + } else { + None + } + } + /// Converts to a raw array view. pub(crate) fn into_raw_view(self) -> RawArrayView { unsafe { RawArrayView::new(self.ptr, self.dim, self.strides) } @@ -114,6 +136,16 @@ where self.try_into_slice().ok() } + /// Return the array’s data as a slice, if it is contiguous. + /// Return `None` otherwise. + /// + /// Note that while this is similar to + /// [`ArrayBase::as_slice_memory_order_mut()`], this method transfers the + /// view's lifetime to the slice. + pub fn into_slice_memory_order(self) -> Option<&'a mut [A]> { + self.try_into_slice_memory_order().ok() + } + /// Return a shared view of the array with elements as if they were embedded in cells. /// /// The cell view itself can be copied and accessed without exclusivity. @@ -215,6 +247,22 @@ where } } + /// Return the array’s data as a slice, if it is contiguous. + /// Otherwise return self in the Err branch of the result. + fn try_into_slice_memory_order(self) -> Result<&'a mut [A], Self> { + if self.is_contiguous() { + let offset = offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides); + unsafe { + Ok(slice::from_raw_parts_mut( + self.ptr.sub(offset).as_ptr(), + self.len(), + )) + } + } else { + Err(self) + } + } + pub(crate) fn into_iter_(self) -> IterMut<'a, A, D> { IterMut::new(self) } diff --git a/tests/array.rs b/tests/array.rs index 16d901568..445659655 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1048,6 +1048,42 @@ fn as_slice_memory_order_mut_contiguous_cowarray() { assert_eq!(b.strides(), &[1, 2]); } +#[test] +fn to_slice_memory_order() { + for shape in vec![[2, 0, 3, 5], [2, 1, 3, 5], [2, 4, 3, 5]] { + let data: Vec = (0..shape.iter().product()).collect(); + let mut orig = Array1::from(data.clone()).into_shape(shape).unwrap(); + for perm in vec![[0, 1, 2, 3], [0, 2, 1, 3], [2, 0, 1, 3]] { + let mut a = orig.view_mut().permuted_axes(perm); + assert_eq!(a.as_slice_memory_order().unwrap(), &data); + assert_eq!(a.as_slice_memory_order_mut().unwrap(), &data); + assert_eq!(a.view().to_slice_memory_order().unwrap(), &data); + assert_eq!(a.view_mut().into_slice_memory_order().unwrap(), &data); + } + } +} + +#[test] +fn to_slice_memory_order_discontiguous() { + let mut orig = Array3::::zeros([3, 2, 4]); + assert!(orig + .slice(s![.., 1.., ..]) + .as_slice_memory_order() + .is_none()); + assert!(orig + .slice_mut(s![.., 1.., ..]) + .as_slice_memory_order_mut() + .is_none()); + assert!(orig + .slice(s![.., 1.., ..]) + .to_slice_memory_order() + .is_none()); + assert!(orig + .slice_mut(s![.., 1.., ..]) + .into_slice_memory_order() + .is_none()); +} + #[test] fn array0_into_scalar() { // With this kind of setup, the `Array`'s pointer is not the same as the From 892dab3575c46d0db87b97005f1bd5b8e07ce93c Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 17 Nov 2021 19:01:26 +0100 Subject: [PATCH 426/651] Release notes for 0.15.4 --- RELEASES.md | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index cfb5e7d31..c0eab1384 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,120 @@ +Version 0.15.4 (2021-11-xx) +=========================== + +New features +------------ + +- Complex matrix multiplication now uses BLAS ``cgemm``/``zgemm`` when + enabled (and matrix layout allows), by [@ethanhs]. + + https://github.com/rust-ndarray/ndarray/pull/1106 + +- Use `matrixmultiply` as fallback for complex matrix multiplication + when BLAS is not available or the matrix layout requires it by [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/1118 + +- Add ``into/to_slice_memory_order`` methods for views, lifetime-preserving + versions of existing similar methods by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/1015 + +- ``kron`` function for Kronecker product by [@ethanhs]. + + https://github.com/rust-ndarray/ndarray/pull/1105 + +- ``split_complex`` method for splitting complex arrays into separate + real and imag view parts by [@jturner314] and [@ethanhs]. + + https://github.com/rust-ndarray/ndarray/pull/1107 + +- New method ``try_into_owned_nocopy`` by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/1022 + +- New producer and iterable ``axis_windows`` by [@VasanthakumarV] + and [@jturner314]. + + https://github.com/rust-ndarray/ndarray/pull/1022 + +- New method ``Zip::par_fold`` by [@adamreichold] + + https://github.com/rust-ndarray/ndarray/pull/1095 + +- New constructor ``from_diag_elem`` by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/1076 + +- ``Parallel::with_min_len`` method for parallel iterators by [@adamreichold] + + https://github.com/rust-ndarray/ndarray/pull/1081 + +- Allocation-preserving map function ``.mapv_into_any()`` added by [@benkay86] + +Enhancements +------------ + +- Improve performance of ``.sum_axis()`` for some cases by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/1061 + +Bug fixes +--------- + +- Fix error in calling dgemv (matrix-vector multiplication) with BLAS and + broadcasted arrays, by [@jturner314]. + + https://github.com/rust-ndarray/ndarray/pull/1088 + +API changes +----------- + +- Support approx 0.5 partially alongside the already existing approx 0.4 support. + New feature flag is `approx-0_5`, by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/1025 + +- Slice and reference-to-array conversions to CowArray added for by [@jturner314]. + + https://github.com/rust-ndarray/ndarray/pull/1038 + +- Allow trailing comma in stack and concatenate macros by [@jturner314] + + https://github.com/rust-ndarray/ndarray/pull/1044 + +- ``Zip`` now has a ``must_use`` marker to help users by [@adamreichold] + + https://github.com/rust-ndarray/ndarray/pull/1082 + +Other changes +------------- + +- Fixing the crates.io badge on github by [@atouchet] + + https://github.com/rust-ndarray/ndarray/pull/1104 + +- Use intra-doc links in docs by [@LeSeulArtichaut] + + https://github.com/rust-ndarray/ndarray/pull/1033 + +- Clippy fixes by [@adamreichold] + + https://github.com/rust-ndarray/ndarray/pull/1092
+ https://github.com/rust-ndarray/ndarray/pull/1091 + +- Minor fixes in links and punctuation in docs by [@jimblandy] + + https://github.com/rust-ndarray/ndarray/pull/1056 + +- Minor fixes in docs by [@chohner] + + https://github.com/rust-ndarray/ndarray/pull/1119 + +- Update tests to quickcheck 1.0 by [@bluss] + + https://github.com/rust-ndarray/ndarray/pull/1114 + + Version 0.15.3 (2021-06-05) =========================== @@ -1427,13 +1544,20 @@ Earlier releases [@jturner314]: https://github.com/jturner314 [@LukeMathWalker]: https://github.com/LukeMathWalker [@acj]: https://github.com/acj +[@adamreichold]: https://github.com/adamreichold +[@atouchet]: https://github.com/atouchet [@andrei-papou]: https://github.com/andrei-papou +[@benkay]: https://github.com/benkay [@cassiersg]: https://github.com/cassiersg +[@chohner]: https://github.com/chohner [@dam5h]: https://github.com/dam5h +[@ethanhs]: https://github.com/ethanhs [@d-dorazio]: https://github.com/d-dorazio [@Eijebong]: https://github.com/Eijebong [@insideoutclub]: https://github.com/insideoutclub [@JP-Ellis]: https://github.com/JP-Ellis +[@jimblandy]: https://github.com/jimblandy +[@LeSeulArtichaut]: https://github.com/LeSeulArtichaut [@lifuyang]: https://github.com/liufuyang [@kdubovikov]: https://github.com/kdubovikov [@max-sixty]: https://github.com/max-sixty @@ -1448,5 +1572,6 @@ Earlier releases [@termoshtt]: https://github.com/termoshtt [@TheLortex]: https://github.com/TheLortex [@viniciusd]: https://github.com/viniciusd +[@VasanthakumarV]: https://github.com/VasanthakumarV [@xd009642]: https://github.com/xd009642 [@Zuse64]: https://github.com/Zuse64 From 752809254e36259da3248b2768f8c965bfe51a45 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 23 Nov 2021 20:22:22 +0100 Subject: [PATCH 427/651] API: Rename split_re_im to split_complex As agreed with ethanhs and jturner314 --- src/impl_raw_views.rs | 6 +++--- src/impl_views/splitting.rs | 12 ++++++------ tests/array.rs | 26 +++++++++++++------------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/impl_raw_views.rs b/src/impl_raw_views.rs index 425af96c2..64a5c4c8d 100644 --- a/src/impl_raw_views.rs +++ b/src/impl_raw_views.rs @@ -156,7 +156,7 @@ where { /// Splits the view into views of the real and imaginary components of the /// elements. - pub fn split_re_im(self) -> Complex> { + pub fn split_complex(self) -> Complex> { // Check that the size and alignment of `Complex` are as expected. // These assertions should always pass, for arbitrary `T`. assert_eq!( @@ -375,8 +375,8 @@ where { /// Splits the view into views of the real and imaginary components of the /// elements. - pub fn split_re_im(self) -> Complex> { - let Complex { re, im } = self.into_raw_view().split_re_im(); + pub fn split_complex(self) -> Complex> { + let Complex { re, im } = self.into_raw_view().split_complex(); unsafe { Complex { re: RawArrayViewMut::new(re.ptr, re.dim, re.strides), diff --git a/src/impl_views/splitting.rs b/src/impl_views/splitting.rs index 84d24038a..028148dde 100644 --- a/src/impl_views/splitting.rs +++ b/src/impl_views/splitting.rs @@ -112,13 +112,13 @@ where /// [Complex64::new(5., 6.), Complex64::new(7., 8.)], /// [Complex64::new(9., 10.), Complex64::new(11., 12.)], /// ]; - /// let Complex { re, im } = arr.view().split_re_im(); + /// let Complex { re, im } = arr.view().split_complex(); /// assert_eq!(re, array![[1., 3.], [5., 7.], [9., 11.]]); /// assert_eq!(im, array![[2., 4.], [6., 8.], [10., 12.]]); /// ``` - pub fn split_re_im(self) -> Complex> { + pub fn split_complex(self) -> Complex> { unsafe { - let Complex { re, im } = self.into_raw_view().split_re_im(); + let Complex { re, im } = self.into_raw_view().split_complex(); Complex { re: re.deref_into_view(), im: im.deref_into_view(), @@ -185,7 +185,7 @@ where /// [Complex64::new(9., 10.), Complex64::new(11., 12.)], /// ]; /// - /// let Complex { mut re, mut im } = arr.view_mut().split_re_im(); + /// let Complex { mut re, mut im } = arr.view_mut().split_complex(); /// assert_eq!(re, array![[1., 3.], [5., 7.], [9., 11.]]); /// assert_eq!(im, array![[2., 4.], [6., 8.], [10., 12.]]); /// @@ -195,9 +195,9 @@ where /// assert_eq!(arr[[0, 1]], Complex64::new(13., 4.)); /// assert_eq!(arr[[2, 0]], Complex64::new(9., 14.)); /// ``` - pub fn split_re_im(self) -> Complex> { + pub fn split_complex(self) -> Complex> { unsafe { - let Complex { re, im } = self.into_raw_view_mut().split_re_im(); + let Complex { re, im } = self.into_raw_view_mut().split_complex(); Complex { re: re.deref_into_view_mut(), im: im.deref_into_view_mut(), diff --git a/tests/array.rs b/tests/array.rs index 445659655..a16e75fd0 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -2541,17 +2541,17 @@ fn test_remove_index_oob3() { } #[test] -fn test_split_re_im_view() { +fn test_split_complex_view() { let a = Array3::from_shape_fn((3, 4, 5), |(i, j, k)| { Complex::::new(i as f32 * j as f32, k as f32) }); - let Complex { re, im } = a.view().split_re_im(); + let Complex { re, im } = a.view().split_complex(); assert_relative_eq!(re.sum(), 90.); assert_relative_eq!(im.sum(), 120.); } #[test] -fn test_split_re_im_view_roundtrip() { +fn test_split_complex_view_roundtrip() { let a_re = Array3::from_shape_fn((3,1,5), |(i, j, _k)| { i * j }); @@ -2561,49 +2561,49 @@ fn test_split_re_im_view_roundtrip() { let a = Array3::from_shape_fn((3,1,5), |(i,j,k)| { Complex::new(a_re[[i,j,k]], a_im[[i,j,k]]) }); - let Complex { re, im } = a.view().split_re_im(); + let Complex { re, im } = a.view().split_complex(); assert_eq!(a_re, re); assert_eq!(a_im, im); } #[test] -fn test_split_re_im_view_mut() { +fn test_split_complex_view_mut() { let eye_scalar = Array2::::eye(4); let eye_complex = Array2::>::eye(4); let mut a = Array2::>::zeros((4, 4)); - let Complex { mut re, im } = a.view_mut().split_re_im(); + let Complex { mut re, im } = a.view_mut().split_complex(); re.assign(&eye_scalar); assert_eq!(im.sum(), 0); assert_eq!(a, eye_complex); } #[test] -fn test_split_re_im_zerod() { +fn test_split_complex_zerod() { let mut a = Array0::from_elem((), Complex::new(42, 32)); - let Complex { re, im } = a.view().split_re_im(); + let Complex { re, im } = a.view().split_complex(); assert_eq!(re.get(()), Some(&42)); assert_eq!(im.get(()), Some(&32)); - let cmplx = a.view_mut().split_re_im(); + let cmplx = a.view_mut().split_complex(); cmplx.re.assign_to(cmplx.im); assert_eq!(a.get(()).unwrap().im, 42); } #[test] -fn test_split_re_im_permuted() { +fn test_split_complex_permuted() { let a = Array3::from_shape_fn((3, 4, 5), |(i, j, k)| { Complex::new(i * k + j, k) }); let permuted = a.view().permuted_axes([1,0,2]); - let Complex { re, im } = permuted.split_re_im(); + let Complex { re, im } = permuted.split_complex(); assert_eq!(re.get((3,2,4)).unwrap(), &11); assert_eq!(im.get((3,2,4)).unwrap(), &4); } #[test] -fn test_split_re_im_invert_axis() { +fn test_split_complex_invert_axis() { let mut a = Array::from_shape_fn((2, 3, 2), |(i, j, k)| Complex::new(i as f64 + j as f64, i as f64 + k as f64)); a.invert_axis(Axis(1)); - let cmplx = a.view().split_re_im(); + let cmplx = a.view().split_complex(); assert_eq!(cmplx.re, a.mapv(|z| z.re)); assert_eq!(cmplx.im, a.mapv(|z| z.im)); } From 25e286377479928aa82c8126296ef176dd51d89d Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 23 Nov 2021 20:56:05 +0100 Subject: [PATCH 428/651] doc: Give crate feature flags its own doc page Tighten up the crate feature list on the main page, and link over to a page with more full information - where we can explain more. This gives more room to the main page. --- src/doc/crate_feature_flags.rs | 35 ++++++++++++++++++++++++++++++++++ src/doc/mod.rs | 1 + src/lib.rs | 31 ++++++++---------------------- 3 files changed, 44 insertions(+), 23 deletions(-) create mode 100644 src/doc/crate_feature_flags.rs diff --git a/src/doc/crate_feature_flags.rs b/src/doc/crate_feature_flags.rs new file mode 100644 index 000000000..b396755c7 --- /dev/null +++ b/src/doc/crate_feature_flags.rs @@ -0,0 +1,35 @@ +//! Crate Feature Flags +//! +//! The following crate feature flags are available. They are configured in your +//! `Cargo.toml` where the dependency on `ndarray` is defined. +//! +//! ## `std` +//! - Rust standard library (enabled by default) +//! - This crate can be used without the standard library by disabling the +//! default `std` feature. To do so, use `default-features = false` in +//! your `Cargo.toml`. +//! - The `geomspace` `linspace` `logspace` `range` `std` `var` `var_axis` +//! and `std_axis` methods are only available when `std` is enabled. +//! +//! ## `serde` +//! - Enables serialization support for serde 1.x +//! +//! ## `rayon` +//! - Enables parallel iterators, parallelized methods, the [`parallel`] module and [`par_azip!`]. +//! - Implies std +//! +//! ## `approx` +//! - Enables implementations of traits from version 0.4 of the [`approx`] crate. +//! +//! ## `approx-0_5` +//! - Enables implementations of traits from version 0.5 of the [`approx`] crate. +//! +//! ## `blas` +//! - Enable transparent BLAS support for matrix multiplication. +//! Uses ``blas-src`` for pluggable backend, which needs to be configured +//! separately (see the README). +//! +//! ## `matrixmultiply-threading` +//! - Enable the ``threading`` feature in the matrixmultiply package +//! +//! [`parallel`]: crate::parallel diff --git a/src/doc/mod.rs b/src/doc/mod.rs index b98c9cab8..c0d7fab91 100644 --- a/src/doc/mod.rs +++ b/src/doc/mod.rs @@ -1,3 +1,4 @@ //! Standalone documentation pages. +pub mod crate_feature_flags; pub mod ndarray_for_numpy_users; diff --git a/src/lib.rs b/src/lib.rs index aadef4c9c..4a80043f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,30 +75,15 @@ //! ## Crate Feature Flags //! //! The following crate feature flags are available. They are configured in your -//! `Cargo.toml`. +//! `Cargo.toml`. See [`doc::crate_feature_flags`] for more information. //! -//! - `std` -//! - Rust standard library (enabled by default) -//! - This crate can be used without the standard library by disabling the -//! default `std` feature. To do so, use `default-features = false` in -//! your `Cargo.toml`. -//! - The `geomspace` `linspace` `logspace` `range` `std` `var` `var_axis` -//! and `std_axis` methods are only available when `std` is enabled. -//! - `serde` -//! - Enables serialization support for serde 1.x -//! - `rayon` -//! - Enables parallel iterators, parallelized methods, the [`parallel`] module and [`par_azip!`]. -//! - Implies std -//! - `approx` -//! - Enables implementations of traits from version 0.4 of the [`approx`] crate. -//! - `approx-0_5` -//! - Enables implementations of traits from version 0.5 of the [`approx`] crate. -//! - `blas` -//! - Enable transparent BLAS support for matrix multiplication. -//! Uses ``blas-src`` for pluggable backend, which needs to be configured -//! separately (see the README). -//! - `matrixmultiply-threading` -//! - Enable the ``threading`` feature in the matrixmultiply package +//! - `std`: Rust standard library-using functionality (enabled by default) +//! - `serde`: serialization support for serde 1.x +//! - `rayon`: Parallel iterators, parallelized methods, the [`parallel`] module and [`par_azip!`]. +//! - `approx` Implementations of traits from version 0.4 of the [`approx`] crate. +//! - `approx-0_5`: Implementations of traits from version 0.5 of the [`approx`] crate. +//! - `blas`: transparent BLAS support for matrix multiplication, needs configuration. +//! - `matrixmultiply-threading`: Use threading from `matrixmultiply`. //! //! ## Documentation //! From ebe9e98d43f8b68beae6f6b8d00fc227a108f0a8 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 23 Nov 2021 21:04:30 +0100 Subject: [PATCH 429/651] Fix typos in push/append docs There were some typos or forgotten words due to copying the same text to cover these methods, since they are all variants of the same operation. --- src/impl_owned_array.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index e58932747..8cfb82b55 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -120,7 +120,7 @@ impl
Array { /// Append a column to an array /// - /// The elements from `column` are cloned and added as a new row in the array. + /// The elements from `column` are cloned and added as a new column in the array. /// /// ***Errors*** with a shape error if the length of the column does not match the length of /// the columns in the array. @@ -380,7 +380,7 @@ impl Array /// appending to an array along the same axis. /// /// The amortized average complexity of the append, when appending along its growing axis, is - /// O(*m*) where *m* is the length of the row. + /// O(*m*) where *m* is the number of individual elements to append. /// /// The memory layout of the argument `array` does not matter to the same extent. /// @@ -438,14 +438,14 @@ impl Array /// appending to an array along the same axis. /// /// The amortized average complexity of the append, when appending along its growing axis, is - /// O(*m*) where *m* is the length of the row. + /// O(*m*) where *m* is the number of individual elements to append. /// /// The memory layout of the argument `array` does not matter to the same extent. /// /// ```rust /// use ndarray::{Array, ArrayView, array, Axis}; /// - /// // create an empty array and append + /// // create an empty array and append two rows at a time /// let mut a = Array::zeros((0, 4)); /// let ones = ArrayView::from(&[1.; 8]).into_shape((2, 4)).unwrap(); /// let zeros = ArrayView::from(&[0.; 8]).into_shape((2, 4)).unwrap(); From dca300ae2bc74c2725c4834eac18fa6811f02945 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 23 Nov 2021 21:08:03 +0100 Subject: [PATCH 430/651] doc: Slight tweaks to the introduction paragraphs of ArrayBase --- src/lib.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4a80043f1..a8ee9ee6d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -231,9 +231,13 @@ pub type Ixs = isize; /// An *n*-dimensional array. /// -/// The array is a general container of elements. It cannot grow or shrink (with some exceptions), -/// but can be sliced into subsets of its data. -/// The array supports arithmetic operations by applying them elementwise. +/// The array is a general container of elements. +/// The array supports arithmetic operations by applying them elementwise, if the +/// elements are numeric, but it supports non-numeric elements too. +/// +/// The arrays rarely grow or shrink, since those operations can be costly. On +/// the other hand there is a rich set of methods and operations for taking views, +/// slices, and making traversals over one or more arrays. /// /// In *n*-dimensional we include for example 1-dimensional rows or columns, /// 2-dimensional matrices, and higher dimensional arrays. If the array has *n* @@ -244,7 +248,7 @@ pub type Ixs = isize; /// /// Type aliases [`Array`], [`ArcArray`], [`CowArray`], [`ArrayView`], and /// [`ArrayViewMut`] refer to `ArrayBase` with different types for the data -/// container. +/// container: arrays with different kinds of ownership or different kinds of array views. /// /// ## Contents /// From 075199dcd127fcb89ec02ef54c7139cfb280b914 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 23 Nov 2021 21:22:23 +0100 Subject: [PATCH 431/651] 0.15.4 --- Cargo.toml | 2 +- RELEASES.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 65f3d3693..a9885f4a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ndarray" -version = "0.15.3" +version = "0.15.4" edition = "2018" authors = [ "Ulrik Sverdrup \"bluss\"", diff --git a/RELEASES.md b/RELEASES.md index c0eab1384..99ec442ef 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,8 @@ -Version 0.15.4 (2021-11-xx) +Version 0.15.4 (2021-11-23) =========================== +The Dr. Turner release 🚀 + New features ------------ From 5d30944b94f8d947dc80c6e80169f164b4ea1989 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 26 Nov 2021 19:52:05 +0100 Subject: [PATCH 432/651] doc: Elaborate nrows/ncols docs Use a 3 x 2 array and show other ways of accessing axis lengths. Fixes #1120 --- src/impl_2d.rs | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/impl_2d.rs b/src/impl_2d.rs index 6e2cfa2c7..9af833103 100644 --- a/src/impl_2d.rs +++ b/src/impl_2d.rs @@ -50,9 +50,19 @@ where /// Return the number of rows (length of `Axis(0)`) in the two-dimensional array. /// /// ``` - /// use ndarray::array; - /// let array = array![[1., 2.], [3., 4.]]; - /// assert_eq!(array.nrows(), 2usize); + /// use ndarray::{array, Axis}; + /// + /// let array = array![[1., 2.], + /// [3., 4.], + /// [5., 6.]]; + /// assert_eq!(array.nrows(), 3); + /// + /// // equivalent ways of getting the dimensions + /// // get nrows, ncols by using dim: + /// let (m, n) = array.dim(); + /// assert_eq!(m, array.nrows()); + /// // get length of any particular axis with .len_of() + /// assert_eq!(m, array.len_of(Axis(0))); /// ``` pub fn nrows(&self) -> usize { self.len_of(Axis(0)) @@ -94,9 +104,19 @@ where /// Return the number of columns (length of `Axis(1)`) in the two-dimensional array. /// /// ``` - /// use ndarray::array; - /// let array = array![[1., 2.], [3., 4.]]; - /// assert_eq!(array.ncols(), 2usize); + /// use ndarray::{array, Axis}; + /// + /// let array = array![[1., 2.], + /// [3., 4.], + /// [5., 6.]]; + /// assert_eq!(array.ncols(), 2); + /// + /// // equivalent ways of getting the dimensions + /// // get nrows, ncols by using dim: + /// let (m, n) = array.dim(); + /// assert_eq!(n, array.ncols()); + /// // get length of any particular axis with .len_of() + /// assert_eq!(n, array.len_of(Axis(1))); /// ``` pub fn ncols(&self) -> usize { self.len_of(Axis(1)) From 4c19f96cab08d8349cb3d734587fa532fefd4ed9 Mon Sep 17 00:00:00 2001 From: YuhanLiin Date: Mon, 12 Apr 2021 21:19:31 -0400 Subject: [PATCH 433/651] Add IntoNdProducer impl for &[T; N] Add From impl to convert from 2D slices to 2D views Move NdIndex implementations for arrays out of macros Remove FixedInitializer trait Make bounds checks in aview2 and aview_mut2 conditional on slices of ZSTs Refactor aview2 and aview_mut2 implementations into From --- .github/workflows/ci.yml | 2 +- src/arraytraits.rs | 58 +++++++++++++++-- src/dimension/ndindex.rs | 102 ++++++++++++++++------------- src/free_functions.rs | 135 +++++++-------------------------------- src/zip/ndproducer.rs | 22 ++++++- tests/array.rs | 88 ++++++++----------------- 6 files changed, 184 insertions(+), 223 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a44568ae8..1c76bd9d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: - stable - beta - nightly - - 1.49.0 # MSRV + - 1.51.0 # MSRV steps: - uses: actions/checkout@v2 diff --git a/src/arraytraits.rs b/src/arraytraits.rs index a7f22c1f7..0bec319e6 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -6,17 +6,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::hash; -use std::iter::FromIterator; +use alloc::boxed::Box; +use alloc::vec::Vec; use std::iter::IntoIterator; use std::mem; use std::ops::{Index, IndexMut}; -use alloc::boxed::Box; -use alloc::vec::Vec; +use std::{hash, mem::size_of}; +use std::{iter::FromIterator, slice}; -use crate::imp_prelude::*; use crate::iter::{Iter, IterMut}; use crate::NdIndex; +use crate::{dimension, imp_prelude::*}; use crate::numeric_util; use crate::{FoldWhile, Zip}; @@ -323,6 +323,30 @@ where } } +/// Implementation of ArrayView2::from(&S) where S is a slice to a 2D array +/// +/// **Panics** if the product of non-zero axis lengths overflows `isize` (This can only occur if A +/// is zero-sized because slices cannot contain more than `isize::MAX` number of bytes). +impl<'a, A, const N: usize> From<&'a [[A; N]]> for ArrayView<'a, A, Ix2> { + /// Create a two-dimensional read-only array view of the data in `slice` + fn from(xs: &'a [[A; N]]) -> Self { + let cols = N; + let rows = xs.len(); + let dim = Ix2(rows, cols); + if size_of::() == 0 { + dimension::size_of_shape_checked(&dim) + .expect("Product of non-zero axis lengths must not overflow isize."); + } + + // `cols * rows` is guaranteed to fit in `isize` because we checked that it fits in + // `isize::MAX` + unsafe { + let data = slice::from_raw_parts(xs.as_ptr() as *const A, cols * rows); + ArrayView::from_shape_ptr(dim, data.as_ptr()) + } + } +} + /// Implementation of `ArrayView::from(&A)` where `A` is an array. impl<'a, A, S, D> From<&'a ArrayBase> for ArrayView<'a, A, D> where @@ -355,6 +379,30 @@ where } } +/// Implementation of ArrayViewMut2::from(&S) where S is a slice to a 2D array +/// +/// **Panics** if the product of non-zero axis lengths overflows `isize` (This can only occur if A +/// is zero-sized because slices cannot contain more than `isize::MAX` number of bytes). +impl<'a, A, const N: usize> From<&'a mut [[A; N]]> for ArrayViewMut<'a, A, Ix2> { + /// Create a two-dimensional read-write array view of the data in `slice` + fn from(xs: &'a mut [[A; N]]) -> Self { + let cols = N; + let rows = xs.len(); + let dim = Ix2(rows, cols); + if size_of::() == 0 { + dimension::size_of_shape_checked(&dim) + .expect("Product of non-zero axis lengths must not overflow isize."); + } + + // `cols * rows` is guaranteed to fit in `isize` because we checked that it fits in + // `isize::MAX` + unsafe { + let data = slice::from_raw_parts_mut(xs.as_mut_ptr() as *mut A, cols * rows); + ArrayViewMut::from_shape_ptr(dim, data.as_mut_ptr()) + } + } +} + /// Implementation of `ArrayViewMut::from(&mut A)` where `A` is an array. impl<'a, A, S, D> From<&'a mut ArrayBase> for ArrayViewMut<'a, A, D> where diff --git a/src/dimension/ndindex.rs b/src/dimension/ndindex.rs index 810a5b097..718ee059b 100644 --- a/src/dimension/ndindex.rs +++ b/src/dimension/ndindex.rs @@ -140,50 +140,6 @@ macro_rules! ndindex_with_array { 0 } } - - // implement NdIndex for Dim<[Ix; 2]> and so on - unsafe impl NdIndex for Dim<[Ix; $n]> { - #[inline] - fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { - debug_assert_eq!(strides.ndim(), $n, - "Attempted to index with {:?} in array with {} axes", - self, strides.ndim()); - stride_offset_checked(dim.ix(), strides.ix(), self.ix()) - } - - #[inline] - fn index_unchecked(&self, strides: &IxDyn) -> isize { - debug_assert_eq!(strides.ndim(), $n, - "Attempted to index with {:?} in array with {} axes", - self, strides.ndim()); - $( - stride_offset(get!(self, $index), get!(strides, $index)) + - )* - 0 - } - } - - // implement NdIndex for [Ix; 2] and so on - unsafe impl NdIndex for [Ix; $n] { - #[inline] - fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { - debug_assert_eq!(strides.ndim(), $n, - "Attempted to index with {:?} in array with {} axes", - self, strides.ndim()); - stride_offset_checked(dim.ix(), strides.ix(), self) - } - - #[inline] - fn index_unchecked(&self, strides: &IxDyn) -> isize { - debug_assert_eq!(strides.ndim(), $n, - "Attempted to index with {:?} in array with {} axes", - self, strides.ndim()); - $( - stride_offset(self[$index], get!(strides, $index)) + - )* - 0 - } - } )+ }; } @@ -198,6 +154,64 @@ ndindex_with_array! { [6, Ix6 0 1 2 3 4 5] } +// implement NdIndex for Dim<[Ix; 2]> and so on +unsafe impl NdIndex for Dim<[Ix; N]> { + #[inline] + fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { + debug_assert_eq!( + strides.ndim(), + N, + "Attempted to index with {:?} in array with {} axes", + self, + strides.ndim() + ); + stride_offset_checked(dim.ix(), strides.ix(), self.ix()) + } + + #[inline] + fn index_unchecked(&self, strides: &IxDyn) -> isize { + debug_assert_eq!( + strides.ndim(), + N, + "Attempted to index with {:?} in array with {} axes", + self, + strides.ndim() + ); + (0..N) + .map(|i| stride_offset(get!(self, i), get!(strides, i))) + .sum() + } +} + +// implement NdIndex for [Ix; 2] and so on +unsafe impl NdIndex for [Ix; N] { + #[inline] + fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { + debug_assert_eq!( + strides.ndim(), + N, + "Attempted to index with {:?} in array with {} axes", + self, + strides.ndim() + ); + stride_offset_checked(dim.ix(), strides.ix(), self) + } + + #[inline] + fn index_unchecked(&self, strides: &IxDyn) -> isize { + debug_assert_eq!( + strides.ndim(), + N, + "Attempted to index with {:?} in array with {} axes", + self, + strides.ndim() + ); + (0..N) + .map(|i| stride_offset(self[i], get!(strides, i))) + .sum() + } +} + impl<'a> IntoDimension for &'a [Ix] { type Dim = IxDyn; fn into_dimension(self) -> Self::Dim { diff --git a/src/free_functions.rs b/src/free_functions.rs index 2b30e0bd3..156eee6b9 100644 --- a/src/free_functions.rs +++ b/src/free_functions.rs @@ -6,10 +6,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::mem::{forget, size_of}; -use alloc::slice; use alloc::vec; use alloc::vec::Vec; +use std::mem::{forget, size_of}; use crate::imp_prelude::*; use crate::{dimension, ArcArray1, ArcArray2}; @@ -87,26 +86,10 @@ pub fn aview1(xs: &[A]) -> ArrayView1<'_, A> { /// Create a two-dimensional array view with elements borrowing `xs`. /// -/// **Panics** if the product of non-zero axis lengths overflows `isize`. (This -/// can only occur when `V` is zero-sized.) -pub fn aview2>(xs: &[V]) -> ArrayView2<'_, A> { - let cols = V::len(); - let rows = xs.len(); - let dim = Ix2(rows, cols); - if size_of::() == 0 { - dimension::size_of_shape_checked(&dim) - .expect("Product of non-zero axis lengths must not overflow isize."); - } - // `rows` is guaranteed to fit in `isize` because we've checked the ZST - // case and slices never contain > `isize::MAX` bytes. `cols` is guaranteed - // to fit in `isize` because `FixedInitializer` is not implemented for any - // array lengths > `isize::MAX`. `cols * rows` is guaranteed to fit in - // `isize` because we've checked the ZST case and slices never contain > - // `isize::MAX` bytes. - unsafe { - let data = slice::from_raw_parts(xs.as_ptr() as *const A, cols * rows); - ArrayView::from_shape_ptr(dim, data.as_ptr()) - } +/// **Panics** if the product of non-zero axis lengths overflows `isize` (This can only occur if A +/// is zero-sized because slices cannot contain more than `isize::MAX` number of bytes). +pub fn aview2(xs: &[[A; N]]) -> ArrayView2<'_, A> { + ArrayView2::from(xs) } /// Create a one-dimensional read-write array view with elements borrowing `xs`. @@ -127,16 +110,15 @@ pub fn aview_mut1(xs: &mut [A]) -> ArrayViewMut1<'_, A> { /// Create a two-dimensional read-write array view with elements borrowing `xs`. /// -/// **Panics** if the product of non-zero axis lengths overflows `isize`. (This -/// can only occur when `V` is zero-sized.) +/// **Panics** if the product of non-zero axis lengths overflows `isize` (This can only occur if A +/// is zero-sized because slices cannot contain more than `isize::MAX` number of bytes). /// /// # Example /// /// ``` /// use ndarray::aview_mut2; /// -/// // The inner (nested) array must be of length 1 to 16, but the outer -/// // can be of any length. +/// // The inner (nested) and outer arrays can be of any length. /// let mut data = [[0.; 2]; 128]; /// { /// // Make a 128 x 2 mut array view then turn it into 2 x 128 @@ -148,57 +130,10 @@ pub fn aview_mut1(xs: &mut [A]) -> ArrayViewMut1<'_, A> { /// // look at the start of the result /// assert_eq!(&data[..3], [[1., -1.], [1., -1.], [1., -1.]]); /// ``` -pub fn aview_mut2>(xs: &mut [V]) -> ArrayViewMut2<'_, A> { - let cols = V::len(); - let rows = xs.len(); - let dim = Ix2(rows, cols); - if size_of::() == 0 { - dimension::size_of_shape_checked(&dim) - .expect("Product of non-zero axis lengths must not overflow isize."); - } - // `rows` is guaranteed to fit in `isize` because we've checked the ZST - // case and slices never contain > `isize::MAX` bytes. `cols` is guaranteed - // to fit in `isize` because `FixedInitializer` is not implemented for any - // array lengths > `isize::MAX`. `cols * rows` is guaranteed to fit in - // `isize` because we've checked the ZST case and slices never contain > - // `isize::MAX` bytes. - unsafe { - let data = slice::from_raw_parts_mut(xs.as_mut_ptr() as *mut A, cols * rows); - ArrayViewMut::from_shape_ptr(dim, data.as_mut_ptr()) - } +pub fn aview_mut2(xs: &mut [[A; N]]) -> ArrayViewMut2<'_, A> { + ArrayViewMut2::from(xs) } -/// Fixed-size array used for array initialization -#[allow(clippy::missing_safety_doc)] // Should not be implemented downstream and to be deprecated. -pub unsafe trait FixedInitializer { - type Elem; - fn as_init_slice(&self) -> &[Self::Elem]; - fn len() -> usize; -} - -macro_rules! impl_arr_init { - (__impl $n: expr) => ( - unsafe impl FixedInitializer for [T; $n] { - type Elem = T; - fn as_init_slice(&self) -> &[T] { self } - fn len() -> usize { $n } - } - ); - () => (); - ($n: expr, $($m:expr,)*) => ( - impl_arr_init!(__impl $n); - impl_arr_init!($($m,)*); - ) - -} - -// For implementors: If you ever implement `FixedInitializer` for array lengths -// > `isize::MAX` (e.g. once Rust adds const generics), you must update -// `aview2` and `aview_mut2` to perform the necessary checks. In particular, -// the assumption that `cols` can never exceed `isize::MAX` would be incorrect. -// (Consider e.g. `let xs: &[[i32; ::std::usize::MAX]] = &[]`.) -impl_arr_init!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,); - /// Create a two-dimensional array with elements from `xs`. /// /// ``` @@ -210,22 +145,16 @@ impl_arr_init!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,); /// a.shape() == [2, 3] /// ); /// ``` -pub fn arr2>(xs: &[V]) -> Array2 -where - V: Clone, -{ +pub fn arr2(xs: &[[A; N]]) -> Array2 { Array2::from(xs.to_vec()) } -impl From> for Array2 -where - V: FixedInitializer, -{ +impl From> for Array2 { /// Converts the `Vec` of arrays to an owned 2-D array. /// /// **Panics** if the product of non-zero axis lengths overflows `isize`. - fn from(mut xs: Vec) -> Self { - let dim = Ix2(xs.len(), V::len()); + fn from(mut xs: Vec<[A; N]>) -> Self { + let dim = Ix2(xs.len(), N); let ptr = xs.as_mut_ptr(); let cap = xs.capacity(); let expand_len = dimension::size_of_shape_checked(&dim) @@ -234,12 +163,12 @@ where unsafe { let v = if size_of::() == 0 { Vec::from_raw_parts(ptr as *mut A, expand_len, expand_len) - } else if V::len() == 0 { + } else if N == 0 { Vec::new() } else { // Guaranteed not to overflow in this case since A is non-ZST // and Vec never allocates more than isize bytes. - let expand_cap = cap * V::len(); + let expand_cap = cap * N; Vec::from_raw_parts(ptr as *mut A, expand_len, expand_cap) }; ArrayBase::from_shape_vec_unchecked(dim, v) @@ -247,16 +176,12 @@ where } } -impl From> for Array3 -where - V: FixedInitializer, - U: FixedInitializer, -{ +impl From> for Array3 { /// Converts the `Vec` of arrays to an owned 3-D array. /// /// **Panics** if the product of non-zero axis lengths overflows `isize`. - fn from(mut xs: Vec) -> Self { - let dim = Ix3(xs.len(), V::len(), U::len()); + fn from(mut xs: Vec<[[A; M]; N]>) -> Self { + let dim = Ix3(xs.len(), N, M); let ptr = xs.as_mut_ptr(); let cap = xs.capacity(); let expand_len = dimension::size_of_shape_checked(&dim) @@ -265,12 +190,12 @@ where unsafe { let v = if size_of::() == 0 { Vec::from_raw_parts(ptr as *mut A, expand_len, expand_len) - } else if V::len() == 0 || U::len() == 0 { + } else if N == 0 || M == 0 { Vec::new() } else { // Guaranteed not to overflow in this case since A is non-ZST // and Vec never allocates more than isize bytes. - let expand_cap = cap * V::len() * U::len(); + let expand_cap = cap * N * M; Vec::from_raw_parts(ptr as *mut A, expand_len, expand_cap) }; ArrayBase::from_shape_vec_unchecked(dim, v) @@ -280,7 +205,7 @@ where /// Create a two-dimensional array with elements from `xs`. /// -pub fn rcarr2>(xs: &[V]) -> ArcArray2 { +pub fn rcarr2(xs: &[[A; N]]) -> ArcArray2 { arr2(xs).into_shared() } @@ -301,23 +226,11 @@ pub fn rcarr2>(xs: &[V]) -> ArcA /// a.shape() == [3, 2, 2] /// ); /// ``` -pub fn arr3, U: FixedInitializer>( - xs: &[V], -) -> Array3 -where - V: Clone, - U: Clone, -{ +pub fn arr3(xs: &[[[A; M]; N]]) -> Array3 { Array3::from(xs.to_vec()) } /// Create a three-dimensional array with elements from `xs`. -pub fn rcarr3, U: FixedInitializer>( - xs: &[V], -) -> ArcArray -where - V: Clone, - U: Clone, -{ +pub fn rcarr3(xs: &[[[A; M]; N]]) -> ArcArray { arr3(xs).into_shared() } diff --git a/src/zip/ndproducer.rs b/src/zip/ndproducer.rs index 619fadcc3..ca7e75fd3 100644 --- a/src/zip/ndproducer.rs +++ b/src/zip/ndproducer.rs @@ -1,4 +1,3 @@ - use crate::imp_prelude::*; use crate::Layout; use crate::NdIndex; @@ -168,6 +167,26 @@ impl<'a, A: 'a> IntoNdProducer for &'a mut [A] { } } +/// A one-dimensional array is a one-dimensional producer +impl<'a, A: 'a, const N: usize> IntoNdProducer for &'a [A; N] { + type Item = ::Item; + type Dim = Ix1; + type Output = ArrayView1<'a, A>; + fn into_producer(self) -> Self::Output { + <_>::from(self) + } +} + +/// A mutable one-dimensional array is a mutable one-dimensional producer +impl<'a, A: 'a, const N: usize> IntoNdProducer for &'a mut [A; N] { + type Item = ::Item; + type Dim = Ix1; + type Output = ArrayViewMut1<'a, A>; + fn into_producer(self) -> Self::Output { + <_>::from(self) + } +} + /// A Vec is a one-dimensional producer impl<'a, A: 'a> IntoNdProducer for &'a Vec { type Item = ::Item; @@ -399,4 +418,3 @@ impl NdProducer for RawArrayViewMut { self.split_at(axis, index) } } - diff --git a/tests/array.rs b/tests/array.rs index a16e75fd0..821246be1 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -12,7 +12,6 @@ use defmac::defmac; use itertools::{zip, Itertools}; use ndarray::prelude::*; use ndarray::{arr3, rcarr2}; -use ndarray::indices; use ndarray::{Slice, SliceInfo, SliceInfoElem}; use num_complex::Complex; use std::convert::TryFrom; @@ -731,7 +730,7 @@ fn diag() { let a = arr2(&[[1., 2., 3.0f32], [0., 0., 0.]]); let d = a.view().into_diag(); assert_eq!(d.dim(), 2); - let d = arr2::(&[[]]).into_diag(); + let d = arr2::(&[[]]).into_diag(); assert_eq!(d.dim(), 0); let d = ArcArray::::zeros(()).into_diag(); assert_eq!(d.dim(), 1); @@ -960,7 +959,7 @@ fn zero_axes() { a.map_inplace(|_| panic!()); a.for_each(|_| panic!()); println!("{:?}", a); - let b = arr2::(&[[], [], [], []]); + let b = arr2::(&[[], [], [], []]); println!("{:?}\n{:?}", b.shape(), b); // we can even get a subarray of b @@ -2071,9 +2070,8 @@ fn test_view_from_shape_ptr() { #[test] fn test_view_from_shape_ptr_deny_neg_strides() { let data = [0, 1, 2, 3, 4, 5]; - let _view = unsafe { - ArrayView::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_ptr()) - }; + let _view = + unsafe { ArrayView::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_ptr()) }; } #[should_panic(expected = "Unsupported")] @@ -2466,74 +2464,48 @@ mod array_cow_tests { #[test] fn test_remove_index() { - let mut a = arr2(&[[1, 2, 3], - [4, 5, 6], - [7, 8, 9], - [10,11,12]]); + let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); a.remove_index(Axis(0), 1); a.remove_index(Axis(1), 2); assert_eq!(a.shape(), &[3, 2]); - assert_eq!(a, - array![[1, 2], - [7, 8], - [10,11]]); - - let mut a = arr2(&[[1, 2, 3], - [4, 5, 6], - [7, 8, 9], - [10,11,12]]); + assert_eq!(a, array![[1, 2], [7, 8], [10, 11]]); + + let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); a.invert_axis(Axis(0)); a.remove_index(Axis(0), 1); a.remove_index(Axis(1), 2); assert_eq!(a.shape(), &[3, 2]); - assert_eq!(a, - array![[10,11], - [4, 5], - [1, 2]]); + assert_eq!(a, array![[10, 11], [4, 5], [1, 2]]); a.remove_index(Axis(1), 1); assert_eq!(a.shape(), &[3, 1]); - assert_eq!(a, - array![[10], - [4], - [1]]); + assert_eq!(a, array![[10], [4], [1]]); a.remove_index(Axis(1), 0); assert_eq!(a.shape(), &[3, 0]); - assert_eq!(a, - array![[], - [], - []]); + assert_eq!(a, array![[], [], []]); } -#[should_panic(expected="must be less")] +#[should_panic(expected = "must be less")] #[test] fn test_remove_index_oob1() { - let mut a = arr2(&[[1, 2, 3], - [4, 5, 6], - [7, 8, 9], - [10,11,12]]); + let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); a.remove_index(Axis(0), 4); } -#[should_panic(expected="must be less")] +#[should_panic(expected = "must be less")] #[test] fn test_remove_index_oob2() { let mut a = array![[10], [4], [1]]; a.remove_index(Axis(1), 0); assert_eq!(a.shape(), &[3, 0]); - assert_eq!(a, - array![[], - [], - []]); + assert_eq!(a, array![[], [], []]); a.remove_index(Axis(0), 1); // ok - assert_eq!(a, - array![[], - []]); + assert_eq!(a, array![[], []]); a.remove_index(Axis(1), 0); // oob } -#[should_panic(expected="index out of bounds")] +#[should_panic(expected = "index out of bounds")] #[test] fn test_remove_index_oob3() { let mut a = array![[10], [4], [1]]; @@ -2552,14 +2524,10 @@ fn test_split_complex_view() { #[test] fn test_split_complex_view_roundtrip() { - let a_re = Array3::from_shape_fn((3,1,5), |(i, j, _k)| { - i * j - }); - let a_im = Array3::from_shape_fn((3,1,5), |(_i, _j, k)| { - k - }); - let a = Array3::from_shape_fn((3,1,5), |(i,j,k)| { - Complex::new(a_re[[i,j,k]], a_im[[i,j,k]]) + let a_re = Array3::from_shape_fn((3, 1, 5), |(i, j, _k)| i * j); + let a_im = Array3::from_shape_fn((3, 1, 5), |(_i, _j, k)| k); + let a = Array3::from_shape_fn((3, 1, 5), |(i, j, k)| { + Complex::new(a_re[[i, j, k]], a_im[[i, j, k]]) }); let Complex { re, im } = a.view().split_complex(); assert_eq!(a_re, re); @@ -2590,18 +2558,18 @@ fn test_split_complex_zerod() { #[test] fn test_split_complex_permuted() { - let a = Array3::from_shape_fn((3, 4, 5), |(i, j, k)| { - Complex::new(i * k + j, k) - }); - let permuted = a.view().permuted_axes([1,0,2]); + let a = Array3::from_shape_fn((3, 4, 5), |(i, j, k)| Complex::new(i * k + j, k)); + let permuted = a.view().permuted_axes([1, 0, 2]); let Complex { re, im } = permuted.split_complex(); - assert_eq!(re.get((3,2,4)).unwrap(), &11); - assert_eq!(im.get((3,2,4)).unwrap(), &4); + assert_eq!(re.get((3, 2, 4)).unwrap(), &11); + assert_eq!(im.get((3, 2, 4)).unwrap(), &4); } #[test] fn test_split_complex_invert_axis() { - let mut a = Array::from_shape_fn((2, 3, 2), |(i, j, k)| Complex::new(i as f64 + j as f64, i as f64 + k as f64)); + let mut a = Array::from_shape_fn((2, 3, 2), |(i, j, k)| { + Complex::new(i as f64 + j as f64, i as f64 + k as f64) + }); a.invert_axis(Axis(1)); let cmplx = a.view().split_complex(); assert_eq!(cmplx.re, a.mapv(|z| z.re)); From ad66360f09874931ee19bf1356f4076c31c39b49 Mon Sep 17 00:00:00 2001 From: YuhanLiin Date: Fri, 3 Dec 2021 21:30:36 -0500 Subject: [PATCH 434/651] Fix imports --- tests/array.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/array.rs b/tests/array.rs index 821246be1..c4b590b4a 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -11,8 +11,7 @@ use approx::assert_relative_eq; use defmac::defmac; use itertools::{zip, Itertools}; use ndarray::prelude::*; -use ndarray::{arr3, rcarr2}; -use ndarray::{Slice, SliceInfo, SliceInfoElem}; +use ndarray::{arr3, indices, rcarr2, Slice, SliceInfo, SliceInfoElem}; use num_complex::Complex; use std::convert::TryFrom; From a1d268b4d243ca59e63277f055e2afaeb9d11d75 Mon Sep 17 00:00:00 2001 From: YuhanLiin Date: Sat, 4 Dec 2021 13:16:21 -0500 Subject: [PATCH 435/651] Fix arraytraits imports --- src/arraytraits.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 0bec319e6..6a4fd1137 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -14,12 +14,12 @@ use std::ops::{Index, IndexMut}; use std::{hash, mem::size_of}; use std::{iter::FromIterator, slice}; -use crate::iter::{Iter, IterMut}; -use crate::NdIndex; -use crate::{dimension, imp_prelude::*}; - -use crate::numeric_util; -use crate::{FoldWhile, Zip}; +use crate::imp_prelude::*; +use crate::{ + dimension, + iter::{Iter, IterMut}, + numeric_util, FoldWhile, NdIndex, Zip, +}; #[cold] #[inline(never)] From 75a27e515d494ac72b3a909bca45401b70ea18e6 Mon Sep 17 00:00:00 2001 From: YuhanLiin Date: Sat, 4 Dec 2021 13:31:01 -0500 Subject: [PATCH 436/651] Undo formatting changes in tests --- tests/array.rs | 87 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 27 deletions(-) diff --git a/tests/array.rs b/tests/array.rs index c4b590b4a..e3922ea8d 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -11,7 +11,9 @@ use approx::assert_relative_eq; use defmac::defmac; use itertools::{zip, Itertools}; use ndarray::prelude::*; -use ndarray::{arr3, indices, rcarr2, Slice, SliceInfo, SliceInfoElem}; +use ndarray::{arr3, rcarr2}; +use ndarray::indices; +use ndarray::{Slice, SliceInfo, SliceInfoElem}; use num_complex::Complex; use std::convert::TryFrom; @@ -2069,8 +2071,9 @@ fn test_view_from_shape_ptr() { #[test] fn test_view_from_shape_ptr_deny_neg_strides() { let data = [0, 1, 2, 3, 4, 5]; - let _view = - unsafe { ArrayView::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_ptr()) }; + let _view = unsafe { + ArrayView::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_ptr()) + }; } #[should_panic(expected = "Unsupported")] @@ -2463,48 +2466,74 @@ mod array_cow_tests { #[test] fn test_remove_index() { - let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); + let mut a = arr2(&[[1, 2, 3], + [4, 5, 6], + [7, 8, 9], + [10,11,12]]); a.remove_index(Axis(0), 1); a.remove_index(Axis(1), 2); assert_eq!(a.shape(), &[3, 2]); - assert_eq!(a, array![[1, 2], [7, 8], [10, 11]]); - - let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); + assert_eq!(a, + array![[1, 2], + [7, 8], + [10,11]]); + + let mut a = arr2(&[[1, 2, 3], + [4, 5, 6], + [7, 8, 9], + [10,11,12]]); a.invert_axis(Axis(0)); a.remove_index(Axis(0), 1); a.remove_index(Axis(1), 2); assert_eq!(a.shape(), &[3, 2]); - assert_eq!(a, array![[10, 11], [4, 5], [1, 2]]); + assert_eq!(a, + array![[10,11], + [4, 5], + [1, 2]]); a.remove_index(Axis(1), 1); assert_eq!(a.shape(), &[3, 1]); - assert_eq!(a, array![[10], [4], [1]]); + assert_eq!(a, + array![[10], + [4], + [1]]); a.remove_index(Axis(1), 0); assert_eq!(a.shape(), &[3, 0]); - assert_eq!(a, array![[], [], []]); + assert_eq!(a, + array![[], + [], + []]); } -#[should_panic(expected = "must be less")] +#[should_panic(expected="must be less")] #[test] fn test_remove_index_oob1() { - let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); + let mut a = arr2(&[[1, 2, 3], + [4, 5, 6], + [7, 8, 9], + [10,11,12]]); a.remove_index(Axis(0), 4); } -#[should_panic(expected = "must be less")] +#[should_panic(expected="must be less")] #[test] fn test_remove_index_oob2() { let mut a = array![[10], [4], [1]]; a.remove_index(Axis(1), 0); assert_eq!(a.shape(), &[3, 0]); - assert_eq!(a, array![[], [], []]); + assert_eq!(a, + array![[], + [], + []]); a.remove_index(Axis(0), 1); // ok - assert_eq!(a, array![[], []]); + assert_eq!(a, + array![[], + []]); a.remove_index(Axis(1), 0); // oob } -#[should_panic(expected = "index out of bounds")] +#[should_panic(expected="index out of bounds")] #[test] fn test_remove_index_oob3() { let mut a = array![[10], [4], [1]]; @@ -2523,10 +2552,14 @@ fn test_split_complex_view() { #[test] fn test_split_complex_view_roundtrip() { - let a_re = Array3::from_shape_fn((3, 1, 5), |(i, j, _k)| i * j); - let a_im = Array3::from_shape_fn((3, 1, 5), |(_i, _j, k)| k); - let a = Array3::from_shape_fn((3, 1, 5), |(i, j, k)| { - Complex::new(a_re[[i, j, k]], a_im[[i, j, k]]) + let a_re = Array3::from_shape_fn((3,1,5), |(i, j, _k)| { + i * j + }); + let a_im = Array3::from_shape_fn((3,1,5), |(_i, _j, k)| { + k + }); + let a = Array3::from_shape_fn((3,1,5), |(i,j,k)| { + Complex::new(a_re[[i,j,k]], a_im[[i,j,k]]) }); let Complex { re, im } = a.view().split_complex(); assert_eq!(a_re, re); @@ -2557,18 +2590,18 @@ fn test_split_complex_zerod() { #[test] fn test_split_complex_permuted() { - let a = Array3::from_shape_fn((3, 4, 5), |(i, j, k)| Complex::new(i * k + j, k)); - let permuted = a.view().permuted_axes([1, 0, 2]); + let a = Array3::from_shape_fn((3, 4, 5), |(i, j, k)| { + Complex::new(i * k + j, k) + }); + let permuted = a.view().permuted_axes([1,0,2]); let Complex { re, im } = permuted.split_complex(); - assert_eq!(re.get((3, 2, 4)).unwrap(), &11); - assert_eq!(im.get((3, 2, 4)).unwrap(), &4); + assert_eq!(re.get((3,2,4)).unwrap(), &11); + assert_eq!(im.get((3,2,4)).unwrap(), &4); } #[test] fn test_split_complex_invert_axis() { - let mut a = Array::from_shape_fn((2, 3, 2), |(i, j, k)| { - Complex::new(i as f64 + j as f64, i as f64 + k as f64) - }); + let mut a = Array::from_shape_fn((2, 3, 2), |(i, j, k)| Complex::new(i as f64 + j as f64, i as f64 + k as f64)); a.invert_axis(Axis(1)); let cmplx = a.view().split_complex(); assert_eq!(cmplx.re, a.mapv(|z| z.re)); From 0188b560e57714f575351d9f5770332b8b663981 Mon Sep 17 00:00:00 2001 From: Simon Gasse Date: Sun, 28 Nov 2021 23:39:07 +0100 Subject: [PATCH 437/651] Add examples for type conversion - Add `type_conversion.rs` to illustrate some common conversions. - Update the documentation for numpy users. --- examples/type_conversion.rs | 114 +++++++++++++++++++++++++ src/doc/ndarray_for_numpy_users/mod.rs | 95 +++++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 examples/type_conversion.rs diff --git a/examples/type_conversion.rs b/examples/type_conversion.rs new file mode 100644 index 000000000..57c6393de --- /dev/null +++ b/examples/type_conversion.rs @@ -0,0 +1,114 @@ +#[cfg(feature = "approx")] +use {approx::assert_abs_diff_eq, ndarray::prelude::*, std::convert::TryFrom}; + +#[cfg(feature = "approx")] +fn main() { + // Converting an array from one datatype to another is implemented with the + // `ArrayBase::mapv()` function. We pass a closure that is applied to each + // element independently. This allows for more control and flexiblity in + // converting types. + // + // Below, we illustrate four different approaches for the actual conversion + // in the closure. + // - `From` ensures lossless conversions known at compile time and is the + // best default choice. + // - `TryFrom` either converts data losslessly or panics, ensuring that the + // rest of the program does not continue with unexpected data. + // - `as` never panics and may silently convert in a lossy way, depending + // on the source and target datatypes. More details can be found in the + // reference: https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast + // - Using custom logic in the closure, e.g. to clip values or for NaN + // handling in floats. + // + // For a brush-up on casting between numeric types in Rust, refer to: + // https://doc.rust-lang.org/rust-by-example/types/cast.html + + // Infallible, lossless conversion with `From` + // The trait `std::convert::From` is only implemented for conversions that + // can be guaranteed to be lossless at compile time. This is the safest + // approach. + let a_u8: Array = array![[1, 2, 3], [4, 5, 6]]; + let a_f32 = a_u8.mapv(|element| f32::from(element)); + assert_abs_diff_eq!(a_f32, array![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]); + + // Fallible, lossless conversion with `TryFrom` + // `i8` numbers can be negative, in such a case, there is no perfect + // conversion to `u8` defined. In this example, all numbers are positive and + // in bounds and can be converted at runtime. But for unknown runtime input, + // this would panic with the message provided in `.expect()`. Note that you + // can also use `.unwrap()` to be more concise. + let a_i8: Array = array![120, 8, 0]; + let a_u8 = a_i8.mapv(|element| u8::try_from(element).expect("Could not convert i8 to u8")); + assert_eq!(a_u8, array![120u8, 8u8, 0u8]); + + // Unsigned to signed integer conversion with `as` + // A real-life example of this would be coordinates on a grid. + // A `usize` value can be larger than what fits into a `isize`, therefore, + // it would be safer to use `TryFrom`. Nevertheless, `as` can be used for + // either simplicity or performance. + // The example includes `usize::MAX` to illustrate potentially undesired + // behavior. It will be interpreted as -1 (noop-casting + 2-complement), see + // https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast + let a_usize: Array = array![1, 2, 3, usize::MAX]; + let a_isize = a_usize.mapv(|element| element as isize); + assert_eq!(a_isize, array![1_isize, 2_isize, 3_isize, -1_isize]); + + // Simple upcasting with `as` + // Every `u8` fits perfectly into a `u32`, therefore this is a lossless + // conversion. + // Note that it is up to the programmer to ensure the validity of the + // conversion over the lifetime of a program. With type inference, subtle + // bugs can creep in since conversions with `as` will always compile, so a + // programmer might not notice that a prior lossless conversion became a + // lossy conversion. With `From`, this would be noticed at compile-time and + // with `TryFrom`, it would also be either handled or make the program + // panic. + let a_u8: Array = array![[1, 2, 3], [4, 5, 6]]; + let a_u32 = a_u8.mapv(|element| element as u32); + assert_eq!(a_u32, array![[1u32, 2u32, 3u32], [4u32, 5u32, 6u32]]); + + // Saturating cast with `as` + // The `as` keyword performs a *saturating cast* When casting floats to + // ints. This means that numbers which do not fit into the target datatype + // will silently be clipped to the maximum/minimum numbers. Since this is + // not obvious, we discourage the intentional use of casting with `as` with + // silent saturation and recommend a custom logic instead which makes the + // intent clear. + let a_f32: Array = array![ + 256.0, // saturated to 255 + 255.7, // saturated to 255 + 255.1, // saturated to 255 + 254.7, // rounded down to 254 by cutting the decimal part + 254.1, // rounded down to 254 by cutting the decimal part + -1.0, // saturated to 0 on the lower end + f32::INFINITY, // saturated to 255 + f32::NAN, // converted to zero + ]; + let a_u8 = a_f32.mapv(|element| element as u8); + assert_eq!(a_u8, array![255, 255, 255, 254, 254, 0, 255, 0]); + + // Custom mapping logic + // Given that we pass a closure for the conversion, we can also define + // custom logic to e.g. replace NaN values and clip others. This also + // makes the intent clear. + let a_f32: Array = array![ + 270.0, // clipped to 200 + -1.2, // clipped to 0 + 4.7, // rounded up to 5 instead of just stripping decimals + f32::INFINITY, // clipped to 200 + f32::NAN, // replaced with upper bound 200 + ]; + let a_u8_custom = a_f32.mapv(|element| { + if element == f32::INFINITY || element.is_nan() { + return 200; + } + if let Some(std::cmp::Ordering::Less) = element.partial_cmp(&0.0) { + return 0; + } + 200.min(element.round() as u8) + }); + assert_eq!(a_u8_custom, array![200, 0, 5, 200, 200]); +} + +#[cfg(not(feature = "approx"))] +fn main() {} diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index f9c05c612..eff60cbea 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -524,6 +524,101 @@ //! //!
//! -//! In `ndarray`, all arrays are instances of [`ArrayBase`][ArrayBase], but -//! `ArrayBase` is generic over the ownership of the data. [`Array`][Array] -//! owns its data; [`ArrayView`][ArrayView] is a view; -//! [`ArrayViewMut`][ArrayViewMut] is a mutable view; [`CowArray`][CowArray] +//! In `ndarray`, all arrays are instances of [`ArrayBase`], but +//! `ArrayBase` is generic over the ownership of the data. [`Array`] +//! owns its data; [`ArrayView`] is a view; +//! [`ArrayViewMut`] is a mutable view; [`CowArray`] //! either owns its data or is a view (with copy-on-write mutation of the view -//! variant); and [`ArcArray`][ArcArray] has a reference-counted pointer to its +//! variant); and [`ArcArray`] has a reference-counted pointer to its //! data (with copy-on-write mutation). Arrays and views follow Rust's aliasing //! rules. //! @@ -91,7 +91,7 @@ //! //! //! In `ndarray`, you can create fixed-dimension arrays, such as -//! [`Array2`][Array2]. This takes advantage of the type system to help you +//! [`Array2`]. This takes advantage of the type system to help you //! write correct code and also avoids small heap allocations for the shape and //! strides. //! @@ -263,7 +263,7 @@ //! Note that [`a.shape()`][.shape()], [`a.dim()`][.dim()], and //! [`a.raw_dim()`][.raw_dim()] all return the shape of the array, but as //! different types. `a.shape()` returns the shape as `&[Ix]`, (where -//! [`Ix`][Ix] is `usize`) which is useful for general operations on the shape. +//! [`Ix`] is `usize`) which is useful for general operations on the shape. //! `a.dim()` returns the shape as `D::Pattern`, which is useful for //! pattern-matching shapes. `a.raw_dim()` returns the shape as `D`, which is //! useful for creating other arrays of the same shape. @@ -376,7 +376,7 @@ //! //! //! -//! [`a * b`, `a + b`, etc.](../../struct.ArrayBase.html#arithmetic-operations) +//! [`a * b`, `a + b`, etc.](ArrayBase#arithmetic-operations) //! //! //! @@ -540,17 +540,17 @@ //! ## Iteration //! //! `ndarray` has lots of interesting iterators/producers that implement the -//! [`NdProducer`][NdProducer] trait, which is a generalization of `Iterator` +//! [`NdProducer`](crate::NdProducer) trait, which is a generalization of `Iterator` //! to multiple dimensions. This makes it possible to correctly and efficiently //! zip together slices/subviews of arrays in multiple dimensions with -//! [`Zip`][Zip] or [`azip!()`][azip!]. The purpose of this is similar to +//! [`Zip`] or [`azip!()`]. The purpose of this is similar to //! [`np.nditer`](https://docs.scipy.org/doc/numpy/reference/generated/numpy.nditer.html), -//! but [`Zip`][Zip] is implemented and used somewhat differently. +//! but [`Zip`] is implemented and used somewhat differently. //! //! This table lists some of the iterators/producers which have a direct //! equivalent in NumPy. For a more complete introduction to producers and //! iterators, see [*Loops, Producers, and -//! Iterators*](../../struct.ArrayBase.html#loops-producers-and-iterators). +//! Iterators*](ArrayBase#loops-producers-and-iterators). //! Note that there are also variants of these iterators (with a `_mut` suffix) //! that yield `ArrayViewMut` instead of `ArrayView`. //! @@ -570,88 +570,77 @@ //! `a[:,4]` | [`a.column(4)`][.column()] or [`a.column_mut(4)`][.column_mut()] | view (or mutable view) of column 4 in a 2-D array //! `a.shape[0] == a.shape[1]` | [`a.is_square()`][.is_square()] | check if the array is square //! -//! [.abs_diff_eq()]: ../../struct.ArrayBase.html#impl-AbsDiffEq> -//! [ArcArray]: ../../type.ArcArray.html -//! [arr2()]: ../../fn.arr2.html -//! [array!]: ../../macro.array.html -//! [Array]: ../../type.Array.html -//! [Array2]: ../../type.Array2.html -//! [ArrayBase]: ../../struct.ArrayBase.html -//! [ArrayView]: ../../type.ArrayView.html -//! [ArrayViewMut]: ../../type.ArrayViewMut.html -//! [.assign()]: ../../struct.ArrayBase.html#method.assign -//! [.axis_iter()]: ../../struct.ArrayBase.html#method.axis_iter -//! [azip!]: ../../macro.azip.html -//! [.ncols()]: ../../struct.ArrayBase.html#method.ncols -//! [.column()]: ../../struct.ArrayBase.html#method.column -//! [.column_mut()]: ../../struct.ArrayBase.html#method.column_mut -//! [concatenate!]: ../../macro.concatenate.html -//! [concatenate()]: ../../fn.concatenate.html -//! [CowArray]: ../../type.CowArray.html -//! [::default()]: ../../struct.ArrayBase.html#method.default -//! [.diag()]: ../../struct.ArrayBase.html#method.diag -//! [.dim()]: ../../struct.ArrayBase.html#method.dim -//! [::eye()]: ../../struct.ArrayBase.html#method.eye -//! [.fill()]: ../../struct.ArrayBase.html#method.fill -//! [.fold()]: ../../struct.ArrayBase.html#method.fold -//! [.fold_axis()]: ../../struct.ArrayBase.html#method.fold_axis -//! [::from_elem()]: ../../struct.ArrayBase.html#method.from_elem -//! [::from_iter()]: ../../struct.ArrayBase.html#method.from_iter -//! [::from_diag()]: ../../struct.ArrayBase.html#method.from_diag -//! [::from_shape_fn()]: ../../struct.ArrayBase.html#method.from_shape_fn -//! [::from_shape_vec()]: ../../struct.ArrayBase.html#method.from_shape_vec -//! [::from_shape_vec_unchecked()]: ../../struct.ArrayBase.html#method.from_shape_vec_unchecked -//! [::from_vec()]: ../../struct.ArrayBase.html#method.from_vec -//! [.index()]: ../../struct.ArrayBase.html#impl-Index -//! [.indexed_iter()]: ../../struct.ArrayBase.html#method.indexed_iter -//! [.insert_axis()]: ../../struct.ArrayBase.html#method.insert_axis -//! [.is_empty()]: ../../struct.ArrayBase.html#method.is_empty -//! [.is_square()]: ../../struct.ArrayBase.html#method.is_square -//! [.iter()]: ../../struct.ArrayBase.html#method.iter -//! [Ix]: ../../type.Ix.html -//! [.len()]: ../../struct.ArrayBase.html#method.len -//! [.len_of()]: ../../struct.ArrayBase.html#method.len_of -//! [::linspace()]: ../../struct.ArrayBase.html#method.linspace -//! [::logspace()]: ../../struct.ArrayBase.html#method.logspace -//! [::geomspace()]: ../../struct.ArrayBase.html#method.geomspace -//! [.map()]: ../../struct.ArrayBase.html#method.map -//! [.map_axis()]: ../../struct.ArrayBase.html#method.map_axis -//! [.map_inplace()]: ../../struct.ArrayBase.html#method.map_inplace -//! [.mapv()]: ../../struct.ArrayBase.html#method.mapv -//! [.mapv_inplace()]: ../../struct.ArrayBase.html#method.mapv_inplace -//! [.mapv_into()]: ../../struct.ArrayBase.html#method.mapv_into -//! [matrix-* dot]: ../../struct.ArrayBase.html#method.dot-1 -//! [.mean()]: ../../struct.ArrayBase.html#method.mean -//! [.mean_axis()]: ../../struct.ArrayBase.html#method.mean_axis -//! [.ndim()]: ../../struct.ArrayBase.html#method.ndim -//! [NdProducer]: ../../trait.NdProducer.html -//! [::ones()]: ../../struct.ArrayBase.html#method.ones -//! [.outer_iter()]: ../../struct.ArrayBase.html#method.outer_iter -//! [::range()]: ../../struct.ArrayBase.html#method.range -//! [.raw_dim()]: ../../struct.ArrayBase.html#method.raw_dim -//! [.reversed_axes()]: ../../struct.ArrayBase.html#method.reversed_axes -//! [.row()]: ../../struct.ArrayBase.html#method.row -//! [.row_mut()]: ../../struct.ArrayBase.html#method.row_mut -//! [.nrows()]: ../../struct.ArrayBase.html#method.nrows -//! [s!]: ../../macro.s.html -//! [.sum()]: ../../struct.ArrayBase.html#method.sum -//! [.slice()]: ../../struct.ArrayBase.html#method.slice -//! [.slice_axis()]: ../../struct.ArrayBase.html#method.slice_axis -//! [.slice_collapse()]: ../../struct.ArrayBase.html#method.slice_collapse -//! [.slice_move()]: ../../struct.ArrayBase.html#method.slice_move -//! [.slice_mut()]: ../../struct.ArrayBase.html#method.slice_mut -//! [.shape()]: ../../struct.ArrayBase.html#method.shape -//! [stack!]: ../../macro.stack.html -//! [stack()]: ../../fn.stack.html -//! [.strides()]: ../../struct.ArrayBase.html#method.strides -//! [.index_axis()]: ../../struct.ArrayBase.html#method.index_axis -//! [.sum_axis()]: ../../struct.ArrayBase.html#method.sum_axis -//! [.t()]: ../../struct.ArrayBase.html#method.t -//! [vec-* dot]: ../../struct.ArrayBase.html#method.dot -//! [.for_each()]: ../../struct.ArrayBase.html#method.for_each -//! [::zeros()]: ../../struct.ArrayBase.html#method.zeros -//! [Zip]: ../../struct.Zip.html +//! [.abs_diff_eq()]: ArrayBase#impl-AbsDiffEq> +//! [.assign()]: ArrayBase::assign +//! [.axis_iter()]: ArrayBase::axis_iter +//! [.ncols()]: ArrayBase::ncols +//! [.column()]: ArrayBase::column +//! [.column_mut()]: ArrayBase::column_mut +//! [concatenate()]: crate::concatenate() +//! [::default()]: ArrayBase::default +//! [.diag()]: ArrayBase::diag +//! [.dim()]: ArrayBase::dim +//! [::eye()]: ArrayBase::eye +//! [.fill()]: ArrayBase::fill +//! [.fold()]: ArrayBase::fold +//! [.fold_axis()]: ArrayBase::fold_axis +//! [::from_elem()]: ArrayBase::from_elem +//! [::from_iter()]: ArrayBase::from_iter +//! [::from_diag()]: ArrayBase::from_diag +//! [::from_shape_fn()]: ArrayBase::from_shape_fn +//! [::from_shape_vec()]: ArrayBase::from_shape_vec +//! [::from_shape_vec_unchecked()]: ArrayBase::from_shape_vec_unchecked +//! [::from_vec()]: ArrayBase::from_vec +//! [.index()]: ArrayBase#impl-Index +//! [.indexed_iter()]: ArrayBase::indexed_iter +//! [.insert_axis()]: ArrayBase::insert_axis +//! [.is_empty()]: ArrayBase::is_empty +//! [.is_square()]: ArrayBase::is_square +//! [.iter()]: ArrayBase::iter +//! [.len()]: ArrayBase::len +//! [.len_of()]: ArrayBase::len_of +//! [::linspace()]: ArrayBase::linspace +//! [::logspace()]: ArrayBase::logspace +//! [::geomspace()]: ArrayBase::geomspace +//! [.map()]: ArrayBase::map +//! [.map_axis()]: ArrayBase::map_axis +//! [.map_inplace()]: ArrayBase::map_inplace +//! [.mapv()]: ArrayBase::mapv +//! [.mapv_inplace()]: ArrayBase::mapv_inplace +//! [.mapv_into()]: ArrayBase::mapv_into +//! [matrix-* dot]: ArrayBase::dot-1 +//! [.mean()]: ArrayBase::mean +//! [.mean_axis()]: ArrayBase::mean_axis +//! [.ndim()]: ArrayBase::ndim +//! [::ones()]: ArrayBase::ones +//! [.outer_iter()]: ArrayBase::outer_iter +//! [::range()]: ArrayBase::range +//! [.raw_dim()]: ArrayBase::raw_dim +//! [.reversed_axes()]: ArrayBase::reversed_axes +//! [.row()]: ArrayBase::row +//! [.row_mut()]: ArrayBase::row_mut +//! [.nrows()]: ArrayBase::nrows +//! [.sum()]: ArrayBase::sum +//! [.slice()]: ArrayBase::slice +//! [.slice_axis()]: ArrayBase::slice_axis +//! [.slice_collapse()]: ArrayBase::slice_collapse +//! [.slice_move()]: ArrayBase::slice_move +//! [.slice_mut()]: ArrayBase::slice_mut +//! [.shape()]: ArrayBase::shape +//! [stack()]: crate::stack() +//! [.strides()]: ArrayBase::strides +//! [.index_axis()]: ArrayBase::index_axis +//! [.sum_axis()]: ArrayBase::sum_axis +//! [.t()]: ArrayBase::t +//! [vec-* dot]: ArrayBase::dot +//! [.for_each()]: ArrayBase::for_each +//! [::zeros()]: ArrayBase::zeros +//! [`Zip`]: crate::Zip pub mod coord_transform; pub mod rk_step; pub mod simple_math; + +// This is to avoid putting `crate::` everywhere +#[allow(unused_imports)] +use crate::imp_prelude::*; diff --git a/src/doc/ndarray_for_numpy_users/rk_step.rs b/src/doc/ndarray_for_numpy_users/rk_step.rs index 92935f3e7..0448e0705 100644 --- a/src/doc/ndarray_for_numpy_users/rk_step.rs +++ b/src/doc/ndarray_for_numpy_users/rk_step.rs @@ -120,10 +120,10 @@ //! * Don't return a newly allocated `f_new` array. If the caller wants this //! information, they can get it from the last row of `k`. //! -//! * Use [`c.mul_add(h, t)`][f64.mul_add()] instead of `t + c * h`. This is +//! * Use [`c.mul_add(h, t)`](f64::mul_add) instead of `t + c * h`. This is //! faster and reduces the floating-point error. It might also be beneficial -//! to use [`.scaled_add()`][.scaled_add()] or a combination of -//! [`azip!()`][azip!] and [`.mul_add()`][f64.mul_add()] on the arrays in +//! to use [`.scaled_add()`] or a combination of +//! [`azip!()`] and [`.mul_add()`](f64::mul_add) on the arrays in //! some places, but that's not demonstrated in the example below. //! //! ``` @@ -168,9 +168,7 @@ //! # fn main() { let _ = rk_step::, ArrayViewMut1<'_, f64>)>; } //! ``` //! -//! [f64.mul_add()]: https://doc.rust-lang.org/std/primitive.f64.html#method.mul_add -//! [.scaled_add()]: ../../../struct.ArrayBase.html#method.scaled_add -//! [azip!]: ../../../macro.azip.html +//! [`.scaled_add()`]: crate::ArrayBase::scaled_add //! //! ### SciPy license //! diff --git a/src/free_functions.rs b/src/free_functions.rs index 4a9d66810..2b30e0bd3 100644 --- a/src/free_functions.rs +++ b/src/free_functions.rs @@ -14,7 +14,7 @@ use alloc::vec::Vec; use crate::imp_prelude::*; use crate::{dimension, ArcArray1, ArcArray2}; -/// Create an [**`Array`**](type.Array.html) with one, two or +/// Create an **[`Array`]** with one, two or /// three dimensions. /// /// ``` diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 99c393e9d..e800a17ea 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -269,7 +269,7 @@ macro_rules! size_of_shape_checked_unwrap { /// column major (“f” order) memory layout instead of the default row major. /// For example `Array::zeros((5, 6).f())` makes a column major 5 × 6 array. /// -/// Use [`IxDyn`](type.IxDyn.html) for the shape to create an array with dynamic +/// Use [`type@IxDyn`] for the shape to create an array with dynamic /// number of axes. /// /// Finally, the few constructors that take a completely general diff --git a/src/impl_cow.rs b/src/impl_cow.rs index b880fd62c..5cd00077e 100644 --- a/src/impl_cow.rs +++ b/src/impl_cow.rs @@ -11,8 +11,6 @@ use crate::imp_prelude::*; /// Methods specific to `CowArray`. /// /// ***See also all methods for [`ArrayBase`]*** -/// -/// [`ArrayBase`]: struct.ArrayBase.html impl<'a, A, D> CowArray<'a, A, D> where D: Dimension, diff --git a/src/impl_methods.rs b/src/impl_methods.rs index e4d238f11..27abe6893 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -179,7 +179,7 @@ where /// If the input array is contiguous, then the output array will have the same /// memory layout. Otherwise, the layout of the output array is unspecified. /// If you need a particular layout, you can allocate a new array with the - /// desired memory layout and [`.assign()`](#method.assign) the data. + /// desired memory layout and [`.assign()`](Self::assign) the data. /// Alternatively, you can collectan iterator, like this for a result in /// standard layout: /// @@ -400,7 +400,7 @@ where /// /// Iterator element type is `(D::Pattern, &A)`. /// - /// See also [`Zip::indexed`](struct.Zip.html) + /// See also [`Zip::indexed`] pub fn indexed_iter(&self) -> IndexedIter<'_, A, D> where S: Data, @@ -544,9 +544,9 @@ where /// collapsed, as in [`.collapse_axis()`], rather than removed, as in /// [`.slice_move()`] or [`.index_axis_move()`]. /// - /// [`.collapse_axis()`]: #method.collapse_axis - /// [`.slice_move()`]: #method.slice_move - /// [`.index_axis_move()`]: #method.index_axis_move + /// [`.collapse_axis()`]: Self::collapse_axis + /// [`.slice_move()`]: Self::slice_move + /// [`.index_axis_move()`]: Self::index_axis_move /// /// See [*Slicing*](#slicing) for full documentation. /// See also [`s!`], [`SliceArg`], and [`SliceInfo`](crate::SliceInfo). @@ -916,7 +916,7 @@ where /// Collapses the array to `index` along the axis and removes the axis. /// - /// See [`.index_axis()`](#method.index_axis) and [*Subviews*](#subviews) for full documentation. + /// See [`.index_axis()`](Self::index_axis) and [*Subviews*](#subviews) for full documentation. /// /// **Panics** if `axis` or `index` is out of bounds. pub fn index_axis_move(mut self, axis: Axis, index: usize) -> ArrayBase diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index efe67a046..e58932747 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -17,8 +17,6 @@ use crate::Zip; /// Methods specific to `Array0`. /// /// ***See also all methods for [`ArrayBase`]*** -/// -/// [`ArrayBase`]: struct.ArrayBase.html impl Array { /// Returns the single element in the array without cloning it. /// @@ -56,8 +54,6 @@ impl Array { /// Methods specific to `Array`. /// /// ***See also all methods for [`ArrayBase`]*** -/// -/// [`ArrayBase`]: struct.ArrayBase.html impl Array where D: Dimension, @@ -75,8 +71,6 @@ where /// Methods specific to `Array2`. /// /// ***See also all methods for [`ArrayBase`]*** -/// -/// [`ArrayBase`]: struct.ArrayBase.html impl Array { /// Append a row to an array /// diff --git a/src/impl_special_element_types.rs b/src/impl_special_element_types.rs index bf4384e42..5d5f18491 100644 --- a/src/impl_special_element_types.rs +++ b/src/impl_special_element_types.rs @@ -15,8 +15,6 @@ use crate::RawDataSubst; /// Methods specific to arrays with `MaybeUninit` elements. /// /// ***See also all methods for [`ArrayBase`]*** -/// -/// [`ArrayBase`]: struct.ArrayBase.html impl ArrayBase where S: RawDataSubst>, diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs index d0f91b22b..7498add48 100644 --- a/src/impl_views/conversions.rs +++ b/src/impl_views/conversions.rs @@ -53,9 +53,6 @@ where /// Methods specific to `ArrayView0`. /// /// ***See also all methods for [`ArrayView`] and [`ArrayBase`]*** -/// -/// [`ArrayBase`]: struct.ArrayBase.html -/// [`ArrayView`]: struct.ArrayView.html impl<'a, A> ArrayView<'a, A, Ix0> { /// Consume the view and return a reference to the single element in the array. /// @@ -82,9 +79,6 @@ impl<'a, A> ArrayView<'a, A, Ix0> { /// Methods specific to `ArrayViewMut0`. /// /// ***See also all methods for [`ArrayViewMut`] and [`ArrayBase`]*** -/// -/// [`ArrayBase`]: struct.ArrayBase.html -/// [`ArrayViewMut`]: struct.ArrayViewMut.html impl<'a, A> ArrayViewMut<'a, A, Ix0> { /// Consume the mutable view and return a mutable reference to the single element in the array. /// diff --git a/src/impl_views/indexing.rs b/src/impl_views/indexing.rs index d8ed956f2..c73e870e6 100644 --- a/src/impl_views/indexing.rs +++ b/src/impl_views/indexing.rs @@ -59,7 +59,7 @@ pub trait IndexLonger { /// See also [the `get` method][1] which works for all arrays and array /// views. /// - /// [1]: struct.ArrayBase.html#method.get + /// [1]: ArrayBase::get /// /// **Panics** if index is out of bounds. fn index(self, index: I) -> Self::Output; @@ -73,8 +73,8 @@ pub trait IndexLonger { /// See also [the `get` method][1] (and [`get_mut`][2]) which works for all arrays and array /// views. /// - /// [1]: struct.ArrayBase.html#method.get - /// [2]: struct.ArrayBase.html#method.get_mut + /// [1]: ArrayBase::get + /// [2]: ArrayBase::get_mut /// /// **Panics** if index is out of bounds. fn get(self, index: I) -> Option; @@ -87,7 +87,7 @@ pub trait IndexLonger { /// See also [the `uget` method][1] which works for all arrays and array /// views. /// - /// [1]: struct.ArrayBase.html#method.uget + /// [1]: ArrayBase::uget /// /// **Note:** only unchecked for non-debug builds of ndarray. /// @@ -113,7 +113,7 @@ where /// See also [the `get` method][1] which works for all arrays and array /// views. /// - /// [1]: struct.ArrayBase.html#method.get + /// [1]: ArrayBase::get /// /// **Panics** if index is out of bounds. fn index(self, index: I) -> &'a A { @@ -133,7 +133,7 @@ where /// See also [the `uget` method][1] which works for all arrays and array /// views. /// - /// [1]: struct.ArrayBase.html#method.uget + /// [1]: ArrayBase::uget /// /// **Note:** only unchecked for non-debug builds of ndarray. unsafe fn uget(self, index: I) -> &'a A { @@ -158,7 +158,7 @@ where /// See also [the `get_mut` method][1] which works for all arrays and array /// views. /// - /// [1]: struct.ArrayBase.html#method.get_mut + /// [1]: ArrayBase::get_mut /// /// **Panics** if index is out of bounds. fn index(mut self, index: I) -> &'a mut A { @@ -177,7 +177,7 @@ where /// See also [the `get_mut` method][1] which works for all arrays and array /// views. /// - /// [1]: struct.ArrayBase.html#method.get_mut + /// [1]: ArrayBase::get_mut /// fn get(mut self, index: I) -> Option<&'a mut A> { debug_bounds_check!(self, index); @@ -195,7 +195,7 @@ where /// See also [the `uget_mut` method][1] which works for all arrays and array /// views. /// - /// [1]: struct.ArrayBase.html#method.uget_mut + /// [1]: ArrayBase::uget_mut /// /// **Note:** only unchecked for non-debug builds of ndarray. unsafe fn uget(mut self, index: I) -> &'a mut A { diff --git a/src/impl_views/splitting.rs b/src/impl_views/splitting.rs index a36ae4ddb..f2c0dc41a 100644 --- a/src/impl_views/splitting.rs +++ b/src/impl_views/splitting.rs @@ -121,7 +121,7 @@ where /// [`MultiSliceArg`], [`s!`], [`SliceArg`](crate::SliceArg), and /// [`SliceInfo`](crate::SliceInfo). /// - /// [`.multi_slice_mut()`]: struct.ArrayBase.html#method.multi_slice_mut + /// [`.multi_slice_mut()`]: ArrayBase::multi_slice_mut /// /// **Panics** if any of the following occur: /// diff --git a/src/iterators/chunks.rs b/src/iterators/chunks.rs index e41c1bf25..ba0e789fb 100644 --- a/src/iterators/chunks.rs +++ b/src/iterators/chunks.rs @@ -28,7 +28,7 @@ type BaseProducerMut<'a, A, D> = ArrayViewMut<'a, A, D>; /// Exact chunks producer and iterable. /// -/// See [`.exact_chunks()`](../struct.ArrayBase.html#method.exact_chunks) for more +/// See [`.exact_chunks()`](ArrayBase::exact_chunks) for more /// information. //#[derive(Debug)] pub struct ExactChunks<'a, A, D> { @@ -88,7 +88,7 @@ where /// Exact chunks iterator. /// -/// See [`.exact_chunks()`](../struct.ArrayBase.html#method.exact_chunks) for more +/// See [`.exact_chunks()`](ArrayBase::exact_chunks) for more /// information. pub struct ExactChunksIter<'a, A, D> { iter: ElementsBase<'a, A, D>, @@ -118,7 +118,7 @@ impl_ndproducer! { /// Exact chunks producer and iterable. /// -/// See [`.exact_chunks_mut()`](../struct.ArrayBase.html#method.exact_chunks_mut) +/// See [`.exact_chunks_mut()`](ArrayBase::exact_chunks_mut) /// for more information. //#[derive(Debug)] pub struct ExactChunksMut<'a, A, D> { @@ -222,7 +222,7 @@ impl_iterator! { /// Exact chunks iterator. /// -/// See [`.exact_chunks_mut()`](../struct.ArrayBase.html#method.exact_chunks_mut) +/// See [`.exact_chunks_mut()`](ArrayBase::exact_chunks_mut) /// for more information. pub struct ExactChunksIterMut<'a, A, D> { iter: ElementsBaseMut<'a, A, D>, diff --git a/src/iterators/iter.rs b/src/iterators/iter.rs index 7352e5e18..7b6d80cd5 100644 --- a/src/iterators/iter.rs +++ b/src/iterators/iter.rs @@ -4,7 +4,7 @@ //! implementation structs. //! //! -//! See also [`NdProducer`](../trait.NdProducer.html). +//! See also [`NdProducer`](crate::NdProducer). pub use crate::dimension::Axes; pub use crate::indexes::{Indices, IndicesIter}; diff --git a/src/iterators/lanes.rs b/src/iterators/lanes.rs index 2163c58a6..cda783a80 100644 --- a/src/iterators/lanes.rs +++ b/src/iterators/lanes.rs @@ -23,7 +23,7 @@ impl_ndproducer! { } } -/// See [`.lanes()`](../struct.ArrayBase.html#method.lanes) +/// See [`.lanes()`](ArrayBase::lanes) /// for more information. pub struct Lanes<'a, A, D> { base: ArrayView<'a, A, D>, @@ -92,7 +92,7 @@ where } } -/// See [`.lanes_mut()`](../struct.ArrayBase.html#method.lanes_mut) +/// See [`.lanes_mut()`](ArrayBase::lanes_mut) /// for more information. pub struct LanesMut<'a, A, D> { base: ArrayViewMut<'a, A, D>, diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 14ba342f4..b2e4b9c60 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -315,7 +315,7 @@ pub enum ElementsRepr { /// /// Iterator element type is `&'a A`. /// -/// See [`.iter()`](../struct.ArrayBase.html#method.iter) for more information. +/// See [`.iter()`](ArrayBase::iter) for more information. pub struct Iter<'a, A, D> { inner: ElementsRepr, ElementsBase<'a, A, D>>, } @@ -330,7 +330,7 @@ pub struct ElementsBase<'a, A, D> { /// /// Iterator element type is `&'a mut A`. /// -/// See [`.iter_mut()`](../struct.ArrayBase.html#method.iter_mut) for more information. +/// See [`.iter_mut()`](ArrayBase::iter_mut) for more information. pub struct IterMut<'a, A, D> { inner: ElementsRepr, ElementsBaseMut<'a, A, D>>, } @@ -354,12 +354,12 @@ impl<'a, A, D: Dimension> ElementsBaseMut<'a, A, D> { /// An iterator over the indexes and elements of an array. /// -/// See [`.indexed_iter()`](../struct.ArrayBase.html#method.indexed_iter) for more information. +/// See [`.indexed_iter()`](ArrayBase::indexed_iter) for more information. #[derive(Clone)] pub struct IndexedIter<'a, A, D>(ElementsBase<'a, A, D>); /// An iterator over the indexes and elements of an array (mutable). /// -/// See [`.indexed_iter_mut()`](../struct.ArrayBase.html#method.indexed_iter_mut) for more information. +/// See [`.indexed_iter_mut()`](ArrayBase::indexed_iter_mut) for more information. pub struct IndexedIterMut<'a, A, D>(ElementsBaseMut<'a, A, D>); impl<'a, A, D> IndexedIter<'a, A, D> @@ -681,7 +681,7 @@ where /// An iterator that traverses over all axes but one, and yields a view for /// each lane along that axis. /// -/// See [`.lanes()`](../struct.ArrayBase.html#method.lanes) for more information. +/// See [`.lanes()`](ArrayBase::lanes) for more information. pub struct LanesIter<'a, A, D> { inner_len: Ix, inner_stride: Ixs, @@ -732,7 +732,7 @@ where /// An iterator that traverses over all dimensions but the innermost, /// and yields each inner row (mutable). /// -/// See [`.lanes_mut()`](../struct.ArrayBase.html#method.lanes_mut) +/// See [`.lanes_mut()`](ArrayBase::lanes_mut) /// for more information. pub struct LanesIterMut<'a, A, D> { inner_len: Ix, @@ -927,8 +927,8 @@ where /// /// Iterator element type is `ArrayView<'a, A, D>`. /// -/// See [`.outer_iter()`](../struct.ArrayBase.html#method.outer_iter) -/// or [`.axis_iter()`](../struct.ArrayBase.html#method.axis_iter) +/// See [`.outer_iter()`](ArrayBase::outer_iter) +/// or [`.axis_iter()`](ArrayBase::axis_iter) /// for more information. #[derive(Debug)] pub struct AxisIter<'a, A, D> { @@ -1024,8 +1024,8 @@ where /// /// Iterator element type is `ArrayViewMut<'a, A, D>`. /// -/// See [`.outer_iter_mut()`](../struct.ArrayBase.html#method.outer_iter_mut) -/// or [`.axis_iter_mut()`](../struct.ArrayBase.html#method.axis_iter_mut) +/// See [`.outer_iter_mut()`](ArrayBase::outer_iter_mut) +/// or [`.axis_iter_mut()`](ArrayBase::axis_iter_mut) /// for more information. pub struct AxisIterMut<'a, A, D> { iter: AxisIterCore, @@ -1222,7 +1222,7 @@ impl<'a, A, D: Dimension> NdProducer for AxisIterMut<'a, A, D> { /// /// Iterator element type is `ArrayView<'a, A, D>`. /// -/// See [`.axis_chunks_iter()`](../struct.ArrayBase.html#method.axis_chunks_iter) for more information. +/// See [`.axis_chunks_iter()`](ArrayBase::axis_chunks_iter) for more information. pub struct AxisChunksIter<'a, A, D> { iter: AxisIterCore, /// Index of the partial chunk (the chunk smaller than the specified chunk @@ -1401,7 +1401,7 @@ macro_rules! chunk_iter_impl { /// /// Iterator element type is `ArrayViewMut<'a, A, D>`. /// -/// See [`.axis_chunks_iter_mut()`](../struct.ArrayBase.html#method.axis_chunks_iter_mut) +/// See [`.axis_chunks_iter_mut()`](ArrayBase::axis_chunks_iter_mut) /// for more information. pub struct AxisChunksIterMut<'a, A, D> { iter: AxisIterCore, diff --git a/src/iterators/windows.rs b/src/iterators/windows.rs index 4538f7abb..c47bfecec 100644 --- a/src/iterators/windows.rs +++ b/src/iterators/windows.rs @@ -6,7 +6,7 @@ use crate::NdProducer; /// Window producer and iterable /// -/// See [`.windows()`](../struct.ArrayBase.html#method.windows) for more +/// See [`.windows()`](ArrayBase::windows) for more /// information. pub struct Windows<'a, A, D> { base: ArrayView<'a, A, D>, @@ -86,7 +86,7 @@ where /// Window iterator. /// -/// See [`.windows()`](../struct.ArrayBase.html#method.windows) for more +/// See [`.windows()`](ArrayBase::windows) for more /// information. pub struct WindowsIter<'a, A, D> { iter: ElementsBase<'a, A, D>, diff --git a/src/itertools.rs b/src/itertools.rs index 96732f903..4fa8145ab 100644 --- a/src/itertools.rs +++ b/src/itertools.rs @@ -64,11 +64,9 @@ where /// The special cases of one and two arguments produce the equivalent of /// `$a.into_iter()` and `$a.into_iter().zip($b)` respectively. /// -/// Prefer this macro `izip!()` over [`multizip`] for the performance benefits +/// Prefer this macro `izip!()` over `multizip` for the performance benefits /// of using the standard library `.zip()`. /// -/// [`multizip`]: fn.multizip.html -/// /// ``` /// #[macro_use] extern crate itertools; /// # fn main() { diff --git a/src/lib.rs b/src/lib.rs index 1448b89a0..f773226f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,11 +31,11 @@ //! dimensions, then an element in the array is accessed by using that many indices. //! Each dimension is also called an *axis*. //! -//! - **[`ArrayBase`](struct.ArrayBase.html)**: +//! - **[`ArrayBase`]**: //! The *n*-dimensional array type itself.
//! It is used to implement both the owned arrays and the views; see its docs //! for an overview of all array features.
-//! - The main specific array type is **[`Array`](type.Array.html)**, which owns +//! - The main specific array type is **[`Array`]**, which owns //! its elements. //! //! ## Highlights @@ -47,8 +47,8 @@ //! - Higher order operations and arithmetic are performant //! - Array views can be used to slice and mutate any `[T]` data using //! `ArrayView::from` and `ArrayViewMut::from`. -//! - [`Zip`](struct.Zip.html) for lock step function application across two or more arrays or other -//! item producers ([`NdProducer`](trait.NdProducer.html) trait). +//! - [`Zip`] for lock step function application across two or more arrays or other +//! item producers ([`NdProducer`] trait). //! //! ## Crate Status //! @@ -101,13 +101,13 @@ //! //! ## Documentation //! -//! * The docs for [`ArrayBase`](struct.ArrayBase.html) provide an overview of +//! * The docs for [`ArrayBase`] provide an overview of //! the *n*-dimensional array type. Other good pages to look at are the -//! documentation for the [`s![]`](macro.s.html) and -//! [`azip!()`](macro.azip.html) macros. +//! documentation for the [`s![]`](s!) and +//! [`azip!()`](azip!) macros. //! //! * If you have experience with NumPy, you may also be interested in -//! [`ndarray_for_numpy_users`](doc/ndarray_for_numpy_users/index.html). +//! [`ndarray_for_numpy_users`](doc::ndarray_for_numpy_users). //! //! ## The ndarray ecosystem //! @@ -260,12 +260,6 @@ pub type Ixs = isize; /// [`ArrayViewMut`] refer to `ArrayBase` with different types for the data /// container. /// -/// [`Array`]: type.Array.html -/// [`ArcArray`]: type.ArcArray.html -/// [`ArrayView`]: type.ArrayView.html -/// [`ArrayViewMut`]: type.ArrayViewMut.html -/// [`CowArray`]: type.CowArray.html -/// /// ## Contents /// /// + [Array](#array) @@ -288,7 +282,7 @@ pub type Ixs = isize; /// /// ## `Array` /// -/// [`Array`](type.Array.html) is an owned array that owns the underlying array +/// [`Array`] is an owned array that owns the underlying array /// elements directly (just like a `Vec`) and it is the default way to create and /// store n-dimensional data. `Array` has two type parameters: `A` for /// the element type, and `D` for the dimensionality. A particular @@ -307,17 +301,16 @@ pub type Ixs = isize; /// /// ## `ArcArray` /// -/// [`ArcArray`](type.ArcArray.html) is an owned array with reference counted +/// [`ArcArray`] is an owned array with reference counted /// data (shared ownership). /// Sharing requires that it uses copy-on-write for mutable operations. /// Calling a method for mutating elements on `ArcArray`, for example -/// [`view_mut()`](#method.view_mut) or [`get_mut()`](#method.get_mut), +/// [`view_mut()`](Self::view_mut) or [`get_mut()`](Self::get_mut), /// will break sharing and require a clone of the data (if it is not uniquely held). /// /// ## `CowArray` /// -/// [`CowArray`](type.CowArray.html) is analogous to -/// [`std::borrow::Cow`](https://doc.rust-lang.org/std/borrow/enum.Cow.html). +/// [`CowArray`] is analogous to [`std::borrow::Cow`]. /// It can represent either an immutable view or a uniquely owned array. If a /// `CowArray` instance is the immutable view variant, then calling a method /// for mutating elements in the array will cause it to be converted into the @@ -382,15 +375,15 @@ pub type Ixs = isize; /// /// Important traits and types for dimension and indexing: /// -/// - A [`Dim`](struct.Dim.html) value represents a dimensionality or index. -/// - Trait [`Dimension`](trait.Dimension.html) is implemented by all +/// - A [`struct@Dim`] value represents a dimensionality or index. +/// - Trait [`Dimension`] is implemented by all /// dimensionalities. It defines many operations for dimensions and indices. -/// - Trait [`IntoDimension`](trait.IntoDimension.html) is used to convert into a +/// - Trait [`IntoDimension`] is used to convert into a /// `Dim` value. -/// - Trait [`ShapeBuilder`](trait.ShapeBuilder.html) is an extension of +/// - Trait [`ShapeBuilder`] is an extension of /// `IntoDimension` and is used when constructing an array. A shape describes /// not just the extent of each axis but also their strides. -/// - Trait [`NdIndex`](trait.NdIndex.html) is an extension of `Dimension` and is +/// - Trait [`NdIndex`] is an extension of `Dimension` and is /// for values that can be used with indexing syntax. /// /// @@ -405,10 +398,10 @@ pub type Ixs = isize; /// /// ## Loops, Producers and Iterators /// -/// Using [`Zip`](struct.Zip.html) is the most general way to apply a procedure +/// Using [`Zip`] is the most general way to apply a procedure /// across one or several arrays or *producers*. /// -/// [`NdProducer`](trait.NdProducer.html) is like an iterable but for +/// [`NdProducer`] is like an iterable but for /// multidimensional data. All producers have dimensions and axes, like an /// array view, and they can be split and used with parallelization using `Zip`. /// @@ -480,12 +473,12 @@ pub type Ixs = isize; /// [`.columns()`][gc], [`.columns_mut()`][gcm], /// [`.lanes(axis)`][l], [`.lanes_mut(axis)`][lm]. /// -/// [gr]: #method.rows -/// [grm]: #method.rows_mut -/// [gc]: #method.columns -/// [gcm]: #method.columns_mut -/// [l]: #method.lanes -/// [lm]: #method.lanes_mut +/// [gr]: Self::rows +/// [grm]: Self::rows_mut +/// [gc]: Self::columns +/// [gcm]: Self::columns_mut +/// [l]: Self::lanes +/// [lm]: Self::lanes_mut /// /// Yes, for 2D arrays `.rows()` and `.outer_iter()` have about the same /// effect: @@ -499,7 +492,7 @@ pub type Ixs = isize; /// the array. Slicing methods include [`.slice()`], [`.slice_mut()`], /// [`.slice_move()`], and [`.slice_collapse()`]. /// -/// The slicing argument can be passed using the macro [`s![]`](macro.s!.html), +/// The slicing argument can be passed using the macro [`s![]`](s!), /// which will be used in all examples. (The explicit form is an instance of /// [`SliceInfo`] or another type which implements [`SliceArg`]; see their docs /// for more information.) @@ -511,11 +504,10 @@ pub type Ixs = isize; /// [`.slice_collapse()`] panics on `NewAxis` elements and behaves like /// [`.collapse_axis()`] by preserving the number of dimensions. /// -/// [`.slice()`]: #method.slice -/// [`.slice_mut()`]: #method.slice_mut -/// [`.slice_move()`]: #method.slice_move -/// [`.slice_collapse()`]: #method.slice_collapse -/// [`NewAxis`]: struct.NewAxis.html +/// [`.slice()`]: Self::slice +/// [`.slice_mut()`]: Self::slice_mut +/// [`.slice_move()`]: Self::slice_move +/// [`.slice_collapse()`]: Self::slice_collapse /// /// When slicing arrays with generic dimensionality, creating an instance of /// [`SliceInfo`] to pass to the multi-axis slicing methods like [`.slice()`] @@ -524,18 +516,18 @@ pub type Ixs = isize; /// or to create a view and then slice individual axes of the view using /// methods such as [`.slice_axis_inplace()`] and [`.collapse_axis()`]. /// -/// [`.slice_each_axis()`]: #method.slice_each_axis -/// [`.slice_each_axis_mut()`]: #method.slice_each_axis_mut -/// [`.slice_each_axis_inplace()`]: #method.slice_each_axis_inplace -/// [`.slice_axis_inplace()`]: #method.slice_axis_inplace -/// [`.collapse_axis()`]: #method.collapse_axis +/// [`.slice_each_axis()`]: Self::slice_each_axis +/// [`.slice_each_axis_mut()`]: Self::slice_each_axis_mut +/// [`.slice_each_axis_inplace()`]: Self::slice_each_axis_inplace +/// [`.slice_axis_inplace()`]: Self::slice_axis_inplace +/// [`.collapse_axis()`]: Self::collapse_axis /// /// It's possible to take multiple simultaneous *mutable* slices with /// [`.multi_slice_mut()`] or (for [`ArrayViewMut`] only) /// [`.multi_slice_move()`]. /// -/// [`.multi_slice_mut()`]: #method.multi_slice_mut -/// [`.multi_slice_move()`]: type.ArrayViewMut.html#method.multi_slice_move +/// [`.multi_slice_mut()`]: Self::multi_slice_mut +/// [`.multi_slice_move()`]: ArrayViewMut#method.multi_slice_move /// /// ``` /// use ndarray::{arr2, arr3, s, ArrayBase, DataMut, Dimension, NewAxis, Slice}; @@ -633,16 +625,16 @@ pub type Ixs = isize; /// Methods for selecting an individual subview take two arguments: `axis` and /// `index`. /// -/// [`.axis_iter()`]: #method.axis_iter -/// [`.axis_iter_mut()`]: #method.axis_iter_mut -/// [`.fold_axis()`]: #method.fold_axis -/// [`.index_axis()`]: #method.index_axis -/// [`.index_axis_inplace()`]: #method.index_axis_inplace -/// [`.index_axis_mut()`]: #method.index_axis_mut -/// [`.index_axis_move()`]: #method.index_axis_move -/// [`.collapse_axis()`]: #method.collapse_axis -/// [`.outer_iter()`]: #method.outer_iter -/// [`.outer_iter_mut()`]: #method.outer_iter_mut +/// [`.axis_iter()`]: Self::axis_iter +/// [`.axis_iter_mut()`]: Self::axis_iter_mut +/// [`.fold_axis()`]: Self::fold_axis +/// [`.index_axis()`]: Self::index_axis +/// [`.index_axis_inplace()`]: Self::index_axis_inplace +/// [`.index_axis_mut()`]: Self::index_axis_mut +/// [`.index_axis_move()`]: Self::index_axis_move +/// [`.collapse_axis()`]: Self::collapse_axis +/// [`.outer_iter()`]: Self::outer_iter +/// [`.outer_iter_mut()`]: Self::outer_iter_mut /// /// ``` /// @@ -725,7 +717,7 @@ pub type Ixs = isize; /// /// ### Binary Operators with Array and Scalar /// -/// The trait [`ScalarOperand`](trait.ScalarOperand.html) marks types that can be used in arithmetic +/// The trait [`ScalarOperand`] marks types that can be used in arithmetic /// with arrays directly. For a scalar `K` the following combinations of operands /// are supported (scalar can be on either the left or right side, but /// `ScalarOperand` docs has the detailed condtions). @@ -748,7 +740,7 @@ pub type Ixs = isize; /// Arrays support limited *broadcasting*, where arithmetic operations with /// array operands of different sizes can be carried out by repeating the /// elements of the smaller dimension array. See -/// [`.broadcast()`](#method.broadcast) for a more detailed +/// [`.broadcast()`](Self::broadcast) for a more detailed /// description. /// /// ``` @@ -894,12 +886,12 @@ pub type Ixs = isize; ///
/// -/// [`CowArray::from(a)`](type.CowArray.html#impl-From%2C%20D>>) +/// [`CowArray::from(a)`](CowArray#impl-From%2C%20D>>) /// /// /// -/// [`CowArray::from(a.into_owned())`](type.CowArray.html#impl-From%2C%20D>>) +/// [`CowArray::from(a.into_owned())`](CowArray#impl-From%2C%20D>>) /// /// @@ -909,12 +901,12 @@ pub type Ixs = isize; /// /// -/// [`CowArray::from(a)`](type.CowArray.html#impl-From%2C%20D>>) +/// [`CowArray::from(a)`](CowArray#impl-From%2C%20D>>) /// /// /// -/// [`CowArray::from(a.view())`](type.CowArray.html#impl-From%2C%20D>>) +/// [`CowArray::from(a.view())`](CowArray#impl-From%2C%20D>>) /// ///
//! +//! ## Type conversions +//! +//! In `ndarray`, conversions between datatypes are done with `mapv()` by +//! passing a closure to convert every element independently. +//! For the conversion itself, we have several options: +//! - `std::convert::From` ensures lossless, safe conversions at compile-time +//! and is generally recommended. +//! - `std::convert::TryFrom` can be used for potentially unsafe conversions. It +//! will return a `Result` which can be handled or `unwrap()`ed to panic if +//! any value at runtime cannot be converted losslessly. +//! - The `as` keyword compiles to lossless/lossy conversions depending on the +//! source and target datatypes. It can be useful when `TryFrom` is a +//! performance issue or does not apply. A notable difference to NumPy is that +//! `as` performs a [*saturating* cast][sat_conv] when casting +//! from floats to integers. Further information can be found in the +//! [reference on type cast expressions][as_typecast]. +//! +//! For details, be sure to check out the type conversion examples. +//! + +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! [as_conv]: https://doc.rust-lang.org/rust-by-example/types/cast.html +//! [sat_conv]: https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#fixing-unsoundness-in-casts +//! [as_typecast]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions +//! //! ## Array manipulation //! //! NumPy | `ndarray` | Notes From 735afb0397264451f06b04d9379cac099885a948 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 4 Dec 2021 14:23:55 +0100 Subject: [PATCH 438/651] Fix style of imports in type_conversion example --- examples/type_conversion.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/type_conversion.rs b/examples/type_conversion.rs index 57c6393de..7bec2542f 100644 --- a/examples/type_conversion.rs +++ b/examples/type_conversion.rs @@ -1,5 +1,10 @@ #[cfg(feature = "approx")] -use {approx::assert_abs_diff_eq, ndarray::prelude::*, std::convert::TryFrom}; +use std::convert::TryFrom; + +#[cfg(feature = "approx")] +use approx::assert_abs_diff_eq; +#[cfg(feature = "approx")] +use ndarray::prelude::*; #[cfg(feature = "approx")] fn main() { From ce3f541f8778749708059d52289c1dee853606a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hyeon=20Kim=20=28=EA=B9=80=EC=A7=80=ED=98=84=29?= Date: Sun, 5 Dec 2021 21:55:59 +0900 Subject: [PATCH 439/651] Add support for isize, usize scalars Closes #1090 References: https://github.com/rust-ndarray/ndarray/issues/1090 https://stackoverflow.com/q/70232567/13977061 --- src/impl_ops.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 4c255dfff..a267198ba 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -336,6 +336,8 @@ mod arithmetic_ops { all_scalar_ops!(u32); all_scalar_ops!(i64); all_scalar_ops!(u64); + all_scalar_ops!(isize); + all_scalar_ops!(usize); all_scalar_ops!(i128); all_scalar_ops!(u128); From d1bb045dcf54142b21972be0aa0d9983cf7df687 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 6 Dec 2021 20:08:25 +0100 Subject: [PATCH 440/651] Fix end tag, toc and reorder type conversion doc --- src/doc/ndarray_for_numpy_users/mod.rs | 74 +++++++++++++------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index eff60cbea..c494816fb 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -19,6 +19,7 @@ //! * [Mathematics](#mathematics) //! * [Array manipulation](#array-manipulation) //! * [Iteration](#iteration) +//! * [Type conversions](#type-conversions) //! * [Convenience methods for 2-D arrays](#convenience-methods-for-2-d-arrays) //! //! # Similarities @@ -524,6 +525,42 @@ //! //!
+//! +//! NumPy +//! +//! +//! +//! `ndarray` +//! +//! +//! +//! Notes +//! +//!
+//! +//! `a.astype(np.float32)` +//! +//! +//! +//! `a.mapv(|x| f32::from(x))` +//! +//! +//! +//! convert `u8` array infallibly to `f32` array with `std::convert::From`, generally recommended +//! +//!
+//! +//! `a.astype(np.int32)` +//! +//! +//! +//! `a.mapv(|x| i32::from(x))` +//! +//! +//! +//! upcast `u8` array to `i32` array with `std::convert::From`, preferable over `as` because it ensures at compile-time that the conversion is lossless +//! +//!
+//! +//! `a.astype(np.uint8)` +//! +//! +//! +//! `a.mapv(|x| u8::try_from(x).unwrap())` +//! +//! +//! +//! try to convert `i8` array to `u8` array, panic if any value cannot be converted lossless at runtime (e.g. negative value) +//! +//!
+//! +//! `a.astype(np.int32)` +//! +//! +//! +//! `a.mapv(|x| x as i32)` +//! +//! +//! +//! convert `f32` array to `i32` array with ["saturating" conversion][sat_conv]; care needed because it can be a lossy conversion or result in non-finite values! See [the reference for information][as_typecast]. +//! +//!
//! +//! ## Array manipulation +//! +//! NumPy | `ndarray` | Notes +//! ------|-----------|------ +//! `a[:] = 3.` | [`a.fill(3.)`][.fill()] | set all array elements to the same scalar value +//! `a[:] = b` | [`a.assign(&b)`][.assign()] | copy the data from array `b` into array `a` +//! `np.concatenate((a,b), axis=1)` | [`concatenate![Axis(1), a, b]`][concatenate!] or [`concatenate(Axis(1), &[a.view(), b.view()])`][concatenate()] | concatenate arrays `a` and `b` along axis 1 +//! `np.stack((a,b), axis=1)` | [`stack![Axis(1), a, b]`][stack!] or [`stack(Axis(1), vec![a.view(), b.view()])`][stack()] | stack arrays `a` and `b` along axis 1 +//! `a[:,np.newaxis]` or `np.expand_dims(a, axis=1)` | [`a.slice(s![.., NewAxis])`][.slice()] or [`a.insert_axis(Axis(1))`][.insert_axis()] | create an view of 1-D array `a`, inserting a new axis 1 +//! `a.transpose()` or `a.T` | [`a.t()`][.t()] or [`a.reversed_axes()`][.reversed_axes()] | transpose of array `a` (view for `.t()` or by-move for `.reversed_axes()`) +//! `np.diag(a)` | [`a.diag()`][.diag()] | view the diagonal of `a` +//! `a.flatten()` | [`use std::iter::FromIterator; Array::from_iter(a.iter().cloned())`][::from_iter()] | create a 1-D array by flattening `a` +//! +//! ## Iteration +//! +//! `ndarray` has lots of interesting iterators/producers that implement the +//! [`NdProducer`](crate::NdProducer) trait, which is a generalization of `Iterator` +//! to multiple dimensions. This makes it possible to correctly and efficiently +//! zip together slices/subviews of arrays in multiple dimensions with +//! [`Zip`] or [`azip!()`]. The purpose of this is similar to +//! [`np.nditer`](https://docs.scipy.org/doc/numpy/reference/generated/numpy.nditer.html), +//! but [`Zip`] is implemented and used somewhat differently. +//! +//! This table lists some of the iterators/producers which have a direct +//! equivalent in NumPy. For a more complete introduction to producers and +//! iterators, see [*Loops, Producers, and +//! Iterators*](ArrayBase#loops-producers-and-iterators). +//! Note that there are also variants of these iterators (with a `_mut` suffix) +//! that yield `ArrayViewMut` instead of `ArrayView`. +//! +//! NumPy | `ndarray` | Notes +//! ------|-----------|------ +//! `a.flat` | [`a.iter()`][.iter()] | iterator over the array elements in logical order +//! `np.ndenumerate(a)` | [`a.indexed_iter()`][.indexed_iter()] | flat iterator yielding the index along with each element reference +//! `iter(a)` | [`a.outer_iter()`][.outer_iter()] or [`a.axis_iter(Axis(0))`][.axis_iter()] | iterator over the first (outermost) axis, yielding each subview +//! //! ## Type conversions //! //! In `ndarray`, conversions between datatypes are done with `mapv()` by @@ -614,47 +651,12 @@ //! convert `f32` array to `i32` array with ["saturating" conversion][sat_conv]; care needed because it can be a lossy conversion or result in non-finite values! See [the reference for information][as_typecast]. //! //! +//! //! //! [as_conv]: https://doc.rust-lang.org/rust-by-example/types/cast.html //! [sat_conv]: https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#fixing-unsoundness-in-casts //! [as_typecast]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions //! -//! ## Array manipulation -//! -//! NumPy | `ndarray` | Notes -//! ------|-----------|------ -//! `a[:] = 3.` | [`a.fill(3.)`][.fill()] | set all array elements to the same scalar value -//! `a[:] = b` | [`a.assign(&b)`][.assign()] | copy the data from array `b` into array `a` -//! `np.concatenate((a,b), axis=1)` | [`concatenate![Axis(1), a, b]`][concatenate!] or [`concatenate(Axis(1), &[a.view(), b.view()])`][concatenate()] | concatenate arrays `a` and `b` along axis 1 -//! `np.stack((a,b), axis=1)` | [`stack![Axis(1), a, b]`][stack!] or [`stack(Axis(1), vec![a.view(), b.view()])`][stack()] | stack arrays `a` and `b` along axis 1 -//! `a[:,np.newaxis]` or `np.expand_dims(a, axis=1)` | [`a.slice(s![.., NewAxis])`][.slice()] or [`a.insert_axis(Axis(1))`][.insert_axis()] | create an view of 1-D array `a`, inserting a new axis 1 -//! `a.transpose()` or `a.T` | [`a.t()`][.t()] or [`a.reversed_axes()`][.reversed_axes()] | transpose of array `a` (view for `.t()` or by-move for `.reversed_axes()`) -//! `np.diag(a)` | [`a.diag()`][.diag()] | view the diagonal of `a` -//! `a.flatten()` | [`use std::iter::FromIterator; Array::from_iter(a.iter().cloned())`][::from_iter()] | create a 1-D array by flattening `a` -//! -//! ## Iteration -//! -//! `ndarray` has lots of interesting iterators/producers that implement the -//! [`NdProducer`](crate::NdProducer) trait, which is a generalization of `Iterator` -//! to multiple dimensions. This makes it possible to correctly and efficiently -//! zip together slices/subviews of arrays in multiple dimensions with -//! [`Zip`] or [`azip!()`]. The purpose of this is similar to -//! [`np.nditer`](https://docs.scipy.org/doc/numpy/reference/generated/numpy.nditer.html), -//! but [`Zip`] is implemented and used somewhat differently. -//! -//! This table lists some of the iterators/producers which have a direct -//! equivalent in NumPy. For a more complete introduction to producers and -//! iterators, see [*Loops, Producers, and -//! Iterators*](ArrayBase#loops-producers-and-iterators). -//! Note that there are also variants of these iterators (with a `_mut` suffix) -//! that yield `ArrayViewMut` instead of `ArrayView`. -//! -//! NumPy | `ndarray` | Notes -//! ------|-----------|------ -//! `a.flat` | [`a.iter()`][.iter()] | iterator over the array elements in logical order -//! `np.ndenumerate(a)` | [`a.indexed_iter()`][.indexed_iter()] | flat iterator yielding the index along with each element reference -//! `iter(a)` | [`a.outer_iter()`][.outer_iter()] or [`a.axis_iter(Axis(0))`][.axis_iter()] | iterator over the first (outermost) axis, yielding each subview -//! //! ## Convenience methods for 2-D arrays //! //! NumPy | `ndarray` | Notes From a7d1fd6e672280295b0238774b7585232ee2246f Mon Sep 17 00:00:00 2001 From: Frank Steffahn Date: Mon, 20 Dec 2021 02:17:59 +0100 Subject: [PATCH 441/651] Fix some typos (#1134) --- RELEASES.md | 6 +++--- examples/axis_ops.rs | 2 +- src/arraytraits.rs | 4 ++-- src/impl_1d.rs | 2 +- src/impl_2d.rs | 4 ++-- src/impl_constructors.rs | 10 +++++----- src/lib.rs | 2 +- src/linalg/impl_linalg.rs | 2 +- src/parallel/impl_par_methods.rs | 4 ++-- src/zip/mod.rs | 2 +- src/zip/ndproducer.rs | 2 +- tests/iterators.rs | 4 ++-- tests/windows.rs | 2 +- 13 files changed, 23 insertions(+), 23 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 99ec442ef..a4bd7e53a 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -366,7 +366,7 @@ API changes ----------- - New constructors `Array::from_iter` and `Array::from_vec` by [@bluss]. - No new functionality, just that these constructors are avaiable without trait + No new functionality, just that these constructors are available without trait imports. https://github.com/rust-ndarray/ndarray/pull/921 @@ -545,7 +545,7 @@ New features Enhancements ------------ -- Handle inhomogenous shape inputs better in Zip, in practice: guess better whether +- Handle inhomogeneous shape inputs better in Zip, in practice: guess better whether to prefer c- or f-order for the inner loop by [@bluss] https://github.com/rust-ndarray/ndarray/pull/809 @@ -978,7 +978,7 @@ Earlier releases - Add `Zip::indexed` - New methods `genrows/_mut, gencolumns/_mut, lanes/_mut` that - return iterable producers (producer means `Zip` compatibile). + return iterable producers (producer means `Zip` compatible). - New method `.windows()` by @Robbepop, returns an iterable producer - New function `general_mat_vec_mul` (with fast default and blas acceleration) - `Zip::apply` and `fold_while` now take `self` as the first argument diff --git a/examples/axis_ops.rs b/examples/axis_ops.rs index 98d2d3d7f..0b2c3dec8 100644 --- a/examples/axis_ops.rs +++ b/examples/axis_ops.rs @@ -11,7 +11,7 @@ use ndarray::prelude::*; /// make sure axes are in positive stride direction, and merge adjacent /// axes if possible. /// -/// This changes the logical order of the elments in the +/// This changes the logical order of the elements in the /// array, so that if we read them in row-major order after regularization, /// it corresponds to their order in memory. /// diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 6a4fd1137..7d84dc5f2 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -303,7 +303,7 @@ where pub const ARRAY_FORMAT_VERSION: u8 = 1u8; // use "raw" form instead of type aliases here so that they show up in docs -/// Implementation of `ArrayView::from(&S)` where `S` is a slice or slicable. +/// Implementation of `ArrayView::from(&S)` where `S` is a slice or sliceable. impl<'a, A, Slice: ?Sized> From<&'a Slice> for ArrayView<'a, A, Ix1> where Slice: AsRef<[A]>, @@ -359,7 +359,7 @@ where } } -/// Implementation of `ArrayViewMut::from(&mut S)` where `S` is a slice or slicable. +/// Implementation of `ArrayViewMut::from(&mut S)` where `S` is a slice or sliceable. impl<'a, A, Slice: ?Sized> From<&'a mut Slice> for ArrayViewMut<'a, A, Ix1> where Slice: AsMut<[A]>, diff --git a/src/impl_1d.rs b/src/impl_1d.rs index 74eaaad21..17f7729d6 100644 --- a/src/impl_1d.rs +++ b/src/impl_1d.rs @@ -42,7 +42,7 @@ where let mut dst = if let Some(dst) = lane_iter.next() { dst } else { return }; // Logically we do a circular swap here, all elements in a chain - // Using MaybeUninit to avoid unecessary writes in the safe swap solution + // Using MaybeUninit to avoid unnecessary writes in the safe swap solution // // for elt in lane_iter { // std::mem::swap(dst, elt); diff --git a/src/impl_2d.rs b/src/impl_2d.rs index 9af833103..6ef15c501 100644 --- a/src/impl_2d.rs +++ b/src/impl_2d.rs @@ -125,13 +125,13 @@ where /// Return true if the array is square, false otherwise. /// /// # Examples - /// Sqaure: + /// Square: /// ``` /// use ndarray::array; /// let array = array![[1., 2.], [3., 4.]]; /// assert!(array.is_square()); /// ``` - /// Not sqaure: + /// Not square: /// ``` /// use ndarray::array; /// let array = array![[1., 2., 5.], [3., 4., 6.]]; diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index df5e295c6..4a18ff832 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -540,7 +540,7 @@ where } - /// Create an array with uninitalized elements, shape `shape`. + /// Create an array with uninitialized elements, shape `shape`. /// /// The uninitialized elements of type `A` are represented by the type `MaybeUninit`, /// an easier way to handle uninit values correctly. @@ -598,7 +598,7 @@ where } } - /// Create an array with uninitalized elements, shape `shape`. + /// Create an array with uninitialized elements, shape `shape`. /// /// The uninitialized elements of type `A` are represented by the type `MaybeUninit`, /// an easier way to handle uninit values correctly. @@ -634,7 +634,7 @@ where #[deprecated(note = "This method is hard to use correctly. Use `uninit` instead.", since = "0.15.0")] - /// Create an array with uninitalized elements, shape `shape`. + /// Create an array with uninitialized elements, shape `shape`. /// /// Prefer to use [`uninit()`](ArrayBase::uninit) if possible, because it is /// easier to use correctly. @@ -643,7 +643,7 @@ where /// /// ### Safety /// - /// Accessing uninitalized values is undefined behaviour. You must overwrite *all* the elements + /// Accessing uninitialized values is undefined behaviour. You must overwrite *all* the elements /// in the array after it is created; for example using /// [`raw_view_mut`](ArrayBase::raw_view_mut) or other low-level element access. /// @@ -676,7 +676,7 @@ where S: DataOwned>, D: Dimension, { - /// Create an array with uninitalized elements, shape `shape`. + /// Create an array with uninitialized elements, shape `shape`. /// /// This method has been renamed to `uninit` #[deprecated(note = "Renamed to `uninit`", since = "0.15.0")] diff --git a/src/lib.rs b/src/lib.rs index a8ee9ee6d..915714fa4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -710,7 +710,7 @@ pub type Ixs = isize; /// The trait [`ScalarOperand`] marks types that can be used in arithmetic /// with arrays directly. For a scalar `K` the following combinations of operands /// are supported (scalar can be on either the left or right side, but -/// `ScalarOperand` docs has the detailed condtions). +/// `ScalarOperand` docs has the detailed conditions). /// /// - `&A @ K` or `K @ &A` which produces a new `Array` /// - `B @ K` or `K @ B` which consumes `B`, updates it with the result and returns it diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index d953dfdb4..52a15f44e 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -677,7 +677,7 @@ pub fn general_mat_vec_mul( /// General matrix-vector multiplication /// -/// Use a raw view for the destination vector, so that it can be uninitalized. +/// Use a raw view for the destination vector, so that it can be uninitialized. /// /// ## Safety /// diff --git a/src/parallel/impl_par_methods.rs b/src/parallel/impl_par_methods.rs index 381b86b52..ed0dcad7a 100644 --- a/src/parallel/impl_par_methods.rs +++ b/src/parallel/impl_par_methods.rs @@ -94,7 +94,7 @@ macro_rules! zip_impl { -> Array where R: Send { - let mut output = self.uninitalized_for_current_layout::(); + let mut output = self.uninitialized_for_current_layout::(); let total_len = output.len(); // Create a parallel iterator that produces chunks of the zip with the output @@ -191,7 +191,7 @@ macro_rules! zip_impl { /// Note that it is often more efficient to parallelize not per-element but rather /// based on larger chunks of an array like generalized rows and operating on each chunk /// using a sequential variant of the accumulation. - /// For example, sum each row sequentially and in parallel, taking advatange of locality + /// For example, sum each row sequentially and in parallel, taking advantage of locality /// and vectorization within each task, and then reduce their sums to the sum of the matrix. /// /// Also note that the splitting of the producer into multiple tasks is _not_ deterministic diff --git a/src/zip/mod.rs b/src/zip/mod.rs index a24adc2c1..a6cd0c1fe 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -426,7 +426,7 @@ where } #[cfg(feature = "rayon")] - pub(crate) fn uninitalized_for_current_layout(&self) -> Array, D> + pub(crate) fn uninitialized_for_current_layout(&self) -> Array, D> { let is_f = self.prefer_f(); Array::uninit(self.dimension.clone().set_f(is_f)) diff --git a/src/zip/ndproducer.rs b/src/zip/ndproducer.rs index ca7e75fd3..9829c6afc 100644 --- a/src/zip/ndproducer.rs +++ b/src/zip/ndproducer.rs @@ -43,7 +43,7 @@ where /// Most `NdProducers` are *iterable* (implement `IntoIterator`) but not directly /// iterators. This separation is needed because the producer represents /// a multidimensional set of items, it can be split along a particular axis for -/// parallelization, and it has no fixed correspondance to a sequence. +/// parallelization, and it has no fixed correspondence to a sequence. /// /// The natural exception is one dimensional producers, like `AxisIter`, which /// implement `Iterator` directly diff --git a/tests/iterators.rs b/tests/iterators.rs index fb78c0ccc..d7f7c5823 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -532,8 +532,8 @@ fn axis_iter_mut_zip_partially_consumed_discontiguous() { fn axis_chunks_iter_corner_cases() { // examples provided by @bluss in PR #65 // these tests highlight corner cases of the axis_chunks_iter implementation - // and enable checking if no pointer offseting is out of bounds. However - // checking the absence of of out of bounds offseting cannot (?) be + // and enable checking if no pointer offsetting is out of bounds. However + // checking the absence of of out of bounds offsetting cannot (?) be // done automatically, so one has to launch this test in a debugger. let a = ArcArray::::linspace(0., 7., 8).reshape((8, 1)); let it = a.axis_chunks_iter(Axis(0), 4); diff --git a/tests/windows.rs b/tests/windows.rs index 664616f67..432be5e41 100644 --- a/tests/windows.rs +++ b/tests/windows.rs @@ -116,7 +116,7 @@ fn test_window_zip() { } } -/// Test verifies that non existant Axis results in panic +/// Test verifies that non existent Axis results in panic #[test] #[should_panic] fn axis_windows_outofbound() { From 31244100631382bb8ee30721872a928bfdf07f44 Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Wed, 2 Mar 2022 10:47:01 +0900 Subject: [PATCH 442/651] Allow macro ndarray::s! in no_std. (#1154) --- src/slice.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/slice.rs b/src/slice.rs index dd0d162e2..0146d6dba 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -842,14 +842,14 @@ macro_rules! s( } }; // empty call, i.e. `s![]` - (@parse ::std::marker::PhantomData::<$crate::Ix0>, ::std::marker::PhantomData::<$crate::Ix0>, []) => { + (@parse ::core::marker::PhantomData::<$crate::Ix0>, ::core::marker::PhantomData::<$crate::Ix0>, []) => { { #[allow(unsafe_code)] unsafe { $crate::SliceInfo::new_unchecked( [], - ::std::marker::PhantomData::<$crate::Ix0>, - ::std::marker::PhantomData::<$crate::Ix0>, + ::core::marker::PhantomData::<$crate::Ix0>, + ::core::marker::PhantomData::<$crate::Ix0>, ) } } @@ -858,18 +858,18 @@ macro_rules! s( (@parse $($t:tt)*) => { compile_error!("Invalid syntax in s![] call.") }; // convert range/index/new-axis into SliceInfoElem (@convert $r:expr) => { - <$crate::SliceInfoElem as ::std::convert::From<_>>::from($r) + <$crate::SliceInfoElem as ::core::convert::From<_>>::from($r) }; // convert range/index/new-axis and step into SliceInfoElem (@convert $r:expr, $s:expr) => { - <$crate::SliceInfoElem as ::std::convert::From<_>>::from( - <$crate::Slice as ::std::convert::From<_>>::from($r).step_by($s as isize) + <$crate::SliceInfoElem as ::core::convert::From<_>>::from( + <$crate::Slice as ::core::convert::From<_>>::from($r).step_by($s as isize) ) }; ($($t:tt)*) => { $crate::s![@parse - ::std::marker::PhantomData::<$crate::Ix0>, - ::std::marker::PhantomData::<$crate::Ix0>, + ::core::marker::PhantomData::<$crate::Ix0>, + ::core::marker::PhantomData::<$crate::Ix0>, [] $($t)* ] From ddef4d280fb5abc82325287e68ecacfc81882a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hendrik=20Schr=C3=B6ter?= <16517898+Rikorose@users.noreply.github.com> Date: Wed, 18 May 2022 02:26:15 +0200 Subject: [PATCH 443/651] Add link to Slicing docs on libary documentation page (#1164) --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 915714fa4..07e5ed680 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,8 +40,8 @@ //! ## Highlights //! //! - Generic *n*-dimensional array -//! - Slicing, also with arbitrary step size, and negative indices to mean -//! elements from the end of the axis. +//! - [Slicing](ArrayBase#slicing), also with arbitrary step size, and negative +//! indices to mean elements from the end of the axis. //! - Views and subviews of arrays; iterators that yield subviews. //! - Higher order operations and arithmetic are performant //! - Array views can be used to slice and mutate any `[T]` data using From 633450e49cbd7a68cce739f09cf5ceda7e19497b Mon Sep 17 00:00:00 2001 From: Frank Steffahn Date: Mon, 20 Dec 2021 02:17:59 +0100 Subject: [PATCH 444/651] Fix some typos (#1134) --- RELEASES.md | 6 +++--- examples/axis_ops.rs | 2 +- src/arraytraits.rs | 4 ++-- src/impl_1d.rs | 2 +- src/impl_2d.rs | 4 ++-- src/impl_constructors.rs | 10 +++++----- src/lib.rs | 2 +- src/linalg/impl_linalg.rs | 2 +- src/parallel/impl_par_methods.rs | 4 ++-- src/zip/mod.rs | 2 +- src/zip/ndproducer.rs | 2 +- tests/iterators.rs | 4 ++-- tests/windows.rs | 2 +- 13 files changed, 23 insertions(+), 23 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 99ec442ef..a4bd7e53a 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -366,7 +366,7 @@ API changes ----------- - New constructors `Array::from_iter` and `Array::from_vec` by [@bluss]. - No new functionality, just that these constructors are avaiable without trait + No new functionality, just that these constructors are available without trait imports. https://github.com/rust-ndarray/ndarray/pull/921 @@ -545,7 +545,7 @@ New features Enhancements ------------ -- Handle inhomogenous shape inputs better in Zip, in practice: guess better whether +- Handle inhomogeneous shape inputs better in Zip, in practice: guess better whether to prefer c- or f-order for the inner loop by [@bluss] https://github.com/rust-ndarray/ndarray/pull/809 @@ -978,7 +978,7 @@ Earlier releases - Add `Zip::indexed` - New methods `genrows/_mut, gencolumns/_mut, lanes/_mut` that - return iterable producers (producer means `Zip` compatibile). + return iterable producers (producer means `Zip` compatible). - New method `.windows()` by @Robbepop, returns an iterable producer - New function `general_mat_vec_mul` (with fast default and blas acceleration) - `Zip::apply` and `fold_while` now take `self` as the first argument diff --git a/examples/axis_ops.rs b/examples/axis_ops.rs index 98d2d3d7f..0b2c3dec8 100644 --- a/examples/axis_ops.rs +++ b/examples/axis_ops.rs @@ -11,7 +11,7 @@ use ndarray::prelude::*; /// make sure axes are in positive stride direction, and merge adjacent /// axes if possible. /// -/// This changes the logical order of the elments in the +/// This changes the logical order of the elements in the /// array, so that if we read them in row-major order after regularization, /// it corresponds to their order in memory. /// diff --git a/src/arraytraits.rs b/src/arraytraits.rs index a7f22c1f7..31d27b003 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -303,7 +303,7 @@ where pub const ARRAY_FORMAT_VERSION: u8 = 1u8; // use "raw" form instead of type aliases here so that they show up in docs -/// Implementation of `ArrayView::from(&S)` where `S` is a slice or slicable. +/// Implementation of `ArrayView::from(&S)` where `S` is a slice or sliceable. impl<'a, A, Slice: ?Sized> From<&'a Slice> for ArrayView<'a, A, Ix1> where Slice: AsRef<[A]>, @@ -335,7 +335,7 @@ where } } -/// Implementation of `ArrayViewMut::from(&mut S)` where `S` is a slice or slicable. +/// Implementation of `ArrayViewMut::from(&mut S)` where `S` is a slice or sliceable. impl<'a, A, Slice: ?Sized> From<&'a mut Slice> for ArrayViewMut<'a, A, Ix1> where Slice: AsMut<[A]>, diff --git a/src/impl_1d.rs b/src/impl_1d.rs index 74eaaad21..17f7729d6 100644 --- a/src/impl_1d.rs +++ b/src/impl_1d.rs @@ -42,7 +42,7 @@ where let mut dst = if let Some(dst) = lane_iter.next() { dst } else { return }; // Logically we do a circular swap here, all elements in a chain - // Using MaybeUninit to avoid unecessary writes in the safe swap solution + // Using MaybeUninit to avoid unnecessary writes in the safe swap solution // // for elt in lane_iter { // std::mem::swap(dst, elt); diff --git a/src/impl_2d.rs b/src/impl_2d.rs index 9af833103..6ef15c501 100644 --- a/src/impl_2d.rs +++ b/src/impl_2d.rs @@ -125,13 +125,13 @@ where /// Return true if the array is square, false otherwise. /// /// # Examples - /// Sqaure: + /// Square: /// ``` /// use ndarray::array; /// let array = array![[1., 2.], [3., 4.]]; /// assert!(array.is_square()); /// ``` - /// Not sqaure: + /// Not square: /// ``` /// use ndarray::array; /// let array = array![[1., 2., 5.], [3., 4., 6.]]; diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index df5e295c6..4a18ff832 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -540,7 +540,7 @@ where } - /// Create an array with uninitalized elements, shape `shape`. + /// Create an array with uninitialized elements, shape `shape`. /// /// The uninitialized elements of type `A` are represented by the type `MaybeUninit`, /// an easier way to handle uninit values correctly. @@ -598,7 +598,7 @@ where } } - /// Create an array with uninitalized elements, shape `shape`. + /// Create an array with uninitialized elements, shape `shape`. /// /// The uninitialized elements of type `A` are represented by the type `MaybeUninit`, /// an easier way to handle uninit values correctly. @@ -634,7 +634,7 @@ where #[deprecated(note = "This method is hard to use correctly. Use `uninit` instead.", since = "0.15.0")] - /// Create an array with uninitalized elements, shape `shape`. + /// Create an array with uninitialized elements, shape `shape`. /// /// Prefer to use [`uninit()`](ArrayBase::uninit) if possible, because it is /// easier to use correctly. @@ -643,7 +643,7 @@ where /// /// ### Safety /// - /// Accessing uninitalized values is undefined behaviour. You must overwrite *all* the elements + /// Accessing uninitialized values is undefined behaviour. You must overwrite *all* the elements /// in the array after it is created; for example using /// [`raw_view_mut`](ArrayBase::raw_view_mut) or other low-level element access. /// @@ -676,7 +676,7 @@ where S: DataOwned>, D: Dimension, { - /// Create an array with uninitalized elements, shape `shape`. + /// Create an array with uninitialized elements, shape `shape`. /// /// This method has been renamed to `uninit` #[deprecated(note = "Renamed to `uninit`", since = "0.15.0")] diff --git a/src/lib.rs b/src/lib.rs index a8ee9ee6d..915714fa4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -710,7 +710,7 @@ pub type Ixs = isize; /// The trait [`ScalarOperand`] marks types that can be used in arithmetic /// with arrays directly. For a scalar `K` the following combinations of operands /// are supported (scalar can be on either the left or right side, but -/// `ScalarOperand` docs has the detailed condtions). +/// `ScalarOperand` docs has the detailed conditions). /// /// - `&A @ K` or `K @ &A` which produces a new `Array` /// - `B @ K` or `K @ B` which consumes `B`, updates it with the result and returns it diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index d953dfdb4..52a15f44e 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -677,7 +677,7 @@ pub fn general_mat_vec_mul( /// General matrix-vector multiplication /// -/// Use a raw view for the destination vector, so that it can be uninitalized. +/// Use a raw view for the destination vector, so that it can be uninitialized. /// /// ## Safety /// diff --git a/src/parallel/impl_par_methods.rs b/src/parallel/impl_par_methods.rs index 381b86b52..ed0dcad7a 100644 --- a/src/parallel/impl_par_methods.rs +++ b/src/parallel/impl_par_methods.rs @@ -94,7 +94,7 @@ macro_rules! zip_impl { -> Array where R: Send { - let mut output = self.uninitalized_for_current_layout::(); + let mut output = self.uninitialized_for_current_layout::(); let total_len = output.len(); // Create a parallel iterator that produces chunks of the zip with the output @@ -191,7 +191,7 @@ macro_rules! zip_impl { /// Note that it is often more efficient to parallelize not per-element but rather /// based on larger chunks of an array like generalized rows and operating on each chunk /// using a sequential variant of the accumulation. - /// For example, sum each row sequentially and in parallel, taking advatange of locality + /// For example, sum each row sequentially and in parallel, taking advantage of locality /// and vectorization within each task, and then reduce their sums to the sum of the matrix. /// /// Also note that the splitting of the producer into multiple tasks is _not_ deterministic diff --git a/src/zip/mod.rs b/src/zip/mod.rs index a24adc2c1..a6cd0c1fe 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -426,7 +426,7 @@ where } #[cfg(feature = "rayon")] - pub(crate) fn uninitalized_for_current_layout(&self) -> Array, D> + pub(crate) fn uninitialized_for_current_layout(&self) -> Array, D> { let is_f = self.prefer_f(); Array::uninit(self.dimension.clone().set_f(is_f)) diff --git a/src/zip/ndproducer.rs b/src/zip/ndproducer.rs index 619fadcc3..0ec49f3ac 100644 --- a/src/zip/ndproducer.rs +++ b/src/zip/ndproducer.rs @@ -44,7 +44,7 @@ where /// Most `NdProducers` are *iterable* (implement `IntoIterator`) but not directly /// iterators. This separation is needed because the producer represents /// a multidimensional set of items, it can be split along a particular axis for -/// parallelization, and it has no fixed correspondance to a sequence. +/// parallelization, and it has no fixed correspondence to a sequence. /// /// The natural exception is one dimensional producers, like `AxisIter`, which /// implement `Iterator` directly diff --git a/tests/iterators.rs b/tests/iterators.rs index fb78c0ccc..d7f7c5823 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -532,8 +532,8 @@ fn axis_iter_mut_zip_partially_consumed_discontiguous() { fn axis_chunks_iter_corner_cases() { // examples provided by @bluss in PR #65 // these tests highlight corner cases of the axis_chunks_iter implementation - // and enable checking if no pointer offseting is out of bounds. However - // checking the absence of of out of bounds offseting cannot (?) be + // and enable checking if no pointer offsetting is out of bounds. However + // checking the absence of of out of bounds offsetting cannot (?) be // done automatically, so one has to launch this test in a debugger. let a = ArcArray::::linspace(0., 7., 8).reshape((8, 1)); let it = a.axis_chunks_iter(Axis(0), 4); diff --git a/tests/windows.rs b/tests/windows.rs index 664616f67..432be5e41 100644 --- a/tests/windows.rs +++ b/tests/windows.rs @@ -116,7 +116,7 @@ fn test_window_zip() { } } -/// Test verifies that non existant Axis results in panic +/// Test verifies that non existent Axis results in panic #[test] #[should_panic] fn axis_windows_outofbound() { From 4aa1b247fd33b9b9553f9019b2a9e4c41d83e77b Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Wed, 2 Mar 2022 10:47:01 +0900 Subject: [PATCH 445/651] Allow macro ndarray::s! in no_std. (#1154) --- src/slice.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/slice.rs b/src/slice.rs index dd0d162e2..0146d6dba 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -842,14 +842,14 @@ macro_rules! s( } }; // empty call, i.e. `s![]` - (@parse ::std::marker::PhantomData::<$crate::Ix0>, ::std::marker::PhantomData::<$crate::Ix0>, []) => { + (@parse ::core::marker::PhantomData::<$crate::Ix0>, ::core::marker::PhantomData::<$crate::Ix0>, []) => { { #[allow(unsafe_code)] unsafe { $crate::SliceInfo::new_unchecked( [], - ::std::marker::PhantomData::<$crate::Ix0>, - ::std::marker::PhantomData::<$crate::Ix0>, + ::core::marker::PhantomData::<$crate::Ix0>, + ::core::marker::PhantomData::<$crate::Ix0>, ) } } @@ -858,18 +858,18 @@ macro_rules! s( (@parse $($t:tt)*) => { compile_error!("Invalid syntax in s![] call.") }; // convert range/index/new-axis into SliceInfoElem (@convert $r:expr) => { - <$crate::SliceInfoElem as ::std::convert::From<_>>::from($r) + <$crate::SliceInfoElem as ::core::convert::From<_>>::from($r) }; // convert range/index/new-axis and step into SliceInfoElem (@convert $r:expr, $s:expr) => { - <$crate::SliceInfoElem as ::std::convert::From<_>>::from( - <$crate::Slice as ::std::convert::From<_>>::from($r).step_by($s as isize) + <$crate::SliceInfoElem as ::core::convert::From<_>>::from( + <$crate::Slice as ::core::convert::From<_>>::from($r).step_by($s as isize) ) }; ($($t:tt)*) => { $crate::s![@parse - ::std::marker::PhantomData::<$crate::Ix0>, - ::std::marker::PhantomData::<$crate::Ix0>, + ::core::marker::PhantomData::<$crate::Ix0>, + ::core::marker::PhantomData::<$crate::Ix0>, [] $($t)* ] From f41ed03b14b78223c743274b1b618a69b1fc1d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hendrik=20Schr=C3=B6ter?= <16517898+Rikorose@users.noreply.github.com> Date: Wed, 18 May 2022 02:26:15 +0200 Subject: [PATCH 446/651] Add link to Slicing docs on libary documentation page (#1164) --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 915714fa4..07e5ed680 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,8 +40,8 @@ //! ## Highlights //! //! - Generic *n*-dimensional array -//! - Slicing, also with arbitrary step size, and negative indices to mean -//! elements from the end of the axis. +//! - [Slicing](ArrayBase#slicing), also with arbitrary step size, and negative +//! indices to mean elements from the end of the axis. //! - Views and subviews of arrays; iterators that yield subviews. //! - Higher order operations and arithmetic are performant //! - Array views can be used to slice and mutate any `[T]` data using From 1063148b1b4963aafb460b38d2c55a32af7d6ba8 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 30 Jul 2022 13:02:12 -0400 Subject: [PATCH 447/651] Release version 0.15.5 --- Cargo.toml | 2 +- RELEASES.md | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a9885f4a8..926ea064c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ndarray" -version = "0.15.4" +version = "0.15.5" edition = "2018" authors = [ "Ulrik Sverdrup \"bluss\"", diff --git a/RELEASES.md b/RELEASES.md index a4bd7e53a..209f4282e 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,22 @@ +Version 0.15.5 (2022-07-30) +=========================== + +Enhancements +------------ + +- The `s!` macro now works in `no_std` environments, by [@makotokato]. + + https://github.com/rust-ndarray/ndarray/pull/1154 + +Other changes +------------- + +- Improve docs and fix typos, by [@steffahn] and [@Rikorose]. + + https://github.com/rust-ndarray/ndarray/pull/1134 + https://github.com/rust-ndarray/ndarray/pull/1164 + + Version 0.15.4 (2021-11-23) =========================== @@ -1562,14 +1581,17 @@ Earlier releases [@LeSeulArtichaut]: https://github.com/LeSeulArtichaut [@lifuyang]: https://github.com/liufuyang [@kdubovikov]: https://github.com/kdubovikov +[@makotokato]: https://github.com/makotokato [@max-sixty]: https://github.com/max-sixty [@mneumann]: https://github.com/mneumann [@mockersf]: https://github.com/mockersf [@nilgoyette]: https://github.com/nilgoyette [@nitsky]: https://github.com/nitsky +[@Rikorose]: https://github.com/Rikorose [@rth]: https://github.com/rth [@sebasv]: https://github.com/sebasv [@SparrowLii]: https://github.com/SparrowLii +[@steffahn]: https://github.com/steffahn [@stokhos]: https://github.com/stokhos [@termoshtt]: https://github.com/termoshtt [@TheLortex]: https://github.com/TheLortex From 6631b48811a054dc393442b758401d698dc910ec Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 30 Jul 2022 13:30:44 -0400 Subject: [PATCH 448/651] Add missing line break in RELEASES.md --- RELEASES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 209f4282e..79909f66c 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -13,7 +13,7 @@ Other changes - Improve docs and fix typos, by [@steffahn] and [@Rikorose]. - https://github.com/rust-ndarray/ndarray/pull/1134 + https://github.com/rust-ndarray/ndarray/pull/1134
https://github.com/rust-ndarray/ndarray/pull/1164 From 7ff66a81adcee603bed1b589829a9f9b921b698d Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 30 Jul 2022 14:08:45 -0400 Subject: [PATCH 449/651] Remove doc(hidden) attr from items in trait impls (#1165) These attributes produce warnings like the following on nightly: ``` warning: `#[doc(hidden)]` is ignored on trait impl items --> src/indexes.rs:212:5 | 212 | #[doc(hidden)] | ^^^^^^^^^^^^^^ help: remove this attribute | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: whether the impl item is `doc(hidden)` or not entirely depends on the corresponding trait item ``` I verified that these attributes have no effect on stable (1.60.0) or nightly (2022-05-16), so they can be safely removed. --- src/data_traits.rs | 1 - src/indexes.rs | 8 -------- src/iterators/macros.rs | 9 +-------- src/iterators/mod.rs | 22 ++++++++-------------- src/private.rs | 2 +- src/zip/ndproducer.rs | 36 ++++-------------------------------- 6 files changed, 14 insertions(+), 64 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index b117792c4..679f9c782 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -630,7 +630,6 @@ where } } - #[doc(hidden)] unsafe fn clone_from_with_ptr( &mut self, other: &Self, diff --git a/src/indexes.rs b/src/indexes.rs index 6f45fb3de..23ae2744c 100644 --- a/src/indexes.rs +++ b/src/indexes.rs @@ -180,22 +180,18 @@ impl NdProducer for Indices { private_impl! {} - #[doc(hidden)] fn raw_dim(&self) -> Self::Dim { self.dim } - #[doc(hidden)] fn equal_dim(&self, dim: &Self::Dim) -> bool { self.dim.equal(dim) } - #[doc(hidden)] fn as_ptr(&self) -> Self::Ptr { IndexPtr { index: self.start } } - #[doc(hidden)] fn layout(&self) -> Layout { if self.dim.ndim() <= 1 { Layout::one_dimensional() @@ -204,19 +200,16 @@ impl NdProducer for Indices { } } - #[doc(hidden)] unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { ptr.index.into_pattern() } - #[doc(hidden)] unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { let mut index = *i; index += &self.start; IndexPtr { index } } - #[doc(hidden)] fn stride_of(&self, axis: Axis) -> Self::Stride { axis.index() } @@ -226,7 +219,6 @@ impl NdProducer for Indices { 0 } - #[doc(hidden)] fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { let start_a = self.start; let mut start_b = start_a; diff --git a/src/iterators/macros.rs b/src/iterators/macros.rs index d3a54453e..ff4b3bb93 100644 --- a/src/iterators/macros.rs +++ b/src/iterators/macros.rs @@ -63,42 +63,34 @@ impl<$($typarm)*> NdProducer for $fulltype { type Ptr = *mut A; type Stride = isize; - #[doc(hidden)] fn raw_dim(&self) -> D { self.$base.raw_dim() } - #[doc(hidden)] fn layout(&self) -> Layout { self.$base.layout() } - #[doc(hidden)] fn as_ptr(&self) -> *mut A { self.$base.as_ptr() as *mut _ } - #[doc(hidden)] fn contiguous_stride(&self) -> isize { self.$base.contiguous_stride() } - #[doc(hidden)] unsafe fn as_ref(&$self_, $ptr: *mut A) -> Self::Item { $refexpr } - #[doc(hidden)] unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { self.$base.uget_ptr(i) } - #[doc(hidden)] fn stride_of(&self, axis: Axis) -> isize { self.$base.stride_of(axis) } - #[doc(hidden)] fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { let (a, b) = self.$base.split_at(axis, index); ($typename { @@ -114,6 +106,7 @@ impl<$($typarm)*> NdProducer for $fulltype { )* }) } + private_impl!{} } diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index b2e4b9c60..e660a55d0 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -1105,15 +1105,14 @@ impl<'a, A, D: Dimension> NdProducer for AxisIter<'a, A, D> { type Ptr = *mut A; type Stride = isize; - #[doc(hidden)] fn layout(&self) -> crate::Layout { crate::Layout::one_dimensional() } - #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { Ix1(self.len()) } - #[doc(hidden)] + fn as_ptr(&self) -> Self::Ptr { if self.len() > 0 { // `self.iter.index` is guaranteed to be in-bounds if any of the @@ -1131,7 +1130,6 @@ impl<'a, A, D: Dimension> NdProducer for AxisIter<'a, A, D> { self.iter.stride } - #[doc(hidden)] unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { ArrayView::new_( ptr, @@ -1139,20 +1137,19 @@ impl<'a, A, D: Dimension> NdProducer for AxisIter<'a, A, D> { self.iter.inner_strides.clone(), ) } - #[doc(hidden)] + unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { self.iter.offset(self.iter.index + i[0]) } - #[doc(hidden)] fn stride_of(&self, _axis: Axis) -> isize { self.contiguous_stride() } - #[doc(hidden)] fn split_at(self, _axis: Axis, index: usize) -> (Self, Self) { self.split_at(index) } + private_impl! {} } @@ -1162,15 +1159,14 @@ impl<'a, A, D: Dimension> NdProducer for AxisIterMut<'a, A, D> { type Ptr = *mut A; type Stride = isize; - #[doc(hidden)] fn layout(&self) -> crate::Layout { crate::Layout::one_dimensional() } - #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { Ix1(self.len()) } - #[doc(hidden)] + fn as_ptr(&self) -> Self::Ptr { if self.len() > 0 { // `self.iter.index` is guaranteed to be in-bounds if any of the @@ -1188,7 +1184,6 @@ impl<'a, A, D: Dimension> NdProducer for AxisIterMut<'a, A, D> { self.iter.stride } - #[doc(hidden)] unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { ArrayViewMut::new_( ptr, @@ -1196,20 +1191,19 @@ impl<'a, A, D: Dimension> NdProducer for AxisIterMut<'a, A, D> { self.iter.inner_strides.clone(), ) } - #[doc(hidden)] + unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { self.iter.offset(self.iter.index + i[0]) } - #[doc(hidden)] fn stride_of(&self, _axis: Axis) -> isize { self.contiguous_stride() } - #[doc(hidden)] fn split_at(self, _axis: Axis, index: usize) -> (Self, Self) { self.split_at(index) } + private_impl! {} } diff --git a/src/private.rs b/src/private.rs index 552b31497..1fa779ff0 100644 --- a/src/private.rs +++ b/src/private.rs @@ -11,13 +11,13 @@ macro_rules! private_decl { () => { /// This trait is private to implement; this method exists to make it /// impossible to implement outside the crate. + #[doc(hidden)] fn __private__(&self) -> crate::private::PrivateMarker; } } macro_rules! private_impl { () => { - #[doc(hidden)] fn __private__(&self) -> crate::private::PrivateMarker { crate::private::PrivateMarker } diff --git a/src/zip/ndproducer.rs b/src/zip/ndproducer.rs index 9829c6afc..0ddf33faf 100644 --- a/src/zip/ndproducer.rs +++ b/src/zip/ndproducer.rs @@ -214,37 +214,31 @@ impl<'a, A, D: Dimension> NdProducer for ArrayView<'a, A, D> { type Stride = isize; private_impl! {} - #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { self.raw_dim() } - #[doc(hidden)] fn equal_dim(&self, dim: &Self::Dim) -> bool { self.dim.equal(dim) } - #[doc(hidden)] fn as_ptr(&self) -> *mut A { self.as_ptr() as _ } - #[doc(hidden)] fn layout(&self) -> Layout { self.layout_impl() } - #[doc(hidden)] unsafe fn as_ref(&self, ptr: *mut A) -> Self::Item { &*ptr } - #[doc(hidden)] unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) } - #[doc(hidden)] fn stride_of(&self, axis: Axis) -> isize { self.stride_of(axis) } @@ -254,7 +248,6 @@ impl<'a, A, D: Dimension> NdProducer for ArrayView<'a, A, D> { 1 } - #[doc(hidden)] fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { self.split_at(axis, index) } @@ -267,37 +260,31 @@ impl<'a, A, D: Dimension> NdProducer for ArrayViewMut<'a, A, D> { type Stride = isize; private_impl! {} - #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { self.raw_dim() } - #[doc(hidden)] fn equal_dim(&self, dim: &Self::Dim) -> bool { self.dim.equal(dim) } - #[doc(hidden)] fn as_ptr(&self) -> *mut A { self.as_ptr() as _ } - #[doc(hidden)] fn layout(&self) -> Layout { self.layout_impl() } - #[doc(hidden)] unsafe fn as_ref(&self, ptr: *mut A) -> Self::Item { &mut *ptr } - #[doc(hidden)] unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) } - #[doc(hidden)] fn stride_of(&self, axis: Axis) -> isize { self.stride_of(axis) } @@ -307,7 +294,6 @@ impl<'a, A, D: Dimension> NdProducer for ArrayViewMut<'a, A, D> { 1 } - #[doc(hidden)] fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { self.split_at(axis, index) } @@ -320,37 +306,31 @@ impl NdProducer for RawArrayView { type Stride = isize; private_impl! {} - #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { self.raw_dim() } - #[doc(hidden)] fn equal_dim(&self, dim: &Self::Dim) -> bool { self.dim.equal(dim) } - #[doc(hidden)] fn as_ptr(&self) -> *const A { self.as_ptr() } - #[doc(hidden)] fn layout(&self) -> Layout { self.layout_impl() } - #[doc(hidden)] unsafe fn as_ref(&self, ptr: *const A) -> *const A { ptr } - #[doc(hidden)] unsafe fn uget_ptr(&self, i: &Self::Dim) -> *const A { self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) } - #[doc(hidden)] fn stride_of(&self, axis: Axis) -> isize { self.stride_of(axis) } @@ -360,7 +340,6 @@ impl NdProducer for RawArrayView { 1 } - #[doc(hidden)] fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { self.split_at(axis, index) } @@ -373,37 +352,31 @@ impl NdProducer for RawArrayViewMut { type Stride = isize; private_impl! {} - #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { self.raw_dim() } - #[doc(hidden)] fn equal_dim(&self, dim: &Self::Dim) -> bool { self.dim.equal(dim) } - #[doc(hidden)] fn as_ptr(&self) -> *mut A { self.as_ptr() as _ } - #[doc(hidden)] fn layout(&self) -> Layout { self.layout_impl() } - #[doc(hidden)] unsafe fn as_ref(&self, ptr: *mut A) -> *mut A { ptr } - #[doc(hidden)] unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) } - #[doc(hidden)] fn stride_of(&self, axis: Axis) -> isize { self.stride_of(axis) } @@ -413,7 +386,6 @@ impl NdProducer for RawArrayViewMut { 1 } - #[doc(hidden)] fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { self.split_at(axis, index) } From 1839ec6b80624b4a37acd01b5dc55578f7e35662 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 30 Jul 2022 14:10:46 -0400 Subject: [PATCH 450/651] Fix description of stack! in quick start (#1156) --- README-quick-start.md | 71 ++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/README-quick-start.md b/README-quick-start.md index 981ef3e9c..dec039dcf 100644 --- a/README-quick-start.md +++ b/README-quick-start.md @@ -352,39 +352,68 @@ c = [4.0, 9.0]], shape=[6, 2], strides=[2, 1], layout=C (0x1), const ndim=2 ``` -### Stacking together different arrays +### Stacking/concatenating together different arrays + +The `stack!` and `concatenate!` macros are helpful for stacking/concatenating +arrays. The `stack!` macro stacks arrays along a new axis, while the +`concatenate!` macro concatenates arrays along an existing axis: -Macro `stack!` is helpful for stacking arrays: ```rust use ndarray::prelude::*; -use ndarray::{Array, Axis, stack}; +use ndarray::{concatenate, stack, Axis}; fn main() { - let a = array![ - [9., 7.], - [5., 2.]]; - + [3., 7., 8.], + [5., 2., 4.], + ]; + let b = array![ - [1., 9.], - [5., 1.]]; - - println!("a vstack b = \n{:?}\n", stack![Axis(0), a, b]); - - println!("a hstack b = \n{:?}\n", stack![Axis(1), a, b]); + [1., 9., 0.], + [5., 4., 1.], + ]; + + println!("stack, axis 0:\n{:?}\n", stack![Axis(0), a, b]); + println!("stack, axis 1:\n{:?}\n", stack![Axis(1), a, b]); + println!("stack, axis 2:\n{:?}\n", stack![Axis(2), a, b]); + println!("concatenate, axis 0:\n{:?}\n", concatenate![Axis(0), a, b]); + println!("concatenate, axis 1:\n{:?}\n", concatenate![Axis(1), a, b]); } ``` The output is: ``` -a vstack b = -[[9.0, 7.0], - [5.0, 2.0], - [1.0, 9.0], - [5.0, 1.0]], shape=[4, 2], strides=[2, 1], layout=C (0x1), const ndim=2 +stack, axis 0: +[[[3.0, 7.0, 8.0], + [5.0, 2.0, 4.0]], + + [[1.0, 9.0, 0.0], + [5.0, 4.0, 1.0]]], shape=[2, 2, 3], strides=[6, 3, 1], layout=Cc (0x5), const ndim=3 + +stack, axis 1: +[[[3.0, 7.0, 8.0], + [1.0, 9.0, 0.0]], + + [[5.0, 2.0, 4.0], + [5.0, 4.0, 1.0]]], shape=[2, 2, 3], strides=[3, 6, 1], layout=c (0x4), const ndim=3 + +stack, axis 2: +[[[3.0, 1.0], + [7.0, 9.0], + [8.0, 0.0]], + + [[5.0, 5.0], + [2.0, 4.0], + [4.0, 1.0]]], shape=[2, 3, 2], strides=[1, 2, 6], layout=Ff (0xa), const ndim=3 + +concatenate, axis 0: +[[3.0, 7.0, 8.0], + [5.0, 2.0, 4.0], + [1.0, 9.0, 0.0], + [5.0, 4.0, 1.0]], shape=[4, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2 -a hstack b = -[[9.0, 7.0, 1.0, 9.0], - [5.0, 2.0, 5.0, 1.0]], shape=[2, 4], strides=[4, 1], layout=C (0x1), const ndim=2 +concatenate, axis 1: +[[3.0, 7.0, 8.0, 1.0, 9.0, 0.0], + [5.0, 2.0, 4.0, 5.0, 4.0, 1.0]], shape=[2, 6], strides=[1, 2], layout=Ff (0xa), const ndim=2 ``` ### Splitting one array into several smaller ones From e7f098cdfd7d6cdb12500e14b012069042c4a535 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 30 Jul 2022 15:39:06 -0400 Subject: [PATCH 451/651] Enable CI for 0.15.x branch (#1188) --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a44568ae8..762c534a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,8 +1,8 @@ on: push: - branches: [ master ] + branches: [ master, 0.15.x ] pull_request: - branches: [ master ] + branches: [ master, 0.15.x ] name: Continuous integration From 3c92c51ce75d583d4bc199931a1d8aeec96d86af Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 30 Jul 2022 13:30:44 -0400 Subject: [PATCH 452/651] Add missing line break in RELEASES.md --- RELEASES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 209f4282e..79909f66c 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -13,7 +13,7 @@ Other changes - Improve docs and fix typos, by [@steffahn] and [@Rikorose]. - https://github.com/rust-ndarray/ndarray/pull/1134 + https://github.com/rust-ndarray/ndarray/pull/1134
https://github.com/rust-ndarray/ndarray/pull/1164 From 0b80af3ddad6727cebb5f55f9e4dff548f3b16a4 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 30 Jul 2022 14:08:45 -0400 Subject: [PATCH 453/651] Remove doc(hidden) attr from items in trait impls (#1165) These attributes produce warnings like the following on nightly: ``` warning: `#[doc(hidden)]` is ignored on trait impl items --> src/indexes.rs:212:5 | 212 | #[doc(hidden)] | ^^^^^^^^^^^^^^ help: remove this attribute | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: whether the impl item is `doc(hidden)` or not entirely depends on the corresponding trait item ``` I verified that these attributes have no effect on stable (1.60.0) or nightly (2022-05-16), so they can be safely removed. --- src/data_traits.rs | 1 - src/indexes.rs | 8 -------- src/iterators/macros.rs | 9 +-------- src/iterators/mod.rs | 22 ++++++++-------------- src/private.rs | 2 +- src/zip/ndproducer.rs | 36 ++++-------------------------------- 6 files changed, 14 insertions(+), 64 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index b117792c4..679f9c782 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -630,7 +630,6 @@ where } } - #[doc(hidden)] unsafe fn clone_from_with_ptr( &mut self, other: &Self, diff --git a/src/indexes.rs b/src/indexes.rs index 6f45fb3de..23ae2744c 100644 --- a/src/indexes.rs +++ b/src/indexes.rs @@ -180,22 +180,18 @@ impl NdProducer for Indices { private_impl! {} - #[doc(hidden)] fn raw_dim(&self) -> Self::Dim { self.dim } - #[doc(hidden)] fn equal_dim(&self, dim: &Self::Dim) -> bool { self.dim.equal(dim) } - #[doc(hidden)] fn as_ptr(&self) -> Self::Ptr { IndexPtr { index: self.start } } - #[doc(hidden)] fn layout(&self) -> Layout { if self.dim.ndim() <= 1 { Layout::one_dimensional() @@ -204,19 +200,16 @@ impl NdProducer for Indices { } } - #[doc(hidden)] unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { ptr.index.into_pattern() } - #[doc(hidden)] unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { let mut index = *i; index += &self.start; IndexPtr { index } } - #[doc(hidden)] fn stride_of(&self, axis: Axis) -> Self::Stride { axis.index() } @@ -226,7 +219,6 @@ impl NdProducer for Indices { 0 } - #[doc(hidden)] fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { let start_a = self.start; let mut start_b = start_a; diff --git a/src/iterators/macros.rs b/src/iterators/macros.rs index d3a54453e..ff4b3bb93 100644 --- a/src/iterators/macros.rs +++ b/src/iterators/macros.rs @@ -63,42 +63,34 @@ impl<$($typarm)*> NdProducer for $fulltype { type Ptr = *mut A; type Stride = isize; - #[doc(hidden)] fn raw_dim(&self) -> D { self.$base.raw_dim() } - #[doc(hidden)] fn layout(&self) -> Layout { self.$base.layout() } - #[doc(hidden)] fn as_ptr(&self) -> *mut A { self.$base.as_ptr() as *mut _ } - #[doc(hidden)] fn contiguous_stride(&self) -> isize { self.$base.contiguous_stride() } - #[doc(hidden)] unsafe fn as_ref(&$self_, $ptr: *mut A) -> Self::Item { $refexpr } - #[doc(hidden)] unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { self.$base.uget_ptr(i) } - #[doc(hidden)] fn stride_of(&self, axis: Axis) -> isize { self.$base.stride_of(axis) } - #[doc(hidden)] fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { let (a, b) = self.$base.split_at(axis, index); ($typename { @@ -114,6 +106,7 @@ impl<$($typarm)*> NdProducer for $fulltype { )* }) } + private_impl!{} } diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index b2e4b9c60..e660a55d0 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -1105,15 +1105,14 @@ impl<'a, A, D: Dimension> NdProducer for AxisIter<'a, A, D> { type Ptr = *mut A; type Stride = isize; - #[doc(hidden)] fn layout(&self) -> crate::Layout { crate::Layout::one_dimensional() } - #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { Ix1(self.len()) } - #[doc(hidden)] + fn as_ptr(&self) -> Self::Ptr { if self.len() > 0 { // `self.iter.index` is guaranteed to be in-bounds if any of the @@ -1131,7 +1130,6 @@ impl<'a, A, D: Dimension> NdProducer for AxisIter<'a, A, D> { self.iter.stride } - #[doc(hidden)] unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { ArrayView::new_( ptr, @@ -1139,20 +1137,19 @@ impl<'a, A, D: Dimension> NdProducer for AxisIter<'a, A, D> { self.iter.inner_strides.clone(), ) } - #[doc(hidden)] + unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { self.iter.offset(self.iter.index + i[0]) } - #[doc(hidden)] fn stride_of(&self, _axis: Axis) -> isize { self.contiguous_stride() } - #[doc(hidden)] fn split_at(self, _axis: Axis, index: usize) -> (Self, Self) { self.split_at(index) } + private_impl! {} } @@ -1162,15 +1159,14 @@ impl<'a, A, D: Dimension> NdProducer for AxisIterMut<'a, A, D> { type Ptr = *mut A; type Stride = isize; - #[doc(hidden)] fn layout(&self) -> crate::Layout { crate::Layout::one_dimensional() } - #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { Ix1(self.len()) } - #[doc(hidden)] + fn as_ptr(&self) -> Self::Ptr { if self.len() > 0 { // `self.iter.index` is guaranteed to be in-bounds if any of the @@ -1188,7 +1184,6 @@ impl<'a, A, D: Dimension> NdProducer for AxisIterMut<'a, A, D> { self.iter.stride } - #[doc(hidden)] unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { ArrayViewMut::new_( ptr, @@ -1196,20 +1191,19 @@ impl<'a, A, D: Dimension> NdProducer for AxisIterMut<'a, A, D> { self.iter.inner_strides.clone(), ) } - #[doc(hidden)] + unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { self.iter.offset(self.iter.index + i[0]) } - #[doc(hidden)] fn stride_of(&self, _axis: Axis) -> isize { self.contiguous_stride() } - #[doc(hidden)] fn split_at(self, _axis: Axis, index: usize) -> (Self, Self) { self.split_at(index) } + private_impl! {} } diff --git a/src/private.rs b/src/private.rs index 552b31497..1fa779ff0 100644 --- a/src/private.rs +++ b/src/private.rs @@ -11,13 +11,13 @@ macro_rules! private_decl { () => { /// This trait is private to implement; this method exists to make it /// impossible to implement outside the crate. + #[doc(hidden)] fn __private__(&self) -> crate::private::PrivateMarker; } } macro_rules! private_impl { () => { - #[doc(hidden)] fn __private__(&self) -> crate::private::PrivateMarker { crate::private::PrivateMarker } diff --git a/src/zip/ndproducer.rs b/src/zip/ndproducer.rs index 0ec49f3ac..2c6d14e71 100644 --- a/src/zip/ndproducer.rs +++ b/src/zip/ndproducer.rs @@ -195,37 +195,31 @@ impl<'a, A, D: Dimension> NdProducer for ArrayView<'a, A, D> { type Stride = isize; private_impl! {} - #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { self.raw_dim() } - #[doc(hidden)] fn equal_dim(&self, dim: &Self::Dim) -> bool { self.dim.equal(dim) } - #[doc(hidden)] fn as_ptr(&self) -> *mut A { self.as_ptr() as _ } - #[doc(hidden)] fn layout(&self) -> Layout { self.layout_impl() } - #[doc(hidden)] unsafe fn as_ref(&self, ptr: *mut A) -> Self::Item { &*ptr } - #[doc(hidden)] unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) } - #[doc(hidden)] fn stride_of(&self, axis: Axis) -> isize { self.stride_of(axis) } @@ -235,7 +229,6 @@ impl<'a, A, D: Dimension> NdProducer for ArrayView<'a, A, D> { 1 } - #[doc(hidden)] fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { self.split_at(axis, index) } @@ -248,37 +241,31 @@ impl<'a, A, D: Dimension> NdProducer for ArrayViewMut<'a, A, D> { type Stride = isize; private_impl! {} - #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { self.raw_dim() } - #[doc(hidden)] fn equal_dim(&self, dim: &Self::Dim) -> bool { self.dim.equal(dim) } - #[doc(hidden)] fn as_ptr(&self) -> *mut A { self.as_ptr() as _ } - #[doc(hidden)] fn layout(&self) -> Layout { self.layout_impl() } - #[doc(hidden)] unsafe fn as_ref(&self, ptr: *mut A) -> Self::Item { &mut *ptr } - #[doc(hidden)] unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) } - #[doc(hidden)] fn stride_of(&self, axis: Axis) -> isize { self.stride_of(axis) } @@ -288,7 +275,6 @@ impl<'a, A, D: Dimension> NdProducer for ArrayViewMut<'a, A, D> { 1 } - #[doc(hidden)] fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { self.split_at(axis, index) } @@ -301,37 +287,31 @@ impl NdProducer for RawArrayView { type Stride = isize; private_impl! {} - #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { self.raw_dim() } - #[doc(hidden)] fn equal_dim(&self, dim: &Self::Dim) -> bool { self.dim.equal(dim) } - #[doc(hidden)] fn as_ptr(&self) -> *const A { self.as_ptr() } - #[doc(hidden)] fn layout(&self) -> Layout { self.layout_impl() } - #[doc(hidden)] unsafe fn as_ref(&self, ptr: *const A) -> *const A { ptr } - #[doc(hidden)] unsafe fn uget_ptr(&self, i: &Self::Dim) -> *const A { self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) } - #[doc(hidden)] fn stride_of(&self, axis: Axis) -> isize { self.stride_of(axis) } @@ -341,7 +321,6 @@ impl NdProducer for RawArrayView { 1 } - #[doc(hidden)] fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { self.split_at(axis, index) } @@ -354,37 +333,31 @@ impl NdProducer for RawArrayViewMut { type Stride = isize; private_impl! {} - #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { self.raw_dim() } - #[doc(hidden)] fn equal_dim(&self, dim: &Self::Dim) -> bool { self.dim.equal(dim) } - #[doc(hidden)] fn as_ptr(&self) -> *mut A { self.as_ptr() as _ } - #[doc(hidden)] fn layout(&self) -> Layout { self.layout_impl() } - #[doc(hidden)] unsafe fn as_ref(&self, ptr: *mut A) -> *mut A { ptr } - #[doc(hidden)] unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) } - #[doc(hidden)] fn stride_of(&self, axis: Axis) -> isize { self.stride_of(axis) } @@ -394,7 +367,6 @@ impl NdProducer for RawArrayViewMut { 1 } - #[doc(hidden)] fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { self.split_at(axis, index) } From dc561aa22ef62969e5602ff0c38cae856493beb6 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 30 Jul 2022 14:10:46 -0400 Subject: [PATCH 454/651] Fix description of stack! in quick start (#1156) --- README-quick-start.md | 71 ++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/README-quick-start.md b/README-quick-start.md index 981ef3e9c..dec039dcf 100644 --- a/README-quick-start.md +++ b/README-quick-start.md @@ -352,39 +352,68 @@ c = [4.0, 9.0]], shape=[6, 2], strides=[2, 1], layout=C (0x1), const ndim=2 ``` -### Stacking together different arrays +### Stacking/concatenating together different arrays + +The `stack!` and `concatenate!` macros are helpful for stacking/concatenating +arrays. The `stack!` macro stacks arrays along a new axis, while the +`concatenate!` macro concatenates arrays along an existing axis: -Macro `stack!` is helpful for stacking arrays: ```rust use ndarray::prelude::*; -use ndarray::{Array, Axis, stack}; +use ndarray::{concatenate, stack, Axis}; fn main() { - let a = array![ - [9., 7.], - [5., 2.]]; - + [3., 7., 8.], + [5., 2., 4.], + ]; + let b = array![ - [1., 9.], - [5., 1.]]; - - println!("a vstack b = \n{:?}\n", stack![Axis(0), a, b]); - - println!("a hstack b = \n{:?}\n", stack![Axis(1), a, b]); + [1., 9., 0.], + [5., 4., 1.], + ]; + + println!("stack, axis 0:\n{:?}\n", stack![Axis(0), a, b]); + println!("stack, axis 1:\n{:?}\n", stack![Axis(1), a, b]); + println!("stack, axis 2:\n{:?}\n", stack![Axis(2), a, b]); + println!("concatenate, axis 0:\n{:?}\n", concatenate![Axis(0), a, b]); + println!("concatenate, axis 1:\n{:?}\n", concatenate![Axis(1), a, b]); } ``` The output is: ``` -a vstack b = -[[9.0, 7.0], - [5.0, 2.0], - [1.0, 9.0], - [5.0, 1.0]], shape=[4, 2], strides=[2, 1], layout=C (0x1), const ndim=2 +stack, axis 0: +[[[3.0, 7.0, 8.0], + [5.0, 2.0, 4.0]], + + [[1.0, 9.0, 0.0], + [5.0, 4.0, 1.0]]], shape=[2, 2, 3], strides=[6, 3, 1], layout=Cc (0x5), const ndim=3 + +stack, axis 1: +[[[3.0, 7.0, 8.0], + [1.0, 9.0, 0.0]], + + [[5.0, 2.0, 4.0], + [5.0, 4.0, 1.0]]], shape=[2, 2, 3], strides=[3, 6, 1], layout=c (0x4), const ndim=3 + +stack, axis 2: +[[[3.0, 1.0], + [7.0, 9.0], + [8.0, 0.0]], + + [[5.0, 5.0], + [2.0, 4.0], + [4.0, 1.0]]], shape=[2, 3, 2], strides=[1, 2, 6], layout=Ff (0xa), const ndim=3 + +concatenate, axis 0: +[[3.0, 7.0, 8.0], + [5.0, 2.0, 4.0], + [1.0, 9.0, 0.0], + [5.0, 4.0, 1.0]], shape=[4, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2 -a hstack b = -[[9.0, 7.0, 1.0, 9.0], - [5.0, 2.0, 5.0, 1.0]], shape=[2, 4], strides=[4, 1], layout=C (0x1), const ndim=2 +concatenate, axis 1: +[[3.0, 7.0, 8.0, 1.0, 9.0, 0.0], + [5.0, 2.0, 4.0, 5.0, 4.0, 1.0]], shape=[2, 6], strides=[1, 2], layout=Ff (0xa), const ndim=2 ``` ### Splitting one array into several smaller ones From 66a4b380f8c3dcec2064acba3f7711c465f64a3d Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Thu, 16 Jun 2022 12:15:23 -0400 Subject: [PATCH 455/651] fix(ci): lint with clippy --- src/data_traits.rs | 1 + src/impl_constructors.rs | 1 + src/iterators/lanes.rs | 18 ++++++++---------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index 679f9c782..acf4b0b7a 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -162,6 +162,7 @@ pub unsafe trait DataMut: Data + RawDataMut { /// Returns whether the array has unique access to its data. #[doc(hidden)] #[inline] + #[allow(clippy::wrong_self_convention)] // mut needed for Arc types fn is_unique(&mut self) -> bool { self.try_is_unique().unwrap() } diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 4a18ff832..049384ceb 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -634,6 +634,7 @@ where #[deprecated(note = "This method is hard to use correctly. Use `uninit` instead.", since = "0.15.0")] + #[allow(clippy::uninit_vec)] // this is explicitly intended to create uninitialized memory /// Create an array with uninitialized elements, shape `shape`. /// /// Prefer to use [`uninit()`](ArrayBase::uninit) if possible, because it is diff --git a/src/iterators/lanes.rs b/src/iterators/lanes.rs index cda783a80..7286e0696 100644 --- a/src/iterators/lanes.rs +++ b/src/iterators/lanes.rs @@ -39,17 +39,16 @@ impl<'a, A, D: Dimension> Lanes<'a, A, D> { let ndim = v.ndim(); let len; let stride; - let iter_v; - if ndim == 0 { + let iter_v = if ndim == 0 { len = 1; stride = 1; - iter_v = v.try_remove_axis(Axis(0)) + v.try_remove_axis(Axis(0)) } else { let i = axis.index(); len = v.dim[i]; stride = v.strides[i] as isize; - iter_v = v.try_remove_axis(axis) - } + v.try_remove_axis(axis) + }; Lanes { inner_len: len, inner_stride: stride, @@ -108,17 +107,16 @@ impl<'a, A, D: Dimension> LanesMut<'a, A, D> { let ndim = v.ndim(); let len; let stride; - let iter_v; - if ndim == 0 { + let iter_v = if ndim == 0 { len = 1; stride = 1; - iter_v = v.try_remove_axis(Axis(0)) + v.try_remove_axis(Axis(0)) } else { let i = axis.index(); len = v.dim[i]; stride = v.strides[i] as isize; - iter_v = v.try_remove_axis(axis) - } + v.try_remove_axis(axis) + }; LanesMut { inner_len: len, inner_stride: stride, From d9cf511e494c32507608a399257a72aaa89e8873 Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Thu, 16 Jun 2022 15:04:03 -0400 Subject: [PATCH 456/651] fix(clippy): remove unused lifetimes from trait impls --- src/arrayformat.rs | 12 ++++++------ src/arraytraits.rs | 2 +- src/dimension/mod.rs | 2 +- src/iterators/mod.rs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/arrayformat.rs b/src/arrayformat.rs index 5998a0c1e..428390660 100644 --- a/src/arrayformat.rs +++ b/src/arrayformat.rs @@ -189,7 +189,7 @@ where /// to each element. /// /// The array is shown in multiline style. -impl<'a, A: fmt::Display, S, D: Dimension> fmt::Display for ArrayBase +impl fmt::Display for ArrayBase where S: Data, { @@ -203,7 +203,7 @@ where /// to each element. /// /// The array is shown in multiline style. -impl<'a, A: fmt::Debug, S, D: Dimension> fmt::Debug for ArrayBase +impl fmt::Debug for ArrayBase where S: Data, { @@ -231,7 +231,7 @@ where /// to each element. /// /// The array is shown in multiline style. -impl<'a, A: fmt::LowerExp, S, D: Dimension> fmt::LowerExp for ArrayBase +impl fmt::LowerExp for ArrayBase where S: Data, { @@ -245,7 +245,7 @@ where /// to each element. /// /// The array is shown in multiline style. -impl<'a, A: fmt::UpperExp, S, D: Dimension> fmt::UpperExp for ArrayBase +impl fmt::UpperExp for ArrayBase where S: Data, { @@ -258,7 +258,7 @@ where /// to each element. /// /// The array is shown in multiline style. -impl<'a, A: fmt::LowerHex, S, D: Dimension> fmt::LowerHex for ArrayBase +impl fmt::LowerHex for ArrayBase where S: Data, { @@ -272,7 +272,7 @@ where /// to each element. /// /// The array is shown in multiline style. -impl<'a, A: fmt::Binary, S, D: Dimension> fmt::Binary for ArrayBase +impl fmt::Binary for ArrayBase where S: Data, { diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 7d84dc5f2..74ce9d644 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -252,7 +252,7 @@ where } } -impl<'a, S, D> hash::Hash for ArrayBase +impl hash::Hash for ArrayBase where D: Dimension, S: Data, diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index f0752142b..2ba3b1ffa 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -333,7 +333,7 @@ where } } -impl<'a> DimensionExt for [Ix] { +impl DimensionExt for [Ix] { #[inline] fn axis(&self, axis: Axis) -> Ix { self[axis.index()] diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index e660a55d0..85da56fe5 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -103,7 +103,7 @@ impl Iterator for Baseiter { } } -impl<'a, A, D: Dimension> ExactSizeIterator for Baseiter { +impl ExactSizeIterator for Baseiter { fn len(&self) -> usize { match self.index { None => 0, From 70db56128b601c365017a71afc9ef5b35a80807c Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 30 Jul 2022 15:21:05 -0400 Subject: [PATCH 457/651] Add MSRV to Cargo.toml --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 926ea064c..2b0b93b3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ name = "ndarray" version = "0.15.5" edition = "2018" +rust-version = "1.51" authors = [ "Ulrik Sverdrup \"bluss\"", "Jim Turner" From 51203e528a850cd60a321015181008467192f1b0 Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Thu, 16 Jun 2022 15:14:04 -0400 Subject: [PATCH 458/651] fix(clippy): change abs() as usize to unsigned_abs() --- src/dimension/dimension_trait.rs | 2 +- src/dimension/mod.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index a4d73b1b1..6ebb9cad4 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -293,7 +293,7 @@ pub trait Dimension: let mut cstride = 1; for &i in order.slice() { // a dimension of length 1 can have unequal strides - if dim_slice[i] != 1 && (strides[i] as isize).abs() as usize != cstride { + if dim_slice[i] != 1 && (strides[i] as isize).unsigned_abs() != cstride { return false; } cstride *= dim_slice[i]; diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 2ba3b1ffa..dd34052ec 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -185,7 +185,7 @@ where .try_fold(0usize, |acc, (&d, &s)| { let s = s as isize; // Calculate maximum possible absolute movement along this axis. - let off = d.saturating_sub(1).checked_mul(s.abs() as usize)?; + let off = d.saturating_sub(1).checked_mul(s.unsigned_abs())?; acc.checked_add(off) }) .ok_or_else(|| from_kind(ErrorKind::Overflow))?; @@ -457,7 +457,7 @@ pub fn do_slice(dim: &mut usize, stride: &mut usize, slice: Slice) -> isize { }; // Update dimension. - let abs_step = step.abs() as usize; + let abs_step = step.unsigned_abs(); *dim = if abs_step == 1 { m } else { @@ -651,7 +651,7 @@ pub fn slices_intersect( Some(m) => m, None => return false, }; - if ind < min || ind > max || (ind - min) % step.abs() as usize != 0 { + if ind < min || ind > max || (ind - min) % step.unsigned_abs() != 0 { return false; } } From d7aec2a27a186bdf12983929cf23a425e8390a54 Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Thu, 16 Jun 2022 15:31:10 -0400 Subject: [PATCH 459/651] fix(doctest): allow warning of unused #[macro_use] in itertools doctest --- src/itertools.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/itertools.rs b/src/itertools.rs index 4fa8145ab..8edfd75eb 100644 --- a/src/itertools.rs +++ b/src/itertools.rs @@ -86,7 +86,8 @@ where /// **Note:** To enable the macros in this crate, use the `#[macro_use]` /// attribute when importing the crate: /// -/// ``` +/// ```no_run +/// # #[allow(unused_imports)] /// #[macro_use] extern crate itertools; /// # fn main() { } /// ``` From 84aa5f981576257acf353366ce93f7fd54e28f43 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 30 Jul 2022 14:35:43 -0400 Subject: [PATCH 460/651] fix(clippy): remove unnecessary derefs --- src/dimension/dynindeximpl.rs | 4 ++-- src/dimension/ndindex.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dimension/dynindeximpl.rs b/src/dimension/dynindeximpl.rs index b75db91c5..85b34bdbe 100644 --- a/src/dimension/dynindeximpl.rs +++ b/src/dimension/dynindeximpl.rs @@ -21,7 +21,7 @@ impl Deref for IxDynRepr { debug_assert!(len as usize <= ar.len()); unsafe { ar.get_unchecked(..len as usize) } } - IxDynRepr::Alloc(ref ar) => &*ar, + IxDynRepr::Alloc(ref ar) => ar, } } } @@ -33,7 +33,7 @@ impl DerefMut for IxDynRepr { debug_assert!(len as usize <= ar.len()); unsafe { ar.get_unchecked_mut(..len as usize) } } - IxDynRepr::Alloc(ref mut ar) => &mut *ar, + IxDynRepr::Alloc(ref mut ar) => ar, } } } diff --git a/src/dimension/ndindex.rs b/src/dimension/ndindex.rs index 718ee059b..4d4046bc8 100644 --- a/src/dimension/ndindex.rs +++ b/src/dimension/ndindex.rs @@ -230,7 +230,7 @@ unsafe impl<'a> NdIndex for &'a IxDyn { unsafe impl<'a> NdIndex for &'a [Ix] { fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { - stride_offset_checked(dim.ix(), strides.ix(), *self) + stride_offset_checked(dim.ix(), strides.ix(), self) } fn index_unchecked(&self, strides: &IxDyn) -> isize { zip(strides.ix(), *self) From f1fd0d43104b84a826a3f8092d03049e3e14f885 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 30 Jul 2022 14:36:13 -0400 Subject: [PATCH 461/651] fix(clippy): remove unnecessary keyword in format --- src/arrayformat.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arrayformat.rs b/src/arrayformat.rs index 428390660..ae781df6a 100644 --- a/src/arrayformat.rs +++ b/src/arrayformat.rs @@ -217,7 +217,7 @@ where ", shape={:?}, strides={:?}, layout={:?}", self.shape(), self.strides(), - layout = self.view().layout() + self.view().layout(), )?; match D::NDIM { Some(ndim) => write!(f, ", const ndim={}", ndim)?, From 739316e8fc550ec2c6b7a0f9d6ef8aca7e6d990e Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 30 Jul 2022 16:12:43 -0400 Subject: [PATCH 462/651] Add MSRV to Cargo.toml (#1191) --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 926ea064c..1d21ac642 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ name = "ndarray" version = "0.15.5" edition = "2018" +rust-version = "1.49" authors = [ "Ulrik Sverdrup \"bluss\"", "Jim Turner" From ff74996f1610a0055840ca1951793754b93f97d6 Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Thu, 16 Jun 2022 12:15:23 -0400 Subject: [PATCH 463/651] fix(ci): lint with clippy --- src/data_traits.rs | 1 + src/impl_constructors.rs | 1 + src/iterators/lanes.rs | 18 ++++++++---------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index 679f9c782..acf4b0b7a 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -162,6 +162,7 @@ pub unsafe trait DataMut: Data + RawDataMut { /// Returns whether the array has unique access to its data. #[doc(hidden)] #[inline] + #[allow(clippy::wrong_self_convention)] // mut needed for Arc types fn is_unique(&mut self) -> bool { self.try_is_unique().unwrap() } diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 4a18ff832..049384ceb 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -634,6 +634,7 @@ where #[deprecated(note = "This method is hard to use correctly. Use `uninit` instead.", since = "0.15.0")] + #[allow(clippy::uninit_vec)] // this is explicitly intended to create uninitialized memory /// Create an array with uninitialized elements, shape `shape`. /// /// Prefer to use [`uninit()`](ArrayBase::uninit) if possible, because it is diff --git a/src/iterators/lanes.rs b/src/iterators/lanes.rs index cda783a80..7286e0696 100644 --- a/src/iterators/lanes.rs +++ b/src/iterators/lanes.rs @@ -39,17 +39,16 @@ impl<'a, A, D: Dimension> Lanes<'a, A, D> { let ndim = v.ndim(); let len; let stride; - let iter_v; - if ndim == 0 { + let iter_v = if ndim == 0 { len = 1; stride = 1; - iter_v = v.try_remove_axis(Axis(0)) + v.try_remove_axis(Axis(0)) } else { let i = axis.index(); len = v.dim[i]; stride = v.strides[i] as isize; - iter_v = v.try_remove_axis(axis) - } + v.try_remove_axis(axis) + }; Lanes { inner_len: len, inner_stride: stride, @@ -108,17 +107,16 @@ impl<'a, A, D: Dimension> LanesMut<'a, A, D> { let ndim = v.ndim(); let len; let stride; - let iter_v; - if ndim == 0 { + let iter_v = if ndim == 0 { len = 1; stride = 1; - iter_v = v.try_remove_axis(Axis(0)) + v.try_remove_axis(Axis(0)) } else { let i = axis.index(); len = v.dim[i]; stride = v.strides[i] as isize; - iter_v = v.try_remove_axis(axis) - } + v.try_remove_axis(axis) + }; LanesMut { inner_len: len, inner_stride: stride, From cd6fc28f2db4b393c4eca33521148cf67a911cb6 Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Thu, 16 Jun 2022 15:04:03 -0400 Subject: [PATCH 464/651] fix(clippy): remove unused lifetimes from trait impls --- src/arrayformat.rs | 12 ++++++------ src/arraytraits.rs | 2 +- src/dimension/mod.rs | 2 +- src/iterators/mod.rs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/arrayformat.rs b/src/arrayformat.rs index 5998a0c1e..428390660 100644 --- a/src/arrayformat.rs +++ b/src/arrayformat.rs @@ -189,7 +189,7 @@ where /// to each element. /// /// The array is shown in multiline style. -impl<'a, A: fmt::Display, S, D: Dimension> fmt::Display for ArrayBase +impl fmt::Display for ArrayBase where S: Data, { @@ -203,7 +203,7 @@ where /// to each element. /// /// The array is shown in multiline style. -impl<'a, A: fmt::Debug, S, D: Dimension> fmt::Debug for ArrayBase +impl fmt::Debug for ArrayBase where S: Data, { @@ -231,7 +231,7 @@ where /// to each element. /// /// The array is shown in multiline style. -impl<'a, A: fmt::LowerExp, S, D: Dimension> fmt::LowerExp for ArrayBase +impl fmt::LowerExp for ArrayBase where S: Data, { @@ -245,7 +245,7 @@ where /// to each element. /// /// The array is shown in multiline style. -impl<'a, A: fmt::UpperExp, S, D: Dimension> fmt::UpperExp for ArrayBase +impl fmt::UpperExp for ArrayBase where S: Data, { @@ -258,7 +258,7 @@ where /// to each element. /// /// The array is shown in multiline style. -impl<'a, A: fmt::LowerHex, S, D: Dimension> fmt::LowerHex for ArrayBase +impl fmt::LowerHex for ArrayBase where S: Data, { @@ -272,7 +272,7 @@ where /// to each element. /// /// The array is shown in multiline style. -impl<'a, A: fmt::Binary, S, D: Dimension> fmt::Binary for ArrayBase +impl fmt::Binary for ArrayBase where S: Data, { diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 31d27b003..f51f5335e 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -252,7 +252,7 @@ where } } -impl<'a, S, D> hash::Hash for ArrayBase +impl hash::Hash for ArrayBase where D: Dimension, S: Data, diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index f0752142b..2ba3b1ffa 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -333,7 +333,7 @@ where } } -impl<'a> DimensionExt for [Ix] { +impl DimensionExt for [Ix] { #[inline] fn axis(&self, axis: Axis) -> Ix { self[axis.index()] diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index e660a55d0..85da56fe5 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -103,7 +103,7 @@ impl Iterator for Baseiter { } } -impl<'a, A, D: Dimension> ExactSizeIterator for Baseiter { +impl ExactSizeIterator for Baseiter { fn len(&self) -> usize { match self.index { None => 0, From 45a1ded3d7ab79bbf65f1accfb4db905a6fe3fe2 Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Thu, 16 Jun 2022 15:31:10 -0400 Subject: [PATCH 465/651] fix(doctest): allow warning of unused #[macro_use] in itertools doctest --- src/itertools.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/itertools.rs b/src/itertools.rs index 4fa8145ab..8edfd75eb 100644 --- a/src/itertools.rs +++ b/src/itertools.rs @@ -86,7 +86,8 @@ where /// **Note:** To enable the macros in this crate, use the `#[macro_use]` /// attribute when importing the crate: /// -/// ``` +/// ```no_run +/// # #[allow(unused_imports)] /// #[macro_use] extern crate itertools; /// # fn main() { } /// ``` From a6c8bd2147c9c3d8bb512bd3af084130135a257b Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 30 Jul 2022 14:35:43 -0400 Subject: [PATCH 466/651] fix(clippy): remove unnecessary derefs --- src/dimension/dynindeximpl.rs | 4 ++-- src/dimension/ndindex.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dimension/dynindeximpl.rs b/src/dimension/dynindeximpl.rs index b75db91c5..85b34bdbe 100644 --- a/src/dimension/dynindeximpl.rs +++ b/src/dimension/dynindeximpl.rs @@ -21,7 +21,7 @@ impl Deref for IxDynRepr { debug_assert!(len as usize <= ar.len()); unsafe { ar.get_unchecked(..len as usize) } } - IxDynRepr::Alloc(ref ar) => &*ar, + IxDynRepr::Alloc(ref ar) => ar, } } } @@ -33,7 +33,7 @@ impl DerefMut for IxDynRepr { debug_assert!(len as usize <= ar.len()); unsafe { ar.get_unchecked_mut(..len as usize) } } - IxDynRepr::Alloc(ref mut ar) => &mut *ar, + IxDynRepr::Alloc(ref mut ar) => ar, } } } diff --git a/src/dimension/ndindex.rs b/src/dimension/ndindex.rs index 810a5b097..3a687c813 100644 --- a/src/dimension/ndindex.rs +++ b/src/dimension/ndindex.rs @@ -216,7 +216,7 @@ unsafe impl<'a> NdIndex for &'a IxDyn { unsafe impl<'a> NdIndex for &'a [Ix] { fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { - stride_offset_checked(dim.ix(), strides.ix(), *self) + stride_offset_checked(dim.ix(), strides.ix(), self) } fn index_unchecked(&self, strides: &IxDyn) -> isize { zip(strides.ix(), *self) From 1d0212c6985c7178fa4a3f2c60367a6039914f03 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 30 Jul 2022 14:36:13 -0400 Subject: [PATCH 467/651] fix(clippy): remove unnecessary keyword in format --- src/arrayformat.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arrayformat.rs b/src/arrayformat.rs index 428390660..ae781df6a 100644 --- a/src/arrayformat.rs +++ b/src/arrayformat.rs @@ -217,7 +217,7 @@ where ", shape={:?}, strides={:?}, layout={:?}", self.shape(), self.strides(), - layout = self.view().layout() + self.view().layout(), )?; match D::NDIM { Some(ndim) => write!(f, ", const ndim={}", ndim)?, From 2f0b37b7113262a3e613300dcdeca5d5fb4c023e Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Sat, 22 Jan 2022 11:31:34 +0100 Subject: [PATCH 468/651] Make ArrayBase::get_ptr(_mut) public to enable indexing into raw views. --- src/impl_methods.rs | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 745a8e60b..59b3e4a73 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -732,13 +732,26 @@ where /// ``` pub fn get(&self, index: I) -> Option<&A> where - I: NdIndex, S: Data, + I: NdIndex, { unsafe { self.get_ptr(index).map(|ptr| &*ptr) } } - pub(crate) fn get_ptr(&self, index: I) -> Option<*const A> + /// Return a raw pointer to the element at `index`, or return `None` + /// if the index is out of bounds. + /// + /// ``` + /// use ndarray::arr2; + /// + /// let a = arr2(&[[1., 2.], [3., 4.]]); + /// + /// let v = a.raw_view(); + /// let p = a.get_ptr((0, 1)).unwrap(); + /// + /// assert_eq!(unsafe { *p }, 2.); + /// ``` + pub fn get_ptr(&self, index: I) -> Option<*const A> where I: NdIndex, { @@ -758,7 +771,24 @@ where unsafe { self.get_ptr_mut(index).map(|ptr| &mut *ptr) } } - pub(crate) fn get_ptr_mut(&mut self, index: I) -> Option<*mut A> + /// Return a raw pointer to the element at `index`, or return `None` + /// if the index is out of bounds. + /// + /// ``` + /// use ndarray::arr2; + /// + /// let mut a = arr2(&[[1., 2.], [3., 4.]]); + /// + /// let v = a.raw_view_mut(); + /// let p = a.get_ptr_mut((0, 1)).unwrap(); + /// + /// unsafe { + /// *p = 5.; + /// } + /// + /// assert_eq!(a.get((0, 1)), Some(&5.)); + /// ``` + pub fn get_ptr_mut(&mut self, index: I) -> Option<*mut A> where S: RawDataMut, I: NdIndex, From eb506634de4f49f8c1686fd1965e408655a2c6b1 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Sun, 23 Jan 2022 09:43:40 +0100 Subject: [PATCH 469/651] Rename get_ptr_mut to get_mut_ptr to be more consistent with the as_mut_ptr method. --- src/impl_methods.rs | 6 +++--- src/impl_views/indexing.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 59b3e4a73..21df6c9b8 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -768,7 +768,7 @@ where S: DataMut, I: NdIndex, { - unsafe { self.get_ptr_mut(index).map(|ptr| &mut *ptr) } + unsafe { self.get_mut_ptr(index).map(|ptr| &mut *ptr) } } /// Return a raw pointer to the element at `index`, or return `None` @@ -780,7 +780,7 @@ where /// let mut a = arr2(&[[1., 2.], [3., 4.]]); /// /// let v = a.raw_view_mut(); - /// let p = a.get_ptr_mut((0, 1)).unwrap(); + /// let p = a.get_mut_ptr((0, 1)).unwrap(); /// /// unsafe { /// *p = 5.; @@ -788,7 +788,7 @@ where /// /// assert_eq!(a.get((0, 1)), Some(&5.)); /// ``` - pub fn get_ptr_mut(&mut self, index: I) -> Option<*mut A> + pub fn get_mut_ptr(&mut self, index: I) -> Option<*mut A> where S: RawDataMut, I: NdIndex, diff --git a/src/impl_views/indexing.rs b/src/impl_views/indexing.rs index c73e870e6..adb911a48 100644 --- a/src/impl_views/indexing.rs +++ b/src/impl_views/indexing.rs @@ -164,7 +164,7 @@ where fn index(mut self, index: I) -> &'a mut A { debug_bounds_check!(self, index); unsafe { - match self.get_ptr_mut(index) { + match self.get_mut_ptr(index) { Some(ptr) => &mut *ptr, None => array_out_of_bounds(), } @@ -182,7 +182,7 @@ where fn get(mut self, index: I) -> Option<&'a mut A> { debug_bounds_check!(self, index); unsafe { - match self.get_ptr_mut(index) { + match self.get_mut_ptr(index) { Some(ptr) => Some(&mut *ptr), None => None, } From e080d62bb23a1099468c5941345d71e8738472a3 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 30 Jul 2022 16:57:15 -0400 Subject: [PATCH 470/651] Release version 0.15.6 --- Cargo.toml | 2 +- RELEASES.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1d21ac642..1bd4c5e91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ndarray" -version = "0.15.5" +version = "0.15.6" edition = "2018" rust-version = "1.49" authors = [ diff --git a/RELEASES.md b/RELEASES.md index 79909f66c..364166718 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,32 @@ +Version 0.15.6 (2022-07-30) +=========================== + +New features +------------ + +- Add `get_ptr` and `get_mut_ptr` methods for getting an element's pointer from + an index, by [@adamreichold]. + + https://github.com/rust-ndarray/ndarray/pull/1151 + +Other changes +------------- + +- Various fixes to resolve compiler and Clippy warnings/errors, by [@aganders3] + and [@jturner314]. + + https://github.com/rust-ndarray/ndarray/pull/1171 + +- Fix description of `stack!` in quick start docs, by [@jturner314]. Thanks to + [@HyeokSuLee] for pointing out the issue. + + https://github.com/rust-ndarray/ndarray/pull/1156 + +- Add MSRV to `Cargo.toml`. + + https://github.com/rust-ndarray/ndarray/pull/1191 + + Version 0.15.5 (2022-07-30) =========================== @@ -1561,6 +1590,8 @@ Earlier releases - Starting point for evolution to come +[@adamreichold]: https://github.com/adamreichold +[@aganders3]: https://github.com/aganders3 [@bluss]: https://github.com/bluss [@jturner314]: https://github.com/jturner314 [@LukeMathWalker]: https://github.com/LukeMathWalker @@ -1575,6 +1606,7 @@ Earlier releases [@ethanhs]: https://github.com/ethanhs [@d-dorazio]: https://github.com/d-dorazio [@Eijebong]: https://github.com/Eijebong +[@HyeokSuLee]: https://github.com/HyeokSuLee [@insideoutclub]: https://github.com/insideoutclub [@JP-Ellis]: https://github.com/JP-Ellis [@jimblandy]: https://github.com/jimblandy From 6b5e8298287583c178010825f83391f4b44cb18b Mon Sep 17 00:00:00 2001 From: Piotr Date: Tue, 23 Aug 2022 02:11:06 +0100 Subject: [PATCH 471/651] Update README.rst (#1199) Fix formatting. --- README.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index a6ed9e90f..eab086c9c 100644 --- a/README.rst +++ b/README.rst @@ -65,8 +65,11 @@ your `Cargo.toml`. - This crate can be used without the standard library by disabling the default `std` feature. To do so, use this in your `Cargo.toml`: - [dependencies] - ndarray = { version = "0.x.y", default-features = false } + :: + + [dependencies] + ndarray = { version = "0.x.y", default-features = false } + - The `geomspace` `linspace` `logspace` `range` `std` `var` `var_axis` and `std_axis` methods are only available when `std` is enabled. From 01f7182c0fca85e2f565e5adc4c4e1c4de44791c Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 24 Sep 2022 18:06:21 -0400 Subject: [PATCH 472/651] Add NumPy examples combining slicing and assignment --- src/doc/ndarray_for_numpy_users/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index c494816fb..eb139a75f 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -531,6 +531,8 @@ //! ------|-----------|------ //! `a[:] = 3.` | [`a.fill(3.)`][.fill()] | set all array elements to the same scalar value //! `a[:] = b` | [`a.assign(&b)`][.assign()] | copy the data from array `b` into array `a` +//! `a[:5, 2] = 3.` | [`a.slice_mut(s![..5, 2]).fill(3.)`][.fill()] | set a portion of the array to the same scalar value +//! `a[:5, 2] = b` | [`a.slice_mut(s![..5, 2]).assign(&b)`][.assign()] | copy the data from array `b` into part of array `a` //! `np.concatenate((a,b), axis=1)` | [`concatenate![Axis(1), a, b]`][concatenate!] or [`concatenate(Axis(1), &[a.view(), b.view()])`][concatenate()] | concatenate arrays `a` and `b` along axis 1 //! `np.stack((a,b), axis=1)` | [`stack![Axis(1), a, b]`][stack!] or [`stack(Axis(1), vec![a.view(), b.view()])`][stack()] | stack arrays `a` and `b` along axis 1 //! `a[:,np.newaxis]` or `np.expand_dims(a, axis=1)` | [`a.slice(s![.., NewAxis])`][.slice()] or [`a.insert_axis(Axis(1))`][.insert_axis()] | create an view of 1-D array `a`, inserting a new axis 1 From 07406955868dd98985d7e2f1de1f643be4d8888f Mon Sep 17 00:00:00 2001 From: Gorka Kobeaga Date: Thu, 13 Oct 2022 00:37:09 +0200 Subject: [PATCH 473/651] Improve example in doc for columns method (#1221) --- src/impl_methods.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 21df6c9b8..115cd2d71 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1148,7 +1148,7 @@ where /// [[ 6, 7, 8], [ 9, 10, 11]]]); /// /// // Here `columns` will yield the six generalized columns of the array. - /// for row in a.columns() { + /// for column in a.columns() { /// /* loop body */ /// } /// ``` From a26a17e12981a89aa2cf26c62f8729780d1d21dc Mon Sep 17 00:00:00 2001 From: Phoeniix <40521731+Ph03nixStyle@users.noreply.github.com> Date: Sat, 17 Dec 2022 23:27:48 +0100 Subject: [PATCH 474/651] Update README-quick-start.md Fixed typos, reformulated sentences for an overall clearer document. --- README-quick-start.md | 63 +++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 38 deletions(-) diff --git a/README-quick-start.md b/README-quick-start.md index dec039dcf..0c127ab59 100644 --- a/README-quick-start.md +++ b/README-quick-start.md @@ -7,7 +7,7 @@ You can use [play.integer32.com](https://play.integer32.com/) to immediately try ## The Basics -Just create your first 2x3 floating-point ndarray +You can create your first 2x3 floating-point ndarray as such: ```rust use ndarray::prelude::*; @@ -24,7 +24,7 @@ fn main() { println!("{:?}", a); } ``` -This code will create a simple array and output to stdout: +This code will create a simple array, then print it to stdout as such: ``` [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], shape=[2, 3], strides=[3, 1], layout=C (0x1), const ndim=2 @@ -34,8 +34,7 @@ This code will create a simple array and output to stdout: ### Element type and dimensionality -Now let's create more arrays. How about try make a zero array with dimension of (3, 2, 4)? - +Now let's create more arrays. A common operation on matrices is to create a matrix full of 0's of certain dimensions. Let's try to do that with dimensions (3, 2, 4) using the `Array::zeros` function: ```rust use ndarray::prelude::*; use ndarray::Array; @@ -44,13 +43,13 @@ fn main() { println!("{:?}", a); } ``` -gives +Unfortunately, this code does not compile. ``` | let a = Array::zeros((3, 2, 4).f()); | - ^^^^^^^^^^^^ cannot infer type for type parameter `A` ``` -Note that the compiler needs to infer the element type and dimensionality from context. In this -case the compiler failed to do that. Now we give it the type and let it infer dimensionality +Indeed, note that the compiler needs to infer the element type and dimensionality from context only. In this +case the compiler does not have enough information. To fix the code, we can explicitly give the element type through turbofish syntax, and let it infer the dimensionality type: ```rust use ndarray::prelude::*; @@ -60,7 +59,7 @@ fn main() { println!("{:?}", a); } ``` -and now it works: +This code now compiles to what we wanted: ``` [[[0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]], @@ -72,24 +71,11 @@ and now it works: [0.0, 0.0, 0.0, 0.0]]], shape=[3, 2, 4], strides=[1, 3, 6], layout=F (0x2), const ndim=3 ``` -We can also specify its dimensionality +We could also specify its dimensionality explicitly `Array::::zeros(...)`, with`Ix3` standing for 3D array type. Phew! We achieved type safety. If you tried changing the code above to `Array::::zeros((3, 2, 4, 5).f());`, which is not of dimension 3 anymore, Rust's type system would gracefully prevent you from compiling the code. -```rust -use ndarray::prelude::*; -use ndarray::{Array, Ix3}; -fn main() { - let a = Array::::zeros((3, 2, 4).f()); - println!("{:?}", a); -} -``` -`Ix3` stands for 3D array. - -And now we are type checked. Try change the code above to `Array::::zeros((3, 2, 4, 5).f());` -and compile, see what happens. - -### How about create array of different type and having different initial values? +### Creating arrays with different initial values and/or different types -The [`from_elem`](http://docs.rs/ndarray/latest/ndarray/struct.ArrayBase.html#method.from_elem) method can be handy here: +The [`from_elem`](http://docs.rs/ndarray/latest/ndarray/struct.ArrayBase.html#method.from_elem) method allows initializing an array of given dimension to a specific value of any type: ```rust use ndarray::{Array, Ix3}; @@ -99,7 +85,7 @@ fn main() { } ``` -### Some common create helper functions +### Some common array initializing helper functions `linspace` - Create a 1-D array with 11 elements with values 0., …, 5. ```rust use ndarray::prelude::*; @@ -114,10 +100,11 @@ The output is: [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0], shape=[11], strides=[1], layout=C | F (0x3), const ndim=1 ``` -And there are also `range`, `logspace`, `ones`, `eye` and so on you can choose to use. +Common array initializing methods include [`range`](https://docs.rs/ndarray/0.13.0/ndarray/struct.ArrayBase.html#method.range), [`logspace`](https://docs.rs/ndarray/0.13.0/ndarray/struct.ArrayBase.html#method.logspace), [`eye`](https://docs.rs/ndarray/0.13.0/ndarray/struct.ArrayBase.html#method.eye), [`ones`](https://docs.rs/ndarray/0.13.0/ndarray/struct.ArrayBase.html#method.ones)... ## Basic operations +Basic operations on arrays are all element-wise; you need to use specific methods for operations such as matrix multiplication (see later section). ```rust use ndarray::prelude::*; use ndarray::Array; @@ -136,16 +123,19 @@ fn main() { } ``` -Try remove all the `&` sign in front of `a` and `b`, does it still compile? Why? -Note that +Note that (for any binary operator `@`): * `&A @ &A` produces a new `Array` * `B @ A` consumes `B`, updates it with the result, and returns it * `B @ &A` consumes `B`, updates it with the result, and returns it * `C @= &A` performs an arithmetic operation in place +Try removing all the `&` sign in front of `a` and `b` in the last example: it will not compile anymore because of those rules. + For more info checkout https://docs.rs/ndarray/latest/ndarray/struct.ArrayBase.html#arithmetic-operations + + Some operations have `_axis` appended to the function name: they generally take in a parameter of type `Axis` as one of their inputs, such as `sum_axis`: @@ -237,7 +227,7 @@ The output is: For more info about iteration see [Loops, Producers, and Iterators](https://docs.rs/ndarray/0.13.0/ndarray/struct.ArrayBase.html#loops-producers-and-iterators) -Let's try a 3D array with elements of type `isize`. This is how you index it: +Let's try a iterating over a 3D array with elements of type `isize`. This is how you index it: ```rust use ndarray::prelude::*; @@ -250,8 +240,8 @@ fn main() { [110,112,113]] ]; - let a = a.mapv(|a: isize| a.pow(1)); // numpy equivlant of `a ** 1`; - // This line does nothing but illustrate mapv with isize type + let a = a.mapv(|a: isize| a.pow(1)); // numpy equivalent of `a ** 1`; + // This line does nothing except illustrating mapv with isize type println!("a -> \n{}\n", a); println!("`a.slice(s![1, .., ..])` -> \n{}\n", a.slice(s![1, .., ..])); @@ -461,11 +451,8 @@ s2 = ## Copies and Views ### View, Ref or Shallow Copy -As in Rust we have owner ship, so we cannot simply -update an element of an array while we have a -shared view of it. This will help us write more -robust code. +Rust has ownership, so we cannot simply update an element of an array while we have a shared view of it. This brings guarantees & helps having more robust code. ```rust use ndarray::prelude::*; use ndarray::{Array, Axis}; @@ -566,9 +553,9 @@ b clone of a = [2, 3]] ``` -Noticing that using `clone()` (or cloning) an `Array` type also copies the array's elements. It creates an independently owned array of the same type. +Notice that using `clone()` (or cloning) an `Array` type also copies the array's elements. It creates an independently owned array of the same type. -Cloning an `ArrayView` does not clone or copy the underlying elements - it just clones the view reference (as it happens in Rust when cloning a `&` reference). +Cloning an `ArrayView` does not clone or copy the underlying elements - it only clones the view reference (as it happens in Rust when cloning a `&` reference). ## Broadcasting @@ -600,7 +587,7 @@ fn main() { See [.broadcast()](https://docs.rs/ndarray/latest/ndarray/struct.ArrayBase.html#method.broadcast) for a more detailed description. -And there is a short example of it: +And here is a short example of it: ```rust use ndarray::prelude::*; From 2c0ae321add5b567a454231dc3d3225b46cf3a5a Mon Sep 17 00:00:00 2001 From: lazarohurtado Date: Thu, 29 Dec 2022 11:41:53 -0500 Subject: [PATCH 475/651] added stride support to windows --- src/impl_methods.rs | 52 +++++++++++++++++++++++++++++ src/iterators/windows.rs | 58 ++++++++++++++++++++++++++++++++ tests/windows.rs | 72 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 181 insertions(+), 1 deletion(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 115cd2d71..a368e9ddc 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1457,6 +1457,58 @@ where Windows::new(self.view(), window_size) } + /// Return a window producer and iterable. + /// + /// The windows are all distinct views of size `window_size` + /// that fit into the array's shape. + /// + /// The stride is ordered by the outermost axis.
+ /// Hence, a (x₀, x₁, ..., xₙ) stride will be applied to + /// (A₀, A₁, ..., Aₙ) where Aₓ stands for `Axis(x)`. + /// + /// This produces all windows that fit within the array for the given stride, + /// assuming the window size is not larger than the array size. + /// + /// The produced element is an `ArrayView` with exactly the dimension + /// `window_size`. + /// + /// Note that passing a stride of only ones is similar to + /// calling [`ArrayBase::windows()`]. + /// + /// **Panics** if any dimension of `window_size` or `stride` is zero.
+ /// (**Panics** if `D` is `IxDyn` and `window_size` or `stride` does not match the + /// number of array axes.) + /// + /// This is the same illustration found in [`ArrayBase::windows()`], + /// 2×2 windows in a 3×4 array, but now with a (1, 2) stride: + /// + /// ```text + /// ──▶ Axis(1) + /// + /// │ ┏━━━━━┳━━━━━┱─────┬─────┐ ┌─────┬─────┲━━━━━┳━━━━━┓ + /// ▼ ┃ a₀₀ ┃ a₀₁ ┃ │ │ │ │ ┃ a₀₂ ┃ a₀₃ ┃ + /// Axis(0) ┣━━━━━╋━━━━━╉─────┼─────┤ ├─────┼─────╊━━━━━╋━━━━━┫ + /// ┃ a₁₀ ┃ a₁₁ ┃ │ │ │ │ ┃ a₁₂ ┃ a₁₃ ┃ + /// ┡━━━━━╇━━━━━╃─────┼─────┤ ├─────┼─────╄━━━━━╇━━━━━┩ + /// │ │ │ │ │ │ │ │ │ │ + /// └─────┴─────┴─────┴─────┘ └─────┴─────┴─────┴─────┘ + /// + /// ┌─────┬─────┬─────┬─────┐ ┌─────┬─────┬─────┬─────┐ + /// │ │ │ │ │ │ │ │ │ │ + /// ┢━━━━━╈━━━━━╅─────┼─────┤ ├─────┼─────╆━━━━━╈━━━━━┪ + /// ┃ a₁₀ ┃ a₁₁ ┃ │ │ │ │ ┃ a₁₂ ┃ a₁₃ ┃ + /// ┣━━━━━╋━━━━━╉─────┼─────┤ ├─────┼─────╊━━━━━╋━━━━━┫ + /// ┃ a₂₀ ┃ a₂₁ ┃ │ │ │ │ ┃ a₂₂ ┃ a₂₃ ┃ + /// ┗━━━━━┻━━━━━┹─────┴─────┘ └─────┴─────┺━━━━━┻━━━━━┛ + /// ``` + pub fn windows_with_stride(&self, window_size: E, stride: E) -> Windows<'_, A, D> + where + E: IntoDimension, + S: Data, + { + Windows::new_with_stride(self.view(), window_size, stride) + } + /// Returns a producer which traverses over all windows of a given length along an axis. /// /// The windows are all distinct, possibly-overlapping views. The shape of each window diff --git a/src/iterators/windows.rs b/src/iterators/windows.rs index c47bfecec..19833c279 100644 --- a/src/iterators/windows.rs +++ b/src/iterators/windows.rs @@ -47,6 +47,64 @@ impl<'a, A, D: Dimension> Windows<'a, A, D> { } } } + + pub(crate) fn new_with_stride(a: ArrayView<'a, A, D>, window_size: E, strides: E) -> Self + where + E: IntoDimension, + { + let window = window_size.into_dimension(); + let strides_d = strides.into_dimension(); + ndassert!( + a.ndim() == window.ndim(), + concat!( + "Window dimension {} does not match array dimension {} ", + "(with array of shape {:?})" + ), + window.ndim(), + a.ndim(), + a.shape() + ); + ndassert!( + a.ndim() == strides_d.ndim(), + concat!( + "Stride dimension {} does not match array dimension {} ", + "(with array of shape {:?})" + ), + strides_d.ndim(), + a.ndim(), + a.shape() + ); + let mut size = a.dim; + for ((sz, &ws), &stride) in size + .slice_mut() + .iter_mut() + .zip(window.slice()) + .zip(strides_d.slice()) + { + assert_ne!(ws, 0, "window-size must not be zero!"); + assert_ne!(stride, 0, "stride cannot have a dimension as zero!"); + // cannot use std::cmp::max(0, ..) since arithmetic underflow panics + *sz = if *sz < ws { + 0 + } else { + ((*sz - (ws - 1) - 1) / stride) + 1 + }; + } + let window_strides = a.strides.clone(); + + let mut array_strides = a.strides.clone(); + for (arr_stride, ix_stride) in array_strides.slice_mut().iter_mut().zip(strides_d.slice()) { + *arr_stride *= ix_stride; + } + + unsafe { + Windows { + base: ArrayView::new(a.ptr, size, array_strides), + window, + strides: window_strides, + } + } + } } impl_ndproducer! { diff --git a/tests/windows.rs b/tests/windows.rs index 432be5e41..8468e4593 100644 --- a/tests/windows.rs +++ b/tests/windows.rs @@ -30,7 +30,7 @@ fn windows_iterator_zero_size() { a.windows(Dim((0, 0, 0))); } -/// Test that verifites that no windows are yielded on oversized window sizes. +/// Test that verifies that no windows are yielded on oversized window sizes. #[test] fn windows_iterator_oversized() { let a = Array::from_iter(10..37).into_shape((3, 3, 3)).unwrap(); @@ -95,6 +95,76 @@ fn windows_iterator_3d() { ); } +/// Test that verifies the `Windows` iterator panics when stride has an axis equal to zero. +#[test] +#[should_panic] +fn windows_iterator_stride_axis_zero() { + let a = Array::from_iter(10..37).into_shape((3, 3, 3)).unwrap(); + a.windows_with_stride(Dim((2, 2, 2)), Dim((0,2,2))); +} + +/// Test that verifies that only first window is yielded when stride is oversized on every axis. +#[test] +fn windows_iterator_only_one_valid_window_for_oversized_stride() { + let a = Array::from_iter(10..135).into_shape((5, 5, 5)).unwrap(); + let mut iter = a.windows_with_stride((2, 2, 2), (8, 8, 8)).into_iter(); // (4,3,2) doesn't fit into (3,3,3) => oversized! + itertools::assert_equal( + iter.next(), + Some(arr3(&[[[10, 11], [15, 16]],[[35, 36], [40, 41]]])) + ); +} + +/// Simple test for iterating 1d-arrays via `Windows` with stride. +#[test] +fn windows_iterator_1d_with_stride() { + let a = Array::from_iter(10..20).into_shape(10).unwrap(); + itertools::assert_equal( + a.windows_with_stride(Dim(4), Dim(2)), + vec![ + arr1(&[10, 11, 12, 13]), + arr1(&[12, 13, 14, 15]), + arr1(&[14, 15, 16, 17]), + arr1(&[16, 17, 18, 19]), + ], + ); +} + +/// Simple test for iterating 2d-arrays via `Windows` with stride. +#[test] +fn windows_iterator_2d_with_stride() { + let a = Array::from_iter(10..30).into_shape((5, 4)).unwrap(); + itertools::assert_equal( + a.windows_with_stride(Dim((3, 2)), Dim((2,1))), + vec![ + arr2(&[[10, 11], [14, 15], [18, 19]]), + arr2(&[[11, 12], [15, 16], [19, 20]]), + arr2(&[[12, 13], [16, 17], [20, 21]]), + arr2(&[[18, 19], [22, 23], [26, 27]]), + arr2(&[[19, 20], [23, 24], [27, 28]]), + arr2(&[[20, 21], [24, 25], [28, 29]]), + ], + ); +} + +/// Simple test for iterating 3d-arrays via `Windows` with stride. +#[test] +fn windows_iterator_3d_with_stride() { + let a = Array::from_iter(10..74).into_shape((4, 4, 4)).unwrap(); + itertools::assert_equal( + a.windows_with_stride(Dim((2, 2, 2)), Dim((2,2,2))), + vec![ + arr3(&[[[10, 11], [14, 15]], [[26, 27], [30, 31]]]), + arr3(&[[[12, 13], [16, 17]], [[28, 29], [32, 33]]]), + arr3(&[[[18, 19], [22, 23]], [[34, 35], [38, 39]]]), + arr3(&[[[20, 21], [24, 25]], [[36, 37], [40, 41]]]), + arr3(&[[[42, 43], [46, 47]], [[58, 59], [62, 63]]]), + arr3(&[[[44, 45], [48, 49]], [[60, 61], [64, 65]]]), + arr3(&[[[50, 51], [54, 55]], [[66, 67], [70, 71]]]), + arr3(&[[[52, 53], [56, 57]], [[68, 69], [72, 73]]]), + ], + ); +} + #[test] fn test_window_zip() { let a = Array::from_iter(0..64).into_shape((4, 4, 4)).unwrap(); From 4cadbfddb2ba29ffb1a369d1979041c42c64cc12 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Tue, 23 May 2023 21:33:36 +0200 Subject: [PATCH 476/651] fix(ci): Allow use of deprecated zip function from itertools. --- tests/array.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/array.rs b/tests/array.rs index e3922ea8d..8fdbb9992 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -9,6 +9,7 @@ use approx::assert_relative_eq; use defmac::defmac; +#[allow(deprecated)] use itertools::{zip, Itertools}; use ndarray::prelude::*; use ndarray::{arr3, rcarr2}; @@ -499,12 +500,14 @@ fn test_index() { *elt = i; } + #[allow(deprecated)] for ((i, j), a) in zip(indices((2, 3)), &A) { assert_eq!(*a, A[[i, j]]); } let vi = A.slice(s![1.., ..;2]); let mut it = vi.iter(); + #[allow(deprecated)] for ((i, j), x) in zip(indices((1, 2)), &mut it) { assert_eq!(*x, vi[[i, j]]); } From eea3bdd82e9e0c7d9dc3983e7b463e7e24a37f3e Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Tue, 23 May 2023 21:38:12 +0200 Subject: [PATCH 477/651] fix(ci): Avoid the unmaintained action-rs actions. --- .github/workflows/ci.yml | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c76bd9d1..eabc21c61 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,12 +24,11 @@ jobs: - 1.51.0 # MSRV steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: ${{ matrix.rust }} - override: true + - uses: Swatinem/rust-cache@v2 - name: Install openblas run: sudo apt-get install libopenblas-dev gfortran - run: ./scripts/all-tests.sh "$FEATURES" ${{ matrix.rust }} @@ -45,20 +44,14 @@ jobs: target: i686-unknown-linux-gnu steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: ${{ matrix.rust }} - target: ${{ matrix.target }} - override: true - - name: Cache cargo plugins - uses: actions/cache@v1 - with: - path: ~/.cargo/bin/ - key: ${{ runner.os }}-cargo-plugins + targets: ${{ matrix.target }} + - uses: Swatinem/rust-cache@v2 - name: Install cross - run: cargo install cross || true + run: cargo install cross - run: ./scripts/cross-tests.sh "docs" ${{ matrix.rust }} ${{ matrix.target }} clippy: @@ -68,12 +61,10 @@ jobs: rust: - beta steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: ${{ matrix.rust }} - override: true components: clippy + - uses: Swatinem/rust-cache@v2 - run: cargo clippy --features docs - From 73545b7f64cc70457f6af320c1a962748c67a092 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Tue, 23 May 2023 21:45:14 +0200 Subject: [PATCH 478/651] fix(clippy): Make current version of Clippy happy. --- src/arraytraits.rs | 2 +- src/dimension/dimension_trait.rs | 6 +++--- src/dimension/dynindeximpl.rs | 2 +- src/dimension/mod.rs | 2 +- src/impl_methods.rs | 2 +- src/indexes.rs | 4 ++-- src/iterators/mod.rs | 2 +- src/zip/mod.rs | 1 + 8 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 74ce9d644..9aa45bde3 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -298,7 +298,7 @@ where { } -#[cfg(any(feature = "serde"))] +#[cfg(feature = "serde")] // Use version number so we can add a packed format later. pub const ARRAY_FORMAT_VERSION: u8 = 1u8; diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 6ebb9cad4..7181d2268 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -83,7 +83,7 @@ pub trait Dimension: /// Compute the size of the dimension (number of elements) fn size(&self) -> usize { - self.slice().iter().fold(1, |s, &a| s * a as usize) + self.slice().iter().product() } /// Compute the size while checking for overflow. @@ -603,7 +603,7 @@ impl Dimension for Dim<[Ix; 2]> { fn size_checked(&self) -> Option { let m = get!(self, 0); let n = get!(self, 1); - (m as usize).checked_mul(n as usize) + m.checked_mul(n) } #[inline] @@ -728,7 +728,7 @@ impl Dimension for Dim<[Ix; 3]> { let m = get!(self, 0); let n = get!(self, 1); let o = get!(self, 2); - m as usize * n as usize * o as usize + m * n * o } #[inline] diff --git a/src/dimension/dynindeximpl.rs b/src/dimension/dynindeximpl.rs index 85b34bdbe..e81bf4f3d 100644 --- a/src/dimension/dynindeximpl.rs +++ b/src/dimension/dynindeximpl.rs @@ -96,7 +96,7 @@ impl PartialEq for IxDynRepr { match (self, rhs) { (&IxDynRepr::Inline(slen, ref sarr), &IxDynRepr::Inline(rlen, ref rarr)) => { slen == rlen - && (0..CAP as usize) + && (0..CAP) .filter(|&i| i < slen as usize) .all(|i| sarr[i] == rarr[i]) } diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index dd34052ec..6755efeb7 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -47,7 +47,7 @@ mod sequence; /// Calculate offset from `Ix` stride converting sign properly #[inline(always)] pub fn stride_offset(n: Ix, stride: Ix) -> isize { - (n as isize) * ((stride as Ixs) as isize) + (n as isize) * (stride as Ixs) } /// Check whether the given `dim` and `stride` lead to overlapping indices diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 115cd2d71..4a6b42223 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1990,7 +1990,7 @@ where let strides = unlimited_transmute::(self.strides); return Ok(ArrayBase::from_data_ptr(self.data, self.ptr) .with_strides_dim(strides, dim)); - } else if D::NDIM == None || D2::NDIM == None { // one is dynamic dim + } else if D::NDIM.is_none() || D2::NDIM.is_none() { // one is dynamic dim // safe because dim, strides are equivalent under a different type if let Some(dim) = D2::from_dimension(&self.dim) { if let Some(strides) = D2::from_dimension(&self.strides) { diff --git a/src/indexes.rs b/src/indexes.rs index 23ae2744c..541570184 100644 --- a/src/indexes.rs +++ b/src/indexes.rs @@ -75,7 +75,7 @@ where .slice() .iter() .zip(ix.slice().iter()) - .fold(0, |s, (&a, &b)| s + a as usize * b as usize); + .fold(0, |s, (&a, &b)| s + a * b); self.dim.size() - gone } }; @@ -286,7 +286,7 @@ where .slice() .iter() .zip(self.index.slice().iter()) - .fold(0, |s, (&a, &b)| s + a as usize * b as usize); + .fold(0, |s, (&a, &b)| s + a * b); let l = self.dim.size() - gone; (l, Some(l)) } diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 85da56fe5..063b22053 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -114,7 +114,7 @@ impl ExactSizeIterator for Baseiter { .slice() .iter() .zip(ix.slice().iter()) - .fold(0, |s, (&a, &b)| s + a as usize * b as usize); + .fold(0, |s, (&a, &b)| s + a * b); self.dim.size() - gone } } diff --git a/src/zip/mod.rs b/src/zip/mod.rs index a6cd0c1fe..cc7a4187d 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -92,6 +92,7 @@ where { type Output = ArrayView<'a, A, E::Dim>; fn broadcast_unwrap(self, shape: E) -> Self::Output { + #[allow(clippy::needless_borrow)] let res: ArrayView<'_, A, E::Dim> = (&self).broadcast_unwrap(shape.into_dimension()); unsafe { ArrayView::new(res.ptr, res.dim, res.strides) } } From 16885098ce1fc6a0b0190f9175637ecd62819580 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Tue, 23 May 2023 22:25:35 +0200 Subject: [PATCH 479/651] fix(ci): Pin dependencies to MSRV-compatible versions. --- Cargo.toml | 1 - scripts/all-tests.sh | 18 ++++++++++++++++-- xtest-blas/Cargo.toml | 2 +- xtest-numeric/Cargo.toml | 2 +- xtest-serialization/Cargo.toml | 2 +- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 85c410449..1c16e5bf1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,7 +73,6 @@ rayon = ["rayon_", "std"] matrixmultiply-threading = ["matrixmultiply/threading"] -[profile.release] [profile.bench] debug = true diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index 98f4e3390..70f8e8d45 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -6,6 +6,20 @@ set -e FEATURES=$1 CHANNEL=$2 +if [ "$CHANNEL" = "1.51.0" ]; then + cargo update --package rayon --precise 1.5.3 + cargo update --package rayon-core --precise 1.9.3 + cargo update --package serde --precise 1.0.156 + cargo update --package thiserror --precise 1.0.39 + cargo update --package once_cell --precise 1.14.0 + cargo update --package openblas-src --precise 0.10.5 + cargo update --package openblas-build --precise 0.10.5 + + cargo update --manifest-path=xtest-numeric/Cargo.toml --package thiserror --precise 1.0.39 + cargo update --manifest-path=xtest-numeric/Cargo.toml --package openblas-src --precise 0.10.5 + cargo update --manifest-path=xtest-numeric/Cargo.toml --package openblas-build --precise 0.10.5 +fi + cargo build --verbose --no-default-features # Testing both dev and release profiles helps find bugs, especially in low level code cargo test --verbose --no-default-features @@ -17,6 +31,6 @@ cargo test --manifest-path=ndarray-rand/Cargo.toml --features quickcheck --verbo cargo test --manifest-path=xtest-serialization/Cargo.toml --verbose cargo test --manifest-path=xtest-blas/Cargo.toml --verbose --features openblas-system cargo test --examples -CARGO_TARGET_DIR=target/ cargo test --manifest-path=xtest-numeric/Cargo.toml --verbose -CARGO_TARGET_DIR=target/ cargo test --manifest-path=xtest-numeric/Cargo.toml --verbose --features test_blas +cargo test --manifest-path=xtest-numeric/Cargo.toml --verbose +cargo test --manifest-path=xtest-numeric/Cargo.toml --verbose --features test_blas ([ "$CHANNEL" != "nightly" ] || cargo bench --no-run --verbose --features "$FEATURES") diff --git a/xtest-blas/Cargo.toml b/xtest-blas/Cargo.toml index aee8c3ce4..7ad33953c 100644 --- a/xtest-blas/Cargo.toml +++ b/xtest-blas/Cargo.toml @@ -14,7 +14,7 @@ num-traits = "0.2" num-complex = { version = "0.4", default-features = false } [dependencies] -ndarray = { path = "../", features = ["approx", "blas"] } +ndarray = { path = "..", features = ["approx", "blas"] } blas-src = { version = "0.8", optional = true } diff --git a/xtest-numeric/Cargo.toml b/xtest-numeric/Cargo.toml index 7a6c3cedd..bdfe15044 100644 --- a/xtest-numeric/Cargo.toml +++ b/xtest-numeric/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" [dependencies] approx = "0.4" ndarray = { path = "..", features = ["approx"] } -ndarray-rand = { path = "../ndarray-rand/" } +ndarray-rand = { path = "../ndarray-rand" } rand_distr = "0.4" blas-src = { optional = true, version = "0.8", default-features = false, features = ["openblas"] } diff --git a/xtest-serialization/Cargo.toml b/xtest-serialization/Cargo.toml index 973a688fe..857e31fe6 100644 --- a/xtest-serialization/Cargo.toml +++ b/xtest-serialization/Cargo.toml @@ -8,7 +8,7 @@ publish = false test = false [dependencies] -ndarray = { path = "../", features = ["serde"] } +ndarray = { path = "..", features = ["serde"] } [features] default = ["ron"] From b9c4f1013ab59d33fffe61b1a5fc3d033d807e9b Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Wed, 24 May 2023 09:08:44 +0200 Subject: [PATCH 480/651] fix(ci): Integrate numeric tests into workspace to avoid confusing cross. --- Cargo.toml | 7 +++++-- scripts/all-tests.sh | 4 ---- scripts/cross-tests.sh | 2 +- xtest-numeric/Cargo.toml | 5 ----- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1c16e5bf1..a648b09bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,10 +75,13 @@ matrixmultiply-threading = ["matrixmultiply/threading"] [profile.bench] debug = true +[profile.dev.package.numeric-tests] +opt-level = 2 +[profile.test.package.numeric-tests] +opt-level = 2 [workspace] -members = ["ndarray-rand", "xtest-serialization", "xtest-blas"] -exclude = ["xtest-numeric"] +members = ["ndarray-rand", "xtest-serialization", "xtest-blas", "xtest-numeric"] [package.metadata.release] no-dev-version = true diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index 70f8e8d45..2167f3451 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -14,10 +14,6 @@ if [ "$CHANNEL" = "1.51.0" ]; then cargo update --package once_cell --precise 1.14.0 cargo update --package openblas-src --precise 0.10.5 cargo update --package openblas-build --precise 0.10.5 - - cargo update --manifest-path=xtest-numeric/Cargo.toml --package thiserror --precise 1.0.39 - cargo update --manifest-path=xtest-numeric/Cargo.toml --package openblas-src --precise 0.10.5 - cargo update --manifest-path=xtest-numeric/Cargo.toml --package openblas-build --precise 0.10.5 fi cargo build --verbose --no-default-features diff --git a/scripts/cross-tests.sh b/scripts/cross-tests.sh index 7c4f13111..7473a72fa 100755 --- a/scripts/cross-tests.sh +++ b/scripts/cross-tests.sh @@ -11,4 +11,4 @@ cross build -v --features="$FEATURES" --target=$TARGET cross test -v --no-fail-fast --features="$FEATURES" --target=$TARGET cross test -v --no-fail-fast --target=$TARGET --manifest-path=ndarray-rand/Cargo.toml --features quickcheck cross test -v --no-fail-fast --target=$TARGET --manifest-path=xtest-serialization/Cargo.toml --verbose -CARGO_TARGET_DIR=target/ cross test -v --no-fail-fast --target=$TARGET --manifest-path=xtest-numeric/Cargo.toml +cross test -v --no-fail-fast --target=$TARGET --manifest-path=xtest-numeric/Cargo.toml diff --git a/xtest-numeric/Cargo.toml b/xtest-numeric/Cargo.toml index bdfe15044..8558d39ad 100644 --- a/xtest-numeric/Cargo.toml +++ b/xtest-numeric/Cargo.toml @@ -27,8 +27,3 @@ test = false [features] test_blas = ["ndarray/blas", "blas-src", "openblas-src"] - -[profile.dev] -opt-level = 2 -[profile.test] -opt-level = 2 From d98cfb3ab8606b33c34df36d0fcdec991c59001d Mon Sep 17 00:00:00 2001 From: Nil Goyette Date: Fri, 2 Jun 2023 11:56:44 -0400 Subject: [PATCH 481/651] Add both approx features --- README.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.rst b/README.rst index eab086c9c..9e3d37247 100644 --- a/README.rst +++ b/README.rst @@ -83,6 +83,14 @@ your `Cargo.toml`. - Enables parallel iterators, parallelized methods and ``par_azip!``. - Implies std +- ``approx`` + + - Implementations of traits from version 0.4 of the [`approx`] crate. + +- ``approx-0_5`` + + - Implementations of traits from version 0.5 of the [`approx`] crate. + - ``blas`` - Enable transparent BLAS support for matrix multiplication. From 61d55cdfa9db2237d22e77770f934f6634357f38 Mon Sep 17 00:00:00 2001 From: Lazaro Hurtado Date: Wed, 7 Jun 2023 23:30:39 -0700 Subject: [PATCH 482/651] updated Window::new to use new_with_stride with unit stride --- src/iterators/windows.rs | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/src/iterators/windows.rs b/src/iterators/windows.rs index 19833c279..efa84fb8e 100644 --- a/src/iterators/windows.rs +++ b/src/iterators/windows.rs @@ -20,32 +20,15 @@ impl<'a, A, D: Dimension> Windows<'a, A, D> { E: IntoDimension, { let window = window_size.into_dimension(); - ndassert!( - a.ndim() == window.ndim(), - concat!( - "Window dimension {} does not match array dimension {} ", - "(with array of shape {:?})" - ), - window.ndim(), - a.ndim(), - a.shape() - ); - let mut size = a.dim; - for (sz, &ws) in size.slice_mut().iter_mut().zip(window.slice()) { - assert_ne!(ws, 0, "window-size must not be zero!"); - // cannot use std::cmp::max(0, ..) since arithmetic underflow panics - *sz = if *sz < ws { 0 } else { *sz - ws + 1 }; - } + let ndim = window.ndim(); - let window_strides = a.strides.clone(); - - unsafe { - Windows { - base: ArrayView::new(a.ptr, size, a.strides), - window, - strides: window_strides, - } + let mut unit_stride = D::zeros(ndim); + let stride_slice = unit_stride.slice_mut(); + for s in stride_slice.iter_mut() { + *s = 1; } + + Windows::new_with_stride(a, window, unit_stride) } pub(crate) fn new_with_stride(a: ArrayView<'a, A, D>, window_size: E, strides: E) -> Self From 0449baee6d1a989e12065198e8d7dc27bf3a9b66 Mon Sep 17 00:00:00 2001 From: Lazaro Hurtado Date: Fri, 9 Jun 2023 00:23:00 -0700 Subject: [PATCH 483/651] formatted test file --- tests/windows.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/windows.rs b/tests/windows.rs index 8468e4593..2c928aaef 100644 --- a/tests/windows.rs +++ b/tests/windows.rs @@ -100,7 +100,7 @@ fn windows_iterator_3d() { #[should_panic] fn windows_iterator_stride_axis_zero() { let a = Array::from_iter(10..37).into_shape((3, 3, 3)).unwrap(); - a.windows_with_stride(Dim((2, 2, 2)), Dim((0,2,2))); + a.windows_with_stride((2, 2, 2), (0, 2, 2)); } /// Test that verifies that only first window is yielded when stride is oversized on every axis. @@ -110,7 +110,7 @@ fn windows_iterator_only_one_valid_window_for_oversized_stride() { let mut iter = a.windows_with_stride((2, 2, 2), (8, 8, 8)).into_iter(); // (4,3,2) doesn't fit into (3,3,3) => oversized! itertools::assert_equal( iter.next(), - Some(arr3(&[[[10, 11], [15, 16]],[[35, 36], [40, 41]]])) + Some(arr3(&[[[10, 11], [15, 16]], [[35, 36], [40, 41]]])), ); } @@ -119,7 +119,7 @@ fn windows_iterator_only_one_valid_window_for_oversized_stride() { fn windows_iterator_1d_with_stride() { let a = Array::from_iter(10..20).into_shape(10).unwrap(); itertools::assert_equal( - a.windows_with_stride(Dim(4), Dim(2)), + a.windows_with_stride(4, 2), vec![ arr1(&[10, 11, 12, 13]), arr1(&[12, 13, 14, 15]), @@ -134,7 +134,7 @@ fn windows_iterator_1d_with_stride() { fn windows_iterator_2d_with_stride() { let a = Array::from_iter(10..30).into_shape((5, 4)).unwrap(); itertools::assert_equal( - a.windows_with_stride(Dim((3, 2)), Dim((2,1))), + a.windows_with_stride((3, 2), (2, 1)), vec![ arr2(&[[10, 11], [14, 15], [18, 19]]), arr2(&[[11, 12], [15, 16], [19, 20]]), @@ -151,7 +151,7 @@ fn windows_iterator_2d_with_stride() { fn windows_iterator_3d_with_stride() { let a = Array::from_iter(10..74).into_shape((4, 4, 4)).unwrap(); itertools::assert_equal( - a.windows_with_stride(Dim((2, 2, 2)), Dim((2,2,2))), + a.windows_with_stride((2, 2, 2), (2, 2, 2)), vec![ arr3(&[[[10, 11], [14, 15]], [[26, 27], [30, 31]]]), arr3(&[[[12, 13], [16, 17]], [[28, 29], [32, 33]]]), From a7680ad5db44bc003d8459292110f07dd1430eed Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Fri, 9 Jun 2023 09:17:25 -0700 Subject: [PATCH 484/651] Added select to numpy user docs --- src/doc/ndarray_for_numpy_users/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index eb139a75f..0cabf7f2a 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -258,6 +258,7 @@ //! `a[-5:]` or `a[-5:, :]` | [`a.slice(s![-5.., ..])`][.slice()] or [`a.slice_axis(Axis(0), Slice::from(-5..))`][.slice_axis()] | get the last 5 rows of a 2-D array //! `a[:3, 4:9]` | [`a.slice(s![..3, 4..9])`][.slice()] | columns 4, 5, 6, 7, and 8 of the first 3 rows //! `a[1:4:2, ::-1]` | [`a.slice(s![1..4;2, ..;-1])`][.slice()] | rows 1 and 3 with the columns in reverse order +//! `a.take([4, 2])` | `a.select(Axis(0), &[4, 2])` | rows 4 and 2 of the array //! //! ## Shape and strides //! From c357f0db208786e08aab62c6eca3218296710fcb Mon Sep 17 00:00:00 2001 From: Lazaro Hurtado Date: Fri, 9 Jun 2023 21:45:06 -0700 Subject: [PATCH 485/651] updated windows method doc description to reference windows_with_stride --- src/impl_methods.rs | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index a368e9ddc..282c382be 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1418,37 +1418,7 @@ where /// The windows are all distinct overlapping views of size `window_size` /// that fit into the array's shape. /// - /// This produces no elements if the window size is larger than the actual array size along any - /// axis. - /// - /// The produced element is an `ArrayView` with exactly the dimension - /// `window_size`. - /// - /// **Panics** if any dimension of `window_size` is zero.
- /// (**Panics** if `D` is `IxDyn` and `window_size` does not match the - /// number of array axes.) - /// - /// This is an illustration of the 2×2 windows in a 3×4 array: - /// - /// ```text - /// ──▶ Axis(1) - /// - /// │ ┏━━━━━┳━━━━━┱─────┬─────┐ ┌─────┲━━━━━┳━━━━━┱─────┐ ┌─────┬─────┲━━━━━┳━━━━━┓ - /// ▼ ┃ a₀₀ ┃ a₀₁ ┃ │ │ │ ┃ a₀₁ ┃ a₀₂ ┃ │ │ │ ┃ a₀₂ ┃ a₀₃ ┃ - /// Axis(0) ┣━━━━━╋━━━━━╉─────┼─────┤ ├─────╊━━━━━╋━━━━━╉─────┤ ├─────┼─────╊━━━━━╋━━━━━┫ - /// ┃ a₁₀ ┃ a₁₁ ┃ │ │ │ ┃ a₁₁ ┃ a₁₂ ┃ │ │ │ ┃ a₁₂ ┃ a₁₃ ┃ - /// ┡━━━━━╇━━━━━╃─────┼─────┤ ├─────╄━━━━━╇━━━━━╃─────┤ ├─────┼─────╄━━━━━╇━━━━━┩ - /// │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ - /// └─────┴─────┴─────┴─────┘ └─────┴─────┴─────┴─────┘ └─────┴─────┴─────┴─────┘ - /// - /// ┌─────┬─────┬─────┬─────┐ ┌─────┬─────┬─────┬─────┐ ┌─────┬─────┬─────┬─────┐ - /// │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ - /// ┢━━━━━╈━━━━━╅─────┼─────┤ ├─────╆━━━━━╈━━━━━╅─────┤ ├─────┼─────╆━━━━━╈━━━━━┪ - /// ┃ a₁₀ ┃ a₁₁ ┃ │ │ │ ┃ a₁₁ ┃ a₁₂ ┃ │ │ │ ┃ a₁₂ ┃ a₁₃ ┃ - /// ┣━━━━━╋━━━━━╉─────┼─────┤ ├─────╊━━━━━╋━━━━━╉─────┤ ├─────┼─────╊━━━━━╋━━━━━┫ - /// ┃ a₂₀ ┃ a₂₁ ┃ │ │ │ ┃ a₂₁ ┃ a₂₂ ┃ │ │ │ ┃ a₂₂ ┃ a₂₃ ┃ - /// ┗━━━━━┻━━━━━┹─────┴─────┘ └─────┺━━━━━┻━━━━━┹─────┘ └─────┴─────┺━━━━━┻━━━━━┛ - /// ``` + /// This is essentially equivalent to [`.windows_with_stride()`] with unit stride. pub fn windows(&self, window_size: E) -> Windows<'_, A, D> where E: IntoDimension, From 1b00771a20d57c07f3f28aa5d5132a70529b8087 Mon Sep 17 00:00:00 2001 From: Lazaro Hurtado Date: Sat, 10 Jun 2023 10:08:01 -0700 Subject: [PATCH 486/651] refactored unit stride logic --- src/iterators/windows.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/iterators/windows.rs b/src/iterators/windows.rs index efa84fb8e..d84b0e7b8 100644 --- a/src/iterators/windows.rs +++ b/src/iterators/windows.rs @@ -23,11 +23,8 @@ impl<'a, A, D: Dimension> Windows<'a, A, D> { let ndim = window.ndim(); let mut unit_stride = D::zeros(ndim); - let stride_slice = unit_stride.slice_mut(); - for s in stride_slice.iter_mut() { - *s = 1; - } - + unit_stride.slice_mut().fill(1); + Windows::new_with_stride(a, window, unit_stride) } From 5bcc73ec5a2a47a698ca418ec88ee43ae5ca2ff6 Mon Sep 17 00:00:00 2001 From: Lazaro Hurtado Date: Sat, 17 Jun 2023 15:25:29 -0700 Subject: [PATCH 487/651] updated Windows base computations to be safer --- src/iterators/windows.rs | 56 ++++++++++++++++++---------------------- tests/windows.rs | 28 ++++++++++++++++++++ 2 files changed, 53 insertions(+), 31 deletions(-) diff --git a/src/iterators/windows.rs b/src/iterators/windows.rs index d84b0e7b8..6238d0ed1 100644 --- a/src/iterators/windows.rs +++ b/src/iterators/windows.rs @@ -3,6 +3,7 @@ use crate::imp_prelude::*; use crate::IntoDimension; use crate::Layout; use crate::NdProducer; +use crate::Slice; /// Window producer and iterable /// @@ -24,16 +25,19 @@ impl<'a, A, D: Dimension> Windows<'a, A, D> { let mut unit_stride = D::zeros(ndim); unit_stride.slice_mut().fill(1); - + Windows::new_with_stride(a, window, unit_stride) } - pub(crate) fn new_with_stride(a: ArrayView<'a, A, D>, window_size: E, strides: E) -> Self + pub(crate) fn new_with_stride(a: ArrayView<'a, A, D>, window_size: E, axis_strides: E) -> Self where E: IntoDimension, { let window = window_size.into_dimension(); - let strides_d = strides.into_dimension(); + + let strides = axis_strides.into_dimension(); + let window_strides = a.strides.clone(); + ndassert!( a.ndim() == window.ndim(), concat!( @@ -44,45 +48,35 @@ impl<'a, A, D: Dimension> Windows<'a, A, D> { a.ndim(), a.shape() ); + ndassert!( - a.ndim() == strides_d.ndim(), + a.ndim() == strides.ndim(), concat!( "Stride dimension {} does not match array dimension {} ", "(with array of shape {:?})" ), - strides_d.ndim(), + strides.ndim(), a.ndim(), a.shape() ); - let mut size = a.dim; - for ((sz, &ws), &stride) in size - .slice_mut() - .iter_mut() - .zip(window.slice()) - .zip(strides_d.slice()) - { - assert_ne!(ws, 0, "window-size must not be zero!"); - assert_ne!(stride, 0, "stride cannot have a dimension as zero!"); - // cannot use std::cmp::max(0, ..) since arithmetic underflow panics - *sz = if *sz < ws { - 0 - } else { - ((*sz - (ws - 1) - 1) / stride) + 1 - }; - } - let window_strides = a.strides.clone(); - let mut array_strides = a.strides.clone(); - for (arr_stride, ix_stride) in array_strides.slice_mut().iter_mut().zip(strides_d.slice()) { - *arr_stride *= ix_stride; - } + let mut base = a; + base.slice_each_axis_inplace(|ax_desc| { + let len = ax_desc.len; + let wsz = window[ax_desc.axis.index()]; + let stride = strides[ax_desc.axis.index()]; - unsafe { - Windows { - base: ArrayView::new(a.ptr, size, array_strides), - window, - strides: window_strides, + if len < wsz { + Slice::new(0, Some(0), 1) + } else { + Slice::new(0, Some((len - wsz + 1) as isize), stride as isize) } + }); + + Windows { + base, + window, + strides: window_strides, } } } diff --git a/tests/windows.rs b/tests/windows.rs index 2c928aaef..095976eaa 100644 --- a/tests/windows.rs +++ b/tests/windows.rs @@ -302,3 +302,31 @@ fn test_window_neg_stride() { answer.iter() ); } + +#[test] +fn test_windows_with_stride_on_inverted_axis() { + let mut array = Array::from_iter(1..17).into_shape((4, 4)).unwrap(); + + // inverting axis results in negative stride + array.invert_axis(Axis(0)); + itertools::assert_equal( + array.windows_with_stride((2, 2), (2,2)), + vec![ + arr2(&[[13, 14], [9, 10]]), + arr2(&[[15, 16], [11, 12]]), + arr2(&[[5, 6], [1, 2]]), + arr2(&[[7, 8], [3, 4]]), + ], + ); + + array.invert_axis(Axis(1)); + itertools::assert_equal( + array.windows_with_stride((2, 2), (2,2)), + vec![ + arr2(&[[16, 15], [12, 11]]), + arr2(&[[14, 13], [10, 9]]), + arr2(&[[8, 7], [4, 3]]), + arr2(&[[6, 5], [2, 1]]), + ], + ); +} \ No newline at end of file From 8ec4d917e5c1b19fcfc6c15d2f781fd4828ae224 Mon Sep 17 00:00:00 2001 From: Venkat Venkatesan Date: Sat, 5 Aug 2023 08:30:52 -0400 Subject: [PATCH 488/651] Fix comparison with NumPy of slicing with negative step The existing documentation states that the behavior of slicing is the same as in NumPy except when `step < -1`, implying that the behavior is the same when `step = -1`. But this is not true: In [1]: import numpy as np In [2]: x = np.arange(10) In [3]: x[2 : 5 : -1] # Analogous slice in `ndarray`: `array![4, 3, 2]` Out[3]: array([], dtype=int32) In [4]: x[5 : 2 : -1] # Analogous slice in `ndarray`: `array![]` Out[4]: array([5, 4, 3]) So `step < -1` should be replaced by `step < 0` in the documentation. There are some further differences in slicing behavior with negative step, having to do with the default values for `start` and `end`: In [5]: x[: 7 : -1] # Analogous slice in `ndarray`: `array![6, 5, 4, 3, 2, 1, 0]` Out[5]: array([9, 8]) In [6]: x[7 : : -1] # Analogous slice in `ndarray`: `array![9, 8, 7]` Out[6]: array([7, 6, 5, 4, 3, 2, 1, 0]) --- src/doc/ndarray_for_numpy_users/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index 0cabf7f2a..7eaaf7d01 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -111,7 +111,7 @@ //! When slicing in `ndarray`, the axis is first sliced with `start..end`. Then if //! `step` is positive, the first index is the front of the slice; if `step` is //! negative, the first index is the back of the slice. This means that the -//! behavior is the same as NumPy except when `step < -1`. See the docs for the +//! behavior is different from NumPy when `step < 0`. See the docs for the //! [`s![]` macro][s!] for more details. //! //! @@ -246,8 +246,8 @@ //! methods [`.slice_mut()`][.slice_mut()], [`.slice_move()`][.slice_move()], and //! [`.slice_collapse()`][.slice_collapse()]. //! -//! * The behavior of slicing is slightly different from NumPy for slices with -//! `step < -1`. See the docs for the [`s![]` macro][s!] for more details. +//! * The behavior of slicing is different from NumPy for slices with +//! `step < 0`. See the docs for the [`s![]` macro][s!] for more details. //! //! NumPy | `ndarray` | Notes //! ------|-----------|------ From 40bb0b203a224a767dafc80be09ae185e8f97758 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Mon, 7 Aug 2023 07:56:51 +0200 Subject: [PATCH 489/651] Make Clippy happy and fix MSRV build --- scripts/all-tests.sh | 4 ++++ src/dimension/dimension_trait.rs | 2 +- src/zip/ndproducer.rs | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index 2167f3451..a89b496e0 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -9,6 +9,10 @@ CHANNEL=$2 if [ "$CHANNEL" = "1.51.0" ]; then cargo update --package rayon --precise 1.5.3 cargo update --package rayon-core --precise 1.9.3 + cargo update --package quote --precise 1.0.30 + cargo update --package proc-macro2 --precise 1.0.65 + cargo update --package rmp --precise 0.8.11 + cargo update --package serde_json --precise 1.0.99 cargo update --package serde --precise 1.0.156 cargo update --package thiserror --precise 1.0.39 cargo update --package once_cell --precise 1.14.0 diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 7181d2268..7a3955fb7 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -90,7 +90,7 @@ pub trait Dimension: fn size_checked(&self) -> Option { self.slice() .iter() - .fold(Some(1), |s, &a| s.and_then(|s_| s_.checked_mul(a))) + .try_fold(1_usize, |s, &a| s.checked_mul(a)) } #[doc(hidden)] diff --git a/src/zip/ndproducer.rs b/src/zip/ndproducer.rs index 0ddf33faf..4eb986d37 100644 --- a/src/zip/ndproducer.rs +++ b/src/zip/ndproducer.rs @@ -1,7 +1,7 @@ use crate::imp_prelude::*; use crate::Layout; use crate::NdIndex; -#[cfg(not(features = "std"))] +#[cfg(not(feature = "std"))] use alloc::vec::Vec; /// Argument conversion into a producer. From 7330141f07b4dc8136e7717b3a90f4a81591c629 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Sat, 2 Dec 2023 07:25:36 +0100 Subject: [PATCH 490/651] Fix new rustc lints to make the CI pass. --- src/impl_views/mod.rs | 3 --- src/lib.rs | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/impl_views/mod.rs b/src/impl_views/mod.rs index 487cc3cb2..cabea5b37 100644 --- a/src/impl_views/mod.rs +++ b/src/impl_views/mod.rs @@ -3,7 +3,4 @@ mod conversions; mod indexing; mod splitting; -pub use constructors::*; -pub use conversions::*; pub use indexing::*; -pub use splitting::*; diff --git a/src/lib.rs b/src/lib.rs index 07e5ed680..41a23ae3f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ #![doc(html_root_url = "https://docs.rs/ndarray/0.15/")] #![doc(html_logo_url = "https://rust-ndarray.github.io/images/rust-ndarray_logo.svg")] #![allow( + unstable_name_collisions, // our `PointerExt` collides with upcoming inherent methods on `NonNull` clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, From 8e72e33487141927c2991b4a919265c59dd4f307 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Sat, 2 Dec 2023 07:35:05 +0100 Subject: [PATCH 491/651] Use PowerPC instead of MIPS for big endian cross tests as it is still tier 2 whereas MIPS is now tier 3. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eabc21c61..5fd4d9b30 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: matrix: include: - rust: stable - target: mips-unknown-linux-gnu + target: powerpc-unknown-linux-gnu - rust: stable target: i686-unknown-linux-gnu From d3334a565fb6ace1d1468da37be7cfbc7e9c31e2 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Sat, 2 Dec 2023 08:06:42 +0100 Subject: [PATCH 492/651] Update forcing dependency versions to fix MSRV build. --- scripts/all-tests.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index a89b496e0..9d7f5dd39 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -7,17 +7,18 @@ FEATURES=$1 CHANNEL=$2 if [ "$CHANNEL" = "1.51.0" ]; then + cargo update --package openblas-src --precise 0.10.5 + cargo update --package openblas-build --precise 0.10.5 + cargo update --package once_cell --precise 1.14.0 + cargo update --package byteorder --precise 1.4.3 cargo update --package rayon --precise 1.5.3 cargo update --package rayon-core --precise 1.9.3 - cargo update --package quote --precise 1.0.30 - cargo update --package proc-macro2 --precise 1.0.65 cargo update --package rmp --precise 0.8.11 cargo update --package serde_json --precise 1.0.99 cargo update --package serde --precise 1.0.156 cargo update --package thiserror --precise 1.0.39 - cargo update --package once_cell --precise 1.14.0 - cargo update --package openblas-src --precise 0.10.5 - cargo update --package openblas-build --precise 0.10.5 + cargo update --package quote --precise 1.0.30 + cargo update --package proc-macro2 --precise 1.0.65 fi cargo build --verbose --no-default-features From 2a5553f1f9bb1c2f43bfe249c99cf7a8e649085c Mon Sep 17 00:00:00 2001 From: KmolYuan Date: Tue, 13 Jul 2021 11:35:54 +0800 Subject: [PATCH 493/651] Add common used functions. --- src/impl_float_maths.rs | 181 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 2 files changed, 183 insertions(+) create mode 100644 src/impl_float_maths.rs diff --git a/src/impl_float_maths.rs b/src/impl_float_maths.rs new file mode 100644 index 000000000..c7402cebe --- /dev/null +++ b/src/impl_float_maths.rs @@ -0,0 +1,181 @@ +//! Element-wise methods for ndarray + +use num_traits::Float; + +use crate::imp_prelude::*; + +macro_rules! boolean_op { + ($(#[$meta1:meta])* fn $id1:ident $(#[$meta2:meta])* fn $id2:ident -> $func:ident) => { + $(#[$meta1])* + pub fn $id1(&self) -> Array { + self.mapv(A::$func) + } + $(#[$meta2])* + pub fn $id2(&self) -> bool { + self.mapv(A::$func).iter().any(|&b|b) + } + }; +} + +macro_rules! map_op { + ($(#[$meta:meta])* fn $id:ident) => { + $(#[$meta])* + pub fn $id(&self) -> Array { + self.mapv(A::$id) + } + }; +} + +macro_rules! bin_op { + ($(#[$meta:meta])* fn $id:ident($ty:ty)) => { + $(#[$meta])* + pub fn $id(&self, rhs: $ty) -> Array { + self.mapv(|v| A::$id(v, rhs)) + } + }; +} + +/// # Element-wise methods for Float Array +/// +/// Element-wise math functions for any array type that contains float number. +impl ArrayBase +where + A: Float, + S: RawData + Data, + D: Dimension, +{ + boolean_op! { + /// If the number is `NaN` (not a number), then `true` is returned for each element. + fn is_nan + /// Return `true` if any element is `NaN` (not a number). + fn is_nan_any -> is_nan + } + boolean_op! { + /// If the number is infinity, then `true` is returned for each element. + fn is_infinite + /// Return `true` if any element is infinity. + fn is_infinite_any -> is_infinite + } + map_op! { + /// The largest integer less than or equal to each element. + fn floor + } + map_op! { + /// The smallest integer less than or equal to each element. + fn ceil + } + map_op! { + /// The nearest integer of each element. + fn round + } + map_op! { + /// The integer part of each element. + fn trunc + } + map_op! { + /// The fractional part of each element. + fn fract + } + map_op! { + /// Absolute of each element. + fn abs + } + map_op! { + /// Sign number of each element. + /// + /// + `1.0` for all positive numbers. + /// + `-1.0` for all negative numbers. + /// + `NaN` for all `NaN` (not a number). + fn signum + } + map_op! { + /// The reciprocal (inverse) of each element, `1/x`. + fn recip + } + bin_op! { + /// Integer power of each element. + /// + /// This function is generally faster than using float power. + fn powi(i32) + } + bin_op! { + /// Float power of each element. + fn powf(A) + } + + /// Square of each element. + pub fn square(&self) -> Array { + self.mapv(|v| v * v) + } + + map_op! { + /// Square root of each element. + fn sqrt + } + map_op! { + /// `e^x` of each element. (Exponential function) + fn exp + } + map_op! { + /// `2^x` of each element. + fn exp2 + } + map_op! { + /// Natural logarithm of each element. + fn ln + } + bin_op! { + /// Logarithm of each element with respect to an arbitrary base. + fn log(A) + } + map_op! { + /// Base 2 logarithm of each element. + fn log2 + } + map_op! { + /// Base 10 logarithm of each element. + fn log10 + } + bin_op! { + /// The positive difference between given number and each element. + fn abs_sub(A) + } + map_op! { + /// Cubic root of each element. + fn cbrt + } + map_op! { + /// Sine of each element. (in radians) + fn sin + } + map_op! { + /// Cosine of each element. (in radians) + fn cos + } + map_op! { + /// Tangent of each element. (in radians) + fn tan + } + map_op! { + /// Converts radians to degrees for each element. + fn to_degrees + } + map_op! { + /// Converts degrees to radians for each element. + fn to_radians + } + + /// Limit the values for each element. + /// + /// ``` + /// use ndarray::{Array1, array}; + /// + /// let a = Array1::range(0., 10., 1.); + /// assert_eq!(a.clip(1., 8.), array![1., 1., 2., 3., 4., 5., 6., 7., 8., 8.]); + /// assert_eq!(a.clip(8., 1.), array![1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]); + /// assert_eq!(a.clip(3., 6.), array![3., 3., 3., 3., 4., 5., 6., 6., 6., 6.]); + /// ``` + pub fn clip(&self, min: A, max: A) -> Array { + self.mapv(|v| A::max(v, min)).mapv(|v| A::min(v, max)) + } +} diff --git a/src/lib.rs b/src/lib.rs index 41a23ae3f..bcb205667 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1506,6 +1506,8 @@ mod impl_internal_constructors; mod impl_constructors; mod impl_methods; +#[cfg(feature = "std")] +mod impl_float_maths; mod impl_owned_array; mod impl_special_element_types; From ccb1f761f885dddf8e410985bc6bb39960019893 Mon Sep 17 00:00:00 2001 From: KmolYuan Date: Tue, 13 Jul 2021 14:38:22 +0800 Subject: [PATCH 494/651] Chain the mapping. --- src/impl_float_maths.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impl_float_maths.rs b/src/impl_float_maths.rs index c7402cebe..9433aecec 100644 --- a/src/impl_float_maths.rs +++ b/src/impl_float_maths.rs @@ -176,6 +176,6 @@ where /// assert_eq!(a.clip(3., 6.), array![3., 3., 3., 3., 4., 5., 6., 6., 6., 6.]); /// ``` pub fn clip(&self, min: A, max: A) -> Array { - self.mapv(|v| A::max(v, min)).mapv(|v| A::min(v, max)) + self.mapv(|v| A::min(A::max(v, min), max)) } } From bd696720f6eda52c2b6376f8608ea798ecb41d36 Mon Sep 17 00:00:00 2001 From: KmolYuan Date: Fri, 23 Jul 2021 18:07:00 +0800 Subject: [PATCH 495/651] Change macro names. --- src/impl_float_maths.rs | 52 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/impl_float_maths.rs b/src/impl_float_maths.rs index 9433aecec..aebab2084 100644 --- a/src/impl_float_maths.rs +++ b/src/impl_float_maths.rs @@ -17,7 +17,7 @@ macro_rules! boolean_op { }; } -macro_rules! map_op { +macro_rules! unary_op { ($(#[$meta:meta])* fn $id:ident) => { $(#[$meta])* pub fn $id(&self) -> Array { @@ -26,7 +26,7 @@ macro_rules! map_op { }; } -macro_rules! bin_op { +macro_rules! binary_op { ($(#[$meta:meta])* fn $id:ident($ty:ty)) => { $(#[$meta])* pub fn $id(&self, rhs: $ty) -> Array { @@ -56,31 +56,31 @@ where /// Return `true` if any element is infinity. fn is_infinite_any -> is_infinite } - map_op! { + unary_op! { /// The largest integer less than or equal to each element. fn floor } - map_op! { + unary_op! { /// The smallest integer less than or equal to each element. fn ceil } - map_op! { + unary_op! { /// The nearest integer of each element. fn round } - map_op! { + unary_op! { /// The integer part of each element. fn trunc } - map_op! { + unary_op! { /// The fractional part of each element. fn fract } - map_op! { + unary_op! { /// Absolute of each element. fn abs } - map_op! { + unary_op! { /// Sign number of each element. /// /// + `1.0` for all positive numbers. @@ -88,17 +88,17 @@ where /// + `NaN` for all `NaN` (not a number). fn signum } - map_op! { + unary_op! { /// The reciprocal (inverse) of each element, `1/x`. fn recip } - bin_op! { + binary_op! { /// Integer power of each element. /// /// This function is generally faster than using float power. fn powi(i32) } - bin_op! { + binary_op! { /// Float power of each element. fn powf(A) } @@ -108,59 +108,59 @@ where self.mapv(|v| v * v) } - map_op! { + unary_op! { /// Square root of each element. fn sqrt } - map_op! { + unary_op! { /// `e^x` of each element. (Exponential function) fn exp } - map_op! { + unary_op! { /// `2^x` of each element. fn exp2 } - map_op! { + unary_op! { /// Natural logarithm of each element. fn ln } - bin_op! { + binary_op! { /// Logarithm of each element with respect to an arbitrary base. fn log(A) } - map_op! { + unary_op! { /// Base 2 logarithm of each element. fn log2 } - map_op! { + unary_op! { /// Base 10 logarithm of each element. fn log10 } - bin_op! { + binary_op! { /// The positive difference between given number and each element. fn abs_sub(A) } - map_op! { + unary_op! { /// Cubic root of each element. fn cbrt } - map_op! { + unary_op! { /// Sine of each element. (in radians) fn sin } - map_op! { + unary_op! { /// Cosine of each element. (in radians) fn cos } - map_op! { + unary_op! { /// Tangent of each element. (in radians) fn tan } - map_op! { + unary_op! { /// Converts radians to degrees for each element. fn to_degrees } - map_op! { + unary_op! { /// Converts degrees to radians for each element. fn to_radians } From 1de789853b96f43e91a233a4074d3c1c510b8366 Mon Sep 17 00:00:00 2001 From: KmolYuan Date: Fri, 6 Aug 2021 07:37:06 +0800 Subject: [PATCH 496/651] Simplify macros. --- src/impl_float_maths.rs | 120 ++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 72 deletions(-) diff --git a/src/impl_float_maths.rs b/src/impl_float_maths.rs index aebab2084..3037a05b4 100644 --- a/src/impl_float_maths.rs +++ b/src/impl_float_maths.rs @@ -5,33 +5,33 @@ use num_traits::Float; use crate::imp_prelude::*; macro_rules! boolean_op { - ($(#[$meta1:meta])* fn $id1:ident $(#[$meta2:meta])* fn $id2:ident -> $func:ident) => { - $(#[$meta1])* + ($($(#[$meta1:meta])* fn $id1:ident $(#[$meta2:meta])* fn $id2:ident -> $func:ident)+) => { + $($(#[$meta1])* pub fn $id1(&self) -> Array { self.mapv(A::$func) } $(#[$meta2])* pub fn $id2(&self) -> bool { self.mapv(A::$func).iter().any(|&b|b) - } + })+ }; } macro_rules! unary_op { - ($(#[$meta:meta])* fn $id:ident) => { - $(#[$meta])* + ($($(#[$meta:meta])* fn $id:ident)+) => { + $($(#[$meta])* pub fn $id(&self) -> Array { self.mapv(A::$id) - } + })+ }; } macro_rules! binary_op { - ($(#[$meta:meta])* fn $id:ident($ty:ty)) => { - $(#[$meta])* + ($($(#[$meta:meta])* fn $id:ident($ty:ty))+) => { + $($(#[$meta])* pub fn $id(&self, rhs: $ty) -> Array { self.mapv(|v| A::$id(v, rhs)) - } + })+ }; } @@ -49,8 +49,7 @@ where fn is_nan /// Return `true` if any element is `NaN` (not a number). fn is_nan_any -> is_nan - } - boolean_op! { + /// If the number is infinity, then `true` is returned for each element. fn is_infinite /// Return `true` if any element is infinity. @@ -59,111 +58,88 @@ where unary_op! { /// The largest integer less than or equal to each element. fn floor - } - unary_op! { + /// The smallest integer less than or equal to each element. fn ceil - } - unary_op! { + /// The nearest integer of each element. fn round - } - unary_op! { + /// The integer part of each element. fn trunc - } - unary_op! { + /// The fractional part of each element. fn fract - } - unary_op! { + /// Absolute of each element. fn abs - } - unary_op! { + /// Sign number of each element. /// /// + `1.0` for all positive numbers. /// + `-1.0` for all negative numbers. /// + `NaN` for all `NaN` (not a number). fn signum - } - unary_op! { + /// The reciprocal (inverse) of each element, `1/x`. fn recip - } - binary_op! { - /// Integer power of each element. - /// - /// This function is generally faster than using float power. - fn powi(i32) - } - binary_op! { - /// Float power of each element. - fn powf(A) - } - - /// Square of each element. - pub fn square(&self) -> Array { - self.mapv(|v| v * v) - } - unary_op! { /// Square root of each element. fn sqrt - } - unary_op! { + /// `e^x` of each element. (Exponential function) fn exp - } - unary_op! { + /// `2^x` of each element. fn exp2 - } - unary_op! { + /// Natural logarithm of each element. fn ln - } - binary_op! { - /// Logarithm of each element with respect to an arbitrary base. - fn log(A) - } - unary_op! { + /// Base 2 logarithm of each element. fn log2 - } - unary_op! { + /// Base 10 logarithm of each element. fn log10 - } - binary_op! { - /// The positive difference between given number and each element. - fn abs_sub(A) - } - unary_op! { + /// Cubic root of each element. fn cbrt - } - unary_op! { + /// Sine of each element. (in radians) fn sin - } - unary_op! { + /// Cosine of each element. (in radians) fn cos - } - unary_op! { + /// Tangent of each element. (in radians) fn tan - } - unary_op! { + /// Converts radians to degrees for each element. fn to_degrees - } - unary_op! { + /// Converts degrees to radians for each element. fn to_radians } + binary_op! { + /// Integer power of each element. + /// + /// This function is generally faster than using float power. + fn powi(i32) + + /// Float power of each element. + fn powf(A) + + /// Logarithm of each element with respect to an arbitrary base. + fn log(A) + + /// The positive difference between given number and each element. + fn abs_sub(A) + } + + /// Square of each element. + pub fn square(&self) -> Array { + self.mapv(|v| v * v) + } /// Limit the values for each element. /// From 82af40d3a6e3d63a4da40a988fb19e138c3bf3ce Mon Sep 17 00:00:00 2001 From: KmolYuan Date: Sun, 3 Oct 2021 10:21:30 +0800 Subject: [PATCH 497/651] Apply partial suggestions. --- src/impl_float_maths.rs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/impl_float_maths.rs b/src/impl_float_maths.rs index 3037a05b4..12100fb99 100644 --- a/src/impl_float_maths.rs +++ b/src/impl_float_maths.rs @@ -7,12 +7,14 @@ use crate::imp_prelude::*; macro_rules! boolean_op { ($($(#[$meta1:meta])* fn $id1:ident $(#[$meta2:meta])* fn $id2:ident -> $func:ident)+) => { $($(#[$meta1])* + #[must_use = "method returns a new array and does not mutate the original value"] pub fn $id1(&self) -> Array { self.mapv(A::$func) } $(#[$meta2])* + #[must_use = "method returns a new boolean value and does not mutate the original value"] pub fn $id2(&self) -> bool { - self.mapv(A::$func).iter().any(|&b|b) + self.mapv(A::$func).iter().any(|&b| b) })+ }; } @@ -20,6 +22,7 @@ macro_rules! boolean_op { macro_rules! unary_op { ($($(#[$meta:meta])* fn $id:ident)+) => { $($(#[$meta])* + #[must_use = "method returns a new array and does not mutate the original value"] pub fn $id(&self) -> Array { self.mapv(A::$id) })+ @@ -29,6 +32,7 @@ macro_rules! unary_op { macro_rules! binary_op { ($($(#[$meta:meta])* fn $id:ident($ty:ty))+) => { $($(#[$meta])* + #[must_use = "method returns a new array and does not mutate the original value"] pub fn $id(&self, rhs: $ty) -> Array { self.mapv(|v| A::$id(v, rhs)) })+ @@ -41,19 +45,19 @@ macro_rules! binary_op { impl ArrayBase where A: Float, - S: RawData + Data, + S: Data, D: Dimension, { boolean_op! { /// If the number is `NaN` (not a number), then `true` is returned for each element. fn is_nan /// Return `true` if any element is `NaN` (not a number). - fn is_nan_any -> is_nan + fn is_any_nan -> is_nan /// If the number is infinity, then `true` is returned for each element. fn is_infinite /// Return `true` if any element is infinity. - fn is_infinite_any -> is_infinite + fn is_any_infinite -> is_infinite } unary_op! { /// The largest integer less than or equal to each element. @@ -87,7 +91,7 @@ where /// Square root of each element. fn sqrt - /// `e^x` of each element. (Exponential function) + /// `e^x` of each element (exponential function). fn exp /// `2^x` of each element. @@ -105,13 +109,13 @@ where /// Cubic root of each element. fn cbrt - /// Sine of each element. (in radians) + /// Sine of each element (in radians). fn sin - /// Cosine of each element. (in radians) + /// Cosine of each element (in radians). fn cos - /// Tangent of each element. (in radians) + /// Tangent of each element (in radians). fn tan /// Converts radians to degrees for each element. @@ -136,9 +140,10 @@ where fn abs_sub(A) } - /// Square of each element. - pub fn square(&self) -> Array { - self.mapv(|v| v * v) + /// Square (two powers) of each element. + #[must_use = "method returns a new array and does not mutate the original value"] + pub fn pow2(&self) -> Array { + self.mapv(|v: A| v * v) } /// Limit the values for each element. From a796dc5e50d0f5f3379934f73f96dfcb1153295e Mon Sep 17 00:00:00 2001 From: KmolYuan Date: Wed, 8 Dec 2021 15:10:11 +0800 Subject: [PATCH 498/651] Apply most of the suggestions. --- src/lib.rs | 26 +++--- src/{ => numeric}/impl_float_maths.rs | 110 +++++++++++++++----------- src/numeric/mod.rs | 2 + 3 files changed, 74 insertions(+), 64 deletions(-) rename src/{ => numeric}/impl_float_maths.rs (63%) diff --git a/src/lib.rs b/src/lib.rs index bcb205667..e72ed7e65 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,13 +112,12 @@ //! For conversion between `ndarray`, [`nalgebra`](https://crates.io/crates/nalgebra) and //! [`image`](https://crates.io/crates/image) check out [`nshare`](https://crates.io/crates/nshare). - extern crate alloc; -#[cfg(feature = "std")] -extern crate std; #[cfg(not(feature = "std"))] extern crate core as std; +#[cfg(feature = "std")] +extern crate std; #[cfg(feature = "blas")] extern crate cblas_sys; @@ -126,8 +125,8 @@ extern crate cblas_sys; #[cfg(feature = "docs")] pub mod doc; -use std::marker::PhantomData; use alloc::sync::Arc; +use std::marker::PhantomData; pub use crate::dimension::dim::*; pub use crate::dimension::{Axis, AxisDescription, Dimension, IntoDimension, RemoveAxis}; @@ -146,16 +145,16 @@ use crate::iterators::Baseiter; use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut}; pub use crate::arraytraits::AsArray; +pub use crate::linalg_traits::LinalgScalar; #[cfg(feature = "std")] pub use crate::linalg_traits::NdFloat; -pub use crate::linalg_traits::LinalgScalar; #[allow(deprecated)] // stack_new_axis pub use crate::stacking::{concatenate, stack, stack_new_axis}; -pub use crate::math_cell::MathCell; pub use crate::impl_views::IndexLonger; -pub use crate::shape_builder::{Shape, ShapeBuilder, ShapeArg, StrideShape}; +pub use crate::math_cell::MathCell; +pub use crate::shape_builder::{Shape, ShapeArg, ShapeBuilder, StrideShape}; #[macro_use] mod macro_utils; @@ -176,8 +175,7 @@ mod data_traits; pub use crate::aliases::*; pub use crate::data_traits::{ - Data, DataMut, DataOwned, DataShared, RawData, RawDataClone, RawDataMut, - RawDataSubst, + Data, DataMut, DataOwned, DataShared, RawData, RawDataClone, RawDataMut, RawDataSubst, }; mod free_functions; @@ -200,9 +198,9 @@ mod partial; mod shape_builder; #[macro_use] mod slice; +mod low_level_util; mod split_at; mod stacking; -mod low_level_util; #[macro_use] mod zip; @@ -1502,12 +1500,10 @@ impl<'a, A> CowRepr<'a, A> { // Consider the doc effect of ordering modules here. mod impl_clone; -mod impl_internal_constructors; mod impl_constructors; +mod impl_internal_constructors; mod impl_methods; -#[cfg(feature = "std")] -mod impl_float_maths; mod impl_owned_array; mod impl_special_element_types; @@ -1566,9 +1562,7 @@ where let d = self.dim.try_remove_axis(axis); let s = self.strides.try_remove_axis(axis); // safe because new dimension, strides allow access to a subset of old data - unsafe { - self.with_strides_dim(s, d) - } + unsafe { self.with_strides_dim(s, d) } } } diff --git a/src/impl_float_maths.rs b/src/numeric/impl_float_maths.rs similarity index 63% rename from src/impl_float_maths.rs rename to src/numeric/impl_float_maths.rs index 12100fb99..55cc57d3f 100644 --- a/src/impl_float_maths.rs +++ b/src/numeric/impl_float_maths.rs @@ -1,25 +1,35 @@ -//! Element-wise methods for ndarray +// Element-wise methods for ndarray +#[cfg(feature = "std")] use num_traits::Float; use crate::imp_prelude::*; -macro_rules! boolean_op { - ($($(#[$meta1:meta])* fn $id1:ident $(#[$meta2:meta])* fn $id2:ident -> $func:ident)+) => { - $($(#[$meta1])* +#[cfg(feature = "std")] +macro_rules! boolean_ops { + ($(#[$meta1:meta])* fn $func:ident + $(#[$meta2:meta])* fn $all:ident + $(#[$meta3:meta])* fn $any:ident) => { + $(#[$meta1])* #[must_use = "method returns a new array and does not mutate the original value"] - pub fn $id1(&self) -> Array { + pub fn $func(&self) -> Array { self.mapv(A::$func) } $(#[$meta2])* #[must_use = "method returns a new boolean value and does not mutate the original value"] - pub fn $id2(&self) -> bool { - self.mapv(A::$func).iter().any(|&b| b) - })+ + pub fn $all(&self) -> bool { + $crate::Zip::from(self).all(|&elt| !elt.$func()) + } + $(#[$meta3])* + #[must_use = "method returns a new boolean value and does not mutate the original value"] + pub fn $any(&self) -> bool { + !self.$all() + } }; } -macro_rules! unary_op { +#[cfg(feature = "std")] +macro_rules! unary_ops { ($($(#[$meta:meta])* fn $id:ident)+) => { $($(#[$meta])* #[must_use = "method returns a new array and does not mutate the original value"] @@ -29,7 +39,8 @@ macro_rules! unary_op { }; } -macro_rules! binary_op { +#[cfg(feature = "std")] +macro_rules! binary_ops { ($($(#[$meta:meta])* fn $id:ident($ty:ty))+) => { $($(#[$meta])* #[must_use = "method returns a new array and does not mutate the original value"] @@ -39,103 +50,87 @@ macro_rules! binary_op { }; } -/// # Element-wise methods for Float Array +/// # Element-wise methods for float arrays /// /// Element-wise math functions for any array type that contains float number. +#[cfg(feature = "std")] impl ArrayBase where - A: Float, + A: 'static + Float, S: Data, D: Dimension, { - boolean_op! { + boolean_ops! { /// If the number is `NaN` (not a number), then `true` is returned for each element. fn is_nan + /// Return `true` if all elements are `NaN` (not a number). + fn is_all_nan /// Return `true` if any element is `NaN` (not a number). - fn is_any_nan -> is_nan - + fn is_any_nan + } + boolean_ops! { /// If the number is infinity, then `true` is returned for each element. fn is_infinite + /// Return `true` if all elements are infinity. + fn is_all_infinite /// Return `true` if any element is infinity. - fn is_any_infinite -> is_infinite + fn is_any_infinite } - unary_op! { + unary_ops! { /// The largest integer less than or equal to each element. fn floor - /// The smallest integer less than or equal to each element. fn ceil - /// The nearest integer of each element. fn round - /// The integer part of each element. fn trunc - /// The fractional part of each element. fn fract - /// Absolute of each element. fn abs - /// Sign number of each element. /// /// + `1.0` for all positive numbers. /// + `-1.0` for all negative numbers. /// + `NaN` for all `NaN` (not a number). fn signum - /// The reciprocal (inverse) of each element, `1/x`. fn recip - /// Square root of each element. fn sqrt - /// `e^x` of each element (exponential function). fn exp - /// `2^x` of each element. fn exp2 - /// Natural logarithm of each element. fn ln - /// Base 2 logarithm of each element. fn log2 - /// Base 10 logarithm of each element. fn log10 - /// Cubic root of each element. fn cbrt - /// Sine of each element (in radians). fn sin - /// Cosine of each element (in radians). fn cos - /// Tangent of each element (in radians). fn tan - /// Converts radians to degrees for each element. fn to_degrees - /// Converts degrees to radians for each element. fn to_radians } - binary_op! { + binary_ops! { /// Integer power of each element. /// /// This function is generally faster than using float power. fn powi(i32) - /// Float power of each element. fn powf(A) - /// Logarithm of each element with respect to an arbitrary base. fn log(A) - /// The positive difference between given number and each element. fn abs_sub(A) } @@ -145,18 +140,37 @@ where pub fn pow2(&self) -> Array { self.mapv(|v: A| v * v) } +} - /// Limit the values for each element. +impl ArrayBase +where + A: 'static + PartialOrd + Clone, + S: Data, + D: Dimension, +{ + /// Limit the values for each element, similar to NumPy's `clip` function. /// /// ``` - /// use ndarray::{Array1, array}; + /// use ndarray::array; /// - /// let a = Array1::range(0., 10., 1.); - /// assert_eq!(a.clip(1., 8.), array![1., 1., 2., 3., 4., 5., 6., 7., 8., 8.]); - /// assert_eq!(a.clip(8., 1.), array![1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]); - /// assert_eq!(a.clip(3., 6.), array![3., 3., 3., 3., 4., 5., 6., 6., 6., 6.]); + /// let a = array![0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]; + /// assert_eq!(a.clamp(1., 8.), array![1., 1., 2., 3., 4., 5., 6., 7., 8., 8.]); + /// assert_eq!(a.clamp(3., 6.), array![3., 3., 3., 3., 4., 5., 6., 6., 6., 6.]); /// ``` - pub fn clip(&self, min: A, max: A) -> Array { - self.mapv(|v| A::min(A::max(v, min), max)) + /// + /// # Panics + /// + /// Panics if `min > max`, `min` is `NaN`, or `max` is `NaN`. + pub fn clamp(&self, min: A, max: A) -> Array { + assert!(min <= max, "min must be less than or equal to max"); + self.mapv(|v| { + if v < min { + min.clone() + } else if v > max { + max.clone() + } else { + v + } + }) } } diff --git a/src/numeric/mod.rs b/src/numeric/mod.rs index b3da06746..c0a7228c5 100644 --- a/src/numeric/mod.rs +++ b/src/numeric/mod.rs @@ -1 +1,3 @@ mod impl_numeric; + +mod impl_float_maths; From a982f0ba0f30887469903cc84b12e1fbfde72fa9 Mon Sep 17 00:00:00 2001 From: KmolYuan Date: Wed, 8 Dec 2021 15:30:34 +0800 Subject: [PATCH 499/651] Revert cargo fmt. --- src/lib.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e72ed7e65..41a23ae3f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,12 +112,13 @@ //! For conversion between `ndarray`, [`nalgebra`](https://crates.io/crates/nalgebra) and //! [`image`](https://crates.io/crates/image) check out [`nshare`](https://crates.io/crates/nshare). + extern crate alloc; -#[cfg(not(feature = "std"))] -extern crate core as std; #[cfg(feature = "std")] extern crate std; +#[cfg(not(feature = "std"))] +extern crate core as std; #[cfg(feature = "blas")] extern crate cblas_sys; @@ -125,8 +126,8 @@ extern crate cblas_sys; #[cfg(feature = "docs")] pub mod doc; -use alloc::sync::Arc; use std::marker::PhantomData; +use alloc::sync::Arc; pub use crate::dimension::dim::*; pub use crate::dimension::{Axis, AxisDescription, Dimension, IntoDimension, RemoveAxis}; @@ -145,16 +146,16 @@ use crate::iterators::Baseiter; use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut}; pub use crate::arraytraits::AsArray; -pub use crate::linalg_traits::LinalgScalar; #[cfg(feature = "std")] pub use crate::linalg_traits::NdFloat; +pub use crate::linalg_traits::LinalgScalar; #[allow(deprecated)] // stack_new_axis pub use crate::stacking::{concatenate, stack, stack_new_axis}; -pub use crate::impl_views::IndexLonger; pub use crate::math_cell::MathCell; -pub use crate::shape_builder::{Shape, ShapeArg, ShapeBuilder, StrideShape}; +pub use crate::impl_views::IndexLonger; +pub use crate::shape_builder::{Shape, ShapeBuilder, ShapeArg, StrideShape}; #[macro_use] mod macro_utils; @@ -175,7 +176,8 @@ mod data_traits; pub use crate::aliases::*; pub use crate::data_traits::{ - Data, DataMut, DataOwned, DataShared, RawData, RawDataClone, RawDataMut, RawDataSubst, + Data, DataMut, DataOwned, DataShared, RawData, RawDataClone, RawDataMut, + RawDataSubst, }; mod free_functions; @@ -198,9 +200,9 @@ mod partial; mod shape_builder; #[macro_use] mod slice; -mod low_level_util; mod split_at; mod stacking; +mod low_level_util; #[macro_use] mod zip; @@ -1500,8 +1502,8 @@ impl<'a, A> CowRepr<'a, A> { // Consider the doc effect of ordering modules here. mod impl_clone; -mod impl_constructors; mod impl_internal_constructors; +mod impl_constructors; mod impl_methods; mod impl_owned_array; @@ -1562,7 +1564,9 @@ where let d = self.dim.try_remove_axis(axis); let s = self.strides.try_remove_axis(axis); // safe because new dimension, strides allow access to a subset of old data - unsafe { self.with_strides_dim(s, d) } + unsafe { + self.with_strides_dim(s, d) + } } } From fa57078a3fee50b8eb57a4a0cbf41f407863e2cb Mon Sep 17 00:00:00 2001 From: KmolYuan Date: Sun, 26 Nov 2023 20:46:08 +0800 Subject: [PATCH 500/651] Improve clamp implementation --- src/numeric/impl_float_maths.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/numeric/impl_float_maths.rs b/src/numeric/impl_float_maths.rs index 55cc57d3f..4b3208800 100644 --- a/src/numeric/impl_float_maths.rs +++ b/src/numeric/impl_float_maths.rs @@ -160,17 +160,9 @@ where /// /// # Panics /// - /// Panics if `min > max`, `min` is `NaN`, or `max` is `NaN`. + /// Panics if `!(min <= max)`. pub fn clamp(&self, min: A, max: A) -> Array { assert!(min <= max, "min must be less than or equal to max"); - self.mapv(|v| { - if v < min { - min.clone() - } else if v > max { - max.clone() - } else { - v - } - }) + self.mapv(|a| num_traits::clamp(a, min.clone(), max.clone())) } } From 174ca76f761cb52b1494f9712edd436d8c503880 Mon Sep 17 00:00:00 2001 From: joelchen Date: Fri, 22 Dec 2023 15:37:04 +0800 Subject: [PATCH 501/651] Update README-quick-start.md Fix typo --- README-quick-start.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README-quick-start.md b/README-quick-start.md index 0c127ab59..811299651 100644 --- a/README-quick-start.md +++ b/README-quick-start.md @@ -170,7 +170,7 @@ fn main() { println!("b shape {:?}", &b.shape()); let b = b.into_shape((4,1)).unwrap(); // reshape b to shape [4, 1] - println!("b shape {:?}", &b.shape()); + println!("b shape after reshape {:?}", &b.shape()); println!("{}", a.dot(&b)); // [1, 4] x [4, 1] -> [1, 1] println!("{}", a.t().dot(&b.t())); // [4, 1] x [1, 4] -> [4, 4] From 23d7cabcf7e37e814ce97f446e6a3d9ae71474db Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Fri, 22 Dec 2023 09:08:43 +0100 Subject: [PATCH 502/651] Fix MSRV build by pinning crossbeam crates. --- scripts/all-tests.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index 9d7f5dd39..81d240ba2 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -13,6 +13,10 @@ if [ "$CHANNEL" = "1.51.0" ]; then cargo update --package byteorder --precise 1.4.3 cargo update --package rayon --precise 1.5.3 cargo update --package rayon-core --precise 1.9.3 + cargo update --package crossbeam-channel --precise 0.5.8 + cargo update --package crossbeam-deque --precise 0.8.3 + cargo update --package crossbeam-epoch --precise 0.9.15 + cargo update --package crossbeam-utils --precise 0.8.16 cargo update --package rmp --precise 0.8.11 cargo update --package serde_json --precise 1.0.99 cargo update --package serde --precise 1.0.156 From 4374a9cee3c6de435945ecf092595a33dbfb90ff Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 7 Jan 2024 18:22:42 +0200 Subject: [PATCH 503/651] Use `clone_from()` in two places In `fill()` and in `assign()`. --- src/impl_methods.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index c1e28d900..0b6d0b2e1 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1431,7 +1431,7 @@ where /// /// The windows are all distinct views of size `window_size` /// that fit into the array's shape. - /// + /// /// The stride is ordered by the outermost axis.
/// Hence, a (x₀, x₁, ..., xₙ) stride will be applied to /// (A₀, A₁, ..., Aₙ) where Aₓ stands for `Axis(x)`. @@ -1441,7 +1441,7 @@ where /// /// The produced element is an `ArrayView` with exactly the dimension /// `window_size`. - /// + /// /// Note that passing a stride of only ones is similar to /// calling [`ArrayBase::windows()`]. /// @@ -2374,7 +2374,7 @@ where A: Clone, S2: Data, { - self.zip_mut_with(rhs, |x, y| *x = y.clone()); + self.zip_mut_with(rhs, |x, y| x.clone_from(y)); } /// Perform an elementwise assigment of values cloned from `self` into array or producer `to`. @@ -2400,7 +2400,7 @@ where S: DataMut, A: Clone, { - self.map_inplace(move |elt| *elt = x.clone()); + self.map_inplace(move |elt| elt.clone_from(&x)); } pub(crate) fn zip_mut_with_same_shape(&mut self, rhs: &ArrayBase, mut f: F) From 05e854cfb1274c442b371eb028a3fc1c1627613a Mon Sep 17 00:00:00 2001 From: Johann Carl Meyer Date: Mon, 15 Jan 2024 22:13:04 +0100 Subject: [PATCH 504/651] fix and improve doc-comments --- src/linspace.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/linspace.rs b/src/linspace.rs index 6e9b1203c..513044e00 100644 --- a/src/linspace.rs +++ b/src/linspace.rs @@ -66,8 +66,8 @@ impl ExactSizeIterator for Linspace where Linspace: Iterator {} /// /// The `Linspace` has `n` elements from `a` to `b` (inclusive). /// -/// The iterator element type is `F`, where `F` must implement `Float`, e.g. -/// `f32` or `f64`. +/// 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. #[inline] @@ -89,13 +89,13 @@ where } } -/// Return an iterator of floats from `start` to `end` (exclusive), +/// Return an iterator of floats from `a` to `b` (exclusive), /// incrementing by `step`. /// /// Numerical reasons can result in `b` being included in the result. /// -/// The iterator element type is `F`, where `F` must implement `Float`, e.g. -/// `f32` or `f64`. +/// The iterator element type is `F`, where `F` must implement [`Float`], e.g. +/// [`f32`] or [`f64`]. /// /// **Panics** if converting `((b - a) / step).ceil()` to type `F` fails. #[inline] From 5b931a9972d876e9cd22be2a15052af8983d1e7b Mon Sep 17 00:00:00 2001 From: Johann Carl Meyer Date: Mon, 15 Jan 2024 22:22:34 +0100 Subject: [PATCH 505/651] improve doc-comments --- src/logspace.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/logspace.rs b/src/logspace.rs index 4dc6e1f32..53be034b5 100644 --- a/src/logspace.rs +++ b/src/logspace.rs @@ -68,12 +68,12 @@ impl ExactSizeIterator for Logspace where Logspace: Iterator {} /// An iterator of a sequence of logarithmically spaced numbers. /// -/// The `Logspace` has `n` elements, where the first element is `base.powf(a)` +/// The [`Logspace`] has `n` elements, where the first element is `base.powf(a)` /// and the last element is `base.powf(b)`. If `base` is negative, this /// iterator will return all negative values. /// -/// The iterator element type is `F`, where `F` must implement `Float`, e.g. -/// `f32` or `f64`. +/// 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. #[inline] From 81fae813c58a3226c765d2e118d64fd73b7eb469 Mon Sep 17 00:00:00 2001 From: Johann Carl Meyer Date: Mon, 15 Jan 2024 22:23:32 +0100 Subject: [PATCH 506/651] export Linspace and Logspace iterators --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 41a23ae3f..afeed1f20 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -192,7 +192,9 @@ mod iterators; mod layout; mod linalg_traits; mod linspace; +pub use crate::linspace::{Linspace, linspace, range}; mod logspace; +pub use crate::logspace::{Logspace, logspace}; mod math_cell; mod numeric_util; mod order; From 8d91bc263bce94c9da8d1f205f423f83cb59e7c8 Mon Sep 17 00:00:00 2001 From: Johann Carl Meyer Date: Wed, 17 Jan 2024 23:33:02 +0100 Subject: [PATCH 507/651] fix no-std build --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index afeed1f20..fc735c4b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -192,8 +192,10 @@ mod iterators; mod layout; mod linalg_traits; mod linspace; +#[cfg(feature = "std")] pub use crate::linspace::{Linspace, linspace, range}; mod logspace; +#[cfg(feature = "std")] pub use crate::logspace::{Logspace, logspace}; mod math_cell; mod numeric_util; From b53f41846ab7167cc0b6cf94b7c8b9a8115b643c Mon Sep 17 00:00:00 2001 From: Ho Kim Date: Sun, 28 Jan 2024 16:30:37 +0000 Subject: [PATCH 508/651] fix(docs): minimum rust version mismatch in lib.rs --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index fc735c4b5..e80f2651a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,7 +71,7 @@ //! needs matching memory layout to be efficient (with some exceptions). //! + Efficient floating point matrix multiplication even for very large //! matrices; can optionally use BLAS to improve it further. -//! - **Requires Rust 1.49 or later** +//! - **Requires Rust 1.51 or later** //! //! ## Crate Feature Flags //! From 5981cceebb4f2b1cb5acd784e8e329c8f7942eae Mon Sep 17 00:00:00 2001 From: Yuta Hinokuma Date: Sat, 17 Feb 2024 03:08:47 +0900 Subject: [PATCH 509/651] ndarray_from_numpy example to code not pointed out to clippy --- src/doc/ndarray_for_numpy_users/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index 7eaaf7d01..5ac15e300 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -605,7 +605,7 @@ //! //!
-//!
//! -//! `a.mapv(|x| f32::from(x))` +//! `a.mapv(f32::from)` //! //! //! @@ -619,7 +619,7 @@ //! //! //! -//! `a.mapv(|x| i32::from(x))` +//! `a.mapv(i32::from)` //! //! //! From 30bdfeeb15094dea1be609e5e506392668b736c0 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 26 Feb 2024 20:30:53 +0100 Subject: [PATCH 510/651] Fix nightly lints Co-authored-by: Adam Reichold --- src/array_serde.rs | 1 + src/arrayformat.rs | 3 ++- src/arraytraits.rs | 5 ++++- src/data_repr.rs | 2 ++ src/data_traits.rs | 1 + src/dimension/axes.rs | 7 ------- src/dimension/conversion.rs | 1 + src/dimension/dimension_trait.rs | 1 + src/dimension/dynindeximpl.rs | 2 ++ src/extension/nonnull.rs | 1 + src/free_functions.rs | 1 + src/impl_1d.rs | 1 + src/impl_constructors.rs | 1 + src/impl_methods.rs | 1 + src/impl_ops.rs | 1 - src/impl_owned_array.rs | 1 + src/iterators/mod.rs | 1 + src/linalg/impl_linalg.rs | 1 + src/numeric/impl_numeric.rs | 2 +- src/slice.rs | 1 + src/split_at.rs | 1 + src/stacking.rs | 1 + tests/iterators.rs | 2 +- 23 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/array_serde.rs b/src/array_serde.rs index f1c9e1219..a6f3c617c 100644 --- a/src/array_serde.rs +++ b/src/array_serde.rs @@ -12,6 +12,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use std::marker::PhantomData; use alloc::format; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use crate::imp_prelude::*; diff --git a/src/arrayformat.rs b/src/arrayformat.rs index ae781df6a..ec5b041d9 100644 --- a/src/arrayformat.rs +++ b/src/arrayformat.rs @@ -285,8 +285,9 @@ where #[cfg(test)] mod formatting_with_omit { use itertools::Itertools; - use std::fmt; + #[cfg(not(feature = "std"))] use alloc::string::String; + #[cfg(not(feature = "std"))] use alloc::vec::Vec; use super::*; diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 9aa45bde3..f45c8a1bd 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -6,9 +6,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(not(feature = "std"))] use alloc::boxed::Box; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; -use std::iter::IntoIterator; use std::mem; use std::ops::{Index, IndexMut}; use std::{hash, mem::size_of}; @@ -115,6 +116,7 @@ where /// Return `true` if the array shapes and all elements of `self` and /// `rhs` are equal. Return `false` otherwise. +#[allow(clippy::unconditional_recursion)] // false positive impl<'a, A, B, S, S2, D> PartialEq<&'a ArrayBase> for ArrayBase where A: PartialEq, @@ -129,6 +131,7 @@ where /// Return `true` if the array shapes and all elements of `self` and /// `rhs` are equal. Return `false` otherwise. +#[allow(clippy::unconditional_recursion)] // false positive impl<'a, A, B, S, S2, D> PartialEq> for &'a ArrayBase where A: PartialEq, diff --git a/src/data_repr.rs b/src/data_repr.rs index 6630f9ddf..f740988f4 100644 --- a/src/data_repr.rs +++ b/src/data_repr.rs @@ -2,7 +2,9 @@ use std::mem; use std::mem::ManuallyDrop; use std::ptr::NonNull; use alloc::slice; +#[cfg(not(feature = "std"))] use alloc::borrow::ToOwned; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use crate::extension::nonnull; diff --git a/src/data_traits.rs b/src/data_traits.rs index acf4b0b7a..50e5a3e6e 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -14,6 +14,7 @@ use std::mem::{self, size_of}; use std::mem::MaybeUninit; use std::ptr::NonNull; use alloc::sync::Arc; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use crate::{ diff --git a/src/dimension/axes.rs b/src/dimension/axes.rs index fb1ff0995..5660675b5 100644 --- a/src/dimension/axes.rs +++ b/src/dimension/axes.rs @@ -143,7 +143,6 @@ where trait IncOps: Copy { fn post_inc(&mut self) -> Self; - fn post_dec(&mut self) -> Self; fn pre_dec(&mut self) -> Self; } @@ -155,12 +154,6 @@ impl IncOps for usize { x } #[inline(always)] - fn post_dec(&mut self) -> Self { - let x = *self; - *self -= 1; - x - } - #[inline(always)] fn pre_dec(&mut self) -> Self { *self -= 1; *self diff --git a/src/dimension/conversion.rs b/src/dimension/conversion.rs index 6b53a4eef..f6c408a75 100644 --- a/src/dimension/conversion.rs +++ b/src/dimension/conversion.rs @@ -10,6 +10,7 @@ use num_traits::Zero; use std::ops::{Index, IndexMut}; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use crate::{Dim, Dimension, Ix, Ix1, IxDyn, IxDynImpl}; diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 7a3955fb7..14ec2cd31 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -9,6 +9,7 @@ use std::fmt::Debug; use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; use std::ops::{Index, IndexMut}; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use super::axes_of; diff --git a/src/dimension/dynindeximpl.rs b/src/dimension/dynindeximpl.rs index e81bf4f3d..c2aea032e 100644 --- a/src/dimension/dynindeximpl.rs +++ b/src/dimension/dynindeximpl.rs @@ -2,7 +2,9 @@ use crate::imp_prelude::*; use std::hash::{Hash, Hasher}; use std::ops::{Deref, DerefMut, Index, IndexMut}; use alloc::vec; +#[cfg(not(feature = "std"))] use alloc::boxed::Box; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; const CAP: usize = 4; diff --git a/src/extension/nonnull.rs b/src/extension/nonnull.rs index 5aa50fdc2..4deee11ac 100644 --- a/src/extension/nonnull.rs +++ b/src/extension/nonnull.rs @@ -1,4 +1,5 @@ use std::ptr::NonNull; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; /// Return a NonNull pointer to the vector's data diff --git a/src/free_functions.rs b/src/free_functions.rs index 156eee6b9..e9c3abff6 100644 --- a/src/free_functions.rs +++ b/src/free_functions.rs @@ -7,6 +7,7 @@ // except according to those terms. use alloc::vec; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::mem::{forget, size_of}; diff --git a/src/impl_1d.rs b/src/impl_1d.rs index 17f7729d6..a9fe84407 100644 --- a/src/impl_1d.rs +++ b/src/impl_1d.rs @@ -7,6 +7,7 @@ // except according to those terms. //! Methods for one-dimensional arrays. +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::mem::MaybeUninit; diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 049384ceb..2779e27d0 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -17,6 +17,7 @@ use num_traits::{One, Zero}; use std::mem; use std::mem::MaybeUninit; use alloc::vec; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use crate::dimension; diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 0b6d0b2e1..19249f9e7 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -9,6 +9,7 @@ use std::mem::{size_of, ManuallyDrop}; use alloc::slice; use alloc::vec; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use rawpointer::PointerExt; diff --git a/src/impl_ops.rs b/src/impl_ops.rs index a267198ba..9eacd6b69 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -288,7 +288,6 @@ mod arithmetic_ops { use super::*; use crate::imp_prelude::*; - use num_complex::Complex; use std::ops::*; fn clone_opf(f: impl Fn(A, B) -> C) -> impl FnMut(&A, &B) -> C { diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 8cfb82b55..aaa9f30b5 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -1,4 +1,5 @@ +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::mem; use std::mem::MaybeUninit; diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 063b22053..6bac471bd 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -17,6 +17,7 @@ mod windows; use std::iter::FromIterator; use std::marker::PhantomData; use std::ptr; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use crate::Ix1; diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index 52a15f44e..6a4542edd 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -16,6 +16,7 @@ use crate::{LinalgScalar, Zip}; use std::any::TypeId; use std::mem::MaybeUninit; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use num_complex::Complex; diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index afcd7e896..0a720cb3e 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -8,7 +8,7 @@ #[cfg(feature = "std")] use num_traits::Float; -use num_traits::{self, FromPrimitive, Zero}; +use num_traits::{FromPrimitive, Zero}; use std::ops::{Add, Div, Mul}; use crate::imp_prelude::*; diff --git a/src/slice.rs b/src/slice.rs index 0146d6dba..182d3723c 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -8,6 +8,7 @@ use crate::dimension::slices_intersect; use crate::error::{ErrorKind, ShapeError}; use crate::{ArrayViewMut, DimAdd, Dimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn}; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::convert::TryFrom; use std::fmt; diff --git a/src/split_at.rs b/src/split_at.rs index b05e58346..50466afdf 100644 --- a/src/split_at.rs +++ b/src/split_at.rs @@ -7,6 +7,7 @@ pub(crate) trait SplitAt { } pub(crate) trait SplitPreference : SplitAt { + #[allow(dead_code)] // used only when Rayon support is enabled fn can_split(&self) -> bool; fn split_preference(&self) -> (Axis, usize); fn split(self) -> (Self, Self) where Self: Sized { diff --git a/src/stacking.rs b/src/stacking.rs index 5a47589d5..16058f39d 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use crate::dimension; diff --git a/tests/iterators.rs b/tests/iterators.rs index d7f7c5823..addd59adc 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -6,7 +6,7 @@ )] use ndarray::prelude::*; -use ndarray::{arr3, aview1, indices, s, Axis, Slice, Zip}; +use ndarray::{arr3, indices, s, Slice, Zip}; use itertools::assert_equal; use itertools::enumerate; From 97ecc0db70d53a1119c7b03149fcd7207d4580bd Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 26 Feb 2024 20:20:03 +0100 Subject: [PATCH 511/651] dimension: Fix contig check for single element arrays When an array has 0 or 1 elements, strides don't matter anymore. The general case of this function handled this correctly, but the special case for ndim == 1 did not. --- src/dimension/dimension_trait.rs | 30 +++++++++++++++++------------- tests/array.rs | 16 ++++++++++++++++ 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 14ec2cd31..c594859e9 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -285,21 +285,25 @@ pub trait Dimension: return true; } if dim.ndim() == 1 { - return strides[0] as isize == -1; - } - let order = strides._fastest_varying_stride_order(); - let strides = strides.slice(); - - let dim_slice = dim.slice(); - let mut cstride = 1; - for &i in order.slice() { - // a dimension of length 1 can have unequal strides - if dim_slice[i] != 1 && (strides[i] as isize).unsigned_abs() != cstride { - return false; + // fast case for ndim == 1: + // Either we have length <= 1, then stride is arbitrary, + // or we have stride == 1 or stride == -1, but +1 case is already handled above. + dim[0] <= 1 || strides[0] as isize == -1 + } else { + let order = strides._fastest_varying_stride_order(); + let strides = strides.slice(); + + let dim_slice = dim.slice(); + let mut cstride = 1; + for &i in order.slice() { + // a dimension of length 1 can have unequal strides + if dim_slice[i] != 1 && (strides[i] as isize).unsigned_abs() != cstride { + return false; + } + cstride *= dim_slice[i]; } - cstride *= dim_slice[i]; + true } - true } /// Return the axis ordering corresponding to the fastest variation diff --git a/tests/array.rs b/tests/array.rs index 8fdbb9992..d770d7822 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1957,6 +1957,22 @@ fn test_contiguous() { assert!(b.as_slice_memory_order().is_some()); } +#[test] +fn test_contiguous_single_element() +{ + assert_matches!(array![1].as_slice_memory_order(), Some(&[1])); + + let arr1 = array![1, 2, 3]; + assert_matches!(arr1.slice(s![0..1]).as_slice_memory_order(), Some(&[1])); + assert_matches!(arr1.slice(s![1..2]).as_slice_memory_order(), Some(&[2])); + assert_matches!(arr1.slice(s![2..3]).as_slice_memory_order(), Some(&[3])); + assert_matches!(arr1.slice(s![0..0]).as_slice_memory_order(), Some(&[])); + + let arr2 = array![[1, 2, 3], [4, 5, 6]]; + assert_matches!(arr2.slice(s![.., 2..3]).as_slice_memory_order(), None); + assert_matches!(arr2.slice(s![1, 2..3]).as_slice_memory_order(), Some(&[6])); +} + #[test] fn test_contiguous_neg_strides() { let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; From 9a16e3e67228b51cc4e073675bce946dd0b55ce2 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 9 Mar 2024 14:28:22 +0100 Subject: [PATCH 512/651] ci: Only run cross tests on master --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5fd4d9b30..565ea5143 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,7 @@ jobs: cross_test: runs-on: ubuntu-latest + if: github.ref == 'refs/heads/master' strategy: matrix: include: From 436b4c126dfc93b1f003f92e74c7db63b51cf0ea Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 9 Mar 2024 14:28:22 +0100 Subject: [PATCH 513/651] ci: Use checkout@v4 --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 565ea5143..67d9dc50b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: - 1.51.0 # MSRV steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} @@ -45,7 +45,7 @@ jobs: target: i686-unknown-linux-gnu steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} @@ -62,7 +62,7 @@ jobs: rust: - beta steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} From 8e558727f682c03de2bf0833dd0786cd906eb9df Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 9 Mar 2024 15:18:30 +0100 Subject: [PATCH 514/651] ci: Use merge queue --- .github/workflows/{ci.yml => merge-check.yml} | 23 ++--------------- .github/workflows/pr-check.yml | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+), 21 deletions(-) rename .github/workflows/{ci.yml => merge-check.yml} (71%) create mode 100644 .github/workflows/pr-check.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/merge-check.yml similarity index 71% rename from .github/workflows/ci.yml rename to .github/workflows/merge-check.yml index 67d9dc50b..87c6de597 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/merge-check.yml @@ -1,10 +1,7 @@ on: - push: - branches: [ master ] - pull_request: - branches: [ master ] + merge_group: -name: Continuous integration +name: Merge Queue Check env: CARGO_TERM_COLOR: always @@ -35,7 +32,6 @@ jobs: cross_test: runs-on: ubuntu-latest - if: github.ref == 'refs/heads/master' strategy: matrix: include: @@ -54,18 +50,3 @@ jobs: - name: Install cross run: cargo install cross - run: ./scripts/cross-tests.sh "docs" ${{ matrix.rust }} ${{ matrix.target }} - - clippy: - runs-on: ubuntu-latest - strategy: - matrix: - rust: - - beta - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.rust }} - components: clippy - - uses: Swatinem/rust-cache@v2 - - run: cargo clippy --features docs diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml new file mode 100644 index 000000000..78760d306 --- /dev/null +++ b/.github/workflows/pr-check.yml @@ -0,0 +1,25 @@ +on: + pull_request: + merge_group: + +name: PR Check + +env: + CARGO_TERM_COLOR: always + RUSTFLAGS: "-D warnings" + +jobs: + clippy: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - beta + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + components: clippy + - uses: Swatinem/rust-cache@v2 + - run: cargo clippy --features docs From fe3eb5ab91b0056d4dada657e00182018f45dbbb Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 9 Mar 2024 16:13:11 +0100 Subject: [PATCH 515/651] ci: Setup so that most checks run in merge queue only --- .github/workflows/merge-check.yml | 52 ------------------------------- .github/workflows/pr-check.yml | 46 ++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 53 deletions(-) delete mode 100644 .github/workflows/merge-check.yml diff --git a/.github/workflows/merge-check.yml b/.github/workflows/merge-check.yml deleted file mode 100644 index 87c6de597..000000000 --- a/.github/workflows/merge-check.yml +++ /dev/null @@ -1,52 +0,0 @@ -on: - merge_group: - -name: Merge Queue Check - -env: - CARGO_TERM_COLOR: always - HOST: x86_64-unknown-linux-gnu - FEATURES: "test docs" - RUSTFLAGS: "-D warnings" - -jobs: - tests: - runs-on: ubuntu-latest - strategy: - matrix: - rust: - - stable - - beta - - nightly - - 1.51.0 # MSRV - - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.rust }} - - uses: Swatinem/rust-cache@v2 - - name: Install openblas - run: sudo apt-get install libopenblas-dev gfortran - - run: ./scripts/all-tests.sh "$FEATURES" ${{ matrix.rust }} - - cross_test: - runs-on: ubuntu-latest - strategy: - matrix: - include: - - rust: stable - target: powerpc-unknown-linux-gnu - - rust: stable - target: i686-unknown-linux-gnu - - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.rust }} - targets: ${{ matrix.target }} - - uses: Swatinem/rust-cache@v2 - - name: Install cross - run: cargo install cross - - run: ./scripts/cross-tests.sh "docs" ${{ matrix.rust }} ${{ matrix.target }} diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 78760d306..76abf49fd 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -2,10 +2,12 @@ on: pull_request: merge_group: -name: PR Check +name: CI Check env: CARGO_TERM_COLOR: always + HOST: x86_64-unknown-linux-gnu + FEATURES: "test docs" RUSTFLAGS: "-D warnings" jobs: @@ -23,3 +25,45 @@ jobs: components: clippy - uses: Swatinem/rust-cache@v2 - run: cargo clippy --features docs + tests: + if: ${{ github.event_name == 'merge_group' }} + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - stable + - beta + - nightly + - 1.51.0 # MSRV + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + - uses: Swatinem/rust-cache@v2 + - name: Install openblas + run: sudo apt-get install libopenblas-dev gfortran + - run: ./scripts/all-tests.sh "$FEATURES" ${{ matrix.rust }} + + cross_test: + if: ${{ github.event_name == 'merge_group' }} + runs-on: ubuntu-latest + strategy: + matrix: + include: + - rust: stable + target: powerpc-unknown-linux-gnu + - rust: stable + target: i686-unknown-linux-gnu + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + targets: ${{ matrix.target }} + - uses: Swatinem/rust-cache@v2 + - name: Install cross + run: cargo install cross + - run: ./scripts/cross-tests.sh "docs" ${{ matrix.rust }} ${{ matrix.target }} From 909ec76b683d1cec9850df3f4f1a66ba4284a0bb Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 9 Mar 2024 17:19:39 +0100 Subject: [PATCH 516/651] ci: Adopt conclusion job that summarizes ci status Originally contributed by @messense in pyo3, copied their solution with thanks. --- .github/workflows/{pr-check.yml => ci.yaml} | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) rename .github/workflows/{pr-check.yml => ci.yaml} (75%) diff --git a/.github/workflows/pr-check.yml b/.github/workflows/ci.yaml similarity index 75% rename from .github/workflows/pr-check.yml rename to .github/workflows/ci.yaml index 76abf49fd..c49a85a38 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/ci.yaml @@ -2,7 +2,7 @@ on: pull_request: merge_group: -name: CI Check +name: Continuous integration env: CARGO_TERM_COLOR: always @@ -36,6 +36,7 @@ jobs: - nightly - 1.51.0 # MSRV + name: tests/${{ matrix.rust }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master @@ -57,6 +58,7 @@ jobs: - rust: stable target: i686-unknown-linux-gnu + name: cross_test/${{ matrix.target }}/${{ matrix.rust }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master @@ -67,3 +69,19 @@ jobs: - name: Install cross run: cargo install cross - run: ./scripts/cross-tests.sh "docs" ${{ matrix.rust }} ${{ matrix.target }} + + conclusion: + needs: + - clippy + - tests + - cross_test + if: always() + runs-on: ubuntu-latest + steps: + - name: Result + run: | + jq -C <<< "${needs}" + # Check if all needs were successful or skipped. + "$(jq -r 'all(.result as $result | (["success", "skipped"] | contains([$result])))' <<< "${needs}")" + env: + needs: ${{ toJson(needs) }} From 899db5cfebe87c4486c41d9886780e6c31ffa26a Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 9 Mar 2024 18:39:38 +0100 Subject: [PATCH 517/651] iterators: Re-export IntoIter --- src/iterators/iter.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/iterators/iter.rs b/src/iterators/iter.rs index 7b6d80cd5..3f8b05009 100644 --- a/src/iterators/iter.rs +++ b/src/iterators/iter.rs @@ -9,7 +9,22 @@ pub use crate::dimension::Axes; pub use crate::indexes::{Indices, IndicesIter}; pub use crate::iterators::{ - AxisChunksIter, AxisChunksIterMut, AxisIter, AxisIterMut, ExactChunks, ExactChunksIter, - ExactChunksIterMut, ExactChunksMut, IndexedIter, IndexedIterMut, Iter, IterMut, Lanes, - LanesIter, LanesIterMut, LanesMut, Windows, + AxisChunksIter, + AxisChunksIterMut, + AxisIter, + AxisIterMut, + ExactChunks, + ExactChunksIter, + ExactChunksIterMut, + ExactChunksMut, + IndexedIter, + IndexedIterMut, + Iter, + IterMut, + IntoIter, + Lanes, + LanesIter, + LanesIterMut, + LanesMut, + Windows, }; From 08bb5a9a80410d8ec6403f2ae1a63bcab7b3602b Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 9 Mar 2024 18:35:47 +0100 Subject: [PATCH 518/651] ci: Cross tests - run numeric as release, use s390x Somehow, these take 30m on powerpc, which is too slow (much slower than anything else.) Try to use s390x instead and run numeric tests in release mode. --- .github/workflows/ci.yaml | 2 +- scripts/cross-tests.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c49a85a38..092700611 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -54,7 +54,7 @@ jobs: matrix: include: - rust: stable - target: powerpc-unknown-linux-gnu + target: s390x-unknown-linux-gnu - rust: stable target: i686-unknown-linux-gnu diff --git a/scripts/cross-tests.sh b/scripts/cross-tests.sh index 7473a72fa..dc27058f8 100755 --- a/scripts/cross-tests.sh +++ b/scripts/cross-tests.sh @@ -11,4 +11,4 @@ cross build -v --features="$FEATURES" --target=$TARGET cross test -v --no-fail-fast --features="$FEATURES" --target=$TARGET cross test -v --no-fail-fast --target=$TARGET --manifest-path=ndarray-rand/Cargo.toml --features quickcheck cross test -v --no-fail-fast --target=$TARGET --manifest-path=xtest-serialization/Cargo.toml --verbose -cross test -v --no-fail-fast --target=$TARGET --manifest-path=xtest-numeric/Cargo.toml +cross test -v --no-fail-fast --target=$TARGET --manifest-path=xtest-numeric/Cargo.toml --release From 8e41436547afa3b73b128ba9bc08e1aaaf0b14b7 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 9 Mar 2024 20:21:02 +0100 Subject: [PATCH 519/651] ci: Run regular tests in pr check We want feedback for contributors from the tests, directly on the PR. --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 092700611..df47549c7 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -17,6 +17,7 @@ jobs: matrix: rust: - beta + name: clippy/${{ matrix.rust }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master @@ -26,7 +27,6 @@ jobs: - uses: Swatinem/rust-cache@v2 - run: cargo clippy --features docs tests: - if: ${{ github.event_name == 'merge_group' }} runs-on: ubuntu-latest strategy: matrix: From fa195d8166a3c2836409256d754531ae1c7faa1a Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 13 Aug 2022 11:23:51 -0400 Subject: [PATCH 520/651] Fix unsafe blocks in s![] macro Before, the user could silently violate safety requirements of `SliceInfo::new_unchecked` by directly calling `s![@parse inconsistent_values]`, where `inconsistent_values` represents inconsistent values for the dimensions, etc. Now, the macro calls `SliceInfo::new_unchecked` only in the `($($t:tt)*)` arm, which always constructs the correct values for the call. --- src/slice.rs | 61 +++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/src/slice.rs b/src/slice.rs index 182d3723c..14ab0dd67 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -780,14 +780,11 @@ macro_rules! s( r => { let in_dim = $crate::SliceNextDim::next_in_dim(&r, $in_dim); let out_dim = $crate::SliceNextDim::next_out_dim(&r, $out_dim); - #[allow(unsafe_code)] - unsafe { - $crate::SliceInfo::new_unchecked( - [$($stack)* $crate::s!(@convert r, $s)], - in_dim, - out_dim, - ) - } + ( + [$($stack)* $crate::s!(@convert r, $s)], + in_dim, + out_dim, + ) } } }; @@ -797,14 +794,11 @@ macro_rules! s( r => { let in_dim = $crate::SliceNextDim::next_in_dim(&r, $in_dim); let out_dim = $crate::SliceNextDim::next_out_dim(&r, $out_dim); - #[allow(unsafe_code)] - unsafe { - $crate::SliceInfo::new_unchecked( - [$($stack)* $crate::s!(@convert r)], - in_dim, - out_dim, - ) - } + ( + [$($stack)* $crate::s!(@convert r)], + in_dim, + out_dim, + ) } } }; @@ -844,16 +838,11 @@ macro_rules! s( }; // empty call, i.e. `s![]` (@parse ::core::marker::PhantomData::<$crate::Ix0>, ::core::marker::PhantomData::<$crate::Ix0>, []) => { - { - #[allow(unsafe_code)] - unsafe { - $crate::SliceInfo::new_unchecked( - [], - ::core::marker::PhantomData::<$crate::Ix0>, - ::core::marker::PhantomData::<$crate::Ix0>, - ) - } - } + ( + [], + ::core::marker::PhantomData::<$crate::Ix0>, + ::core::marker::PhantomData::<$crate::Ix0>, + ) }; // Catch-all clause for syntax errors (@parse $($t:tt)*) => { compile_error!("Invalid syntax in s![] call.") }; @@ -868,12 +857,20 @@ macro_rules! s( ) }; ($($t:tt)*) => { - $crate::s![@parse - ::core::marker::PhantomData::<$crate::Ix0>, - ::core::marker::PhantomData::<$crate::Ix0>, - [] - $($t)* - ] + { + let (indices, in_dim, out_dim) = $crate::s![@parse + ::core::marker::PhantomData::<$crate::Ix0>, + ::core::marker::PhantomData::<$crate::Ix0>, + [] + $($t)* + ]; + // Safety: The `s![@parse ...]` above always constructs the correct + // values to meet the constraints of `SliceInfo::new_unchecked`. + #[allow(unsafe_code)] + unsafe { + $crate::SliceInfo::new_unchecked(indices, in_dim, out_dim) + } + } }; ); From 31009f34dbe5925abfd2b9b06ccd5fc07a4354db Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 9 Mar 2024 21:12:07 +0100 Subject: [PATCH 521/651] ci: Test using cargo-careful --- .github/workflows/ci.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index df47549c7..7e9834087 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -70,11 +70,27 @@ jobs: run: cargo install cross - run: ./scripts/cross-tests.sh "docs" ${{ matrix.rust }} ${{ matrix.target }} + cargo-careful: + if: ${{ github.event_name == 'merge_group' }} + runs-on: ubuntu-latest + name: cargo-careful + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly + - uses: Swatinem/rust-cache@v2 + - name: Install cargo-careful + run: cargo install cargo-careful + - run: cargo careful test -Zcareful-sanitizer --features="$FEATURES" + - run: cargo careful test -Zcareful-sanitizer -p ndarray-rand + conclusion: needs: - clippy - tests - cross_test + - cargo-careful if: always() runs-on: ubuntu-latest steps: From a70bf19769224a2db435773a44005782d4eaec6b Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 19 Dec 2021 19:22:14 -0500 Subject: [PATCH 522/651] Fix Miri failure with -Zmiri-tag-raw-pointers Before this commit, running `MIRIFLAGS="-Zmiri-tag-raw-pointers" cargo miri test test_map_axis` caused Miri to report undefined behavior in the `test_map_axis` test. This commit fixes the underlying issue. Basically, Miri doesn't like us using a reference to an element to access other elements. Here's some sample code to illustrate the issue: ```rust let a: Vec = vec![5, 6]; let first_elt: &i32 = &a[0]; let ptr: *const i32 = first_elt as *const i32; // Okay: the data is contained in the data referenced by `first_elt`. let a0 = unsafe { &*ptr.offset(0) }; assert_eq!(*a0, 5); // Not okay: the data is not contained in the data referenced by `first_elt`. let a1 = unsafe { &*ptr.offset(1) }; assert_eq!(*a1, 6); ``` Before this commit, we were using `self.index_axis(axis, 0).map(|first_elt| ...)` to create views of the lanes, from references to the first elements in the lanes. Accessing elements within those views (other than the first element) led to the Miri error, since the view's pointer was derived from a reference to a single element. Thanks to @5225225 for reporting the issue. (This commit fixes #1137.) --- src/impl_methods.rs | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 19249f9e7..9186cd762 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2752,17 +2752,11 @@ where A: 'a, S: Data, { - let view_len = self.len_of(axis); - let view_stride = self.strides.axis(axis); - if view_len == 0 { + if self.len_of(axis) == 0 { let new_dim = self.dim.remove_axis(axis); Array::from_shape_simple_fn(new_dim, move || mapping(ArrayView::from(&[]))) } else { - // use the 0th subview as a map to each 1d array view extended from - // the 0th element. - self.index_axis(axis, 0).map(|first_elt| unsafe { - mapping(ArrayView::new_(first_elt, Ix1(view_len), Ix1(view_stride))) - }) + Zip::from(self.lanes(axis)).map_collect(mapping) } } @@ -2783,21 +2777,11 @@ where A: 'a, S: DataMut, { - let view_len = self.len_of(axis); - let view_stride = self.strides.axis(axis); - if view_len == 0 { + if self.len_of(axis) == 0 { let new_dim = self.dim.remove_axis(axis); Array::from_shape_simple_fn(new_dim, move || mapping(ArrayViewMut::from(&mut []))) } else { - // use the 0th subview as a map to each 1d array view extended from - // the 0th element. - self.index_axis_mut(axis, 0).map_mut(|first_elt| unsafe { - mapping(ArrayViewMut::new_( - first_elt, - Ix1(view_len), - Ix1(view_stride), - )) - }) + Zip::from(self.lanes_mut(axis)).map_collect(mapping) } } From 6df86a3a778a6c849ee0b72576e259d3bb2ea135 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Thu, 23 Dec 2021 19:11:48 -0500 Subject: [PATCH 523/651] Fix Miri errors for WindowsIter and ExactChunksIter/Mut Before this commit, running `MIRIFLAGS="-Zmiri-tag-raw-pointers" cargo miri test` caused Miri to report undefined behavior for code using the `WindowsIter`, `ExactChunksIter`, and `ExactChunksIterMut` iterators. This commit fixes the underlying issue. Basically, Miri doesn't like code which uses a reference to an element to access other elements. Before this commit, these iterators wrapped the `ElementsBase` and `ElementsBaseMut` iterators, and they created views from the references returned by those inner iterators. Accessing elements within those views (other than the first element) led to the Miri error, since the view's pointer was derived from a reference to a single element. Now, the iterators wrap `Baseiter` instead, which produces raw pointers instead of references. Although not necessary to satisfy Miri, this commit also changes the `Windows`, `ExactChunks`, and `ExactChunksMut` producers to wrap raw views instead of normal views. This avoids potential confusion regarding which elements are accessible through the views produced by these producers. --- src/impl_views/conversions.rs | 21 +++++++++++++ src/iterators/chunks.rs | 55 +++++++++++++++++++++++------------ src/iterators/macros.rs | 2 +- src/iterators/windows.rs | 25 +++++++++++----- src/lib.rs | 4 --- 5 files changed, 76 insertions(+), 31 deletions(-) diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs index 1e9a1499f..ca571a761 100644 --- a/src/impl_views/conversions.rs +++ b/src/impl_views/conversions.rs @@ -183,6 +183,27 @@ where } } +/// Private raw array view methods +impl RawArrayView +where + D: Dimension, +{ + #[inline] + pub(crate) fn into_base_iter(self) -> Baseiter { + unsafe { Baseiter::new(self.ptr.as_ptr(), self.dim, self.strides) } + } +} + +impl RawArrayViewMut +where + D: Dimension, +{ + #[inline] + pub(crate) fn into_base_iter(self) -> Baseiter { + unsafe { Baseiter::new(self.ptr.as_ptr(), self.dim, self.strides) } + } +} + /// Private array view methods impl<'a, A, D> ArrayView<'a, A, D> where diff --git a/src/iterators/chunks.rs b/src/iterators/chunks.rs index ba0e789fb..9cf06b55f 100644 --- a/src/iterators/chunks.rs +++ b/src/iterators/chunks.rs @@ -1,6 +1,7 @@ +use std::marker::PhantomData; + use crate::imp_prelude::*; -use crate::ElementsBase; -use crate::ElementsBaseMut; +use crate::Baseiter; use crate::IntoDimension; use crate::{Layout, NdProducer}; @@ -9,6 +10,7 @@ impl_ndproducer! { [Clone => 'a, A, D: Clone ] ExactChunks { base, + life, chunk, inner_strides, } @@ -23,16 +25,14 @@ impl_ndproducer! { } } -type BaseProducerRef<'a, A, D> = ArrayView<'a, A, D>; -type BaseProducerMut<'a, A, D> = ArrayViewMut<'a, A, D>; - /// Exact chunks producer and iterable. /// /// See [`.exact_chunks()`](ArrayBase::exact_chunks) for more /// information. //#[derive(Debug)] pub struct ExactChunks<'a, A, D> { - base: BaseProducerRef<'a, A, D>, + base: RawArrayView, + life: PhantomData<&'a A>, chunk: D, inner_strides: D, } @@ -41,10 +41,11 @@ impl<'a, A, D: Dimension> ExactChunks<'a, A, D> { /// Creates a new exact chunks producer. /// /// **Panics** if any chunk dimension is zero - pub(crate) fn new(mut a: ArrayView<'a, A, D>, chunk: E) -> Self + pub(crate) fn new(a: ArrayView<'a, A, D>, chunk: E) -> Self where E: IntoDimension, { + let mut a = a.into_raw_view(); let chunk = chunk.into_dimension(); ndassert!( a.ndim() == chunk.ndim(), @@ -59,11 +60,12 @@ impl<'a, A, D: Dimension> ExactChunks<'a, A, D> { for i in 0..a.ndim() { a.dim[i] /= chunk[i]; } - let inner_strides = a.raw_strides(); + let inner_strides = a.strides.clone(); a.strides *= &chunk; ExactChunks { base: a, + life: PhantomData, chunk, inner_strides, } @@ -79,7 +81,8 @@ where type IntoIter = ExactChunksIter<'a, A, D>; fn into_iter(self) -> Self::IntoIter { ExactChunksIter { - iter: self.base.into_elements_base(), + iter: self.base.into_base_iter(), + life: self.life, chunk: self.chunk, inner_strides: self.inner_strides, } @@ -91,7 +94,8 @@ where /// See [`.exact_chunks()`](ArrayBase::exact_chunks) for more /// information. pub struct ExactChunksIter<'a, A, D> { - iter: ElementsBase<'a, A, D>, + iter: Baseiter, + life: PhantomData<&'a A>, chunk: D, inner_strides: D, } @@ -101,6 +105,7 @@ impl_ndproducer! { [Clone => ] ExactChunksMut { base, + life, chunk, inner_strides, } @@ -122,7 +127,8 @@ impl_ndproducer! { /// for more information. //#[derive(Debug)] pub struct ExactChunksMut<'a, A, D> { - base: BaseProducerMut<'a, A, D>, + base: RawArrayViewMut, + life: PhantomData<&'a mut A>, chunk: D, inner_strides: D, } @@ -131,10 +137,11 @@ impl<'a, A, D: Dimension> ExactChunksMut<'a, A, D> { /// Creates a new exact chunks producer. /// /// **Panics** if any chunk dimension is zero - pub(crate) fn new(mut a: ArrayViewMut<'a, A, D>, chunk: E) -> Self + pub(crate) fn new(a: ArrayViewMut<'a, A, D>, chunk: E) -> Self where E: IntoDimension, { + let mut a = a.into_raw_view_mut(); let chunk = chunk.into_dimension(); ndassert!( a.ndim() == chunk.ndim(), @@ -149,11 +156,12 @@ impl<'a, A, D: Dimension> ExactChunksMut<'a, A, D> { for i in 0..a.ndim() { a.dim[i] /= chunk[i]; } - let inner_strides = a.raw_strides(); + let inner_strides = a.strides.clone(); a.strides *= &chunk; ExactChunksMut { base: a, + life: PhantomData, chunk, inner_strides, } @@ -169,7 +177,8 @@ where type IntoIter = ExactChunksIterMut<'a, A, D>; fn into_iter(self) -> Self::IntoIter { ExactChunksIterMut { - iter: self.base.into_elements_base(), + iter: self.base.into_base_iter(), + life: self.life, chunk: self.chunk, inner_strides: self.inner_strides, } @@ -181,16 +190,17 @@ impl_iterator! { [Clone => 'a, A, D: Clone] ExactChunksIter { iter, + life, chunk, inner_strides, } ExactChunksIter<'a, A, D> { type Item = ArrayView<'a, A, D>; - fn item(&mut self, elt) { + fn item(&mut self, ptr) { unsafe { ArrayView::new_( - elt, + ptr, self.chunk.clone(), self.inner_strides.clone()) } @@ -209,10 +219,10 @@ impl_iterator! { ExactChunksIterMut<'a, A, D> { type Item = ArrayViewMut<'a, A, D>; - fn item(&mut self, elt) { + fn item(&mut self, ptr) { unsafe { ArrayViewMut::new_( - elt, + ptr, self.chunk.clone(), self.inner_strides.clone()) } @@ -225,7 +235,14 @@ impl_iterator! { /// See [`.exact_chunks_mut()`](ArrayBase::exact_chunks_mut) /// for more information. pub struct ExactChunksIterMut<'a, A, D> { - iter: ElementsBaseMut<'a, A, D>, + iter: Baseiter, + life: PhantomData<&'a mut A>, chunk: D, inner_strides: D, } + +send_sync_read_only!(ExactChunks); +send_sync_read_only!(ExactChunksIter); + +send_sync_read_write!(ExactChunksMut); +send_sync_read_write!(ExactChunksIterMut); diff --git a/src/iterators/macros.rs b/src/iterators/macros.rs index ff4b3bb93..7fbe410fe 100644 --- a/src/iterators/macros.rs +++ b/src/iterators/macros.rs @@ -84,7 +84,7 @@ impl<$($typarm)*> NdProducer for $fulltype { } unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { - self.$base.uget_ptr(i) + self.$base.uget_ptr(i) as *mut _ } fn stride_of(&self, axis: Axis) -> isize { diff --git a/src/iterators/windows.rs b/src/iterators/windows.rs index 6238d0ed1..9140f43b9 100644 --- a/src/iterators/windows.rs +++ b/src/iterators/windows.rs @@ -1,4 +1,6 @@ -use super::ElementsBase; +use std::marker::PhantomData; + +use super::Baseiter; use crate::imp_prelude::*; use crate::IntoDimension; use crate::Layout; @@ -10,7 +12,8 @@ use crate::Slice; /// See [`.windows()`](ArrayBase::windows) for more /// information. pub struct Windows<'a, A, D> { - base: ArrayView<'a, A, D>, + base: RawArrayView, + life: PhantomData<&'a A>, window: D, strides: D, } @@ -74,7 +77,8 @@ impl<'a, A, D: Dimension> Windows<'a, A, D> { }); Windows { - base, + base: base.into_raw_view(), + life: PhantomData, window, strides: window_strides, } @@ -86,6 +90,7 @@ impl_ndproducer! { [Clone => 'a, A, D: Clone ] Windows { base, + life, window, strides, } @@ -109,7 +114,8 @@ where type IntoIter = WindowsIter<'a, A, D>; fn into_iter(self) -> Self::IntoIter { WindowsIter { - iter: self.base.into_elements_base(), + iter: self.base.into_base_iter(), + life: self.life, window: self.window, strides: self.strides, } @@ -121,7 +127,8 @@ where /// See [`.windows()`](ArrayBase::windows) for more /// information. pub struct WindowsIter<'a, A, D> { - iter: ElementsBase<'a, A, D>, + iter: Baseiter, + life: PhantomData<&'a A>, window: D, strides: D, } @@ -131,19 +138,23 @@ impl_iterator! { [Clone => 'a, A, D: Clone] WindowsIter { iter, + life, window, strides, } WindowsIter<'a, A, D> { type Item = ArrayView<'a, A, D>; - fn item(&mut self, elt) { + fn item(&mut self, ptr) { unsafe { ArrayView::new_( - elt, + ptr, self.window.clone(), self.strides.clone()) } } } } + +send_sync_read_only!(Windows); +send_sync_read_only!(WindowsIter); diff --git a/src/lib.rs b/src/lib.rs index e80f2651a..a651ae58c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1559,10 +1559,6 @@ where unsafe { ArrayView::new(ptr, dim, strides) } } - fn raw_strides(&self) -> D { - self.strides.clone() - } - /// Remove array axis `axis` and return the result. fn try_remove_axis(self, axis: Axis) -> ArrayBase { let d = self.dim.try_remove_axis(axis); From 39f0bfabd5f211d3efa437b2eb72565441a46e2c Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 29 Jan 2024 14:25:20 +0900 Subject: [PATCH 524/651] derived Debug for Iter and IterMut --- src/iterators/mod.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 6bac471bd..821e8d153 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -14,11 +14,11 @@ pub mod iter; mod lanes; mod windows; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; use std::iter::FromIterator; use std::marker::PhantomData; use std::ptr; -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; use crate::Ix1; @@ -26,15 +26,16 @@ use super::{ArrayBase, ArrayView, ArrayViewMut, Axis, Data, NdProducer, RemoveAx use super::{Dimension, Ix, Ixs}; pub use self::chunks::{ExactChunks, ExactChunksIter, ExactChunksIterMut, ExactChunksMut}; +pub use self::into_iter::IntoIter; pub use self::lanes::{Lanes, LanesMut}; pub use self::windows::Windows; -pub use self::into_iter::IntoIter; use std::slice::{self, Iter as SliceIter, IterMut as SliceIterMut}; /// Base for iterators over all axes. /// /// Iterator element type is `*mut A`. +#[derive(Debug)] pub struct Baseiter { ptr: *mut A, dim: D, @@ -306,7 +307,7 @@ where } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum ElementsRepr { Slice(S), Counted(C), @@ -317,11 +318,19 @@ pub enum ElementsRepr { /// Iterator element type is `&'a A`. /// /// See [`.iter()`](ArrayBase::iter) for more information. +#[derive(Debug)] pub struct Iter<'a, A, D> { inner: ElementsRepr, ElementsBase<'a, A, D>>, } +// impl<'a, A, D> fmt::Debug for Iter<'a, A, D> { +// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +// f.debug_struct("Iter").field("inner", &self.inner).finish() +// } +// } + /// Counted read only iterator +#[derive(Debug)] pub struct ElementsBase<'a, A, D> { inner: Baseiter, life: PhantomData<&'a A>, @@ -332,6 +341,7 @@ pub struct ElementsBase<'a, A, D> { /// Iterator element type is `&'a mut A`. /// /// See [`.iter_mut()`](ArrayBase::iter_mut) for more information. +#[derive(Debug)] pub struct IterMut<'a, A, D> { inner: ElementsRepr, ElementsBaseMut<'a, A, D>>, } @@ -339,6 +349,7 @@ pub struct IterMut<'a, A, D> { /// An iterator over the elements of an array. /// /// Iterator element type is `&'a mut A`. +#[derive(Debug)] pub struct ElementsBaseMut<'a, A, D> { inner: Baseiter, life: PhantomData<&'a mut A>, From 2205612c96a30bdb3b60736f3d04f6ee7db09f6a Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 30 Jan 2024 16:00:30 +0900 Subject: [PATCH 525/651] removed commented code --- src/iterators/mod.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 821e8d153..49c9c1887 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -323,12 +323,6 @@ pub struct Iter<'a, A, D> { inner: ElementsRepr, ElementsBase<'a, A, D>>, } -// impl<'a, A, D> fmt::Debug for Iter<'a, A, D> { -// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -// f.debug_struct("Iter").field("inner", &self.inner).finish() -// } -// } - /// Counted read only iterator #[derive(Debug)] pub struct ElementsBase<'a, A, D> { From 7b1df49888f88374a9bf2fb9e8250f85e9072aff Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 6 Dec 2021 23:53:35 -0500 Subject: [PATCH 526/651] Add tests for overflow converting slices to views --- tests/array-construct.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/array-construct.rs b/tests/array-construct.rs index c8948d1a3..420a9d925 100644 --- a/tests/array-construct.rs +++ b/tests/array-construct.rs @@ -244,6 +244,29 @@ fn deny_wraparound_uninit() { let _five_large = Array::::uninit((3, 7, 29, 36760123, 823996703)); } +#[should_panic] +#[test] +fn deny_slice_with_too_many_rows_to_arrayview2() { + let _view = ArrayView2::from(&[[0u8; 0]; usize::MAX][..]); +} + +#[should_panic] +#[test] +fn deny_slice_with_too_many_zero_sized_elems_to_arrayview2() { + let _view = ArrayView2::from(&[[(); isize::MAX as usize]; isize::MAX as usize][..]); +} + +#[should_panic] +#[test] +fn deny_slice_with_too_many_rows_to_arrayviewmut2() { + let _view = ArrayViewMut2::from(&mut [[0u8; 0]; usize::MAX][..]); +} + +#[should_panic] +#[test] +fn deny_slice_with_too_many_zero_sized_elems_to_arrayviewmut2() { + let _view = ArrayViewMut2::from(&mut [[(); isize::MAX as usize]; isize::MAX as usize][..]); +} #[test] fn maybe_uninit_1() { From a68435d3f6d2ec9b7267c0941dc79daafeb128d1 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 7 Dec 2021 00:01:33 -0500 Subject: [PATCH 527/651] Add missing checks when converting slices to views --- src/arraytraits.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/arraytraits.rs b/src/arraytraits.rs index f45c8a1bd..39a82b1ae 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -328,8 +328,9 @@ where /// Implementation of ArrayView2::from(&S) where S is a slice to a 2D array /// -/// **Panics** if the product of non-zero axis lengths overflows `isize` (This can only occur if A -/// is zero-sized because slices cannot contain more than `isize::MAX` number of bytes). +/// **Panics** if the product of non-zero axis lengths overflows `isize`. (This +/// can only occur if A is zero-sized or if `N` is zero, because slices cannot +/// contain more than `isize::MAX` number of bytes.) impl<'a, A, const N: usize> From<&'a [[A; N]]> for ArrayView<'a, A, Ix2> { /// Create a two-dimensional read-only array view of the data in `slice` fn from(xs: &'a [[A; N]]) -> Self { @@ -339,6 +340,11 @@ impl<'a, A, const N: usize> From<&'a [[A; N]]> for ArrayView<'a, A, Ix2> { if size_of::() == 0 { dimension::size_of_shape_checked(&dim) .expect("Product of non-zero axis lengths must not overflow isize."); + } else if N == 0 { + assert!( + xs.len() <= isize::MAX as usize, + "Product of non-zero axis lengths must not overflow isize.", + ); } // `cols * rows` is guaranteed to fit in `isize` because we checked that it fits in @@ -384,8 +390,9 @@ where /// Implementation of ArrayViewMut2::from(&S) where S is a slice to a 2D array /// -/// **Panics** if the product of non-zero axis lengths overflows `isize` (This can only occur if A -/// is zero-sized because slices cannot contain more than `isize::MAX` number of bytes). +/// **Panics** if the product of non-zero axis lengths overflows `isize`. (This +/// can only occur if `A` is zero-sized or if `N` is zero, because slices +/// cannot contain more than `isize::MAX` number of bytes.) impl<'a, A, const N: usize> From<&'a mut [[A; N]]> for ArrayViewMut<'a, A, Ix2> { /// Create a two-dimensional read-write array view of the data in `slice` fn from(xs: &'a mut [[A; N]]) -> Self { @@ -395,6 +402,11 @@ impl<'a, A, const N: usize> From<&'a mut [[A; N]]> for ArrayViewMut<'a, A, Ix2> if size_of::() == 0 { dimension::size_of_shape_checked(&dim) .expect("Product of non-zero axis lengths must not overflow isize."); + } else if N == 0 { + assert!( + xs.len() <= isize::MAX as usize, + "Product of non-zero axis lengths must not overflow isize.", + ); } // `cols * rows` is guaranteed to fit in `isize` because we checked that it fits in From 6034da4b8d1377cd132778af68b73ccbd1044715 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 7 Dec 2021 01:00:35 -0500 Subject: [PATCH 528/651] Make Ix* functions const fn --- src/aliases.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/aliases.rs b/src/aliases.rs index d41c888a6..9a8ea8f2c 100644 --- a/src/aliases.rs +++ b/src/aliases.rs @@ -7,43 +7,43 @@ use crate::{ArcArray, Array, ArrayView, ArrayViewMut, Ix, IxDynImpl}; /// Create a zero-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub fn Ix0() -> Ix0 { +pub const fn Ix0() -> Ix0 { Dim::new([]) } /// Create a one-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub fn Ix1(i0: Ix) -> Ix1 { +pub const fn Ix1(i0: Ix) -> Ix1 { Dim::new([i0]) } /// Create a two-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub fn Ix2(i0: Ix, i1: Ix) -> Ix2 { +pub const fn Ix2(i0: Ix, i1: Ix) -> Ix2 { Dim::new([i0, i1]) } /// Create a three-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub fn Ix3(i0: Ix, i1: Ix, i2: Ix) -> Ix3 { +pub const fn Ix3(i0: Ix, i1: Ix, i2: Ix) -> Ix3 { Dim::new([i0, i1, i2]) } /// Create a four-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub fn Ix4(i0: Ix, i1: Ix, i2: Ix, i3: Ix) -> Ix4 { +pub const fn Ix4(i0: Ix, i1: Ix, i2: Ix, i3: Ix) -> Ix4 { Dim::new([i0, i1, i2, i3]) } /// Create a five-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub fn Ix5(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix) -> Ix5 { +pub const fn Ix5(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix) -> Ix5 { Dim::new([i0, i1, i2, i3, i4]) } /// Create a six-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub fn Ix6(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix, i5: Ix) -> Ix6 { +pub const fn Ix6(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix, i5: Ix) -> Ix6 { Dim::new([i0, i1, i2, i3, i4, i5]) } From ea429b4215e30f09916a1e113f4c454a92229181 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 7 Dec 2021 01:00:57 -0500 Subject: [PATCH 529/651] Make Dim::new and *ViewRepr::new const fns --- src/dimension/dim.rs | 2 +- src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dimension/dim.rs b/src/dimension/dim.rs index 19075f943..3f8ff23e3 100644 --- a/src/dimension/dim.rs +++ b/src/dimension/dim.rs @@ -42,7 +42,7 @@ pub struct Dim { impl Dim { /// Private constructor and accessors for Dim - pub(crate) fn new(index: I) -> Dim { + pub(crate) const fn new(index: I) -> Dim { Dim { index } } #[inline(always)] diff --git a/src/lib.rs b/src/lib.rs index a651ae58c..5d3dc7003 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1450,7 +1450,7 @@ pub struct RawViewRepr { impl RawViewRepr { #[inline(always)] - fn new() -> Self { + const fn new() -> Self { RawViewRepr { ptr: PhantomData } } } @@ -1467,7 +1467,7 @@ pub struct ViewRepr { impl ViewRepr { #[inline(always)] - fn new() -> Self { + const fn new() -> Self { ViewRepr { life: PhantomData } } } From fa9fb4a20f0c11580783207ddde5443cff39e903 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 15 Jan 2021 22:52:59 +0100 Subject: [PATCH 530/651] shape: Deprecate .reshape() --- src/impl_methods.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 9186cd762..434f635da 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1933,6 +1933,9 @@ where /// /// **Panics** if shapes are incompatible. /// + /// *This method is obsolete, because it is inflexible in how logical order + /// of the array is handled. See [`.to_shape()`].* + /// /// ``` /// use ndarray::{rcarr1, rcarr2}; /// @@ -1942,6 +1945,7 @@ where /// [3., 4.]]) /// ); /// ``` + #[deprecated(note="Obsolete, use `to_shape` or `into_shape` instead.", since="0.15.2")] pub fn reshape(&self, shape: E) -> ArrayBase where S: DataShared + DataOwned, From 050434704c4116eca532818ff713c4bcbcff80e0 Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 4 May 2021 23:41:40 +0200 Subject: [PATCH 531/651] shape: Remove usages of deprecated .reshape() --- tests/broadcast.rs | 12 ++++++------ tests/dimension.rs | 4 ++-- tests/oper.rs | 22 +++++++++++----------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/broadcast.rs b/tests/broadcast.rs index 5416e9017..bef3d614c 100644 --- a/tests/broadcast.rs +++ b/tests/broadcast.rs @@ -5,12 +5,12 @@ use ndarray::prelude::*; fn broadcast_1() { let a_dim = Dim([2, 4, 2, 2]); let b_dim = Dim([2, 1, 2, 1]); - let a = ArcArray::linspace(0., 1., a_dim.size()).reshape(a_dim); - let b = ArcArray::linspace(0., 1., b_dim.size()).reshape(b_dim); + let a = ArcArray::linspace(0., 1., a_dim.size()).into_shape(a_dim).unwrap(); + let b = ArcArray::linspace(0., 1., b_dim.size()).into_shape(b_dim).unwrap(); assert!(b.broadcast(a.dim()).is_some()); let c_dim = Dim([2, 1]); - let c = ArcArray::linspace(0., 1., c_dim.size()).reshape(c_dim); + let c = ArcArray::linspace(0., 1., c_dim.size()).into_shape(c_dim).unwrap(); assert!(c.broadcast(1).is_none()); assert!(c.broadcast(()).is_none()); assert!(c.broadcast((2, 1)).is_some()); @@ -31,8 +31,8 @@ fn broadcast_1() { fn test_add() { let a_dim = Dim([2, 4, 2, 2]); let b_dim = Dim([2, 1, 2, 1]); - let mut a = ArcArray::linspace(0.0, 1., a_dim.size()).reshape(a_dim); - let b = ArcArray::linspace(0.0, 1., b_dim.size()).reshape(b_dim); + let mut a = ArcArray::linspace(0.0, 1., a_dim.size()).into_shape(a_dim).unwrap(); + let b = ArcArray::linspace(0.0, 1., b_dim.size()).into_shape(b_dim).unwrap(); a += &b; let t = ArcArray::from_elem((), 1.0f32); a += &t; @@ -43,7 +43,7 @@ fn test_add() { #[cfg(feature = "std")] fn test_add_incompat() { let a_dim = Dim([2, 4, 2, 2]); - let mut a = ArcArray::linspace(0.0, 1., a_dim.size()).reshape(a_dim); + let mut a = ArcArray::linspace(0.0, 1., a_dim.size()).into_shape(a_dim).unwrap(); let incompat = ArcArray::from_elem(3, 1.0f32); a += &incompat; } diff --git a/tests/dimension.rs b/tests/dimension.rs index 939b4f0e3..2787d7588 100644 --- a/tests/dimension.rs +++ b/tests/dimension.rs @@ -55,8 +55,8 @@ fn remove_axis() { let a = ArcArray::::zeros(vec![4, 5, 6]); let _b = a .index_axis_move(Axis(1), 0) - .reshape((4, 6)) - .reshape(vec![2, 3, 4]); + .to_shape((4, 6)).unwrap() + .to_shape(vec![2, 3, 4]).unwrap(); } #[test] diff --git a/tests/oper.rs b/tests/oper.rs index 051728680..fbffd9328 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -17,24 +17,24 @@ use approx::assert_abs_diff_eq; use defmac::defmac; fn test_oper(op: &str, a: &[f32], b: &[f32], c: &[f32]) { - let aa = rcarr1(a); - let bb = rcarr1(b); - let cc = rcarr1(c); + let aa = CowArray::from(arr1(a)); + let bb = CowArray::from(arr1(b)); + let cc = CowArray::from(arr1(c)); test_oper_arr::(op, aa.clone(), bb.clone(), cc.clone()); let dim = (2, 2); - let aa = aa.reshape(dim); - let bb = bb.reshape(dim); - let cc = cc.reshape(dim); + let aa = aa.to_shape(dim).unwrap(); + let bb = bb.to_shape(dim).unwrap(); + let cc = cc.to_shape(dim).unwrap(); test_oper_arr::(op, aa.clone(), bb.clone(), cc.clone()); let dim = (1, 2, 1, 2); - let aa = aa.reshape(dim); - let bb = bb.reshape(dim); - let cc = cc.reshape(dim); + let aa = aa.to_shape(dim).unwrap(); + let bb = bb.to_shape(dim).unwrap(); + let cc = cc.to_shape(dim).unwrap(); test_oper_arr::(op, aa.clone(), bb.clone(), cc.clone()); } -fn test_oper_arr(op: &str, mut aa: ArcArray, bb: ArcArray, cc: ArcArray) +fn test_oper_arr(op: &str, mut aa: CowArray, bb: CowArray, cc: CowArray) where D: Dimension, { @@ -66,7 +66,7 @@ where } "neg" => { assert_eq!(-&aa, cc); - assert_eq!(-aa.clone(), cc); + assert_eq!(-aa.into_owned(), cc); } _ => panic!(), } From 7d613425a85b5a3b35d28344f9fb8ae808147d57 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 6 May 2021 20:20:14 +0200 Subject: [PATCH 532/651] shape: Add .into_shape_clone() --- src/impl_methods.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 434f635da..2f451dac1 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1922,6 +1922,74 @@ where } } + /// Transform the array into `shape`; any shape with the same number of + /// elements is accepted. Array elements are reordered in place if + /// possible, otherwise they are copied to create a new array. + /// + /// If an index ordering is not specified, the default is `RowMajor`. + /// The operation will only succeed if the array's memory layout is compatible with + /// the index ordering, so that the array elements can be rearranged in place. + /// + /// # `.to_shape` vs `.into_shape_clone` + /// + /// - `to_shape` supports views and outputting views + /// - `to_shape` borrows the original array, `into_shape_clone` consumes the original + /// - `into_shape_clone` preserves array type (Array vs ArcArray), but does not support views. + /// + /// **Errors** if the shapes don't have the same number of elements.
+ pub fn into_shape_clone(self, shape: E) -> Result, ShapeError> + where + S: DataOwned, + A: Clone, + E: ShapeArg, + { + let (shape, order) = shape.into_shape_and_order(); + let order = order.unwrap_or(Order::RowMajor); + self.into_shape_clone_order(shape, order) + } + + pub fn into_shape_clone_order(self, shape: E, order: Order) + -> Result, ShapeError> + where + S: DataOwned, + A: Clone, + E: Dimension, + { + let len = self.dim.size(); + if size_of_shape_checked(&shape) != Ok(len) { + return Err(error::incompatible_shapes(&self.dim, &shape)); + } + + // Safe because the array and new shape is empty. + if len == 0 { + unsafe { + return Ok(self.with_strides_dim(shape.default_strides(), shape)); + } + } + + // Try to reshape the array's current data + match reshape_dim(&self.dim, &self.strides, &shape, order) { + Ok(to_strides) => unsafe { + return Ok(self.with_strides_dim(to_strides, shape)); + } + Err(err) if err.kind() == ErrorKind::IncompatibleShape => { + return Err(error::incompatible_shapes(&self.dim, &shape)); + } + _otherwise => { } + } + + // otherwise, clone and allocate a new array + unsafe { + let (shape, view) = match order { + Order::RowMajor => (shape.set_f(false), self.view()), + Order::ColumnMajor => (shape.set_f(true), self.t()), + }; + + Ok(ArrayBase::from_shape_trusted_iter_unchecked( + shape, view.into_iter(), A::clone)) + } + } + /// *Note: Reshape is for `ArcArray` only. Use `.into_shape()` for /// other arrays and array views.* /// @@ -1952,6 +2020,7 @@ where A: Clone, E: IntoDimension, { + return self.clone().into_shape_clone(shape).unwrap(); let shape = shape.into_dimension(); if size_of_shape_checked(&shape) != Ok(self.dim.size()) { panic!( From 3f02ac20d11197f367b19d8f958cacb105bce98f Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 6 May 2021 20:20:22 +0200 Subject: [PATCH 533/651] shape: Convert format tests to use into_shape_clone --- tests/format.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/format.rs b/tests/format.rs index 422fa2957..4b21fe39d 100644 --- a/tests/format.rs +++ b/tests/format.rs @@ -6,7 +6,7 @@ fn formatting() { let a = rcarr1::(&[1., 2., 3., 4.]); assert_eq!(format!("{}", a), "[1, 2, 3, 4]"); assert_eq!(format!("{:4}", a), "[ 1, 2, 3, 4]"); - let a = a.reshape((4, 1, 1)); + let a = a.into_shape_clone((4, 1, 1)).unwrap(); assert_eq!( format!("{}", a), "\ @@ -30,7 +30,7 @@ fn formatting() { [[ 4]]]", ); - let a = a.reshape((2, 2)); + let a = a.into_shape_clone((2, 2)).unwrap(); assert_eq!( format!("{}", a), "\ From 8501d7bbe9ce0750752b41c3f14f3cc78c598d99 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 26 Jul 2023 00:04:14 +0200 Subject: [PATCH 534/651] shape: Deprecate into_shape, add into_shape_with_order into_shape_with_order: If an index ordering is not specified, the default is `RowMajor`. The operation will only succeed if the array's memory layout is compatible with the index ordering. into_shape: `.into_shape()` "moves" elements differently depending on if the input array is C-contig or F-contig, it follows the index order that corresponds to the memory order. Because of this, the method is deprecated. That reshapes depend on memory order is not intuitive. --- src/impl_methods.rs | 79 +++++++++++++++++++++++++++++++++++++++++++-- tests/reshape.rs | 38 ++++++++++++++++++++++ 2 files changed, 115 insertions(+), 2 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 2f451dac1..6a2619ed8 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1885,10 +1885,84 @@ where } } + /// Transform the array into `shape`; any shape with the same number of + /// elements is accepted, but the source array must be contiguous. + /// + /// If an index ordering is not specified, the default is `RowMajor`. + /// The operation will only succeed if the array's memory layout is compatible with + /// the index ordering. + /// + /// Use `.to_shape()` instead for more flexible reshaping of arrays, which + /// allows copying elements if required. + /// + /// **Errors** if the shapes don't have the same number of elements.
+ /// **Errors** if order RowMajor is given but input is not c-contiguous. + /// **Errors** if order ColumnMajor is given but input is not f-contiguous. + /// + /// If shape is not given: use memory layout of incoming array. Row major arrays are + /// reshaped using row major index ordering, column major arrays with column major index + /// ordering. + /// + /// ``` + /// use ndarray::{aview1, aview2}; + /// use ndarray::Order; + /// + /// assert!( + /// aview1(&[1., 2., 3., 4.]).into_shape_with_order((2, 2)).unwrap() + /// == aview2(&[[1., 2.], + /// [3., 4.]]) + /// ); + /// + /// assert!( + /// aview1(&[1., 2., 3., 4.]).into_shape_with_order(((2, 2), Order::ColumnMajor)).unwrap() + /// == aview2(&[[1., 3.], + /// [2., 4.]]) + /// ); + /// ``` + pub fn into_shape_with_order(self, shape: E) -> Result, ShapeError> + where + E: ShapeArg, + { + let (shape, order) = shape.into_shape_and_order(); + self.into_shape_with_order_impl(shape, order.unwrap_or(Order::RowMajor)) + } + + fn into_shape_with_order_impl(self, shape: E, order: Order) + -> Result, ShapeError> + where + E: Dimension, + { + let shape = shape.into_dimension(); + if size_of_shape_checked(&shape) != Ok(self.dim.size()) { + return Err(error::incompatible_shapes(&self.dim, &shape)); + } + + // Check if contiguous, then we can change shape + unsafe { + // safe because arrays are contiguous and len is unchanged + match order { + Order::RowMajor if self.is_standard_layout() => { + Ok(self.with_strides_dim(shape.default_strides(), shape)) + } + Order::ColumnMajor if self.raw_view().reversed_axes().is_standard_layout() => { + Ok(self.with_strides_dim(shape.fortran_strides(), shape)) + } + _otherwise => Err(error::from_kind(error::ErrorKind::IncompatibleLayout)) + } + } + } + /// Transform the array into `shape`; any shape with the same number of /// elements is accepted, but the source array or view must be in standard /// or column-major (Fortran) layout. /// + /// **Note** that `.into_shape()` "moves" elements differently depending on if the input array + /// is C-contig or F-contig, it follows the index order that corresponds to the memory order. + /// Prefer to use `.to_shape()` or `.into_shape_with_order()`. + /// + /// Because of this, the method is deprecated. That reshapes depend on memory order is not + /// intuitive. + /// /// **Errors** if the shapes don't have the same number of elements.
/// **Errors** if the input array is not c- or f-contiguous. /// @@ -1901,6 +1975,7 @@ where /// [3., 4.]]) /// ); /// ``` + #[deprecated = "Use `.into_shape_with_order()` or `.to_shape()`"] pub fn into_shape(self, shape: E) -> Result, ShapeError> where E: IntoDimension, @@ -1948,7 +2023,7 @@ where self.into_shape_clone_order(shape, order) } - pub fn into_shape_clone_order(self, shape: E, order: Order) + fn into_shape_clone_order(self, shape: E, order: Order) -> Result, ShapeError> where S: DataOwned, @@ -2020,7 +2095,7 @@ where A: Clone, E: IntoDimension, { - return self.clone().into_shape_clone(shape).unwrap(); + //return self.clone().into_shape_clone(shape).unwrap(); let shape = shape.into_dimension(); if size_of_shape_checked(&shape) != Ok(self.dim.size()) { panic!( diff --git a/tests/reshape.rs b/tests/reshape.rs index 21fe407ea..52bb6e908 100644 --- a/tests/reshape.rs +++ b/tests/reshape.rs @@ -230,3 +230,41 @@ fn to_shape_broadcast() { } } } + + +#[test] +fn into_shape_with_order() { + // 1D -> C -> C + let data = [1, 2, 3, 4, 5, 6, 7, 8]; + let v = aview1(&data); + let u = v.into_shape_with_order(((3, 3), Order::RowMajor)); + assert!(u.is_err()); + + let u = v.into_shape_with_order(((2, 2, 2), Order::C)); + assert!(u.is_ok()); + + let u = u.unwrap(); + assert_eq!(u.shape(), &[2, 2, 2]); + assert_eq!(u, array![[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); + + let s = u.into_shape_with_order((4, 2)).unwrap(); + assert_eq!(s.shape(), &[4, 2]); + assert_eq!(s, aview2(&[[1, 2], [3, 4], [5, 6], [7, 8]])); + + // 1D -> F -> F + let data = [1, 2, 3, 4, 5, 6, 7, 8]; + let v = aview1(&data); + let u = v.into_shape_with_order(((3, 3), Order::ColumnMajor)); + assert!(u.is_err()); + + let u = v.into_shape_with_order(((2, 2, 2), Order::ColumnMajor)); + assert!(u.is_ok()); + + let u = u.unwrap(); + assert_eq!(u.shape(), &[2, 2, 2]); + assert_eq!(u, array![[[1, 5], [3, 7]], [[2, 6], [4, 8]]]); + + let s = u.into_shape_with_order(((4, 2), Order::ColumnMajor)).unwrap(); + assert_eq!(s.shape(), &[4, 2]); + assert_eq!(s, array![[1, 5], [2, 6], [3, 7], [4, 8]]); +} From e16dc45d0544f98152ce75de660ee56d98952c33 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Thu, 3 Aug 2023 23:06:58 +0200 Subject: [PATCH 535/651] shape: Use into_shape_with_order, not into_shape --- README-quick-start.md | 11 +++---- benches/bench1.rs | 2 +- benches/construct.rs | 4 +-- benches/higher-order.rs | 14 ++++----- benches/iter.rs | 10 +++---- benches/numeric.rs | 2 +- examples/axis_ops.rs | 2 +- examples/life.rs | 2 +- examples/sort-axis.rs | 2 +- src/free_functions.rs | 4 +-- src/impl_constructors.rs | 12 ++++---- src/impl_methods.rs | 10 +++---- src/impl_owned_array.rs | 6 ++-- src/impl_views/indexing.rs | 2 +- src/parallel/mod.rs | 4 +-- src/shape_builder.rs | 14 ++++----- tests/append.rs | 16 +++++------ tests/array-construct.rs | 4 +-- tests/array.rs | 26 ++++++++--------- tests/azip.rs | 4 +-- tests/broadcast.rs | 12 ++++---- tests/dimension.rs | 2 +- tests/indices.rs | 7 +++-- tests/iterator_chunks.rs | 2 +- tests/iterators.rs | 12 ++++---- tests/ixdyn.rs | 17 +++++------ tests/oper.rs | 20 ++++++------- tests/par_rayon.rs | 4 +-- tests/reshape.rs | 18 ++++++------ tests/windows.rs | 40 +++++++++++++------------- xtest-blas/tests/oper.rs | 18 ++++++------ xtest-numeric/tests/accuracy.rs | 2 +- xtest-serialization/tests/serialize.rs | 2 +- 33 files changed, 155 insertions(+), 152 deletions(-) diff --git a/README-quick-start.md b/README-quick-start.md index 811299651..b3783032f 100644 --- a/README-quick-start.md +++ b/README-quick-start.md @@ -169,7 +169,7 @@ fn main() { println!("a shape {:?}", &a.shape()); println!("b shape {:?}", &b.shape()); - let b = b.into_shape((4,1)).unwrap(); // reshape b to shape [4, 1] + let b = b.into_shape_with_order((4,1)).unwrap(); // reshape b to shape [4, 1] println!("b shape after reshape {:?}", &b.shape()); println!("{}", a.dot(&b)); // [1, 4] x [4, 1] -> [1, 1] @@ -295,7 +295,8 @@ row: [[100, 101, 102], ## Shape Manipulation ### Changing the shape of an array -The shape of an array can be changed with `into_shape` method. +The shape of an array can be changed with `into_shape_with_order` method. +(One can also use `to_shape` for this.) ````rust use ndarray::prelude::*; @@ -319,7 +320,7 @@ fn main() { let b = Array::from_iter(a.iter()); println!("b = \n{:?}\n", b); - let c = b.into_shape([6, 2]).unwrap(); // consume b and generate c with new shape + let c = b.into_shape_with_order([6, 2]).unwrap(); // consume b and generate c with new shape println!("c = \n{:?}", c); } ```` @@ -459,7 +460,7 @@ use ndarray::{Array, Axis}; fn main() { - let mut a = Array::range(0., 12., 1.).into_shape([3 ,4]).unwrap(); + let mut a = Array::range(0., 12., 1.).into_shape_with_order([3 ,4]).unwrap(); println!("a = \n{}\n", a); { @@ -519,7 +520,7 @@ use ndarray::Array; fn main() { - let mut a = Array::range(0., 4., 1.).into_shape([2 ,2]).unwrap(); + let mut a = Array::range(0., 4., 1.).into_shape_with_order([2 ,2]).unwrap(); let b = a.clone(); println!("a = \n{}\n", a); diff --git a/benches/bench1.rs b/benches/bench1.rs index c7f18e3c4..6b6864194 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -915,7 +915,7 @@ const MEAN_SUM_N: usize = 127; fn range_mat(m: Ix, n: Ix) -> Array2 { assert!(m * n != 0); Array::linspace(0., (m * n - 1) as f32, m * n) - .into_shape((m, n)) + .into_shape_with_order((m, n)) .unwrap() } diff --git a/benches/construct.rs b/benches/construct.rs index 3d77a89e0..c3603ce7c 100644 --- a/benches/construct.rs +++ b/benches/construct.rs @@ -22,13 +22,13 @@ fn zeros_f64(bench: &mut Bencher) { #[bench] fn map_regular(bench: &mut test::Bencher) { - let a = Array::linspace(0., 127., 128).into_shape((8, 16)).unwrap(); + let a = Array::linspace(0., 127., 128).into_shape_with_order((8, 16)).unwrap(); bench.iter(|| a.map(|&x| 2. * x)); } #[bench] fn map_stride(bench: &mut test::Bencher) { - let a = Array::linspace(0., 127., 256).into_shape((8, 32)).unwrap(); + let a = Array::linspace(0., 127., 256).into_shape_with_order((8, 32)).unwrap(); let av = a.slice(s![.., ..;2]); bench.iter(|| av.map(|&x| 2. * x)); } diff --git a/benches/higher-order.rs b/benches/higher-order.rs index 6bbd57177..f593cd026 100644 --- a/benches/higher-order.rs +++ b/benches/higher-order.rs @@ -17,7 +17,7 @@ const Y: usize = 16; #[bench] fn map_regular(bench: &mut Bencher) { - let a = Array::linspace(0., 127., N).into_shape((X, Y)).unwrap(); + let a = Array::linspace(0., 127., N).into_shape_with_order((X, Y)).unwrap(); bench.iter(|| a.map(|&x| 2. * x)); } @@ -28,7 +28,7 @@ pub fn double_array(mut a: ArrayViewMut2<'_, f64>) { #[bench] fn map_stride_double_f64(bench: &mut Bencher) { let mut a = Array::linspace(0., 127., N * 2) - .into_shape([X, Y * 2]) + .into_shape_with_order([X, Y * 2]) .unwrap(); let mut av = a.slice_mut(s![.., ..;2]); bench.iter(|| { @@ -39,7 +39,7 @@ fn map_stride_double_f64(bench: &mut Bencher) { #[bench] fn map_stride_f64(bench: &mut Bencher) { let a = Array::linspace(0., 127., N * 2) - .into_shape([X, Y * 2]) + .into_shape_with_order([X, Y * 2]) .unwrap(); let av = a.slice(s![.., ..;2]); bench.iter(|| av.map(|&x| 2. * x)); @@ -48,7 +48,7 @@ fn map_stride_f64(bench: &mut Bencher) { #[bench] fn map_stride_u32(bench: &mut Bencher) { let a = Array::linspace(0., 127., N * 2) - .into_shape([X, Y * 2]) + .into_shape_with_order([X, Y * 2]) .unwrap(); let b = a.mapv(|x| x as u32); let av = b.slice(s![.., ..;2]); @@ -58,7 +58,7 @@ fn map_stride_u32(bench: &mut Bencher) { #[bench] fn fold_axis(bench: &mut Bencher) { let a = Array::linspace(0., 127., N * 2) - .into_shape([X, Y * 2]) + .into_shape_with_order([X, Y * 2]) .unwrap(); bench.iter(|| a.fold_axis(Axis(0), 0., |&acc, &elt| acc + elt)); } @@ -69,7 +69,7 @@ const MASZ: usize = MA * MA; #[bench] fn map_axis_0(bench: &mut Bencher) { let a = Array::from_iter(0..MASZ as i32) - .into_shape([MA, MA]) + .into_shape_with_order([MA, MA]) .unwrap(); bench.iter(|| a.map_axis(Axis(0), black_box)); } @@ -77,7 +77,7 @@ fn map_axis_0(bench: &mut Bencher) { #[bench] fn map_axis_1(bench: &mut Bencher) { let a = Array::from_iter(0..MASZ as i32) - .into_shape([MA, MA]) + .into_shape_with_order([MA, MA]) .unwrap(); bench.iter(|| a.map_axis(Axis(1), black_box)); } diff --git a/benches/iter.rs b/benches/iter.rs index d8c716dc8..22c8b8d17 100644 --- a/benches/iter.rs +++ b/benches/iter.rs @@ -46,21 +46,21 @@ 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).into_shape((16, 16)).unwrap(); + let a = Array::linspace(0., 1., 256).into_shape_with_order((16, 16)).unwrap(); let b = a.mapv(|x| (x * 100.) as u32); bench.iter(|| b.iter().filter(|&&x| x < 75).sum::()); } #[bench] fn iter_filter_sum_2d_f32(bench: &mut Bencher) { - let a = Array::linspace(0., 1., 256).into_shape((16, 16)).unwrap(); + let a = Array::linspace(0., 1., 256).into_shape_with_order((16, 16)).unwrap(); let b = a * 100.; bench.iter(|| b.iter().filter(|&&x| x < 75.).sum::()); } #[bench] fn iter_filter_sum_2d_stride_u32(bench: &mut Bencher) { - let a = Array::linspace(0., 1., 256).into_shape((16, 16)).unwrap(); + let a = Array::linspace(0., 1., 256).into_shape_with_order((16, 16)).unwrap(); let b = a.mapv(|x| (x * 100.) as u32); let b = b.slice(s![.., ..;2]); bench.iter(|| b.iter().filter(|&&x| x < 75).sum::()); @@ -68,7 +68,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).into_shape((16, 16)).unwrap(); + let a = Array::linspace(0., 1., 256).into_shape_with_order((16, 16)).unwrap(); let b = a * 100.; let b = b.slice(s![.., ..;2]); bench.iter(|| b.iter().filter(|&&x| x < 75.).sum::()); @@ -321,7 +321,7 @@ fn indexed_iter_3d_dyn(bench: &mut Bencher) { for ((i, j, k), elt) in a.indexed_iter_mut() { *elt = (i + 100 * j + 10000 * k) as _; } - let a = a.into_shape(&[ISZ; 3][..]).unwrap(); + let a = a.into_shape_with_order(&[ISZ; 3][..]).unwrap(); bench.iter(|| { for (i, &_elt) in a.indexed_iter() { diff --git a/benches/numeric.rs b/benches/numeric.rs index 4c579eb71..d9b9187ff 100644 --- a/benches/numeric.rs +++ b/benches/numeric.rs @@ -12,7 +12,7 @@ const Y: usize = 16; #[bench] fn clip(bench: &mut Bencher) { let mut a = Array::linspace(0., 127., N * 2) - .into_shape([X, Y * 2]) + .into_shape_with_order([X, Y * 2]) .unwrap(); let min = 2.; let max = 5.; diff --git a/examples/axis_ops.rs b/examples/axis_ops.rs index 0b2c3dec8..e84d112c2 100644 --- a/examples/axis_ops.rs +++ b/examples/axis_ops.rs @@ -74,7 +74,7 @@ fn main() { } regularize(&mut b).unwrap(); - let mut b = b.into_shape(a.len()).unwrap(); + let mut b = b.into_shape_with_order(a.len()).unwrap(); regularize(&mut b).unwrap(); b.invert_axis(Axis(0)); diff --git a/examples/life.rs b/examples/life.rs index 48f1d609f..e0675ae17 100644 --- a/examples/life.rs +++ b/examples/life.rs @@ -22,7 +22,7 @@ fn parse(x: &[u8]) -> Board { _ => None, })); - let a = a.into_shape((N, N)).unwrap(); + let a = a.into_shape_with_order((N, N)).unwrap(); map.slice_mut(s![1..-1, 1..-1]).assign(&a); map } diff --git a/examples/sort-axis.rs b/examples/sort-axis.rs index ee5ff3465..2ff6ceb32 100644 --- a/examples/sort-axis.rs +++ b/examples/sort-axis.rs @@ -166,7 +166,7 @@ where #[cfg(feature = "std")] fn main() { - let a = Array::linspace(0., 63., 64).into_shape((8, 8)).unwrap(); + let a = Array::linspace(0., 63., 64).into_shape_with_order((8, 8)).unwrap(); let strings = a.map(|x| x.to_string()); let perm = a.sort_axis_by(Axis(1), |i, j| a[[i, 0]] > a[[j, 0]]); diff --git a/src/free_functions.rs b/src/free_functions.rs index e9c3abff6..6deaafcb7 100644 --- a/src/free_functions.rs +++ b/src/free_functions.rs @@ -77,7 +77,7 @@ pub fn aview0
(x: &A) -> ArrayView0<'_, A> { /// let data = [1.0; 1024]; /// /// // Create a 2D array view from borrowed data -/// let a2d = aview1(&data).into_shape((32, 32)).unwrap(); +/// let a2d = aview1(&data).into_shape_with_order((32, 32)).unwrap(); /// /// assert_eq!(a2d.sum(), 1024.0); /// ``` @@ -100,7 +100,7 @@ pub fn aview2(xs: &[[A; N]]) -> ArrayView2<'_, A> { /// // Create an array view over some data, then slice it and modify it. /// let mut data = [0; 1024]; /// { -/// let mut a = aview_mut1(&mut data).into_shape((32, 32)).unwrap(); +/// let mut a = aview_mut1(&mut data).into_shape_with_order((32, 32)).unwrap(); /// a.slice_mut(s![.., ..;3]).fill(5); /// } /// assert_eq!(&data[..10], [5, 0, 0, 5, 0, 0, 5, 0, 0, 5]); diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 2779e27d0..94ddebcd6 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -329,7 +329,7 @@ where A: Clone, Sh: ShapeBuilder, { - let shape = shape.into_shape(); + let shape = shape.into_shape_with_order(); let size = size_of_shape_checked_unwrap!(&shape.dim); let v = vec![elem; size]; unsafe { Self::from_shape_vec_unchecked(shape, v) } @@ -383,7 +383,7 @@ where Sh: ShapeBuilder, F: FnMut() -> A, { - let shape = shape.into_shape(); + let shape = shape.into_shape_with_order(); let len = size_of_shape_checked_unwrap!(&shape.dim); let v = to_vec_mapped(0..len, move |_| f()); unsafe { Self::from_shape_vec_unchecked(shape, v) } @@ -414,7 +414,7 @@ where Sh: ShapeBuilder, F: FnMut(D::Pattern) -> A, { - let shape = shape.into_shape(); + let shape = shape.into_shape_with_order(); let _ = size_of_shape_checked_unwrap!(&shape.dim); if shape.is_c() { let v = to_vec_mapped(indices(shape.dim.clone()).into_iter(), f); @@ -591,7 +591,7 @@ where Sh: ShapeBuilder, { unsafe { - let shape = shape.into_shape(); + let shape = shape.into_shape_with_order(); let size = size_of_shape_checked_unwrap!(&shape.dim); let mut v = Vec::with_capacity(size); v.set_len(size); @@ -664,7 +664,7 @@ where A: Copy, Sh: ShapeBuilder, { - let shape = shape.into_shape(); + let shape = shape.into_shape_with_order(); let size = size_of_shape_checked_unwrap!(&shape.dim); let mut v = Vec::with_capacity(size); v.set_len(size); @@ -687,7 +687,7 @@ where Sh: ShapeBuilder, { unsafe { - let shape = shape.into_shape(); + let shape = shape.into_shape_with_order(); let size = size_of_shape_checked_unwrap!(&shape.dim); let mut v = Vec::with_capacity(size); v.set_len(size); diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 6a2619ed8..f07ecb43a 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1323,7 +1323,7 @@ where /// use ndarray::Array; /// use ndarray::{arr3, Axis}; /// - /// let a = Array::from_iter(0..28).into_shape((2, 7, 2)).unwrap(); + /// let a = Array::from_iter(0..28).into_shape_with_order((2, 7, 2)).unwrap(); /// let mut iter = a.axis_chunks_iter(Axis(1), 2); /// /// // first iteration yields a 2 × 2 × 2 view @@ -1956,7 +1956,7 @@ where /// elements is accepted, but the source array or view must be in standard /// or column-major (Fortran) layout. /// - /// **Note** that `.into_shape()` "moves" elements differently depending on if the input array + /// **Note** that `.into_shape_with_order()` "moves" elements differently depending on if the input array /// is C-contig or F-contig, it follows the index order that corresponds to the memory order. /// Prefer to use `.to_shape()` or `.into_shape_with_order()`. /// @@ -1970,7 +1970,7 @@ where /// use ndarray::{aview1, aview2}; /// /// assert!( - /// aview1(&[1., 2., 3., 4.]).into_shape((2, 2)).unwrap() + /// aview1(&[1., 2., 3., 4.]).into_shape_with_order((2, 2)).unwrap() /// == aview2(&[[1., 2.], /// [3., 4.]]) /// ); @@ -2065,7 +2065,7 @@ where } } - /// *Note: Reshape is for `ArcArray` only. Use `.into_shape()` for + /// *Note: Reshape is for `ArcArray` only. Use `.into_shape_with_order()` for /// other arrays and array views.* /// /// Transform the array into `shape`; any shape with the same number of @@ -2088,7 +2088,7 @@ where /// [3., 4.]]) /// ); /// ``` - #[deprecated(note="Obsolete, use `to_shape` or `into_shape` instead.", since="0.15.2")] + #[deprecated(note="Obsolete, use `to_shape` or `into_shape_with_order` instead.", since="0.15.2")] pub fn reshape(&self, shape: E) -> ArrayBase where S: DataShared + DataOwned, diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index aaa9f30b5..3e9001132 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -232,7 +232,7 @@ impl Array /// ``` /// use ndarray::Array; /// - /// let a = Array::from_iter(0..100).into_shape((10, 10)).unwrap(); + /// let a = Array::from_iter(0..100).into_shape_with_order((10, 10)).unwrap(); /// let mut b = Array::uninit((10, 10)); /// a.move_into_uninit(&mut b); /// unsafe { @@ -448,8 +448,8 @@ impl Array /// /// // create an empty array and append two rows at a time /// let mut a = Array::zeros((0, 4)); - /// let ones = ArrayView::from(&[1.; 8]).into_shape((2, 4)).unwrap(); - /// let zeros = ArrayView::from(&[0.; 8]).into_shape((2, 4)).unwrap(); + /// let ones = ArrayView::from(&[1.; 8]).into_shape_with_order((2, 4)).unwrap(); + /// let zeros = ArrayView::from(&[0.; 8]).into_shape_with_order((2, 4)).unwrap(); /// a.append(Axis(0), ones).unwrap(); /// a.append(Axis(0), zeros).unwrap(); /// a.append(Axis(0), ones).unwrap(); diff --git a/src/impl_views/indexing.rs b/src/impl_views/indexing.rs index adb911a48..c30f97e01 100644 --- a/src/impl_views/indexing.rs +++ b/src/impl_views/indexing.rs @@ -33,7 +33,7 @@ use crate::NdIndex; /// let data = [0.; 256]; /// let long_life_ref = { /// // make a 16 × 16 array view -/// let view = ArrayView::from(&data[..]).into_shape((16, 16)).unwrap(); +/// let view = ArrayView::from(&data[..]).into_shape_with_order((16, 16)).unwrap(); /// /// // index the view and with `IndexLonger`. /// // Note here that we get a reference with a life that is derived from diff --git a/src/parallel/mod.rs b/src/parallel/mod.rs index 48e97be47..552515f11 100644 --- a/src/parallel/mod.rs +++ b/src/parallel/mod.rs @@ -65,7 +65,7 @@ //! use ndarray::Axis; //! use ndarray::parallel::prelude::*; //! -//! let a = Array::linspace(0., 63., 64).into_shape((4, 16)).unwrap(); +//! let a = Array::linspace(0., 63., 64).into_shape_with_order((4, 16)).unwrap(); //! let mut sums = Vec::new(); //! a.axis_iter(Axis(0)) //! .into_par_iter() @@ -84,7 +84,7 @@ //! use ndarray::Axis; //! use ndarray::parallel::prelude::*; //! -//! let a = Array::linspace(0., 63., 64).into_shape((4, 16)).unwrap(); +//! let a = Array::linspace(0., 63., 64).into_shape_with_order((4, 16)).unwrap(); //! let mut shapes = Vec::new(); //! a.axis_chunks_iter(Axis(0), 3) //! .into_par_iter() diff --git a/src/shape_builder.rs b/src/shape_builder.rs index 470374077..877102b5c 100644 --- a/src/shape_builder.rs +++ b/src/shape_builder.rs @@ -90,7 +90,7 @@ pub trait ShapeBuilder { type Dim: Dimension; type Strides; - fn into_shape(self) -> Shape; + fn into_shape_with_order(self) -> Shape; fn f(self) -> Shape; fn set_f(self, is_f: bool) -> Shape; fn strides(self, strides: Self::Strides) -> StrideShape; @@ -102,7 +102,7 @@ where { /// Create a `Shape` from `dimension`, using the default memory layout. fn from(dimension: D) -> Shape { - dimension.into_shape() + dimension.into_shape_with_order() } } @@ -112,7 +112,7 @@ where T: ShapeBuilder, { fn from(value: T) -> Self { - let shape = value.into_shape(); + let shape = value.into_shape_with_order(); let st = if shape.is_c() { Strides::C } else { Strides::F }; StrideShape { strides: st, @@ -127,7 +127,7 @@ where { type Dim = T::Dim; type Strides = T; - fn into_shape(self) -> Shape { + fn into_shape_with_order(self) -> Shape { Shape { dim: self.into_dimension(), strides: Strides::C, @@ -137,10 +137,10 @@ where self.set_f(true) } fn set_f(self, is_f: bool) -> Shape { - self.into_shape().set_f(is_f) + self.into_shape_with_order().set_f(is_f) } fn strides(self, st: T) -> StrideShape { - self.into_shape().strides(st.into_dimension()) + self.into_shape_with_order().strides(st.into_dimension()) } } @@ -151,7 +151,7 @@ where type Dim = D; type Strides = D; - fn into_shape(self) -> Shape { + fn into_shape_with_order(self) -> Shape { self } diff --git a/tests/append.rs b/tests/append.rs index dcebcf50e..cbb10d853 100644 --- a/tests/append.rs +++ b/tests/append.rs @@ -282,8 +282,8 @@ fn append_array_3d() { fn test_append_2d() { // create an empty array and append let mut a = Array::zeros((0, 4)); - let ones = ArrayView::from(&[1.; 12]).into_shape((3, 4)).unwrap(); - let zeros = ArrayView::from(&[0.; 8]).into_shape((2, 4)).unwrap(); + let ones = ArrayView::from(&[1.; 12]).into_shape_with_order((3, 4)).unwrap(); + let zeros = ArrayView::from(&[0.; 8]).into_shape_with_order((2, 4)).unwrap(); a.append(Axis(0), ones).unwrap(); a.append(Axis(0), zeros).unwrap(); a.append(Axis(0), ones).unwrap(); @@ -314,16 +314,16 @@ fn test_append_2d() { fn test_append_middle_axis() { // ensure we can append to Axis(1) by letting it become outermost let mut a = Array::::zeros((3, 0, 2)); - a.append(Axis(1), Array::from_iter(0..12).into_shape((3, 2, 2)).unwrap().view()).unwrap(); + a.append(Axis(1), Array::from_iter(0..12).into_shape_with_order((3, 2, 2)).unwrap().view()).unwrap(); println!("{:?}", a); - a.append(Axis(1), Array::from_iter(12..24).into_shape((3, 2, 2)).unwrap().view()).unwrap(); + a.append(Axis(1), Array::from_iter(12..24).into_shape_with_order((3, 2, 2)).unwrap().view()).unwrap(); println!("{:?}", a); // ensure we can append to Axis(1) by letting it become outermost let mut a = Array::::zeros((3, 1, 2)); - a.append(Axis(1), Array::from_iter(0..12).into_shape((3, 2, 2)).unwrap().view()).unwrap(); + a.append(Axis(1), Array::from_iter(0..12).into_shape_with_order((3, 2, 2)).unwrap().view()).unwrap(); println!("{:?}", a); - a.append(Axis(1), Array::from_iter(12..24).into_shape((3, 2, 2)).unwrap().view()).unwrap(); + a.append(Axis(1), Array::from_iter(12..24).into_shape_with_order((3, 2, 2)).unwrap().view()).unwrap(); println!("{:?}", a); } @@ -339,8 +339,8 @@ fn test_append_zero_size() { { let mut a = Array::::zeros((0, 0)); - a.append(Axis(1), ArrayView::from(&[]).into_shape((0, 1)).unwrap()).unwrap(); - a.append(Axis(1), ArrayView::from(&[]).into_shape((0, 1)).unwrap()).unwrap(); + a.append(Axis(1), ArrayView::from(&[]).into_shape_with_order((0, 1)).unwrap()).unwrap(); + a.append(Axis(1), ArrayView::from(&[]).into_shape_with_order((0, 1)).unwrap()).unwrap(); assert_eq!(a.len(), 0); assert_eq!(a.shape(), &[0, 2]); } diff --git a/tests/array-construct.rs b/tests/array-construct.rs index 420a9d925..a3949fcab 100644 --- a/tests/array-construct.rs +++ b/tests/array-construct.rs @@ -60,7 +60,7 @@ fn test_uninit() { assert_eq!(a.dim(), (3, 4)); assert_eq!(a.strides(), &[1, 3]); let b = Array::::linspace(0., 25., a.len()) - .into_shape(a.dim()) + .into_shape_with_order(a.dim()) .unwrap(); a.assign(&b); assert_eq!(&a, &b); @@ -214,7 +214,7 @@ fn deny_wraparound_zeros() { fn deny_wraparound_reshape() { //2^64 + 5 = 18446744073709551621 = 3×7×29×36760123×823996703 (5 distinct prime factors) let five = Array::::zeros(5); - let _five_large = five.into_shape((3, 7, 29, 36760123, 823996703)).unwrap(); + let _five_large = five.into_shape_with_order((3, 7, 29, 36760123, 823996703)).unwrap(); } #[should_panic] diff --git a/tests/array.rs b/tests/array.rs index d770d7822..0cfc6cedf 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -149,7 +149,7 @@ fn test_slice_with_many_dim() { [A[&[0, 0, 0, 0, 0, 1, 0][..]], A[&[0, 0, 2, 0, 0, 1, 0][..]]], [A[&[1, 0, 0, 0, 0, 1, 0][..]], A[&[1, 0, 2, 0, 0, 1, 0][..]]] ] - .into_shape(new_shape) + .into_shape_with_order(new_shape) .unwrap(); assert_eq!(vi, correct); @@ -404,7 +404,7 @@ fn test_multislice() { }; } - let mut arr = Array1::from_iter(0..48).into_shape((8, 6)).unwrap(); + let mut arr = Array1::from_iter(0..48).into_shape_with_order((8, 6)).unwrap(); assert_eq!( (arr.clone().view_mut(),), @@ -518,9 +518,9 @@ fn test_index() { fn test_index_arrays() { let a = Array1::from_iter(0..12); assert_eq!(a[1], a[[1]]); - let v = a.view().into_shape((3, 4)).unwrap(); + let v = a.view().into_shape_with_order((3, 4)).unwrap(); assert_eq!(a[1], v[[0, 1]]); - let w = v.into_shape((2, 2, 3)).unwrap(); + let w = v.into_shape_with_order((2, 2, 3)).unwrap(); assert_eq!(a[1], w[[0, 0, 1]]); } @@ -855,7 +855,7 @@ fn permuted_axes() { let permuted = a.view().permuted_axes([0]); assert_eq!(a, permuted); - let a = Array::from_iter(0..24).into_shape((2, 3, 4)).unwrap(); + let a = Array::from_iter(0..24).into_shape_with_order((2, 3, 4)).unwrap(); let permuted = a.view().permuted_axes([2, 1, 0]); for ((i0, i1, i2), elem) in a.indexed_iter() { assert_eq!(*elem, permuted[(i2, i1, i0)]); @@ -865,7 +865,7 @@ fn permuted_axes() { assert_eq!(*elem, permuted[&[i0, i2, i1][..]]); } - let a = Array::from_iter(0..120).into_shape((2, 3, 4, 5)).unwrap(); + let a = Array::from_iter(0..120).into_shape_with_order((2, 3, 4, 5)).unwrap(); let permuted = a.view().permuted_axes([1, 0, 3, 2]); for ((i0, i1, i2, i3), elem) in a.indexed_iter() { assert_eq!(*elem, permuted[(i1, i0, i3, i2)]); @@ -879,7 +879,7 @@ fn permuted_axes() { #[should_panic] #[test] fn permuted_axes_repeated_axis() { - let a = Array::from_iter(0..24).into_shape((2, 3, 4)).unwrap(); + let a = Array::from_iter(0..24).into_shape_with_order((2, 3, 4)).unwrap(); a.view().permuted_axes([1, 0, 1]); } @@ -887,7 +887,7 @@ fn permuted_axes_repeated_axis() { #[test] fn permuted_axes_missing_axis() { let a = Array::from_iter(0..24) - .into_shape((2, 3, 4)) + .into_shape_with_order((2, 3, 4)) .unwrap() .into_dyn(); a.view().permuted_axes(&[2, 0][..]); @@ -896,7 +896,7 @@ fn permuted_axes_missing_axis() { #[should_panic] #[test] fn permuted_axes_oob() { - let a = Array::from_iter(0..24).into_shape((2, 3, 4)).unwrap(); + let a = Array::from_iter(0..24).into_shape_with_order((2, 3, 4)).unwrap(); a.view().permuted_axes([1, 0, 3]); } @@ -1055,7 +1055,7 @@ fn as_slice_memory_order_mut_contiguous_cowarray() { fn to_slice_memory_order() { for shape in vec![[2, 0, 3, 5], [2, 1, 3, 5], [2, 4, 3, 5]] { let data: Vec = (0..shape.iter().product()).collect(); - let mut orig = Array1::from(data.clone()).into_shape(shape).unwrap(); + let mut orig = Array1::from(data.clone()).into_shape_with_order(shape).unwrap(); for perm in vec![[0, 1, 2, 3], [0, 2, 1, 3], [2, 0, 1, 3]] { let mut a = orig.view_mut().permuted_axes(perm); assert_eq!(a.as_slice_memory_order().unwrap(), &data); @@ -1424,7 +1424,7 @@ fn aview() { fn aview_mut() { let mut data = [0; 16]; { - let mut a = aview_mut1(&mut data).into_shape((4, 4)).unwrap(); + let mut a = aview_mut1(&mut data).into_shape_with_order((4, 4)).unwrap(); { let mut slc = a.slice_mut(s![..2, ..;2]); slc += 1; @@ -1906,7 +1906,7 @@ fn map_mut_with_unsharing() { fn test_view_from_shape() { let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; let a = ArrayView::from_shape((2, 3, 2), &s).unwrap(); - let mut answer = Array::from(s.to_vec()).into_shape((2, 3, 2)).unwrap(); + let mut answer = Array::from(s.to_vec()).into_shape_with_order((2, 3, 2)).unwrap(); assert_eq!(a, answer); // custom strides (row major) @@ -2240,7 +2240,7 @@ fn test_array_clone_unalias() { #[test] fn test_array_clone_same_view() { - let mut a = Array::from_iter(0..9).into_shape((3, 3)).unwrap(); + let mut a = Array::from_iter(0..9).into_shape_with_order((3, 3)).unwrap(); a.slice_collapse(s![..;-1, ..;-1]); let b = a.clone(); assert_eq!(a, b); diff --git a/tests/azip.rs b/tests/azip.rs index 6f0327f5d..d41c019dd 100644 --- a/tests/azip.rs +++ b/tests/azip.rs @@ -263,7 +263,7 @@ fn test_zip_dim_mismatch_1() { // where A is F-contiguous and B contiguous but neither F nor C contiguous. #[test] fn test_contiguous_but_not_c_or_f() { - let a = Array::from_iter(0..27).into_shape((3, 3, 3)).unwrap(); + let a = Array::from_iter(0..27).into_shape_with_order((3, 3, 3)).unwrap(); // both F order let a = a.reversed_axes(); @@ -287,7 +287,7 @@ fn test_contiguous_but_not_c_or_f() { #[test] fn test_clone() { - let a = Array::from_iter(0..27).into_shape((3, 3, 3)).unwrap(); + let a = Array::from_iter(0..27).into_shape_with_order((3, 3, 3)).unwrap(); let z = Zip::from(&a).and(a.exact_chunks((1, 1, 1))); let w = z.clone(); diff --git a/tests/broadcast.rs b/tests/broadcast.rs index bef3d614c..6dee901e2 100644 --- a/tests/broadcast.rs +++ b/tests/broadcast.rs @@ -5,12 +5,12 @@ use ndarray::prelude::*; fn broadcast_1() { let a_dim = Dim([2, 4, 2, 2]); let b_dim = Dim([2, 1, 2, 1]); - let a = ArcArray::linspace(0., 1., a_dim.size()).into_shape(a_dim).unwrap(); - let b = ArcArray::linspace(0., 1., b_dim.size()).into_shape(b_dim).unwrap(); + let a = ArcArray::linspace(0., 1., a_dim.size()).into_shape_with_order(a_dim).unwrap(); + let b = ArcArray::linspace(0., 1., b_dim.size()).into_shape_with_order(b_dim).unwrap(); assert!(b.broadcast(a.dim()).is_some()); let c_dim = Dim([2, 1]); - let c = ArcArray::linspace(0., 1., c_dim.size()).into_shape(c_dim).unwrap(); + let c = ArcArray::linspace(0., 1., c_dim.size()).into_shape_with_order(c_dim).unwrap(); assert!(c.broadcast(1).is_none()); assert!(c.broadcast(()).is_none()); assert!(c.broadcast((2, 1)).is_some()); @@ -31,8 +31,8 @@ fn broadcast_1() { fn test_add() { let a_dim = Dim([2, 4, 2, 2]); let b_dim = Dim([2, 1, 2, 1]); - let mut a = ArcArray::linspace(0.0, 1., a_dim.size()).into_shape(a_dim).unwrap(); - let b = ArcArray::linspace(0.0, 1., b_dim.size()).into_shape(b_dim).unwrap(); + let mut a = ArcArray::linspace(0.0, 1., a_dim.size()).into_shape_with_order(a_dim).unwrap(); + let b = ArcArray::linspace(0.0, 1., b_dim.size()).into_shape_with_order(b_dim).unwrap(); a += &b; let t = ArcArray::from_elem((), 1.0f32); a += &t; @@ -43,7 +43,7 @@ fn test_add() { #[cfg(feature = "std")] fn test_add_incompat() { let a_dim = Dim([2, 4, 2, 2]); - let mut a = ArcArray::linspace(0.0, 1., a_dim.size()).into_shape(a_dim).unwrap(); + let mut a = ArcArray::linspace(0.0, 1., a_dim.size()).into_shape_with_order(a_dim).unwrap(); let incompat = ArcArray::from_elem(3, 1.0f32); a += &incompat; } diff --git a/tests/dimension.rs b/tests/dimension.rs index 2787d7588..5dae5b5a3 100644 --- a/tests/dimension.rs +++ b/tests/dimension.rs @@ -62,7 +62,7 @@ fn remove_axis() { #[test] #[allow(clippy::eq_op)] fn dyn_dimension() { - let a = arr2(&[[1., 2.], [3., 4.0]]).into_shape(vec![2, 2]).unwrap(); + let a = arr2(&[[1., 2.], [3., 4.0]]).into_shape_with_order(vec![2, 2]).unwrap(); assert_eq!(&a - &a, Array::zeros(vec![2, 2])); assert_eq!(a[&[0, 0][..]], 1.); assert_eq!(a[[0, 0]], 1.); diff --git a/tests/indices.rs b/tests/indices.rs index 3e2c0796c..ca6ca9887 100644 --- a/tests/indices.rs +++ b/tests/indices.rs @@ -1,15 +1,16 @@ use ndarray::indices_of; use ndarray::prelude::*; +use ndarray::Order; #[test] fn test_ixdyn_index_iterate() { - for &rev in &[false, true] { - let mut a = Array::zeros((2, 3, 4).set_f(rev)); + for &order in &[Order::C, Order::F] { + let mut a = Array::zeros((2, 3, 4).set_f(order.is_column_major())); let dim = a.shape().to_vec(); for ((i, j, k), elt) in a.indexed_iter_mut() { *elt = i + 10 * j + 100 * k; } - let a = a.into_shape(dim).unwrap(); + let a = a.into_shape_with_order((dim, order)).unwrap(); println!("{:?}", a.dim()); let mut c = 0; for i in indices_of(&a) { diff --git a/tests/iterator_chunks.rs b/tests/iterator_chunks.rs index 67ddd1e38..bf3af2d56 100644 --- a/tests/iterator_chunks.rs +++ b/tests/iterator_chunks.rs @@ -13,7 +13,7 @@ use ndarray::prelude::*; fn chunks() { use ndarray::NdProducer; let a = >::linspace(1., 100., 10 * 10) - .into_shape((10, 10)) + .into_shape_with_order((10, 10)) .unwrap(); let (m, n) = a.dim(); diff --git a/tests/iterators.rs b/tests/iterators.rs index addd59adc..8b9dfd94e 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -564,7 +564,7 @@ fn axis_chunks_iter_corner_cases() { fn axis_chunks_iter_zero_stride() { { // stride 0 case - let b = Array::from(vec![0f32; 0]).into_shape((5, 0, 3)).unwrap(); + let b = Array::from(vec![0f32; 0]).into_shape_with_order((5, 0, 3)).unwrap(); let shapes: Vec<_> = b .axis_chunks_iter(Axis(0), 2) .map(|v| v.raw_dim()) @@ -574,7 +574,7 @@ fn axis_chunks_iter_zero_stride() { { // stride 0 case reverse - let b = Array::from(vec![0f32; 0]).into_shape((5, 0, 3)).unwrap(); + let b = Array::from(vec![0f32; 0]).into_shape_with_order((5, 0, 3)).unwrap(); let shapes: Vec<_> = b .axis_chunks_iter(Axis(0), 2) .rev() @@ -744,7 +744,7 @@ fn iterators_are_send_sync() { // are too. fn _send_sync(_: &T) {} - let mut a = ArcArray::from_iter(0..30).into_shape((5, 3, 2)).unwrap(); + let mut a = ArcArray::from_iter(0..30).into_shape_with_order((5, 3, 2)).unwrap(); _send_sync(&a.view()); _send_sync(&a.view_mut()); @@ -904,11 +904,11 @@ fn test_into_iter() { #[test] fn test_into_iter_2d() { - let a = Array1::from(vec![1, 2, 3, 4]).into_shape((2, 2)).unwrap(); + let a = Array1::from(vec![1, 2, 3, 4]).into_shape_with_order((2, 2)).unwrap(); let v = a.into_iter().collect::>(); assert_eq!(v, [1, 2, 3, 4]); - let a = Array1::from(vec![1, 2, 3, 4]).into_shape((2, 2)).unwrap().reversed_axes(); + let a = Array1::from(vec![1, 2, 3, 4]).into_shape_with_order((2, 2)).unwrap().reversed_axes(); let v = a.into_iter().collect::>(); assert_eq!(v, [1, 3, 2, 4]); } @@ -930,7 +930,7 @@ fn test_into_iter_sliced() { let j2 = j2 as isize; let mut a = Array1::from_iter(0..(m * n) as i32) .mapv(|v| DropCount::new(v, &drops)) - .into_shape((m, n)).unwrap(); + .into_shape_with_order((m, n)).unwrap(); a.slice_collapse(s![i..i2, j..j2]); if invert < a.ndim() { a.invert_axis(Axis(invert)); diff --git a/tests/ixdyn.rs b/tests/ixdyn.rs index 11af2c97a..5b7ef9327 100644 --- a/tests/ixdyn.rs +++ b/tests/ixdyn.rs @@ -10,6 +10,7 @@ use ndarray::Array; use ndarray::IntoDimension; use ndarray::ShapeBuilder; use ndarray::Ix3; +use ndarray::Order; #[test] fn test_ixdyn() { @@ -39,14 +40,14 @@ fn test_ixdyn_out_of_bounds() { #[test] fn test_ixdyn_iterate() { - for &rev in &[false, true] { - let mut a = Array::zeros((2, 3, 4).set_f(rev)); + for &order in &[Order::C, Order::F] { + let mut a = Array::zeros((2, 3, 4).set_f(order.is_column_major())); let dim = a.shape().to_vec(); for (i, elt) in a.iter_mut().enumerate() { *elt = i; } println!("{:?}", a.dim()); - let mut a = a.into_shape(dim).unwrap(); + let mut a = a.into_shape_with_order((dim, order)).unwrap(); println!("{:?}", a.dim()); let mut c = 0; for (i, elt) in a.iter_mut().enumerate() { @@ -59,13 +60,13 @@ fn test_ixdyn_iterate() { #[test] fn test_ixdyn_index_iterate() { - for &rev in &[false, true] { - let mut a = Array::zeros((2, 3, 4).set_f(rev)); + for &order in &[Order::C, Order::F] { + let mut a = Array::zeros((2, 3, 4).set_f(order.is_column_major())); let dim = a.shape().to_vec(); for ((i, j, k), elt) in a.indexed_iter_mut() { *elt = i + 10 * j + 100 * k; } - let a = a.into_shape(dim).unwrap(); + let a = a.into_shape_with_order((dim, order)).unwrap(); println!("{:?}", a.dim()); let mut c = 0; for (i, elt) in a.indexed_iter() { @@ -159,8 +160,8 @@ fn test_0_add_broad() { fn test_into_dimension() { use ndarray::{Ix0, Ix1, Ix2, IxDyn}; - let a = Array::linspace(0., 41., 6 * 7).into_shape((6, 7)).unwrap(); - let a2 = a.clone().into_shape(IxDyn(&[6, 7])).unwrap(); + let a = Array::linspace(0., 41., 6 * 7).into_shape_with_order((6, 7)).unwrap(); + let a2 = a.clone().into_shape_with_order(IxDyn(&[6, 7])).unwrap(); let b = a2.clone().into_dimensionality::().unwrap(); assert_eq!(a, b); diff --git a/tests/oper.rs b/tests/oper.rs index fbffd9328..12e822cb7 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -237,7 +237,7 @@ fn dot_product_neg_stride() { #[test] fn fold_and_sum() { - let a = Array::linspace(0., 127., 128).into_shape((8, 16)).unwrap(); + let a = Array::linspace(0., 127., 128).into_shape_with_order((8, 16)).unwrap(); assert_abs_diff_eq!(a.fold(0., |acc, &x| acc + x), a.sum(), epsilon = 1e-5); // test different strides @@ -276,7 +276,7 @@ fn fold_and_sum() { #[test] fn product() { - let a = Array::linspace(0.5, 2., 128).into_shape((8, 16)).unwrap(); + let a = Array::linspace(0.5, 2., 128).into_shape_with_order((8, 16)).unwrap(); assert_abs_diff_eq!(a.fold(1., |acc, &x| acc * x), a.product(), epsilon = 1e-5); // test different strides @@ -296,13 +296,13 @@ fn product() { fn range_mat(m: Ix, n: Ix) -> Array2 { Array::linspace(0., (m * n) as f32 - 1., m * n) - .into_shape((m, n)) + .into_shape_with_order((m, n)) .unwrap() } fn range_mat64(m: Ix, n: Ix) -> Array2 { Array::linspace(0., (m * n) as f64 - 1., m * n) - .into_shape((m, n)) + .into_shape_with_order((m, n)) .unwrap() } @@ -313,7 +313,7 @@ fn range1_mat64(m: Ix) -> Array1 { fn range_i32(m: Ix, n: Ix) -> Array2 { Array::from_iter(0..(m * n) as i32) - .into_shape((m, n)) + .into_shape_with_order((m, n)) .unwrap() } @@ -593,7 +593,7 @@ fn scaled_add_3() { ] }; - let c = range_mat64(n, q).into_shape(cdim).unwrap(); + let c = range_mat64(n, q).into_shape_with_order(cdim).unwrap(); { let mut av = a.slice_mut(s![..;s1, ..;s2]); @@ -711,8 +711,8 @@ fn gen_mat_vec_mul() { S2: Data, { let ((m, _), k) = (lhs.dim(), rhs.dim()); - reference_mat_mul(lhs, &rhs.as_standard_layout().into_shape((k, 1)).unwrap()) - .into_shape(m) + reference_mat_mul(lhs, &rhs.as_standard_layout().into_shape_with_order((k, 1)).unwrap()) + .into_shape_with_order(m) .unwrap() } @@ -776,8 +776,8 @@ fn vec_mat_mul() { S2: Data, { let (m, (_, n)) = (lhs.dim(), rhs.dim()); - reference_mat_mul(&lhs.as_standard_layout().into_shape((1, m)).unwrap(), rhs) - .into_shape(n) + reference_mat_mul(&lhs.as_standard_layout().into_shape_with_order((1, m)).unwrap(), rhs) + .into_shape_with_order(n) .unwrap() } diff --git a/tests/par_rayon.rs b/tests/par_rayon.rs index 4d5a8f1a9..40670c6bf 100644 --- a/tests/par_rayon.rs +++ b/tests/par_rayon.rs @@ -25,7 +25,7 @@ fn test_axis_iter() { fn test_axis_iter_mut() { use approx::assert_abs_diff_eq; let mut a = Array::linspace(0., 1.0f64, M * N) - .into_shape((M, N)) + .into_shape_with_order((M, N)) .unwrap(); let b = a.mapv(|x| x.exp()); a.axis_iter_mut(Axis(0)) @@ -77,7 +77,7 @@ fn test_axis_chunks_iter() { fn test_axis_chunks_iter_mut() { use approx::assert_abs_diff_eq; let mut a = Array::linspace(0., 1.0f64, M * N) - .into_shape((M, N)) + .into_shape_with_order((M, N)) .unwrap(); let b = a.mapv(|x| x.exp()); a.axis_chunks_iter_mut(Axis(0), CHUNK_SIZE) diff --git a/tests/reshape.rs b/tests/reshape.rs index 52bb6e908..7d9bab55c 100644 --- a/tests/reshape.rs +++ b/tests/reshape.rs @@ -8,13 +8,13 @@ use ndarray::Order; fn reshape() { let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); - let u = v.into_shape((3, 3)); + let u = v.into_shape_with_order((3, 3)); assert!(u.is_err()); - let u = v.into_shape((2, 2, 2)); + let u = v.into_shape_with_order((2, 2, 2)); assert!(u.is_ok()); let u = u.unwrap(); assert_eq!(u.shape(), &[2, 2, 2]); - let s = u.into_shape((4, 2)).unwrap(); + let s = u.into_shape_with_order((4, 2)).unwrap(); assert_eq!(s.shape(), &[4, 2]); assert_eq!(s, aview2(&[[1, 2], [3, 4], [5, 6], [7, 8]])); } @@ -24,7 +24,7 @@ fn reshape() { fn reshape_error1() { let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); - let _u = v.into_shape((2, 5)).unwrap(); + let _u = v.into_shape_with_order((2, 5)).unwrap(); } #[test] @@ -32,9 +32,9 @@ fn reshape_error1() { fn reshape_error2() { let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); - let mut u = v.into_shape((2, 2, 2)).unwrap(); + let mut u = v.into_shape_with_order((2, 2, 2)).unwrap(); u.swap_axes(0, 1); - let _s = u.into_shape((2, 4)).unwrap(); + let _s = u.into_shape_with_order((2, 4)).unwrap(); } #[test] @@ -47,16 +47,16 @@ fn reshape_f() { println!("{:?}", v); // noop ok - let v2 = v.into_shape((3, 4)); + let v2 = v.into_shape_with_order(((3, 4), Order::F)); assert!(v2.is_ok()); assert_eq!(v, v2.unwrap()); - let u = v.into_shape((3, 2, 2)); + let u = v.into_shape_with_order(((3, 2, 2), Order::F)); assert!(u.is_ok()); let u = u.unwrap(); println!("{:?}", u); assert_eq!(u.shape(), &[3, 2, 2]); - let s = u.into_shape((4, 3)).unwrap(); + let s = u.into_shape_with_order(((4, 3), Order::F)).unwrap(); println!("{:?}", s); assert_eq!(s.shape(), &[4, 3]); assert_eq!(s, aview2(&[[0, 4, 8], [1, 5, 9], [2, 6, 10], [3, 7, 11]])); diff --git a/tests/windows.rs b/tests/windows.rs index 095976eaa..c24a47ef9 100644 --- a/tests/windows.rs +++ b/tests/windows.rs @@ -26,14 +26,14 @@ use ndarray::{arr3, Zip}; #[test] #[should_panic] fn windows_iterator_zero_size() { - let a = Array::from_iter(10..37).into_shape((3, 3, 3)).unwrap(); + let a = Array::from_iter(10..37).into_shape_with_order((3, 3, 3)).unwrap(); a.windows(Dim((0, 0, 0))); } /// Test that verifies that no windows are yielded on oversized window sizes. #[test] fn windows_iterator_oversized() { - let a = Array::from_iter(10..37).into_shape((3, 3, 3)).unwrap(); + let a = Array::from_iter(10..37).into_shape_with_order((3, 3, 3)).unwrap(); let mut iter = a.windows((4, 3, 2)).into_iter(); // (4,3,2) doesn't fit into (3,3,3) => oversized! assert_eq!(iter.next(), None); } @@ -41,7 +41,7 @@ fn windows_iterator_oversized() { /// Simple test for iterating 1d-arrays via `Windows`. #[test] fn windows_iterator_1d() { - let a = Array::from_iter(10..20).into_shape(10).unwrap(); + let a = Array::from_iter(10..20).into_shape_with_order(10).unwrap(); itertools::assert_equal( a.windows(Dim(4)), vec![ @@ -59,7 +59,7 @@ fn windows_iterator_1d() { /// Simple test for iterating 2d-arrays via `Windows`. #[test] fn windows_iterator_2d() { - let a = Array::from_iter(10..30).into_shape((5, 4)).unwrap(); + let a = Array::from_iter(10..30).into_shape_with_order((5, 4)).unwrap(); itertools::assert_equal( a.windows(Dim((3, 2))), vec![ @@ -79,7 +79,7 @@ fn windows_iterator_2d() { /// Simple test for iterating 3d-arrays via `Windows`. #[test] fn windows_iterator_3d() { - let a = Array::from_iter(10..37).into_shape((3, 3, 3)).unwrap(); + let a = Array::from_iter(10..37).into_shape_with_order((3, 3, 3)).unwrap(); itertools::assert_equal( a.windows(Dim((2, 2, 2))), vec![ @@ -99,14 +99,14 @@ fn windows_iterator_3d() { #[test] #[should_panic] fn windows_iterator_stride_axis_zero() { - let a = Array::from_iter(10..37).into_shape((3, 3, 3)).unwrap(); + let a = Array::from_iter(10..37).into_shape_with_order((3, 3, 3)).unwrap(); a.windows_with_stride((2, 2, 2), (0, 2, 2)); } /// Test that verifies that only first window is yielded when stride is oversized on every axis. #[test] fn windows_iterator_only_one_valid_window_for_oversized_stride() { - let a = Array::from_iter(10..135).into_shape((5, 5, 5)).unwrap(); + let a = Array::from_iter(10..135).into_shape_with_order((5, 5, 5)).unwrap(); let mut iter = a.windows_with_stride((2, 2, 2), (8, 8, 8)).into_iter(); // (4,3,2) doesn't fit into (3,3,3) => oversized! itertools::assert_equal( iter.next(), @@ -117,7 +117,7 @@ fn windows_iterator_only_one_valid_window_for_oversized_stride() { /// Simple test for iterating 1d-arrays via `Windows` with stride. #[test] fn windows_iterator_1d_with_stride() { - let a = Array::from_iter(10..20).into_shape(10).unwrap(); + let a = Array::from_iter(10..20).into_shape_with_order(10).unwrap(); itertools::assert_equal( a.windows_with_stride(4, 2), vec![ @@ -132,7 +132,7 @@ fn windows_iterator_1d_with_stride() { /// Simple test for iterating 2d-arrays via `Windows` with stride. #[test] fn windows_iterator_2d_with_stride() { - let a = Array::from_iter(10..30).into_shape((5, 4)).unwrap(); + let a = Array::from_iter(10..30).into_shape_with_order((5, 4)).unwrap(); itertools::assert_equal( a.windows_with_stride((3, 2), (2, 1)), vec![ @@ -149,7 +149,7 @@ fn windows_iterator_2d_with_stride() { /// Simple test for iterating 3d-arrays via `Windows` with stride. #[test] fn windows_iterator_3d_with_stride() { - let a = Array::from_iter(10..74).into_shape((4, 4, 4)).unwrap(); + let a = Array::from_iter(10..74).into_shape_with_order((4, 4, 4)).unwrap(); itertools::assert_equal( a.windows_with_stride((2, 2, 2), (2, 2, 2)), vec![ @@ -167,7 +167,7 @@ fn windows_iterator_3d_with_stride() { #[test] fn test_window_zip() { - let a = Array::from_iter(0..64).into_shape((4, 4, 4)).unwrap(); + let a = Array::from_iter(0..64).into_shape_with_order((4, 4, 4)).unwrap(); for x in 1..4 { for y in 1..4 { @@ -190,7 +190,7 @@ fn test_window_zip() { #[test] #[should_panic] fn axis_windows_outofbound() { - let a = Array::from_iter(10..37).into_shape((3, 3, 3)).unwrap(); + let a = Array::from_iter(10..37).into_shape_with_order((3, 3, 3)).unwrap(); a.axis_windows(Axis(4), 2); } @@ -198,14 +198,14 @@ fn axis_windows_outofbound() { #[test] #[should_panic] fn axis_windows_zero_size() { - let a = Array::from_iter(10..37).into_shape((3, 3, 3)).unwrap(); + let a = Array::from_iter(10..37).into_shape_with_order((3, 3, 3)).unwrap(); a.axis_windows(Axis(0), 0); } /// Test verifies that over sized windows yield nothing #[test] fn axis_windows_oversized() { - let a = Array::from_iter(10..37).into_shape((3, 3, 3)).unwrap(); + let a = Array::from_iter(10..37).into_shape_with_order((3, 3, 3)).unwrap(); let mut iter = a.axis_windows(Axis(2), 4).into_iter(); assert_eq!(iter.next(), None); } @@ -213,7 +213,7 @@ fn axis_windows_oversized() { /// Simple test for iterating 1d-arrays via `Axis Windows`. #[test] fn test_axis_windows_1d() { - let a = Array::from_iter(10..20).into_shape(10).unwrap(); + let a = Array::from_iter(10..20).into_shape_with_order(10).unwrap(); itertools::assert_equal( a.axis_windows(Axis(0), 5), @@ -231,7 +231,7 @@ fn test_axis_windows_1d() { /// Simple test for iterating 2d-arrays via `Axis Windows`. #[test] fn test_axis_windows_2d() { - let a = Array::from_iter(10..30).into_shape((5, 4)).unwrap(); + let a = Array::from_iter(10..30).into_shape_with_order((5, 4)).unwrap(); itertools::assert_equal( a.axis_windows(Axis(0), 2), @@ -247,7 +247,7 @@ fn test_axis_windows_2d() { /// Simple test for iterating 3d-arrays via `Axis Windows`. #[test] fn test_axis_windows_3d() { - let a = Array::from_iter(0..27).into_shape((3, 3, 3)).unwrap(); + let a = Array::from_iter(0..27).into_shape_with_order((3, 3, 3)).unwrap(); itertools::assert_equal( a.axis_windows(Axis(1), 2), @@ -269,14 +269,14 @@ fn test_axis_windows_3d() { #[test] fn test_window_neg_stride() { - let array = Array::from_iter(1..10).into_shape((3, 3)).unwrap(); + let array = Array::from_iter(1..10).into_shape_with_order((3, 3)).unwrap(); // window neg/pos stride combinations // Make a 2 x 2 array of the windows of the 3 x 3 array // and compute test answers from here let mut answer = Array::from_iter(array.windows((2, 2)).into_iter().map(|a| a.to_owned())) - .into_shape((2, 2)).unwrap(); + .into_shape_with_order((2, 2)).unwrap(); answer.invert_axis(Axis(1)); answer.map_inplace(|a| a.invert_axis(Axis(1))); @@ -305,7 +305,7 @@ fn test_window_neg_stride() { #[test] fn test_windows_with_stride_on_inverted_axis() { - let mut array = Array::from_iter(1..17).into_shape((4, 4)).unwrap(); + let mut array = Array::from_iter(1..17).into_shape_with_order((4, 4)).unwrap(); // inverting axis results in negative stride array.invert_axis(Axis(0)); diff --git a/xtest-blas/tests/oper.rs b/xtest-blas/tests/oper.rs index 120626d0b..5f11893df 100644 --- a/xtest-blas/tests/oper.rs +++ b/xtest-blas/tests/oper.rs @@ -45,26 +45,26 @@ fn mat_vec_product_1d_inverted_axis() { fn range_mat(m: Ix, n: Ix) -> Array2 { Array::linspace(0., (m * n) as f32 - 1., m * n) - .into_shape((m, n)) + .into_shape_with_order((m, n)) .unwrap() } fn range_mat64(m: Ix, n: Ix) -> Array2 { Array::linspace(0., (m * n) as f64 - 1., m * n) - .into_shape((m, n)) + .into_shape_with_order((m, n)) .unwrap() } fn range_mat_complex(m: Ix, n: Ix) -> Array2 { Array::linspace(0., (m * n) as f32 - 1., m * n) - .into_shape((m, n)) + .into_shape_with_order((m, n)) .unwrap() .map(|&f| Complex32::new(f, 0.)) } fn range_mat_complex64(m: Ix, n: Ix) -> Array2 { Array::linspace(0., (m * n) as f64 - 1., m * n) - .into_shape((m, n)) + .into_shape_with_order((m, n)) .unwrap() .map(|&f| Complex64::new(f, 0.)) } @@ -75,7 +75,7 @@ fn range1_mat64(m: Ix) -> Array1 { fn range_i32(m: Ix, n: Ix) -> Array2 { Array::from_iter(0..(m * n) as i32) - .into_shape((m, n)) + .into_shape_with_order((m, n)) .unwrap() } @@ -119,8 +119,8 @@ where S2: Data, { let ((m, _), k) = (lhs.dim(), rhs.dim()); - reference_mat_mul(lhs, &rhs.as_standard_layout().into_shape((k, 1)).unwrap()) - .into_shape(m) + reference_mat_mul(lhs, &rhs.as_standard_layout().into_shape_with_order((k, 1)).unwrap()) + .into_shape_with_order(m) .unwrap() } @@ -132,8 +132,8 @@ where S2: Data, { let (m, (_, n)) = (lhs.dim(), rhs.dim()); - reference_mat_mul(&lhs.as_standard_layout().into_shape((1, m)).unwrap(), rhs) - .into_shape(n) + reference_mat_mul(&lhs.as_standard_layout().into_shape_with_order((1, m)).unwrap(), rhs) + .into_shape_with_order(n) .unwrap() } diff --git a/xtest-numeric/tests/accuracy.rs b/xtest-numeric/tests/accuracy.rs index 1f8e9a82c..679267096 100644 --- a/xtest-numeric/tests/accuracy.rs +++ b/xtest-numeric/tests/accuracy.rs @@ -65,7 +65,7 @@ fn reference_mat_mul(lhs: &ArrayBase, rhs: &ArrayBase } } - res_elems.into_shape((m, n)).unwrap() + res_elems.into_shape_with_order((m, n)).unwrap() } fn gen(d: D, rng: &mut SmallRng) -> Array diff --git a/xtest-serialization/tests/serialize.rs b/xtest-serialization/tests/serialize.rs index efb3bacd9..e0e6f1d42 100644 --- a/xtest-serialization/tests/serialize.rs +++ b/xtest-serialization/tests/serialize.rs @@ -77,7 +77,7 @@ fn serial_ixdyn_serde() { { let a = arr2(&[[3., 1., 2.2], [3.1, 4., 7.]]) - .into_shape(IxDyn(&[3, 1, 1, 1, 2, 1])) + .into_shape_with_order(IxDyn(&[3, 1, 1, 1, 2, 1])) .unwrap(); let serial = serde_json::to_string(&a).unwrap(); println!("Serde encode {:?} => {:?}", a, serial); From 49f58708410fdba6ffc4a5e1e5ec69952d38af6e Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 9 Mar 2024 11:42:38 +0100 Subject: [PATCH 536/651] shape: Replace reshape with into_shape_with_order in tests --- tests/array.rs | 26 +++++++++--------- tests/iterators.rs | 38 +++++++++++++------------- xtest-serialization/tests/serialize.rs | 6 ++-- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/tests/array.rs b/tests/array.rs index 0cfc6cedf..561284869 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -74,7 +74,7 @@ fn arrayviewmut_shrink_lifetime<'a, 'b: 'a>( fn test_mat_mul() { // smoke test, a big matrix multiplication of uneven size let (n, m) = (45, 33); - let a = ArcArray::linspace(0., ((n * m) - 1) as f32, n as usize * m as usize).reshape((n, m)); + let a = ArcArray::linspace(0., ((n * m) - 1) as f32, n as usize * m as usize).into_shape_with_order((n, m)).unwrap(); let b = ArcArray::eye(m); assert_eq!(a.dot(&b), a); let c = ArcArray::eye(n); @@ -542,7 +542,7 @@ fn test_add() { #[test] fn test_multidim() { - let mut mat = ArcArray::zeros(2 * 3 * 4 * 5 * 6).reshape((2, 3, 4, 5, 6)); + let mut mat = ArcArray::zeros(2 * 3 * 4 * 5 * 6).into_shape_with_order((2, 3, 4, 5, 6)).unwrap(); mat[(0, 0, 0, 0, 0)] = 22u8; { for (i, elt) in mat.iter_mut().enumerate() { @@ -604,7 +604,7 @@ fn test_cow() { assert_eq!(n[[0, 0]], 1); assert_eq!(n[[0, 1]], 0); assert_eq!(n.get((0, 1)), Some(&0)); - let mut rev = mat.reshape(4); + let mut rev = mat.into_shape_with_order(4).unwrap(); rev.slice_collapse(s![..;-1]); assert_eq!(rev[0], 4); assert_eq!(rev[1], 3); @@ -643,7 +643,7 @@ fn test_cow_shrink() { assert_eq!(n[[0, 1]], 0); assert_eq!(n.get((0, 1)), Some(&0)); // small has non-C strides this way - let mut small = mat.reshape(6); + let mut small = mat.into_shape_with_order(6).unwrap(); small.slice_collapse(s![4..;-1]); assert_eq!(small[0], 6); assert_eq!(small[1], 5); @@ -660,14 +660,14 @@ fn test_cow_shrink() { #[test] #[cfg(feature = "std")] fn test_sub() { - let mat = ArcArray::linspace(0., 15., 16).reshape((2, 4, 2)); + let mat = ArcArray::linspace(0., 15., 16).into_shape_with_order((2, 4, 2)).unwrap(); let s1 = mat.index_axis(Axis(0), 0); let s2 = mat.index_axis(Axis(0), 1); assert_eq!(s1.shape(), &[4, 2]); assert_eq!(s2.shape(), &[4, 2]); - let n = ArcArray::linspace(8., 15., 8).reshape((4, 2)); + let n = ArcArray::linspace(8., 15., 8).into_shape_with_order((4, 2)).unwrap(); assert_eq!(n, s2); - let m = ArcArray::from(vec![2., 3., 10., 11.]).reshape((2, 2)); + let m = ArcArray::from(vec![2., 3., 10., 11.]).into_shape_with_order((2, 2)).unwrap(); assert_eq!(m, mat.index_axis(Axis(1), 1)); } @@ -675,7 +675,7 @@ fn test_sub() { #[test] #[cfg(feature = "std")] fn test_sub_oob_1() { - let mat = ArcArray::linspace(0., 15., 16).reshape((2, 4, 2)); + let mat = ArcArray::linspace(0., 15., 16).into_shape_with_order((2, 4, 2)).unwrap(); mat.index_axis(Axis(0), 2); } @@ -1337,7 +1337,7 @@ fn from_vec_dim_stride_2d_rejects() { #[test] fn views() { - let a = ArcArray::from(vec![1, 2, 3, 4]).reshape((2, 2)); + let a = ArcArray::from(vec![1, 2, 3, 4]).into_shape_with_order((2, 2)).unwrap(); let b = a.view(); assert_eq!(a, b); assert_eq!(a.shape(), b.shape()); @@ -1354,7 +1354,7 @@ fn views() { #[test] fn view_mut() { - let mut a = ArcArray::from(vec![1, 2, 3, 4]).reshape((2, 2)); + let mut a = ArcArray::from(vec![1, 2, 3, 4]).into_shape_with_order((2, 2)).unwrap(); for elt in &mut a.view_mut() { *elt = 0; } @@ -1373,7 +1373,7 @@ fn view_mut() { #[test] fn slice_mut() { - let mut a = ArcArray::from(vec![1, 2, 3, 4]).reshape((2, 2)); + let mut a = ArcArray::from(vec![1, 2, 3, 4]).into_shape_with_order((2, 2)).unwrap(); for elt in a.slice_mut(s![.., ..]) { *elt = 0; } @@ -1680,7 +1680,7 @@ fn arithmetic_broadcast() { #[test] fn char_array() { // test compilation & basics of non-numerical array - let cc = ArcArray::from_iter("alphabet".chars()).reshape((4, 2)); + let cc = ArcArray::from_iter("alphabet".chars()).into_shape_with_order((4, 2)).unwrap(); assert!(cc.index_axis(Axis(1), 0) == ArcArray::from_iter("apae".chars())); } @@ -1740,7 +1740,7 @@ fn split_at() { } assert_eq!(a, arr2(&[[1., 5.], [8., 4.]])); - let b = ArcArray::linspace(0., 59., 60).reshape((3, 4, 5)); + let b = ArcArray::linspace(0., 59., 60).into_shape_with_order((3, 4, 5)).unwrap(); let (left, right) = b.view().split_at(Axis(2), 2); assert_eq!(left.shape(), [3, 4, 2]); diff --git a/tests/iterators.rs b/tests/iterators.rs index 8b9dfd94e..fd0716036 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -42,7 +42,7 @@ fn double_ended() { #[test] fn iter_size_hint() { // Check that the size hint is correctly computed - let a = ArcArray::from_iter(0..24).reshape((2, 3, 4)); + let a = ArcArray::from_iter(0..24).into_shape_with_order((2, 3, 4)).unwrap(); let mut data = [0; 24]; for (i, elt) in enumerate(&mut data) { *elt = i as i32; @@ -64,7 +64,7 @@ fn indexed() { for (i, elt) in a.indexed_iter() { assert_eq!(i, *elt as usize); } - let a = a.reshape((2, 4, 1)); + let a = a.into_shape_with_order((2, 4, 1)).unwrap(); let (mut i, mut j, k) = (0, 0, 0); for (idx, elt) in a.indexed_iter() { assert_eq!(idx, (i, j, k)); @@ -96,11 +96,11 @@ fn as_slice() { } let a = ArcArray::linspace(0., 7., 8); - let a = a.reshape((2, 4, 1)); + let a = a.into_shape_with_order((2, 4, 1)).unwrap(); assert_slice_correct(&a); - let a = a.reshape((2, 4)); + let a = a.into_shape_with_order((2, 4)).unwrap(); assert_slice_correct(&a); assert!(a.view().index_axis(Axis(1), 0).as_slice().is_none()); @@ -123,7 +123,7 @@ fn as_slice() { assert!(u.as_slice().is_some()); assert_slice_correct(&u); - let a = a.reshape((8, 1)); + let a = a.into_shape_with_order((8, 1)).unwrap(); assert_slice_correct(&a); let u = a.slice(s![..;2, ..]); println!( @@ -138,7 +138,7 @@ fn as_slice() { #[test] fn inner_iter() { let a = ArcArray::from_iter(0..12); - let a = a.reshape((2, 3, 2)); + let a = a.into_shape_with_order((2, 3, 2)).unwrap(); // [[[0, 1], // [2, 3], // [4, 5]], @@ -187,7 +187,7 @@ fn inner_iter_corner_cases() { #[test] fn inner_iter_size_hint() { // Check that the size hint is correctly computed - let a = ArcArray::from_iter(0..24).reshape((2, 3, 4)); + let a = ArcArray::from_iter(0..24).into_shape_with_order((2, 3, 4)).unwrap(); let mut len = 6; let mut it = a.rows().into_iter(); assert_eq!(it.len(), len); @@ -202,7 +202,7 @@ fn inner_iter_size_hint() { #[test] fn outer_iter() { let a = ArcArray::from_iter(0..12); - let a = a.reshape((2, 3, 2)); + let a = a.into_shape_with_order((2, 3, 2)).unwrap(); // [[[0, 1], // [2, 3], // [4, 5]], @@ -261,7 +261,7 @@ fn outer_iter() { #[test] fn axis_iter() { let a = ArcArray::from_iter(0..12); - let a = a.reshape((2, 3, 2)); + let a = a.into_shape_with_order((2, 3, 2)).unwrap(); // [[[0, 1], // [2, 3], // [4, 5]], @@ -353,7 +353,7 @@ fn outer_iter_corner_cases() { #[test] fn outer_iter_mut() { let a = ArcArray::from_iter(0..12); - let a = a.reshape((2, 3, 2)); + let a = a.into_shape_with_order((2, 3, 2)).unwrap(); // [[[0, 1], // [2, 3], // [4, 5]], @@ -380,7 +380,7 @@ fn outer_iter_mut() { #[test] fn axis_iter_mut() { let a = ArcArray::from_iter(0..12); - let a = a.reshape((2, 3, 2)); + let a = a.into_shape_with_order((2, 3, 2)).unwrap(); // [[[0, 1], // [2, 3], // [4, 5]], @@ -400,7 +400,7 @@ fn axis_iter_mut() { #[test] fn axis_chunks_iter() { let a = ArcArray::from_iter(0..24); - let a = a.reshape((2, 6, 2)); + let a = a.into_shape_with_order((2, 6, 2)).unwrap(); let it = a.axis_chunks_iter(Axis(1), 2); assert_equal( @@ -413,7 +413,7 @@ fn axis_chunks_iter() { ); let a = ArcArray::from_iter(0..28); - let a = a.reshape((2, 7, 2)); + let a = a.into_shape_with_order((2, 7, 2)).unwrap(); let it = a.axis_chunks_iter(Axis(1), 2); assert_equal( @@ -535,7 +535,7 @@ fn axis_chunks_iter_corner_cases() { // and enable checking if no pointer offsetting is out of bounds. However // checking the absence of of out of bounds offsetting cannot (?) be // done automatically, so one has to launch this test in a debugger. - let a = ArcArray::::linspace(0., 7., 8).reshape((8, 1)); + let a = ArcArray::::linspace(0., 7., 8).into_shape_with_order((8, 1)).unwrap(); let it = a.axis_chunks_iter(Axis(0), 4); assert_equal(it, vec![a.slice(s![..4, ..]), a.slice(s![4.., ..])]); let a = a.slice(s![..;-1,..]); @@ -634,7 +634,7 @@ fn axis_chunks_iter_split_at() { #[test] fn axis_chunks_iter_mut() { let a = ArcArray::from_iter(0..24); - let mut a = a.reshape((2, 6, 2)); + let mut a = a.into_shape_with_order((2, 6, 2)).unwrap(); let mut it = a.axis_chunks_iter_mut(Axis(1), 2); let mut col0 = it.next().unwrap(); @@ -658,7 +658,7 @@ fn axis_chunks_iter_mut_zero_axis_len() { #[test] fn outer_iter_size_hint() { // Check that the size hint is correctly computed - let a = ArcArray::from_iter(0..24).reshape((4, 3, 2)); + let a = ArcArray::from_iter(0..24).into_shape_with_order((4, 3, 2)).unwrap(); let mut len = 4; let mut it = a.outer_iter(); assert_eq!(it.len(), len); @@ -690,7 +690,7 @@ fn outer_iter_size_hint() { #[test] fn outer_iter_split_at() { - let a = ArcArray::from_iter(0..30).reshape((5, 3, 2)); + let a = ArcArray::from_iter(0..30).into_shape_with_order((5, 3, 2)).unwrap(); let it = a.outer_iter(); let (mut itl, mut itr) = it.clone().split_at(2); @@ -712,7 +712,7 @@ fn outer_iter_split_at() { #[test] #[should_panic] fn outer_iter_split_at_panics() { - let a = ArcArray::from_iter(0..30).reshape((5, 3, 2)); + let a = ArcArray::from_iter(0..30).into_shape_with_order((5, 3, 2)).unwrap(); let it = a.outer_iter(); it.split_at(6); @@ -720,7 +720,7 @@ fn outer_iter_split_at_panics() { #[test] fn outer_iter_mut_split_at() { - let mut a = ArcArray::from_iter(0..30).reshape((5, 3, 2)); + let mut a = ArcArray::from_iter(0..30).into_shape_with_order((5, 3, 2)).unwrap(); { let it = a.outer_iter_mut(); diff --git a/xtest-serialization/tests/serialize.rs b/xtest-serialization/tests/serialize.rs index e0e6f1d42..cea84dd7f 100644 --- a/xtest-serialization/tests/serialize.rs +++ b/xtest-serialization/tests/serialize.rs @@ -45,7 +45,7 @@ fn serial_many_dim_serde() { { // Test a sliced array. - let mut a = ArcArray::linspace(0., 31., 32).reshape((2, 2, 2, 4)); + let mut a = ArcArray::linspace(0., 31., 32).into_shape_with_order((2, 2, 2, 4)).unwrap(); a.slice_collapse(s![..;-1, .., .., ..2]); let serial = serde_json::to_string(&a).unwrap(); println!("Encode {:?} => {:?}", a, serial); @@ -155,7 +155,7 @@ fn serial_many_dim_serde_msgpack() { { // Test a sliced array. - let mut a = ArcArray::linspace(0., 31., 32).reshape((2, 2, 2, 4)); + let mut a = ArcArray::linspace(0., 31., 32).into_shape_with_order((2, 2, 2, 4)).unwrap(); a.slice_collapse(s![..;-1, .., .., ..2]); let mut buf = Vec::new(); @@ -208,7 +208,7 @@ fn serial_many_dim_ron() { { // Test a sliced array. - let mut a = ArcArray::linspace(0., 31., 32).reshape((2, 2, 2, 4)); + let mut a = ArcArray::linspace(0., 31., 32).into_shape_with_order((2, 2, 2, 4)).unwrap(); a.slice_collapse(s![..;-1, .., .., ..2]); let a_s = ron_serialize(&a).unwrap(); From a5a4b9819d442d2d5b41bbf003d7f02a62e23fda Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 9 Mar 2024 11:49:47 +0100 Subject: [PATCH 537/651] shape: More explicit examples for to_shape and into_shape --- src/impl_methods.rs | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index f07ecb43a..0f97aeb8d 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1815,9 +1815,20 @@ where /// number of rows and columns (or more axes if applicable), it is important to pick an index /// ordering, and that's the reason for the function parameter for `order`. /// + /// The `new_shape` parameter should be a dimension and an optional order like these examples: + /// + /// ```text + /// (3, 4) // Shape 3 x 4 with default order (RowMajor) + /// ((3, 4), Order::RowMajor)) // use specific order + /// ((3, 4), Order::ColumnMajor)) // use specific order + /// ((3, 4), Order::C)) // use shorthand for order - shorthands C and F + /// ``` + /// /// **Errors** if the new shape doesn't have the same number of elements as the array's current /// shape. /// + /// # Example + /// /// ``` /// use ndarray::array; /// use ndarray::Order; @@ -1890,10 +1901,10 @@ where /// /// If an index ordering is not specified, the default is `RowMajor`. /// The operation will only succeed if the array's memory layout is compatible with - /// the index ordering. + /// the index ordering, so that the array elements can be rearranged in place. /// - /// Use `.to_shape()` instead for more flexible reshaping of arrays, which - /// allows copying elements if required. + /// If required use `.to_shape()` or `.into_shape_clone` instead for more flexible reshaping of + /// arrays, which allows copying elements if required. /// /// **Errors** if the shapes don't have the same number of elements.
/// **Errors** if order RowMajor is given but input is not c-contiguous. @@ -1903,6 +1914,17 @@ where /// reshaped using row major index ordering, column major arrays with column major index /// ordering. /// + /// The `new_shape` parameter should be a dimension and an optional order like these examples: + /// + /// ```text + /// (3, 4) // Shape 3 x 4 with default order (RowMajor) + /// ((3, 4), Order::RowMajor)) // use specific order + /// ((3, 4), Order::ColumnMajor)) // use specific order + /// ((3, 4), Order::C)) // use shorthand for order - shorthands C and F + /// ``` + /// + /// # Example + /// /// ``` /// use ndarray::{aview1, aview2}; /// use ndarray::Order; @@ -1960,7 +1982,7 @@ where /// is C-contig or F-contig, it follows the index order that corresponds to the memory order. /// Prefer to use `.to_shape()` or `.into_shape_with_order()`. /// - /// Because of this, the method is deprecated. That reshapes depend on memory order is not + /// Because of this, the method **is deprecated**. That reshapes depend on memory order is not /// intuitive. /// /// **Errors** if the shapes don't have the same number of elements.
From ee7f4351656e07a4c8406a354fa795486d1f70e9 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 9 Mar 2024 12:13:43 +0100 Subject: [PATCH 538/651] shape: Add test for into_shape_clone --- tests/reshape.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/reshape.rs b/tests/reshape.rs index 7d9bab55c..24e7d01f8 100644 --- a/tests/reshape.rs +++ b/tests/reshape.rs @@ -268,3 +268,51 @@ fn into_shape_with_order() { assert_eq!(s.shape(), &[4, 2]); assert_eq!(s, array![[1, 5], [2, 6], [3, 7], [4, 8]]); } + +#[test] +fn into_shape_clone() { + // 1D -> C -> C + { + let data = [1, 2, 3, 4, 5, 6, 7, 8]; + let v = Array::from(data.to_vec()); + let u = v.clone().into_shape_clone(((3, 3), Order::RowMajor)); + assert!(u.is_err()); + + let u = v.clone().into_shape_clone(((2, 2, 2), Order::C)); + assert!(u.is_ok()); + + let u = u.unwrap(); + assert_eq!(u.shape(), &[2, 2, 2]); + assert_eq!(u, array![[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); + + let s = u.into_shape_clone((4, 2)).unwrap(); + assert_eq!(s.shape(), &[4, 2]); + assert_eq!(s, aview2(&[[1, 2], [3, 4], [5, 6], [7, 8]])); + + let u = v.clone().into_shape_clone(((2, 2, 2), Order::F)); + assert!(u.is_ok()); + + let u = u.unwrap(); + assert_eq!(u.shape(), &[2, 2, 2]); + assert_eq!(u, array![[[1, 5], [3, 7]], [[2, 6], [4, 8]]]); + } + + // 1D -> F -> F + { + let data = [1, 2, 3, 4, 5, 6, 7, 8]; + let v = Array::from(data.to_vec()); + let u = v.clone().into_shape_clone(((3, 3), Order::ColumnMajor)); + assert!(u.is_err()); + + let u = v.into_shape_clone(((2, 2, 2), Order::ColumnMajor)); + assert!(u.is_ok()); + + let u = u.unwrap(); + assert_eq!(u.shape(), &[2, 2, 2]); + assert_eq!(u, array![[[1, 5], [3, 7]], [[2, 6], [4, 8]]]); + + let s = u.into_shape_clone(((4, 2), Order::ColumnMajor)).unwrap(); + assert_eq!(s.shape(), &[4, 2]); + assert_eq!(s, array![[1, 5], [2, 6], [3, 7], [4, 8]]); + } +} From 1fbe2e609d97a9d66a5725d49000c67dcfec6c6e Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 7 Dec 2021 01:22:02 -0500 Subject: [PATCH 539/651] Update MSRV to 1.57 --- .github/workflows/ci.yaml | 2 +- Cargo.toml | 2 +- scripts/all-tests.sh | 2 +- src/lib.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7e9834087..d0a0c0118 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -34,7 +34,7 @@ jobs: - stable - beta - nightly - - 1.51.0 # MSRV + - 1.57.0 # MSRV name: tests/${{ matrix.rust }} steps: diff --git a/Cargo.toml b/Cargo.toml index a648b09bc..ae9e33f06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "ndarray" version = "0.15.6" edition = "2018" -rust-version = "1.51" +rust-version = "1.57" authors = [ "Ulrik Sverdrup \"bluss\"", "Jim Turner" diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index 81d240ba2..cbea6dba7 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -6,7 +6,7 @@ set -e FEATURES=$1 CHANNEL=$2 -if [ "$CHANNEL" = "1.51.0" ]; then +if [ "$CHANNEL" = "1.57.0" ]; then cargo update --package openblas-src --precise 0.10.5 cargo update --package openblas-build --precise 0.10.5 cargo update --package once_cell --precise 1.14.0 diff --git a/src/lib.rs b/src/lib.rs index 5d3dc7003..b15b0ea88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,7 +71,7 @@ //! needs matching memory layout to be efficient (with some exceptions). //! + Efficient floating point matrix multiplication even for very large //! matrices; can optionally use BLAS to improve it further. -//! - **Requires Rust 1.51 or later** +//! - **Requires Rust 1.57 or later** //! //! ## Crate Feature Flags //! From 0e461f0de9bf07fe8b11ce48cfde59eb369113f1 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 7 Dec 2021 01:01:49 -0500 Subject: [PATCH 540/651] Make aview* free functions be const fns --- src/free_functions.rs | 92 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 83 insertions(+), 9 deletions(-) diff --git a/src/free_functions.rs b/src/free_functions.rs index e9c3abff6..342aae980 100644 --- a/src/free_functions.rs +++ b/src/free_functions.rs @@ -10,6 +10,7 @@ use alloc::vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::mem::{forget, size_of}; +use std::ptr::NonNull; use crate::imp_prelude::*; use crate::{dimension, ArcArray1, ArcArray2}; @@ -65,14 +66,24 @@ pub fn rcarr1(xs: &[A]) -> ArcArray1
{ } /// Create a zero-dimensional array view borrowing `x`. -pub fn aview0(x: &A) -> ArrayView0<'_, A> { - unsafe { ArrayView::from_shape_ptr(Ix0(), x) } +pub const fn aview0(x: &A) -> ArrayView0<'_, A> { + ArrayBase { + data: ViewRepr::new(), + // Safe because references are always non-null. + ptr: unsafe { NonNull::new_unchecked(x as *const A as *mut A) }, + dim: Ix0(), + strides: Ix0(), + } } /// Create a one-dimensional array view with elements borrowing `xs`. /// +/// **Panics** if the length of the slice overflows `isize`. (This can only +/// occur if `A` is zero-sized, because slices cannot contain more than +/// `isize::MAX` number of bytes.) +/// /// ``` -/// use ndarray::aview1; +/// use ndarray::{aview1, ArrayView1}; /// /// let data = [1.0; 1024]; /// @@ -80,17 +91,80 @@ pub fn aview0(x: &A) -> ArrayView0<'_, A> { /// let a2d = aview1(&data).into_shape((32, 32)).unwrap(); /// /// assert_eq!(a2d.sum(), 1024.0); +/// +/// // Create a const 1D array view +/// const C: ArrayView1<'static, f64> = aview1(&[1., 2., 3.]); +/// +/// assert_eq!(C.sum(), 6.); /// ``` -pub fn aview1(xs: &[A]) -> ArrayView1<'_, A> { - ArrayView::from(xs) +pub const fn aview1(xs: &[A]) -> ArrayView1<'_, A> { + if size_of::() == 0 { + assert!( + xs.len() <= isize::MAX as usize, + "Slice length must fit in `isize`.", + ); + } + ArrayBase { + data: ViewRepr::new(), + // Safe because references are always non-null. + ptr: unsafe { NonNull::new_unchecked(xs.as_ptr() as *mut A) }, + dim: Ix1(xs.len()), + strides: Ix1(1), + } } /// Create a two-dimensional array view with elements borrowing `xs`. /// -/// **Panics** if the product of non-zero axis lengths overflows `isize` (This can only occur if A -/// is zero-sized because slices cannot contain more than `isize::MAX` number of bytes). -pub fn aview2(xs: &[[A; N]]) -> ArrayView2<'_, A> { - ArrayView2::from(xs) +/// **Panics** if the product of non-zero axis lengths overflows `isize` (This +/// can only occur if A is zero-sized or if `N` is zero, because slices cannot +/// contain more than `isize::MAX` number of bytes). +/// +/// ``` +/// use ndarray::{aview2, ArrayView2}; +/// +/// let data = vec![[1., 2., 3.], [4., 5., 6.]]; +/// +/// let view = aview2(&data); +/// assert_eq!(view.sum(), 21.); +/// +/// // Create a const 2D array view +/// const C: ArrayView2<'static, f64> = aview2(&[[1., 2., 3.], [4., 5., 6.]]); +/// assert_eq!(C.sum(), 21.); +/// ``` +pub const fn aview2(xs: &[[A; N]]) -> ArrayView2<'_, A> { + let cols = N; + let rows = xs.len(); + if size_of::() == 0 { + if let Some(n_elems) = rows.checked_mul(cols) { + assert!( + rows <= isize::MAX as usize + && cols <= isize::MAX as usize + && n_elems <= isize::MAX as usize, + "Product of non-zero axis lengths must not overflow isize.", + ); + } else { + panic!("Overflow in number of elements."); + } + } else if N == 0 { + assert!( + rows <= isize::MAX as usize, + "Product of non-zero axis lengths must not overflow isize.", + ); + } + // Safe because references are always non-null. + let ptr = unsafe { NonNull::new_unchecked(xs.as_ptr() as *mut A) }; + let dim = Ix2(rows, cols); + let strides = if rows == 0 || cols == 0 { + Ix2(0, 0) + } else { + Ix2(cols, 1) + }; + ArrayBase { + data: ViewRepr::new(), + ptr, + dim, + strides, + } } /// Create a one-dimensional read-write array view with elements borrowing `xs`. From cab158f9a64eea0054d6020991b7eb0b521aae94 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 7 Dec 2021 01:02:10 -0500 Subject: [PATCH 541/651] Remove redundant code --- src/arraytraits.rs | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 39a82b1ae..8d44c1e72 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -307,6 +307,10 @@ pub const ARRAY_FORMAT_VERSION: u8 = 1u8; // use "raw" form instead of type aliases here so that they show up in docs /// Implementation of `ArrayView::from(&S)` where `S` is a slice or sliceable. +/// +/// **Panics** if the length of the slice overflows `isize`. (This can only +/// occur if `A` is zero-sized, because slices cannot contain more than +/// `isize::MAX` number of bytes.) impl<'a, A, Slice: ?Sized> From<&'a Slice> for ArrayView<'a, A, Ix1> where Slice: AsRef<[A]>, @@ -315,14 +319,7 @@ where /// /// **Panics** if the slice length is greater than `isize::MAX`. fn from(slice: &'a Slice) -> Self { - let xs = slice.as_ref(); - if mem::size_of::() == 0 { - assert!( - xs.len() <= ::std::isize::MAX as usize, - "Slice length must fit in `isize`.", - ); - } - unsafe { Self::from_shape_ptr(xs.len(), xs.as_ptr()) } + aview1(slice.as_ref()) } } @@ -334,25 +331,7 @@ where impl<'a, A, const N: usize> From<&'a [[A; N]]> for ArrayView<'a, A, Ix2> { /// Create a two-dimensional read-only array view of the data in `slice` fn from(xs: &'a [[A; N]]) -> Self { - let cols = N; - let rows = xs.len(); - let dim = Ix2(rows, cols); - if size_of::() == 0 { - dimension::size_of_shape_checked(&dim) - .expect("Product of non-zero axis lengths must not overflow isize."); - } else if N == 0 { - assert!( - xs.len() <= isize::MAX as usize, - "Product of non-zero axis lengths must not overflow isize.", - ); - } - - // `cols * rows` is guaranteed to fit in `isize` because we checked that it fits in - // `isize::MAX` - unsafe { - let data = slice::from_raw_parts(xs.as_ptr() as *const A, cols * rows); - ArrayView::from_shape_ptr(dim, data.as_ptr()) - } + aview2(xs) } } From d32248df363590190452a1c70491bca357ae772b Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 10 Mar 2024 11:18:54 +0100 Subject: [PATCH 542/651] Apply examples changes from code review Co-authored-by: Adam Reichold --- README-quick-start.md | 3 +-- src/impl_methods.rs | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/README-quick-start.md b/README-quick-start.md index b3783032f..ad13acc72 100644 --- a/README-quick-start.md +++ b/README-quick-start.md @@ -295,8 +295,7 @@ row: [[100, 101, 102], ## Shape Manipulation ### Changing the shape of an array -The shape of an array can be changed with `into_shape_with_order` method. -(One can also use `to_shape` for this.) +The shape of an array can be changed with the `into_shape_with_order` or `to_shape` method. ````rust use ndarray::prelude::*; diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 0f97aeb8d..caeed2254 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1978,7 +1978,7 @@ where /// elements is accepted, but the source array or view must be in standard /// or column-major (Fortran) layout. /// - /// **Note** that `.into_shape_with_order()` "moves" elements differently depending on if the input array + /// **Note** that `.into_shape()` "moves" elements differently depending on if the input array /// is C-contig or F-contig, it follows the index order that corresponds to the memory order. /// Prefer to use `.to_shape()` or `.into_shape_with_order()`. /// @@ -1992,7 +1992,7 @@ where /// use ndarray::{aview1, aview2}; /// /// assert!( - /// aview1(&[1., 2., 3., 4.]).into_shape_with_order((2, 2)).unwrap() + /// aview1(&[1., 2., 3., 4.]).into_shape((2, 2)).unwrap() /// == aview2(&[[1., 2.], /// [3., 4.]]) /// ); @@ -2117,7 +2117,6 @@ where A: Clone, E: IntoDimension, { - //return self.clone().into_shape_clone(shape).unwrap(); let shape = shape.into_dimension(); if size_of_shape_checked(&shape) != Ok(self.dim.size()) { panic!( From d517bef5af0389608f0b328110e7e1a1be6ef1fb Mon Sep 17 00:00:00 2001 From: Nil Goyette Date: Mon, 7 Nov 2022 20:55:07 -0500 Subject: [PATCH 543/651] Add Zip::any --- src/zip/mod.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/zip/mod.rs b/src/zip/mod.rs index cc7a4187d..7c3f10b10 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -670,6 +670,33 @@ macro_rules! map_impl { }).is_done() } + /// Tests if at least one element of the iterator matches a predicate. + /// + /// Returns `true` if `predicate` evaluates to `true` for at least one element. + /// Returns `false` if the input arrays are empty. + /// + /// Example: + /// + /// ``` + /// use ndarray::{array, Zip}; + /// let a = array![1, 2, 3]; + /// let b = array![1, 4, 9]; + /// assert!(Zip::from(&a).and(&b).any(|&a, &b| a == b)); + /// assert!(!Zip::from(&a).and(&b).any(|&a, &b| a - 1 == b)); + /// ``` + pub fn any(mut self, mut predicate: F) -> bool + where F: FnMut($($p::Item),*) -> bool + { + self.for_each_core((), move |_, args| { + let ($($p,)*) = args; + if predicate($($p),*) { + FoldWhile::Done(()) + } else { + FoldWhile::Continue(()) + } + }).is_done() + } + expand_if!(@bool [$notlast] /// Include the producer `p` in the Zip. From 2033b7630ce100f05110ad68229afc49e5475eb9 Mon Sep 17 00:00:00 2001 From: Piet Hessenius Date: Tue, 29 Nov 2022 21:04:58 +0100 Subject: [PATCH 544/651] implement `DoubleEndedIterator` for 1d lanes This is especially useful when one wants to iterate over rows or columns of a 2d array in reverse. Co-authored-by: Ulrik Sverdrup --- src/iterators/mod.rs | 18 ++++++++++++++++++ tests/iterators.rs | 16 ++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 49c9c1887..35b2d725f 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -732,6 +732,15 @@ where } } +impl<'a, A> DoubleEndedIterator for LanesIter<'a, A, Ix1> +{ + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|ptr| unsafe { + ArrayView::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) + }) + } +} + // NOTE: LanesIterMut is a mutable iterator and must not expose aliasing // pointers. Due to this we use an empty slice for the raw data (it's unused // anyway). @@ -772,6 +781,15 @@ where } } +impl<'a, A> DoubleEndedIterator for LanesIterMut<'a, A, Ix1> +{ + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|ptr| unsafe { + ArrayViewMut::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) + }) + } +} + #[derive(Debug)] pub struct AxisIterCore { /// Index along the axis of the value of `.next()`, relative to the start diff --git a/tests/iterators.rs b/tests/iterators.rs index fd0716036..b2cd58ecf 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -39,6 +39,22 @@ fn double_ended() { assert_equal(aview1(&[1, 2, 3]).into_iter().rev(), [1, 2, 3].iter().rev()); } +#[test] +fn double_ended_rows() { + let a = ArcArray::from_iter(0..8).into_shape_clone((4, 2)).unwrap(); + let mut row_it = a.rows().into_iter(); + assert_equal(row_it.next_back().unwrap(), &[6, 7]); + assert_equal(row_it.next().unwrap(), &[0, 1]); + assert_equal(row_it.next_back().unwrap(), &[4, 5]); + assert_equal(row_it.next_back().unwrap(), &[2, 3]); + assert!(row_it.next().is_none()); + assert!(row_it.next_back().is_none()); + + for (row, check) in a.rows().into_iter().rev().zip(&[[6, 7], [4, 5], [2, 3], [0, 1]]) { + assert_equal(row, check); + } +} + #[test] fn iter_size_hint() { // Check that the size hint is correctly computed From a90146a49d70cb2273d62e230db47143d791757c Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Thu, 18 Feb 2021 15:12:59 -0500 Subject: [PATCH 545/651] Add docs to CI This is primarily to detect broken intra-doc links, which `cargo doc` warns about. --- .github/workflows/ci.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d0a0c0118..1bca099aa 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -85,12 +85,28 @@ jobs: - run: cargo careful test -Zcareful-sanitizer --features="$FEATURES" - run: cargo careful test -Zcareful-sanitizer -p ndarray-rand + docs: + if: ${{ github.event_name == 'merge_group' }} + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - stable + name: docs/${{ matrix.rust }} + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + - run: cargo doc + conclusion: needs: - clippy - tests - cross_test - cargo-careful + - docs if: always() runs-on: ubuntu-latest steps: From 20844f1e9edfa91b40b0cf4bda7082b400c4f34d Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 24 Sep 2022 18:27:18 -0400 Subject: [PATCH 546/651] Add slice_axis_move method --- src/impl_methods.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index caeed2254..061e9e117 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -656,6 +656,16 @@ where debug_assert!(self.pointer_is_inbounds()); } + /// Slice the array in place along the specified axis, then return the sliced array. + /// + /// **Panics** if an index is out of bounds or step size is zero.
+ /// **Panics** if `axis` is out of bounds. + #[must_use = "slice_axis_move returns an array with the sliced result"] + pub fn slice_axis_move(mut self, axis: Axis, indices: Slice) -> Self { + self.slice_axis_inplace(axis, indices); + self + } + /// Return a view of a slice of the array, with a closure specifying the /// slice for each axis. /// From 2cb374af083be088e838bf8e51919b25e5a70f01 Mon Sep 17 00:00:00 2001 From: xd009642 Date: Wed, 7 Apr 2021 09:12:43 +0400 Subject: [PATCH 547/651] Track-caller all the panics --- src/dimension/mod.rs | 5 +++++ src/impl_2d.rs | 4 ++++ src/impl_dyn.rs | 2 ++ src/impl_methods.rs | 41 +++++++++++++++++++++++++++++++++++++ src/impl_ops.rs | 5 +++++ src/impl_raw_views.rs | 8 ++++++-- src/impl_views/indexing.rs | 4 ++++ src/impl_views/splitting.rs | 3 +++ src/iterators/mod.rs | 5 +++++ src/linalg/impl_linalg.rs | 8 ++++++++ src/numeric/impl_numeric.rs | 6 ++++++ src/zip/mod.rs | 4 ++++ 12 files changed, 93 insertions(+), 2 deletions(-) diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 6755efeb7..76a3984fe 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -310,11 +310,13 @@ pub trait DimensionExt { /// Get the dimension at `axis`. /// /// *Panics* if `axis` is out of bounds. + #[track_caller] fn axis(&self, axis: Axis) -> Ix; /// Set the dimension at `axis`. /// /// *Panics* if `axis` is out of bounds. + #[track_caller] fn set_axis(&mut self, axis: Axis, value: Ix); } @@ -349,6 +351,7 @@ impl DimensionExt for [Ix] { /// available. /// /// **Panics** if `index` is larger than the size of the axis +#[track_caller] // FIXME: Move to Dimension trait pub fn do_collapse_axis( dims: &mut D, @@ -385,6 +388,7 @@ pub fn abs_index(len: Ix, index: Ixs) -> Ix { /// The return value is (start, end, step). /// /// **Panics** if stride is 0 or if any index is out of bounds. +#[track_caller] fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize) { let Slice { start, end, step } = slice; let start = abs_index(axis_len, start); @@ -426,6 +430,7 @@ pub fn offset_from_low_addr_ptr_to_logical_ptr(dim: &D, strides: & /// Modify dimension, stride and return data pointer offset /// /// **Panics** if stride is 0 or if any index is out of bounds. +#[track_caller] pub fn do_slice(dim: &mut usize, stride: &mut usize, slice: Slice) -> isize { let (start, end, step) = to_abs_slice(*dim, slice); diff --git a/src/impl_2d.rs b/src/impl_2d.rs index 6ef15c501..cd5cf7e5c 100644 --- a/src/impl_2d.rs +++ b/src/impl_2d.rs @@ -23,6 +23,7 @@ where /// let array = array![[1., 2.], [3., 4.]]; /// assert_eq!(array.row(0), array![1., 2.]); /// ``` + #[track_caller] pub fn row(&self, index: Ix) -> ArrayView1<'_, A> where S: Data, @@ -40,6 +41,7 @@ where /// array.row_mut(0)[1] = 5.; /// assert_eq!(array, array![[1., 5.], [3., 4.]]); /// ``` + #[track_caller] pub fn row_mut(&mut self, index: Ix) -> ArrayViewMut1<'_, A> where S: DataMut, @@ -77,6 +79,7 @@ where /// let array = array![[1., 2.], [3., 4.]]; /// assert_eq!(array.column(0), array![1., 3.]); /// ``` + #[track_caller] pub fn column(&self, index: Ix) -> ArrayView1<'_, A> where S: Data, @@ -94,6 +97,7 @@ where /// array.column_mut(0)[1] = 5.; /// assert_eq!(array, array![[1., 2.], [5., 4.]]); /// ``` + #[track_caller] pub fn column_mut(&mut self, index: Ix) -> ArrayViewMut1<'_, A> where S: DataMut, diff --git a/src/impl_dyn.rs b/src/impl_dyn.rs index 72851cc81..d6e1c6957 100644 --- a/src/impl_dyn.rs +++ b/src/impl_dyn.rs @@ -29,6 +29,7 @@ where /// assert_eq!(a, arr3(&[[[1, 2, 3]], [[4, 5, 6]]]).into_dyn()); /// assert_eq!(a.shape(), &[2, 1, 3]); /// ``` + #[track_caller] pub fn insert_axis_inplace(&mut self, axis: Axis) { assert!(axis.index() <= self.ndim()); self.dim = self.dim.insert_axis(axis); @@ -50,6 +51,7 @@ where /// assert_eq!(a, arr1(&[2, 5]).into_dyn()); /// assert_eq!(a.shape(), &[2]); /// ``` + #[track_caller] pub fn index_axis_inplace(&mut self, axis: Axis, index: usize) { self.collapse_axis(axis, index); self.dim = self.dim.remove_axis(axis); diff --git a/src/impl_methods.rs b/src/impl_methods.rs index caeed2254..4d736c51b 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -58,6 +58,7 @@ where /// number of dimensions (axes) of the array. /// /// ***Panics*** if the axis is out of bounds. + #[track_caller] pub fn len_of(&self, axis: Axis) -> usize { self.dim[axis.index()] } @@ -138,6 +139,7 @@ where /// number of dimensions (axes) of the array. /// /// ***Panics*** if the axis is out of bounds. + #[track_caller] pub fn stride_of(&self, axis: Axis) -> isize { // strides are reinterpreted as isize self.strides[axis.index()] as isize @@ -457,6 +459,7 @@ where /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) + #[track_caller] pub fn slice(&self, info: I) -> ArrayView<'_, A, I::OutDim> where I: SliceArg, @@ -472,6 +475,7 @@ where /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) + #[track_caller] pub fn slice_mut(&mut self, info: I) -> ArrayViewMut<'_, A, I::OutDim> where I: SliceArg, @@ -503,6 +507,7 @@ where /// middle.fill(0); /// assert_eq!(a, arr2(&[[1, 0, 1], [1, 0, 1]])); /// ``` + #[track_caller] pub fn multi_slice_mut<'a, M>(&'a mut self, info: M) -> M::Output where M: MultiSliceArg<'a, A, D>, @@ -518,6 +523,7 @@ where /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) + #[track_caller] pub fn slice_move(mut self, info: I) -> ArrayBase where I: SliceArg, @@ -587,6 +593,7 @@ where /// - if [`SliceInfoElem::NewAxis`] is in `info`, e.g. if [`NewAxis`] was /// used in the [`s!`] macro /// - if `D` is `IxDyn` and `info` does not match the number of array axes + #[track_caller] pub fn slice_collapse(&mut self, info: I) where I: SliceArg, @@ -616,6 +623,7 @@ where /// /// **Panics** if an index is out of bounds or step size is zero.
/// **Panics** if `axis` is out of bounds. + #[track_caller] #[must_use = "slice_axis returns an array view with the sliced result"] pub fn slice_axis(&self, axis: Axis, indices: Slice) -> ArrayView<'_, A, D> where @@ -630,6 +638,7 @@ where /// /// **Panics** if an index is out of bounds or step size is zero.
/// **Panics** if `axis` is out of bounds. + #[track_caller] #[must_use = "slice_axis_mut returns an array view with the sliced result"] pub fn slice_axis_mut(&mut self, axis: Axis, indices: Slice) -> ArrayViewMut<'_, A, D> where @@ -644,6 +653,7 @@ where /// /// **Panics** if an index is out of bounds or step size is zero.
/// **Panics** if `axis` is out of bounds. + #[track_caller] pub fn slice_axis_inplace(&mut self, axis: Axis, indices: Slice) { let offset = do_slice( &mut self.dim.slice_mut()[axis.index()], @@ -663,6 +673,7 @@ where /// dimensionality of the array. /// /// **Panics** if an index is out of bounds or step size is zero. + #[track_caller] pub fn slice_each_axis(&self, f: F) -> ArrayView<'_, A, D> where F: FnMut(AxisDescription) -> Slice, @@ -680,6 +691,7 @@ where /// dimensionality of the array. /// /// **Panics** if an index is out of bounds or step size is zero. + #[track_caller] pub fn slice_each_axis_mut(&mut self, f: F) -> ArrayViewMut<'_, A, D> where F: FnMut(AxisDescription) -> Slice, @@ -697,6 +709,7 @@ where /// dimensionality of the array. /// /// **Panics** if an index is out of bounds or step size is zero. + #[track_caller] pub fn slice_each_axis_inplace(&mut self, mut f: F) where F: FnMut(AxisDescription) -> Slice, @@ -853,6 +866,7 @@ where /// Indices may be equal. /// /// ***Panics*** if an index is out of bounds. + #[track_caller] pub fn swap(&mut self, index1: I, index2: I) where S: DataMut, @@ -933,6 +947,7 @@ where /// a.index_axis(Axis(1), 1) == ArrayView::from(&[2., 4., 6.]) /// ); /// ``` + #[track_caller] pub fn index_axis(&self, axis: Axis, index: usize) -> ArrayView<'_, A, D::Smaller> where S: Data, @@ -965,6 +980,7 @@ where /// [3., 14.]]) /// ); /// ``` + #[track_caller] pub fn index_axis_mut(&mut self, axis: Axis, index: usize) -> ArrayViewMut<'_, A, D::Smaller> where S: DataMut, @@ -978,6 +994,7 @@ where /// See [`.index_axis()`](Self::index_axis) and [*Subviews*](#subviews) for full documentation. /// /// **Panics** if `axis` or `index` is out of bounds. + #[track_caller] pub fn index_axis_move(mut self, axis: Axis, index: usize) -> ArrayBase where D: RemoveAxis, @@ -994,6 +1011,7 @@ where /// Selects `index` along the axis, collapsing the axis into length one. /// /// **Panics** if `axis` or `index` is out of bounds. + #[track_caller] pub fn collapse_axis(&mut self, axis: Axis, index: usize) { let offset = dimension::do_collapse_axis(&mut self.dim, &self.strides, axis.index(), index); self.ptr = unsafe { self.ptr.offset(offset) }; @@ -1021,6 +1039,7 @@ where /// [6., 7.]]) ///); /// ``` + #[track_caller] pub fn select(&self, axis: Axis, indices: &[Ix]) -> Array where A: Clone, @@ -1286,6 +1305,7 @@ where /// **Panics** if `axis` is out of bounds. /// /// + #[track_caller] pub fn axis_iter(&self, axis: Axis) -> AxisIter<'_, A, D::Smaller> where S: Data, @@ -1301,6 +1321,7 @@ where /// (read-write array view). /// /// **Panics** if `axis` is out of bounds. + #[track_caller] pub fn axis_iter_mut(&mut self, axis: Axis) -> AxisIterMut<'_, A, D::Smaller> where S: DataMut, @@ -1335,6 +1356,7 @@ where /// assert_eq!(iter.next_back().unwrap(), arr3(&[[[12, 13]], /// [[26, 27]]])); /// ``` + #[track_caller] pub fn axis_chunks_iter(&self, axis: Axis, size: usize) -> AxisChunksIter<'_, A, D> where S: Data, @@ -1348,6 +1370,7 @@ where /// Iterator element is `ArrayViewMut` /// /// **Panics** if `axis` is out of bounds or if `size` is zero. + #[track_caller] pub fn axis_chunks_iter_mut(&mut self, axis: Axis, size: usize) -> AxisChunksIterMut<'_, A, D> where S: DataMut, @@ -1366,6 +1389,7 @@ where /// **Panics** if any dimension of `chunk_size` is zero
/// (**Panics** if `D` is `IxDyn` and `chunk_size` does not match the /// number of array axes.) + #[track_caller] pub fn exact_chunks(&self, chunk_size: E) -> ExactChunks<'_, A, D> where E: IntoDimension, @@ -1406,6 +1430,7 @@ where /// [6, 6, 7, 7, 8, 8, 0], /// [6, 6, 7, 7, 8, 8, 0]])); /// ``` + #[track_caller] pub fn exact_chunks_mut(&mut self, chunk_size: E) -> ExactChunksMut<'_, A, D> where E: IntoDimension, @@ -1420,6 +1445,7 @@ where /// that fit into the array's shape. /// /// This is essentially equivalent to [`.windows_with_stride()`] with unit stride. + #[track_caller] pub fn windows(&self, window_size: E) -> Windows<'_, A, D> where E: IntoDimension, @@ -1472,6 +1498,7 @@ where /// ┃ a₂₀ ┃ a₂₁ ┃ │ │ │ │ ┃ a₂₂ ┃ a₂₃ ┃ /// ┗━━━━━┻━━━━━┹─────┴─────┘ └─────┴─────┺━━━━━┻━━━━━┛ /// ``` + #[track_caller] pub fn windows_with_stride(&self, window_size: E, stride: E) -> Windows<'_, A, D> where E: IntoDimension, @@ -2110,6 +2137,7 @@ where /// [3., 4.]]) /// ); /// ``` + #[track_caller] #[deprecated(note="Obsolete, use `to_shape` or `into_shape_with_order` instead.", since="0.15.2")] pub fn reshape(&self, shape: E) -> ArrayBase where @@ -2335,6 +2363,7 @@ where /// a == arr2(&[[1.], [2.], [3.]]) /// ); /// ``` + #[track_caller] pub fn swap_axes(&mut self, ax: usize, bx: usize) { self.dim.slice_mut().swap(ax, bx); self.strides.slice_mut().swap(ax, bx); @@ -2362,6 +2391,7 @@ where /// let b = Array3::::zeros((1, 2, 3)); /// assert_eq!(b.permuted_axes([1, 0, 2]).shape(), &[2, 1, 3]); /// ``` + #[track_caller] pub fn permuted_axes(self, axes: T) -> ArrayBase where T: IntoDimension, @@ -2435,6 +2465,7 @@ where /// Reverse the stride of `axis`. /// /// ***Panics*** if the axis is out of bounds. + #[track_caller] pub fn invert_axis(&mut self, axis: Axis) { unsafe { let s = self.strides.axis(axis) as Ixs; @@ -2481,6 +2512,7 @@ where /// ``` /// /// ***Panics*** if an axis is out of bounds. + #[track_caller] pub fn merge_axes(&mut self, take: Axis, into: Axis) -> bool { merge_axes(&mut self.dim, &mut self.strides, take, into) } @@ -2506,6 +2538,7 @@ where /// ``` /// /// ***Panics*** if the axis is out of bounds. + #[track_caller] pub fn insert_axis(self, axis: Axis) -> ArrayBase { assert!(axis.index() <= self.ndim()); // safe because a new axis of length one does not affect memory layout @@ -2522,6 +2555,7 @@ where /// axis to remove is of length 1. /// /// **Panics** if the axis is out of bounds or its length is zero. + #[track_caller] pub fn remove_axis(self, axis: Axis) -> ArrayBase where D: RemoveAxis, @@ -2538,6 +2572,7 @@ where /// If their shapes disagree, `rhs` is broadcast to the shape of `self`. /// /// **Panics** if broadcasting isn’t possible. + #[track_caller] pub fn assign(&mut self, rhs: &ArrayBase) where S: DataMut, @@ -2553,6 +2588,7 @@ where /// [`AssignElem`] determines how elements are assigned. /// /// **Panics** if shapes disagree. + #[track_caller] pub fn assign_to

(self, p: P) -> Zip<($($p,)* P::Output, ), D> where P: IntoNdProducer, { @@ -735,6 +738,7 @@ macro_rules! map_impl { /// If their shapes disagree, `rhs` is broadcast to the shape of `self`. /// /// ***Panics*** if broadcasting isn’t possible. + #[track_caller] pub fn and_broadcast<'a, P, D2, Elem>(self, p: P) -> Zip<($($p,)* ArrayView<'a, Elem, D>, ), D> where P: IntoNdProducer, Item=&'a Elem>, From f700e19e32822395aa1000753db8887f58c2b523 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 12 Mar 2024 18:10:37 +0100 Subject: [PATCH 548/651] rustfmt: Add rustfmt.toml --- rustfmt.toml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 rustfmt.toml diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..f3e376ccc --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,26 @@ +edition = "2018" +array_width = 100 +chain_width = 60 +fn_call_width = 100 +max_width = 120 +brace_style = "AlwaysNextLine" +control_brace_style = "AlwaysSameLine" +fn_params_layout = "Compressed" # ? +format_macro_bodies = false +imports_granularity = "Preserve" +imports_indent = "Block" +imports_layout = "HorizontalVertical" +inline_attribute_width = 0 +indent_style = "Block" +match_arm_blocks = false +match_arm_leading_pipes = "Preserve" +merge_derives = false +overflow_delimited_expr = true +reorder_modules = false # impacts rustdoc order +short_array_element_width_threshold = 32 +skip_macro_invocations = ["*"] +unstable_features = true +where_single_line = true + +# ignored files +ignore = [] From f642c56dbea703607c6853ae60b2773ea3ba4062 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Thu, 18 Feb 2021 13:15:24 -0500 Subject: [PATCH 549/651] rustfmt: Add rustfmt check to CI --- .github/workflows/ci.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1bca099aa..35f72acf4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -26,6 +26,22 @@ jobs: components: clippy - uses: Swatinem/rust-cache@v2 - run: cargo clippy --features docs + + format: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - nightly + name: format/${{ matrix.rust }} + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + components: rustfmt + - run: cargo fmt --all --check + tests: runs-on: ubuntu-latest strategy: @@ -103,6 +119,7 @@ jobs: conclusion: needs: - clippy + # - format # should format be required? - tests - cross_test - cargo-careful From c57115f82c198c61d444d524d75a48f0a1090c93 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 12 Mar 2024 18:38:40 +0100 Subject: [PATCH 550/651] rustfmt: Add rustfmt::skip to macro with a problem --- src/linalg/impl_linalg.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index dc8ef3428..cd2f16199 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -375,6 +375,7 @@ where #[cfg(not(feature = "blas"))] use self::mat_mul_general as mat_mul_impl; +#[rustfmt::skip] #[cfg(feature = "blas")] fn mat_mul_impl( alpha: A, From d07f5f33800e5240e7edb02bdbc4815ab30ef37e Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 12 Mar 2024 21:34:28 +0100 Subject: [PATCH 551/651] rustfmt: Apply new formatting to whole repo --- benches/append.rs | 21 +- benches/bench1.rs | 343 ++++++++------ benches/chunks.rs | 15 +- benches/construct.rs | 25 +- benches/gemv_gemm.rs | 25 +- benches/higher-order.rs | 33 +- benches/iter.rs | 126 +++-- benches/numeric.rs | 3 +- benches/par_rayon.rs | 68 +-- benches/to_shape.rs | 77 ++-- benches/zip.rs | 51 +- examples/axis_ops.rs | 8 +- examples/bounds_check_elim.rs | 26 +- examples/column_standardize.rs | 3 +- examples/convo.rs | 6 +- examples/life.rs | 20 +- examples/rollaxis.rs | 3 +- examples/sort-axis.rs | 51 +- examples/type_conversion.rs | 3 +- examples/zip_many.rs | 15 +- ndarray-rand/benches/bench.rs | 9 +- ndarray-rand/src/lib.rs | 46 +- ndarray-rand/tests/tests.rs | 33 +- src/aliases.rs | 24 +- src/argument_traits.rs | 27 +- src/array_approx.rs | 10 +- src/array_serde.rs | 84 ++-- src/arrayformat.rs | 174 +++---- src/arraytraits.rs | 107 +++-- src/data_repr.rs | 99 ++-- src/data_traits.rs | 333 ++++++++------ src/dimension/axes.rs | 51 +- src/dimension/axis.rs | 8 +- src/dimension/broadcast.rs | 52 +-- src/dimension/conversion.rs | 44 +- src/dimension/dim.rs | 33 +- src/dimension/dimension_trait.rs | 300 +++++++----- src/dimension/dynindeximpl.rs | 134 ++++-- src/dimension/mod.rs | 196 ++++---- src/dimension/ndindex.rs | 121 +++-- src/dimension/ops.rs | 9 +- src/dimension/remove_axis.rs | 15 +- src/dimension/reshape.rs | 39 +- src/dimension/sequence.rs | 82 ++-- src/error.rs | 42 +- src/extension/nonnull.rs | 8 +- src/free_functions.rs | 56 ++- src/geomspace.rs | 42 +- src/impl_1d.rs | 6 +- src/impl_2d.rs | 24 +- src/impl_clone.rs | 9 +- src/impl_constructors.rs | 64 ++- src/impl_cow.rs | 38 +- src/impl_dyn.rs | 9 +- src/impl_internal_constructors.rs | 12 +- src/impl_methods.rs | 464 +++++++++---------- src/impl_ops.rs | 27 +- src/impl_owned_array.rs | 89 ++-- src/impl_raw_views.rs | 79 ++-- src/impl_special_element_types.rs | 13 +- src/impl_views/constructors.rs | 65 +-- src/impl_views/conversions.rs | 118 ++--- src/impl_views/indexing.rs | 21 +- src/impl_views/splitting.rs | 27 +- src/indexes.rs | 111 ++--- src/iterators/chunks.rs | 30 +- src/iterators/into_iter.rs | 52 ++- src/iterators/iter.rs | 2 +- src/iterators/lanes.rs | 30 +- src/iterators/macros.rs | 4 +- src/iterators/mod.rs | 557 ++++++++++++---------- src/iterators/windows.rs | 18 +- src/itertools.rs | 3 +- src/layout/layoutfmt.rs | 6 +- src/layout/mod.rs | 61 ++- src/lib.rs | 98 ++-- src/linalg/impl_linalg.rs | 120 +++-- src/linalg/mod.rs | 2 +- src/linalg_traits.rs | 23 +- src/linspace.rs | 24 +- src/logspace.rs | 33 +- src/low_level_util.rs | 17 +- src/macro_utils.rs | 4 +- src/math_cell.rs | 89 ++-- src/numeric/impl_float_maths.rs | 6 +- src/numeric/impl_numeric.rs | 26 +- src/numeric_util.rs | 30 +- src/order.rs | 28 +- src/parallel/impl_par_methods.rs | 11 +- src/parallel/into_impls.rs | 12 +- src/parallel/mod.rs | 26 +- src/parallel/par.rs | 59 +-- src/parallel/send_producer.rs | 62 ++- src/partial.rs | 39 +- src/prelude.rs | 18 +- src/private.rs | 2 +- src/shape_builder.rs | 104 +++-- src/slice.rs | 143 +++--- src/split_at.rs | 31 +- src/stacking.rs | 16 +- src/zip/mod.rs | 108 ++--- src/zip/ndproducer.rs | 195 +++++--- tests/append.rs | 114 +++-- tests/array-construct.rs | 112 +++-- tests/array.rs | 613 +++++++++++++++---------- tests/assign.rs | 68 +-- tests/azip.rs | 98 ++-- tests/broadcast.rs | 39 +- tests/clone.rs | 3 +- tests/complex.rs | 6 +- tests/dimension.rs | 62 ++- tests/format.rs | 6 +- tests/higher_order_f.rs | 3 +- tests/indices.rs | 3 +- tests/into-ixdyn.rs | 11 +- tests/iterator_chunks.rs | 23 +- tests/iterators.rs | 315 +++++++------ tests/ix0.rs | 17 +- tests/ixdyn.rs | 44 +- tests/numeric.rs | 74 +-- tests/oper.rs | 194 ++++---- tests/par_azip.rs | 15 +- tests/par_rayon.rs | 18 +- tests/par_zip.rs | 40 +- tests/raw_views.rs | 30 +- tests/reshape.rs | 53 ++- tests/s.rs | 8 +- tests/stacking.rs | 6 +- tests/views.rs | 3 +- tests/windows.rs | 233 +++++----- tests/zst.rs | 6 +- xtest-blas/src/lib.rs | 2 - xtest-blas/tests/oper.rs | 118 ++--- xtest-numeric/src/lib.rs | 1 - xtest-numeric/tests/accuracy.rs | 102 ++-- xtest-serialization/tests/serialize.rs | 27 +- 136 files changed, 4998 insertions(+), 3834 deletions(-) diff --git a/benches/append.rs b/benches/append.rs index 1a911a278..a37df256f 100644 --- a/benches/append.rs +++ b/benches/append.rs @@ -6,30 +6,27 @@ use test::Bencher; use ndarray::prelude::*; #[bench] -fn select_axis0(bench: &mut Bencher) { +fn select_axis0(bench: &mut Bencher) +{ let a = Array::::zeros((256, 256)); let selectable = vec![0, 1, 2, 0, 1, 3, 0, 4, 16, 32, 128, 147, 149, 220, 221, 255, 221, 0, 1]; - bench.iter(|| { - a.select(Axis(0), &selectable) - }); + bench.iter(|| a.select(Axis(0), &selectable)); } #[bench] -fn select_axis1(bench: &mut Bencher) { +fn select_axis1(bench: &mut Bencher) +{ let a = Array::::zeros((256, 256)); let selectable = vec![0, 1, 2, 0, 1, 3, 0, 4, 16, 32, 128, 147, 149, 220, 221, 255, 221, 0, 1]; - bench.iter(|| { - a.select(Axis(1), &selectable) - }); + bench.iter(|| a.select(Axis(1), &selectable)); } #[bench] -fn select_1d(bench: &mut Bencher) { +fn select_1d(bench: &mut Bencher) +{ let a = Array::::zeros(1024); let mut selectable = (0..a.len()).step_by(17).collect::>(); selectable.extend(selectable.clone().iter().rev()); - bench.iter(|| { - a.select(Axis(0), &selectable) - }); + bench.iter(|| a.select(Axis(0), &selectable)); } diff --git a/benches/bench1.rs b/benches/bench1.rs index 6b6864194..33185844a 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -1,25 +1,23 @@ #![feature(test)] #![allow(unused_imports)] #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] extern crate test; use std::mem::MaybeUninit; -use ndarray::{ShapeBuilder, Array3, Array4}; use ndarray::{arr0, arr1, arr2, azip, s}; use ndarray::{Array, Array1, Array2, Axis, Ix, Zip}; +use ndarray::{Array3, Array4, ShapeBuilder}; use ndarray::{Ix1, Ix2, Ix3, Ix5, IxDyn}; use test::black_box; #[bench] -fn iter_sum_1d_regular(bench: &mut test::Bencher) { +fn iter_sum_1d_regular(bench: &mut test::Bencher) +{ let a = Array::::zeros(64 * 64); let a = black_box(a); bench.iter(|| { @@ -32,7 +30,8 @@ fn iter_sum_1d_regular(bench: &mut test::Bencher) { } #[bench] -fn iter_sum_1d_raw(bench: &mut test::Bencher) { +fn iter_sum_1d_raw(bench: &mut test::Bencher) +{ // this is autovectorized to death (= great performance) let a = Array::::zeros(64 * 64); let a = black_box(a); @@ -46,7 +45,8 @@ fn iter_sum_1d_raw(bench: &mut test::Bencher) { } #[bench] -fn iter_sum_2d_regular(bench: &mut test::Bencher) { +fn iter_sum_2d_regular(bench: &mut test::Bencher) +{ let a = Array::::zeros((64, 64)); let a = black_box(a); bench.iter(|| { @@ -59,7 +59,8 @@ fn iter_sum_2d_regular(bench: &mut test::Bencher) { } #[bench] -fn iter_sum_2d_by_row(bench: &mut test::Bencher) { +fn iter_sum_2d_by_row(bench: &mut test::Bencher) +{ let a = Array::::zeros((64, 64)); let a = black_box(a); bench.iter(|| { @@ -74,7 +75,8 @@ fn iter_sum_2d_by_row(bench: &mut test::Bencher) { } #[bench] -fn iter_sum_2d_raw(bench: &mut test::Bencher) { +fn iter_sum_2d_raw(bench: &mut test::Bencher) +{ // this is autovectorized to death (= great performance) let a = Array::::zeros((64, 64)); let a = black_box(a); @@ -88,7 +90,8 @@ fn iter_sum_2d_raw(bench: &mut test::Bencher) { } #[bench] -fn iter_sum_2d_cutout(bench: &mut test::Bencher) { +fn iter_sum_2d_cutout(bench: &mut test::Bencher) +{ let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]); let a = black_box(av); @@ -102,7 +105,8 @@ fn iter_sum_2d_cutout(bench: &mut test::Bencher) { } #[bench] -fn iter_sum_2d_cutout_by_row(bench: &mut test::Bencher) { +fn iter_sum_2d_cutout_by_row(bench: &mut test::Bencher) +{ let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]); let a = black_box(av); @@ -118,7 +122,8 @@ fn iter_sum_2d_cutout_by_row(bench: &mut test::Bencher) { } #[bench] -fn iter_sum_2d_cutout_outer_iter(bench: &mut test::Bencher) { +fn iter_sum_2d_cutout_outer_iter(bench: &mut test::Bencher) +{ let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]); let a = black_box(av); @@ -134,7 +139,8 @@ fn iter_sum_2d_cutout_outer_iter(bench: &mut test::Bencher) { } #[bench] -fn iter_sum_2d_transpose_regular(bench: &mut test::Bencher) { +fn iter_sum_2d_transpose_regular(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((64, 64)); a.swap_axes(0, 1); let a = black_box(a); @@ -148,7 +154,8 @@ fn iter_sum_2d_transpose_regular(bench: &mut test::Bencher) { } #[bench] -fn iter_sum_2d_transpose_by_row(bench: &mut test::Bencher) { +fn iter_sum_2d_transpose_by_row(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((64, 64)); a.swap_axes(0, 1); let a = black_box(a); @@ -164,14 +171,16 @@ fn iter_sum_2d_transpose_by_row(bench: &mut test::Bencher) { } #[bench] -fn sum_2d_regular(bench: &mut test::Bencher) { +fn sum_2d_regular(bench: &mut test::Bencher) +{ let a = Array::::zeros((64, 64)); let a = black_box(a); bench.iter(|| a.sum()); } #[bench] -fn sum_2d_cutout(bench: &mut test::Bencher) { +fn sum_2d_cutout(bench: &mut test::Bencher) +{ let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]); let a = black_box(av); @@ -179,14 +188,16 @@ fn sum_2d_cutout(bench: &mut test::Bencher) { } #[bench] -fn sum_2d_float(bench: &mut test::Bencher) { +fn sum_2d_float(bench: &mut test::Bencher) +{ let a = Array::::zeros((64, 64)); let a = black_box(a.view()); bench.iter(|| a.sum()); } #[bench] -fn sum_2d_float_cutout(bench: &mut test::Bencher) { +fn sum_2d_float_cutout(bench: &mut test::Bencher) +{ let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]); let a = black_box(av); @@ -194,7 +205,8 @@ fn sum_2d_float_cutout(bench: &mut test::Bencher) { } #[bench] -fn sum_2d_float_t_cutout(bench: &mut test::Bencher) { +fn sum_2d_float_t_cutout(bench: &mut test::Bencher) +{ let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]).reversed_axes(); let a = black_box(av); @@ -202,13 +214,15 @@ fn sum_2d_float_t_cutout(bench: &mut test::Bencher) { } #[bench] -fn fold_sum_i32_2d_regular(bench: &mut test::Bencher) { +fn fold_sum_i32_2d_regular(bench: &mut test::Bencher) +{ let a = Array::::zeros((64, 64)); bench.iter(|| a.fold(0, |acc, &x| acc + x)); } #[bench] -fn fold_sum_i32_2d_cutout(bench: &mut test::Bencher) { +fn fold_sum_i32_2d_cutout(bench: &mut test::Bencher) +{ let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]); let a = black_box(av); @@ -216,7 +230,8 @@ fn fold_sum_i32_2d_cutout(bench: &mut test::Bencher) { } #[bench] -fn fold_sum_i32_2d_stride(bench: &mut test::Bencher) { +fn fold_sum_i32_2d_stride(bench: &mut test::Bencher) +{ let a = Array::::zeros((64, 128)); let av = a.slice(s![.., ..;2]); let a = black_box(av); @@ -224,14 +239,16 @@ fn fold_sum_i32_2d_stride(bench: &mut test::Bencher) { } #[bench] -fn fold_sum_i32_2d_transpose(bench: &mut test::Bencher) { +fn fold_sum_i32_2d_transpose(bench: &mut test::Bencher) +{ let a = Array::::zeros((64, 64)); let a = a.t(); bench.iter(|| a.fold(0, |acc, &x| acc + x)); } #[bench] -fn fold_sum_i32_2d_cutout_transpose(bench: &mut test::Bencher) { +fn fold_sum_i32_2d_cutout_transpose(bench: &mut test::Bencher) +{ let a = Array::::zeros((66, 66)); let mut av = a.slice(s![1..-1, 1..-1]); av.swap_axes(0, 1); @@ -242,7 +259,8 @@ fn fold_sum_i32_2d_cutout_transpose(bench: &mut test::Bencher) { const ADD2DSZ: usize = 64; #[bench] -fn add_2d_regular(bench: &mut test::Bencher) { +fn add_2d_regular(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); let bv = b.view(); @@ -252,7 +270,8 @@ fn add_2d_regular(bench: &mut test::Bencher) { } #[bench] -fn add_2d_zip(bench: &mut test::Bencher) { +fn add_2d_zip(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| { @@ -261,14 +280,16 @@ fn add_2d_zip(bench: &mut test::Bencher) { } #[bench] -fn add_2d_alloc_plus(bench: &mut test::Bencher) { +fn add_2d_alloc_plus(bench: &mut test::Bencher) +{ let a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| &a + &b); } #[bench] -fn add_2d_alloc_zip_uninit(bench: &mut test::Bencher) { +fn add_2d_alloc_zip_uninit(bench: &mut test::Bencher) +{ let a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| unsafe { @@ -281,49 +302,44 @@ fn add_2d_alloc_zip_uninit(bench: &mut test::Bencher) { } #[bench] -fn add_2d_alloc_zip_collect(bench: &mut test::Bencher) { +fn add_2d_alloc_zip_collect(bench: &mut test::Bencher) +{ let a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); - bench.iter(|| { - Zip::from(&a).and(&b).map_collect(|&x, &y| x + y) - }); + bench.iter(|| Zip::from(&a).and(&b).map_collect(|&x, &y| x + y)); } #[bench] -fn vec_string_collect(bench: &mut test::Bencher) { +fn vec_string_collect(bench: &mut test::Bencher) +{ let v = vec![""; 10240]; - bench.iter(|| { - v.iter().map(|s| s.to_owned()).collect::>() - }); + bench.iter(|| v.iter().map(|s| s.to_owned()).collect::>()); } #[bench] -fn array_string_collect(bench: &mut test::Bencher) { +fn array_string_collect(bench: &mut test::Bencher) +{ let v = Array::from(vec![""; 10240]); - bench.iter(|| { - Zip::from(&v).map_collect(|s| s.to_owned()) - }); + bench.iter(|| Zip::from(&v).map_collect(|s| s.to_owned())); } #[bench] -fn vec_f64_collect(bench: &mut test::Bencher) { +fn vec_f64_collect(bench: &mut test::Bencher) +{ let v = vec![1.; 10240]; - bench.iter(|| { - v.iter().map(|s| s + 1.).collect::>() - }); + bench.iter(|| v.iter().map(|s| s + 1.).collect::>()); } #[bench] -fn array_f64_collect(bench: &mut test::Bencher) { +fn array_f64_collect(bench: &mut test::Bencher) +{ let v = Array::from(vec![1.; 10240]); - bench.iter(|| { - Zip::from(&v).map_collect(|s| s + 1.) - }); + bench.iter(|| Zip::from(&v).map_collect(|s| s + 1.)); } - #[bench] -fn add_2d_assign_ops(bench: &mut test::Bencher) { +fn add_2d_assign_ops(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); let bv = b.view(); @@ -335,7 +351,8 @@ fn add_2d_assign_ops(bench: &mut test::Bencher) { } #[bench] -fn add_2d_cutout(bench: &mut test::Bencher) { +fn add_2d_cutout(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD2DSZ + 2, ADD2DSZ + 2)); let mut acut = a.slice_mut(s![1..-1, 1..-1]); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); @@ -346,7 +363,8 @@ fn add_2d_cutout(bench: &mut test::Bencher) { } #[bench] -fn add_2d_zip_cutout(bench: &mut test::Bencher) { +fn add_2d_zip_cutout(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD2DSZ + 2, ADD2DSZ + 2)); let mut acut = a.slice_mut(s![1..-1, 1..-1]); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); @@ -357,7 +375,8 @@ fn add_2d_zip_cutout(bench: &mut test::Bencher) { #[bench] #[allow(clippy::identity_op)] -fn add_2d_cutouts_by_4(bench: &mut test::Bencher) { +fn add_2d_cutouts_by_4(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((64 * 1, 64 * 1)); let b = Array::::zeros((64 * 1, 64 * 1)); let chunksz = (4, 4); @@ -370,7 +389,8 @@ fn add_2d_cutouts_by_4(bench: &mut test::Bencher) { #[bench] #[allow(clippy::identity_op)] -fn add_2d_cutouts_by_16(bench: &mut test::Bencher) { +fn add_2d_cutouts_by_16(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((64 * 1, 64 * 1)); let b = Array::::zeros((64 * 1, 64 * 1)); let chunksz = (16, 16); @@ -383,7 +403,8 @@ fn add_2d_cutouts_by_16(bench: &mut test::Bencher) { #[bench] #[allow(clippy::identity_op)] -fn add_2d_cutouts_by_32(bench: &mut test::Bencher) { +fn add_2d_cutouts_by_32(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((64 * 1, 64 * 1)); let b = Array::::zeros((64 * 1, 64 * 1)); let chunksz = (32, 32); @@ -395,7 +416,8 @@ fn add_2d_cutouts_by_32(bench: &mut test::Bencher) { } #[bench] -fn add_2d_broadcast_1_to_2(bench: &mut test::Bencher) { +fn add_2d_broadcast_1_to_2(bench: &mut test::Bencher) +{ let mut a = Array2::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array1::::zeros(ADD2DSZ); let bv = b.view(); @@ -405,7 +427,8 @@ fn add_2d_broadcast_1_to_2(bench: &mut test::Bencher) { } #[bench] -fn add_2d_broadcast_0_to_2(bench: &mut test::Bencher) { +fn add_2d_broadcast_0_to_2(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros(()); let bv = b.view(); @@ -415,50 +438,55 @@ fn add_2d_broadcast_0_to_2(bench: &mut test::Bencher) { } #[bench] -fn scalar_toowned(bench: &mut test::Bencher) { +fn scalar_toowned(bench: &mut test::Bencher) +{ let a = Array::::zeros((64, 64)); bench.iter(|| a.to_owned()); } #[bench] -fn scalar_add_1(bench: &mut test::Bencher) { +fn scalar_add_1(bench: &mut test::Bencher) +{ let a = Array::::zeros((64, 64)); let n = 1.; bench.iter(|| &a + n); } #[bench] -fn scalar_add_2(bench: &mut test::Bencher) { +fn scalar_add_2(bench: &mut test::Bencher) +{ let a = Array::::zeros((64, 64)); let n = 1.; bench.iter(|| n + &a); } #[bench] -fn scalar_add_strided_1(bench: &mut test::Bencher) { - let a = - Array::from_shape_fn((64, 64 * 2), |(i, j)| (i * 64 + j) as f32).slice_move(s![.., ..;2]); +fn scalar_add_strided_1(bench: &mut test::Bencher) +{ + let a = Array::from_shape_fn((64, 64 * 2), |(i, j)| (i * 64 + j) as f32).slice_move(s![.., ..;2]); let n = 1.; bench.iter(|| &a + n); } #[bench] -fn scalar_add_strided_2(bench: &mut test::Bencher) { - let a = - Array::from_shape_fn((64, 64 * 2), |(i, j)| (i * 64 + j) as f32).slice_move(s![.., ..;2]); +fn scalar_add_strided_2(bench: &mut test::Bencher) +{ + let a = Array::from_shape_fn((64, 64 * 2), |(i, j)| (i * 64 + j) as f32).slice_move(s![.., ..;2]); let n = 1.; bench.iter(|| n + &a); } #[bench] -fn scalar_sub_1(bench: &mut test::Bencher) { +fn scalar_sub_1(bench: &mut test::Bencher) +{ let a = Array::::zeros((64, 64)); let n = 1.; bench.iter(|| &a - n); } #[bench] -fn scalar_sub_2(bench: &mut test::Bencher) { +fn scalar_sub_2(bench: &mut test::Bencher) +{ let a = Array::::zeros((64, 64)); let n = 1.; bench.iter(|| n - &a); @@ -466,7 +494,8 @@ fn scalar_sub_2(bench: &mut test::Bencher) { // This is for comparison with add_2d_broadcast_0_to_2 #[bench] -fn add_2d_0_to_2_iadd_scalar(bench: &mut test::Bencher) { +fn add_2d_0_to_2_iadd_scalar(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let n = black_box(0); bench.iter(|| { @@ -475,7 +504,8 @@ fn add_2d_0_to_2_iadd_scalar(bench: &mut test::Bencher) { } #[bench] -fn add_2d_strided(bench: &mut test::Bencher) { +fn add_2d_strided(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ * 2)); let mut a = a.slice_mut(s![.., ..;2]); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); @@ -486,7 +516,8 @@ fn add_2d_strided(bench: &mut test::Bencher) { } #[bench] -fn add_2d_regular_dyn(bench: &mut test::Bencher) { +fn add_2d_regular_dyn(bench: &mut test::Bencher) +{ let mut a = Array::::zeros(&[ADD2DSZ, ADD2DSZ][..]); let b = Array::::zeros(&[ADD2DSZ, ADD2DSZ][..]); let bv = b.view(); @@ -496,7 +527,8 @@ fn add_2d_regular_dyn(bench: &mut test::Bencher) { } #[bench] -fn add_2d_strided_dyn(bench: &mut test::Bencher) { +fn add_2d_strided_dyn(bench: &mut test::Bencher) +{ let mut a = Array::::zeros(&[ADD2DSZ, ADD2DSZ * 2][..]); let mut a = a.slice_mut(s![.., ..;2]); let b = Array::::zeros(&[ADD2DSZ, ADD2DSZ][..]); @@ -507,7 +539,8 @@ fn add_2d_strided_dyn(bench: &mut test::Bencher) { } #[bench] -fn add_2d_zip_strided(bench: &mut test::Bencher) { +fn add_2d_zip_strided(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ * 2)); let mut a = a.slice_mut(s![.., ..;2]); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); @@ -517,7 +550,8 @@ fn add_2d_zip_strided(bench: &mut test::Bencher) { } #[bench] -fn add_2d_one_transposed(bench: &mut test::Bencher) { +fn add_2d_one_transposed(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); a.swap_axes(0, 1); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); @@ -527,7 +561,8 @@ fn add_2d_one_transposed(bench: &mut test::Bencher) { } #[bench] -fn add_2d_zip_one_transposed(bench: &mut test::Bencher) { +fn add_2d_zip_one_transposed(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); a.swap_axes(0, 1); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); @@ -537,7 +572,8 @@ fn add_2d_zip_one_transposed(bench: &mut test::Bencher) { } #[bench] -fn add_2d_both_transposed(bench: &mut test::Bencher) { +fn add_2d_both_transposed(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); a.swap_axes(0, 1); let mut b = Array::::zeros((ADD2DSZ, ADD2DSZ)); @@ -548,7 +584,8 @@ fn add_2d_both_transposed(bench: &mut test::Bencher) { } #[bench] -fn add_2d_zip_both_transposed(bench: &mut test::Bencher) { +fn add_2d_zip_both_transposed(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); a.swap_axes(0, 1); let mut b = Array::::zeros((ADD2DSZ, ADD2DSZ)); @@ -559,7 +596,8 @@ fn add_2d_zip_both_transposed(bench: &mut test::Bencher) { } #[bench] -fn add_2d_f32_regular(bench: &mut test::Bencher) { +fn add_2d_f32_regular(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); let bv = b.view(); @@ -571,7 +609,8 @@ fn add_2d_f32_regular(bench: &mut test::Bencher) { const ADD3DSZ: usize = 16; #[bench] -fn add_3d_strided(bench: &mut test::Bencher) { +fn add_3d_strided(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD3DSZ, ADD3DSZ, ADD3DSZ * 2)); let mut a = a.slice_mut(s![.., .., ..;2]); let b = Array::::zeros(a.dim()); @@ -582,7 +621,8 @@ fn add_3d_strided(bench: &mut test::Bencher) { } #[bench] -fn add_3d_strided_dyn(bench: &mut test::Bencher) { +fn add_3d_strided_dyn(bench: &mut test::Bencher) +{ let mut a = Array::::zeros(&[ADD3DSZ, ADD3DSZ, ADD3DSZ * 2][..]); let mut a = a.slice_mut(s![.., .., ..;2]); let b = Array::::zeros(a.dim()); @@ -595,7 +635,8 @@ fn add_3d_strided_dyn(bench: &mut test::Bencher) { const ADD1D_SIZE: usize = 64 * 64; #[bench] -fn add_1d_regular(bench: &mut test::Bencher) { +fn add_1d_regular(bench: &mut test::Bencher) +{ let mut a = Array::::zeros(ADD1D_SIZE); let b = Array::::zeros(a.dim()); bench.iter(|| { @@ -604,7 +645,8 @@ fn add_1d_regular(bench: &mut test::Bencher) { } #[bench] -fn add_1d_strided(bench: &mut test::Bencher) { +fn add_1d_strided(bench: &mut test::Bencher) +{ let mut a = Array::::zeros(ADD1D_SIZE * 2); let mut av = a.slice_mut(s![..;2]); let b = Array::::zeros(av.dim()); @@ -614,7 +656,8 @@ fn add_1d_strided(bench: &mut test::Bencher) { } #[bench] -fn iadd_scalar_2d_regular(bench: &mut test::Bencher) { +fn iadd_scalar_2d_regular(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| { a += 1.; @@ -622,7 +665,8 @@ fn iadd_scalar_2d_regular(bench: &mut test::Bencher) { } #[bench] -fn iadd_scalar_2d_strided(bench: &mut test::Bencher) { +fn iadd_scalar_2d_strided(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ * 2)); let mut a = a.slice_mut(s![.., ..;2]); bench.iter(|| { @@ -631,7 +675,8 @@ fn iadd_scalar_2d_strided(bench: &mut test::Bencher) { } #[bench] -fn iadd_scalar_2d_regular_dyn(bench: &mut test::Bencher) { +fn iadd_scalar_2d_regular_dyn(bench: &mut test::Bencher) +{ let mut a = Array::::zeros(vec![ADD2DSZ, ADD2DSZ]); bench.iter(|| { a += 1.; @@ -639,7 +684,8 @@ fn iadd_scalar_2d_regular_dyn(bench: &mut test::Bencher) { } #[bench] -fn iadd_scalar_2d_strided_dyn(bench: &mut test::Bencher) { +fn iadd_scalar_2d_strided_dyn(bench: &mut test::Bencher) +{ let mut a = Array::::zeros(vec![ADD2DSZ, ADD2DSZ * 2]); let mut a = a.slice_mut(s![.., ..;2]); bench.iter(|| { @@ -648,7 +694,8 @@ fn iadd_scalar_2d_strided_dyn(bench: &mut test::Bencher) { } #[bench] -fn scaled_add_2d_f32_regular(bench: &mut test::Bencher) { +fn scaled_add_2d_f32_regular(bench: &mut test::Bencher) +{ let mut av = Array::::zeros((ADD2DSZ, ADD2DSZ)); let bv = Array::::zeros((ADD2DSZ, ADD2DSZ)); let scalar = std::f32::consts::PI; @@ -658,7 +705,8 @@ fn scaled_add_2d_f32_regular(bench: &mut test::Bencher) { } #[bench] -fn assign_scalar_2d_corder(bench: &mut test::Bencher) { +fn assign_scalar_2d_corder(bench: &mut test::Bencher) +{ let a = Array::zeros((ADD2DSZ, ADD2DSZ)); let mut a = black_box(a); let s = 3.; @@ -666,7 +714,8 @@ fn assign_scalar_2d_corder(bench: &mut test::Bencher) { } #[bench] -fn assign_scalar_2d_cutout(bench: &mut test::Bencher) { +fn assign_scalar_2d_cutout(bench: &mut test::Bencher) +{ let mut a = Array::zeros((66, 66)); let a = a.slice_mut(s![1..-1, 1..-1]); let mut a = black_box(a); @@ -675,7 +724,8 @@ fn assign_scalar_2d_cutout(bench: &mut test::Bencher) { } #[bench] -fn assign_scalar_2d_forder(bench: &mut test::Bencher) { +fn assign_scalar_2d_forder(bench: &mut test::Bencher) +{ let mut a = Array::zeros((ADD2DSZ, ADD2DSZ)); a.swap_axes(0, 1); let mut a = black_box(a); @@ -684,14 +734,16 @@ fn assign_scalar_2d_forder(bench: &mut test::Bencher) { } #[bench] -fn assign_zero_2d_corder(bench: &mut test::Bencher) { +fn assign_zero_2d_corder(bench: &mut test::Bencher) +{ let a = Array::zeros((ADD2DSZ, ADD2DSZ)); let mut a = black_box(a); bench.iter(|| a.fill(0.)) } #[bench] -fn assign_zero_2d_cutout(bench: &mut test::Bencher) { +fn assign_zero_2d_cutout(bench: &mut test::Bencher) +{ let mut a = Array::zeros((66, 66)); let a = a.slice_mut(s![1..-1, 1..-1]); let mut a = black_box(a); @@ -699,7 +751,8 @@ fn assign_zero_2d_cutout(bench: &mut test::Bencher) { } #[bench] -fn assign_zero_2d_forder(bench: &mut test::Bencher) { +fn assign_zero_2d_forder(bench: &mut test::Bencher) +{ let mut a = Array::zeros((ADD2DSZ, ADD2DSZ)); a.swap_axes(0, 1); let mut a = black_box(a); @@ -707,7 +760,8 @@ fn assign_zero_2d_forder(bench: &mut test::Bencher) { } #[bench] -fn bench_iter_diag(bench: &mut test::Bencher) { +fn bench_iter_diag(bench: &mut test::Bencher) +{ let a = Array::::zeros((1024, 1024)); bench.iter(|| { for elt in a.diag() { @@ -717,7 +771,8 @@ fn bench_iter_diag(bench: &mut test::Bencher) { } #[bench] -fn bench_row_iter(bench: &mut test::Bencher) { +fn bench_row_iter(bench: &mut test::Bencher) +{ let a = Array::::zeros((1024, 1024)); let it = a.row(17); bench.iter(|| { @@ -728,7 +783,8 @@ fn bench_row_iter(bench: &mut test::Bencher) { } #[bench] -fn bench_col_iter(bench: &mut test::Bencher) { +fn bench_col_iter(bench: &mut test::Bencher) +{ let a = Array::::zeros((1024, 1024)); let it = a.column(17); bench.iter(|| { @@ -755,7 +811,7 @@ macro_rules! mat_mul { } )+ } - } + }; } mat_mul! {mat_mul_f32, f32, @@ -798,7 +854,8 @@ mat_mul! {mat_mul_i32, i32, } #[bench] -fn create_iter_4d(bench: &mut test::Bencher) { +fn create_iter_4d(bench: &mut test::Bencher) +{ let mut a = Array::from_elem((4, 5, 3, 2), 1.0); a.swap_axes(0, 1); a.swap_axes(2, 1); @@ -808,82 +865,94 @@ fn create_iter_4d(bench: &mut test::Bencher) { } #[bench] -fn bench_to_owned_n(bench: &mut test::Bencher) { +fn bench_to_owned_n(bench: &mut test::Bencher) +{ let a = Array::::zeros((32, 32)); bench.iter(|| a.to_owned()); } #[bench] -fn bench_to_owned_t(bench: &mut test::Bencher) { +fn bench_to_owned_t(bench: &mut test::Bencher) +{ let mut a = Array::::zeros((32, 32)); a.swap_axes(0, 1); bench.iter(|| a.to_owned()); } #[bench] -fn bench_to_owned_strided(bench: &mut test::Bencher) { +fn bench_to_owned_strided(bench: &mut test::Bencher) +{ let a = Array::::zeros((32, 64)); let a = a.slice(s![.., ..;2]); bench.iter(|| a.to_owned()); } #[bench] -fn equality_i32(bench: &mut test::Bencher) { +fn equality_i32(bench: &mut test::Bencher) +{ let a = Array::::zeros((64, 64)); let b = Array::::zeros((64, 64)); bench.iter(|| a == b); } #[bench] -fn equality_f32(bench: &mut test::Bencher) { +fn equality_f32(bench: &mut test::Bencher) +{ let a = Array::::zeros((64, 64)); let b = Array::::zeros((64, 64)); bench.iter(|| a == b); } #[bench] -fn equality_f32_mixorder(bench: &mut test::Bencher) { +fn equality_f32_mixorder(bench: &mut test::Bencher) +{ let a = Array::::zeros((64, 64)); let b = Array::::zeros((64, 64).f()); bench.iter(|| a == b); } #[bench] -fn dot_f32_16(bench: &mut test::Bencher) { +fn dot_f32_16(bench: &mut test::Bencher) +{ let a = Array::::zeros(16); let b = Array::::zeros(16); bench.iter(|| a.dot(&b)); } #[bench] -fn dot_f32_20(bench: &mut test::Bencher) { +fn dot_f32_20(bench: &mut test::Bencher) +{ let a = Array::::zeros(20); let b = Array::::zeros(20); bench.iter(|| a.dot(&b)); } #[bench] -fn dot_f32_32(bench: &mut test::Bencher) { +fn dot_f32_32(bench: &mut test::Bencher) +{ let a = Array::::zeros(32); let b = Array::::zeros(32); bench.iter(|| a.dot(&b)); } #[bench] -fn dot_f32_256(bench: &mut test::Bencher) { +fn dot_f32_256(bench: &mut test::Bencher) +{ let a = Array::::zeros(256); let b = Array::::zeros(256); bench.iter(|| a.dot(&b)); } #[bench] -fn dot_f32_1024(bench: &mut test::Bencher) { +fn dot_f32_1024(bench: &mut test::Bencher) +{ let av = Array::::zeros(1024); let bv = Array::::zeros(1024); bench.iter(|| av.dot(&bv)); } #[bench] -fn dot_f32_10e6(bench: &mut test::Bencher) { +fn dot_f32_10e6(bench: &mut test::Bencher) +{ let n = 1_000_000; let av = Array::::zeros(n); let bv = Array::::zeros(n); @@ -891,7 +960,8 @@ fn dot_f32_10e6(bench: &mut test::Bencher) { } #[bench] -fn dot_extended(bench: &mut test::Bencher) { +fn dot_extended(bench: &mut test::Bencher) +{ let m = 10; let n = 33; let k = 10; @@ -912,7 +982,8 @@ fn dot_extended(bench: &mut test::Bencher) { const MEAN_SUM_N: usize = 127; -fn range_mat(m: Ix, n: Ix) -> Array2 { +fn range_mat(m: Ix, n: Ix) -> Array2 +{ assert!(m * n != 0); Array::linspace(0., (m * n - 1) as f32, m * n) .into_shape_with_order((m, n)) @@ -920,87 +991,100 @@ fn range_mat(m: Ix, n: Ix) -> Array2 { } #[bench] -fn mean_axis0(bench: &mut test::Bencher) { +fn mean_axis0(bench: &mut test::Bencher) +{ let a = range_mat(MEAN_SUM_N, MEAN_SUM_N); bench.iter(|| a.mean_axis(Axis(0))); } #[bench] -fn mean_axis1(bench: &mut test::Bencher) { +fn mean_axis1(bench: &mut test::Bencher) +{ let a = range_mat(MEAN_SUM_N, MEAN_SUM_N); bench.iter(|| a.mean_axis(Axis(1))); } #[bench] -fn sum_axis0(bench: &mut test::Bencher) { +fn sum_axis0(bench: &mut test::Bencher) +{ let a = range_mat(MEAN_SUM_N, MEAN_SUM_N); bench.iter(|| a.sum_axis(Axis(0))); } #[bench] -fn sum_axis1(bench: &mut test::Bencher) { +fn sum_axis1(bench: &mut test::Bencher) +{ let a = range_mat(MEAN_SUM_N, MEAN_SUM_N); bench.iter(|| a.sum_axis(Axis(1))); } #[bench] -fn into_dimensionality_ix1_ok(bench: &mut test::Bencher) { +fn into_dimensionality_ix1_ok(bench: &mut test::Bencher) +{ let a = Array::::zeros(Ix1(10)); let a = a.view(); bench.iter(|| a.into_dimensionality::()); } #[bench] -fn into_dimensionality_ix3_ok(bench: &mut test::Bencher) { +fn into_dimensionality_ix3_ok(bench: &mut test::Bencher) +{ let a = Array::::zeros(Ix3(10, 10, 10)); let a = a.view(); bench.iter(|| a.into_dimensionality::()); } #[bench] -fn into_dimensionality_ix3_err(bench: &mut test::Bencher) { +fn into_dimensionality_ix3_err(bench: &mut test::Bencher) +{ let a = Array::::zeros(Ix3(10, 10, 10)); let a = a.view(); bench.iter(|| a.into_dimensionality::()); } #[bench] -fn into_dimensionality_dyn_to_ix3(bench: &mut test::Bencher) { +fn into_dimensionality_dyn_to_ix3(bench: &mut test::Bencher) +{ let a = Array::::zeros(IxDyn(&[10, 10, 10])); let a = a.view(); bench.iter(|| a.clone().into_dimensionality::()); } #[bench] -fn into_dimensionality_dyn_to_dyn(bench: &mut test::Bencher) { +fn into_dimensionality_dyn_to_dyn(bench: &mut test::Bencher) +{ let a = Array::::zeros(IxDyn(&[10, 10, 10])); let a = a.view(); bench.iter(|| a.clone().into_dimensionality::()); } #[bench] -fn into_dyn_ix3(bench: &mut test::Bencher) { +fn into_dyn_ix3(bench: &mut test::Bencher) +{ let a = Array::::zeros(Ix3(10, 10, 10)); let a = a.view(); bench.iter(|| a.into_dyn()); } #[bench] -fn into_dyn_ix5(bench: &mut test::Bencher) { +fn into_dyn_ix5(bench: &mut test::Bencher) +{ let a = Array::::zeros(Ix5(2, 2, 2, 2, 2)); let a = a.view(); bench.iter(|| a.into_dyn()); } #[bench] -fn into_dyn_dyn(bench: &mut test::Bencher) { +fn into_dyn_dyn(bench: &mut test::Bencher) +{ let a = Array::::zeros(IxDyn(&[10, 10, 10])); let a = a.view(); bench.iter(|| a.clone().into_dyn()); } #[bench] -fn broadcast_same_dim(bench: &mut test::Bencher) { +fn broadcast_same_dim(bench: &mut test::Bencher) +{ let s = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; let s = Array4::from_shape_vec((2, 2, 3, 2), s.to_vec()).unwrap(); let a = s.slice(s![.., ..;-1, ..;2, ..]); @@ -1009,10 +1093,11 @@ fn broadcast_same_dim(bench: &mut test::Bencher) { } #[bench] -fn broadcast_one_side(bench: &mut test::Bencher) { +fn broadcast_one_side(bench: &mut test::Bencher) +{ let s = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; - let s2 = [1 ,2 ,3 ,4 ,5 ,6]; + let s2 = [1, 2, 3, 4, 5, 6]; let a = Array4::from_shape_vec((4, 1, 3, 2), s.to_vec()).unwrap(); let b = Array3::from_shape_vec((1, 3, 2), s2.to_vec()).unwrap(); bench.iter(|| &a + &b); -} \ No newline at end of file +} diff --git a/benches/chunks.rs b/benches/chunks.rs index 5ea9ba466..46780492f 100644 --- a/benches/chunks.rs +++ b/benches/chunks.rs @@ -7,7 +7,8 @@ use ndarray::prelude::*; use ndarray::NdProducer; #[bench] -fn chunk2x2_iter_sum(bench: &mut Bencher) { +fn chunk2x2_iter_sum(bench: &mut Bencher) +{ let a = Array::::zeros((256, 256)); let chunksz = (2, 2); let mut sum = Array::zeros(a.exact_chunks(chunksz).raw_dim()); @@ -19,7 +20,8 @@ fn chunk2x2_iter_sum(bench: &mut Bencher) { } #[bench] -fn chunk2x2_sum(bench: &mut Bencher) { +fn chunk2x2_sum(bench: &mut Bencher) +{ let a = Array::::zeros((256, 256)); let chunksz = (2, 2); let mut sum = Array::zeros(a.exact_chunks(chunksz).raw_dim()); @@ -31,7 +33,8 @@ fn chunk2x2_sum(bench: &mut Bencher) { } #[bench] -fn chunk2x2_sum_get1(bench: &mut Bencher) { +fn chunk2x2_sum_get1(bench: &mut Bencher) +{ let a = Array::::zeros((256, 256)); let chunksz = (2, 2); let mut sum = Array::::zeros(a.exact_chunks(chunksz).raw_dim()); @@ -46,7 +49,8 @@ fn chunk2x2_sum_get1(bench: &mut Bencher) { } #[bench] -fn chunk2x2_sum_uget1(bench: &mut Bencher) { +fn chunk2x2_sum_uget1(bench: &mut Bencher) +{ let a = Array::::zeros((256, 256)); let chunksz = (2, 2); let mut sum = Array::::zeros(a.exact_chunks(chunksz).raw_dim()); @@ -64,7 +68,8 @@ fn chunk2x2_sum_uget1(bench: &mut Bencher) { #[bench] #[allow(clippy::identity_op)] -fn chunk2x2_sum_get2(bench: &mut Bencher) { +fn chunk2x2_sum_get2(bench: &mut Bencher) +{ let a = Array::::zeros((256, 256)); let chunksz = (2, 2); let mut sum = Array::::zeros(a.exact_chunks(chunksz).raw_dim()); diff --git a/benches/construct.rs b/benches/construct.rs index c3603ce7c..278174388 100644 --- a/benches/construct.rs +++ b/benches/construct.rs @@ -1,9 +1,6 @@ #![feature(test)] #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] extern crate test; use test::Bencher; @@ -11,24 +8,32 @@ use test::Bencher; use ndarray::prelude::*; #[bench] -fn default_f64(bench: &mut Bencher) { +fn default_f64(bench: &mut Bencher) +{ bench.iter(|| Array::::default((128, 128))) } #[bench] -fn zeros_f64(bench: &mut Bencher) { +fn zeros_f64(bench: &mut Bencher) +{ bench.iter(|| Array::::zeros((128, 128))) } #[bench] -fn map_regular(bench: &mut test::Bencher) { - let a = Array::linspace(0., 127., 128).into_shape_with_order((8, 16)).unwrap(); +fn map_regular(bench: &mut test::Bencher) +{ + let a = Array::linspace(0., 127., 128) + .into_shape_with_order((8, 16)) + .unwrap(); bench.iter(|| a.map(|&x| 2. * x)); } #[bench] -fn map_stride(bench: &mut test::Bencher) { - let a = Array::linspace(0., 127., 256).into_shape_with_order((8, 32)).unwrap(); +fn map_stride(bench: &mut test::Bencher) +{ + let a = Array::linspace(0., 127., 256) + .into_shape_with_order((8, 32)) + .unwrap(); let av = a.slice(s![.., ..;2]); bench.iter(|| av.map(|&x| 2. * x)); } diff --git a/benches/gemv_gemm.rs b/benches/gemv_gemm.rs index cfa14beac..2d1642623 100644 --- a/benches/gemv_gemm.rs +++ b/benches/gemv_gemm.rs @@ -1,9 +1,6 @@ #![feature(test)] #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] extern crate test; @@ -14,12 +11,13 @@ use num_traits::{Float, One, Zero}; use ndarray::prelude::*; -use ndarray::LinalgScalar; use ndarray::linalg::general_mat_mul; use ndarray::linalg::general_mat_vec_mul; +use ndarray::LinalgScalar; #[bench] -fn gemv_64_64c(bench: &mut Bencher) { +fn gemv_64_64c(bench: &mut Bencher) +{ let a = Array::zeros((64, 64)); let (m, n) = a.dim(); let x = Array::zeros(n); @@ -30,7 +28,8 @@ fn gemv_64_64c(bench: &mut Bencher) { } #[bench] -fn gemv_64_64f(bench: &mut Bencher) { +fn gemv_64_64f(bench: &mut Bencher) +{ let a = Array::zeros((64, 64).f()); let (m, n) = a.dim(); let x = Array::zeros(n); @@ -41,7 +40,8 @@ fn gemv_64_64f(bench: &mut Bencher) { } #[bench] -fn gemv_64_32(bench: &mut Bencher) { +fn gemv_64_32(bench: &mut Bencher) +{ let a = Array::zeros((64, 32)); let (m, n) = a.dim(); let x = Array::zeros(n); @@ -52,18 +52,19 @@ fn gemv_64_32(bench: &mut Bencher) { } #[bench] -fn cgemm_100(bench: &mut Bencher) { +fn cgemm_100(bench: &mut Bencher) +{ cgemm_bench::(100, bench); } #[bench] -fn zgemm_100(bench: &mut Bencher) { +fn zgemm_100(bench: &mut Bencher) +{ cgemm_bench::(100, bench); } fn cgemm_bench(size: usize, bench: &mut Bencher) -where - A: LinalgScalar + Float, +where A: LinalgScalar + Float { let (m, k, n) = (size, size, size); let a = Array::, _>::zeros((m, k)); diff --git a/benches/higher-order.rs b/benches/higher-order.rs index f593cd026..9cc3bd961 100644 --- a/benches/higher-order.rs +++ b/benches/higher-order.rs @@ -1,9 +1,6 @@ #![feature(test)] #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] extern crate test; use test::black_box; @@ -16,17 +13,22 @@ const X: usize = 64; const Y: usize = 16; #[bench] -fn map_regular(bench: &mut Bencher) { - let a = Array::linspace(0., 127., N).into_shape_with_order((X, Y)).unwrap(); +fn map_regular(bench: &mut Bencher) +{ + let a = Array::linspace(0., 127., N) + .into_shape_with_order((X, Y)) + .unwrap(); bench.iter(|| a.map(|&x| 2. * x)); } -pub fn double_array(mut a: ArrayViewMut2<'_, f64>) { +pub fn double_array(mut a: ArrayViewMut2<'_, f64>) +{ a *= 2.0; } #[bench] -fn map_stride_double_f64(bench: &mut Bencher) { +fn map_stride_double_f64(bench: &mut Bencher) +{ let mut a = Array::linspace(0., 127., N * 2) .into_shape_with_order([X, Y * 2]) .unwrap(); @@ -37,7 +39,8 @@ fn map_stride_double_f64(bench: &mut Bencher) { } #[bench] -fn map_stride_f64(bench: &mut Bencher) { +fn map_stride_f64(bench: &mut Bencher) +{ let a = Array::linspace(0., 127., N * 2) .into_shape_with_order([X, Y * 2]) .unwrap(); @@ -46,7 +49,8 @@ fn map_stride_f64(bench: &mut Bencher) { } #[bench] -fn map_stride_u32(bench: &mut Bencher) { +fn map_stride_u32(bench: &mut Bencher) +{ let a = Array::linspace(0., 127., N * 2) .into_shape_with_order([X, Y * 2]) .unwrap(); @@ -56,7 +60,8 @@ fn map_stride_u32(bench: &mut Bencher) { } #[bench] -fn fold_axis(bench: &mut Bencher) { +fn fold_axis(bench: &mut Bencher) +{ let a = Array::linspace(0., 127., N * 2) .into_shape_with_order([X, Y * 2]) .unwrap(); @@ -67,7 +72,8 @@ const MA: usize = 64; const MASZ: usize = MA * MA; #[bench] -fn map_axis_0(bench: &mut Bencher) { +fn map_axis_0(bench: &mut Bencher) +{ let a = Array::from_iter(0..MASZ as i32) .into_shape_with_order([MA, MA]) .unwrap(); @@ -75,7 +81,8 @@ fn map_axis_0(bench: &mut Bencher) { } #[bench] -fn map_axis_1(bench: &mut Bencher) { +fn map_axis_1(bench: &mut Bencher) +{ let a = Array::from_iter(0..MASZ as i32) .into_shape_with_order([MA, MA]) .unwrap(); diff --git a/benches/iter.rs b/benches/iter.rs index 22c8b8d17..77f511745 100644 --- a/benches/iter.rs +++ b/benches/iter.rs @@ -1,9 +1,6 @@ #![feature(test)] #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] extern crate test; @@ -16,13 +13,15 @@ use ndarray::Slice; use ndarray::{FoldWhile, Zip}; #[bench] -fn iter_sum_2d_regular(bench: &mut Bencher) { +fn iter_sum_2d_regular(bench: &mut Bencher) +{ let a = Array::::zeros((64, 64)); bench.iter(|| a.iter().sum::()); } #[bench] -fn iter_sum_2d_cutout(bench: &mut Bencher) { +fn iter_sum_2d_cutout(bench: &mut Bencher) +{ let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]); let a = av; @@ -30,7 +29,8 @@ fn iter_sum_2d_cutout(bench: &mut Bencher) { } #[bench] -fn iter_all_2d_cutout(bench: &mut Bencher) { +fn iter_all_2d_cutout(bench: &mut Bencher) +{ let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]); let a = av; @@ -38,44 +38,58 @@ fn iter_all_2d_cutout(bench: &mut Bencher) { } #[bench] -fn iter_sum_2d_transpose(bench: &mut Bencher) { +fn iter_sum_2d_transpose(bench: &mut Bencher) +{ let a = Array::::zeros((66, 66)); let a = a.t(); bench.iter(|| a.iter().sum::()); } #[bench] -fn iter_filter_sum_2d_u32(bench: &mut Bencher) { - let a = Array::linspace(0., 1., 256).into_shape_with_order((16, 16)).unwrap(); +fn iter_filter_sum_2d_u32(bench: &mut Bencher) +{ + let a = Array::linspace(0., 1., 256) + .into_shape_with_order((16, 16)) + .unwrap(); let b = a.mapv(|x| (x * 100.) as u32); bench.iter(|| b.iter().filter(|&&x| x < 75).sum::()); } #[bench] -fn iter_filter_sum_2d_f32(bench: &mut Bencher) { - let a = Array::linspace(0., 1., 256).into_shape_with_order((16, 16)).unwrap(); +fn iter_filter_sum_2d_f32(bench: &mut Bencher) +{ + let a = Array::linspace(0., 1., 256) + .into_shape_with_order((16, 16)) + .unwrap(); let b = a * 100.; bench.iter(|| b.iter().filter(|&&x| x < 75.).sum::()); } #[bench] -fn iter_filter_sum_2d_stride_u32(bench: &mut Bencher) { - let a = Array::linspace(0., 1., 256).into_shape_with_order((16, 16)).unwrap(); +fn iter_filter_sum_2d_stride_u32(bench: &mut Bencher) +{ + let a = Array::linspace(0., 1., 256) + .into_shape_with_order((16, 16)) + .unwrap(); let b = a.mapv(|x| (x * 100.) as u32); let b = b.slice(s![.., ..;2]); bench.iter(|| b.iter().filter(|&&x| x < 75).sum::()); } #[bench] -fn iter_filter_sum_2d_stride_f32(bench: &mut Bencher) { - let a = Array::linspace(0., 1., 256).into_shape_with_order((16, 16)).unwrap(); +fn iter_filter_sum_2d_stride_f32(bench: &mut Bencher) +{ + let a = Array::linspace(0., 1., 256) + .into_shape_with_order((16, 16)) + .unwrap(); let b = a * 100.; let b = b.slice(s![.., ..;2]); bench.iter(|| b.iter().filter(|&&x| x < 75.).sum::()); } #[bench] -fn iter_rev_step_by_contiguous(bench: &mut Bencher) { +fn iter_rev_step_by_contiguous(bench: &mut Bencher) +{ let a = Array::linspace(0., 1., 512); bench.iter(|| { a.iter().rev().step_by(2).for_each(|x| { @@ -85,7 +99,8 @@ fn iter_rev_step_by_contiguous(bench: &mut Bencher) { } #[bench] -fn iter_rev_step_by_discontiguous(bench: &mut Bencher) { +fn iter_rev_step_by_discontiguous(bench: &mut Bencher) +{ let mut a = Array::linspace(0., 1., 1024); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); bench.iter(|| { @@ -98,7 +113,8 @@ fn iter_rev_step_by_discontiguous(bench: &mut Bencher) { const ZIPSZ: usize = 10_000; #[bench] -fn sum_3_std_zip1(bench: &mut Bencher) { +fn sum_3_std_zip1(bench: &mut Bencher) +{ let a = vec![1; ZIPSZ]; let b = vec![1; ZIPSZ]; let c = vec![1; ZIPSZ]; @@ -110,7 +126,8 @@ fn sum_3_std_zip1(bench: &mut Bencher) { } #[bench] -fn sum_3_std_zip2(bench: &mut Bencher) { +fn sum_3_std_zip2(bench: &mut Bencher) +{ let a = vec![1; ZIPSZ]; let b = vec![1; ZIPSZ]; let c = vec![1; ZIPSZ]; @@ -123,7 +140,8 @@ fn sum_3_std_zip2(bench: &mut Bencher) { } #[bench] -fn sum_3_std_zip3(bench: &mut Bencher) { +fn sum_3_std_zip3(bench: &mut Bencher) +{ let a = vec![1; ZIPSZ]; let b = vec![1; ZIPSZ]; let c = vec![1; ZIPSZ]; @@ -137,7 +155,8 @@ fn sum_3_std_zip3(bench: &mut Bencher) { } #[bench] -fn vector_sum_3_std_zip(bench: &mut Bencher) { +fn vector_sum_3_std_zip(bench: &mut Bencher) +{ let a = vec![1.; ZIPSZ]; let b = vec![1.; ZIPSZ]; let mut c = vec![1.; ZIPSZ]; @@ -149,7 +168,8 @@ fn vector_sum_3_std_zip(bench: &mut Bencher) { } #[bench] -fn sum_3_azip(bench: &mut Bencher) { +fn sum_3_azip(bench: &mut Bencher) +{ let a = vec![1; ZIPSZ]; let b = vec![1; ZIPSZ]; let c = vec![1; ZIPSZ]; @@ -163,7 +183,8 @@ fn sum_3_azip(bench: &mut Bencher) { } #[bench] -fn sum_3_azip_fold(bench: &mut Bencher) { +fn sum_3_azip_fold(bench: &mut Bencher) +{ let a = vec![1; ZIPSZ]; let b = vec![1; ZIPSZ]; let c = vec![1; ZIPSZ]; @@ -177,7 +198,8 @@ fn sum_3_azip_fold(bench: &mut Bencher) { } #[bench] -fn vector_sum_3_azip(bench: &mut Bencher) { +fn vector_sum_3_azip(bench: &mut Bencher) +{ let a = vec![1.; ZIPSZ]; let b = vec![1.; ZIPSZ]; let mut c = vec![1.; ZIPSZ]; @@ -188,7 +210,8 @@ fn vector_sum_3_azip(bench: &mut Bencher) { }); } -fn vector_sum3_unchecked(a: &[f64], b: &[f64], c: &mut [f64]) { +fn vector_sum3_unchecked(a: &[f64], b: &[f64], c: &mut [f64]) +{ for i in 0..c.len() { unsafe { *c.get_unchecked_mut(i) += *a.get_unchecked(i) + *b.get_unchecked(i); @@ -197,7 +220,8 @@ fn vector_sum3_unchecked(a: &[f64], b: &[f64], c: &mut [f64]) { } #[bench] -fn vector_sum_3_zip_unchecked(bench: &mut Bencher) { +fn vector_sum_3_zip_unchecked(bench: &mut Bencher) +{ let a = vec![1.; ZIPSZ]; let b = vec![1.; ZIPSZ]; let mut c = vec![1.; ZIPSZ]; @@ -207,7 +231,8 @@ fn vector_sum_3_zip_unchecked(bench: &mut Bencher) { } #[bench] -fn vector_sum_3_zip_unchecked_manual(bench: &mut Bencher) { +fn vector_sum_3_zip_unchecked_manual(bench: &mut Bencher) +{ let a = vec![1.; ZIPSZ]; let b = vec![1.; ZIPSZ]; let mut c = vec![1.; ZIPSZ]; @@ -227,7 +252,8 @@ const ISZ: usize = 16; const I2DSZ: usize = 64; #[bench] -fn indexed_iter_1d_ix1(bench: &mut Bencher) { +fn indexed_iter_1d_ix1(bench: &mut Bencher) +{ let mut a = Array::::zeros(I2DSZ * I2DSZ); for (i, elt) in a.indexed_iter_mut() { *elt = i as _; @@ -242,7 +268,8 @@ fn indexed_iter_1d_ix1(bench: &mut Bencher) { } #[bench] -fn indexed_zip_1d_ix1(bench: &mut Bencher) { +fn indexed_zip_1d_ix1(bench: &mut Bencher) +{ let mut a = Array::::zeros(I2DSZ * I2DSZ); for (i, elt) in a.indexed_iter_mut() { *elt = i as _; @@ -257,7 +284,8 @@ fn indexed_zip_1d_ix1(bench: &mut Bencher) { } #[bench] -fn indexed_iter_2d_ix2(bench: &mut Bencher) { +fn indexed_iter_2d_ix2(bench: &mut Bencher) +{ let mut a = Array::::zeros((I2DSZ, I2DSZ)); for ((i, j), elt) in a.indexed_iter_mut() { *elt = (i + 100 * j) as _; @@ -271,7 +299,8 @@ fn indexed_iter_2d_ix2(bench: &mut Bencher) { }) } #[bench] -fn indexed_zip_2d_ix2(bench: &mut Bencher) { +fn indexed_zip_2d_ix2(bench: &mut Bencher) +{ let mut a = Array::::zeros((I2DSZ, I2DSZ)); for ((i, j), elt) in a.indexed_iter_mut() { *elt = (i + 100 * j) as _; @@ -286,7 +315,8 @@ fn indexed_zip_2d_ix2(bench: &mut Bencher) { } #[bench] -fn indexed_iter_3d_ix3(bench: &mut Bencher) { +fn indexed_iter_3d_ix3(bench: &mut Bencher) +{ let mut a = Array::::zeros((ISZ, ISZ, ISZ)); for ((i, j, k), elt) in a.indexed_iter_mut() { *elt = (i + 100 * j + 10000 * k) as _; @@ -301,7 +331,8 @@ fn indexed_iter_3d_ix3(bench: &mut Bencher) { } #[bench] -fn indexed_zip_3d_ix3(bench: &mut Bencher) { +fn indexed_zip_3d_ix3(bench: &mut Bencher) +{ let mut a = Array::::zeros((ISZ, ISZ, ISZ)); for ((i, j, k), elt) in a.indexed_iter_mut() { *elt = (i + 100 * j + 10000 * k) as _; @@ -316,7 +347,8 @@ fn indexed_zip_3d_ix3(bench: &mut Bencher) { } #[bench] -fn indexed_iter_3d_dyn(bench: &mut Bencher) { +fn indexed_iter_3d_dyn(bench: &mut Bencher) +{ let mut a = Array::::zeros((ISZ, ISZ, ISZ)); for ((i, j, k), elt) in a.indexed_iter_mut() { *elt = (i + 100 * j + 10000 * k) as _; @@ -332,27 +364,31 @@ fn indexed_iter_3d_dyn(bench: &mut Bencher) { } #[bench] -fn iter_sum_1d_strided_fold(bench: &mut Bencher) { +fn iter_sum_1d_strided_fold(bench: &mut Bencher) +{ let mut a = Array::::ones(10240); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); bench.iter(|| a.iter().sum::()); } #[bench] -fn iter_sum_1d_strided_rfold(bench: &mut Bencher) { +fn iter_sum_1d_strided_rfold(bench: &mut Bencher) +{ let mut a = Array::::ones(10240); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); bench.iter(|| a.iter().rfold(0, |acc, &x| acc + x)); } #[bench] -fn iter_axis_iter_sum(bench: &mut Bencher) { +fn iter_axis_iter_sum(bench: &mut Bencher) +{ let a = Array::::zeros((64, 64)); bench.iter(|| a.axis_iter(Axis(0)).map(|plane| plane.sum()).sum::()); } #[bench] -fn iter_axis_chunks_1_iter_sum(bench: &mut Bencher) { +fn iter_axis_chunks_1_iter_sum(bench: &mut Bencher) +{ let a = Array::::zeros((64, 64)); bench.iter(|| { a.axis_chunks_iter(Axis(0), 1) @@ -362,7 +398,8 @@ fn iter_axis_chunks_1_iter_sum(bench: &mut Bencher) { } #[bench] -fn iter_axis_chunks_5_iter_sum(bench: &mut Bencher) { +fn iter_axis_chunks_5_iter_sum(bench: &mut Bencher) +{ let a = Array::::zeros((64, 64)); bench.iter(|| { a.axis_chunks_iter(Axis(0), 5) @@ -371,21 +408,24 @@ fn iter_axis_chunks_5_iter_sum(bench: &mut Bencher) { }); } -pub fn zip_mut_with(data: &Array3, out: &mut Array3) { +pub fn zip_mut_with(data: &Array3, out: &mut Array3) +{ out.zip_mut_with(&data, |o, &i| { *o = i; }); } #[bench] -fn zip_mut_with_cc(b: &mut Bencher) { +fn zip_mut_with_cc(b: &mut Bencher) +{ let data: Array3 = Array3::zeros((ISZ, ISZ, ISZ)); let mut out = Array3::zeros(data.dim()); b.iter(|| zip_mut_with(&data, &mut out)); } #[bench] -fn zip_mut_with_ff(b: &mut Bencher) { +fn zip_mut_with_ff(b: &mut Bencher) +{ let data: Array3 = Array3::zeros((ISZ, ISZ, ISZ).f()); let mut out = Array3::zeros(data.dim().f()); b.iter(|| zip_mut_with(&data, &mut out)); diff --git a/benches/numeric.rs b/benches/numeric.rs index d9b9187ff..e2ffa1b84 100644 --- a/benches/numeric.rs +++ b/benches/numeric.rs @@ -10,7 +10,8 @@ const X: usize = 64; const Y: usize = 16; #[bench] -fn clip(bench: &mut Bencher) { +fn clip(bench: &mut Bencher) +{ let mut a = Array::linspace(0., 127., N * 2) .into_shape_with_order([X, Y * 2]) .unwrap(); diff --git a/benches/par_rayon.rs b/benches/par_rayon.rs index 18176d846..1301ae75a 100644 --- a/benches/par_rayon.rs +++ b/benches/par_rayon.rs @@ -12,7 +12,8 @@ use ndarray::Zip; const EXP_N: usize = 256; const ADDN: usize = 512; -fn set_threads() { +fn set_threads() +{ // Consider setting a fixed number of threads here, for example to avoid // oversubscribing on hyperthreaded cores. // let n = 4; @@ -20,7 +21,8 @@ fn set_threads() { } #[bench] -fn map_exp_regular(bench: &mut Bencher) { +fn map_exp_regular(bench: &mut Bencher) +{ let mut a = Array2::::zeros((EXP_N, EXP_N)); a.swap_axes(0, 1); bench.iter(|| { @@ -29,7 +31,8 @@ fn map_exp_regular(bench: &mut Bencher) { } #[bench] -fn rayon_exp_regular(bench: &mut Bencher) { +fn rayon_exp_regular(bench: &mut Bencher) +{ set_threads(); let mut a = Array2::::zeros((EXP_N, EXP_N)); a.swap_axes(0, 1); @@ -41,19 +44,22 @@ fn rayon_exp_regular(bench: &mut Bencher) { const FASTEXP: usize = EXP_N; #[inline] -fn fastexp(x: f64) -> f64 { +fn fastexp(x: f64) -> f64 +{ let x = 1. + x / 1024.; x.powi(1024) } #[bench] -fn map_fastexp_regular(bench: &mut Bencher) { +fn map_fastexp_regular(bench: &mut Bencher) +{ let mut a = Array2::::zeros((FASTEXP, FASTEXP)); bench.iter(|| a.mapv_inplace(|x| fastexp(x))); } #[bench] -fn rayon_fastexp_regular(bench: &mut Bencher) { +fn rayon_fastexp_regular(bench: &mut Bencher) +{ set_threads(); let mut a = Array2::::zeros((FASTEXP, FASTEXP)); bench.iter(|| { @@ -62,14 +68,16 @@ fn rayon_fastexp_regular(bench: &mut Bencher) { } #[bench] -fn map_fastexp_cut(bench: &mut Bencher) { +fn map_fastexp_cut(bench: &mut Bencher) +{ let mut a = Array2::::zeros((FASTEXP, FASTEXP)); let mut a = a.slice_mut(s![.., ..-1]); bench.iter(|| a.mapv_inplace(|x| fastexp(x))); } #[bench] -fn rayon_fastexp_cut(bench: &mut Bencher) { +fn rayon_fastexp_cut(bench: &mut Bencher) +{ set_threads(); let mut a = Array2::::zeros((FASTEXP, FASTEXP)); let mut a = a.slice_mut(s![.., ..-1]); @@ -79,7 +87,8 @@ fn rayon_fastexp_cut(bench: &mut Bencher) { } #[bench] -fn map_fastexp_by_axis(bench: &mut Bencher) { +fn map_fastexp_by_axis(bench: &mut Bencher) +{ let mut a = Array2::::zeros((FASTEXP, FASTEXP)); bench.iter(|| { for mut sheet in a.axis_iter_mut(Axis(0)) { @@ -89,7 +98,8 @@ fn map_fastexp_by_axis(bench: &mut Bencher) { } #[bench] -fn rayon_fastexp_by_axis(bench: &mut Bencher) { +fn rayon_fastexp_by_axis(bench: &mut Bencher) +{ set_threads(); let mut a = Array2::::zeros((FASTEXP, FASTEXP)); bench.iter(|| { @@ -100,7 +110,8 @@ fn rayon_fastexp_by_axis(bench: &mut Bencher) { } #[bench] -fn rayon_fastexp_zip(bench: &mut Bencher) { +fn rayon_fastexp_zip(bench: &mut Bencher) +{ set_threads(); let mut a = Array2::::zeros((FASTEXP, FASTEXP)); bench.iter(|| { @@ -111,7 +122,8 @@ fn rayon_fastexp_zip(bench: &mut Bencher) { } #[bench] -fn add(bench: &mut Bencher) { +fn add(bench: &mut Bencher) +{ let mut a = Array2::::zeros((ADDN, ADDN)); let b = Array2::::zeros((ADDN, ADDN)); let c = Array2::::zeros((ADDN, ADDN)); @@ -124,7 +136,8 @@ fn add(bench: &mut Bencher) { } #[bench] -fn rayon_add(bench: &mut Bencher) { +fn rayon_add(bench: &mut Bencher) +{ set_threads(); let mut a = Array2::::zeros((ADDN, ADDN)); let b = Array2::::zeros((ADDN, ADDN)); @@ -141,34 +154,29 @@ const COLL_STRING_N: usize = 64; const COLL_F64_N: usize = 128; #[bench] -fn vec_string_collect(bench: &mut test::Bencher) { +fn vec_string_collect(bench: &mut test::Bencher) +{ let v = vec![""; COLL_STRING_N * COLL_STRING_N]; - bench.iter(|| { - v.iter().map(|s| s.to_owned()).collect::>() - }); + bench.iter(|| v.iter().map(|s| s.to_owned()).collect::>()); } #[bench] -fn array_string_collect(bench: &mut test::Bencher) { +fn array_string_collect(bench: &mut test::Bencher) +{ let v = Array::from_elem((COLL_STRING_N, COLL_STRING_N), ""); - bench.iter(|| { - Zip::from(&v).par_map_collect(|s| s.to_owned()) - }); + bench.iter(|| Zip::from(&v).par_map_collect(|s| s.to_owned())); } #[bench] -fn vec_f64_collect(bench: &mut test::Bencher) { +fn vec_f64_collect(bench: &mut test::Bencher) +{ let v = vec![1.; COLL_F64_N * COLL_F64_N]; - bench.iter(|| { - v.iter().map(|s| s + 1.).collect::>() - }); + bench.iter(|| v.iter().map(|s| s + 1.).collect::>()); } #[bench] -fn array_f64_collect(bench: &mut test::Bencher) { +fn array_f64_collect(bench: &mut test::Bencher) +{ let v = Array::from_elem((COLL_F64_N, COLL_F64_N), 1.); - bench.iter(|| { - Zip::from(&v).par_map_collect(|s| s + 1.) - }); + bench.iter(|| Zip::from(&v).par_map_collect(|s| s + 1.)); } - diff --git a/benches/to_shape.rs b/benches/to_shape.rs index a048eb774..f056a9852 100644 --- a/benches/to_shape.rs +++ b/benches/to_shape.rs @@ -7,100 +7,89 @@ use ndarray::prelude::*; use ndarray::Order; #[bench] -fn to_shape2_1(bench: &mut Bencher) { +fn to_shape2_1(bench: &mut Bencher) +{ let a = Array::::zeros((4, 5)); let view = a.view(); - bench.iter(|| { - view.to_shape(4 * 5).unwrap() - }); + bench.iter(|| view.to_shape(4 * 5).unwrap()); } #[bench] -fn to_shape2_2_same(bench: &mut Bencher) { +fn to_shape2_2_same(bench: &mut Bencher) +{ let a = Array::::zeros((4, 5)); let view = a.view(); - bench.iter(|| { - view.to_shape((4, 5)).unwrap() - }); + bench.iter(|| view.to_shape((4, 5)).unwrap()); } #[bench] -fn to_shape2_2_flip(bench: &mut Bencher) { +fn to_shape2_2_flip(bench: &mut Bencher) +{ let a = Array::::zeros((4, 5)); let view = a.view(); - bench.iter(|| { - view.to_shape((5, 4)).unwrap() - }); + bench.iter(|| view.to_shape((5, 4)).unwrap()); } #[bench] -fn to_shape2_3(bench: &mut Bencher) { +fn to_shape2_3(bench: &mut Bencher) +{ let a = Array::::zeros((4, 5)); let view = a.view(); - bench.iter(|| { - view.to_shape((2, 5, 2)).unwrap() - }); + bench.iter(|| view.to_shape((2, 5, 2)).unwrap()); } #[bench] -fn to_shape3_1(bench: &mut Bencher) { +fn to_shape3_1(bench: &mut Bencher) +{ let a = Array::::zeros((3, 4, 5)); let view = a.view(); - bench.iter(|| { - view.to_shape(3 * 4 * 5).unwrap() - }); + bench.iter(|| view.to_shape(3 * 4 * 5).unwrap()); } #[bench] -fn to_shape3_2_order(bench: &mut Bencher) { +fn to_shape3_2_order(bench: &mut Bencher) +{ let a = Array::::zeros((3, 4, 5)); let view = a.view(); - bench.iter(|| { - view.to_shape((12, 5)).unwrap() - }); + bench.iter(|| view.to_shape((12, 5)).unwrap()); } #[bench] -fn to_shape3_2_outoforder(bench: &mut Bencher) { +fn to_shape3_2_outoforder(bench: &mut Bencher) +{ let a = Array::::zeros((3, 4, 5)); let view = a.view(); - bench.iter(|| { - view.to_shape((4, 15)).unwrap() - }); + bench.iter(|| view.to_shape((4, 15)).unwrap()); } #[bench] -fn to_shape3_3c(bench: &mut Bencher) { +fn to_shape3_3c(bench: &mut Bencher) +{ let a = Array::::zeros((3, 4, 5)); let view = a.view(); - bench.iter(|| { - view.to_shape((3, 4, 5)).unwrap() - }); + bench.iter(|| view.to_shape((3, 4, 5)).unwrap()); } #[bench] -fn to_shape3_3f(bench: &mut Bencher) { +fn to_shape3_3f(bench: &mut Bencher) +{ let a = Array::::zeros((3, 4, 5).f()); let view = a.view(); - bench.iter(|| { - view.to_shape(((3, 4, 5), Order::F)).unwrap() - }); + bench.iter(|| view.to_shape(((3, 4, 5), Order::F)).unwrap()); } #[bench] -fn to_shape3_4c(bench: &mut Bencher) { +fn to_shape3_4c(bench: &mut Bencher) +{ let a = Array::::zeros((3, 4, 5)); let view = a.view(); - bench.iter(|| { - view.to_shape(((2, 3, 2, 5), Order::C)).unwrap() - }); + bench.iter(|| view.to_shape(((2, 3, 2, 5), Order::C)).unwrap()); } #[bench] -fn to_shape3_4f(bench: &mut Bencher) { +fn to_shape3_4f(bench: &mut Bencher) +{ let a = Array::::zeros((3, 4, 5).f()); let view = a.view(); - bench.iter(|| { - view.to_shape(((2, 3, 2, 5), Order::F)).unwrap() - }); + bench.iter(|| view.to_shape(((2, 3, 2, 5), Order::F)).unwrap()); } diff --git a/benches/zip.rs b/benches/zip.rs index 10e1dee3c..461497310 100644 --- a/benches/zip.rs +++ b/benches/zip.rs @@ -1,14 +1,15 @@ #![feature(test)] extern crate test; -use test::{black_box, Bencher}; -use ndarray::{Array3, ShapeBuilder, Zip}; use ndarray::s; use ndarray::IntoNdProducer; +use ndarray::{Array3, ShapeBuilder, Zip}; +use test::{black_box, Bencher}; pub fn zip_copy<'a, A, P, Q>(data: P, out: Q) - where P: IntoNdProducer, - Q: IntoNdProducer, - A: Copy + 'a +where + P: IntoNdProducer, + Q: IntoNdProducer, + A: Copy + 'a, { Zip::from(data).and(out).for_each(|&i, o| { *o = i; @@ -16,9 +17,10 @@ pub fn zip_copy<'a, A, P, Q>(data: P, out: Q) } pub fn zip_copy_split<'a, A, P, Q>(data: P, out: Q) - where P: IntoNdProducer, - Q: IntoNdProducer, - A: Copy + 'a +where + P: IntoNdProducer, + Q: IntoNdProducer, + A: Copy + 'a, { let z = Zip::from(data).and(out); let (z1, z2) = z.split(); @@ -31,7 +33,8 @@ pub fn zip_copy_split<'a, A, P, Q>(data: P, out: Q) z22.for_each(f); } -pub fn zip_indexed(data: &Array3, out: &mut Array3) { +pub fn zip_indexed(data: &Array3, out: &mut Array3) +{ Zip::indexed(data).and(out).for_each(|idx, &i, o| { let _ = black_box(idx); *o = i; @@ -42,49 +45,56 @@ pub fn zip_indexed(data: &Array3, out: &mut Array3) { const SZ3: (usize, usize, usize) = (100, 110, 100); #[bench] -fn zip_cc(b: &mut Bencher) { +fn zip_cc(b: &mut Bencher) +{ let data: Array3 = Array3::zeros(SZ3); let mut out = Array3::zeros(data.dim()); b.iter(|| zip_copy(&data, &mut out)); } #[bench] -fn zip_cf(b: &mut Bencher) { +fn zip_cf(b: &mut Bencher) +{ let data: Array3 = Array3::zeros(SZ3); let mut out = Array3::zeros(data.dim().f()); b.iter(|| zip_copy(&data, &mut out)); } #[bench] -fn zip_fc(b: &mut Bencher) { +fn zip_fc(b: &mut Bencher) +{ let data: Array3 = Array3::zeros(SZ3.f()); let mut out = Array3::zeros(data.dim()); b.iter(|| zip_copy(&data, &mut out)); } #[bench] -fn zip_ff(b: &mut Bencher) { +fn zip_ff(b: &mut Bencher) +{ let data: Array3 = Array3::zeros(SZ3.f()); let mut out = Array3::zeros(data.dim().f()); b.iter(|| zip_copy(&data, &mut out)); } #[bench] -fn zip_indexed_cc(b: &mut Bencher) { +fn zip_indexed_cc(b: &mut Bencher) +{ let data: Array3 = Array3::zeros(SZ3); let mut out = Array3::zeros(data.dim()); b.iter(|| zip_indexed(&data, &mut out)); } #[bench] -fn zip_indexed_ff(b: &mut Bencher) { +fn zip_indexed_ff(b: &mut Bencher) +{ let data: Array3 = Array3::zeros(SZ3.f()); let mut out = Array3::zeros(data.dim().f()); b.iter(|| zip_indexed(&data, &mut out)); } #[bench] -fn slice_zip_cc(b: &mut Bencher) { +fn slice_zip_cc(b: &mut Bencher) +{ let data: Array3 = Array3::zeros(SZ3); let mut out = Array3::zeros(data.dim()); let data = data.slice(s![1.., 1.., 1..]); @@ -93,7 +103,8 @@ fn slice_zip_cc(b: &mut Bencher) { } #[bench] -fn slice_zip_ff(b: &mut Bencher) { +fn slice_zip_ff(b: &mut Bencher) +{ let data: Array3 = Array3::zeros(SZ3.f()); let mut out = Array3::zeros(data.dim().f()); let data = data.slice(s![1.., 1.., 1..]); @@ -102,7 +113,8 @@ fn slice_zip_ff(b: &mut Bencher) { } #[bench] -fn slice_split_zip_cc(b: &mut Bencher) { +fn slice_split_zip_cc(b: &mut Bencher) +{ let data: Array3 = Array3::zeros(SZ3); let mut out = Array3::zeros(data.dim()); let data = data.slice(s![1.., 1.., 1..]); @@ -111,7 +123,8 @@ fn slice_split_zip_cc(b: &mut Bencher) { } #[bench] -fn slice_split_zip_ff(b: &mut Bencher) { +fn slice_split_zip_ff(b: &mut Bencher) +{ let data: Array3 = Array3::zeros(SZ3.f()); let mut out = Array3::zeros(data.dim().f()); let data = data.slice(s![1.., 1.., 1..]); diff --git a/examples/axis_ops.rs b/examples/axis_ops.rs index e84d112c2..3a54a52fb 100644 --- a/examples/axis_ops.rs +++ b/examples/axis_ops.rs @@ -1,8 +1,5 @@ #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] use ndarray::prelude::*; @@ -58,7 +55,8 @@ where Ok(()) } -fn main() { +fn main() +{ let mut a = Array::::zeros((2, 3, 4)); for (i, elt) in (0..).zip(&mut a) { *elt = i; diff --git a/examples/bounds_check_elim.rs b/examples/bounds_check_elim.rs index d1c247433..e6b57c719 100644 --- a/examples/bounds_check_elim.rs +++ b/examples/bounds_check_elim.rs @@ -1,9 +1,6 @@ #![crate_type = "lib"] #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] // Test cases for bounds check elimination @@ -38,7 +35,8 @@ pub fn testvec_as_slice(a: &Vec) -> f64 { */ #[no_mangle] -pub fn test1d_single(a: &Array1, i: usize) -> f64 { +pub fn test1d_single(a: &Array1, i: usize) -> f64 +{ if i < a.len() { a[i] } else { @@ -47,7 +45,8 @@ pub fn test1d_single(a: &Array1, i: usize) -> f64 { } #[no_mangle] -pub fn test1d_single_mut(a: &mut Array1, i: usize) -> f64 { +pub fn test1d_single_mut(a: &mut Array1, i: usize) -> f64 +{ if i < a.len() { *&mut a[i] } else { @@ -56,7 +55,8 @@ pub fn test1d_single_mut(a: &mut Array1, i: usize) -> f64 { } #[no_mangle] -pub fn test1d_len_of(a: &Array1) -> f64 { +pub fn test1d_len_of(a: &Array1) -> f64 +{ let a = &*a; let mut sum = 0.; for i in 0..a.len_of(Axis(0)) { @@ -66,7 +66,8 @@ pub fn test1d_len_of(a: &Array1) -> f64 { } #[no_mangle] -pub fn test1d_range(a: &Array1) -> f64 { +pub fn test1d_range(a: &Array1) -> f64 +{ let mut sum = 0.; for i in 0..a.len() { sum += a[i]; @@ -75,7 +76,8 @@ pub fn test1d_range(a: &Array1) -> f64 { } #[no_mangle] -pub fn test1d_while(a: &Array1) -> f64 { +pub fn test1d_while(a: &Array1) -> f64 +{ let mut sum = 0.; let mut i = 0; while i < a.len() { @@ -86,7 +88,8 @@ pub fn test1d_while(a: &Array1) -> f64 { } #[no_mangle] -pub fn test2d_ranges(a: &Array2) -> f64 { +pub fn test2d_ranges(a: &Array2) -> f64 +{ let mut sum = 0.; for i in 0..a.nrows() { for j in 0..a.ncols() { @@ -97,7 +100,8 @@ pub fn test2d_ranges(a: &Array2) -> f64 { } #[no_mangle] -pub fn test2d_whiles(a: &Array2) -> f64 { +pub fn test2d_whiles(a: &Array2) -> f64 +{ let mut sum = 0.; let mut i = 0; while i < a.nrows() { diff --git a/examples/column_standardize.rs b/examples/column_standardize.rs index 6a1840f03..329ad2ccb 100644 --- a/examples/column_standardize.rs +++ b/examples/column_standardize.rs @@ -2,7 +2,8 @@ use ndarray::prelude::*; #[cfg(feature = "std")] -fn main() { +fn main() +{ // This example recreates the following from python/numpy // counts -= np.mean(counts, axis=0) // counts /= np.std(counts, axis=0) diff --git a/examples/convo.rs b/examples/convo.rs index b50ab5247..a59795e12 100644 --- a/examples/convo.rs +++ b/examples/convo.rs @@ -15,8 +15,7 @@ type Kernel3x3 = [[A; 3]; 3]; #[inline(never)] #[cfg(feature = "std")] fn conv_3x3(a: &ArrayView2<'_, F>, out: &mut ArrayViewMut2<'_, F>, kernel: &Kernel3x3) -where - F: Float, +where F: Float { let (n, m) = a.dim(); let (np, mp) = out.dim(); @@ -43,7 +42,8 @@ where } #[cfg(feature = "std")] -fn main() { +fn main() +{ let n = 16; let mut a = Array::zeros((n, n)); // make a circle diff --git a/examples/life.rs b/examples/life.rs index e0675ae17..7db384678 100644 --- a/examples/life.rs +++ b/examples/life.rs @@ -1,8 +1,5 @@ #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] use ndarray::prelude::*; @@ -13,7 +10,8 @@ const N: usize = 100; type Board = Array2; -fn parse(x: &[u8]) -> Board { +fn parse(x: &[u8]) -> Board +{ // make a border of 0 cells let mut map = Board::from_elem(((N + 2), (N + 2)), 0); let a = Array::from_iter(x.iter().filter_map(|&b| match b { @@ -33,7 +31,8 @@ fn parse(x: &[u8]) -> Board { // 3 neighbors: birth // otherwise: death -fn iterate(z: &mut Board, scratch: &mut Board) { +fn iterate(z: &mut Board, scratch: &mut Board) +{ // compute number of neighbors let mut neigh = scratch.view_mut(); neigh.fill(0); @@ -56,7 +55,8 @@ fn iterate(z: &mut Board, scratch: &mut Board) { zv.zip_mut_with(&neigh, |y, &n| *y = ((n == 3) || (n == 2 && *y > 0)) as u8); } -fn turn_on_corners(z: &mut Board) { +fn turn_on_corners(z: &mut Board) +{ let n = z.nrows(); let m = z.ncols(); z[[1, 1]] = 1; @@ -65,7 +65,8 @@ fn turn_on_corners(z: &mut Board) { z[[n - 2, m - 2]] = 1; } -fn render(a: &Board) { +fn render(a: &Board) +{ for row in a.rows() { for &x in row { if x > 0 { @@ -78,7 +79,8 @@ fn render(a: &Board) { } } -fn main() { +fn main() +{ let mut a = parse(INPUT); let mut scratch = Board::zeros((N, N)); let steps = 100; diff --git a/examples/rollaxis.rs b/examples/rollaxis.rs index 8efdd0ce0..82c381297 100644 --- a/examples/rollaxis.rs +++ b/examples/rollaxis.rs @@ -22,7 +22,8 @@ where a } -fn main() { +fn main() +{ let mut data = array![ [[-1., 0., -2.], [1., 7., -3.]], [[1., 0., -3.], [1., 7., 5.]], diff --git a/examples/sort-axis.rs b/examples/sort-axis.rs index 2ff6ceb32..17ce52e3a 100644 --- a/examples/sort-axis.rs +++ b/examples/sort-axis.rs @@ -12,13 +12,16 @@ use std::ptr::copy_nonoverlapping; // Type invariant: Each index appears exactly once #[derive(Clone, Debug)] -pub struct Permutation { +pub struct Permutation +{ indices: Vec, } -impl Permutation { +impl Permutation +{ /// Checks if the permutation is correct - pub fn from_indices(v: Vec) -> Result { + pub fn from_indices(v: Vec) -> Result + { let perm = Permutation { indices: v }; if perm.correct() { Ok(perm) @@ -27,34 +30,35 @@ impl Permutation { } } - fn correct(&self) -> bool { + fn correct(&self) -> bool + { let axis_len = self.indices.len(); let mut seen = vec![false; axis_len]; for &i in &self.indices { match seen.get_mut(i) { None => return false, - Some(s) => { + Some(s) => if *s { return false; } else { *s = true; - } - } + }, } } true } } -pub trait SortArray { +pub trait SortArray +{ /// ***Panics*** if `axis` is out of bounds. fn identity(&self, axis: Axis) -> Permutation; fn sort_axis_by(&self, axis: Axis, less_than: F) -> Permutation - where - F: FnMut(usize, usize) -> bool; + where F: FnMut(usize, usize) -> bool; } -pub trait PermuteArray { +pub trait PermuteArray +{ type Elem; type Dim; fn permute_axis(self, axis: Axis, perm: &Permutation) -> Array @@ -68,15 +72,15 @@ where S: Data, D: Dimension, { - fn identity(&self, axis: Axis) -> Permutation { + fn identity(&self, axis: Axis) -> Permutation + { Permutation { indices: (0..self.len_of(axis)).collect(), } } fn sort_axis_by(&self, axis: Axis, mut less_than: F) -> Permutation - where - F: FnMut(usize, usize) -> bool, + where F: FnMut(usize, usize) -> bool { let mut perm = self.identity(axis); perm.indices.sort_by(move |&a, &b| { @@ -93,15 +97,13 @@ where } impl PermuteArray for Array -where - D: Dimension, +where D: Dimension { type Elem = A; type Dim = D; fn permute_axis(self, axis: Axis, perm: &Permutation) -> Array - where - D: RemoveAxis, + where D: RemoveAxis { let axis_len = self.len_of(axis); let axis_stride = self.stride_of(axis); @@ -165,8 +167,11 @@ where } #[cfg(feature = "std")] -fn main() { - let a = Array::linspace(0., 63., 64).into_shape_with_order((8, 8)).unwrap(); +fn main() +{ + let a = Array::linspace(0., 63., 64) + .into_shape_with_order((8, 8)) + .unwrap(); let strings = a.map(|x| x.to_string()); let perm = a.sort_axis_by(Axis(1), |i, j| a[[i, 0]] > a[[j, 0]]); @@ -183,10 +188,12 @@ fn main() { fn main() {} #[cfg(test)] -mod tests { +mod tests +{ use super::*; #[test] - fn test_permute_axis() { + fn test_permute_axis() + { let a = array![ [107998.96, 1.], [107999.08, 2.], diff --git a/examples/type_conversion.rs b/examples/type_conversion.rs index 7bec2542f..a419af740 100644 --- a/examples/type_conversion.rs +++ b/examples/type_conversion.rs @@ -7,7 +7,8 @@ use approx::assert_abs_diff_eq; use ndarray::prelude::*; #[cfg(feature = "approx")] -fn main() { +fn main() +{ // Converting an array from one datatype to another is implemented with the // `ArrayBase::mapv()` function. We pass a closure that is applied to each // element independently. This allows for more control and flexiblity in diff --git a/examples/zip_many.rs b/examples/zip_many.rs index e2c5169f2..57d66a956 100644 --- a/examples/zip_many.rs +++ b/examples/zip_many.rs @@ -1,14 +1,12 @@ #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] use ndarray::prelude::*; use ndarray::Zip; -fn main() { +fn main() +{ let n = 6; let mut a = Array::::zeros((n, n)); @@ -20,7 +18,9 @@ fn main() { let c = c.slice(s![.., ..-1]); // Using Zip for arithmetic ops across a, b, c - Zip::from(&mut a).and(&b).and(&c) + Zip::from(&mut a) + .and(&b) + .and(&c) .for_each(|a, &b, &c| *a = b + c); assert_eq!(a, &b + &c); @@ -31,7 +31,8 @@ fn main() { // sum of each row let mut sums = Array::zeros(a.nrows()); - Zip::from(a.rows()).and(&mut sums) + Zip::from(a.rows()) + .and(&mut sums) .for_each(|row, sum| *sum = row.sum()); // show sums as a column matrix println!("{:8.4}", sums.insert_axis(Axis(1))); diff --git a/ndarray-rand/benches/bench.rs b/ndarray-rand/benches/bench.rs index b58d80a88..0e5eb2ff7 100644 --- a/ndarray-rand/benches/bench.rs +++ b/ndarray-rand/benches/bench.rs @@ -10,19 +10,22 @@ use rand_distr::Uniform; use test::Bencher; #[bench] -fn uniform_f32(b: &mut Bencher) { +fn uniform_f32(b: &mut Bencher) +{ let m = 100; b.iter(|| Array::random((m, m), Uniform::new(-1f32, 1.))); } #[bench] -fn norm_f32(b: &mut Bencher) { +fn norm_f32(b: &mut Bencher) +{ let m = 100; b.iter(|| Array::random((m, m), Normal::new(0f32, 1.).unwrap())); } #[bench] -fn norm_f64(b: &mut Bencher) { +fn norm_f64(b: &mut Bencher) +{ let m = 100; b.iter(|| Array::random((m, m), Normal::new(0f64, 1.).unwrap())); } diff --git a/ndarray-rand/src/lib.rs b/ndarray-rand/src/lib.rs index 448eb10d9..027198538 100644 --- a/ndarray-rand/src/lib.rs +++ b/ndarray-rand/src/lib.rs @@ -35,17 +35,19 @@ use crate::rand::seq::index; use crate::rand::{thread_rng, Rng, SeedableRng}; use ndarray::{Array, Axis, RemoveAxis, ShapeBuilder}; -use ndarray::{ArrayBase, DataOwned, RawData, Data, Dimension}; +use ndarray::{ArrayBase, Data, DataOwned, Dimension, RawData}; #[cfg(feature = "quickcheck")] use quickcheck::{Arbitrary, Gen}; /// `rand`, re-exported for convenience and version-compatibility. -pub mod rand { +pub mod rand +{ pub use rand::*; } /// `rand-distr`, re-exported for convenience and version-compatibility. -pub mod rand_distr { +pub mod rand_distr +{ pub use rand_distr::*; } @@ -217,11 +219,7 @@ where /// # } /// ``` fn sample_axis_using( - &self, - axis: Axis, - n_samples: usize, - strategy: SamplingStrategy, - rng: &mut R, + &self, axis: Axis, n_samples: usize, strategy: SamplingStrategy, rng: &mut R, ) -> Array where R: Rng + ?Sized, @@ -263,13 +261,7 @@ where 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 + fn sample_axis_using(&self, axis: Axis, n_samples: usize, strategy: SamplingStrategy, rng: &mut R) -> Array where R: Rng + ?Sized, A: Copy, @@ -281,9 +273,7 @@ where let distribution = Uniform::from(0..self.len_of(axis)); (0..n_samples).map(|_| distribution.sample(rng)).collect() } - SamplingStrategy::WithoutReplacement => { - index::sample(rng, self.len_of(axis), n_samples).into_vec() - } + SamplingStrategy::WithoutReplacement => index::sample(rng, self.len_of(axis), n_samples).into_vec(), }; self.select(axis, &indices) } @@ -296,15 +286,18 @@ where /// [`sample_axis`]: RandomExt::sample_axis /// [`sample_axis_using`]: RandomExt::sample_axis_using #[derive(Debug, Clone)] -pub enum SamplingStrategy { +pub enum SamplingStrategy +{ WithReplacement, WithoutReplacement, } // `Arbitrary` enables `quickcheck` to generate random `SamplingStrategy` values for testing. #[cfg(feature = "quickcheck")] -impl Arbitrary for SamplingStrategy { - fn arbitrary(g: &mut Gen) -> Self { +impl Arbitrary for SamplingStrategy +{ + fn arbitrary(g: &mut Gen) -> Self + { if bool::arbitrary(g) { SamplingStrategy::WithReplacement } else { @@ -313,7 +306,8 @@ impl Arbitrary for SamplingStrategy { } } -fn get_rng() -> SmallRng { +fn get_rng() -> SmallRng +{ SmallRng::from_rng(thread_rng()).expect("create SmallRng from thread_rng failed") } @@ -333,15 +327,15 @@ fn get_rng() -> SmallRng { /// // [ -0.6810, 0.1678, -0.9487, 0.3150, 1.2981]] /// # } #[derive(Copy, Clone, Debug)] -#[deprecated(since="0.14.0", note="Redundant with rand 0.8")] +#[deprecated(since = "0.14.0", note = "Redundant with rand 0.8")] pub struct F32(pub S); #[allow(deprecated)] impl Distribution for F32 -where - S: Distribution, +where S: Distribution { - fn sample(&self, rng: &mut R) -> f32 { + fn sample(&self, rng: &mut R) -> f32 + { self.0.sample(rng) as f32 } } diff --git a/ndarray-rand/tests/tests.rs b/ndarray-rand/tests/tests.rs index 5a0cd6c1b..2db040310 100644 --- a/ndarray-rand/tests/tests.rs +++ b/ndarray-rand/tests/tests.rs @@ -8,7 +8,8 @@ use ndarray_rand::{RandomExt, SamplingStrategy}; use quickcheck::{quickcheck, TestResult}; #[test] -fn test_dim() { +fn test_dim() +{ let (mm, nn) = (5, 5); for m in 0..mm { for n in 0..nn { @@ -22,7 +23,8 @@ fn test_dim() { } #[test] -fn test_dim_f() { +fn test_dim_f() +{ let (mm, nn) = (5, 5); for m in 0..mm { for n in 0..nn { @@ -36,15 +38,19 @@ fn test_dim_f() { } #[test] -fn sample_axis_on_view() { +fn sample_axis_on_view() +{ let m = 5; let a = Array::random((m, 4), Uniform::new(0., 2.)); - let _samples = a.view().sample_axis(Axis(0), m, SamplingStrategy::WithoutReplacement); + let _samples = a + .view() + .sample_axis(Axis(0), m, SamplingStrategy::WithoutReplacement); } #[test] #[should_panic] -fn oversampling_without_replacement_should_panic() { +fn oversampling_without_replacement_should_panic() +{ let m = 5; let a = Array::random((m, 4), Uniform::new(0., 2.)); let _samples = a.sample_axis(Axis(0), m + 1, SamplingStrategy::WithoutReplacement); @@ -109,12 +115,8 @@ quickcheck! { } } -fn sampling_works( - a: &Array2, - strategy: SamplingStrategy, - axis: Axis, - n_samples: usize, -) -> bool { +fn sampling_works(a: &Array2, strategy: SamplingStrategy, axis: Axis, n_samples: usize) -> bool +{ let samples = a.sample_axis(axis, n_samples, strategy); samples .axis_iter(axis) @@ -122,13 +124,15 @@ fn sampling_works( } // Check if, when sliced along `axis`, there is at least one lane in `a` equal to `b` -fn is_subset(a: &Array2, b: &ArrayView1, axis: Axis) -> bool { +fn is_subset(a: &Array2, b: &ArrayView1, axis: Axis) -> bool +{ a.axis_iter(axis).any(|lane| &lane == b) } #[test] #[should_panic] -fn sampling_without_replacement_from_a_zero_length_axis_should_panic() { +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 _samples = a.sample_axis(Axis(0), 1, SamplingStrategy::WithoutReplacement); @@ -136,7 +140,8 @@ fn sampling_without_replacement_from_a_zero_length_axis_should_panic() { #[test] #[should_panic] -fn sampling_with_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 _samples = a.sample_axis(Axis(0), 1, SamplingStrategy::WithReplacement); diff --git a/src/aliases.rs b/src/aliases.rs index 9a8ea8f2c..5df0c95ec 100644 --- a/src/aliases.rs +++ b/src/aliases.rs @@ -7,50 +7,58 @@ use crate::{ArcArray, Array, ArrayView, ArrayViewMut, Ix, IxDynImpl}; /// Create a zero-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub const fn Ix0() -> Ix0 { +pub const fn Ix0() -> Ix0 +{ Dim::new([]) } /// Create a one-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub const fn Ix1(i0: Ix) -> Ix1 { +pub const fn Ix1(i0: Ix) -> Ix1 +{ Dim::new([i0]) } /// Create a two-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub const fn Ix2(i0: Ix, i1: Ix) -> Ix2 { +pub const fn Ix2(i0: Ix, i1: Ix) -> Ix2 +{ Dim::new([i0, i1]) } /// Create a three-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub const fn Ix3(i0: Ix, i1: Ix, i2: Ix) -> Ix3 { +pub const fn Ix3(i0: Ix, i1: Ix, i2: Ix) -> Ix3 +{ Dim::new([i0, i1, i2]) } /// Create a four-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub const fn Ix4(i0: Ix, i1: Ix, i2: Ix, i3: Ix) -> Ix4 { +pub const fn Ix4(i0: Ix, i1: Ix, i2: Ix, i3: Ix) -> Ix4 +{ Dim::new([i0, i1, i2, i3]) } /// Create a five-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub const fn Ix5(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix) -> Ix5 { +pub const fn Ix5(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix) -> Ix5 +{ Dim::new([i0, i1, i2, i3, i4]) } /// Create a six-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub const fn Ix6(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix, i5: Ix) -> Ix6 { +pub const fn Ix6(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix, i5: Ix) -> Ix6 +{ Dim::new([i0, i1, i2, i3, i4, i5]) } /// Create a dynamic-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub fn IxDyn(ix: &[Ix]) -> IxDyn { +pub fn IxDyn(ix: &[Ix]) -> IxDyn +{ Dim(ix) } diff --git a/src/argument_traits.rs b/src/argument_traits.rs index 82d4869a9..de8ac7f99 100644 --- a/src/argument_traits.rs +++ b/src/argument_traits.rs @@ -4,36 +4,45 @@ use std::mem::MaybeUninit; use crate::math_cell::MathCell; /// A producer element that can be assigned to once -pub trait AssignElem { +pub trait AssignElem +{ /// Assign the value `input` to the element that self represents. fn assign_elem(self, input: T); } /// Assignable element, simply `*self = input`. -impl<'a, T> AssignElem for &'a mut T { - fn assign_elem(self, input: T) { +impl<'a, T> AssignElem for &'a mut T +{ + fn assign_elem(self, input: T) + { *self = input; } } /// Assignable element, simply `self.set(input)`. -impl<'a, T> AssignElem for &'a Cell { - fn assign_elem(self, input: T) { +impl<'a, T> AssignElem for &'a Cell +{ + fn assign_elem(self, input: T) + { self.set(input); } } /// Assignable element, simply `self.set(input)`. -impl<'a, T> AssignElem for &'a MathCell { - fn assign_elem(self, input: T) { +impl<'a, T> AssignElem for &'a MathCell +{ + fn assign_elem(self, input: T) + { self.set(input); } } /// Assignable element, the item in the MaybeUninit is overwritten (prior value, if any, is not /// read or dropped). -impl<'a, T> AssignElem for &'a mut MaybeUninit { - fn assign_elem(self, input: T) { +impl<'a, T> AssignElem for &'a mut MaybeUninit +{ + fn assign_elem(self, input: T) + { *self = MaybeUninit::new(input); } } diff --git a/src/array_approx.rs b/src/array_approx.rs index a40982a56..286a1146c 100644 --- a/src/array_approx.rs +++ b/src/array_approx.rs @@ -1,5 +1,6 @@ #[cfg(feature = "approx")] -mod approx_methods { +mod approx_methods +{ use crate::imp_prelude::*; impl ArrayBase @@ -24,12 +25,7 @@ mod approx_methods { /// apart; and the absolute difference otherwise. /// /// **Requires crate feature `"approx"`** - pub fn relative_eq( - &self, - other: &ArrayBase, - epsilon: A::Epsilon, - max_relative: A::Epsilon, - ) -> bool + pub fn relative_eq(&self, other: &ArrayBase, epsilon: A::Epsilon, max_relative: A::Epsilon) -> bool where A: ::approx::RelativeEq, A::Epsilon: Clone, diff --git a/src/array_serde.rs b/src/array_serde.rs index a6f3c617c..31b613d4c 100644 --- a/src/array_serde.rs +++ b/src/array_serde.rs @@ -9,11 +9,11 @@ use serde::de::{self, MapAccess, SeqAccess, Visitor}; use serde::ser::{SerializeSeq, SerializeStruct}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::fmt; -use std::marker::PhantomData; use alloc::format; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +use std::fmt; +use std::marker::PhantomData; use crate::imp_prelude::*; @@ -24,8 +24,7 @@ use crate::IntoDimension; /// Verifies that the version of the deserialized array matches the current /// `ARRAY_FORMAT_VERSION`. pub fn verify_version(v: u8) -> Result<(), E> -where - E: de::Error, +where E: de::Error { if v != ARRAY_FORMAT_VERSION { let err_msg = format!("unknown array version: {}", v); @@ -37,12 +36,10 @@ where /// **Requires crate feature `"serde"`** impl Serialize for Dim -where - I: Serialize, +where I: Serialize { fn serialize(&self, serializer: Se) -> Result - where - Se: Serializer, + where Se: Serializer { self.ix().serialize(serializer) } @@ -50,32 +47,30 @@ where /// **Requires crate feature `"serde"`** impl<'de, I> Deserialize<'de> for Dim -where - I: Deserialize<'de>, +where I: Deserialize<'de> { fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, + where D: Deserializer<'de> { I::deserialize(deserializer).map(Dim::new) } } /// **Requires crate feature `"serde"`** -impl Serialize for IxDyn { +impl Serialize for IxDyn +{ fn serialize(&self, serializer: Se) -> Result - where - Se: Serializer, + where Se: Serializer { self.ix().serialize(serializer) } } /// **Requires crate feature `"serde"`** -impl<'de> Deserialize<'de> for IxDyn { +impl<'de> Deserialize<'de> for IxDyn +{ fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, + where D: Deserializer<'de> { let v = Vec::::deserialize(deserializer)?; Ok(v.into_dimension()) @@ -90,8 +85,7 @@ where S: Data, { fn serialize(&self, serializer: Se) -> Result - where - Se: Serializer, + where Se: Serializer { let mut state = serializer.serialize_struct("Array", 3)?; state.serialize_field("v", &ARRAY_FORMAT_VERSION)?; @@ -110,8 +104,7 @@ where D: Dimension + Serialize, { fn serialize(&self, serializer: S) -> Result - where - S: Serializer, + where S: Serializer { let iter = &self.0; let mut seq = serializer.serialize_seq(Some(iter.len()))?; @@ -122,19 +115,23 @@ where } } -struct ArrayVisitor { +struct ArrayVisitor +{ _marker_a: PhantomData, _marker_b: PhantomData, } -enum ArrayField { +enum ArrayField +{ Version, Dim, Data, } -impl ArrayVisitor { - pub fn new() -> Self { +impl ArrayVisitor +{ + pub fn new() -> Self + { ArrayVisitor { _marker_a: PhantomData, _marker_b: PhantomData, @@ -152,30 +149,30 @@ where S: DataOwned, { fn deserialize(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, + where D: Deserializer<'de> { deserializer.deserialize_struct("Array", ARRAY_FIELDS, ArrayVisitor::new()) } } -impl<'de> Deserialize<'de> for ArrayField { +impl<'de> Deserialize<'de> for ArrayField +{ fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, + where D: Deserializer<'de> { struct ArrayFieldVisitor; - impl<'de> Visitor<'de> for ArrayFieldVisitor { + impl<'de> Visitor<'de> for ArrayFieldVisitor + { type Value = ArrayField; - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result + { formatter.write_str(r#""v", "dim", or "data""#) } fn visit_str(self, value: &str) -> Result - where - E: de::Error, + where E: de::Error { match value { "v" => Ok(ArrayField::Version), @@ -186,17 +183,13 @@ impl<'de> Deserialize<'de> for ArrayField { } fn visit_bytes(self, value: &[u8]) -> Result - where - E: de::Error, + where E: de::Error { match value { b"v" => Ok(ArrayField::Version), b"dim" => Ok(ArrayField::Dim), b"data" => Ok(ArrayField::Data), - other => Err(de::Error::unknown_field( - &format!("{:?}", other), - ARRAY_FIELDS, - )), + other => Err(de::Error::unknown_field(&format!("{:?}", other), ARRAY_FIELDS)), } } } @@ -213,13 +206,13 @@ where { type Value = ArrayBase; - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result + { formatter.write_str("ndarray representation") } fn visit_seq(self, mut visitor: V) -> Result, V::Error> - where - V: SeqAccess<'de>, + where V: SeqAccess<'de> { let v: u8 = match visitor.next_element()? { Some(value) => value, @@ -252,8 +245,7 @@ where } fn visit_map(self, mut visitor: V) -> Result, V::Error> - where - V: MapAccess<'de>, + where V: MapAccess<'de> { let mut v: Option = None; let mut data: Option> = None; diff --git a/src/arrayformat.rs b/src/arrayformat.rs index ec5b041d9..202805604 100644 --- a/src/arrayformat.rs +++ b/src/arrayformat.rs @@ -7,8 +7,8 @@ // except according to those terms. use super::{ArrayBase, ArrayView, Axis, Data, Dimension, NdProducer}; use crate::aliases::{Ix1, IxDyn}; -use std::fmt; use alloc::format; +use std::fmt; /// Default threshold, below this element count, we don't ellipsize const ARRAY_MANY_ELEMENT_LIMIT: usize = 500; @@ -29,14 +29,17 @@ const AXIS_2D_OVERFLOW_LIMIT: usize = 22; const ELLIPSIS: &str = "..."; #[derive(Clone, Debug)] -struct FormatOptions { +struct FormatOptions +{ axis_collapse_limit: usize, axis_collapse_limit_next_last: usize, axis_collapse_limit_last: usize, } -impl FormatOptions { - pub(crate) fn default_for_array(nelem: usize, no_limit: bool) -> Self { +impl FormatOptions +{ + pub(crate) fn default_for_array(nelem: usize, no_limit: bool) -> Self + { let default = Self { axis_collapse_limit: AXIS_LIMIT_STACKED, axis_collapse_limit_next_last: AXIS_LIMIT_COL, @@ -45,7 +48,8 @@ impl FormatOptions { default.set_no_limit(no_limit || nelem < ARRAY_MANY_ELEMENT_LIMIT) } - fn set_no_limit(mut self, no_limit: bool) -> Self { + fn set_no_limit(mut self, no_limit: bool) -> Self + { if no_limit { self.axis_collapse_limit = std::usize::MAX; self.axis_collapse_limit_next_last = std::usize::MAX; @@ -56,7 +60,8 @@ impl FormatOptions { /// Axis length collapse limit before ellipsizing, where `axis_rindex` is /// the index of the axis from the back. - pub(crate) fn collapse_limit(&self, axis_rindex: usize) -> usize { + pub(crate) fn collapse_limit(&self, axis_rindex: usize) -> usize + { match axis_rindex { 0 => self.axis_collapse_limit_last, 1 => self.axis_collapse_limit_next_last, @@ -78,13 +83,10 @@ impl FormatOptions { /// * `fmt_elem`: A function that formats an element in the list, given the /// formatter and the index of the item in the list. fn format_with_overflow( - f: &mut fmt::Formatter<'_>, - length: usize, - limit: usize, - separator: &str, - ellipsis: &str, + f: &mut fmt::Formatter<'_>, length: usize, limit: usize, separator: &str, ellipsis: &str, fmt_elem: &mut dyn FnMut(&mut fmt::Formatter, usize) -> fmt::Result, -) -> fmt::Result { +) -> fmt::Result +{ if length == 0 { // no-op } else if length <= limit { @@ -111,10 +113,7 @@ fn format_with_overflow( } fn format_array( - array: &ArrayBase, - f: &mut fmt::Formatter<'_>, - format: F, - fmt_opt: &FormatOptions, + array: &ArrayBase, f: &mut fmt::Formatter<'_>, format: F, fmt_opt: &FormatOptions, ) -> fmt::Result where F: FnMut(&A, &mut fmt::Formatter<'_>) -> fmt::Result + Clone, @@ -127,11 +126,7 @@ where } fn format_array_inner( - view: ArrayView, - f: &mut fmt::Formatter<'_>, - mut format: F, - fmt_opt: &FormatOptions, - depth: usize, + view: ArrayView, f: &mut fmt::Formatter<'_>, mut format: F, fmt_opt: &FormatOptions, depth: usize, full_ndim: usize, ) -> fmt::Result where @@ -150,14 +145,9 @@ where &[len] => { let view = view.view().into_dimensionality::().unwrap(); f.write_str("[")?; - format_with_overflow( - f, - len, - fmt_opt.collapse_limit(0), - ", ", - ELLIPSIS, - &mut |f, index| format(&view[index], f), - )?; + format_with_overflow(f, len, fmt_opt.collapse_limit(0), ", ", ELLIPSIS, &mut |f, index| { + format(&view[index], f) + })?; f.write_str("]")?; } // For n-dimensional arrays, we proceed recursively @@ -169,14 +159,7 @@ where f.write_str("[")?; let limit = fmt_opt.collapse_limit(full_ndim - depth - 1); format_with_overflow(f, shape[0], limit, &separator, ELLIPSIS, &mut |f, index| { - format_array_inner( - view.index_axis(Axis(0), index), - f, - format.clone(), - fmt_opt, - depth + 1, - full_ndim, - ) + format_array_inner(view.index_axis(Axis(0), index), f, format.clone(), fmt_opt, depth + 1, full_ndim) })?; f.write_str("]")?; } @@ -190,10 +173,10 @@ where /// /// The array is shown in multiline style. impl fmt::Display for ArrayBase -where - S: Data, +where S: Data { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { let fmt_opt = FormatOptions::default_for_array(self.len(), f.alternate()); format_array(self, f, <_>::fmt, &fmt_opt) } @@ -204,10 +187,10 @@ where /// /// The array is shown in multiline style. impl fmt::Debug for ArrayBase -where - S: Data, +where S: Data { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { let fmt_opt = FormatOptions::default_for_array(self.len(), f.alternate()); format_array(self, f, <_>::fmt, &fmt_opt)?; @@ -232,10 +215,10 @@ where /// /// The array is shown in multiline style. impl fmt::LowerExp for ArrayBase -where - S: Data, +where S: Data { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { let fmt_opt = FormatOptions::default_for_array(self.len(), f.alternate()); format_array(self, f, <_>::fmt, &fmt_opt) } @@ -246,10 +229,10 @@ where /// /// The array is shown in multiline style. impl fmt::UpperExp for ArrayBase -where - S: Data, +where S: Data { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { let fmt_opt = FormatOptions::default_for_array(self.len(), f.alternate()); format_array(self, f, <_>::fmt, &fmt_opt) } @@ -259,10 +242,10 @@ where /// /// The array is shown in multiline style. impl fmt::LowerHex for ArrayBase -where - S: Data, +where S: Data { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { let fmt_opt = FormatOptions::default_for_array(self.len(), f.alternate()); format_array(self, f, <_>::fmt, &fmt_opt) } @@ -273,27 +256,29 @@ where /// /// The array is shown in multiline style. impl fmt::Binary for ArrayBase -where - S: Data, +where S: Data { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { let fmt_opt = FormatOptions::default_for_array(self.len(), f.alternate()); format_array(self, f, <_>::fmt, &fmt_opt) } } #[cfg(test)] -mod formatting_with_omit { - use itertools::Itertools; +mod formatting_with_omit +{ #[cfg(not(feature = "std"))] use alloc::string::String; #[cfg(not(feature = "std"))] use alloc::vec::Vec; + use itertools::Itertools; use super::*; use crate::prelude::*; - fn assert_str_eq(expected: &str, actual: &str) { + fn assert_str_eq(expected: &str, actual: &str) + { // use assert to avoid printing the strings twice on failure assert!( expected == actual, @@ -303,11 +288,8 @@ mod formatting_with_omit { ); } - fn ellipsize( - limit: usize, - sep: &str, - elements: impl IntoIterator, - ) -> String { + fn ellipsize(limit: usize, sep: &str, elements: impl IntoIterator) -> String + { let elements = elements.into_iter().collect::>(); let edge = limit / 2; if elements.len() <= limit { @@ -325,7 +307,8 @@ mod formatting_with_omit { } #[test] - fn empty_arrays() { + fn empty_arrays() + { let a: Array2 = arr2(&[[], []]); let actual = format!("{}", a); let expected = "[[]]"; @@ -333,7 +316,8 @@ mod formatting_with_omit { } #[test] - fn zero_length_axes() { + fn zero_length_axes() + { let a = Array3::::zeros((3, 0, 4)); let actual = format!("{}", a); let expected = "[[[]]]"; @@ -341,7 +325,8 @@ mod formatting_with_omit { } #[test] - fn dim_0() { + fn dim_0() + { let element = 12; let a = arr0(element); let actual = format!("{}", a); @@ -350,7 +335,8 @@ mod formatting_with_omit { } #[test] - fn dim_1() { + fn dim_1() + { let overflow: usize = 2; let a = Array1::from_elem(ARRAY_MANY_ELEMENT_LIMIT + overflow, 1); let actual = format!("{}", a); @@ -359,7 +345,8 @@ mod formatting_with_omit { } #[test] - fn dim_1_alternate() { + fn dim_1_alternate() + { let overflow: usize = 2; let a = Array1::from_elem(ARRAY_MANY_ELEMENT_LIMIT + overflow, 1); let actual = format!("{:#}", a); @@ -368,12 +355,10 @@ mod formatting_with_omit { } #[test] - fn dim_2_last_axis_overflow() { + fn dim_2_last_axis_overflow() + { let overflow: usize = 2; - let a = Array2::from_elem( - (AXIS_2D_OVERFLOW_LIMIT, AXIS_2D_OVERFLOW_LIMIT + overflow), - 1, - ); + let a = Array2::from_elem((AXIS_2D_OVERFLOW_LIMIT, AXIS_2D_OVERFLOW_LIMIT + overflow), 1); let actual = format!("{}", a); let expected = "\ [[1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1], @@ -391,7 +376,8 @@ mod formatting_with_omit { } #[test] - fn dim_2_non_last_axis_overflow() { + fn dim_2_non_last_axis_overflow() + { let a = Array2::from_elem((ARRAY_MANY_ELEMENT_LIMIT / 10, 10), 1); let actual = format!("{}", a); let row = format!("{}", a.row(0)); @@ -403,7 +389,8 @@ mod formatting_with_omit { } #[test] - fn dim_2_non_last_axis_overflow_alternate() { + fn dim_2_non_last_axis_overflow_alternate() + { let a = Array2::from_elem((AXIS_LIMIT_COL * 4, 6), 1); let actual = format!("{:#}", a); let row = format!("{}", a.row(0)); @@ -412,15 +399,10 @@ mod formatting_with_omit { } #[test] - fn dim_2_multi_directional_overflow() { + fn dim_2_multi_directional_overflow() + { let overflow: usize = 2; - let a = Array2::from_elem( - ( - AXIS_2D_OVERFLOW_LIMIT + overflow, - AXIS_2D_OVERFLOW_LIMIT + overflow, - ), - 1, - ); + let a = Array2::from_elem((AXIS_2D_OVERFLOW_LIMIT + overflow, AXIS_2D_OVERFLOW_LIMIT + overflow), 1); let actual = format!("{}", a); let row = format!("[{}]", ellipsize(AXIS_LIMIT_ROW, ", ", a.row(0))); let expected = format!( @@ -431,15 +413,10 @@ mod formatting_with_omit { } #[test] - fn dim_2_multi_directional_overflow_alternate() { + fn dim_2_multi_directional_overflow_alternate() + { let overflow: usize = 2; - let a = Array2::from_elem( - ( - AXIS_2D_OVERFLOW_LIMIT + overflow, - AXIS_2D_OVERFLOW_LIMIT + overflow, - ), - 1, - ); + let a = Array2::from_elem((AXIS_2D_OVERFLOW_LIMIT + overflow, AXIS_2D_OVERFLOW_LIMIT + overflow), 1); let actual = format!("{:#}", a); let row = format!("{}", a.row(0)); let expected = format!("[{}]", (0..a.nrows()).map(|_| &row).format(",\n ")); @@ -447,13 +424,11 @@ mod formatting_with_omit { } #[test] - fn dim_3_overflow_most() { - let a = Array3::from_shape_fn( - (AXIS_LIMIT_STACKED + 1, AXIS_LIMIT_COL, AXIS_LIMIT_ROW + 1), - |(i, j, k)| { - 1000. + (100. * ((i as f64).sqrt() + (j as f64).sin() + k as f64)).round() / 100. - }, - ); + fn dim_3_overflow_most() + { + let a = Array3::from_shape_fn((AXIS_LIMIT_STACKED + 1, AXIS_LIMIT_COL, AXIS_LIMIT_ROW + 1), |(i, j, k)| { + 1000. + (100. * ((i as f64).sqrt() + (j as f64).sin() + k as f64)).round() / 100. + }); let actual = format!("{:6.1}", a); let expected = "\ [[[1000.0, 1001.0, 1002.0, 1003.0, 1004.0, ..., 1007.0, 1008.0, 1009.0, 1010.0, 1011.0], @@ -533,7 +508,8 @@ mod formatting_with_omit { } #[test] - fn dim_4_overflow_outer() { + fn dim_4_overflow_outer() + { let a = Array4::from_shape_fn((10, 10, 3, 3), |(i, j, k, l)| i + j + k + l); let actual = format!("{:2}", a); // Generated using NumPy with: diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 8d44c1e72..5c376cb0a 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -19,12 +19,16 @@ use crate::imp_prelude::*; use crate::{ dimension, iter::{Iter, IterMut}, - numeric_util, FoldWhile, NdIndex, Zip, + numeric_util, + FoldWhile, + NdIndex, + Zip, }; #[cold] #[inline(never)] -pub(crate) fn array_out_of_bounds() -> ! { +pub(crate) fn array_out_of_bounds() -> ! +{ panic!("ndarray: index out of bounds"); } @@ -49,7 +53,8 @@ where { type Output = S::Elem; #[inline] - fn index(&self, index: I) -> &S::Elem { + fn index(&self, index: I) -> &S::Elem + { debug_bounds_check!(self, index); unsafe { &*self.ptr.as_ptr().offset( @@ -71,7 +76,8 @@ where S: DataMut, { #[inline] - fn index_mut(&mut self, index: I) -> &mut S::Elem { + fn index_mut(&mut self, index: I) -> &mut S::Elem + { debug_bounds_check!(self, index); unsafe { &mut *self.as_mut_ptr().offset( @@ -92,7 +98,8 @@ where S2: Data, D: Dimension, { - fn eq(&self, rhs: &ArrayBase) -> bool { + fn eq(&self, rhs: &ArrayBase) -> bool + { if self.shape() != rhs.shape() { return false; } @@ -124,7 +131,8 @@ where S2: Data, D: Dimension, { - fn eq(&self, rhs: &&ArrayBase) -> bool { + fn eq(&self, rhs: &&ArrayBase) -> bool + { *self == **rhs } } @@ -139,7 +147,8 @@ where S2: Data, D: Dimension, { - fn eq(&self, rhs: &ArrayBase) -> bool { + fn eq(&self, rhs: &ArrayBase) -> bool + { **self == *rhs } } @@ -153,20 +162,19 @@ where } impl From> for ArrayBase -where - S: DataOwned, +where S: DataOwned { /// Create a one-dimensional array from a boxed slice (no copying needed). /// /// **Panics** if the length is greater than `isize::MAX`. - fn from(b: Box<[A]>) -> Self { + fn from(b: Box<[A]>) -> Self + { Self::from_vec(b.into_vec()) } } impl From> for ArrayBase -where - S: DataOwned, +where S: DataOwned { /// Create a one-dimensional array from a vector (no copying needed). /// @@ -177,14 +185,14 @@ where /// /// let array = Array::from(vec![1., 2., 3., 4.]); /// ``` - fn from(v: Vec) -> Self { + fn from(v: Vec) -> Self + { Self::from_vec(v) } } impl FromIterator for ArrayBase -where - S: DataOwned, +where S: DataOwned { /// Create a one-dimensional array from an iterable. /// @@ -198,8 +206,7 @@ where /// assert!(array == arr1(&[0, 1, 4, 9, 16])) /// ``` fn from_iter(iterable: I) -> ArrayBase - where - I: IntoIterator, + where I: IntoIterator { Self::from_iter(iterable) } @@ -213,7 +220,8 @@ where type Item = &'a S::Elem; type IntoIter = Iter<'a, S::Elem, D>; - fn into_iter(self) -> Self::IntoIter { + fn into_iter(self) -> Self::IntoIter + { self.iter() } } @@ -226,31 +234,32 @@ where type Item = &'a mut S::Elem; type IntoIter = IterMut<'a, S::Elem, D>; - fn into_iter(self) -> Self::IntoIter { + fn into_iter(self) -> Self::IntoIter + { self.iter_mut() } } impl<'a, A, D> IntoIterator for ArrayView<'a, A, D> -where - D: Dimension, +where D: Dimension { type Item = &'a A; type IntoIter = Iter<'a, A, D>; - fn into_iter(self) -> Self::IntoIter { + fn into_iter(self) -> Self::IntoIter + { self.into_iter_() } } impl<'a, A, D> IntoIterator for ArrayViewMut<'a, A, D> -where - D: Dimension, +where D: Dimension { type Item = &'a mut A; type IntoIter = IterMut<'a, A, D>; - fn into_iter(self) -> Self::IntoIter { + fn into_iter(self) -> Self::IntoIter + { self.into_iter_() } } @@ -262,7 +271,8 @@ where S::Elem: hash::Hash, { // Note: elements are hashed in the logical order - fn hash(&self, state: &mut H) { + fn hash(&self, state: &mut H) + { self.shape().hash(state); if let Some(self_s) = self.as_slice() { hash::Hash::hash_slice(self_s, state); @@ -312,13 +322,13 @@ pub const ARRAY_FORMAT_VERSION: u8 = 1u8; /// occur if `A` is zero-sized, because slices cannot contain more than /// `isize::MAX` number of bytes.) impl<'a, A, Slice: ?Sized> From<&'a Slice> for ArrayView<'a, A, Ix1> -where - Slice: AsRef<[A]>, +where Slice: AsRef<[A]> { /// Create a one-dimensional read-only array view of the data in `slice`. /// /// **Panics** if the slice length is greater than `isize::MAX`. - fn from(slice: &'a Slice) -> Self { + fn from(slice: &'a Slice) -> Self + { aview1(slice.as_ref()) } } @@ -328,9 +338,11 @@ where /// **Panics** if the product of non-zero axis lengths overflows `isize`. (This /// can only occur if A is zero-sized or if `N` is zero, because slices cannot /// contain more than `isize::MAX` number of bytes.) -impl<'a, A, const N: usize> From<&'a [[A; N]]> for ArrayView<'a, A, Ix2> { +impl<'a, A, const N: usize> From<&'a [[A; N]]> for ArrayView<'a, A, Ix2> +{ /// Create a two-dimensional read-only array view of the data in `slice` - fn from(xs: &'a [[A; N]]) -> Self { + fn from(xs: &'a [[A; N]]) -> Self + { aview2(xs) } } @@ -342,20 +354,21 @@ where D: Dimension, { /// Create a read-only array view of the array. - fn from(array: &'a ArrayBase) -> Self { + fn from(array: &'a ArrayBase) -> Self + { array.view() } } /// Implementation of `ArrayViewMut::from(&mut S)` where `S` is a slice or sliceable. impl<'a, A, Slice: ?Sized> From<&'a mut Slice> for ArrayViewMut<'a, A, Ix1> -where - Slice: AsMut<[A]>, +where Slice: AsMut<[A]> { /// Create a one-dimensional read-write array view of the data in `slice`. /// /// **Panics** if the slice length is greater than `isize::MAX`. - fn from(slice: &'a mut Slice) -> Self { + fn from(slice: &'a mut Slice) -> Self + { let xs = slice.as_mut(); if mem::size_of::() == 0 { assert!( @@ -372,15 +385,16 @@ where /// **Panics** if the product of non-zero axis lengths overflows `isize`. (This /// can only occur if `A` is zero-sized or if `N` is zero, because slices /// cannot contain more than `isize::MAX` number of bytes.) -impl<'a, A, const N: usize> From<&'a mut [[A; N]]> for ArrayViewMut<'a, A, Ix2> { +impl<'a, A, const N: usize> From<&'a mut [[A; N]]> for ArrayViewMut<'a, A, Ix2> +{ /// Create a two-dimensional read-write array view of the data in `slice` - fn from(xs: &'a mut [[A; N]]) -> Self { + fn from(xs: &'a mut [[A; N]]) -> Self + { let cols = N; let rows = xs.len(); let dim = Ix2(rows, cols); if size_of::() == 0 { - dimension::size_of_shape_checked(&dim) - .expect("Product of non-zero axis lengths must not overflow isize."); + dimension::size_of_shape_checked(&dim).expect("Product of non-zero axis lengths must not overflow isize."); } else if N == 0 { assert!( xs.len() <= isize::MAX as usize, @@ -404,16 +418,17 @@ where D: Dimension, { /// Create a read-write array view of the array. - fn from(array: &'a mut ArrayBase) -> Self { + fn from(array: &'a mut ArrayBase) -> Self + { array.view_mut() } } impl From> for ArcArray -where - D: Dimension, +where D: Dimension { - fn from(arr: Array) -> ArcArray { + fn from(arr: Array) -> ArcArray + { arr.into_shared() } } @@ -440,8 +455,7 @@ where /// /// ``` pub trait AsArray<'a, A: 'a, D = Ix1>: Into> -where - D: Dimension, +where D: Dimension { } impl<'a, A: 'a, D, T> AsArray<'a, A, D> for T @@ -471,7 +485,8 @@ where { // NOTE: We can implement Default for non-zero dimensional array views by // using an empty slice, however we need a trait for nonzero Dimension. - fn default() -> Self { + fn default() -> Self + { ArrayBase::default(D::default()) } } diff --git a/src/data_repr.rs b/src/data_repr.rs index f740988f4..c64cbcfcf 100644 --- a/src/data_repr.rs +++ b/src/data_repr.rs @@ -1,12 +1,12 @@ -use std::mem; -use std::mem::ManuallyDrop; -use std::ptr::NonNull; -use alloc::slice; +use crate::extension::nonnull; #[cfg(not(feature = "std"))] use alloc::borrow::ToOwned; +use alloc::slice; #[cfg(not(feature = "std"))] use alloc::vec::Vec; -use crate::extension::nonnull; +use std::mem; +use std::mem::ManuallyDrop; +use std::ptr::NonNull; use rawpointer::PointerExt; @@ -20,63 +20,68 @@ use rawpointer::PointerExt; // transmutable A -> B. #[derive(Debug)] #[repr(C)] -pub struct OwnedRepr { +pub struct OwnedRepr +{ ptr: NonNull, len: usize, capacity: usize, } -impl OwnedRepr { - pub(crate) fn from(v: Vec) -> Self { +impl OwnedRepr +{ + pub(crate) fn from(v: Vec) -> Self + { let mut v = ManuallyDrop::new(v); let len = v.len(); let capacity = v.capacity(); let ptr = nonnull::nonnull_from_vec_data(&mut v); - Self { - ptr, - len, - capacity, - } + Self { ptr, len, capacity } } - pub(crate) fn into_vec(self) -> Vec { + pub(crate) fn into_vec(self) -> Vec + { ManuallyDrop::new(self).take_as_vec() } - pub(crate) fn as_slice(&self) -> &[A] { - unsafe { - slice::from_raw_parts(self.ptr.as_ptr(), self.len) - } + pub(crate) fn as_slice(&self) -> &[A] + { + unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) } } - pub(crate) fn len(&self) -> usize { self.len } + pub(crate) fn len(&self) -> usize + { + self.len + } - pub(crate) fn as_ptr(&self) -> *const A { + pub(crate) fn as_ptr(&self) -> *const A + { self.ptr.as_ptr() } - pub(crate) fn as_ptr_mut(&self) -> *mut A { + pub(crate) fn as_ptr_mut(&self) -> *mut A + { self.ptr.as_ptr() } - pub(crate) fn as_nonnull_mut(&mut self) -> NonNull { + pub(crate) fn as_nonnull_mut(&mut self) -> NonNull + { self.ptr } /// Return end pointer - pub(crate) fn as_end_nonnull(&self) -> NonNull { - unsafe { - self.ptr.add(self.len) - } + pub(crate) fn as_end_nonnull(&self) -> NonNull + { + unsafe { self.ptr.add(self.len) } } /// Reserve `additional` elements; return the new pointer - /// + /// /// ## Safety /// /// Note that existing pointers into the data are invalidated #[must_use = "must use new pointer to update existing pointers"] - pub(crate) fn reserve(&mut self, additional: usize) -> NonNull { + pub(crate) fn reserve(&mut self, additional: usize) -> NonNull + { self.modify_as_vec(|mut v| { v.reserve(additional); v @@ -89,13 +94,15 @@ impl OwnedRepr { /// ## Safety /// /// The first `new_len` elements of the data should be valid. - pub(crate) unsafe fn set_len(&mut self, new_len: usize) { + pub(crate) unsafe fn set_len(&mut self, new_len: usize) + { debug_assert!(new_len <= self.capacity); self.len = new_len; } /// Return the length (number of elements in total) - pub(crate) fn release_all_elements(&mut self) -> usize { + pub(crate) fn release_all_elements(&mut self) -> usize + { let ret = self.len; self.len = 0; ret @@ -107,7 +114,8 @@ impl OwnedRepr { /// /// Caller must ensure the two types have the same representation. /// **Panics** if sizes don't match (which is not a sufficient check). - pub(crate) unsafe fn data_subst(self) -> OwnedRepr { + pub(crate) unsafe fn data_subst(self) -> OwnedRepr + { // necessary but not sufficient check assert_eq!(mem::size_of::(), mem::size_of::()); let self_ = ManuallyDrop::new(self); @@ -118,30 +126,32 @@ impl OwnedRepr { } } - fn modify_as_vec(&mut self, f: impl FnOnce(Vec) -> Vec) { + fn modify_as_vec(&mut self, f: impl FnOnce(Vec) -> Vec) + { let v = self.take_as_vec(); *self = Self::from(f(v)); } - fn take_as_vec(&mut self) -> Vec { + fn take_as_vec(&mut self) -> Vec + { let capacity = self.capacity; let len = self.len; self.len = 0; self.capacity = 0; - unsafe { - Vec::from_raw_parts(self.ptr.as_ptr(), len, capacity) - } + unsafe { Vec::from_raw_parts(self.ptr.as_ptr(), len, capacity) } } } impl Clone for OwnedRepr - where A: Clone +where A: Clone { - fn clone(&self) -> Self { + fn clone(&self) -> Self + { Self::from(self.as_slice().to_owned()) } - fn clone_from(&mut self, other: &Self) { + fn clone_from(&mut self, other: &Self) + { let mut v = self.take_as_vec(); let other = other.as_slice(); @@ -155,8 +165,10 @@ impl Clone for OwnedRepr } } -impl Drop for OwnedRepr { - fn drop(&mut self) { +impl Drop for OwnedRepr +{ + fn drop(&mut self) + { if self.capacity > 0 { // correct because: If the elements don't need dropping, an // empty Vec is ok. Only the Vec's allocation needs dropping. @@ -176,6 +188,5 @@ impl Drop for OwnedRepr { } } -unsafe impl Sync for OwnedRepr where A: Sync { } -unsafe impl Send for OwnedRepr where A: Send { } - +unsafe impl Sync for OwnedRepr where A: Sync {} +unsafe impl Send for OwnedRepr where A: Send {} diff --git a/src/data_traits.rs b/src/data_traits.rs index 50e5a3e6e..a2784b8d3 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -10,16 +10,14 @@ use rawpointer::PointerExt; -use std::mem::{self, size_of}; -use std::mem::MaybeUninit; -use std::ptr::NonNull; use alloc::sync::Arc; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +use std::mem::MaybeUninit; +use std::mem::{self, size_of}; +use std::ptr::NonNull; -use crate::{ - ArcArray, Array, ArrayBase, CowRepr, Dimension, OwnedArcRepr, OwnedRepr, RawViewRepr, ViewRepr, -}; +use crate::{ArcArray, Array, ArrayBase, CowRepr, Dimension, OwnedArcRepr, OwnedRepr, RawViewRepr, ViewRepr}; /// Array representation trait. /// @@ -31,13 +29,14 @@ use crate::{ /// Traits in Rust can serve many different roles. This trait is public because /// it is used as a bound on public methods. #[allow(clippy::missing_safety_doc)] // not implementable downstream -pub unsafe trait RawData: Sized { +pub unsafe trait RawData: Sized +{ /// The array element type. type Elem; #[doc(hidden)] // This method is only used for debugging - #[deprecated(note="Unused", since="0.15.2")] + #[deprecated(note = "Unused", since = "0.15.2")] fn _data_slice(&self) -> Option<&[Self::Elem]>; #[doc(hidden)] @@ -52,7 +51,8 @@ pub unsafe trait RawData: Sized { /// /// ***Internal trait, see `RawData`.*** #[allow(clippy::missing_safety_doc)] // not implementable downstream -pub unsafe trait RawDataMut: RawData { +pub unsafe trait RawDataMut: RawData +{ /// If possible, ensures that the array has unique access to its data. /// /// The implementer must ensure that if the input is contiguous, then the @@ -80,17 +80,15 @@ pub unsafe trait RawDataMut: RawData { /// /// ***Internal trait, see `RawData`.*** #[allow(clippy::missing_safety_doc)] // not implementable downstream -pub unsafe trait RawDataClone: RawData { +pub unsafe trait RawDataClone: RawData +{ #[doc(hidden)] /// Unsafe because, `ptr` must point inside the current storage. unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull); #[doc(hidden)] - unsafe fn clone_from_with_ptr( - &mut self, - other: &Self, - ptr: NonNull, - ) -> NonNull { + unsafe fn clone_from_with_ptr(&mut self, other: &Self, ptr: NonNull) -> NonNull + { let (data, ptr) = other.clone_with_ptr(ptr); *self = data; ptr @@ -103,7 +101,8 @@ pub unsafe trait RawDataClone: RawData { /// /// ***Internal trait, see `RawData`.*** #[allow(clippy::missing_safety_doc)] // not implementable downstream -pub unsafe trait Data: RawData { +pub unsafe trait Data: RawData +{ /// Converts the array to a uniquely owned array, cloning elements if necessary. #[doc(hidden)] #[allow(clippy::wrong_self_convention)] @@ -115,11 +114,8 @@ pub unsafe trait Data: RawData { /// Converts the array into `Array` if this is possible without /// cloning the array elements. Otherwise, returns `self_` unchanged. #[doc(hidden)] - fn try_into_owned_nocopy( - self_: ArrayBase, - ) -> Result, ArrayBase> - where - D: Dimension; + fn try_into_owned_nocopy(self_: ArrayBase) -> Result, ArrayBase> + where D: Dimension; /// Return a shared ownership (copy on write) array based on the existing one, /// cloning elements if necessary. @@ -148,7 +144,8 @@ pub unsafe trait Data: RawData { // the data is unique. You are also guaranteeing that `try_is_unique` always // returns `Some(_)`. #[allow(clippy::missing_safety_doc)] // not implementable downstream -pub unsafe trait DataMut: Data + RawDataMut { +pub unsafe trait DataMut: Data + RawDataMut +{ /// Ensures that the array has unique access to its data. #[doc(hidden)] #[inline] @@ -163,47 +160,61 @@ pub unsafe trait DataMut: Data + RawDataMut { /// Returns whether the array has unique access to its data. #[doc(hidden)] #[inline] - #[allow(clippy::wrong_self_convention)] // mut needed for Arc types - fn is_unique(&mut self) -> bool { + #[allow(clippy::wrong_self_convention)] // mut needed for Arc types + fn is_unique(&mut self) -> bool + { self.try_is_unique().unwrap() } } -unsafe impl RawData for RawViewRepr<*const A> { +unsafe impl RawData for RawViewRepr<*const A> +{ type Elem = A; #[inline] - fn _data_slice(&self) -> Option<&[A]> { + fn _data_slice(&self) -> Option<&[A]> + { None } #[inline(always)] - fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true } + fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool + { + true + } private_impl! {} } -unsafe impl RawDataClone for RawViewRepr<*const A> { - unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) { +unsafe impl RawDataClone for RawViewRepr<*const A> +{ + unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) + { (*self, ptr) } } -unsafe impl RawData for RawViewRepr<*mut A> { +unsafe impl RawData for RawViewRepr<*mut A> +{ type Elem = A; #[inline] - fn _data_slice(&self) -> Option<&[A]> { + fn _data_slice(&self) -> Option<&[A]> + { None } #[inline(always)] - fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true } + fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool + { + true + } private_impl! {} } -unsafe impl RawDataMut for RawViewRepr<*mut A> { +unsafe impl RawDataMut for RawViewRepr<*mut A> +{ #[inline] fn try_ensure_unique(_: &mut ArrayBase) where @@ -213,24 +224,30 @@ unsafe impl RawDataMut for RawViewRepr<*mut A> { } #[inline] - fn try_is_unique(&mut self) -> Option { + fn try_is_unique(&mut self) -> Option + { None } } -unsafe impl RawDataClone for RawViewRepr<*mut A> { - unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) { +unsafe impl RawDataClone for RawViewRepr<*mut A> +{ + unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) + { (*self, ptr) } } -unsafe impl RawData for OwnedArcRepr { +unsafe impl RawData for OwnedArcRepr +{ type Elem = A; - fn _data_slice(&self) -> Option<&[A]> { + fn _data_slice(&self) -> Option<&[A]> + { Some(self.0.as_slice()) } - fn _is_pointer_inbounds(&self, self_ptr: *const Self::Elem) -> bool { + fn _is_pointer_inbounds(&self, self_ptr: *const Self::Elem) -> bool + { self.0._is_pointer_inbounds(self_ptr) } @@ -239,8 +256,7 @@ unsafe impl RawData for OwnedArcRepr { // NOTE: Copy on write unsafe impl RawDataMut for OwnedArcRepr -where - A: Clone, +where A: Clone { fn try_ensure_unique(self_: &mut ArrayBase) where @@ -269,12 +285,14 @@ where } } - fn try_is_unique(&mut self) -> Option { + fn try_is_unique(&mut self) -> Option + { Some(Arc::get_mut(&mut self.0).is_some()) } } -unsafe impl Data for OwnedArcRepr { +unsafe impl Data for OwnedArcRepr +{ fn into_owned(mut self_: ArrayBase) -> Array where A: Clone, @@ -283,23 +301,16 @@ unsafe impl Data for OwnedArcRepr { Self::ensure_unique(&mut self_); let data = Arc::try_unwrap(self_.data.0).ok().unwrap(); // safe because data is equivalent - unsafe { - ArrayBase::from_data_ptr(data, self_.ptr) - .with_strides_dim(self_.strides, self_.dim) - } + unsafe { ArrayBase::from_data_ptr(data, self_.ptr).with_strides_dim(self_.strides, self_.dim) } } - fn try_into_owned_nocopy( - self_: ArrayBase, - ) -> Result, ArrayBase> - where - D: Dimension, + fn try_into_owned_nocopy(self_: ArrayBase) -> Result, ArrayBase> + where D: Dimension { match Arc::try_unwrap(self_.data.0) { Ok(owned_data) => unsafe { // Safe because the data is equivalent. - Ok(ArrayBase::from_data_ptr(owned_data, self_.ptr) - .with_strides_dim(self_.strides, self_.dim)) + Ok(ArrayBase::from_data_ptr(owned_data, self_.ptr).with_strides_dim(self_.strides, self_.dim)) }, Err(arc_data) => unsafe { // Safe because the data is equivalent; we're just @@ -323,21 +334,26 @@ unsafe impl Data for OwnedArcRepr { unsafe impl DataMut for OwnedArcRepr where A: Clone {} -unsafe impl RawDataClone for OwnedArcRepr { - unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) { +unsafe impl RawDataClone for OwnedArcRepr +{ + unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) + { // pointer is preserved (self.clone(), ptr) } } -unsafe impl RawData for OwnedRepr { +unsafe impl RawData for OwnedRepr +{ type Elem = A; - fn _data_slice(&self) -> Option<&[A]> { + fn _data_slice(&self) -> Option<&[A]> + { Some(self.as_slice()) } - fn _is_pointer_inbounds(&self, self_ptr: *const Self::Elem) -> bool { + fn _is_pointer_inbounds(&self, self_ptr: *const Self::Elem) -> bool + { let slc = self.as_slice(); let ptr = slc.as_ptr() as *mut A; let end = unsafe { ptr.add(slc.len()) }; @@ -347,7 +363,8 @@ unsafe impl RawData for OwnedRepr { private_impl! {} } -unsafe impl RawDataMut for OwnedRepr { +unsafe impl RawDataMut for OwnedRepr +{ #[inline] fn try_ensure_unique(_: &mut ArrayBase) where @@ -357,12 +374,14 @@ unsafe impl RawDataMut for OwnedRepr { } #[inline] - fn try_is_unique(&mut self) -> Option { + fn try_is_unique(&mut self) -> Option + { Some(true) } } -unsafe impl Data for OwnedRepr { +unsafe impl Data for OwnedRepr +{ #[inline] fn into_owned(self_: ArrayBase) -> Array where @@ -373,11 +392,8 @@ unsafe impl Data for OwnedRepr { } #[inline] - fn try_into_owned_nocopy( - self_: ArrayBase, - ) -> Result, ArrayBase> - where - D: Dimension, + fn try_into_owned_nocopy(self_: ArrayBase) -> Result, ArrayBase> + where D: Dimension { Ok(self_) } @@ -386,25 +402,21 @@ unsafe impl Data for OwnedRepr { unsafe impl DataMut for OwnedRepr {} unsafe impl RawDataClone for OwnedRepr -where - A: Clone, +where A: Clone { - unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) { + unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) + { let mut u = self.clone(); let mut new_ptr = u.as_nonnull_mut(); if size_of::() != 0 { - let our_off = - (ptr.as_ptr() as isize - self.as_ptr() as isize) / mem::size_of::() as isize; + let our_off = (ptr.as_ptr() as isize - self.as_ptr() as isize) / mem::size_of::() as isize; new_ptr = new_ptr.offset(our_off); } (u, new_ptr) } - unsafe fn clone_from_with_ptr( - &mut self, - other: &Self, - ptr: NonNull, - ) -> NonNull { + unsafe fn clone_from_with_ptr(&mut self, other: &Self, ptr: NonNull) -> NonNull + { let our_off = if size_of::() != 0 { (ptr.as_ptr() as isize - other.as_ptr() as isize) / mem::size_of::() as isize } else { @@ -415,21 +427,27 @@ where } } -unsafe impl<'a, A> RawData for ViewRepr<&'a A> { +unsafe impl<'a, A> RawData for ViewRepr<&'a A> +{ type Elem = A; #[inline] - fn _data_slice(&self) -> Option<&[A]> { + fn _data_slice(&self) -> Option<&[A]> + { None } #[inline(always)] - fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true } + fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool + { + true + } private_impl! {} } -unsafe impl<'a, A> Data for ViewRepr<&'a A> { +unsafe impl<'a, A> Data for ViewRepr<&'a A> +{ fn into_owned(self_: ArrayBase) -> Array where Self::Elem: Clone, @@ -438,37 +456,42 @@ unsafe impl<'a, A> Data for ViewRepr<&'a A> { self_.to_owned() } - fn try_into_owned_nocopy( - self_: ArrayBase, - ) -> Result, ArrayBase> - where - D: Dimension, + fn try_into_owned_nocopy(self_: ArrayBase) -> Result, ArrayBase> + where D: Dimension { Err(self_) } } -unsafe impl<'a, A> RawDataClone for ViewRepr<&'a A> { - unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) { +unsafe impl<'a, A> RawDataClone for ViewRepr<&'a A> +{ + unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) + { (*self, ptr) } } -unsafe impl<'a, A> RawData for ViewRepr<&'a mut A> { +unsafe impl<'a, A> RawData for ViewRepr<&'a mut A> +{ type Elem = A; #[inline] - fn _data_slice(&self) -> Option<&[A]> { + fn _data_slice(&self) -> Option<&[A]> + { None } #[inline(always)] - fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true } + fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool + { + true + } private_impl! {} } -unsafe impl<'a, A> RawDataMut for ViewRepr<&'a mut A> { +unsafe impl<'a, A> RawDataMut for ViewRepr<&'a mut A> +{ #[inline] fn try_ensure_unique(_: &mut ArrayBase) where @@ -478,12 +501,14 @@ unsafe impl<'a, A> RawDataMut for ViewRepr<&'a mut A> { } #[inline] - fn try_is_unique(&mut self) -> Option { + fn try_is_unique(&mut self) -> Option + { Some(true) } } -unsafe impl<'a, A> Data for ViewRepr<&'a mut A> { +unsafe impl<'a, A> Data for ViewRepr<&'a mut A> +{ fn into_owned(self_: ArrayBase) -> Array where Self::Elem: Clone, @@ -492,11 +517,8 @@ unsafe impl<'a, A> Data for ViewRepr<&'a mut A> { self_.to_owned() } - fn try_into_owned_nocopy( - self_: ArrayBase, - ) -> Result, ArrayBase> - where - D: Dimension, + fn try_into_owned_nocopy(self_: ArrayBase) -> Result, ArrayBase> + where D: Dimension { Err(self_) } @@ -517,10 +539,10 @@ unsafe impl<'a, A> DataMut for ViewRepr<&'a mut A> {} // unsharing storage before mutating it. The initially allocated storage must be mutable so // that it can be mutated directly - through .raw_view_mut_unchecked() - for initialization. #[allow(clippy::missing_safety_doc)] // not implementable downstream -pub unsafe trait DataOwned: Data { +pub unsafe trait DataOwned: Data +{ /// Corresponding owned data with MaybeUninit elements - type MaybeUninit: DataOwned> - + RawDataSubst; + type MaybeUninit: DataOwned> + RawDataSubst; #[doc(hidden)] fn new(elements: Vec) -> Self; @@ -541,34 +563,42 @@ pub unsafe trait DataShared: Clone + Data + RawDataClone {} unsafe impl DataShared for OwnedArcRepr {} unsafe impl<'a, A> DataShared for ViewRepr<&'a A> {} -unsafe impl DataOwned for OwnedRepr { +unsafe impl DataOwned for OwnedRepr +{ type MaybeUninit = OwnedRepr>; - fn new(elements: Vec) -> Self { + fn new(elements: Vec) -> Self + { OwnedRepr::from(elements) } - fn into_shared(self) -> OwnedArcRepr { + fn into_shared(self) -> OwnedArcRepr + { OwnedArcRepr(Arc::new(self)) } } -unsafe impl DataOwned for OwnedArcRepr { +unsafe impl DataOwned for OwnedArcRepr +{ type MaybeUninit = OwnedArcRepr>; - fn new(elements: Vec) -> Self { + fn new(elements: Vec) -> Self + { OwnedArcRepr(Arc::new(OwnedRepr::from(elements))) } - fn into_shared(self) -> OwnedArcRepr { + fn into_shared(self) -> OwnedArcRepr + { self } } -unsafe impl<'a, A> RawData for CowRepr<'a, A> { +unsafe impl<'a, A> RawData for CowRepr<'a, A> +{ type Elem = A; - fn _data_slice(&self) -> Option<&[A]> { + fn _data_slice(&self) -> Option<&[A]> + { #[allow(deprecated)] match self { CowRepr::View(view) => view._data_slice(), @@ -577,7 +607,8 @@ unsafe impl<'a, A> RawData for CowRepr<'a, A> { } #[inline] - fn _is_pointer_inbounds(&self, ptr: *const Self::Elem) -> bool { + fn _is_pointer_inbounds(&self, ptr: *const Self::Elem) -> bool + { match self { CowRepr::View(view) => view._is_pointer_inbounds(ptr), CowRepr::Owned(data) => data._is_pointer_inbounds(ptr), @@ -588,8 +619,7 @@ unsafe impl<'a, A> RawData for CowRepr<'a, A> { } unsafe impl<'a, A> RawDataMut for CowRepr<'a, A> -where - A: Clone, +where A: Clone { #[inline] fn try_ensure_unique(array: &mut ArrayBase) @@ -610,16 +640,17 @@ where } #[inline] - fn try_is_unique(&mut self) -> Option { + fn try_is_unique(&mut self) -> Option + { Some(self.is_owned()) } } unsafe impl<'a, A> RawDataClone for CowRepr<'a, A> -where - A: Clone, +where A: Clone { - unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) { + unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) + { match self { CowRepr::View(view) => { let (new_view, ptr) = view.clone_with_ptr(ptr); @@ -632,11 +663,8 @@ where } } - unsafe fn clone_from_with_ptr( - &mut self, - other: &Self, - ptr: NonNull, - ) -> NonNull { + unsafe fn clone_from_with_ptr(&mut self, other: &Self, ptr: NonNull) -> NonNull + { match (&mut *self, other) { (CowRepr::View(self_), CowRepr::View(other)) => self_.clone_from_with_ptr(other, ptr), (CowRepr::Owned(self_), CowRepr::Owned(other)) => self_.clone_from_with_ptr(other, ptr), @@ -654,7 +682,8 @@ where } } -unsafe impl<'a, A> Data for CowRepr<'a, A> { +unsafe impl<'a, A> Data for CowRepr<'a, A> +{ #[inline] fn into_owned(self_: ArrayBase, D>) -> Array where @@ -665,24 +694,19 @@ unsafe impl<'a, A> Data for CowRepr<'a, A> { CowRepr::View(_) => self_.to_owned(), CowRepr::Owned(data) => unsafe { // safe because the data is equivalent so ptr, dims remain valid - ArrayBase::from_data_ptr(data, self_.ptr) - .with_strides_dim(self_.strides, self_.dim) + ArrayBase::from_data_ptr(data, self_.ptr).with_strides_dim(self_.strides, self_.dim) }, } } - fn try_into_owned_nocopy( - self_: ArrayBase, - ) -> Result, ArrayBase> - where - D: Dimension, + fn try_into_owned_nocopy(self_: ArrayBase) -> Result, ArrayBase> + where D: Dimension { match self_.data { CowRepr::View(_) => Err(self_), CowRepr::Owned(data) => unsafe { // safe because the data is equivalent so ptr, dims remain valid - Ok(ArrayBase::from_data_ptr(data, self_.ptr) - .with_strides_dim(self_.strides, self_.dim)) + Ok(ArrayBase::from_data_ptr(data, self_.ptr).with_strides_dim(self_.strides, self_.dim)) }, } } @@ -696,7 +720,8 @@ unsafe impl<'a, A> DataMut for CowRepr<'a, A> where A: Clone {} /// keeping the same kind of storage. /// /// For example, `RawDataSubst` can map the type `OwnedRepr` to `OwnedRepr`. -pub trait RawDataSubst: RawData { +pub trait RawDataSubst: RawData +{ /// The resulting array storage of the same kind but substituted element type type Output: RawData; @@ -709,58 +734,72 @@ pub trait RawDataSubst: RawData { unsafe fn data_subst(self) -> Self::Output; } -impl RawDataSubst for OwnedRepr { +impl RawDataSubst for OwnedRepr +{ type Output = OwnedRepr; - unsafe fn data_subst(self) -> Self::Output { + unsafe fn data_subst(self) -> Self::Output + { self.data_subst() } } -impl RawDataSubst for OwnedArcRepr { +impl RawDataSubst for OwnedArcRepr +{ type Output = OwnedArcRepr; - unsafe fn data_subst(self) -> Self::Output { + unsafe fn data_subst(self) -> Self::Output + { OwnedArcRepr(Arc::from_raw(Arc::into_raw(self.0) as *const OwnedRepr)) } } -impl RawDataSubst for RawViewRepr<*const A> { +impl RawDataSubst for RawViewRepr<*const A> +{ type Output = RawViewRepr<*const B>; - unsafe fn data_subst(self) -> Self::Output { + unsafe fn data_subst(self) -> Self::Output + { RawViewRepr::new() } } -impl RawDataSubst for RawViewRepr<*mut A> { +impl RawDataSubst for RawViewRepr<*mut A> +{ type Output = RawViewRepr<*mut B>; - unsafe fn data_subst(self) -> Self::Output { + unsafe fn data_subst(self) -> Self::Output + { RawViewRepr::new() } } -impl<'a, A: 'a, B: 'a> RawDataSubst for ViewRepr<&'a A> { +impl<'a, A: 'a, B: 'a> RawDataSubst for ViewRepr<&'a A> +{ type Output = ViewRepr<&'a B>; - unsafe fn data_subst(self) -> Self::Output { + unsafe fn data_subst(self) -> Self::Output + { ViewRepr::new() } } -impl<'a, A: 'a, B: 'a> RawDataSubst for ViewRepr<&'a mut A> { +impl<'a, A: 'a, B: 'a> RawDataSubst for ViewRepr<&'a mut A> +{ type Output = ViewRepr<&'a mut B>; - unsafe fn data_subst(self) -> Self::Output { + unsafe fn data_subst(self) -> Self::Output + { ViewRepr::new() } } -impl<'a, A: 'a, B: 'a> RawDataSubst for CowRepr<'a, A> { +impl<'a, A: 'a, B: 'a> RawDataSubst for CowRepr<'a, A> +{ type Output = CowRepr<'a, B>; - unsafe fn data_subst(self) -> Self::Output { + unsafe fn data_subst(self) -> Self::Output + { match self { CowRepr::View(view) => CowRepr::View(view.data_subst()), CowRepr::Owned(owned) => CowRepr::Owned(owned.data_subst()), diff --git a/src/dimension/axes.rs b/src/dimension/axes.rs index 5660675b5..925b257a7 100644 --- a/src/dimension/axes.rs +++ b/src/dimension/axes.rs @@ -2,8 +2,7 @@ use crate::{Axis, Dimension, Ix, Ixs}; /// Create a new Axes iterator pub(crate) fn axes_of<'a, D>(d: &'a D, strides: &'a D) -> Axes<'a, D> -where - D: Dimension, +where D: Dimension { Axes { dim: d, @@ -38,7 +37,8 @@ where /// assert_eq!(largest_axis.len, 5); /// ``` #[derive(Debug)] -pub struct Axes<'a, D> { +pub struct Axes<'a, D> +{ dim: &'a D, strides: &'a D, start: usize, @@ -47,7 +47,8 @@ pub struct Axes<'a, D> { /// Description of the axis, its length and its stride. #[derive(Debug)] -pub struct AxisDescription { +pub struct AxisDescription +{ /// Axis identifier (index) pub axis: Axis, /// Length in count of elements of the current axis @@ -61,23 +62,27 @@ copy_and_clone!(AxisDescription); // AxisDescription can't really be empty // https://github.com/rust-ndarray/ndarray/pull/642#discussion_r296051702 #[allow(clippy::len_without_is_empty)] -impl AxisDescription { +impl AxisDescription +{ /// Return axis #[deprecated(note = "Use .axis field instead", since = "0.15.0")] #[inline(always)] - pub fn axis(self) -> Axis { + pub fn axis(self) -> Axis + { self.axis } /// Return length #[deprecated(note = "Use .len field instead", since = "0.15.0")] #[inline(always)] - pub fn len(self) -> Ix { + pub fn len(self) -> Ix + { self.len } /// Return stride #[deprecated(note = "Use .stride field instead", since = "0.15.0")] #[inline(always)] - pub fn stride(self) -> Ixs { + pub fn stride(self) -> Ixs + { self.stride } } @@ -85,13 +90,13 @@ impl AxisDescription { copy_and_clone!(['a, D] Axes<'a, D>); impl<'a, D> Iterator for Axes<'a, D> -where - D: Dimension, +where D: Dimension { /// Description of the axis, its length and its stride. type Item = AxisDescription; - fn next(&mut self) -> Option { + fn next(&mut self) -> Option + { if self.start < self.end { let i = self.start.post_inc(); Some(AxisDescription { @@ -105,8 +110,7 @@ where } fn fold(self, init: B, f: F) -> B - where - F: FnMut(B, AxisDescription) -> B, + where F: FnMut(B, AxisDescription) -> B { (self.start..self.end) .map(move |i| AxisDescription { @@ -117,17 +121,18 @@ where .fold(init, f) } - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { let len = self.end - self.start; (len, Some(len)) } } impl<'a, D> DoubleEndedIterator for Axes<'a, D> -where - D: Dimension, +where D: Dimension { - fn next_back(&mut self) -> Option { + fn next_back(&mut self) -> Option + { if self.start < self.end { let i = self.end.pre_dec(); Some(AxisDescription { @@ -141,20 +146,24 @@ where } } -trait IncOps: Copy { +trait IncOps: Copy +{ fn post_inc(&mut self) -> Self; fn pre_dec(&mut self) -> Self; } -impl IncOps for usize { +impl IncOps for usize +{ #[inline(always)] - fn post_inc(&mut self) -> Self { + fn post_inc(&mut self) -> Self + { let x = *self; *self += 1; x } #[inline(always)] - fn pre_dec(&mut self) -> Self { + fn pre_dec(&mut self) -> Self + { *self -= 1; *self } diff --git a/src/dimension/axis.rs b/src/dimension/axis.rs index f4625d2da..8c896f6b7 100644 --- a/src/dimension/axis.rs +++ b/src/dimension/axis.rs @@ -13,7 +13,7 @@ /// /// All array axis arguments use this type to make the code easier to write /// correctly and easier to understand. -/// +/// /// For example: in a method like `index_axis(axis, index)` the code becomes /// self-explanatory when it's called like `.index_axis(Axis(1), i)`; it's /// evident which integer is the axis number and which is the index. @@ -26,10 +26,12 @@ #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Axis(pub usize); -impl Axis { +impl Axis +{ /// Return the index of the axis. #[inline(always)] - pub fn index(self) -> usize { + pub fn index(self) -> usize + { self.0 } } diff --git a/src/dimension/broadcast.rs b/src/dimension/broadcast.rs index dc1513f04..d277cfea2 100644 --- a/src/dimension/broadcast.rs +++ b/src/dimension/broadcast.rs @@ -34,7 +34,8 @@ where Ok(out) } -pub trait DimMax { +pub trait DimMax +{ /// The resulting dimension type after broadcasting. type Output: Dimension; } @@ -42,7 +43,8 @@ pub trait DimMax { /// Dimensions of the same type remain unchanged when co_broadcast. /// So you can directly use D as the resulting type. /// (Instead of >::BroadcastOutput) -impl DimMax for D { +impl DimMax for D +{ type Output = D; } @@ -87,20 +89,18 @@ impl_broadcast_distinct_fixed!(Ix4, IxDyn); impl_broadcast_distinct_fixed!(Ix5, IxDyn); impl_broadcast_distinct_fixed!(Ix6, IxDyn); - #[cfg(test)] #[cfg(feature = "std")] -mod tests { +mod tests +{ use super::co_broadcast; - use crate::{Dimension, Dim, DimMax, ShapeError, Ix0, IxDynImpl, ErrorKind}; + use crate::{Dim, DimMax, Dimension, ErrorKind, Ix0, IxDynImpl, ShapeError}; #[test] - fn test_broadcast_shape() { - fn test_co( - d1: &D1, - d2: &D2, - r: Result<>::Output, ShapeError>, - ) where + fn test_broadcast_shape() + { + fn test_co(d1: &D1, d2: &D2, r: Result<>::Output, ShapeError>) + where D1: Dimension + DimMax, D2: Dimension, { @@ -108,36 +108,16 @@ mod tests { assert_eq!(d, r); } test_co(&Dim([2, 3]), &Dim([4, 1, 3]), Ok(Dim([4, 2, 3]))); - test_co( - &Dim([1, 2, 2]), - &Dim([1, 3, 4]), - Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), - ); + test_co(&Dim([1, 2, 2]), &Dim([1, 3, 4]), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); test_co(&Dim([3, 4, 5]), &Ix0(), Ok(Dim([3, 4, 5]))); let v = vec![1, 2, 3, 4, 5, 6, 7]; - test_co( - &Dim(vec![1, 1, 3, 1, 5, 1, 7]), - &Dim([2, 1, 4, 1, 6, 1]), - Ok(Dim(IxDynImpl::from(v.as_slice()))), - ); + test_co(&Dim(vec![1, 1, 3, 1, 5, 1, 7]), &Dim([2, 1, 4, 1, 6, 1]), Ok(Dim(IxDynImpl::from(v.as_slice())))); let d = Dim([1, 2, 1, 3]); test_co(&d, &d, Ok(d)); - test_co( - &Dim([2, 1, 2]).into_dyn(), - &Dim(0), - Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), - ); - test_co( - &Dim([2, 1, 1]), - &Dim([0, 0, 1, 3, 4]), - Ok(Dim([0, 0, 2, 3, 4])), - ); + test_co(&Dim([2, 1, 2]).into_dyn(), &Dim(0), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); + test_co(&Dim([2, 1, 1]), &Dim([0, 0, 1, 3, 4]), Ok(Dim([0, 0, 2, 3, 4]))); test_co(&Dim([0]), &Dim([0, 0, 0]), Ok(Dim([0, 0, 0]))); test_co(&Dim(1), &Dim([1, 0, 0]), Ok(Dim([1, 0, 0]))); - test_co( - &Dim([1, 3, 0, 1, 1]), - &Dim([1, 2, 3, 1]), - Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)), - ); + test_co(&Dim([1, 3, 0, 1, 1]), &Dim([1, 2, 3, 1]), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); } } diff --git a/src/dimension/conversion.rs b/src/dimension/conversion.rs index f6c408a75..0cf2e1296 100644 --- a/src/dimension/conversion.rs +++ b/src/dimension/conversion.rs @@ -8,10 +8,10 @@ //! Tuple to array conversion, IntoDimension, and related things -use num_traits::Zero; -use std::ops::{Index, IndexMut}; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +use num_traits::Zero; +use std::ops::{Index, IndexMut}; use crate::{Dim, Dimension, Ix, Ix1, IxDyn, IxDynImpl}; @@ -40,47 +40,55 @@ macro_rules! index_item { } /// Argument conversion a dimension. -pub trait IntoDimension { +pub trait IntoDimension +{ type Dim: Dimension; fn into_dimension(self) -> Self::Dim; } -impl IntoDimension for Ix { +impl IntoDimension for Ix +{ type Dim = Ix1; #[inline(always)] - fn into_dimension(self) -> Ix1 { + fn into_dimension(self) -> Ix1 + { Ix1(self) } } impl IntoDimension for D -where - D: Dimension, +where D: Dimension { type Dim = D; #[inline(always)] - fn into_dimension(self) -> Self { + fn into_dimension(self) -> Self + { self } } -impl IntoDimension for IxDynImpl { +impl IntoDimension for IxDynImpl +{ type Dim = IxDyn; #[inline(always)] - fn into_dimension(self) -> Self::Dim { + fn into_dimension(self) -> Self::Dim + { Dim::new(self) } } -impl IntoDimension for Vec { +impl IntoDimension for Vec +{ type Dim = IxDyn; #[inline(always)] - fn into_dimension(self) -> Self::Dim { + fn into_dimension(self) -> Self::Dim + { Dim::new(IxDynImpl::from(self)) } } -pub trait Convert { +pub trait Convert +{ type To; fn convert(self) -> Self::To; } @@ -94,25 +102,25 @@ macro_rules! sub { macro_rules! tuple_type { ([$T:ident] $($index:tt)*) => ( ( $(sub!($index $T), )* ) - ) + ); } macro_rules! tuple_expr { ([$self_:expr] $($index:tt)*) => ( ( $($self_[$index], )* ) - ) + ); } macro_rules! array_expr { ([$self_:expr] $($index:tt)*) => ( [$($self_ . $index, )*] - ) + ); } macro_rules! array_zero { ([] $($index:tt)*) => ( [$(sub!($index 0), )*] - ) + ); } macro_rules! tuple_to_array { @@ -168,7 +176,7 @@ macro_rules! tuple_to_array { } )* - } + }; } index_item!(tuple_to_array [] 7); diff --git a/src/dimension/dim.rs b/src/dimension/dim.rs index 3f8ff23e3..96e433bb3 100644 --- a/src/dimension/dim.rs +++ b/src/dimension/dim.rs @@ -6,12 +6,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - -use std::fmt; use super::Dimension; use super::IntoDimension; use crate::itertools::zip; use crate::Ix; +use std::fmt; /// Dimension description. /// @@ -36,21 +35,26 @@ use crate::Ix; /// assert_eq!(array.raw_dim(), Dim([3, 2])); /// ``` #[derive(Copy, Clone, PartialEq, Eq, Hash, Default)] -pub struct Dim { +pub struct Dim +{ index: I, } -impl Dim { +impl Dim +{ /// Private constructor and accessors for Dim - pub(crate) const fn new(index: I) -> Dim { + pub(crate) const fn new(index: I) -> Dim + { Dim { index } } #[inline(always)] - pub(crate) fn ix(&self) -> &I { + pub(crate) fn ix(&self) -> &I + { &self.index } #[inline(always)] - pub(crate) fn ixm(&mut self) -> &mut I { + pub(crate) fn ixm(&mut self) -> &mut I + { &mut self.index } } @@ -58,26 +62,25 @@ impl Dim { /// Create a new dimension value. #[allow(non_snake_case)] pub fn Dim(index: T) -> T::Dim -where - T: IntoDimension, +where T: IntoDimension { index.into_dimension() } impl PartialEq for Dim -where - I: PartialEq, +where I: PartialEq { - fn eq(&self, rhs: &I) -> bool { + fn eq(&self, rhs: &I) -> bool + { self.index == *rhs } } impl fmt::Debug for Dim -where - I: fmt::Debug, +where I: fmt::Debug { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { write!(f, "{:?}", self.index) } } diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index c594859e9..3544a7f3c 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -6,21 +6,21 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; use std::fmt::Debug; use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; use std::ops::{Index, IndexMut}; -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; use super::axes_of; use super::conversion::Convert; use super::ops::DimAdd; use super::{stride_offset, stride_offset_checked}; use crate::itertools::{enumerate, zip}; -use crate::{Axis, DimMax}; use crate::IntoDimension; use crate::RemoveAxis; use crate::{ArrayView1, ArrayViewMut1}; +use crate::{Axis, DimMax}; use crate::{Dim, Ix, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, IxDynImpl, Ixs}; /// Array shape and index trait. @@ -48,11 +48,11 @@ pub trait Dimension: + MulAssign + for<'x> MulAssign<&'x Self> + MulAssign - + DimMax - + DimMax - + DimMax - + DimMax<::Smaller, Output=Self> - + DimMax<::Larger, Output=::Larger> + + DimMax + + DimMax + + DimMax + + DimMax<::Smaller, Output = Self> + + DimMax<::Larger, Output = ::Larger> + DimAdd + DimAdd<::Smaller> + DimAdd<::Larger> @@ -83,12 +83,14 @@ pub trait Dimension: fn into_pattern(self) -> Self::Pattern; /// Compute the size of the dimension (number of elements) - fn size(&self) -> usize { + fn size(&self) -> usize + { self.slice().iter().product() } /// Compute the size while checking for overflow. - fn size_checked(&self) -> Option { + fn size_checked(&self) -> Option + { self.slice() .iter() .try_fold(1_usize, |s, &a| s.checked_mul(a)) @@ -101,17 +103,20 @@ pub trait Dimension: fn slice_mut(&mut self) -> &mut [Ix]; /// Borrow as a read-only array view. - fn as_array_view(&self) -> ArrayView1<'_, Ix> { + fn as_array_view(&self) -> ArrayView1<'_, Ix> + { ArrayView1::from(self.slice()) } /// Borrow as a read-write array view. - fn as_array_view_mut(&mut self) -> ArrayViewMut1<'_, Ix> { + fn as_array_view_mut(&mut self) -> ArrayViewMut1<'_, Ix> + { ArrayViewMut1::from(self.slice_mut()) } #[doc(hidden)] - fn equal(&self, rhs: &Self) -> bool { + fn equal(&self, rhs: &Self) -> bool + { self.slice() == rhs.slice() } @@ -120,7 +125,8 @@ pub trait Dimension: /// If the array is non-empty, the strides result in contiguous layout; if /// the array is empty, the strides are all zeros. #[doc(hidden)] - fn default_strides(&self) -> Self { + fn default_strides(&self) -> Self + { // Compute default array strides // Shape (a, b, c) => Give strides (b * c, c, 1) let mut strides = Self::zeros(self.ndim()); @@ -145,7 +151,8 @@ pub trait Dimension: /// If the array is non-empty, the strides result in contiguous layout; if /// the array is empty, the strides are all zeros. #[doc(hidden)] - fn fortran_strides(&self) -> Self { + fn fortran_strides(&self) -> Self + { // Compute fortran array strides // Shape (a, b, c) => Give strides (1, a, a * b) let mut strides = Self::zeros(self.ndim()); @@ -175,7 +182,8 @@ pub trait Dimension: #[doc(hidden)] #[inline] - fn first_index(&self) -> Option { + fn first_index(&self) -> Option + { for ax in self.slice().iter() { if *ax == 0 { return None; @@ -189,7 +197,8 @@ pub trait Dimension: /// or None if there are no more. // FIXME: use &Self for index or even &mut? #[inline] - fn next_for(&self, index: Self) -> Option { + fn next_for(&self, index: Self) -> Option + { let mut index = index; let mut done = false; for (&dim, ix) in zip(self.slice(), index.slice_mut()).rev() { @@ -214,7 +223,8 @@ pub trait Dimension: /// /// Next in f-order #[inline] - fn next_for_f(&self, index: &mut Self) -> bool { + fn next_for_f(&self, index: &mut Self) -> bool + { let mut end_iteration = true; for (&dim, ix) in zip(self.slice(), index.slice_mut()) { *ix += 1; @@ -237,8 +247,7 @@ pub trait Dimension: /// Note: Returns `false` if any of the ndims don't match. #[doc(hidden)] fn strides_equivalent(&self, strides1: &Self, strides2: &D) -> bool - where - D: Dimension, + where D: Dimension { let shape_ndim = self.ndim(); shape_ndim == strides1.ndim() @@ -249,7 +258,8 @@ pub trait Dimension: #[doc(hidden)] /// Return stride offset for index. - fn stride_offset(index: &Self, strides: &Self) -> isize { + fn stride_offset(index: &Self, strides: &Self) -> isize + { let mut offset = 0; for (&i, &s) in izip!(index.slice(), strides.slice()) { offset += stride_offset(i, s); @@ -259,12 +269,14 @@ pub trait Dimension: #[doc(hidden)] /// Return stride offset for this dimension and index. - fn stride_offset_checked(&self, strides: &Self, index: &Self) -> Option { + fn stride_offset_checked(&self, strides: &Self, index: &Self) -> Option + { stride_offset_checked(self.slice(), strides.slice(), index.slice()) } #[doc(hidden)] - fn last_elem(&self) -> usize { + fn last_elem(&self) -> usize + { if self.ndim() == 0 { 0 } else { @@ -273,13 +285,15 @@ pub trait Dimension: } #[doc(hidden)] - fn set_last_elem(&mut self, i: usize) { + fn set_last_elem(&mut self, i: usize) + { let nd = self.ndim(); self.slice_mut()[nd - 1] = i; } #[doc(hidden)] - fn is_contiguous(dim: &Self, strides: &Self) -> bool { + fn is_contiguous(dim: &Self, strides: &Self) -> bool + { let defaults = dim.default_strides(); if strides.equal(&defaults) { return true; @@ -311,7 +325,8 @@ pub trait Dimension: /// /// Assumes that no stride value appears twice. #[doc(hidden)] - fn _fastest_varying_stride_order(&self) -> Self { + fn _fastest_varying_stride_order(&self) -> Self + { let mut indices = self.clone(); for (i, elt) in enumerate(indices.slice_mut()) { *elt = i; @@ -326,7 +341,8 @@ pub trait Dimension: /// Compute the minimum stride axis (absolute value), under the constraint /// that the length of the axis is > 1; #[doc(hidden)] - fn min_stride_axis(&self, strides: &Self) -> Axis { + fn min_stride_axis(&self, strides: &Self) -> Axis + { let n = match self.ndim() { 0 => panic!("min_stride_axis: Array must have ndim > 0"), 1 => return Axis(0), @@ -341,7 +357,8 @@ pub trait Dimension: /// Compute the maximum stride axis (absolute value), under the constraint /// that the length of the axis is > 1; #[doc(hidden)] - fn max_stride_axis(&self, strides: &Self) -> Axis { + fn max_stride_axis(&self, strides: &Self) -> Axis + { match self.ndim() { 0 => panic!("max_stride_axis: Array must have ndim > 0"), 1 => return Axis(0), @@ -354,12 +371,14 @@ pub trait Dimension: } /// Convert the dimensional into a dynamic dimensional (IxDyn). - fn into_dyn(self) -> IxDyn { + fn into_dyn(self) -> IxDyn + { IxDyn(self.slice()) } #[doc(hidden)] - fn from_dimension(d: &D2) -> Option { + fn from_dimension(d: &D2) -> Option + { let mut s = Self::default(); if s.ndim() == d.ndim() { for i in 0..d.ndim() { @@ -395,76 +414,91 @@ macro_rules! impl_insert_axis_array( ); ); -impl Dimension for Dim<[Ix; 0]> { +impl Dimension for Dim<[Ix; 0]> +{ const NDIM: Option = Some(0); type Pattern = (); type Smaller = Self; type Larger = Ix1; // empty product is 1 -> size is 1 #[inline] - fn ndim(&self) -> usize { + fn ndim(&self) -> usize + { 0 } #[inline] - fn slice(&self) -> &[Ix] { + fn slice(&self) -> &[Ix] + { &[] } #[inline] - fn slice_mut(&mut self) -> &mut [Ix] { + fn slice_mut(&mut self) -> &mut [Ix] + { &mut [] } #[inline] - fn _fastest_varying_stride_order(&self) -> Self { + fn _fastest_varying_stride_order(&self) -> Self + { Ix0() } #[inline] fn into_pattern(self) -> Self::Pattern {} #[inline] - fn zeros(ndim: usize) -> Self { + fn zeros(ndim: usize) -> Self + { assert_eq!(ndim, 0); Self::default() } #[inline] - fn next_for(&self, _index: Self) -> Option { + fn next_for(&self, _index: Self) -> Option + { None } impl_insert_axis_array!(0); #[inline] - fn try_remove_axis(&self, _ignore: Axis) -> Self::Smaller { + fn try_remove_axis(&self, _ignore: Axis) -> Self::Smaller + { *self } private_impl! {} } -impl Dimension for Dim<[Ix; 1]> { +impl Dimension for Dim<[Ix; 1]> +{ const NDIM: Option = Some(1); type Pattern = Ix; type Smaller = Ix0; type Larger = Ix2; #[inline] - fn ndim(&self) -> usize { + fn ndim(&self) -> usize + { 1 } #[inline] - fn slice(&self) -> &[Ix] { + fn slice(&self) -> &[Ix] + { self.ix() } #[inline] - fn slice_mut(&mut self) -> &mut [Ix] { + fn slice_mut(&mut self) -> &mut [Ix] + { self.ixm() } #[inline] - fn into_pattern(self) -> Self::Pattern { + fn into_pattern(self) -> Self::Pattern + { get!(&self, 0) } #[inline] - fn zeros(ndim: usize) -> Self { + fn zeros(ndim: usize) -> Self + { assert_eq!(ndim, 1); Self::default() } #[inline] - fn next_for(&self, mut index: Self) -> Option { + fn next_for(&self, mut index: Self) -> Option + { getm!(index, 0) += 1; if get!(&index, 0) < get!(self, 0) { Some(index) @@ -474,21 +508,25 @@ impl Dimension for Dim<[Ix; 1]> { } #[inline] - fn equal(&self, rhs: &Self) -> bool { + fn equal(&self, rhs: &Self) -> bool + { get!(self, 0) == get!(rhs, 0) } #[inline] - fn size(&self) -> usize { + fn size(&self) -> usize + { get!(self, 0) } #[inline] - fn size_checked(&self) -> Option { + fn size_checked(&self) -> Option + { Some(get!(self, 0)) } #[inline] - fn default_strides(&self) -> Self { + fn default_strides(&self) -> Self + { if get!(self, 0) == 0 { Ix1(0) } else { @@ -497,22 +535,26 @@ impl Dimension for Dim<[Ix; 1]> { } #[inline] - fn _fastest_varying_stride_order(&self) -> Self { + fn _fastest_varying_stride_order(&self) -> Self + { Ix1(0) } #[inline(always)] - fn min_stride_axis(&self, _: &Self) -> Axis { + fn min_stride_axis(&self, _: &Self) -> Axis + { Axis(0) } #[inline(always)] - fn max_stride_axis(&self, _: &Self) -> Axis { + fn max_stride_axis(&self, _: &Self) -> Axis + { Axis(0) } #[inline] - fn first_index(&self) -> Option { + fn first_index(&self) -> Option + { if get!(self, 0) != 0 { Some(Ix1(0)) } else { @@ -522,13 +564,15 @@ impl Dimension for Dim<[Ix; 1]> { /// Self is an index, return the stride offset #[inline(always)] - fn stride_offset(index: &Self, stride: &Self) -> isize { + fn stride_offset(index: &Self, stride: &Self) -> isize + { stride_offset(get!(index, 0), get!(stride, 0)) } /// Return stride offset for this dimension and index. #[inline] - fn stride_offset_checked(&self, stride: &Self, index: &Self) -> Option { + fn stride_offset_checked(&self, stride: &Self, index: &Self) -> Option + { if get!(index, 0) < get!(self, 0) { Some(stride_offset(get!(index, 0), get!(stride, 0))) } else { @@ -537,11 +581,13 @@ impl Dimension for Dim<[Ix; 1]> { } impl_insert_axis_array!(1); #[inline] - fn try_remove_axis(&self, axis: Axis) -> Self::Smaller { + fn try_remove_axis(&self, axis: Axis) -> Self::Smaller + { self.remove_axis(axis) } - fn from_dimension(d: &D2) -> Option { + fn from_dimension(d: &D2) -> Option + { if 1 == d.ndim() { Some(Ix1(d[0])) } else { @@ -551,34 +597,41 @@ impl Dimension for Dim<[Ix; 1]> { private_impl! {} } -impl Dimension for Dim<[Ix; 2]> { +impl Dimension for Dim<[Ix; 2]> +{ const NDIM: Option = Some(2); type Pattern = (Ix, Ix); type Smaller = Ix1; type Larger = Ix3; #[inline] - fn ndim(&self) -> usize { + fn ndim(&self) -> usize + { 2 } #[inline] - fn into_pattern(self) -> Self::Pattern { + fn into_pattern(self) -> Self::Pattern + { self.ix().convert() } #[inline] - fn slice(&self) -> &[Ix] { + fn slice(&self) -> &[Ix] + { self.ix() } #[inline] - fn slice_mut(&mut self) -> &mut [Ix] { + fn slice_mut(&mut self) -> &mut [Ix] + { self.ixm() } #[inline] - fn zeros(ndim: usize) -> Self { + fn zeros(ndim: usize) -> Self + { assert_eq!(ndim, 2); Self::default() } #[inline] - fn next_for(&self, index: Self) -> Option { + fn next_for(&self, index: Self) -> Option + { let mut i = get!(&index, 0); let mut j = get!(&index, 1); let imax = get!(self, 0); @@ -595,34 +648,40 @@ impl Dimension for Dim<[Ix; 2]> { } #[inline] - fn equal(&self, rhs: &Self) -> bool { + fn equal(&self, rhs: &Self) -> bool + { get!(self, 0) == get!(rhs, 0) && get!(self, 1) == get!(rhs, 1) } #[inline] - fn size(&self) -> usize { + fn size(&self) -> usize + { get!(self, 0) * get!(self, 1) } #[inline] - fn size_checked(&self) -> Option { + fn size_checked(&self) -> Option + { let m = get!(self, 0); let n = get!(self, 1); m.checked_mul(n) } #[inline] - fn last_elem(&self) -> usize { + fn last_elem(&self) -> usize + { get!(self, 1) } #[inline] - fn set_last_elem(&mut self, i: usize) { + fn set_last_elem(&mut self, i: usize) + { getm!(self, 1) = i; } #[inline] - fn default_strides(&self) -> Self { + fn default_strides(&self) -> Self + { let m = get!(self, 0); let n = get!(self, 1); if m == 0 || n == 0 { @@ -632,7 +691,8 @@ impl Dimension for Dim<[Ix; 2]> { } } #[inline] - fn fortran_strides(&self) -> Self { + fn fortran_strides(&self) -> Self + { let m = get!(self, 0); let n = get!(self, 1); if m == 0 || n == 0 { @@ -643,7 +703,8 @@ impl Dimension for Dim<[Ix; 2]> { } #[inline] - fn _fastest_varying_stride_order(&self) -> Self { + fn _fastest_varying_stride_order(&self) -> Self + { if (get!(self, 0) as Ixs).abs() <= (get!(self, 1) as Ixs).abs() { Ix2(0, 1) } else { @@ -652,7 +713,8 @@ impl Dimension for Dim<[Ix; 2]> { } #[inline] - fn min_stride_axis(&self, strides: &Self) -> Axis { + fn min_stride_axis(&self, strides: &Self) -> Axis + { let s = get!(strides, 0) as Ixs; let t = get!(strides, 1) as Ixs; if s.abs() < t.abs() { @@ -663,7 +725,8 @@ impl Dimension for Dim<[Ix; 2]> { } #[inline] - fn first_index(&self) -> Option { + fn first_index(&self) -> Option + { let m = get!(self, 0); let n = get!(self, 1); if m != 0 && n != 0 { @@ -675,7 +738,8 @@ impl Dimension for Dim<[Ix; 2]> { /// Self is an index, return the stride offset #[inline(always)] - fn stride_offset(index: &Self, strides: &Self) -> isize { + fn stride_offset(index: &Self, strides: &Self) -> isize + { let i = get!(index, 0); let j = get!(index, 1); let s = get!(strides, 0); @@ -685,7 +749,8 @@ impl Dimension for Dim<[Ix; 2]> { /// Return stride offset for this dimension and index. #[inline] - fn stride_offset_checked(&self, strides: &Self, index: &Self) -> Option { + fn stride_offset_checked(&self, strides: &Self, index: &Self) -> Option + { let m = get!(self, 0); let n = get!(self, 1); let i = get!(index, 0); @@ -700,36 +765,43 @@ impl Dimension for Dim<[Ix; 2]> { } impl_insert_axis_array!(2); #[inline] - fn try_remove_axis(&self, axis: Axis) -> Self::Smaller { + fn try_remove_axis(&self, axis: Axis) -> Self::Smaller + { self.remove_axis(axis) } private_impl! {} } -impl Dimension for Dim<[Ix; 3]> { +impl Dimension for Dim<[Ix; 3]> +{ const NDIM: Option = Some(3); type Pattern = (Ix, Ix, Ix); type Smaller = Ix2; type Larger = Ix4; #[inline] - fn ndim(&self) -> usize { + fn ndim(&self) -> usize + { 3 } #[inline] - fn into_pattern(self) -> Self::Pattern { + fn into_pattern(self) -> Self::Pattern + { self.ix().convert() } #[inline] - fn slice(&self) -> &[Ix] { + fn slice(&self) -> &[Ix] + { self.ix() } #[inline] - fn slice_mut(&mut self) -> &mut [Ix] { + fn slice_mut(&mut self) -> &mut [Ix] + { self.ixm() } #[inline] - fn size(&self) -> usize { + fn size(&self) -> usize + { let m = get!(self, 0); let n = get!(self, 1); let o = get!(self, 2); @@ -737,13 +809,15 @@ impl Dimension for Dim<[Ix; 3]> { } #[inline] - fn zeros(ndim: usize) -> Self { + fn zeros(ndim: usize) -> Self + { assert_eq!(ndim, 3); Self::default() } #[inline] - fn next_for(&self, index: Self) -> Option { + fn next_for(&self, index: Self) -> Option + { let mut i = get!(&index, 0); let mut j = get!(&index, 1); let mut k = get!(&index, 2); @@ -767,7 +841,8 @@ impl Dimension for Dim<[Ix; 3]> { /// Self is an index, return the stride offset #[inline] - fn stride_offset(index: &Self, strides: &Self) -> isize { + fn stride_offset(index: &Self, strides: &Self) -> isize + { let i = get!(index, 0); let j = get!(index, 1); let k = get!(index, 2); @@ -779,7 +854,8 @@ impl Dimension for Dim<[Ix; 3]> { /// Return stride offset for this dimension and index. #[inline] - fn stride_offset_checked(&self, strides: &Self, index: &Self) -> Option { + fn stride_offset_checked(&self, strides: &Self, index: &Self) -> Option + { let m = get!(self, 0); let n = get!(self, 1); let l = get!(self, 2); @@ -797,7 +873,8 @@ impl Dimension for Dim<[Ix; 3]> { } #[inline] - fn _fastest_varying_stride_order(&self) -> Self { + fn _fastest_varying_stride_order(&self) -> Self + { let mut stride = *self; let mut order = Ix3(0, 1, 2); macro_rules! swap { @@ -819,7 +896,8 @@ impl Dimension for Dim<[Ix; 3]> { } impl_insert_axis_array!(3); #[inline] - fn try_remove_axis(&self, axis: Axis) -> Self::Smaller { + fn try_remove_axis(&self, axis: Axis) -> Self::Smaller + { self.remove_axis(axis) } private_impl! {} @@ -854,7 +932,7 @@ macro_rules! large_dim { } private_impl!{} } - ) + ); } large_dim!(4, Ix4, (Ix, Ix, Ix, Ix), Ix5, { @@ -876,41 +954,49 @@ large_dim!(6, Ix6, (Ix, Ix, Ix, Ix, Ix, Ix), IxDyn, { /// IxDyn is a "dynamic" index, pretty hard to use when indexing, /// and memory wasteful, but it allows an arbitrary and dynamic number of axes. -impl Dimension for IxDyn { +impl Dimension for IxDyn +{ const NDIM: Option = None; type Pattern = Self; type Smaller = Self; type Larger = Self; #[inline] - fn ndim(&self) -> usize { + fn ndim(&self) -> usize + { self.ix().len() } #[inline] - fn slice(&self) -> &[Ix] { + fn slice(&self) -> &[Ix] + { self.ix() } #[inline] - fn slice_mut(&mut self) -> &mut [Ix] { + fn slice_mut(&mut self) -> &mut [Ix] + { self.ixm() } #[inline] - fn into_pattern(self) -> Self::Pattern { + fn into_pattern(self) -> Self::Pattern + { self } #[inline] - fn zeros(ndim: usize) -> Self { + fn zeros(ndim: usize) -> Self + { IxDyn::zeros(ndim) } #[inline] - fn insert_axis(&self, axis: Axis) -> Self::Larger { + fn insert_axis(&self, axis: Axis) -> Self::Larger + { debug_assert!(axis.index() <= self.ndim()); Dim::new(self.ix().insert(axis.index())) } #[inline] - fn try_remove_axis(&self, axis: Axis) -> Self::Smaller { + fn try_remove_axis(&self, axis: Axis) -> Self::Smaller + { if self.ndim() > 0 { self.remove_axis(axis) } else { @@ -918,26 +1004,32 @@ impl Dimension for IxDyn { } } - fn from_dimension(d: &D2) -> Option { + fn from_dimension(d: &D2) -> Option + { Some(IxDyn(d.slice())) } - fn into_dyn(self) -> IxDyn { + fn into_dyn(self) -> IxDyn + { self } private_impl! {} } -impl Index for Dim { +impl Index for Dim +{ type Output = >::Output; - fn index(&self, index: usize) -> &Self::Output { + fn index(&self, index: usize) -> &Self::Output + { &self.ix()[index] } } -impl IndexMut for Dim { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { +impl IndexMut for Dim +{ + fn index_mut(&mut self, index: usize) -> &mut Self::Output + { &mut self.ixm()[index] } } diff --git a/src/dimension/dynindeximpl.rs b/src/dimension/dynindeximpl.rs index c2aea032e..60aeacd80 100644 --- a/src/dimension/dynindeximpl.rs +++ b/src/dimension/dynindeximpl.rs @@ -1,23 +1,26 @@ use crate::imp_prelude::*; -use std::hash::{Hash, Hasher}; -use std::ops::{Deref, DerefMut, Index, IndexMut}; -use alloc::vec; #[cfg(not(feature = "std"))] use alloc::boxed::Box; +use alloc::vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +use std::hash::{Hash, Hasher}; +use std::ops::{Deref, DerefMut, Index, IndexMut}; const CAP: usize = 4; /// T is usize or isize #[derive(Debug)] -enum IxDynRepr { +enum IxDynRepr +{ Inline(u32, [T; CAP]), Alloc(Box<[T]>), } -impl Deref for IxDynRepr { +impl Deref for IxDynRepr +{ type Target = [T]; - fn deref(&self) -> &[T] { + fn deref(&self) -> &[T] + { match *self { IxDynRepr::Inline(len, ref ar) => { debug_assert!(len as usize <= ar.len()); @@ -28,8 +31,10 @@ impl Deref for IxDynRepr { } } -impl DerefMut for IxDynRepr { - fn deref_mut(&mut self) -> &mut [T] { +impl DerefMut for IxDynRepr +{ + fn deref_mut(&mut self) -> &mut [T] + { match *self { IxDynRepr::Inline(len, ref mut ar) => { debug_assert!(len as usize <= ar.len()); @@ -41,16 +46,20 @@ impl DerefMut for IxDynRepr { } /// The default is equivalent to `Self::from(&[0])`. -impl Default for IxDynRepr { - fn default() -> Self { +impl Default for IxDynRepr +{ + fn default() -> Self + { Self::copy_from(&[0]) } } use num_traits::Zero; -impl IxDynRepr { - pub fn copy_from(x: &[T]) -> Self { +impl IxDynRepr +{ + pub fn copy_from(x: &[T]) -> Self + { if x.len() <= CAP { let mut arr = [T::zero(); CAP]; arr[..x.len()].copy_from_slice(x); @@ -61,9 +70,11 @@ impl IxDynRepr { } } -impl IxDynRepr { +impl IxDynRepr +{ // make an Inline or Alloc version as appropriate - fn from_vec_auto(v: Vec) -> Self { + fn from_vec_auto(v: Vec) -> Self + { if v.len() <= CAP { Self::copy_from(&v) } else { @@ -72,18 +83,23 @@ impl IxDynRepr { } } -impl IxDynRepr { - fn from_vec(v: Vec) -> Self { +impl IxDynRepr +{ + fn from_vec(v: Vec) -> Self + { IxDynRepr::Alloc(v.into_boxed_slice()) } - fn from(x: &[T]) -> Self { + fn from(x: &[T]) -> Self + { Self::from_vec(x.to_vec()) } } -impl Clone for IxDynRepr { - fn clone(&self) -> Self { +impl Clone for IxDynRepr +{ + fn clone(&self) -> Self + { match *self { IxDynRepr::Inline(len, arr) => IxDynRepr::Inline(len, arr), _ => Self::from(&self[..]), @@ -93,22 +109,25 @@ impl Clone for IxDynRepr { impl Eq for IxDynRepr {} -impl PartialEq for IxDynRepr { - fn eq(&self, rhs: &Self) -> bool { +impl PartialEq for IxDynRepr +{ + fn eq(&self, rhs: &Self) -> bool + { match (self, rhs) { - (&IxDynRepr::Inline(slen, ref sarr), &IxDynRepr::Inline(rlen, ref rarr)) => { + (&IxDynRepr::Inline(slen, ref sarr), &IxDynRepr::Inline(rlen, ref rarr)) => slen == rlen && (0..CAP) .filter(|&i| i < slen as usize) - .all(|i| sarr[i] == rarr[i]) - } + .all(|i| sarr[i] == rarr[i]), _ => self[..] == rhs[..], } } } -impl Hash for IxDynRepr { - fn hash(&self, state: &mut H) { +impl Hash for IxDynRepr +{ + fn hash(&self, state: &mut H) + { Hash::hash(&self[..], state) } } @@ -121,8 +140,10 @@ impl Hash for IxDynRepr { #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] pub struct IxDynImpl(IxDynRepr); -impl IxDynImpl { - pub(crate) fn insert(&self, i: usize) -> Self { +impl IxDynImpl +{ + pub(crate) fn insert(&self, i: usize) -> Self + { let len = self.len(); debug_assert!(i <= len); IxDynImpl(if len < CAP { @@ -139,7 +160,8 @@ impl IxDynImpl { }) } - fn remove(&self, i: usize) -> Self { + fn remove(&self, i: usize) -> Self + { IxDynImpl(match self.0 { IxDynRepr::Inline(0, _) => IxDynRepr::Inline(0, [0; CAP]), IxDynRepr::Inline(1, _) => IxDynRepr::Inline(0, [0; CAP]), @@ -160,74 +182,88 @@ impl IxDynImpl { } } -impl<'a> From<&'a [Ix]> for IxDynImpl { +impl<'a> From<&'a [Ix]> for IxDynImpl +{ #[inline] - fn from(ix: &'a [Ix]) -> Self { + fn from(ix: &'a [Ix]) -> Self + { IxDynImpl(IxDynRepr::copy_from(ix)) } } -impl From> for IxDynImpl { +impl From> for IxDynImpl +{ #[inline] - fn from(ix: Vec) -> Self { + fn from(ix: Vec) -> Self + { IxDynImpl(IxDynRepr::from_vec_auto(ix)) } } impl Index for IxDynImpl -where - [Ix]: Index, +where [Ix]: Index { type Output = <[Ix] as Index>::Output; - fn index(&self, index: J) -> &Self::Output { + fn index(&self, index: J) -> &Self::Output + { &self.0[index] } } impl IndexMut for IxDynImpl -where - [Ix]: IndexMut, +where [Ix]: IndexMut { - fn index_mut(&mut self, index: J) -> &mut Self::Output { + fn index_mut(&mut self, index: J) -> &mut Self::Output + { &mut self.0[index] } } -impl Deref for IxDynImpl { +impl Deref for IxDynImpl +{ type Target = [Ix]; #[inline] - fn deref(&self) -> &[Ix] { + fn deref(&self) -> &[Ix] + { &self.0 } } -impl DerefMut for IxDynImpl { +impl DerefMut for IxDynImpl +{ #[inline] - fn deref_mut(&mut self) -> &mut [Ix] { + fn deref_mut(&mut self) -> &mut [Ix] + { &mut self.0 } } -impl<'a> IntoIterator for &'a IxDynImpl { +impl<'a> IntoIterator for &'a IxDynImpl +{ type Item = &'a Ix; type IntoIter = <&'a [Ix] as IntoIterator>::IntoIter; #[inline] - fn into_iter(self) -> Self::IntoIter { + fn into_iter(self) -> Self::IntoIter + { self[..].iter() } } -impl RemoveAxis for Dim { - fn remove_axis(&self, axis: Axis) -> Self { +impl RemoveAxis for Dim +{ + fn remove_axis(&self, axis: Axis) -> Self + { debug_assert!(axis.index() < self.ndim()); Dim::new(self.ix().remove(axis.index())) } } -impl IxDyn { +impl IxDyn +{ /// Create a new dimension value with `n` axes, all zeros #[inline] - pub fn zeros(n: usize) -> IxDyn { + pub fn zeros(n: usize) -> IxDyn + { const ZEROS: &[usize] = &[0; 4]; if n <= ZEROS.len() { Dim(&ZEROS[..n]) diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 76a3984fe..e1563613e 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -7,9 +7,9 @@ // except according to those terms. use crate::error::{from_kind, ErrorKind, ShapeError}; +use crate::shape_builder::Strides; use crate::slice::SliceArg; use crate::{Ix, Ixs, Slice, SliceInfoElem}; -use crate::shape_builder::Strides; use num_integer::div_floor; pub use self::axes::{Axes, AxisDescription}; @@ -46,7 +46,8 @@ mod sequence; /// Calculate offset from `Ix` stride converting sign properly #[inline(always)] -pub fn stride_offset(n: Ix, stride: Ix) -> isize { +pub fn stride_offset(n: Ix, stride: Ix) -> isize +{ (n as isize) * (stride as Ixs) } @@ -55,7 +56,8 @@ pub fn stride_offset(n: Ix, stride: Ix) -> isize { /// There is overlap if, when iterating through the dimensions in order of /// increasing stride, the current stride is less than or equal to the maximum /// possible offset along the preceding axes. (Axes of length ≤1 are ignored.) -pub fn dim_stride_overlap(dim: &D, strides: &D) -> bool { +pub fn dim_stride_overlap(dim: &D, strides: &D) -> bool +{ let order = strides._fastest_varying_stride_order(); let mut sum_prev_offsets = 0; for &index in order.slice() { @@ -84,7 +86,8 @@ pub fn dim_stride_overlap(dim: &D, strides: &D) -> bool { /// are met to construct an array from the data buffer, `dim`, and `strides`. /// (The data buffer being a slice or `Vec` guarantees that it contains no more /// than `isize::MAX` bytes.) -pub fn size_of_shape_checked(dim: &D) -> Result { +pub fn size_of_shape_checked(dim: &D) -> Result +{ let size_nonzero = dim .slice() .iter() @@ -122,9 +125,9 @@ pub fn size_of_shape_checked(dim: &D) -> Result /// conditions 1 and 2 are sufficient to guarantee that the offset in units of /// `A` and in units of bytes between the least address and greatest address /// accessible by moving along all axes does not exceed `isize::MAX`. -pub(crate) fn can_index_slice_with_strides(data: &[A], dim: &D, - strides: &Strides) - -> Result<(), ShapeError> +pub(crate) fn can_index_slice_with_strides( + data: &[A], dim: &D, strides: &Strides, +) -> Result<(), ShapeError> { if let Strides::Custom(strides) = strides { can_index_slice(data, dim, strides) @@ -133,8 +136,7 @@ pub(crate) fn can_index_slice_with_strides(data: &[A], dim: &D, } } -pub(crate) fn can_index_slice_not_custom(data_len: usize, dim: &D) - -> Result<(), ShapeError> +pub(crate) fn can_index_slice_not_custom(data_len: usize, dim: &D) -> Result<(), ShapeError> { // Condition 1. let len = size_of_shape_checked(dim)?; @@ -160,16 +162,13 @@ pub(crate) fn can_index_slice_not_custom(data_len: usize, dim: &D) /// also implies that the length of any individual axis does not exceed /// `isize::MAX`.) pub fn max_abs_offset_check_overflow(dim: &D, strides: &D) -> Result -where - D: Dimension, +where D: Dimension { max_abs_offset_check_overflow_impl(mem::size_of::(), dim, strides) } -fn max_abs_offset_check_overflow_impl(elem_size: usize, dim: &D, strides: &D) - -> Result -where - D: Dimension, +fn max_abs_offset_check_overflow_impl(elem_size: usize, dim: &D, strides: &D) -> Result +where D: Dimension { // Condition 1. if dim.ndim() != strides.ndim() { @@ -241,22 +240,17 @@ where /// allocation. (In other words, the pointer to the first element of the array /// must be computed using `offset_from_low_addr_ptr_to_logical_ptr` so that /// negative strides are correctly handled.) -pub(crate) fn can_index_slice( - data: &[A], - dim: &D, - strides: &D, -) -> Result<(), ShapeError> { +pub(crate) fn can_index_slice(data: &[A], dim: &D, strides: &D) -> Result<(), ShapeError> +{ // Check conditions 1 and 2 and calculate `max_offset`. let max_offset = max_abs_offset_check_overflow::(dim, strides)?; can_index_slice_impl(max_offset, data.len(), dim, strides) } fn can_index_slice_impl( - max_offset: usize, - data_len: usize, - dim: &D, - strides: &D, -) -> Result<(), ShapeError> { + max_offset: usize, data_len: usize, dim: &D, strides: &D, +) -> Result<(), ShapeError> +{ // Check condition 3. let is_empty = dim.slice().iter().any(|&d| d == 0); if is_empty && max_offset > data_len { @@ -276,7 +270,8 @@ fn can_index_slice_impl( /// Stride offset checked general version (slices) #[inline] -pub fn stride_offset_checked(dim: &[Ix], strides: &[Ix], index: &[Ix]) -> Option { +pub fn stride_offset_checked(dim: &[Ix], strides: &[Ix], index: &[Ix]) -> Option +{ if index.len() != dim.len() { return None; } @@ -292,8 +287,7 @@ pub fn stride_offset_checked(dim: &[Ix], strides: &[Ix], index: &[Ix]) -> Option /// Checks if strides are non-negative. pub fn strides_non_negative(strides: &D) -> Result<(), ShapeError> -where - D: Dimension, +where D: Dimension { for &stride in strides.slice() { if (stride as isize) < 0 { @@ -304,7 +298,8 @@ where } /// Implementation-specific extensions to `Dimension` -pub trait DimensionExt { +pub trait DimensionExt +{ // note: many extensions go in the main trait if they need to be special- // cased per dimension /// Get the dimension at `axis`. @@ -321,28 +316,32 @@ pub trait DimensionExt { } impl DimensionExt for D -where - D: Dimension, +where D: Dimension { #[inline] - fn axis(&self, axis: Axis) -> Ix { + fn axis(&self, axis: Axis) -> Ix + { self[axis.index()] } #[inline] - fn set_axis(&mut self, axis: Axis, value: Ix) { + fn set_axis(&mut self, axis: Axis, value: Ix) + { self[axis.index()] = value; } } -impl DimensionExt for [Ix] { +impl DimensionExt for [Ix] +{ #[inline] - fn axis(&self, axis: Axis) -> Ix { + fn axis(&self, axis: Axis) -> Ix + { self[axis.index()] } #[inline] - fn set_axis(&mut self, axis: Axis, value: Ix) { + fn set_axis(&mut self, axis: Axis, value: Ix) + { self[axis.index()] = value; } } @@ -353,12 +352,8 @@ impl DimensionExt for [Ix] { /// **Panics** if `index` is larger than the size of the axis #[track_caller] // FIXME: Move to Dimension trait -pub fn do_collapse_axis( - dims: &mut D, - strides: &D, - axis: usize, - index: usize, -) -> isize { +pub fn do_collapse_axis(dims: &mut D, strides: &D, axis: usize, index: usize) -> isize +{ let dim = dims.slice()[axis]; let stride = strides.slice()[axis]; ndassert!( @@ -375,7 +370,8 @@ pub fn do_collapse_axis( /// Compute the equivalent unsigned index given the axis length and signed index. #[inline] -pub fn abs_index(len: Ix, index: Ixs) -> Ix { +pub fn abs_index(len: Ix, index: Ixs) -> Ix +{ if index < 0 { len - (-index as Ix) } else { @@ -389,7 +385,8 @@ pub fn abs_index(len: Ix, index: Ixs) -> Ix { /// /// **Panics** if stride is 0 or if any index is out of bounds. #[track_caller] -fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize) { +fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize) +{ let Slice { start, end, step } = slice; let start = abs_index(axis_len, start); let mut end = abs_index(axis_len, end.unwrap_or(axis_len as isize)); @@ -414,7 +411,8 @@ fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize) { /// Returns the offset from the lowest-address element to the logically first /// element. -pub fn offset_from_low_addr_ptr_to_logical_ptr(dim: &D, strides: &D) -> usize { +pub fn offset_from_low_addr_ptr_to_logical_ptr(dim: &D, strides: &D) -> usize +{ let offset = izip!(dim.slice(), strides.slice()).fold(0, |_offset, (&d, &s)| { let s = s as isize; if s < 0 && d > 1 { @@ -431,7 +429,8 @@ pub fn offset_from_low_addr_ptr_to_logical_ptr(dim: &D, strides: & /// /// **Panics** if stride is 0 or if any index is out of bounds. #[track_caller] -pub fn do_slice(dim: &mut usize, stride: &mut usize, slice: Slice) -> isize { +pub fn do_slice(dim: &mut usize, stride: &mut usize, slice: Slice) -> isize +{ let (start, end, step) = to_abs_slice(*dim, slice); let m = end - start; @@ -484,7 +483,8 @@ pub fn do_slice(dim: &mut usize, stride: &mut usize, slice: Slice) -> isize { /// nonnegative. /// /// See https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm -fn extended_gcd(a: isize, b: isize) -> (isize, (isize, isize)) { +fn extended_gcd(a: isize, b: isize) -> (isize, (isize, isize)) +{ if a == 0 { (b.abs(), (0, b.signum())) } else if b == 0 { @@ -520,7 +520,8 @@ fn extended_gcd(a: isize, b: isize) -> (isize, (isize, isize)) { /// /// See https://en.wikipedia.org/wiki/Diophantine_equation#One_equation /// and https://math.stackexchange.com/questions/1656120#1656138 -fn solve_linear_diophantine_eq(a: isize, b: isize, c: isize) -> Option<(isize, isize)> { +fn solve_linear_diophantine_eq(a: isize, b: isize, c: isize) -> Option<(isize, isize)> +{ debug_assert_ne!(a, 0); debug_assert_ne!(b, 0); let (g, (u, _)) = extended_gcd(a, b); @@ -538,10 +539,8 @@ fn solve_linear_diophantine_eq(a: isize, b: isize, c: isize) -> Option<(isize, i /// consecutive elements (the sign is irrelevant). /// /// **Note** `step1` and `step2` must be nonzero. -fn arith_seq_intersect( - (min1, max1, step1): (isize, isize, isize), - (min2, max2, step2): (isize, isize, isize), -) -> bool { +fn arith_seq_intersect((min1, max1, step1): (isize, isize, isize), (min2, max2, step2): (isize, isize, isize)) -> bool +{ debug_assert!(max1 >= min1); debug_assert!(max2 >= min2); debug_assert_eq!((max1 - min1) % step1, 0); @@ -597,7 +596,8 @@ fn arith_seq_intersect( /// Returns the minimum and maximum values of the indices (inclusive). /// /// If the slice is empty, then returns `None`, otherwise returns `Some((min, max))`. -fn slice_min_max(axis_len: usize, slice: Slice) -> Option<(usize, usize)> { +fn slice_min_max(axis_len: usize, slice: Slice) -> Option<(usize, usize)> +{ let (start, end, step) = to_abs_slice(axis_len, slice); if start == end { None @@ -609,11 +609,8 @@ fn slice_min_max(axis_len: usize, slice: Slice) -> Option<(usize, usize)> { } /// Returns `true` iff the slices intersect. -pub fn slices_intersect( - dim: &D, - indices1: impl SliceArg, - indices2: impl SliceArg, -) -> bool { +pub fn slices_intersect(dim: &D, indices1: impl SliceArg, indices2: impl SliceArg) -> bool +{ debug_assert_eq!(indices1.in_ndim(), indices2.in_ndim()); for (&axis_len, &si1, &si2) in izip!( dim.slice(), @@ -642,10 +639,7 @@ pub fn slices_intersect( Some(m) => m, None => return false, }; - if !arith_seq_intersect( - (min1 as isize, max1 as isize, step1), - (min2 as isize, max2 as isize, step2), - ) { + if !arith_seq_intersect((min1 as isize, max1 as isize, step1), (min2 as isize, max2 as isize, step2)) { return false; } } @@ -673,7 +667,8 @@ pub fn slices_intersect( true } -pub(crate) fn is_layout_c(dim: &D, strides: &D) -> bool { +pub(crate) fn is_layout_c(dim: &D, strides: &D) -> bool +{ if let Some(1) = D::NDIM { return strides[0] == 1 || dim[0] <= 1; } @@ -698,7 +693,8 @@ pub(crate) fn is_layout_c(dim: &D, strides: &D) -> bool { true } -pub(crate) fn is_layout_f(dim: &D, strides: &D) -> bool { +pub(crate) fn is_layout_f(dim: &D, strides: &D) -> bool +{ if let Some(1) = D::NDIM { return strides[0] == 1 || dim[0] <= 1; } @@ -724,8 +720,7 @@ pub(crate) fn is_layout_f(dim: &D, strides: &D) -> bool { } pub fn merge_axes(dim: &mut D, strides: &mut D, take: Axis, into: Axis) -> bool -where - D: Dimension, +where D: Dimension { let into_len = dim.axis(into); let into_stride = strides.axis(into) as isize; @@ -753,20 +748,16 @@ where /// Move the axis which has the smallest absolute stride and a length /// greater than one to be the last axis. pub fn move_min_stride_axis_to_last(dim: &mut D, strides: &mut D) -where - D: Dimension, +where D: Dimension { debug_assert_eq!(dim.ndim(), strides.ndim()); match dim.ndim() { 0 | 1 => {} - 2 => { - if dim[1] <= 1 - || dim[0] > 1 && (strides[0] as isize).abs() < (strides[1] as isize).abs() - { + 2 => + if dim[1] <= 1 || dim[0] > 1 && (strides[0] as isize).abs() < (strides[1] as isize).abs() { dim.slice_mut().swap(0, 1); strides.slice_mut().swap(0, 1); - } - } + }, n => { if let Some(min_stride_axis) = (0..n) .filter(|&ax| dim[ax] > 1) @@ -781,11 +772,18 @@ where } #[cfg(test)] -mod test { +mod test +{ use super::{ - arith_seq_intersect, can_index_slice, can_index_slice_not_custom, extended_gcd, - max_abs_offset_check_overflow, slice_min_max, slices_intersect, - solve_linear_diophantine_eq, IntoDimension, + arith_seq_intersect, + can_index_slice, + can_index_slice_not_custom, + extended_gcd, + max_abs_offset_check_overflow, + slice_min_max, + slices_intersect, + solve_linear_diophantine_eq, + IntoDimension, }; use crate::error::{from_kind, ErrorKind}; use crate::slice::Slice; @@ -794,7 +792,8 @@ mod test { use quickcheck::{quickcheck, TestResult}; #[test] - fn slice_indexing_uncommon_strides() { + fn slice_indexing_uncommon_strides() + { let v: alloc::vec::Vec<_> = (0..12).collect(); let dim = (2, 3, 2).into_dimension(); let strides = (1, 2, 6).into_dimension(); @@ -808,7 +807,8 @@ mod test { } #[test] - fn overlapping_strides_dim() { + fn overlapping_strides_dim() + { let dim = (2, 3, 2).into_dimension(); let strides = (5, 2, 1).into_dimension(); assert!(super::dim_stride_overlap(&dim, &strides)); @@ -830,7 +830,8 @@ mod test { } #[test] - fn max_abs_offset_check_overflow_examples() { + fn max_abs_offset_check_overflow_examples() + { let dim = (1, ::std::isize::MAX as usize, 1).into_dimension(); let strides = (1, 1, 1).into_dimension(); max_abs_offset_check_overflow::(&dim, &strides).unwrap(); @@ -846,13 +847,15 @@ mod test { } #[test] - fn can_index_slice_ix0() { + fn can_index_slice_ix0() + { can_index_slice::(&[1], &Ix0(), &Ix0()).unwrap(); can_index_slice::(&[], &Ix0(), &Ix0()).unwrap_err(); } #[test] - fn can_index_slice_ix1() { + fn can_index_slice_ix1() + { can_index_slice::(&[], &Ix1(0), &Ix1(0)).unwrap(); can_index_slice::(&[], &Ix1(0), &Ix1(1)).unwrap(); can_index_slice::(&[], &Ix1(1), &Ix1(0)).unwrap_err(); @@ -867,7 +870,8 @@ mod test { } #[test] - fn can_index_slice_ix2() { + fn can_index_slice_ix2() + { can_index_slice::(&[], &Ix2(0, 0), &Ix2(0, 0)).unwrap(); can_index_slice::(&[], &Ix2(0, 0), &Ix2(2, 1)).unwrap(); can_index_slice::(&[], &Ix2(0, 1), &Ix2(0, 0)).unwrap(); @@ -882,7 +886,8 @@ mod test { } #[test] - fn can_index_slice_ix3() { + fn can_index_slice_ix3() + { can_index_slice::(&[], &Ix3(0, 0, 1), &Ix3(2, 1, 3)).unwrap(); can_index_slice::(&[], &Ix3(1, 1, 1), &Ix3(2, 1, 3)).unwrap_err(); can_index_slice::(&[1], &Ix3(1, 1, 1), &Ix3(2, 1, 3)).unwrap(); @@ -891,7 +896,8 @@ mod test { } #[test] - fn can_index_slice_zero_size_elem() { + fn can_index_slice_zero_size_elem() + { can_index_slice::<(), _>(&[], &Ix1(0), &Ix1(1)).unwrap(); can_index_slice::<(), _>(&[()], &Ix1(1), &Ix1(1)).unwrap(); can_index_slice::<(), _>(&[(), ()], &Ix1(2), &Ix1(1)).unwrap(); @@ -941,7 +947,8 @@ mod test { } #[test] - fn extended_gcd_zero() { + fn extended_gcd_zero() + { assert_eq!(extended_gcd(0, 0), (0, (0, 0))); assert_eq!(extended_gcd(0, 5), (5, (0, 1))); assert_eq!(extended_gcd(5, 0), (5, (1, 0))); @@ -1031,7 +1038,8 @@ mod test { } #[test] - fn slice_min_max_empty() { + fn slice_min_max_empty() + { assert_eq!(slice_min_max(0, Slice::new(0, None, 3)), None); assert_eq!(slice_min_max(10, Slice::new(1, Some(1), 3)), None); assert_eq!(slice_min_max(10, Slice::new(-1, Some(-1), 3)), None); @@ -1040,7 +1048,8 @@ mod test { } #[test] - fn slice_min_max_pos_step() { + fn slice_min_max_pos_step() + { assert_eq!(slice_min_max(10, Slice::new(1, Some(8), 3)), Some((1, 7))); assert_eq!(slice_min_max(10, Slice::new(1, Some(9), 3)), Some((1, 7))); assert_eq!(slice_min_max(10, Slice::new(-9, Some(8), 3)), Some((1, 7))); @@ -1056,7 +1065,8 @@ mod test { } #[test] - fn slice_min_max_neg_step() { + fn slice_min_max_neg_step() + { assert_eq!(slice_min_max(10, Slice::new(1, Some(8), -3)), Some((1, 7))); assert_eq!(slice_min_max(10, Slice::new(2, Some(8), -3)), Some((4, 7))); assert_eq!(slice_min_max(10, Slice::new(-9, Some(8), -3)), Some((1, 7))); @@ -1078,7 +1088,8 @@ mod test { } #[test] - fn slices_intersect_true() { + fn slices_intersect_true() + { assert!(slices_intersect( &Dim([4, 5]), s![NewAxis, .., NewAxis, ..], @@ -1103,7 +1114,8 @@ mod test { } #[test] - fn slices_intersect_false() { + fn slices_intersect_false() + { assert!(!slices_intersect( &Dim([4, 5]), s![..;2, ..], diff --git a/src/dimension/ndindex.rs b/src/dimension/ndindex.rs index 4d4046bc8..e27e68c99 100644 --- a/src/dimension/ndindex.rs +++ b/src/dimension/ndindex.rs @@ -2,9 +2,7 @@ use std::fmt::Debug; use super::{stride_offset, stride_offset_checked}; use crate::itertools::zip; -use crate::{ - Dim, Dimension, IntoDimension, Ix, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, IxDynImpl, -}; +use crate::{Dim, Dimension, IntoDimension, Ix, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, IxDynImpl}; /// Tuple or fixed size arrays that can be used to index an array. /// @@ -19,7 +17,8 @@ use crate::{ /// assert_eq!(a[(1, 1)], 4); /// ``` #[allow(clippy::missing_safety_doc)] // TODO: Add doc -pub unsafe trait NdIndex: Debug { +pub unsafe trait NdIndex: Debug +{ #[doc(hidden)] fn index_checked(&self, dim: &E, strides: &E) -> Option; #[doc(hidden)] @@ -27,96 +26,118 @@ pub unsafe trait NdIndex: Debug { } unsafe impl NdIndex for D -where - D: Dimension, +where D: Dimension { - fn index_checked(&self, dim: &D, strides: &D) -> Option { + fn index_checked(&self, dim: &D, strides: &D) -> Option + { dim.stride_offset_checked(strides, self) } - fn index_unchecked(&self, strides: &D) -> isize { + fn index_unchecked(&self, strides: &D) -> isize + { D::stride_offset(self, strides) } } -unsafe impl NdIndex for () { +unsafe impl NdIndex for () +{ #[inline] - fn index_checked(&self, dim: &Ix0, strides: &Ix0) -> Option { + fn index_checked(&self, dim: &Ix0, strides: &Ix0) -> Option + { dim.stride_offset_checked(strides, &Ix0()) } #[inline(always)] - fn index_unchecked(&self, _strides: &Ix0) -> isize { + fn index_unchecked(&self, _strides: &Ix0) -> isize + { 0 } } -unsafe impl NdIndex for (Ix, Ix) { +unsafe impl NdIndex for (Ix, Ix) +{ #[inline] - fn index_checked(&self, dim: &Ix2, strides: &Ix2) -> Option { + fn index_checked(&self, dim: &Ix2, strides: &Ix2) -> Option + { dim.stride_offset_checked(strides, &Ix2(self.0, self.1)) } #[inline] - fn index_unchecked(&self, strides: &Ix2) -> isize { + fn index_unchecked(&self, strides: &Ix2) -> isize + { stride_offset(self.0, get!(strides, 0)) + stride_offset(self.1, get!(strides, 1)) } } -unsafe impl NdIndex for (Ix, Ix, Ix) { +unsafe impl NdIndex for (Ix, Ix, Ix) +{ #[inline] - fn index_checked(&self, dim: &Ix3, strides: &Ix3) -> Option { + fn index_checked(&self, dim: &Ix3, strides: &Ix3) -> Option + { dim.stride_offset_checked(strides, &self.into_dimension()) } #[inline] - fn index_unchecked(&self, strides: &Ix3) -> isize { + fn index_unchecked(&self, strides: &Ix3) -> isize + { stride_offset(self.0, get!(strides, 0)) + stride_offset(self.1, get!(strides, 1)) + stride_offset(self.2, get!(strides, 2)) } } -unsafe impl NdIndex for (Ix, Ix, Ix, Ix) { +unsafe impl NdIndex for (Ix, Ix, Ix, Ix) +{ #[inline] - fn index_checked(&self, dim: &Ix4, strides: &Ix4) -> Option { + fn index_checked(&self, dim: &Ix4, strides: &Ix4) -> Option + { dim.stride_offset_checked(strides, &self.into_dimension()) } #[inline] - fn index_unchecked(&self, strides: &Ix4) -> isize { + fn index_unchecked(&self, strides: &Ix4) -> isize + { zip(strides.ix(), self.into_dimension().ix()) .map(|(&s, &i)| stride_offset(i, s)) .sum() } } -unsafe impl NdIndex for (Ix, Ix, Ix, Ix, Ix) { +unsafe impl NdIndex for (Ix, Ix, Ix, Ix, Ix) +{ #[inline] - fn index_checked(&self, dim: &Ix5, strides: &Ix5) -> Option { + fn index_checked(&self, dim: &Ix5, strides: &Ix5) -> Option + { dim.stride_offset_checked(strides, &self.into_dimension()) } #[inline] - fn index_unchecked(&self, strides: &Ix5) -> isize { + fn index_unchecked(&self, strides: &Ix5) -> isize + { zip(strides.ix(), self.into_dimension().ix()) .map(|(&s, &i)| stride_offset(i, s)) .sum() } } -unsafe impl NdIndex for Ix { +unsafe impl NdIndex for Ix +{ #[inline] - fn index_checked(&self, dim: &Ix1, strides: &Ix1) -> Option { + fn index_checked(&self, dim: &Ix1, strides: &Ix1) -> Option + { dim.stride_offset_checked(strides, &Ix1(*self)) } #[inline(always)] - fn index_unchecked(&self, strides: &Ix1) -> isize { + fn index_unchecked(&self, strides: &Ix1) -> isize + { stride_offset(*self, get!(strides, 0)) } } -unsafe impl NdIndex for Ix { +unsafe impl NdIndex for Ix +{ #[inline] - fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { + fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option + { debug_assert_eq!(dim.ndim(), 1); stride_offset_checked(dim.ix(), strides.ix(), &[*self]) } #[inline(always)] - fn index_unchecked(&self, strides: &IxDyn) -> isize { + fn index_unchecked(&self, strides: &IxDyn) -> isize + { debug_assert_eq!(strides.ndim(), 1); stride_offset(*self, get!(strides, 0)) } @@ -155,9 +176,11 @@ ndindex_with_array! { } // implement NdIndex for Dim<[Ix; 2]> and so on -unsafe impl NdIndex for Dim<[Ix; N]> { +unsafe impl NdIndex for Dim<[Ix; N]> +{ #[inline] - fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { + fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option + { debug_assert_eq!( strides.ndim(), N, @@ -169,7 +192,8 @@ unsafe impl NdIndex for Dim<[Ix; N]> { } #[inline] - fn index_unchecked(&self, strides: &IxDyn) -> isize { + fn index_unchecked(&self, strides: &IxDyn) -> isize + { debug_assert_eq!( strides.ndim(), N, @@ -184,9 +208,11 @@ unsafe impl NdIndex for Dim<[Ix; N]> { } // implement NdIndex for [Ix; 2] and so on -unsafe impl NdIndex for [Ix; N] { +unsafe impl NdIndex for [Ix; N] +{ #[inline] - fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { + fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option + { debug_assert_eq!( strides.ndim(), N, @@ -198,7 +224,8 @@ unsafe impl NdIndex for [Ix; N] { } #[inline] - fn index_unchecked(&self, strides: &IxDyn) -> isize { + fn index_unchecked(&self, strides: &IxDyn) -> isize + { debug_assert_eq!( strides.ndim(), N, @@ -212,27 +239,35 @@ unsafe impl NdIndex for [Ix; N] { } } -impl<'a> IntoDimension for &'a [Ix] { +impl<'a> IntoDimension for &'a [Ix] +{ type Dim = IxDyn; - fn into_dimension(self) -> Self::Dim { + fn into_dimension(self) -> Self::Dim + { Dim(IxDynImpl::from(self)) } } -unsafe impl<'a> NdIndex for &'a IxDyn { - fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { +unsafe impl<'a> NdIndex for &'a IxDyn +{ + fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option + { (**self).index_checked(dim, strides) } - fn index_unchecked(&self, strides: &IxDyn) -> isize { + fn index_unchecked(&self, strides: &IxDyn) -> isize + { (**self).index_unchecked(strides) } } -unsafe impl<'a> NdIndex for &'a [Ix] { - fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { +unsafe impl<'a> NdIndex for &'a [Ix] +{ + fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option + { stride_offset_checked(dim.ix(), strides.ix(), self) } - fn index_unchecked(&self, strides: &IxDyn) -> isize { + fn index_unchecked(&self, strides: &IxDyn) -> isize + { zip(strides.ix(), *self) .map(|(&s, &i)| stride_offset(i, s)) .sum() diff --git a/src/dimension/ops.rs b/src/dimension/ops.rs index dd23216f6..1365ab488 100644 --- a/src/dimension/ops.rs +++ b/src/dimension/ops.rs @@ -1,7 +1,8 @@ use crate::imp_prelude::*; /// Adds the two dimensions at compile time. -pub trait DimAdd { +pub trait DimAdd +{ /// The sum of the two dimensions. type Output: Dimension; } @@ -27,7 +28,8 @@ macro_rules! impl_dimadd_const_out_dyn { }; } -impl DimAdd for Ix0 { +impl DimAdd for Ix0 +{ type Output = D; } @@ -85,6 +87,7 @@ impl_dimadd_const_out_dyn!(6, 5); impl_dimadd_const_out_dyn!(6, 6); impl_dimadd_const_out_dyn!(6, IxDyn); -impl DimAdd for IxDyn { +impl DimAdd for IxDyn +{ type Output = IxDyn; } diff --git a/src/dimension/remove_axis.rs b/src/dimension/remove_axis.rs index da366ae17..cbb039fc5 100644 --- a/src/dimension/remove_axis.rs +++ b/src/dimension/remove_axis.rs @@ -12,21 +12,26 @@ use crate::{Axis, Dim, Dimension, Ix, Ix0, Ix1}; /// /// `RemoveAxis` defines a larger-than relation for array shapes: /// removing one axis from *Self* gives smaller dimension *Smaller*. -pub trait RemoveAxis: Dimension { +pub trait RemoveAxis: Dimension +{ fn remove_axis(&self, axis: Axis) -> Self::Smaller; } -impl RemoveAxis for Dim<[Ix; 1]> { +impl RemoveAxis for Dim<[Ix; 1]> +{ #[inline] - fn remove_axis(&self, axis: Axis) -> Ix0 { + fn remove_axis(&self, axis: Axis) -> Ix0 + { debug_assert!(axis.index() < self.ndim()); Ix0() } } -impl RemoveAxis for Dim<[Ix; 2]> { +impl RemoveAxis for Dim<[Ix; 2]> +{ #[inline] - fn remove_axis(&self, axis: Axis) -> Ix1 { + fn remove_axis(&self, axis: Axis) -> Ix1 + { let axis = axis.index(); debug_assert!(axis < self.ndim()); if axis == 0 { diff --git a/src/dimension/reshape.rs b/src/dimension/reshape.rs index c6e08848d..52d9e719a 100644 --- a/src/dimension/reshape.rs +++ b/src/dimension/reshape.rs @@ -1,10 +1,8 @@ - -use crate::{Dimension, Order, ShapeError, ErrorKind}; -use crate::dimension::sequence::{Sequence, SequenceMut, Forward, Reverse}; +use crate::dimension::sequence::{Forward, Reverse, Sequence, SequenceMut}; +use crate::{Dimension, ErrorKind, Order, ShapeError}; #[inline] -pub(crate) fn reshape_dim(from: &D, strides: &D, to: &E, order: Order) - -> Result +pub(crate) fn reshape_dim(from: &D, strides: &D, to: &E, order: Order) -> Result where D: Dimension, E: Dimension, @@ -13,12 +11,10 @@ where let mut to_strides = E::zeros(to.ndim()); match order { Order::RowMajor => { - reshape_dim_c(&Forward(from), &Forward(strides), - &Forward(to), Forward(&mut to_strides))?; + reshape_dim_c(&Forward(from), &Forward(strides), &Forward(to), Forward(&mut to_strides))?; } Order::ColumnMajor => { - reshape_dim_c(&Reverse(from), &Reverse(strides), - &Reverse(to), Reverse(&mut to_strides))?; + reshape_dim_c(&Reverse(from), &Reverse(strides), &Reverse(to), Reverse(&mut to_strides))?; } } Ok(to_strides) @@ -31,7 +27,7 @@ where /// This function uses RowMajor index ordering if the inputs are read in the forward direction /// (index 0 is axis 0 etc) and ColumnMajor index ordering if the inputs are read in reversed /// direction (as made possible with the Sequence trait). -/// +/// /// Preconditions: /// /// 1. from_dim and to_dim are valid dimensions (product of all non-zero axes @@ -47,16 +43,15 @@ where /// - IncompatibleLayout if the input shape and stride can not be remapped to the output shape /// without moving the array data into a new memory layout. /// - Ok if the from dim could be mapped to the new to dim. -fn reshape_dim_c(from_dim: &D, from_strides: &D, to_dim: &E, mut to_strides: E2) - -> Result<(), ShapeError> +fn reshape_dim_c(from_dim: &D, from_strides: &D, to_dim: &E, mut to_strides: E2) -> Result<(), ShapeError> where - D: Sequence, - E: Sequence, - E2: SequenceMut, + D: Sequence, + E: Sequence, + E2: SequenceMut, { // cursor indexes into the from and to dimensions - let mut fi = 0; // index into `from_dim` - let mut ti = 0; // index into `to_dim`. + let mut fi = 0; // index into `from_dim` + let mut ti = 0; // index into `to_dim`. while fi < from_dim.len() && ti < to_dim.len() { let mut fd = from_dim[fi]; @@ -67,7 +62,7 @@ where to_strides[ti] = from_strides[fi]; fi += 1; ti += 1; - continue + continue; } if fd == 1 { @@ -88,8 +83,8 @@ where // stride times element count is to be distributed out over a combination of axes. let mut fstride_whole = fs * (fd as isize); - let mut fd_product = fd; // cumulative product of axis lengths in the combination (from) - let mut td_product = td; // cumulative product of axis lengths in the combination (to) + let mut fd_product = fd; // cumulative product of axis lengths in the combination (from) + let mut td_product = td; // cumulative product of axis lengths in the combination (to) // The two axis lengths are not a match, so try to combine multiple axes // to get it to match up. @@ -151,7 +146,8 @@ where #[cfg(feature = "std")] #[test] -fn test_reshape() { +fn test_reshape() +{ use crate::Dim; macro_rules! test_reshape { @@ -238,4 +234,3 @@ fn test_reshape() { test_reshape!(ok F from [1, 5, 1, 2, 1], [1, 1, 1, 5, 1], to [10], [1]); test_reshape!(fail C from [1, 5, 1, 2, 1], [1, 1, 1, 5, 1], to [10]); } - diff --git a/src/dimension/sequence.rs b/src/dimension/sequence.rs index 835e00d18..ed3605d57 100644 --- a/src/dimension/sequence.rs +++ b/src/dimension/sequence.rs @@ -7,103 +7,123 @@ pub(in crate::dimension) struct Forward(pub(crate) D); pub(in crate::dimension) struct Reverse(pub(crate) D); impl Index for Forward<&D> -where - D: Dimension, +where D: Dimension { type Output = usize; #[inline] - fn index(&self, index: usize) -> &usize { + fn index(&self, index: usize) -> &usize + { &self.0[index] } } impl Index for Forward<&mut D> -where - D: Dimension, +where D: Dimension { type Output = usize; #[inline] - fn index(&self, index: usize) -> &usize { + fn index(&self, index: usize) -> &usize + { &self.0[index] } } impl IndexMut for Forward<&mut D> -where - D: Dimension, +where D: Dimension { #[inline] - fn index_mut(&mut self, index: usize) -> &mut usize { + fn index_mut(&mut self, index: usize) -> &mut usize + { &mut self.0[index] } } impl Index for Reverse<&D> -where - D: Dimension, +where D: Dimension { type Output = usize; #[inline] - fn index(&self, index: usize) -> &usize { + fn index(&self, index: usize) -> &usize + { &self.0[self.len() - index - 1] } } impl Index for Reverse<&mut D> -where - D: Dimension, +where D: Dimension { type Output = usize; #[inline] - fn index(&self, index: usize) -> &usize { + fn index(&self, index: usize) -> &usize + { &self.0[self.len() - index - 1] } } impl IndexMut for Reverse<&mut D> -where - D: Dimension, +where D: Dimension { #[inline] - fn index_mut(&mut self, index: usize) -> &mut usize { + fn index_mut(&mut self, index: usize) -> &mut usize + { let len = self.len(); &mut self.0[len - index - 1] } } /// Indexable sequence with length -pub(in crate::dimension) trait Sequence: Index { +pub(in crate::dimension) trait Sequence: Index +{ fn len(&self) -> usize; } /// Indexable sequence with length (mut) -pub(in crate::dimension) trait SequenceMut: Sequence + IndexMut { } +pub(in crate::dimension) trait SequenceMut: Sequence + IndexMut {} -impl Sequence for Forward<&D> where D: Dimension { +impl Sequence for Forward<&D> +where D: Dimension +{ #[inline] - fn len(&self) -> usize { self.0.ndim() } + fn len(&self) -> usize + { + self.0.ndim() + } } -impl Sequence for Forward<&mut D> where D: Dimension { +impl Sequence for Forward<&mut D> +where D: Dimension +{ #[inline] - fn len(&self) -> usize { self.0.ndim() } + fn len(&self) -> usize + { + self.0.ndim() + } } -impl SequenceMut for Forward<&mut D> where D: Dimension { } +impl SequenceMut for Forward<&mut D> where D: Dimension {} -impl Sequence for Reverse<&D> where D: Dimension { +impl Sequence for Reverse<&D> +where D: Dimension +{ #[inline] - fn len(&self) -> usize { self.0.ndim() } + fn len(&self) -> usize + { + self.0.ndim() + } } -impl Sequence for Reverse<&mut D> where D: Dimension { +impl Sequence for Reverse<&mut D> +where D: Dimension +{ #[inline] - fn len(&self) -> usize { self.0.ndim() } + fn len(&self) -> usize + { + self.0.ndim() + } } -impl SequenceMut for Reverse<&mut D> where D: Dimension { } - +impl SequenceMut for Reverse<&mut D> where D: Dimension {} diff --git a/src/error.rs b/src/error.rs index c45496142..eb7395ad8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,20 +12,24 @@ use std::fmt; /// An error related to array shape or layout. #[derive(Clone)] -pub struct ShapeError { +pub struct ShapeError +{ // we want to be able to change this representation later repr: ErrorKind, } -impl ShapeError { +impl ShapeError +{ /// Return the `ErrorKind` of this error. #[inline] - pub fn kind(&self) -> ErrorKind { + pub fn kind(&self) -> ErrorKind + { self.repr } /// Create a new `ShapeError` - pub fn from_kind(error: ErrorKind) -> Self { + pub fn from_kind(error: ErrorKind) -> Self + { from_kind(error) } } @@ -36,7 +40,8 @@ impl ShapeError { /// is not guaranteed. #[non_exhaustive] #[derive(Copy, Clone, Debug)] -pub enum ErrorKind { +pub enum ErrorKind +{ /// incompatible shape IncompatibleShape = 1, /// incompatible memory layout @@ -52,20 +57,25 @@ pub enum ErrorKind { } #[inline(always)] -pub fn from_kind(k: ErrorKind) -> ShapeError { +pub fn from_kind(k: ErrorKind) -> ShapeError +{ ShapeError { repr: k } } -impl PartialEq for ErrorKind { +impl PartialEq for ErrorKind +{ #[inline(always)] - fn eq(&self, rhs: &Self) -> bool { + fn eq(&self, rhs: &Self) -> bool + { *self as u8 == *rhs as u8 } } -impl PartialEq for ShapeError { +impl PartialEq for ShapeError +{ #[inline(always)] - fn eq(&self, rhs: &Self) -> bool { + fn eq(&self, rhs: &Self) -> bool + { self.repr == rhs.repr } } @@ -73,8 +83,10 @@ impl PartialEq for ShapeError { #[cfg(feature = "std")] impl Error for ShapeError {} -impl fmt::Display for ShapeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl fmt::Display for ShapeError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { let description = match self.kind() { ErrorKind::IncompatibleShape => "incompatible shapes", ErrorKind::IncompatibleLayout => "incompatible memory layout", @@ -87,8 +99,10 @@ impl fmt::Display for ShapeError { } } -impl fmt::Debug for ShapeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl fmt::Debug for ShapeError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { write!(f, "{}", self) } } diff --git a/src/extension/nonnull.rs b/src/extension/nonnull.rs index 4deee11ac..08f80927e 100644 --- a/src/extension/nonnull.rs +++ b/src/extension/nonnull.rs @@ -1,9 +1,10 @@ -use std::ptr::NonNull; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +use std::ptr::NonNull; /// Return a NonNull pointer to the vector's data -pub(crate) fn nonnull_from_vec_data(v: &mut Vec) -> NonNull { +pub(crate) fn nonnull_from_vec_data(v: &mut Vec) -> NonNull +{ // this pointer is guaranteed to be non-null unsafe { NonNull::new_unchecked(v.as_mut_ptr()) } } @@ -14,7 +15,8 @@ pub(crate) fn nonnull_from_vec_data(v: &mut Vec) -> NonNull { /// This is checked with a debug assertion, and will panic if this is not true, /// but treat this as an unconditional conversion. #[inline] -pub(crate) unsafe fn nonnull_debug_checked_from_ptr(ptr: *mut T) -> NonNull { +pub(crate) unsafe fn nonnull_debug_checked_from_ptr(ptr: *mut T) -> NonNull +{ debug_assert!(!ptr.is_null()); NonNull::new_unchecked(ptr) } diff --git a/src/free_functions.rs b/src/free_functions.rs index 11a32a1f0..3adf2d8f3 100644 --- a/src/free_functions.rs +++ b/src/free_functions.rs @@ -51,22 +51,26 @@ macro_rules! array { } /// Create a zero-dimensional array with the element `x`. -pub fn arr0(x: A) -> Array0 { +pub fn arr0(x: A) -> Array0 +{ unsafe { ArrayBase::from_shape_vec_unchecked((), vec![x]) } } /// Create a one-dimensional array with elements from `xs`. -pub fn arr1(xs: &[A]) -> Array1 { +pub fn arr1(xs: &[A]) -> Array1 +{ ArrayBase::from(xs.to_vec()) } /// Create a one-dimensional array with elements from `xs`. -pub fn rcarr1(xs: &[A]) -> ArcArray1 { +pub fn rcarr1(xs: &[A]) -> ArcArray1 +{ arr1(xs).into_shared() } /// Create a zero-dimensional array view borrowing `x`. -pub const fn aview0(x: &A) -> ArrayView0<'_, A> { +pub const fn aview0(x: &A) -> ArrayView0<'_, A> +{ ArrayBase { data: ViewRepr::new(), // Safe because references are always non-null. @@ -97,7 +101,8 @@ pub const fn aview0(x: &A) -> ArrayView0<'_, A> { /// /// assert_eq!(C.sum(), 6.); /// ``` -pub const fn aview1(xs: &[A]) -> ArrayView1<'_, A> { +pub const fn aview1(xs: &[A]) -> ArrayView1<'_, A> +{ if size_of::() == 0 { assert!( xs.len() <= isize::MAX as usize, @@ -131,7 +136,8 @@ pub const fn aview1(xs: &[A]) -> ArrayView1<'_, A> { /// const C: ArrayView2<'static, f64> = aview2(&[[1., 2., 3.], [4., 5., 6.]]); /// assert_eq!(C.sum(), 21.); /// ``` -pub const fn aview2(xs: &[[A; N]]) -> ArrayView2<'_, A> { +pub const fn aview2(xs: &[[A; N]]) -> ArrayView2<'_, A> +{ let cols = N; let rows = xs.len(); if size_of::() == 0 { @@ -179,7 +185,8 @@ pub const fn aview2(xs: &[[A; N]]) -> ArrayView2<'_, A> { /// } /// assert_eq!(&data[..10], [5, 0, 0, 5, 0, 0, 5, 0, 0, 5]); /// ``` -pub fn aview_mut1(xs: &mut [A]) -> ArrayViewMut1<'_, A> { +pub fn aview_mut1(xs: &mut [A]) -> ArrayViewMut1<'_, A> +{ ArrayViewMut::from(xs) } @@ -205,7 +212,8 @@ pub fn aview_mut1(xs: &mut [A]) -> ArrayViewMut1<'_, A> { /// // look at the start of the result /// assert_eq!(&data[..3], [[1., -1.], [1., -1.], [1., -1.]]); /// ``` -pub fn aview_mut2(xs: &mut [[A; N]]) -> ArrayViewMut2<'_, A> { +pub fn aview_mut2(xs: &mut [[A; N]]) -> ArrayViewMut2<'_, A> +{ ArrayViewMut2::from(xs) } @@ -220,20 +228,23 @@ pub fn aview_mut2(xs: &mut [[A; N]]) -> ArrayViewMut2<'_, A> /// a.shape() == [2, 3] /// ); /// ``` -pub fn arr2(xs: &[[A; N]]) -> Array2 { +pub fn arr2(xs: &[[A; N]]) -> Array2 +{ Array2::from(xs.to_vec()) } -impl From> for Array2 { +impl From> for Array2 +{ /// Converts the `Vec` of arrays to an owned 2-D array. /// /// **Panics** if the product of non-zero axis lengths overflows `isize`. - fn from(mut xs: Vec<[A; N]>) -> Self { + fn from(mut xs: Vec<[A; N]>) -> Self + { let dim = Ix2(xs.len(), N); let ptr = xs.as_mut_ptr(); let cap = xs.capacity(); - let expand_len = dimension::size_of_shape_checked(&dim) - .expect("Product of non-zero axis lengths must not overflow isize."); + let expand_len = + dimension::size_of_shape_checked(&dim).expect("Product of non-zero axis lengths must not overflow isize."); forget(xs); unsafe { let v = if size_of::() == 0 { @@ -251,16 +262,18 @@ impl From> for Array2 { } } -impl From> for Array3 { +impl From> for Array3 +{ /// Converts the `Vec` of arrays to an owned 3-D array. /// /// **Panics** if the product of non-zero axis lengths overflows `isize`. - fn from(mut xs: Vec<[[A; M]; N]>) -> Self { + fn from(mut xs: Vec<[[A; M]; N]>) -> Self + { let dim = Ix3(xs.len(), N, M); let ptr = xs.as_mut_ptr(); let cap = xs.capacity(); - let expand_len = dimension::size_of_shape_checked(&dim) - .expect("Product of non-zero axis lengths must not overflow isize."); + let expand_len = + dimension::size_of_shape_checked(&dim).expect("Product of non-zero axis lengths must not overflow isize."); forget(xs); unsafe { let v = if size_of::() == 0 { @@ -280,7 +293,8 @@ impl From> for Array3 { /// Create a two-dimensional array with elements from `xs`. /// -pub fn rcarr2(xs: &[[A; N]]) -> ArcArray2 { +pub fn rcarr2(xs: &[[A; N]]) -> ArcArray2 +{ arr2(xs).into_shared() } @@ -301,11 +315,13 @@ pub fn rcarr2(xs: &[[A; N]]) -> ArcArray2 { /// a.shape() == [3, 2, 2] /// ); /// ``` -pub fn arr3(xs: &[[[A; M]; N]]) -> Array3 { +pub fn arr3(xs: &[[[A; M]; N]]) -> Array3 +{ Array3::from(xs.to_vec()) } /// Create a three-dimensional array with elements from `xs`. -pub fn rcarr3(xs: &[[[A; M]; N]]) -> ArcArray { +pub fn rcarr3(xs: &[[[A; M]; N]]) -> ArcArray +{ arr3(xs).into_shared() } diff --git a/src/geomspace.rs b/src/geomspace.rs index c1935c71e..0ac91f529 100644 --- a/src/geomspace.rs +++ b/src/geomspace.rs @@ -11,7 +11,8 @@ use num_traits::Float; /// An iterator of a sequence of geometrically spaced floats. /// /// Iterator element type is `F`. -pub struct Geomspace { +pub struct Geomspace +{ sign: F, start: F, step: F, @@ -20,13 +21,13 @@ pub struct Geomspace { } impl Iterator for Geomspace -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 { @@ -39,18 +40,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 Geomspace -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,8 +82,7 @@ impl ExactSizeIterator for Geomspace where Geomspace: Iterator {} /// **Panics** if converting `n - 1` to type `F` fails. #[inline] pub fn geomspace(a: F, b: F, n: usize) -> Option> -where - F: Float, +where F: Float { if a == F::zero() || b == F::zero() || a.is_sign_negative() != b.is_sign_negative() { return None; @@ -104,12 +105,14 @@ where } #[cfg(test)] -mod tests { +mod tests +{ use super::geomspace; #[test] #[cfg(feature = "approx")] - fn valid() { + fn valid() + { use crate::{arr1, Array1}; use approx::assert_abs_diff_eq; @@ -127,7 +130,8 @@ mod tests { } #[test] - fn iter_forward() { + fn iter_forward() + { let mut iter = geomspace(1.0f64, 1e3, 4).unwrap(); assert!(iter.size_hint() == (4, Some(4))); @@ -142,7 +146,8 @@ mod tests { } #[test] - fn iter_backward() { + fn iter_backward() + { let mut iter = geomspace(1.0f64, 1e3, 4).unwrap(); assert!(iter.size_hint() == (4, Some(4))); @@ -157,17 +162,20 @@ mod tests { } #[test] - fn zero_lower() { + fn zero_lower() + { assert!(geomspace(0.0, 1.0, 4).is_none()); } #[test] - fn zero_upper() { + fn zero_upper() + { assert!(geomspace(1.0, 0.0, 4).is_none()); } #[test] - fn zero_included() { + fn zero_included() + { assert!(geomspace(-1.0, 1.0, 4).is_none()); } } diff --git a/src/impl_1d.rs b/src/impl_1d.rs index a9fe84407..e49fdd731 100644 --- a/src/impl_1d.rs +++ b/src/impl_1d.rs @@ -16,8 +16,7 @@ use crate::low_level_util::AbortIfPanic; /// # Methods For 1-D Arrays impl ArrayBase -where - S: RawData, +where S: RawData { /// Return an vector with the elements of the one-dimensional array. pub fn to_vec(&self) -> Vec @@ -35,8 +34,7 @@ where /// Rotate the elements of the array by 1 element towards the front; /// the former first element becomes the last. pub(crate) fn rotate1_front(&mut self) - where - S: DataMut, + where S: DataMut { // use swapping to keep all elements initialized (as required by owned storage) let mut lane_iter = self.iter_mut(); diff --git a/src/impl_2d.rs b/src/impl_2d.rs index cd5cf7e5c..c2e9725ac 100644 --- a/src/impl_2d.rs +++ b/src/impl_2d.rs @@ -11,8 +11,7 @@ use crate::imp_prelude::*; /// # Methods For 2-D Arrays impl ArrayBase -where - S: RawData, +where S: RawData { /// Return an array view of row `index`. /// @@ -25,8 +24,7 @@ where /// ``` #[track_caller] pub fn row(&self, index: Ix) -> ArrayView1<'_, A> - where - S: Data, + where S: Data { self.index_axis(Axis(0), index) } @@ -43,8 +41,7 @@ where /// ``` #[track_caller] pub fn row_mut(&mut self, index: Ix) -> ArrayViewMut1<'_, A> - where - S: DataMut, + where S: DataMut { self.index_axis_mut(Axis(0), index) } @@ -66,7 +63,8 @@ where /// // get length of any particular axis with .len_of() /// assert_eq!(m, array.len_of(Axis(0))); /// ``` - pub fn nrows(&self) -> usize { + pub fn nrows(&self) -> usize + { self.len_of(Axis(0)) } @@ -81,8 +79,7 @@ where /// ``` #[track_caller] pub fn column(&self, index: Ix) -> ArrayView1<'_, A> - where - S: Data, + where S: Data { self.index_axis(Axis(1), index) } @@ -99,8 +96,7 @@ where /// ``` #[track_caller] pub fn column_mut(&mut self, index: Ix) -> ArrayViewMut1<'_, A> - where - S: DataMut, + where S: DataMut { self.index_axis_mut(Axis(1), index) } @@ -122,7 +118,8 @@ where /// // get length of any particular axis with .len_of() /// assert_eq!(n, array.len_of(Axis(1))); /// ``` - pub fn ncols(&self) -> usize { + pub fn ncols(&self) -> usize + { self.len_of(Axis(1)) } @@ -141,7 +138,8 @@ where /// let array = array![[1., 2., 5.], [3., 4., 6.]]; /// assert!(!array.is_square()); /// ``` - pub fn is_square(&self) -> bool { + pub fn is_square(&self) -> bool + { let (m, n) = self.dim(); m == n } diff --git a/src/impl_clone.rs b/src/impl_clone.rs index e2e111a12..d65f6c338 100644 --- a/src/impl_clone.rs +++ b/src/impl_clone.rs @@ -9,8 +9,10 @@ use crate::imp_prelude::*; use crate::RawDataClone; -impl Clone for ArrayBase { - fn clone(&self) -> ArrayBase { +impl Clone for ArrayBase +{ + fn clone(&self) -> ArrayBase + { // safe because `clone_with_ptr` promises to provide equivalent data and ptr unsafe { let (data, ptr) = self.data.clone_with_ptr(self.ptr); @@ -26,7 +28,8 @@ impl Clone for ArrayBase { /// `Array` implements `.clone_from()` to reuse an array's existing /// allocation. Semantically equivalent to `*self = other.clone()`, but /// potentially more efficient. - fn clone_from(&mut self, other: &Self) { + fn clone_from(&mut self, other: &Self) + { unsafe { self.ptr = self.data.clone_from_with_ptr(&other.data, other.ptr); self.dim.clone_from(&other.dim); diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 94ddebcd6..e5f19a837 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -11,14 +11,14 @@ //! #![allow(clippy::match_wild_err_arm)] +use alloc::vec; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; #[cfg(feature = "std")] use num_traits::Float; use num_traits::{One, Zero}; use std::mem; use std::mem::MaybeUninit; -use alloc::vec; -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; use crate::dimension; use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr; @@ -36,7 +36,6 @@ use crate::StrideShape; use crate::{geomspace, linspace, logspace}; use rawpointer::PointerExt; - /// # Constructor Methods for Owned Arrays /// /// Note that the constructor methods apply to `Array` and `ArcArray`, @@ -44,8 +43,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 +54,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, @@ -76,7 +75,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()) } @@ -99,8 +99,7 @@ where /// ``` #[cfg(feature = "std")] pub fn linspace(start: A, end: A, n: usize) -> Self - where - A: Float, + where A: Float { Self::from(to_vec(linspace::linspace(start, end, n))) } @@ -118,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))) } @@ -147,8 +145,7 @@ where /// ``` #[cfg(feature = "std")] pub fn logspace(base: A, start: A, end: A, n: usize) -> Self - where - A: Float, + where A: Float { Self::from(to_vec(logspace::logspace(base, start, end, n))) } @@ -182,8 +179,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)?))) } @@ -191,8 +187,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). /// @@ -460,14 +455,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)?; @@ -503,8 +498,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; @@ -512,7 +506,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).is_ok()); @@ -526,8 +521,7 @@ where /// # Safety /// /// See from_shape_vec_unchecked - pub(crate) unsafe fn from_shape_trusted_iter_unchecked(shape: Sh, iter: I, map: F) - -> Self + pub(crate) unsafe fn from_shape_trusted_iter_unchecked(shape: Sh, iter: I, map: F) -> Self where Sh: Into>, I: TrustedIterator + ExactSizeIterator, @@ -540,7 +534,6 @@ where Self::from_vec_dim_stride_unchecked(dim, strides, v) } - /// Create an array with uninitialized elements, shape `shape`. /// /// The uninitialized elements of type `A` are represented by the type `MaybeUninit`, @@ -583,12 +576,11 @@ where /// b.assume_init() /// } /// } - /// + /// /// # let _ = shift_by_two; /// ``` pub fn uninit(shape: Sh) -> ArrayBase - where - Sh: ShapeBuilder, + where Sh: ShapeBuilder { unsafe { let shape = shape.into_shape_with_order(); @@ -633,9 +625,11 @@ where array } - #[deprecated(note = "This method is hard to use correctly. Use `uninit` instead.", - since = "0.15.0")] - #[allow(clippy::uninit_vec)] // this is explicitly intended to create uninitialized memory + #[deprecated( + note = "This method is hard to use correctly. Use `uninit` instead.", + since = "0.15.0" + )] + #[allow(clippy::uninit_vec)] // this is explicitly intended to create uninitialized memory /// Create an array with uninitialized elements, shape `shape`. /// /// Prefer to use [`uninit()`](ArrayBase::uninit) if possible, because it is @@ -670,7 +664,6 @@ where v.set_len(size); Self::from_shape_vec_unchecked(shape, v) } - } impl ArrayBase @@ -683,8 +676,7 @@ where /// This method has been renamed to `uninit` #[deprecated(note = "Renamed to `uninit`", since = "0.15.0")] pub fn maybe_uninit(shape: Sh) -> Self - where - Sh: ShapeBuilder, + where Sh: ShapeBuilder { unsafe { let shape = shape.into_shape_with_order(); diff --git a/src/impl_cow.rs b/src/impl_cow.rs index 22d5c78b2..f064ce7bd 100644 --- a/src/impl_cow.rs +++ b/src/impl_cow.rs @@ -12,49 +12,45 @@ use crate::imp_prelude::*; /// /// ***See also all methods for [`ArrayBase`]*** impl<'a, A, D> CowArray<'a, A, D> -where - D: Dimension, +where D: Dimension { /// Returns `true` iff the array is the view (borrowed) variant. - pub fn is_view(&self) -> bool { + pub fn is_view(&self) -> bool + { self.data.is_view() } /// Returns `true` iff the array is the owned variant. - pub fn is_owned(&self) -> bool { + pub fn is_owned(&self) -> bool + { self.data.is_owned() } } impl<'a, A, D> From> for CowArray<'a, A, D> -where - D: Dimension, +where D: Dimension { - fn from(view: ArrayView<'a, A, D>) -> CowArray<'a, A, D> { + fn from(view: ArrayView<'a, A, D>) -> CowArray<'a, A, D> + { // safe because equivalent data - unsafe { - ArrayBase::from_data_ptr(CowRepr::View(view.data), view.ptr) - .with_strides_dim(view.strides, view.dim) - } + unsafe { ArrayBase::from_data_ptr(CowRepr::View(view.data), view.ptr).with_strides_dim(view.strides, view.dim) } } } impl<'a, A, D> From> for CowArray<'a, A, D> -where - D: Dimension, +where D: Dimension { - fn from(array: Array) -> CowArray<'a, A, D> { + fn from(array: Array) -> CowArray<'a, A, D> + { // safe because equivalent data unsafe { - ArrayBase::from_data_ptr(CowRepr::Owned(array.data), array.ptr) - .with_strides_dim(array.strides, array.dim) + ArrayBase::from_data_ptr(CowRepr::Owned(array.data), array.ptr).with_strides_dim(array.strides, array.dim) } } } impl<'a, A, Slice: ?Sized> From<&'a Slice> for CowArray<'a, A, Ix1> -where - Slice: AsRef<[A]>, +where Slice: AsRef<[A]> { /// Create a one-dimensional clone-on-write view of the data in `slice`. /// @@ -67,7 +63,8 @@ where /// assert!(array.is_view()); /// assert_eq!(array, array![1., 2., 3., 4.]); /// ``` - fn from(slice: &'a Slice) -> Self { + fn from(slice: &'a Slice) -> Self + { Self::from(ArrayView1::from(slice)) } } @@ -78,7 +75,8 @@ where D: Dimension, { /// Create a read-only clone-on-write view of the array. - fn from(array: &'a ArrayBase) -> Self { + fn from(array: &'a ArrayBase) -> Self + { Self::from(array.view()) } } diff --git a/src/impl_dyn.rs b/src/impl_dyn.rs index d6e1c6957..836234cec 100644 --- a/src/impl_dyn.rs +++ b/src/impl_dyn.rs @@ -11,8 +11,7 @@ use crate::imp_prelude::*; /// # Methods for Dynamic-Dimensional Arrays impl ArrayBase -where - S: Data, +where S: Data { /// Insert new array axis of length 1 at `axis`, modifying the shape and /// strides in-place. @@ -30,7 +29,8 @@ where /// assert_eq!(a.shape(), &[2, 1, 3]); /// ``` #[track_caller] - pub fn insert_axis_inplace(&mut self, axis: Axis) { + pub fn insert_axis_inplace(&mut self, axis: Axis) + { assert!(axis.index() <= self.ndim()); self.dim = self.dim.insert_axis(axis); self.strides = self.strides.insert_axis(axis); @@ -52,7 +52,8 @@ where /// assert_eq!(a.shape(), &[2]); /// ``` #[track_caller] - pub fn index_axis_inplace(&mut self, axis: Axis, index: usize) { + pub fn index_axis_inplace(&mut self, axis: Axis, index: usize) + { self.collapse_axis(axis, index); self.dim = self.dim.remove_axis(axis); self.strides = self.strides.remove_axis(axis); diff --git a/src/impl_internal_constructors.rs b/src/impl_internal_constructors.rs index 5d47c9897..ebb2e26e0 100644 --- a/src/impl_internal_constructors.rs +++ b/src/impl_internal_constructors.rs @@ -12,8 +12,7 @@ use crate::imp_prelude::*; // internal "builder-like" methods impl ArrayBase -where - S: RawData, +where S: RawData { /// Create an (initially) empty one-dimensional array from the given data and array head /// pointer @@ -21,9 +20,10 @@ where /// ## Safety /// /// The caller must ensure that the data storage and pointer is valid. - /// + /// /// See ArrayView::from_shape_ptr for general pointer validity documentation. - pub(crate) unsafe fn from_data_ptr(data: S, ptr: NonNull) -> Self { + pub(crate) unsafe fn from_data_ptr(data: S, ptr: NonNull) -> Self + { let array = ArrayBase { data, ptr, @@ -41,7 +41,6 @@ where S: RawData, D: Dimension, { - /// Set strides and dimension of the array to the new values /// /// The argument order with strides before dimensions is used because strides are often @@ -52,8 +51,7 @@ where /// The caller needs to ensure that the new strides and dimensions are correct /// for the array data. pub(crate) unsafe fn with_strides_dim(self, strides: E, dim: E) -> ArrayBase - where - E: Dimension + where E: Dimension { debug_assert_eq!(strides.ndim(), dim.ndim()); ArrayBase { diff --git a/src/impl_methods.rs b/src/impl_methods.rs index feded588a..d1250ec28 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -6,36 +6,54 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::mem::{size_of, ManuallyDrop}; use alloc::slice; use alloc::vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use rawpointer::PointerExt; +use std::mem::{size_of, ManuallyDrop}; use crate::imp_prelude::*; -use crate::{arraytraits, DimMax}; use crate::argument_traits::AssignElem; use crate::dimension; +use crate::dimension::broadcast::co_broadcast; +use crate::dimension::reshape_dim; use crate::dimension::IntoDimension; use crate::dimension::{ - abs_index, axes_of, do_slice, merge_axes, move_min_stride_axis_to_last, - offset_from_low_addr_ptr_to_logical_ptr, size_of_shape_checked, stride_offset, Axes, + abs_index, + axes_of, + do_slice, + merge_axes, + move_min_stride_axis_to_last, + offset_from_low_addr_ptr_to_logical_ptr, + size_of_shape_checked, + stride_offset, + Axes, }; -use crate::dimension::broadcast::co_broadcast; -use crate::dimension::reshape_dim; -use crate::error::{self, ErrorKind, ShapeError, from_kind}; -use crate::math_cell::MathCell; +use crate::error::{self, from_kind, ErrorKind, ShapeError}; use crate::itertools::zip; -use crate::AxisDescription; +use crate::math_cell::MathCell; use crate::order::Order; use crate::shape_builder::ShapeArg; use crate::zip::{IntoNdProducer, Zip}; +use crate::AxisDescription; +use crate::{arraytraits, DimMax}; use crate::iter::{ - AxisChunksIter, AxisChunksIterMut, AxisIter, AxisIterMut, ExactChunks, ExactChunksMut, - IndexedIter, IndexedIterMut, Iter, IterMut, Lanes, LanesMut, Windows, + AxisChunksIter, + AxisChunksIterMut, + AxisIter, + AxisIterMut, + ExactChunks, + ExactChunksMut, + IndexedIter, + IndexedIterMut, + Iter, + IterMut, + Lanes, + LanesMut, + Windows, }; use crate::slice::{MultiSliceArg, SliceArg}; use crate::stacking::concatenate; @@ -48,7 +66,8 @@ where D: Dimension, { /// Return the total number of elements in the array. - pub fn len(&self) -> usize { + pub fn len(&self) -> usize + { self.dim.size() } @@ -59,24 +78,28 @@ where /// /// ***Panics*** if the axis is out of bounds. #[track_caller] - pub fn len_of(&self, axis: Axis) -> usize { + pub fn len_of(&self, axis: Axis) -> usize + { self.dim[axis.index()] } /// Return whether the array has any elements - pub fn is_empty(&self) -> bool { + pub fn is_empty(&self) -> bool + { self.len() == 0 } /// Return the number of dimensions (axes) in the array - pub fn ndim(&self) -> usize { + pub fn ndim(&self) -> usize + { self.dim.ndim() } /// Return the shape of the array in its “pattern” form, /// an integer in the one-dimensional case, tuple in the n-dimensional cases /// and so on. - pub fn dim(&self) -> D::Pattern { + pub fn dim(&self) -> D::Pattern + { self.dim.clone().into_pattern() } @@ -94,7 +117,8 @@ where /// // Create an array of zeros that's the same shape and dimensionality as `a`. /// let b = Array::::zeros(a.raw_dim()); /// ``` - pub fn raw_dim(&self) -> D { + pub fn raw_dim(&self) -> D + { self.dim.clone() } @@ -122,12 +146,14 @@ where /// let c = Array::zeros(a.raw_dim()); /// assert_eq!(a, c); /// ``` - pub fn shape(&self) -> &[usize] { + pub fn shape(&self) -> &[usize] + { self.dim.slice() } /// Return the strides of the array as a slice. - pub fn strides(&self) -> &[isize] { + pub fn strides(&self) -> &[isize] + { let s = self.strides.slice(); // reinterpret unsigned integer as signed unsafe { slice::from_raw_parts(s.as_ptr() as *const _, s.len()) } @@ -140,15 +166,15 @@ where /// /// ***Panics*** if the axis is out of bounds. #[track_caller] - pub fn stride_of(&self, axis: Axis) -> isize { + pub fn stride_of(&self, axis: Axis) -> isize + { // strides are reinterpreted as isize self.strides[axis.index()] as isize } /// Return a read-only view of the array pub fn view(&self) -> ArrayView<'_, A, D> - where - S: Data, + where S: Data { debug_assert!(self.pointer_is_inbounds()); unsafe { ArrayView::new(self.ptr, self.dim.clone(), self.strides.clone()) } @@ -156,8 +182,7 @@ where /// Return a read-write view of the array pub fn view_mut(&mut self) -> ArrayViewMut<'_, A, D> - where - S: DataMut, + where S: DataMut { self.ensure_unique(); unsafe { ArrayViewMut::new(self.ptr, self.dim.clone(), self.strides.clone()) } @@ -171,8 +196,7 @@ where /// The view acts "as if" the elements are temporarily in cells, and elements /// can be changed through shared references using the regular cell methods. pub fn cell_view(&mut self) -> ArrayView<'_, MathCell, D> - where - S: DataMut, + where S: DataMut { self.view_mut().into_cell_view() } @@ -213,12 +237,7 @@ where S: Data, { if let Some(slc) = self.as_slice_memory_order() { - unsafe { - Array::from_shape_vec_unchecked( - self.dim.clone().strides(self.strides.clone()), - slc.to_vec(), - ) - } + unsafe { Array::from_shape_vec_unchecked(self.dim.clone().strides(self.strides.clone()), slc.to_vec()) } } else { self.map(A::clone) } @@ -266,8 +285,7 @@ where /// assert_eq!(unique, array![[1., 2.], [3., 4.]]); /// ``` pub fn try_into_owned_nocopy(self) -> Result, Self> - where - S: Data, + where S: Data { S::try_into_owned_nocopy(self) } @@ -275,14 +293,11 @@ where /// Turn the array into a shared ownership (copy on write) array, /// without any copying. pub fn into_shared(self) -> ArcArray - where - S: DataOwned, + where S: DataOwned { let data = self.data.into_shared(); // safe because: equivalent unmoved data, ptr and dims remain valid - unsafe { - ArrayBase::from_data_ptr(data, self.ptr).with_strides_dim(self.strides, self.dim) - } + unsafe { ArrayBase::from_data_ptr(data, self.ptr).with_strides_dim(self.strides, self.dim) } } /// Returns a reference to the first element of the array, or `None` if it @@ -301,8 +316,7 @@ where /// assert_eq!(b.first(), None); /// ``` pub fn first(&self) -> Option<&A> - where - S: Data, + where S: Data { if self.is_empty() { None @@ -327,8 +341,7 @@ where /// assert_eq!(b.first_mut(), None); /// ``` pub fn first_mut(&mut self) -> Option<&mut A> - where - S: DataMut, + where S: DataMut { if self.is_empty() { None @@ -353,8 +366,7 @@ where /// assert_eq!(b.last(), None); /// ``` pub fn last(&self) -> Option<&A> - where - S: Data, + where S: Data { if self.is_empty() { None @@ -383,8 +395,7 @@ where /// assert_eq!(b.last_mut(), None); /// ``` pub fn last_mut(&mut self) -> Option<&mut A> - where - S: DataMut, + where S: DataMut { if self.is_empty() { None @@ -404,8 +415,7 @@ where /// /// Iterator element type is `&A`. pub fn iter(&self) -> Iter<'_, A, D> - where - S: Data, + where S: Data { debug_assert!(self.pointer_is_inbounds()); self.view().into_iter_() @@ -418,8 +428,7 @@ where /// /// Iterator element type is `&mut A`. pub fn iter_mut(&mut self) -> IterMut<'_, A, D> - where - S: DataMut, + where S: DataMut { self.view_mut().into_iter_() } @@ -433,8 +442,7 @@ where /// /// See also [`Zip::indexed`] pub fn indexed_iter(&self) -> IndexedIter<'_, A, D> - where - S: Data, + where S: Data { IndexedIter::new(self.view().into_elements_base()) } @@ -446,8 +454,7 @@ where /// /// Iterator element type is `(D::Pattern, &mut A)`. pub fn indexed_iter_mut(&mut self) -> IndexedIterMut<'_, A, D> - where - S: DataMut, + where S: DataMut { IndexedIterMut::new(self.view_mut().into_elements_base()) } @@ -525,8 +532,7 @@ where /// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) #[track_caller] pub fn slice_move(mut self, info: I) -> ArrayBase - where - I: SliceArg, + where I: SliceArg { assert_eq!( info.in_ndim(), @@ -595,8 +601,7 @@ where /// - if `D` is `IxDyn` and `info` does not match the number of array axes #[track_caller] pub fn slice_collapse(&mut self, info: I) - where - I: SliceArg, + where I: SliceArg { assert_eq!( info.in_ndim(), @@ -605,17 +610,17 @@ where ); let mut axis = 0; info.as_ref().iter().for_each(|&ax_info| match ax_info { - SliceInfoElem::Slice { start, end, step } => { - self.slice_axis_inplace(Axis(axis), Slice { start, end, step }); - axis += 1; - } - SliceInfoElem::Index(index) => { - let i_usize = abs_index(self.len_of(Axis(axis)), index); - self.collapse_axis(Axis(axis), i_usize); - axis += 1; - } - SliceInfoElem::NewAxis => panic!("`slice_collapse` does not support `NewAxis`."), - }); + SliceInfoElem::Slice { start, end, step } => { + self.slice_axis_inplace(Axis(axis), Slice { start, end, step }); + axis += 1; + } + SliceInfoElem::Index(index) => { + let i_usize = abs_index(self.len_of(Axis(axis)), index); + self.collapse_axis(Axis(axis), i_usize); + axis += 1; + } + SliceInfoElem::NewAxis => panic!("`slice_collapse` does not support `NewAxis`."), + }); debug_assert_eq!(axis, self.ndim()); } @@ -626,8 +631,7 @@ where #[track_caller] #[must_use = "slice_axis returns an array view with the sliced result"] pub fn slice_axis(&self, axis: Axis, indices: Slice) -> ArrayView<'_, A, D> - where - S: Data, + where S: Data { let mut view = self.view(); view.slice_axis_inplace(axis, indices); @@ -641,8 +645,7 @@ where #[track_caller] #[must_use = "slice_axis_mut returns an array view with the sliced result"] pub fn slice_axis_mut(&mut self, axis: Axis, indices: Slice) -> ArrayViewMut<'_, A, D> - where - S: DataMut, + where S: DataMut { let mut view_mut = self.view_mut(); view_mut.slice_axis_inplace(axis, indices); @@ -654,12 +657,10 @@ where /// **Panics** if an index is out of bounds or step size is zero.
/// **Panics** if `axis` is out of bounds. #[track_caller] - pub fn slice_axis_inplace(&mut self, axis: Axis, indices: Slice) { - let offset = do_slice( - &mut self.dim.slice_mut()[axis.index()], - &mut self.strides.slice_mut()[axis.index()], - indices, - ); + pub fn slice_axis_inplace(&mut self, axis: Axis, indices: Slice) + { + let offset = + do_slice(&mut self.dim.slice_mut()[axis.index()], &mut self.strides.slice_mut()[axis.index()], indices); unsafe { self.ptr = self.ptr.offset(offset); } @@ -671,7 +672,8 @@ where /// **Panics** if an index is out of bounds or step size is zero.
/// **Panics** if `axis` is out of bounds. #[must_use = "slice_axis_move returns an array with the sliced result"] - pub fn slice_axis_move(mut self, axis: Axis, indices: Slice) -> Self { + pub fn slice_axis_move(mut self, axis: Axis, indices: Slice) -> Self + { self.slice_axis_inplace(axis, indices); self } @@ -721,8 +723,7 @@ where /// **Panics** if an index is out of bounds or step size is zero. #[track_caller] pub fn slice_each_axis_inplace(&mut self, mut f: F) - where - F: FnMut(AxisDescription) -> Slice, + where F: FnMut(AxisDescription) -> Slice { for ax in 0..self.ndim() { self.slice_axis_inplace( @@ -776,8 +777,7 @@ where /// assert_eq!(unsafe { *p }, 2.); /// ``` pub fn get_ptr(&self, index: I) -> Option<*const A> - where - I: NdIndex, + where I: NdIndex { let ptr = self.ptr; index @@ -920,17 +920,13 @@ where arraytraits::debug_bounds_check(self, &index2); let off1 = index1.index_unchecked(&self.strides); let off2 = index2.index_unchecked(&self.strides); - std::ptr::swap( - self.ptr.as_ptr().offset(off1), - self.ptr.as_ptr().offset(off2), - ); + std::ptr::swap(self.ptr.as_ptr().offset(off1), self.ptr.as_ptr().offset(off2)); } // `get` for zero-dimensional arrays // panics if dimension is not zero. otherwise an element is always present. fn get_0d(&self) -> &A - where - S: Data, + where S: Data { assert!(self.ndim() == 0); unsafe { &*self.as_ptr() } @@ -1006,23 +1002,21 @@ where /// **Panics** if `axis` or `index` is out of bounds. #[track_caller] pub fn index_axis_move(mut self, axis: Axis, index: usize) -> ArrayBase - where - D: RemoveAxis, + where D: RemoveAxis { self.collapse_axis(axis, index); let dim = self.dim.remove_axis(axis); let strides = self.strides.remove_axis(axis); // safe because new dimension, strides allow access to a subset of old data - unsafe { - self.with_strides_dim(strides, dim) - } + unsafe { self.with_strides_dim(strides, dim) } } /// Selects `index` along the axis, collapsing the axis into length one. /// /// **Panics** if `axis` or `index` is out of bounds. #[track_caller] - pub fn collapse_axis(&mut self, axis: Axis, index: usize) { + pub fn collapse_axis(&mut self, axis: Axis, index: usize) + { let offset = dimension::do_collapse_axis(&mut self.dim, &self.strides, axis.index(), index); self.ptr = unsafe { self.ptr.offset(offset) }; debug_assert!(self.pointer_is_inbounds()); @@ -1069,10 +1063,10 @@ where let view = self.view().into_dimensionality::().unwrap(); Array::from_iter(indices.iter().map(move |&index| { // Safety: bounds checked indexes - unsafe { - view.uget(index).clone() - } - })).into_dimensionality::().unwrap() + unsafe { view.uget(index).clone() } + })) + .into_dimensionality::() + .unwrap() } else { let mut subs = vec![self.view(); indices.len()]; for (&i, sub) in zip(indices, &mut subs[..]) { @@ -1115,8 +1109,7 @@ where /// } /// ``` pub fn rows(&self) -> Lanes<'_, A, D::Smaller> - where - S: Data, + where S: Data { let mut n = self.ndim(); if n == 0 { @@ -1125,10 +1118,9 @@ where Lanes::new(self.view(), Axis(n - 1)) } - #[deprecated(note="Renamed to .rows()", since="0.15.0")] + #[deprecated(note = "Renamed to .rows()", since = "0.15.0")] pub fn genrows(&self) -> Lanes<'_, A, D::Smaller> - where - S: Data, + where S: Data { self.rows() } @@ -1138,8 +1130,7 @@ where /// /// Iterator element is `ArrayView1
` (1D read-write array view). pub fn rows_mut(&mut self) -> LanesMut<'_, A, D::Smaller> - where - S: DataMut, + where S: DataMut { let mut n = self.ndim(); if n == 0 { @@ -1148,10 +1139,9 @@ where LanesMut::new(self.view_mut(), Axis(n - 1)) } - #[deprecated(note="Renamed to .rows_mut()", since="0.15.0")] + #[deprecated(note = "Renamed to .rows_mut()", since = "0.15.0")] pub fn genrows_mut(&mut self) -> LanesMut<'_, A, D::Smaller> - where - S: DataMut, + where S: DataMut { self.rows_mut() } @@ -1183,8 +1173,7 @@ where /// } /// ``` pub fn columns(&self) -> Lanes<'_, A, D::Smaller> - where - S: Data, + where S: Data { Lanes::new(self.view(), Axis(0)) } @@ -1193,10 +1182,9 @@ where /// columns of the array. For a 2D array these are the regular columns. /// /// Renamed to `.columns()` - #[deprecated(note="Renamed to .columns()", since="0.15.0")] + #[deprecated(note = "Renamed to .columns()", since = "0.15.0")] pub fn gencolumns(&self) -> Lanes<'_, A, D::Smaller> - where - S: Data, + where S: Data { self.columns() } @@ -1206,8 +1194,7 @@ where /// /// Iterator element is `ArrayView1` (1D read-write array view). pub fn columns_mut(&mut self) -> LanesMut<'_, A, D::Smaller> - where - S: DataMut, + where S: DataMut { LanesMut::new(self.view_mut(), Axis(0)) } @@ -1216,10 +1203,9 @@ where /// columns of the array and yields mutable array views. /// /// Renamed to `.columns_mut()` - #[deprecated(note="Renamed to .columns_mut()", since="0.15.0")] + #[deprecated(note = "Renamed to .columns_mut()", since = "0.15.0")] pub fn gencolumns_mut(&mut self) -> LanesMut<'_, A, D::Smaller> - where - S: DataMut, + where S: DataMut { self.columns_mut() } @@ -1253,8 +1239,7 @@ where /// assert_eq!(inner2.into_iter().next().unwrap(), aview1(&[0, 1, 2])); /// ``` pub fn lanes(&self, axis: Axis) -> Lanes<'_, A, D::Smaller> - where - S: Data, + where S: Data { Lanes::new(self.view(), axis) } @@ -1264,8 +1249,7 @@ where /// /// Iterator element is `ArrayViewMut1` (1D read-write array view). pub fn lanes_mut(&mut self, axis: Axis) -> LanesMut<'_, A, D::Smaller> - where - S: DataMut, + where S: DataMut { LanesMut::new(self.view_mut(), axis) } @@ -1368,8 +1352,7 @@ where /// ``` #[track_caller] pub fn axis_chunks_iter(&self, axis: Axis, size: usize) -> AxisChunksIter<'_, A, D> - where - S: Data, + where S: Data { AxisChunksIter::new(self.view(), axis, size) } @@ -1382,8 +1365,7 @@ where /// **Panics** if `axis` is out of bounds or if `size` is zero. #[track_caller] pub fn axis_chunks_iter_mut(&mut self, axis: Axis, size: usize) -> AxisChunksIterMut<'_, A, D> - where - S: DataMut, + where S: DataMut { AxisChunksIterMut::new(self.view_mut(), axis, size) } @@ -1539,8 +1521,7 @@ where /// } /// ``` pub fn axis_windows(&self, axis: Axis, window_size: usize) -> Windows<'_, A, D> - where - S: Data, + where S: Data { let axis_index = axis.index(); @@ -1562,7 +1543,8 @@ where } // Return (length, stride) for diagonal - fn diag_params(&self) -> (Ix, Ixs) { + fn diag_params(&self) -> (Ix, Ixs) + { /* empty shape has len 1 */ let len = self.dim.slice().iter().cloned().min().unwrap_or(1); let stride = self.strides().iter().sum(); @@ -1574,27 +1556,24 @@ where /// The diagonal is simply the sequence indexed by *(0, 0, .., 0)*, /// *(1, 1, ..., 1)* etc as long as all axes have elements. pub fn diag(&self) -> ArrayView1<'_, A> - where - S: Data, + where S: Data { self.view().into_diag() } /// Return a read-write view over the diagonal elements of the array. pub fn diag_mut(&mut self) -> ArrayViewMut1<'_, A> - where - S: DataMut, + where S: DataMut { self.view_mut().into_diag() } /// Return the diagonal as a one-dimensional array. - pub fn into_diag(self) -> ArrayBase { + pub fn into_diag(self) -> ArrayBase + { let (len, stride) = self.diag_params(); // safe because new len stride allows access to a subset of the current elements - unsafe { - self.with_strides_dim(Ix1(stride as Ix), Ix1(len)) - } + unsafe { self.with_strides_dim(Ix1(stride as Ix), Ix1(len)) } } /// Try to make the array unshared. @@ -1603,8 +1582,7 @@ where /// /// This method is mostly only useful with unsafe code. fn try_ensure_unique(&mut self) - where - S: RawDataMut, + where S: RawDataMut { debug_assert!(self.pointer_is_inbounds()); S::try_ensure_unique(self); @@ -1615,8 +1593,7 @@ where /// /// This method is mostly only useful with unsafe code. fn ensure_unique(&mut self) - where - S: DataMut, + where S: DataMut { debug_assert!(self.pointer_is_inbounds()); S::ensure_unique(self); @@ -1628,12 +1605,14 @@ where /// /// Return `false` otherwise, i.e. the array is possibly not /// contiguous in memory, it has custom strides, etc. - pub fn is_standard_layout(&self) -> bool { + pub fn is_standard_layout(&self) -> bool + { dimension::is_layout_c(&self.dim, &self.strides) } /// Return true if the array is known to be contiguous. - pub(crate) fn is_contiguous(&self) -> bool { + pub(crate) fn is_contiguous(&self) -> bool + { D::is_contiguous(&self.dim, &self.strides) } @@ -1689,7 +1668,8 @@ where /// /// where *d* is `self.ndim()`. #[inline(always)] - pub fn as_ptr(&self) -> *const A { + pub fn as_ptr(&self) -> *const A + { self.ptr.as_ptr() as *const A } @@ -1705,8 +1685,7 @@ where /// the data may change the strides. #[inline(always)] pub fn as_mut_ptr(&mut self) -> *mut A - where - S: RawDataMut, + where S: RawDataMut { self.try_ensure_unique(); // for ArcArray self.ptr.as_ptr() @@ -1714,7 +1693,8 @@ where /// Return a raw view of the array. #[inline] - pub fn raw_view(&self) -> RawArrayView { + pub fn raw_view(&self) -> RawArrayView + { unsafe { RawArrayView::new(self.ptr, self.dim.clone(), self.strides.clone()) } } @@ -1724,8 +1704,7 @@ where /// data is guaranteed to be uniquely held on return. #[inline] pub fn raw_view_mut(&mut self) -> RawArrayViewMut - where - S: RawDataMut, + where S: RawDataMut { self.try_ensure_unique(); // for ArcArray unsafe { RawArrayViewMut::new(self.ptr, self.dim.clone(), self.strides.clone()) } @@ -1736,8 +1715,7 @@ where /// Safety: The caller must ensure that the owned array is unshared when this is called #[inline] pub(crate) unsafe fn raw_view_mut_unchecked(&mut self) -> RawArrayViewMut - where - S: DataOwned, + where S: DataOwned { RawArrayViewMut::new(self.ptr, self.dim.clone(), self.strides.clone()) } @@ -1748,8 +1726,7 @@ where /// If this function returns `Some(_)`, then the element order in the slice /// corresponds to the logical order of the array’s elements. pub fn as_slice(&self) -> Option<&[A]> - where - S: Data, + where S: Data { if self.is_standard_layout() { unsafe { Some(slice::from_raw_parts(self.ptr.as_ptr(), self.len())) } @@ -1761,8 +1738,7 @@ where /// Return the array’s data as a slice, if it is contiguous and in standard order. /// Return `None` otherwise. pub fn as_slice_mut(&mut self) -> Option<&mut [A]> - where - S: DataMut, + where S: DataMut { if self.is_standard_layout() { self.ensure_unique(); @@ -1778,17 +1754,11 @@ where /// If this function returns `Some(_)`, then the elements in the slice /// have whatever order the elements have in memory. pub fn as_slice_memory_order(&self) -> Option<&[A]> - where - S: Data, + where S: Data { if self.is_contiguous() { let offset = offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides); - unsafe { - Some(slice::from_raw_parts( - self.ptr.sub(offset).as_ptr(), - self.len(), - )) - } + unsafe { Some(slice::from_raw_parts(self.ptr.sub(offset).as_ptr(), self.len())) } } else { None } @@ -1801,8 +1771,7 @@ where /// method unshares the data if necessary, but it preserves the existing /// strides. pub fn as_slice_memory_order_mut(&mut self) -> Option<&mut [A]> - where - S: DataMut, + where S: DataMut { self.try_as_slice_memory_order_mut().ok() } @@ -1810,18 +1779,12 @@ where /// Return the array’s data as a slice if it is contiguous, otherwise /// return `self` in the `Err` variant. pub(crate) fn try_as_slice_memory_order_mut(&mut self) -> Result<&mut [A], &mut Self> - where - S: DataMut, + where S: DataMut { if self.is_contiguous() { self.ensure_unique(); let offset = offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides); - unsafe { - Ok(slice::from_raw_parts_mut( - self.ptr.sub(offset).as_ptr(), - self.len(), - )) - } + unsafe { Ok(slice::from_raw_parts_mut(self.ptr.sub(offset).as_ptr(), self.len())) } } else { Err(self) } @@ -1892,8 +1855,7 @@ where self.to_shape_order(shape, order.unwrap_or(Order::RowMajor)) } - fn to_shape_order(&self, shape: E, order: Order) - -> Result, ShapeError> + fn to_shape_order(&self, shape: E, order: Order) -> Result, ShapeError> where E: Dimension, A: Clone, @@ -1915,11 +1877,11 @@ where match reshape_dim(&self.dim, &self.strides, &shape, order) { Ok(to_strides) => unsafe { return Ok(CowArray::from(ArrayView::new(self.ptr, shape, to_strides))); - } + }, Err(err) if err.kind() == ErrorKind::IncompatibleShape => { return Err(error::incompatible_shapes(&self.dim, &shape)); } - _otherwise => { } + _otherwise => {} } // otherwise create a new array and copy the elements @@ -1928,8 +1890,7 @@ where Order::RowMajor => (shape.set_f(false), self.view()), Order::ColumnMajor => (shape.set_f(true), self.t()), }; - Ok(CowArray::from(Array::from_shape_trusted_iter_unchecked( - shape, view.into_iter(), A::clone))) + Ok(CowArray::from(Array::from_shape_trusted_iter_unchecked(shape, view.into_iter(), A::clone))) } } @@ -1979,17 +1940,14 @@ where /// ); /// ``` pub fn into_shape_with_order(self, shape: E) -> Result, ShapeError> - where - E: ShapeArg, + where E: ShapeArg { let (shape, order) = shape.into_shape_and_order(); self.into_shape_with_order_impl(shape, order.unwrap_or(Order::RowMajor)) } - fn into_shape_with_order_impl(self, shape: E, order: Order) - -> Result, ShapeError> - where - E: Dimension, + fn into_shape_with_order_impl(self, shape: E, order: Order) -> Result, ShapeError> + where E: Dimension { let shape = shape.into_dimension(); if size_of_shape_checked(&shape) != Ok(self.dim.size()) { @@ -2000,13 +1958,11 @@ where unsafe { // safe because arrays are contiguous and len is unchanged match order { - Order::RowMajor if self.is_standard_layout() => { - Ok(self.with_strides_dim(shape.default_strides(), shape)) - } - Order::ColumnMajor if self.raw_view().reversed_axes().is_standard_layout() => { - Ok(self.with_strides_dim(shape.fortran_strides(), shape)) - } - _otherwise => Err(error::from_kind(error::ErrorKind::IncompatibleLayout)) + Order::RowMajor if self.is_standard_layout() => + Ok(self.with_strides_dim(shape.default_strides(), shape)), + Order::ColumnMajor if self.raw_view().reversed_axes().is_standard_layout() => + Ok(self.with_strides_dim(shape.fortran_strides(), shape)), + _otherwise => Err(error::from_kind(error::ErrorKind::IncompatibleLayout)), } } } @@ -2036,8 +1992,7 @@ where /// ``` #[deprecated = "Use `.into_shape_with_order()` or `.to_shape()`"] pub fn into_shape(self, shape: E) -> Result, ShapeError> - where - E: IntoDimension, + where E: IntoDimension { let shape = shape.into_dimension(); if size_of_shape_checked(&shape) != Ok(self.dim.size()) { @@ -2082,8 +2037,7 @@ where self.into_shape_clone_order(shape, order) } - fn into_shape_clone_order(self, shape: E, order: Order) - -> Result, ShapeError> + fn into_shape_clone_order(self, shape: E, order: Order) -> Result, ShapeError> where S: DataOwned, A: Clone, @@ -2105,11 +2059,11 @@ where match reshape_dim(&self.dim, &self.strides, &shape, order) { Ok(to_strides) => unsafe { return Ok(self.with_strides_dim(to_strides, shape)); - } + }, Err(err) if err.kind() == ErrorKind::IncompatibleShape => { return Err(error::incompatible_shapes(&self.dim, &shape)); } - _otherwise => { } + _otherwise => {} } // otherwise, clone and allocate a new array @@ -2119,8 +2073,7 @@ where Order::ColumnMajor => (shape.set_f(true), self.t()), }; - Ok(ArrayBase::from_shape_trusted_iter_unchecked( - shape, view.into_iter(), A::clone)) + Ok(ArrayBase::from_shape_trusted_iter_unchecked(shape, view.into_iter(), A::clone)) } } @@ -2148,7 +2101,10 @@ where /// ); /// ``` #[track_caller] - #[deprecated(note="Obsolete, use `to_shape` or `into_shape_with_order` instead.", since="0.15.2")] + #[deprecated( + note = "Obsolete, use `to_shape` or `into_shape_with_order` instead.", + since = "0.15.2" + )] pub fn reshape(&self, shape: E) -> ArrayBase where S: DataShared + DataOwned, @@ -2167,9 +2123,7 @@ where if self.is_standard_layout() { let cl = self.clone(); // safe because array is contiguous and shape has equal number of elements - unsafe { - cl.with_strides_dim(shape.default_strides(), shape) - } + unsafe { cl.with_strides_dim(shape.default_strides(), shape) } } else { let v = self.iter().cloned().collect::>(); unsafe { ArrayBase::from_shape_vec_unchecked(shape, v) } @@ -2185,11 +2139,11 @@ where /// let array: ArrayD = arr2(&[[1, 2], /// [3, 4]]).into_dyn(); /// ``` - pub fn into_dyn(self) -> ArrayBase { + pub fn into_dyn(self) -> ArrayBase + { // safe because new dims equivalent unsafe { - ArrayBase::from_data_ptr(self.data, self.ptr) - .with_strides_dim(self.strides.into_dyn(), self.dim.into_dyn()) + ArrayBase::from_data_ptr(self.data, self.ptr).with_strides_dim(self.strides.into_dyn(), self.dim.into_dyn()) } } @@ -2210,17 +2164,16 @@ where /// assert!(array.into_dimensionality::().is_ok()); /// ``` pub fn into_dimensionality(self) -> Result, ShapeError> - where - D2: Dimension, + where D2: Dimension { unsafe { if D::NDIM == D2::NDIM { // safe because D == D2 let dim = unlimited_transmute::(self.dim); let strides = unlimited_transmute::(self.strides); - return Ok(ArrayBase::from_data_ptr(self.data, self.ptr) - .with_strides_dim(strides, dim)); - } else if D::NDIM.is_none() || D2::NDIM.is_none() { // one is dynamic dim + return Ok(ArrayBase::from_data_ptr(self.data, self.ptr).with_strides_dim(strides, dim)); + } else if D::NDIM.is_none() || D2::NDIM.is_none() { + // one is dynamic dim // safe because dim, strides are equivalent under a different type if let Some(dim) = D2::from_dimension(&self.dim) { if let Some(strides) = D2::from_dimension(&self.strides) { @@ -2274,7 +2227,8 @@ where /// /// **Note:** Cannot be used for mutable iterators, since repeating /// elements would create aliasing pointers. - fn upcast(to: &D, from: &E, stride: &E) -> Option { + fn upcast(to: &D, from: &E, stride: &E) -> Option + { // Make sure the product of non-zero axis lengths does not exceed // `isize::MAX`. This is the only safety check we need to perform // because all the other constraints of `ArrayBase` are guaranteed @@ -2331,28 +2285,34 @@ where /// /// Return `ShapeError` if their shapes can not be broadcast together. #[allow(clippy::type_complexity)] - pub(crate) fn broadcast_with<'a, 'b, B, S2, E>(&'a self, other: &'b ArrayBase) -> - Result<(ArrayView<'a, A, DimMaxOf>, ArrayView<'b, B, DimMaxOf>), ShapeError> + pub(crate) fn broadcast_with<'a, 'b, B, S2, E>( + &'a self, other: &'b ArrayBase, + ) -> Result<(ArrayView<'a, A, DimMaxOf>, ArrayView<'b, B, DimMaxOf>), ShapeError> where - S: Data, - S2: Data, + S: Data, + S2: Data, D: Dimension + DimMax, E: Dimension, { let shape = co_broadcast::>::Output>(&self.dim, &other.dim)?; let view1 = if shape.slice() == self.dim.slice() { - self.view().into_dimensionality::<>::Output>().unwrap() + self.view() + .into_dimensionality::<>::Output>() + .unwrap() } else if let Some(view1) = self.broadcast(shape.clone()) { view1 } else { - return Err(from_kind(ErrorKind::IncompatibleShape)) + return Err(from_kind(ErrorKind::IncompatibleShape)); }; let view2 = if shape.slice() == other.dim.slice() { - other.view().into_dimensionality::<>::Output>().unwrap() + other + .view() + .into_dimensionality::<>::Output>() + .unwrap() } else if let Some(view2) = other.broadcast(shape) { view2 } else { - return Err(from_kind(ErrorKind::IncompatibleShape)) + return Err(from_kind(ErrorKind::IncompatibleShape)); }; Ok((view1, view2)) } @@ -2374,7 +2334,8 @@ where /// ); /// ``` #[track_caller] - pub fn swap_axes(&mut self, ax: usize, bx: usize) { + pub fn swap_axes(&mut self, ax: usize, bx: usize) + { self.dim.slice_mut().swap(ax, bx); self.strides.slice_mut().swap(ax, bx); } @@ -2403,8 +2364,7 @@ where /// ``` #[track_caller] pub fn permuted_axes(self, axes: T) -> ArrayBase - where - T: IntoDimension, + where T: IntoDimension { let axes = axes.into_dimension(); // Ensure that each axis is used exactly once. @@ -2427,16 +2387,15 @@ where } } // safe because axis invariants are checked above; they are a permutation of the old - unsafe { - self.with_strides_dim(new_strides, new_dim) - } + unsafe { self.with_strides_dim(new_strides, new_dim) } } /// Transpose the array by reversing axes. /// /// Transposition reverses the order of the axes (dimensions and strides) /// while retaining the same data. - pub fn reversed_axes(mut self) -> ArrayBase { + pub fn reversed_axes(mut self) -> ArrayBase + { self.dim.slice_mut().reverse(); self.strides.slice_mut().reverse(); self @@ -2448,14 +2407,14 @@ where /// /// See also the more general methods `.reversed_axes()` and `.swap_axes()`. pub fn t(&self) -> ArrayView<'_, A, D> - where - S: Data, + where S: Data { self.view().reversed_axes() } /// Return an iterator over the length and stride of each axis. - pub fn axes(&self) -> Axes<'_, D> { + pub fn axes(&self) -> Axes<'_, D> + { axes_of(&self.dim, &self.strides) } @@ -2468,7 +2427,8 @@ where /// Return the axis with the greatest stride (by absolute value), /// preferring axes with len > 1. - pub fn max_stride_axis(&self) -> Axis { + pub fn max_stride_axis(&self) -> Axis + { self.dim.max_stride_axis(&self.strides) } @@ -2476,7 +2436,8 @@ where /// /// ***Panics*** if the axis is out of bounds. #[track_caller] - pub fn invert_axis(&mut self, axis: Axis) { + pub fn invert_axis(&mut self, axis: Axis) + { unsafe { let s = self.strides.axis(axis) as Ixs; let m = self.dim.axis(axis); @@ -2523,7 +2484,8 @@ where /// /// ***Panics*** if an axis is out of bounds. #[track_caller] - pub fn merge_axes(&mut self, take: Axis, into: Axis) -> bool { + pub fn merge_axes(&mut self, take: Axis, into: Axis) -> bool + { merge_axes(&mut self.dim, &mut self.strides, take, into) } @@ -2549,7 +2511,8 @@ where /// /// ***Panics*** if the axis is out of bounds. #[track_caller] - pub fn insert_axis(self, axis: Axis) -> ArrayBase { + pub fn insert_axis(self, axis: Axis) -> ArrayBase + { assert!(axis.index() <= self.ndim()); // safe because a new axis of length one does not affect memory layout unsafe { @@ -2567,13 +2530,13 @@ where /// **Panics** if the axis is out of bounds or its length is zero. #[track_caller] pub fn remove_axis(self, axis: Axis) -> ArrayBase - where - D: RemoveAxis, + where D: RemoveAxis { self.index_axis_move(axis, 0) } - pub(crate) fn pointer_is_inbounds(&self) -> bool { + pub(crate) fn pointer_is_inbounds(&self) -> bool + { self.data._is_pointer_inbounds(self.as_ptr()) } @@ -2606,8 +2569,7 @@ where P::Item: AssignElem, A: Clone, { - Zip::from(self) - .map_assign_into(to, A::clone); + Zip::from(self).map_assign_into(to, A::clone); } /// Perform an elementwise assigment to `self` from element `x`. @@ -2744,7 +2706,9 @@ where if let Some(slc) = self.as_slice_memory_order() { ArrayBase::from_shape_trusted_iter_unchecked( self.dim.clone().strides(self.strides.clone()), - slc.iter(), f) + slc.iter(), + f, + ) } else { ArrayBase::from_shape_trusted_iter_unchecked(self.dim.clone(), self.iter(), f) } @@ -2767,8 +2731,7 @@ where if self.is_contiguous() { let strides = self.strides.clone(); let slc = self.as_slice_memory_order_mut().unwrap(); - unsafe { ArrayBase::from_shape_trusted_iter_unchecked(dim.strides(strides), - slc.iter_mut(), f) } + unsafe { ArrayBase::from_shape_trusted_iter_unchecked(dim.strides(strides), slc.iter_mut(), f) } } else { unsafe { ArrayBase::from_shape_trusted_iter_unchecked(dim, self.iter_mut(), f) } } @@ -2921,7 +2884,7 @@ where /// on each element. /// /// Elements are visited in arbitrary order. - #[deprecated(note="Renamed to .for_each()", since="0.15.0")] + #[deprecated(note = "Renamed to .for_each()", since = "0.15.0")] pub fn visit<'a, F>(&'a self, f: F) where F: FnMut(&'a A), @@ -3016,8 +2979,7 @@ where /// ***Panics*** if `axis` is out of bounds
/// ***Panics*** if not `index < self.len_of(axis)`. pub fn remove_index(&mut self, axis: Axis, index: usize) - where - S: DataOwned + DataMut, + where S: DataOwned + DataMut { assert!(index < self.len_of(axis), "index {} must be less than length of Axis({})", index, axis.index()); @@ -3085,7 +3047,6 @@ where } } - /// Transmute from A to B. /// /// Like transmute, but does not have the compile-time size check which blocks @@ -3094,7 +3055,8 @@ where /// **Panics** if the size of A and B are different. #[track_caller] #[inline] -unsafe fn unlimited_transmute(data: A) -> B { +unsafe fn unlimited_transmute(data: A) -> B +{ // safe when sizes are equal and caller guarantees that representations are equal assert_eq!(size_of::
(), size_of::()); let old_data = ManuallyDrop::new(data); diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 8d02364d1..46ea18a7c 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -288,21 +288,25 @@ impl<'a, S, D> $trt<&'a ArrayBase> for $scalar ); } -mod arithmetic_ops { +mod arithmetic_ops +{ use super::*; use crate::imp_prelude::*; use std::ops::*; - fn clone_opf(f: impl Fn(A, B) -> C) -> impl FnMut(&A, &B) -> C { + fn clone_opf(f: impl Fn(A, B) -> C) -> impl FnMut(&A, &B) -> C + { move |x, y| f(x.clone(), y.clone()) } - fn clone_iopf(f: impl Fn(A, B) -> A) -> impl FnMut(&mut A, &B) { + fn clone_iopf(f: impl Fn(A, B) -> A) -> impl FnMut(&mut A, &B) + { move |x, y| *x = f(x.clone(), y.clone()) } - fn clone_iopf_rev(f: impl Fn(A, B) -> B) -> impl FnMut(&mut B, &A) { + fn clone_iopf_rev(f: impl Fn(A, B) -> B) -> impl FnMut(&mut B, &A) + { move |x, y| *x = f(y.clone(), x.clone()) } @@ -378,7 +382,8 @@ mod arithmetic_ops { { type Output = Self; /// Perform an elementwise negation of `self` and return the result. - fn neg(mut self) -> Self { + fn neg(mut self) -> Self + { self.map_inplace(|elt| { *elt = -elt.clone(); }); @@ -395,7 +400,8 @@ mod arithmetic_ops { type Output = Array; /// Perform an elementwise negation of reference `self` and return the /// result as a new `Array`. - fn neg(self) -> Array { + fn neg(self) -> Array + { self.map(Neg::neg) } } @@ -408,7 +414,8 @@ mod arithmetic_ops { { type Output = Self; /// Perform an elementwise unary not of `self` and return the result. - fn not(mut self) -> Self { + fn not(mut self) -> Self + { self.map_inplace(|elt| { *elt = !elt.clone(); }); @@ -425,13 +432,15 @@ mod arithmetic_ops { type Output = Array; /// Perform an elementwise unary not of reference `self` and return the /// result as a new `Array`. - fn not(self) -> Array { + fn not(self) -> Array + { self.map(Not::not) } } } -mod assign_ops { +mod assign_ops +{ use super::*; use crate::imp_prelude::*; diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 3e9001132..53be9e48c 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -1,4 +1,3 @@ - #[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::mem; @@ -18,7 +17,8 @@ use crate::Zip; /// Methods specific to `Array0`. /// /// ***See also all methods for [`ArrayBase`]*** -impl Array { +impl Array +{ /// Returns the single element in the array without cloning it. /// /// ``` @@ -32,7 +32,8 @@ impl Array { /// let scalar: Foo = array.into_scalar(); /// assert_eq!(scalar, Foo); /// ``` - pub fn into_scalar(self) -> A { + pub fn into_scalar(self) -> A + { let size = mem::size_of::(); if size == 0 { // Any index in the `Vec` is fine since all elements are identical. @@ -56,15 +57,15 @@ impl Array { /// /// ***See also all methods for [`ArrayBase`]*** impl Array -where - D: Dimension, +where D: Dimension { /// Return a vector of the elements in the array, in the way they are /// stored internally. /// /// If the array is in standard memory layout, the logical element order /// of the array (`.iter()` order) and of the returned vector will be the same. - pub fn into_raw_vec(self) -> Vec { + pub fn into_raw_vec(self) -> Vec + { self.data.into_vec() } } @@ -72,7 +73,8 @@ where /// Methods specific to `Array2`. /// /// ***See also all methods for [`ArrayBase`]*** -impl Array { +impl Array +{ /// Append a row to an array /// /// The elements from `row` are cloned and added as a new row in the array. @@ -113,8 +115,7 @@ impl Array { /// [-1., -2., -3., -4.]]); /// ``` pub fn push_row(&mut self, row: ArrayView) -> Result<(), ShapeError> - where - A: Clone, + where A: Clone { self.append(Axis(0), row.insert_axis(Axis(0))) } @@ -159,15 +160,14 @@ impl Array { /// [2., -2.]]); /// ``` pub fn push_column(&mut self, column: ArrayView) -> Result<(), ShapeError> - where - A: Clone, + where A: Clone { self.append(Axis(1), column.insert_axis(Axis(1))) } } impl Array - where D: Dimension +where D: Dimension { /// Move all elements from self into `new_array`, which must be of the same shape but /// can have a different memory layout. The destination is overwritten completely. @@ -199,18 +199,18 @@ impl Array } else { // If `A` doesn't need drop, we can overwrite the destination. // Safe because: move_into_uninit only writes initialized values - unsafe { - self.move_into_uninit(new_array.into_maybe_uninit()) - } + unsafe { self.move_into_uninit(new_array.into_maybe_uninit()) } } } - fn move_into_needs_drop(mut self, new_array: ArrayViewMut) { + fn move_into_needs_drop(mut self, new_array: ArrayViewMut) + { // Simple case where `A` has a destructor: just swap values between self and new_array. // Afterwards, `self` drops full of initialized values and dropping works as usual. // This avoids moving out of owned values in `self` while at the same time managing // the dropping if the values being overwritten in `new_array`. - Zip::from(&mut self).and(new_array) + Zip::from(&mut self) + .and(new_array) .for_each(|src, dst| mem::swap(src, dst)); } @@ -249,7 +249,8 @@ impl Array self.move_into_impl(new_array.into()) } - fn move_into_impl(mut self, new_array: ArrayViewMut, D>) { + fn move_into_impl(mut self, new_array: ArrayViewMut, D>) + { unsafe { // Safety: copy_to_nonoverlapping cannot panic let guard = AbortIfPanic(&"move_into: moving out of owned value"); @@ -274,7 +275,8 @@ impl Array /// # Safety /// /// This is a panic critical section since `self` is already moved-from. - fn drop_unreachable_elements(mut self) -> OwnedRepr { + fn drop_unreachable_elements(mut self) -> OwnedRepr + { let self_len = self.len(); // "deconstruct" self; the owned repr releases ownership of all elements and we @@ -294,7 +296,8 @@ impl Array #[inline(never)] #[cold] - fn drop_unreachable_elements_slow(mut self) -> OwnedRepr { + fn drop_unreachable_elements_slow(mut self) -> OwnedRepr + { // "deconstruct" self; the owned repr releases ownership of all elements and we // carry on with raw view methods let data_len = self.data.len(); @@ -315,7 +318,8 @@ impl Array /// Create an empty array with an all-zeros shape /// /// ***Panics*** if D is zero-dimensional, because it can't be empty - pub(crate) fn empty() -> Array { + pub(crate) fn empty() -> Array + { assert_ne!(D::NDIM, Some(0)); let ndim = D::NDIM.unwrap_or(1); Array::from_shape_simple_fn(D::zeros(ndim), || unreachable!()) @@ -323,7 +327,8 @@ impl Array /// Create new_array with the right layout for appending to `growing_axis` #[cold] - fn change_to_contig_append_layout(&mut self, growing_axis: Axis) { + fn change_to_contig_append_layout(&mut self, growing_axis: Axis) + { let ndim = self.ndim(); let mut dim = self.raw_dim(); @@ -402,8 +407,7 @@ impl Array /// [0., 0., 0., 0.], /// [1., 1., 1., 1.]]); /// ``` - pub fn push(&mut self, axis: Axis, array: ArrayView) - -> Result<(), ShapeError> + pub fn push(&mut self, axis: Axis, array: ArrayView) -> Result<(), ShapeError> where A: Clone, D: RemoveAxis, @@ -412,7 +416,6 @@ impl Array self.append(axis, array.insert_axis(axis).into_dimensionality::().unwrap()) } - /// Append an array to the array along an axis. /// /// The elements of `array` are cloned and extend the axis `axis` in the present array; @@ -463,8 +466,7 @@ impl Array /// [1., 1., 1., 1.], /// [1., 1., 1., 1.]]); /// ``` - pub fn append(&mut self, axis: Axis, mut array: ArrayView) - -> Result<(), ShapeError> + pub fn append(&mut self, axis: Axis, mut array: ArrayView) -> Result<(), ShapeError> where A: Clone, D: RemoveAxis, @@ -557,7 +559,11 @@ impl Array acc } else { let this_ax = ax.len as isize * ax.stride.abs(); - if this_ax > acc { this_ax } else { acc } + if this_ax > acc { + this_ax + } else { + acc + } } }); let mut strides = self.strides.clone(); @@ -575,7 +581,10 @@ impl Array 0 }; debug_assert!(data_to_array_offset >= 0); - self.ptr = self.data.reserve(len_to_append).offset(data_to_array_offset); + self.ptr = self + .data + .reserve(len_to_append) + .offset(data_to_array_offset); // clone elements from view to the array now // @@ -612,19 +621,22 @@ impl Array debug_assert!(tail_view.is_standard_layout(), "not std layout dim: {:?}, strides: {:?}", tail_view.shape(), tail_view.strides()); - } + } // Keep track of currently filled length of `self.data` and update it // on scope exit (panic or loop finish). This "indirect" way to // write the length is used to help the compiler, the len store to self.data may // otherwise be mistaken to alias with other stores in the loop. - struct SetLenOnDrop<'a, A: 'a> { + struct SetLenOnDrop<'a, A: 'a> + { len: usize, data: &'a mut OwnedRepr, } - impl Drop for SetLenOnDrop<'_, A> { - fn drop(&mut self) { + impl Drop for SetLenOnDrop<'_, A> + { + fn drop(&mut self) + { unsafe { self.data.set_len(self.len); } @@ -636,7 +648,6 @@ impl Array data: &mut self.data, }; - // Safety: tail_view is constructed to have the same shape as array Zip::from(tail_view) .and_unchecked(array) @@ -667,8 +678,7 @@ impl Array /// This is an internal function for use by move_into and IntoIter only, safety invariants may need /// to be upheld across the calls from those implementations. pub(crate) unsafe fn drop_unreachable_raw(mut self_: RawArrayViewMut, data_ptr: *mut A, data_len: usize) -where - D: Dimension, +where D: Dimension { let self_len = self_.len(); @@ -751,8 +761,7 @@ where } fn sort_axes1_impl(adim: &mut D, astrides: &mut D) -where - D: Dimension, +where D: Dimension { debug_assert!(adim.ndim() > 1); debug_assert_eq!(adim.ndim(), astrides.ndim()); @@ -775,7 +784,6 @@ where } } - /// Sort axes to standard order, i.e Axis(0) has biggest stride and Axis(n - 1) least stride /// /// Axes in a and b are sorted by the strides of `a`, and `a`'s axes should have stride >= 0 before @@ -793,8 +801,7 @@ where } fn sort_axes2_impl(adim: &mut D, astrides: &mut D, bdim: &mut D, bstrides: &mut D) -where - D: Dimension, +where D: Dimension { debug_assert!(adim.ndim() > 1); debug_assert_eq!(adim.ndim(), bdim.ndim()); diff --git a/src/impl_raw_views.rs b/src/impl_raw_views.rs index 9f026318c..aeee75cb2 100644 --- a/src/impl_raw_views.rs +++ b/src/impl_raw_views.rs @@ -6,23 +6,23 @@ use crate::dimension::{self, stride_offset}; use crate::extension::nonnull::nonnull_debug_checked_from_ptr; use crate::imp_prelude::*; use crate::is_aligned; -use crate::shape_builder::{Strides, StrideShape}; +use crate::shape_builder::{StrideShape, Strides}; impl RawArrayView -where - D: Dimension, +where D: Dimension { /// Create a new `RawArrayView`. /// /// Unsafe because caller is responsible for ensuring that the array will /// meet all of the invariants of the `ArrayBase` type. #[inline] - pub(crate) unsafe fn new(ptr: NonNull, dim: D, strides: D) -> Self { - RawArrayView::from_data_ptr(RawViewRepr::new(), ptr) - .with_strides_dim(strides, dim) + pub(crate) unsafe fn new(ptr: NonNull, dim: D, strides: D) -> Self + { + RawArrayView::from_data_ptr(RawViewRepr::new(), ptr).with_strides_dim(strides, dim) } - unsafe fn new_(ptr: *const A, dim: D, strides: D) -> Self { + unsafe fn new_(ptr: *const A, dim: D, strides: D) -> Self + { Self::new(nonnull_debug_checked_from_ptr(ptr as *mut A), dim, strides) } @@ -59,7 +59,7 @@ where /// [`.offset()`] regardless of the starting point due to past offsets. /// /// * The product of non-zero axis lengths must not exceed `isize::MAX`. - /// + /// /// * Strides must be non-negative. /// /// This function can use debug assertions to check some of these requirements, @@ -67,8 +67,7 @@ where /// /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset pub unsafe fn from_shape_ptr(shape: Sh, ptr: *const A) -> Self - where - Sh: Into>, + where Sh: Into> { let shape = shape.into(); let dim = shape.dim; @@ -94,7 +93,8 @@ where /// data is valid, ensure that the pointer is aligned, and choose the /// correct lifetime. #[inline] - pub unsafe fn deref_into_view<'a>(self) -> ArrayView<'a, A, D> { + pub unsafe fn deref_into_view<'a>(self) -> ArrayView<'a, A, D> + { debug_assert!( is_aligned(self.ptr.as_ptr()), "The pointer must be aligned." @@ -107,7 +107,8 @@ where /// /// **Panics** if `axis` or `index` is out of bounds. #[track_caller] - pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { + pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) + { assert!(index <= self.len_of(axis)); let left_ptr = self.ptr.as_ptr(); let right_ptr = if index == self.len_of(axis) { @@ -141,7 +142,8 @@ where /// casts are safe, access through the produced raw view is only possible /// in an unsafe block or function. #[track_caller] - pub fn cast(self) -> RawArrayView { + pub fn cast(self) -> RawArrayView + { assert_eq!( mem::size_of::(), mem::size_of::(), @@ -153,12 +155,12 @@ where } impl RawArrayView, D> -where - D: Dimension, +where D: Dimension { /// Splits the view into views of the real and imaginary components of the /// elements. - pub fn split_complex(self) -> Complex> { + pub fn split_complex(self) -> Complex> + { // Check that the size and alignment of `Complex` are as expected. // These assertions should always pass, for arbitrary `T`. assert_eq!( @@ -220,20 +222,20 @@ where } impl RawArrayViewMut -where - D: Dimension, +where D: Dimension { /// Create a new `RawArrayViewMut`. /// /// Unsafe because caller is responsible for ensuring that the array will /// meet all of the invariants of the `ArrayBase` type. #[inline] - pub(crate) unsafe fn new(ptr: NonNull, dim: D, strides: D) -> Self { - RawArrayViewMut::from_data_ptr(RawViewRepr::new(), ptr) - .with_strides_dim(strides, dim) + pub(crate) unsafe fn new(ptr: NonNull, dim: D, strides: D) -> Self + { + RawArrayViewMut::from_data_ptr(RawViewRepr::new(), ptr).with_strides_dim(strides, dim) } - unsafe fn new_(ptr: *mut A, dim: D, strides: D) -> Self { + unsafe fn new_(ptr: *mut A, dim: D, strides: D) -> Self + { Self::new(nonnull_debug_checked_from_ptr(ptr), dim, strides) } @@ -268,7 +270,7 @@ where /// address by moving along all axes must not exceed `isize::MAX`. This /// constraint prevents overflow when calculating the `count` parameter to /// [`.offset()`] regardless of the starting point due to past offsets. - /// + /// /// * The product of non-zero axis lengths must not exceed `isize::MAX`. /// /// * Strides must be non-negative. @@ -278,8 +280,7 @@ where /// /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset pub unsafe fn from_shape_ptr(shape: Sh, ptr: *mut A) -> Self - where - Sh: Into>, + where Sh: Into> { let shape = shape.into(); let dim = shape.dim; @@ -298,7 +299,8 @@ where /// Converts to a non-mutable `RawArrayView`. #[inline] - pub(crate) fn into_raw_view(self) -> RawArrayView { + pub(crate) fn into_raw_view(self) -> RawArrayView + { unsafe { RawArrayView::new(self.ptr, self.dim, self.strides) } } @@ -311,7 +313,8 @@ where /// data is valid, ensure that the pointer is aligned, and choose the /// correct lifetime. #[inline] - pub unsafe fn deref_into_view<'a>(self) -> ArrayView<'a, A, D> { + pub unsafe fn deref_into_view<'a>(self) -> ArrayView<'a, A, D> + { debug_assert!( is_aligned(self.ptr.as_ptr()), "The pointer must be aligned." @@ -328,7 +331,8 @@ where /// data is valid, ensure that the pointer is aligned, and choose the /// correct lifetime. #[inline] - pub unsafe fn deref_into_view_mut<'a>(self) -> ArrayViewMut<'a, A, D> { + pub unsafe fn deref_into_view_mut<'a>(self) -> ArrayViewMut<'a, A, D> + { debug_assert!( is_aligned(self.ptr.as_ptr()), "The pointer must be aligned." @@ -341,14 +345,10 @@ where /// /// **Panics** if `axis` or `index` is out of bounds. #[track_caller] - pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { + pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) + { let (left, right) = self.into_raw_view().split_at(axis, index); - unsafe { - ( - Self::new(left.ptr, left.dim, left.strides), - Self::new(right.ptr, right.dim, right.strides), - ) - } + unsafe { (Self::new(left.ptr, left.dim, left.strides), Self::new(right.ptr, right.dim, right.strides)) } } /// Cast the raw pointer of the raw array view to a different type @@ -362,7 +362,8 @@ where /// casts are safe, access through the produced raw view is only possible /// in an unsafe block or function. #[track_caller] - pub fn cast(self) -> RawArrayViewMut { + pub fn cast(self) -> RawArrayViewMut + { assert_eq!( mem::size_of::(), mem::size_of::(), @@ -374,12 +375,12 @@ where } impl RawArrayViewMut, D> -where - D: Dimension, +where D: Dimension { /// Splits the view into views of the real and imaginary components of the /// elements. - pub fn split_complex(self) -> Complex> { + pub fn split_complex(self) -> Complex> + { let Complex { re, im } = self.into_raw_view().split_complex(); unsafe { Complex { diff --git a/src/impl_special_element_types.rs b/src/impl_special_element_types.rs index 5d5f18491..e430b20bc 100644 --- a/src/impl_special_element_types.rs +++ b/src/impl_special_element_types.rs @@ -11,13 +11,12 @@ use std::mem::MaybeUninit; use crate::imp_prelude::*; use crate::RawDataSubst; - /// Methods specific to arrays with `MaybeUninit` elements. /// /// ***See also all methods for [`ArrayBase`]*** impl ArrayBase where - S: RawDataSubst>, + S: RawDataSubst>, D: Dimension, { /// **Promise** that the array's elements are all fully initialized, and convert @@ -32,8 +31,14 @@ where /// Note that for owned and shared ownership arrays, the promise must include all of the /// array's storage; it is for example possible to slice these in place, but that must /// only be done after all elements have been initialized. - pub unsafe fn assume_init(self) -> ArrayBase<>::Output, D> { - let ArrayBase { data, ptr, dim, strides } = self; + pub unsafe fn assume_init(self) -> ArrayBase<>::Output, D> + { + let ArrayBase { + data, + ptr, + dim, + strides, + } = self; // "transmute" from storage of MaybeUninit to storage of A let data = S::data_subst(data); diff --git a/src/impl_views/constructors.rs b/src/impl_views/constructors.rs index 98cb81fcc..33c7b15be 100644 --- a/src/impl_views/constructors.rs +++ b/src/impl_views/constructors.rs @@ -9,16 +9,15 @@ use std::ptr::NonNull; use crate::dimension; +use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr; use crate::error::ShapeError; use crate::extension::nonnull::nonnull_debug_checked_from_ptr; use crate::imp_prelude::*; use crate::{is_aligned, StrideShape}; -use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr; /// Methods for read-only array views. impl<'a, A, D> ArrayView<'a, A, D> -where - D: Dimension, +where D: Dimension { /// Create a read-only array view borrowing its data from a slice. /// @@ -46,18 +45,25 @@ where /// assert!(a.strides() == &[1, 4, 2]); /// ``` pub fn from_shape(shape: Sh, xs: &'a [A]) -> Result - where - Sh: Into>, + where Sh: Into> { // eliminate the type parameter Sh as soon as possible Self::from_shape_impl(shape.into(), xs) } - fn from_shape_impl(shape: StrideShape, xs: &'a [A]) -> Result { + fn from_shape_impl(shape: StrideShape, xs: &'a [A]) -> Result + { let dim = shape.dim; dimension::can_index_slice_with_strides(xs, &dim, &shape.strides)?; let strides = shape.strides.strides_for_dim(&dim); - unsafe { Ok(Self::new_(xs.as_ptr().add(offset_from_low_addr_ptr_to_logical_ptr(&dim, &strides)), dim, strides)) } + unsafe { + Ok(Self::new_( + xs.as_ptr() + .add(offset_from_low_addr_ptr_to_logical_ptr(&dim, &strides)), + dim, + strides, + )) + } } /// Create an `ArrayView` from shape information and a raw pointer to @@ -105,8 +111,7 @@ where /// /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset pub unsafe fn from_shape_ptr(shape: Sh, ptr: *const A) -> Self - where - Sh: Into>, + where Sh: Into> { RawArrayView::from_shape_ptr(shape, ptr).deref_into_view() } @@ -114,8 +119,7 @@ where /// Methods for read-write array views. impl<'a, A, D> ArrayViewMut<'a, A, D> -where - D: Dimension, +where D: Dimension { /// Create a read-write array view borrowing its data from a slice. /// @@ -143,18 +147,25 @@ where /// assert!(a.strides() == &[1, 4, 2]); /// ``` pub fn from_shape(shape: Sh, xs: &'a mut [A]) -> Result - where - Sh: Into>, + where Sh: Into> { // eliminate the type parameter Sh as soon as possible Self::from_shape_impl(shape.into(), xs) } - fn from_shape_impl(shape: StrideShape, xs: &'a mut [A]) -> Result { + fn from_shape_impl(shape: StrideShape, xs: &'a mut [A]) -> Result + { let dim = shape.dim; dimension::can_index_slice_with_strides(xs, &dim, &shape.strides)?; let strides = shape.strides.strides_for_dim(&dim); - unsafe { Ok(Self::new_(xs.as_mut_ptr().add(offset_from_low_addr_ptr_to_logical_ptr(&dim, &strides)), dim, strides)) } + unsafe { + Ok(Self::new_( + xs.as_mut_ptr() + .add(offset_from_low_addr_ptr_to_logical_ptr(&dim, &strides)), + dim, + strides, + )) + } } /// Create an `ArrayViewMut` from shape information and a @@ -202,8 +213,7 @@ where /// /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset pub unsafe fn from_shape_ptr(shape: Sh, ptr: *mut A) -> Self - where - Sh: Into>, + where Sh: Into> { RawArrayViewMut::from_shape_ptr(shape, ptr).deref_into_view_mut() } @@ -211,8 +221,7 @@ where /// Convert the view into an `ArrayViewMut<'b, A, D>` where `'b` is a lifetime /// outlived by `'a'`. pub fn reborrow<'b>(self) -> ArrayViewMut<'b, A, D> - where - 'a: 'b, + where 'a: 'b { unsafe { ArrayViewMut::new(self.ptr, self.dim, self.strides) } } @@ -220,14 +229,14 @@ where /// Private array view methods impl<'a, A, D> ArrayView<'a, A, D> -where - D: Dimension, +where D: Dimension { /// Create a new `ArrayView` /// /// Unsafe because: `ptr` must be valid for the given dimension and strides. #[inline(always)] - pub(crate) unsafe fn new(ptr: NonNull, dim: D, strides: D) -> Self { + pub(crate) unsafe fn new(ptr: NonNull, dim: D, strides: D) -> Self + { if cfg!(debug_assertions) { assert!(is_aligned(ptr.as_ptr()), "The pointer must be aligned."); dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); @@ -237,20 +246,21 @@ where /// Unsafe because: `ptr` must be valid for the given dimension and strides. #[inline] - pub(crate) unsafe fn new_(ptr: *const A, dim: D, strides: D) -> Self { + pub(crate) unsafe fn new_(ptr: *const A, dim: D, strides: D) -> Self + { Self::new(nonnull_debug_checked_from_ptr(ptr as *mut A), dim, strides) } } impl<'a, A, D> ArrayViewMut<'a, A, D> -where - D: Dimension, +where D: Dimension { /// Create a new `ArrayView` /// /// Unsafe because: `ptr` must be valid for the given dimension and strides. #[inline(always)] - pub(crate) unsafe fn new(ptr: NonNull, dim: D, strides: D) -> Self { + pub(crate) unsafe fn new(ptr: NonNull, dim: D, strides: D) -> Self + { if cfg!(debug_assertions) { assert!(is_aligned(ptr.as_ptr()), "The pointer must be aligned."); dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); @@ -262,7 +272,8 @@ where /// /// Unsafe because: `ptr` must be valid for the given dimension and strides. #[inline(always)] - pub(crate) unsafe fn new_(ptr: *mut A, dim: D, strides: D) -> Self { + pub(crate) unsafe fn new_(ptr: *mut A, dim: D, strides: D) -> Self + { Self::new(nonnull_debug_checked_from_ptr(ptr), dim, strides) } } diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs index ca571a761..f545ebdd0 100644 --- a/src/impl_views/conversions.rs +++ b/src/impl_views/conversions.rs @@ -21,14 +21,12 @@ use crate::IndexLonger; /// Methods for read-only array views. impl<'a, A, D> ArrayView<'a, A, D> -where - D: Dimension, +where D: Dimension { /// Convert the view into an `ArrayView<'b, A, D>` where `'b` is a lifetime /// outlived by `'a'`. pub fn reborrow<'b>(self) -> ArrayView<'b, A, D> - where - 'a: 'b, + where 'a: 'b { unsafe { ArrayView::new(self.ptr, self.dim, self.strides) } } @@ -38,7 +36,8 @@ where /// /// Note that while the method is similar to [`ArrayBase::as_slice()`], this method transfers /// the view's lifetime to the slice, so it is a bit more powerful. - pub fn to_slice(&self) -> Option<&'a [A]> { + pub fn to_slice(&self) -> Option<&'a [A]> + { if self.is_standard_layout() { unsafe { Some(slice::from_raw_parts(self.ptr.as_ptr(), self.len())) } } else { @@ -52,22 +51,19 @@ where /// Note that while the method is similar to /// [`ArrayBase::as_slice_memory_order()`], this method transfers the view's /// lifetime to the slice, so it is a bit more powerful. - pub fn to_slice_memory_order(&self) -> Option<&'a [A]> { + pub fn to_slice_memory_order(&self) -> Option<&'a [A]> + { if self.is_contiguous() { let offset = offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides); - unsafe { - Some(slice::from_raw_parts( - self.ptr.sub(offset).as_ptr(), - self.len(), - )) - } + unsafe { Some(slice::from_raw_parts(self.ptr.sub(offset).as_ptr(), self.len())) } } else { None } } /// Converts to a raw array view. - pub(crate) fn into_raw_view(self) -> RawArrayView { + pub(crate) fn into_raw_view(self) -> RawArrayView + { unsafe { RawArrayView::new(self.ptr, self.dim, self.strides) } } } @@ -75,7 +71,8 @@ where /// Methods specific to `ArrayView0`. /// /// ***See also all methods for [`ArrayView`] and [`ArrayBase`]*** -impl<'a, A> ArrayView<'a, A, Ix0> { +impl<'a, A> ArrayView<'a, A, Ix0> +{ /// Consume the view and return a reference to the single element in the array. /// /// The lifetime of the returned reference matches the lifetime of the data @@ -93,7 +90,8 @@ impl<'a, A> ArrayView<'a, A, Ix0> { /// let scalar: &Foo = view.into_scalar(); /// assert_eq!(scalar, &Foo); /// ``` - pub fn into_scalar(self) -> &'a A { + pub fn into_scalar(self) -> &'a A + { self.index(Ix0()) } } @@ -101,7 +99,8 @@ impl<'a, A> ArrayView<'a, A, Ix0> { /// Methods specific to `ArrayViewMut0`. /// /// ***See also all methods for [`ArrayViewMut`] and [`ArrayBase`]*** -impl<'a, A> ArrayViewMut<'a, A, Ix0> { +impl<'a, A> ArrayViewMut<'a, A, Ix0> +{ /// Consume the mutable view and return a mutable reference to the single element in the array. /// /// The lifetime of the returned reference matches the lifetime of the data @@ -117,22 +116,23 @@ impl<'a, A> ArrayViewMut<'a, A, Ix0> { /// assert_eq!(scalar, &7.); /// assert_eq!(array[()], 7.); /// ``` - pub fn into_scalar(self) -> &'a mut A { + pub fn into_scalar(self) -> &'a mut A + { self.index(Ix0()) } } /// Methods for read-write array views. impl<'a, A, D> ArrayViewMut<'a, A, D> -where - D: Dimension, +where D: Dimension { /// Return the array’s data as a slice, if it is contiguous and in standard order. /// Return `None` otherwise. /// /// Note that while this is similar to [`ArrayBase::as_slice_mut()`], this method transfers the /// view's lifetime to the slice. - pub fn into_slice(self) -> Option<&'a mut [A]> { + pub fn into_slice(self) -> Option<&'a mut [A]> + { self.try_into_slice().ok() } @@ -142,7 +142,8 @@ where /// Note that while this is similar to /// [`ArrayBase::as_slice_memory_order_mut()`], this method transfers the /// view's lifetime to the slice. - pub fn into_slice_memory_order(self) -> Option<&'a mut [A]> { + pub fn into_slice_memory_order(self) -> Option<&'a mut [A]> + { self.try_into_slice_memory_order().ok() } @@ -152,12 +153,15 @@ where /// /// The view acts "as if" the elements are temporarily in cells, and elements /// can be changed through shared references using the regular cell methods. - pub fn into_cell_view(self) -> ArrayView<'a, MathCell, D> { + pub fn into_cell_view(self) -> ArrayView<'a, MathCell, D> + { // safety: valid because // A and MathCell have the same representation // &'a mut T is interchangeable with &'a Cell -- see method Cell::from_mut in std unsafe { - self.into_raw_view_mut().cast::>().deref_into_view() + self.into_raw_view_mut() + .cast::>() + .deref_into_view() } } @@ -174,52 +178,57 @@ where /// This method allows writing uninitialized data into the view, which could leave any /// original array that we borrow from in an inconsistent state. This is not allowed /// when using the resulting array view. - pub(crate) unsafe fn into_maybe_uninit(self) -> ArrayViewMut<'a, MaybeUninit, D> { + pub(crate) unsafe fn into_maybe_uninit(self) -> ArrayViewMut<'a, MaybeUninit, D> + { // Safe because: A and MaybeUninit have the same representation; // and we can go from initialized to (maybe) not unconditionally in terms of // representation. However, the user must be careful to not write uninit elements // through the view. - self.into_raw_view_mut().cast::>().deref_into_view_mut() + self.into_raw_view_mut() + .cast::>() + .deref_into_view_mut() } } /// Private raw array view methods impl RawArrayView -where - D: Dimension, +where D: Dimension { #[inline] - pub(crate) fn into_base_iter(self) -> Baseiter { + pub(crate) fn into_base_iter(self) -> Baseiter + { unsafe { Baseiter::new(self.ptr.as_ptr(), self.dim, self.strides) } } } impl RawArrayViewMut -where - D: Dimension, +where D: Dimension { #[inline] - pub(crate) fn into_base_iter(self) -> Baseiter { + pub(crate) fn into_base_iter(self) -> Baseiter + { unsafe { Baseiter::new(self.ptr.as_ptr(), self.dim, self.strides) } } } /// Private array view methods impl<'a, A, D> ArrayView<'a, A, D> -where - D: Dimension, +where D: Dimension { #[inline] - pub(crate) fn into_base_iter(self) -> Baseiter { + pub(crate) fn into_base_iter(self) -> Baseiter + { unsafe { Baseiter::new(self.ptr.as_ptr(), self.dim, self.strides) } } #[inline] - pub(crate) fn into_elements_base(self) -> ElementsBase<'a, A, D> { + pub(crate) fn into_elements_base(self) -> ElementsBase<'a, A, D> + { ElementsBase::new(self) } - pub(crate) fn into_iter_(self) -> Iter<'a, A, D> { + pub(crate) fn into_iter_(self) -> Iter<'a, A, D> + { Iter::new(self) } @@ -227,40 +236,43 @@ where #[doc(hidden)] // not official #[deprecated(note = "This method will be replaced.")] pub fn into_outer_iter(self) -> iter::AxisIter<'a, A, D::Smaller> - where - D: RemoveAxis, + where D: RemoveAxis { AxisIter::new(self, Axis(0)) } } impl<'a, A, D> ArrayViewMut<'a, A, D> -where - D: Dimension, +where D: Dimension { // Convert into a read-only view - pub(crate) fn into_view(self) -> ArrayView<'a, A, D> { + pub(crate) fn into_view(self) -> ArrayView<'a, A, D> + { unsafe { ArrayView::new(self.ptr, self.dim, self.strides) } } /// Converts to a mutable raw array view. - pub(crate) fn into_raw_view_mut(self) -> RawArrayViewMut { + pub(crate) fn into_raw_view_mut(self) -> RawArrayViewMut + { unsafe { RawArrayViewMut::new(self.ptr, self.dim, self.strides) } } #[inline] - pub(crate) fn into_base_iter(self) -> Baseiter { + pub(crate) fn into_base_iter(self) -> Baseiter + { unsafe { Baseiter::new(self.ptr.as_ptr(), self.dim, self.strides) } } #[inline] - pub(crate) fn into_elements_base(self) -> ElementsBaseMut<'a, A, D> { + pub(crate) fn into_elements_base(self) -> ElementsBaseMut<'a, A, D> + { ElementsBaseMut::new(self) } /// Return the array’s data as a slice, if it is contiguous and in standard order. /// Otherwise return self in the Err branch of the result. - pub(crate) fn try_into_slice(self) -> Result<&'a mut [A], Self> { + pub(crate) fn try_into_slice(self) -> Result<&'a mut [A], Self> + { if self.is_standard_layout() { unsafe { Ok(slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len())) } } else { @@ -270,21 +282,18 @@ where /// Return the array’s data as a slice, if it is contiguous. /// Otherwise return self in the Err branch of the result. - fn try_into_slice_memory_order(self) -> Result<&'a mut [A], Self> { + fn try_into_slice_memory_order(self) -> Result<&'a mut [A], Self> + { if self.is_contiguous() { let offset = offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides); - unsafe { - Ok(slice::from_raw_parts_mut( - self.ptr.sub(offset).as_ptr(), - self.len(), - )) - } + unsafe { Ok(slice::from_raw_parts_mut(self.ptr.sub(offset).as_ptr(), self.len())) } } else { Err(self) } } - pub(crate) fn into_iter_(self) -> IterMut<'a, A, D> { + pub(crate) fn into_iter_(self) -> IterMut<'a, A, D> + { IterMut::new(self) } @@ -292,8 +301,7 @@ where #[doc(hidden)] // not official #[deprecated(note = "This method will be replaced.")] pub fn into_outer_iter(self) -> iter::AxisIterMut<'a, A, D::Smaller> - where - D: RemoveAxis, + where D: RemoveAxis { AxisIterMut::new(self, Axis(0)) } diff --git a/src/impl_views/indexing.rs b/src/impl_views/indexing.rs index 3494b91db..2b72c2142 100644 --- a/src/impl_views/indexing.rs +++ b/src/impl_views/indexing.rs @@ -46,7 +46,8 @@ use crate::NdIndex; /// assert_eq!(long_life_ref, &0.); /// /// ``` -pub trait IndexLonger { +pub trait IndexLonger +{ /// The type of the reference to the element that is produced, including /// its lifetime. type Output; @@ -119,12 +120,14 @@ where /// /// **Panics** if index is out of bounds. #[track_caller] - fn index(self, index: I) -> &'a A { + fn index(self, index: I) -> &'a A + { debug_bounds_check!(self, index); unsafe { &*self.get_ptr(index).unwrap_or_else(|| array_out_of_bounds()) } } - fn get(self, index: I) -> Option<&'a A> { + fn get(self, index: I) -> Option<&'a A> + { unsafe { self.get_ptr(index).map(|ptr| &*ptr) } } @@ -139,7 +142,8 @@ where /// [1]: ArrayBase::uget /// /// **Note:** only unchecked for non-debug builds of ndarray. - unsafe fn uget(self, index: I) -> &'a A { + unsafe fn uget(self, index: I) -> &'a A + { debug_bounds_check!(self, index); &*self.as_ptr().offset(index.index_unchecked(&self.strides)) } @@ -165,7 +169,8 @@ where /// /// **Panics** if index is out of bounds. #[track_caller] - fn index(mut self, index: I) -> &'a mut A { + fn index(mut self, index: I) -> &'a mut A + { debug_bounds_check!(self, index); unsafe { match self.get_mut_ptr(index) { @@ -183,7 +188,8 @@ where /// /// [1]: ArrayBase::get_mut /// - fn get(mut self, index: I) -> Option<&'a mut A> { + fn get(mut self, index: I) -> Option<&'a mut A> + { debug_bounds_check!(self, index); unsafe { match self.get_mut_ptr(index) { @@ -202,7 +208,8 @@ where /// [1]: ArrayBase::uget_mut /// /// **Note:** only unchecked for non-debug builds of ndarray. - unsafe fn uget(mut self, index: I) -> &'a mut A { + unsafe fn uget(mut self, index: I) -> &'a mut A + { debug_bounds_check!(self, index); &mut *self .as_mut_ptr() diff --git a/src/impl_views/splitting.rs b/src/impl_views/splitting.rs index 2ccc3ee91..e26900984 100644 --- a/src/impl_views/splitting.rs +++ b/src/impl_views/splitting.rs @@ -12,8 +12,7 @@ use num_complex::Complex; /// Methods for read-only array views. impl<'a, A, D> ArrayView<'a, A, D> -where - D: Dimension, +where D: Dimension { /// Split the array view along `axis` and return one view strictly before the /// split and one view after the split. @@ -89,7 +88,8 @@ where /// along Axis(1) /// ``` #[track_caller] - pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { + pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) + { unsafe { let (left, right) = self.into_raw_view().split_at(axis, index); (left.deref_into_view(), right.deref_into_view()) @@ -98,8 +98,7 @@ where } impl<'a, T, D> ArrayView<'a, Complex, D> -where - D: Dimension, +where D: Dimension { /// Splits the view into views of the real and imaginary components of the /// elements. @@ -117,7 +116,8 @@ where /// assert_eq!(re, array![[1., 3.], [5., 7.], [9., 11.]]); /// assert_eq!(im, array![[2., 4.], [6., 8.], [10., 12.]]); /// ``` - pub fn split_complex(self) -> Complex> { + pub fn split_complex(self) -> Complex> + { unsafe { let Complex { re, im } = self.into_raw_view().split_complex(); Complex { @@ -130,15 +130,15 @@ where /// Methods for read-write array views. impl<'a, A, D> ArrayViewMut<'a, A, D> -where - D: Dimension, +where D: Dimension { /// Split the array view along `axis` and return one mutable view strictly /// before the split and one mutable view after the split. /// /// **Panics** if `axis` or `index` is out of bounds. #[track_caller] - pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { + pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) + { unsafe { let (left, right) = self.into_raw_view_mut().split_at(axis, index); (left.deref_into_view_mut(), right.deref_into_view_mut()) @@ -164,16 +164,14 @@ where /// * if `D` is `IxDyn` and `info` does not match the number of array axes #[track_caller] pub fn multi_slice_move(self, info: M) -> M::Output - where - M: MultiSliceArg<'a, A, D>, + where M: MultiSliceArg<'a, A, D> { info.multi_slice_move(self) } } impl<'a, T, D> ArrayViewMut<'a, Complex, D> -where - D: Dimension, +where D: Dimension { /// Splits the view into views of the real and imaginary components of the /// elements. @@ -198,7 +196,8 @@ where /// assert_eq!(arr[[0, 1]], Complex64::new(13., 4.)); /// assert_eq!(arr[[2, 0]], Complex64::new(9., 14.)); /// ``` - pub fn split_complex(self) -> Complex> { + pub fn split_complex(self) -> Complex> + { unsafe { let Complex { re, im } = self.into_raw_view_mut().split_complex(); Complex { diff --git a/src/indexes.rs b/src/indexes.rs index 541570184..0fa2b50fb 100644 --- a/src/indexes.rs +++ b/src/indexes.rs @@ -7,8 +7,8 @@ // except according to those terms. use super::Dimension; use crate::dimension::IntoDimension; -use crate::zip::Offset; use crate::split_at::SplitAt; +use crate::zip::Offset; use crate::Axis; use crate::Layout; use crate::NdProducer; @@ -18,7 +18,8 @@ use crate::{ArrayBase, Data}; /// /// Iterator element type is `D`. #[derive(Clone)] -pub struct IndicesIter { +pub struct IndicesIter +{ dim: D, index: Option, } @@ -28,8 +29,7 @@ pub struct IndicesIter { /// *Note:* prefer higher order methods, arithmetic operations and /// non-indexed iteration before using indices. pub fn indices(shape: E) -> Indices -where - E: IntoDimension, +where E: IntoDimension { let dim = shape.into_dimension(); Indices { @@ -51,12 +51,12 @@ where } impl Iterator for IndicesIter -where - D: Dimension, +where D: Dimension { type Item = D::Pattern; #[inline] - fn next(&mut self) -> Option { + fn next(&mut self) -> Option + { let index = match self.index { None => return None, Some(ref ix) => ix.clone(), @@ -65,7 +65,8 @@ where Some(index.into_pattern()) } - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { let l = match self.index { None => 0, Some(ref ix) => { @@ -83,8 +84,7 @@ where } fn fold(self, init: B, mut f: F) -> B - where - F: FnMut(B, D::Pattern) -> B, + where F: FnMut(B, D::Pattern) -> B { let IndicesIter { mut index, dim } = self; let ndim = dim.ndim(); @@ -112,18 +112,15 @@ where impl ExactSizeIterator for IndicesIter where D: Dimension {} impl IntoIterator for Indices -where - D: Dimension, +where D: Dimension { type Item = D::Pattern; type IntoIter = IndicesIter; - fn into_iter(self) -> Self::IntoIter { + fn into_iter(self) -> Self::IntoIter + { let sz = self.dim.size(); let index = if sz != 0 { Some(self.start) } else { None }; - IndicesIter { - index, - dim: self.dim, - } + IndicesIter { index, dim: self.dim } } } @@ -132,26 +129,26 @@ where /// `Indices` is an `NdProducer` that produces the indices of an array shape. #[derive(Copy, Clone, Debug)] pub struct Indices -where - D: Dimension, +where D: Dimension { start: D, dim: D, } #[derive(Copy, Clone, Debug)] -pub struct IndexPtr { +pub struct IndexPtr +{ index: D, } impl Offset for IndexPtr -where - D: Dimension + Copy, +where D: Dimension + Copy { // stride: The axis to increment type Stride = usize; - unsafe fn stride_offset(mut self, stride: Self::Stride, index: usize) -> Self { + unsafe fn stride_offset(mut self, stride: Self::Stride, index: usize) -> Self + { self.index[stride] += index; self } @@ -172,7 +169,8 @@ where // [0, 0, 0].stride_offset(1, 10) => [0, 10, 0] axis 1 is incremented by 10. // // .as_ref() converts the Ptr value to an Item. For example [0, 10, 0] => (0, 10, 0) -impl NdProducer for Indices { +impl NdProducer for Indices +{ type Item = D::Pattern; type Dim = D; type Ptr = IndexPtr; @@ -180,19 +178,23 @@ impl NdProducer for Indices { private_impl! {} - fn raw_dim(&self) -> Self::Dim { + fn raw_dim(&self) -> Self::Dim + { self.dim } - fn equal_dim(&self, dim: &Self::Dim) -> bool { + fn equal_dim(&self, dim: &Self::Dim) -> bool + { self.dim.equal(dim) } - fn as_ptr(&self) -> Self::Ptr { + fn as_ptr(&self) -> Self::Ptr + { IndexPtr { index: self.start } } - fn layout(&self) -> Layout { + fn layout(&self) -> Layout + { if self.dim.ndim() <= 1 { Layout::one_dimensional() } else { @@ -200,40 +202,36 @@ impl NdProducer for Indices { } } - unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { + unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item + { ptr.index.into_pattern() } - unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { + unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr + { let mut index = *i; index += &self.start; IndexPtr { index } } - fn stride_of(&self, axis: Axis) -> Self::Stride { + fn stride_of(&self, axis: Axis) -> Self::Stride + { axis.index() } #[inline(always)] - fn contiguous_stride(&self) -> Self::Stride { + fn contiguous_stride(&self) -> Self::Stride + { 0 } - fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) + { let start_a = self.start; let mut start_b = start_a; let (a, b) = self.dim.split_at(axis, index); start_b[axis.index()] += index; - ( - Indices { - start: start_a, - dim: a, - }, - Indices { - start: start_b, - dim: b, - }, - ) + (Indices { start: start_a, dim: a }, Indices { start: start_b, dim: b }) } } @@ -241,15 +239,15 @@ impl NdProducer for Indices { /// /// Iterator element type is `D`. #[derive(Clone)] -pub struct IndicesIterF { +pub struct IndicesIterF +{ dim: D, index: D, has_remaining: bool, } pub fn indices_iter_f(shape: E) -> IndicesIterF -where - E: IntoDimension, +where E: IntoDimension { let dim = shape.into_dimension(); let zero = E::Dim::zeros(dim.ndim()); @@ -261,12 +259,12 @@ where } impl Iterator for IndicesIterF -where - D: Dimension, +where D: Dimension { type Item = D::Pattern; #[inline] - fn next(&mut self) -> Option { + fn next(&mut self) -> Option + { if !self.has_remaining { None } else { @@ -276,7 +274,8 @@ where } } - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { if !self.has_remaining { return (0, Some(0)); } @@ -295,12 +294,14 @@ where impl ExactSizeIterator for IndicesIterF where D: Dimension {} #[cfg(test)] -mod tests { +mod tests +{ use super::indices; use super::indices_iter_f; #[test] - fn test_indices_iter_c_size_hint() { + fn test_indices_iter_c_size_hint() + { let dim = (3, 4); let mut it = indices(dim).into_iter(); let mut len = dim.0 * dim.1; @@ -313,7 +314,8 @@ mod tests { } #[test] - fn test_indices_iter_c_fold() { + fn test_indices_iter_c_fold() + { macro_rules! run_test { ($dim:expr) => { for num_consume in 0..3 { @@ -341,7 +343,8 @@ mod tests { } #[test] - fn test_indices_iter_f_size_hint() { + fn test_indices_iter_f_size_hint() + { let dim = (3, 4); let mut it = indices_iter_f(dim); let mut len = dim.0 * dim.1; diff --git a/src/iterators/chunks.rs b/src/iterators/chunks.rs index 9cf06b55f..465428968 100644 --- a/src/iterators/chunks.rs +++ b/src/iterators/chunks.rs @@ -30,20 +30,21 @@ impl_ndproducer! { /// See [`.exact_chunks()`](ArrayBase::exact_chunks) for more /// information. //#[derive(Debug)] -pub struct ExactChunks<'a, A, D> { +pub struct ExactChunks<'a, A, D> +{ base: RawArrayView, life: PhantomData<&'a A>, chunk: D, inner_strides: D, } -impl<'a, A, D: Dimension> ExactChunks<'a, A, D> { +impl<'a, A, D: Dimension> ExactChunks<'a, A, D> +{ /// Creates a new exact chunks producer. /// /// **Panics** if any chunk dimension is zero pub(crate) fn new(a: ArrayView<'a, A, D>, chunk: E) -> Self - where - E: IntoDimension, + where E: IntoDimension { let mut a = a.into_raw_view(); let chunk = chunk.into_dimension(); @@ -79,7 +80,8 @@ where { type Item = ::Item; type IntoIter = ExactChunksIter<'a, A, D>; - fn into_iter(self) -> Self::IntoIter { + fn into_iter(self) -> Self::IntoIter + { ExactChunksIter { iter: self.base.into_base_iter(), life: self.life, @@ -93,7 +95,8 @@ where /// /// See [`.exact_chunks()`](ArrayBase::exact_chunks) for more /// information. -pub struct ExactChunksIter<'a, A, D> { +pub struct ExactChunksIter<'a, A, D> +{ iter: Baseiter, life: PhantomData<&'a A>, chunk: D, @@ -126,20 +129,21 @@ impl_ndproducer! { /// See [`.exact_chunks_mut()`](ArrayBase::exact_chunks_mut) /// for more information. //#[derive(Debug)] -pub struct ExactChunksMut<'a, A, D> { +pub struct ExactChunksMut<'a, A, D> +{ base: RawArrayViewMut, life: PhantomData<&'a mut A>, chunk: D, inner_strides: D, } -impl<'a, A, D: Dimension> ExactChunksMut<'a, A, D> { +impl<'a, A, D: Dimension> ExactChunksMut<'a, A, D> +{ /// Creates a new exact chunks producer. /// /// **Panics** if any chunk dimension is zero pub(crate) fn new(a: ArrayViewMut<'a, A, D>, chunk: E) -> Self - where - E: IntoDimension, + where E: IntoDimension { let mut a = a.into_raw_view_mut(); let chunk = chunk.into_dimension(); @@ -175,7 +179,8 @@ where { type Item = ::Item; type IntoIter = ExactChunksIterMut<'a, A, D>; - fn into_iter(self) -> Self::IntoIter { + fn into_iter(self) -> Self::IntoIter + { ExactChunksIterMut { iter: self.base.into_base_iter(), life: self.life, @@ -234,7 +239,8 @@ impl_iterator! { /// /// See [`.exact_chunks_mut()`](ArrayBase::exact_chunks_mut) /// for more information. -pub struct ExactChunksIterMut<'a, A, D> { +pub struct ExactChunksIterMut<'a, A, D> +{ iter: Baseiter, life: PhantomData<&'a mut A>, chunk: D, diff --git a/src/iterators/into_iter.rs b/src/iterators/into_iter.rs index cfa48299a..fcc2e4b8c 100644 --- a/src/iterators/into_iter.rs +++ b/src/iterators/into_iter.rs @@ -15,11 +15,9 @@ use crate::OwnedRepr; use super::Baseiter; use crate::impl_owned_array::drop_unreachable_raw; - /// By-value iterator for an array pub struct IntoIter -where - D: Dimension, +where D: Dimension { array_data: OwnedRepr, inner: Baseiter, @@ -31,12 +29,12 @@ where has_unreachable_elements: bool, } -impl IntoIter -where - D: Dimension, +impl IntoIter +where D: Dimension { /// Create a new by-value iterator that consumes `array` - pub(crate) fn new(mut array: Array) -> Self { + pub(crate) fn new(mut array: Array) -> Self + { unsafe { let array_head_ptr = array.ptr; let ptr = array.as_mut_ptr(); @@ -57,39 +55,45 @@ where } } -impl Iterator for IntoIter { +impl Iterator for IntoIter +{ type Item = A; #[inline] - fn next(&mut self) -> Option { + fn next(&mut self) -> Option + { self.inner.next().map(|p| unsafe { p.read() }) } - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { self.inner.size_hint() } } -impl ExactSizeIterator for IntoIter { - fn len(&self) -> usize { self.inner.len() } +impl ExactSizeIterator for IntoIter +{ + fn len(&self) -> usize + { + self.inner.len() + } } impl Drop for IntoIter -where - D: Dimension +where D: Dimension { - fn drop(&mut self) { + fn drop(&mut self) + { if !self.has_unreachable_elements || mem::size_of::() == 0 || !mem::needs_drop::() { return; } // iterate til the end - while let Some(_) = self.next() { } + while let Some(_) = self.next() {} unsafe { let data_ptr = self.array_data.as_ptr_mut(); - let view = RawArrayViewMut::new(self.array_head_ptr, self.inner.dim.clone(), - self.inner.strides.clone()); + let view = RawArrayViewMut::new(self.array_head_ptr, self.inner.dim.clone(), self.inner.strides.clone()); debug_assert!(self.inner.dim.size() < self.data_len, "data_len {} and dim size {}", self.data_len, self.inner.dim.size()); drop_unreachable_raw(view, data_ptr, self.data_len); @@ -98,13 +102,13 @@ where } impl IntoIterator for Array -where - D: Dimension +where D: Dimension { type Item = A; type IntoIter = IntoIter; - fn into_iter(self) -> Self::IntoIter { + fn into_iter(self) -> Self::IntoIter + { IntoIter::new(self) } } @@ -117,7 +121,8 @@ where type Item = A; type IntoIter = IntoIter; - fn into_iter(self) -> Self::IntoIter { + fn into_iter(self) -> Self::IntoIter + { IntoIter::new(self.into_owned()) } } @@ -130,7 +135,8 @@ where type Item = A; type IntoIter = IntoIter; - fn into_iter(self) -> Self::IntoIter { + fn into_iter(self) -> Self::IntoIter + { IntoIter::new(self.into_owned()) } } diff --git a/src/iterators/iter.rs b/src/iterators/iter.rs index 3f8b05009..5c5acb9d7 100644 --- a/src/iterators/iter.rs +++ b/src/iterators/iter.rs @@ -19,9 +19,9 @@ pub use crate::iterators::{ ExactChunksMut, IndexedIter, IndexedIterMut, + IntoIter, Iter, IterMut, - IntoIter, Lanes, LanesIter, LanesIterMut, diff --git a/src/iterators/lanes.rs b/src/iterators/lanes.rs index 7286e0696..11c83d002 100644 --- a/src/iterators/lanes.rs +++ b/src/iterators/lanes.rs @@ -25,16 +25,17 @@ impl_ndproducer! { /// See [`.lanes()`](ArrayBase::lanes) /// for more information. -pub struct Lanes<'a, A, D> { +pub struct Lanes<'a, A, D> +{ base: ArrayView<'a, A, D>, inner_len: Ix, inner_stride: Ixs, } -impl<'a, A, D: Dimension> Lanes<'a, A, D> { +impl<'a, A, D: Dimension> Lanes<'a, A, D> +{ pub(crate) fn new(v: ArrayView<'a, A, Di>, axis: Axis) -> Self - where - Di: Dimension, + where Di: Dimension { let ndim = v.ndim(); let len; @@ -76,12 +77,12 @@ impl_ndproducer! { } impl<'a, A, D> IntoIterator for Lanes<'a, A, D> -where - D: Dimension, +where D: Dimension { type Item = ::Item; type IntoIter = LanesIter<'a, A, D>; - fn into_iter(self) -> Self::IntoIter { + fn into_iter(self) -> Self::IntoIter + { LanesIter { iter: self.base.into_base_iter(), inner_len: self.inner_len, @@ -93,16 +94,17 @@ where /// See [`.lanes_mut()`](ArrayBase::lanes_mut) /// for more information. -pub struct LanesMut<'a, A, D> { +pub struct LanesMut<'a, A, D> +{ base: ArrayViewMut<'a, A, D>, inner_len: Ix, inner_stride: Ixs, } -impl<'a, A, D: Dimension> LanesMut<'a, A, D> { +impl<'a, A, D: Dimension> LanesMut<'a, A, D> +{ pub(crate) fn new(v: ArrayViewMut<'a, A, Di>, axis: Axis) -> Self - where - Di: Dimension, + where Di: Dimension { let ndim = v.ndim(); let len; @@ -126,12 +128,12 @@ impl<'a, A, D: Dimension> LanesMut<'a, A, D> { } impl<'a, A, D> IntoIterator for LanesMut<'a, A, D> -where - D: Dimension, +where D: Dimension { type Item = ::Item; type IntoIter = LanesIterMut<'a, A, D>; - fn into_iter(self) -> Self::IntoIter { + fn into_iter(self) -> Self::IntoIter + { LanesIterMut { iter: self.base.into_base_iter(), inner_len: self.inner_len, diff --git a/src/iterators/macros.rs b/src/iterators/macros.rs index 7fbe410fe..78697ec25 100644 --- a/src/iterators/macros.rs +++ b/src/iterators/macros.rs @@ -123,7 +123,7 @@ expand_if!(@nonempty [$($cloneparm)*] } ); - } + }; } macro_rules! impl_iterator { @@ -170,5 +170,5 @@ macro_rules! impl_iterator { self.$base.size_hint() } } - } + }; } diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 3e1d6fe43..4851b2827 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -36,19 +36,22 @@ use std::slice::{self, Iter as SliceIter, IterMut as SliceIterMut}; /// /// Iterator element type is `*mut A`. #[derive(Debug)] -pub struct Baseiter { +pub struct Baseiter +{ ptr: *mut A, dim: D, strides: D, index: Option, } -impl Baseiter { +impl Baseiter +{ /// Creating a Baseiter is unsafe because shape and stride parameters need /// to be correct to avoid performing an unsafe pointer offset while /// iterating. #[inline] - pub unsafe fn new(ptr: *mut A, len: D, stride: D) -> Baseiter { + pub unsafe fn new(ptr: *mut A, len: D, stride: D) -> Baseiter + { Baseiter { ptr, index: len.first_index(), @@ -58,11 +61,13 @@ impl Baseiter { } } -impl Iterator for Baseiter { +impl Iterator for Baseiter +{ type Item = *mut A; #[inline] - fn next(&mut self) -> Option<*mut A> { + fn next(&mut self) -> Option<*mut A> + { let index = match self.index { None => return None, Some(ref ix) => ix.clone(), @@ -72,14 +77,14 @@ impl Iterator for Baseiter { unsafe { Some(self.ptr.offset(offset)) } } - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { let len = self.len(); (len, Some(len)) } fn fold(mut self, init: Acc, mut g: G) -> Acc - where - G: FnMut(Acc, *mut A) -> Acc, + where G: FnMut(Acc, *mut A) -> Acc { let ndim = self.dim.ndim(); debug_assert_ne!(ndim, 0); @@ -105,8 +110,10 @@ impl Iterator for Baseiter { } } -impl ExactSizeIterator for Baseiter { - fn len(&self) -> usize { +impl ExactSizeIterator for Baseiter +{ + fn len(&self) -> usize + { match self.index { None => 0, Some(ref ix) => { @@ -123,9 +130,11 @@ impl ExactSizeIterator for Baseiter { } } -impl DoubleEndedIterator for Baseiter { +impl DoubleEndedIterator for Baseiter +{ #[inline] - fn next_back(&mut self) -> Option<*mut A> { + fn next_back(&mut self) -> Option<*mut A> + { let index = match self.index { None => return None, Some(ix) => ix, @@ -139,7 +148,8 @@ impl DoubleEndedIterator for Baseiter { unsafe { Some(self.ptr.offset(offset)) } } - fn nth_back(&mut self, n: usize) -> Option<*mut A> { + fn nth_back(&mut self, n: usize) -> Option<*mut A> + { let index = self.index?; let len = self.dim[0] - index[0]; if n < len { @@ -156,8 +166,7 @@ impl DoubleEndedIterator for Baseiter { } fn rfold(mut self, init: Acc, mut g: G) -> Acc - where - G: FnMut(Acc, *mut A) -> Acc, + where G: FnMut(Acc, *mut A) -> Acc { let mut accum = init; if let Some(index) = self.index { @@ -200,8 +209,10 @@ clone_bounds!( } ); -impl<'a, A, D: Dimension> ElementsBase<'a, A, D> { - pub fn new(v: ArrayView<'a, A, D>) -> Self { +impl<'a, A, D: Dimension> ElementsBase<'a, A, D> +{ + pub fn new(v: ArrayView<'a, A, D>) -> Self + { ElementsBase { inner: v.into_base_iter(), life: PhantomData, @@ -209,44 +220,47 @@ impl<'a, A, D: Dimension> ElementsBase<'a, A, D> { } } -impl<'a, A, D: Dimension> Iterator for ElementsBase<'a, A, D> { +impl<'a, A, D: Dimension> Iterator for ElementsBase<'a, A, D> +{ type Item = &'a A; #[inline] - fn next(&mut self) -> Option<&'a A> { + fn next(&mut self) -> Option<&'a A> + { self.inner.next().map(|p| unsafe { &*p }) } - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { self.inner.size_hint() } fn fold(self, init: Acc, mut g: G) -> Acc - where - G: FnMut(Acc, Self::Item) -> Acc, + where G: FnMut(Acc, Self::Item) -> Acc { unsafe { self.inner.fold(init, move |acc, ptr| g(acc, &*ptr)) } } } -impl<'a, A> DoubleEndedIterator for ElementsBase<'a, A, Ix1> { +impl<'a, A> DoubleEndedIterator for ElementsBase<'a, A, Ix1> +{ #[inline] - fn next_back(&mut self) -> Option<&'a A> { + fn next_back(&mut self) -> Option<&'a A> + { self.inner.next_back().map(|p| unsafe { &*p }) } fn rfold(self, init: Acc, mut g: G) -> Acc - where - G: FnMut(Acc, Self::Item) -> Acc, + where G: FnMut(Acc, Self::Item) -> Acc { unsafe { self.inner.rfold(init, move |acc, ptr| g(acc, &*ptr)) } } } impl<'a, A, D> ExactSizeIterator for ElementsBase<'a, A, D> -where - D: Dimension, +where D: Dimension { - fn len(&self) -> usize { + fn len(&self) -> usize + { self.inner.len() } } @@ -279,10 +293,10 @@ clone_bounds!( ); impl<'a, A, D> Iter<'a, A, D> -where - D: Dimension, +where D: Dimension { - pub(crate) fn new(self_: ArrayView<'a, A, D>) -> Self { + pub(crate) fn new(self_: ArrayView<'a, A, D>) -> Self + { Iter { inner: if let Some(slc) = self_.to_slice() { ElementsRepr::Slice(slc.iter()) @@ -294,10 +308,10 @@ where } impl<'a, A, D> IterMut<'a, A, D> -where - D: Dimension, +where D: Dimension { - pub(crate) fn new(self_: ArrayViewMut<'a, A, D>) -> Self { + pub(crate) fn new(self_: ArrayViewMut<'a, A, D>) -> Self + { IterMut { inner: match self_.try_into_slice() { Ok(x) => ElementsRepr::Slice(x.iter_mut()), @@ -308,7 +322,8 @@ where } #[derive(Clone, Debug)] -pub enum ElementsRepr { +pub enum ElementsRepr +{ Slice(S), Counted(C), } @@ -319,13 +334,15 @@ pub enum ElementsRepr { /// /// See [`.iter()`](ArrayBase::iter) for more information. #[derive(Debug)] -pub struct Iter<'a, A, D> { +pub struct Iter<'a, A, D> +{ inner: ElementsRepr, ElementsBase<'a, A, D>>, } /// Counted read only iterator #[derive(Debug)] -pub struct ElementsBase<'a, A, D> { +pub struct ElementsBase<'a, A, D> +{ inner: Baseiter, life: PhantomData<&'a A>, } @@ -336,7 +353,8 @@ pub struct ElementsBase<'a, A, D> { /// /// See [`.iter_mut()`](ArrayBase::iter_mut) for more information. #[derive(Debug)] -pub struct IterMut<'a, A, D> { +pub struct IterMut<'a, A, D> +{ inner: ElementsRepr, ElementsBaseMut<'a, A, D>>, } @@ -344,13 +362,16 @@ pub struct IterMut<'a, A, D> { /// /// Iterator element type is `&'a mut A`. #[derive(Debug)] -pub struct ElementsBaseMut<'a, A, D> { +pub struct ElementsBaseMut<'a, A, D> +{ inner: Baseiter, life: PhantomData<&'a mut A>, } -impl<'a, A, D: Dimension> ElementsBaseMut<'a, A, D> { - pub fn new(v: ArrayViewMut<'a, A, D>) -> Self { +impl<'a, A, D: Dimension> ElementsBaseMut<'a, A, D> +{ + pub fn new(v: ArrayViewMut<'a, A, D>) -> Self + { ElementsBaseMut { inner: v.into_base_iter(), life: PhantomData, @@ -369,127 +390,130 @@ pub struct IndexedIter<'a, A, D>(ElementsBase<'a, A, D>); pub struct IndexedIterMut<'a, A, D>(ElementsBaseMut<'a, A, D>); impl<'a, A, D> IndexedIter<'a, A, D> -where - D: Dimension, +where D: Dimension { - pub(crate) fn new(x: ElementsBase<'a, A, D>) -> Self { + pub(crate) fn new(x: ElementsBase<'a, A, D>) -> Self + { IndexedIter(x) } } impl<'a, A, D> IndexedIterMut<'a, A, D> -where - D: Dimension, +where D: Dimension { - pub(crate) fn new(x: ElementsBaseMut<'a, A, D>) -> Self { + pub(crate) fn new(x: ElementsBaseMut<'a, A, D>) -> Self + { IndexedIterMut(x) } } -impl<'a, A, D: Dimension> Iterator for Iter<'a, A, D> { +impl<'a, A, D: Dimension> Iterator for Iter<'a, A, D> +{ type Item = &'a A; #[inline] - fn next(&mut self) -> Option<&'a A> { + fn next(&mut self) -> Option<&'a A> + { either_mut!(self.inner, iter => iter.next()) } - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { either!(self.inner, ref iter => iter.size_hint()) } fn fold(self, init: Acc, g: G) -> Acc - where - G: FnMut(Acc, Self::Item) -> Acc, + where G: FnMut(Acc, Self::Item) -> Acc { either!(self.inner, iter => iter.fold(init, g)) } - fn nth(&mut self, n: usize) -> Option { + fn nth(&mut self, n: usize) -> Option + { either_mut!(self.inner, iter => iter.nth(n)) } fn collect(self) -> B - where - B: FromIterator, + where B: FromIterator { either!(self.inner, iter => iter.collect()) } fn all(&mut self, f: F) -> bool - where - F: FnMut(Self::Item) -> bool, + where F: FnMut(Self::Item) -> bool { either_mut!(self.inner, iter => iter.all(f)) } fn any(&mut self, f: F) -> bool - where - F: FnMut(Self::Item) -> bool, + where F: FnMut(Self::Item) -> bool { either_mut!(self.inner, iter => iter.any(f)) } fn find

(&mut self, predicate: P) -> Option - where - P: FnMut(&Self::Item) -> bool, + where P: FnMut(&Self::Item) -> bool { either_mut!(self.inner, iter => iter.find(predicate)) } fn find_map(&mut self, f: F) -> Option - where - F: FnMut(Self::Item) -> Option, + where F: FnMut(Self::Item) -> Option { either_mut!(self.inner, iter => iter.find_map(f)) } - fn count(self) -> usize { + fn count(self) -> usize + { either!(self.inner, iter => iter.count()) } - fn last(self) -> Option { + fn last(self) -> Option + { either!(self.inner, iter => iter.last()) } fn position

(&mut self, predicate: P) -> Option - where - P: FnMut(Self::Item) -> bool, + where P: FnMut(Self::Item) -> bool { either_mut!(self.inner, iter => iter.position(predicate)) } } -impl<'a, A> DoubleEndedIterator for Iter<'a, A, Ix1> { +impl<'a, A> DoubleEndedIterator for Iter<'a, A, Ix1> +{ #[inline] - fn next_back(&mut self) -> Option<&'a A> { + fn next_back(&mut self) -> Option<&'a A> + { either_mut!(self.inner, iter => iter.next_back()) } - fn nth_back(&mut self, n: usize) -> Option<&'a A> { + fn nth_back(&mut self, n: usize) -> Option<&'a A> + { either_mut!(self.inner, iter => iter.nth_back(n)) } fn rfold(self, init: Acc, g: G) -> Acc - where - G: FnMut(Acc, Self::Item) -> Acc, + where G: FnMut(Acc, Self::Item) -> Acc { either!(self.inner, iter => iter.rfold(init, g)) } } impl<'a, A, D> ExactSizeIterator for Iter<'a, A, D> -where - D: Dimension, +where D: Dimension { - fn len(&self) -> usize { + fn len(&self) -> usize + { either!(self.inner, ref iter => iter.len()) } } -impl<'a, A, D: Dimension> Iterator for IndexedIter<'a, A, D> { +impl<'a, A, D: Dimension> Iterator for IndexedIter<'a, A, D> +{ type Item = (D::Pattern, &'a A); #[inline] - fn next(&mut self) -> Option { + fn next(&mut self) -> Option + { let index = match self.0.inner.index { None => return None, Some(ref ix) => ix.clone(), @@ -500,166 +524,173 @@ impl<'a, A, D: Dimension> Iterator for IndexedIter<'a, A, D> { } } - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { self.0.size_hint() } } impl<'a, A, D> ExactSizeIterator for IndexedIter<'a, A, D> -where - D: Dimension, +where D: Dimension { - fn len(&self) -> usize { + fn len(&self) -> usize + { self.0.inner.len() } } -impl<'a, A, D: Dimension> Iterator for IterMut<'a, A, D> { +impl<'a, A, D: Dimension> Iterator for IterMut<'a, A, D> +{ type Item = &'a mut A; #[inline] - fn next(&mut self) -> Option<&'a mut A> { + fn next(&mut self) -> Option<&'a mut A> + { either_mut!(self.inner, iter => iter.next()) } - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { either!(self.inner, ref iter => iter.size_hint()) } fn fold(self, init: Acc, g: G) -> Acc - where - G: FnMut(Acc, Self::Item) -> Acc, + where G: FnMut(Acc, Self::Item) -> Acc { either!(self.inner, iter => iter.fold(init, g)) } - fn nth(&mut self, n: usize) -> Option { + fn nth(&mut self, n: usize) -> Option + { either_mut!(self.inner, iter => iter.nth(n)) } fn collect(self) -> B - where - B: FromIterator, + where B: FromIterator { either!(self.inner, iter => iter.collect()) } fn all(&mut self, f: F) -> bool - where - F: FnMut(Self::Item) -> bool, + where F: FnMut(Self::Item) -> bool { either_mut!(self.inner, iter => iter.all(f)) } fn any(&mut self, f: F) -> bool - where - F: FnMut(Self::Item) -> bool, + where F: FnMut(Self::Item) -> bool { either_mut!(self.inner, iter => iter.any(f)) } fn find

(&mut self, predicate: P) -> Option - where - P: FnMut(&Self::Item) -> bool, + where P: FnMut(&Self::Item) -> bool { either_mut!(self.inner, iter => iter.find(predicate)) } fn find_map(&mut self, f: F) -> Option - where - F: FnMut(Self::Item) -> Option, + where F: FnMut(Self::Item) -> Option { either_mut!(self.inner, iter => iter.find_map(f)) } - fn count(self) -> usize { + fn count(self) -> usize + { either!(self.inner, iter => iter.count()) } - fn last(self) -> Option { + fn last(self) -> Option + { either!(self.inner, iter => iter.last()) } fn position

(&mut self, predicate: P) -> Option - where - P: FnMut(Self::Item) -> bool, + where P: FnMut(Self::Item) -> bool { either_mut!(self.inner, iter => iter.position(predicate)) } } -impl<'a, A> DoubleEndedIterator for IterMut<'a, A, Ix1> { +impl<'a, A> DoubleEndedIterator for IterMut<'a, A, Ix1> +{ #[inline] - fn next_back(&mut self) -> Option<&'a mut A> { + fn next_back(&mut self) -> Option<&'a mut A> + { either_mut!(self.inner, iter => iter.next_back()) } - fn nth_back(&mut self, n: usize) -> Option<&'a mut A> { + fn nth_back(&mut self, n: usize) -> Option<&'a mut A> + { either_mut!(self.inner, iter => iter.nth_back(n)) } fn rfold(self, init: Acc, g: G) -> Acc - where - G: FnMut(Acc, Self::Item) -> Acc, + where G: FnMut(Acc, Self::Item) -> Acc { either!(self.inner, iter => iter.rfold(init, g)) } } impl<'a, A, D> ExactSizeIterator for IterMut<'a, A, D> -where - D: Dimension, +where D: Dimension { - fn len(&self) -> usize { + fn len(&self) -> usize + { either!(self.inner, ref iter => iter.len()) } } -impl<'a, A, D: Dimension> Iterator for ElementsBaseMut<'a, A, D> { +impl<'a, A, D: Dimension> Iterator for ElementsBaseMut<'a, A, D> +{ type Item = &'a mut A; #[inline] - fn next(&mut self) -> Option<&'a mut A> { + fn next(&mut self) -> Option<&'a mut A> + { self.inner.next().map(|p| unsafe { &mut *p }) } - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { self.inner.size_hint() } fn fold(self, init: Acc, mut g: G) -> Acc - where - G: FnMut(Acc, Self::Item) -> Acc, + where G: FnMut(Acc, Self::Item) -> Acc { unsafe { self.inner.fold(init, move |acc, ptr| g(acc, &mut *ptr)) } } } -impl<'a, A> DoubleEndedIterator for ElementsBaseMut<'a, A, Ix1> { +impl<'a, A> DoubleEndedIterator for ElementsBaseMut<'a, A, Ix1> +{ #[inline] - fn next_back(&mut self) -> Option<&'a mut A> { + fn next_back(&mut self) -> Option<&'a mut A> + { self.inner.next_back().map(|p| unsafe { &mut *p }) } fn rfold(self, init: Acc, mut g: G) -> Acc - where - G: FnMut(Acc, Self::Item) -> Acc, + where G: FnMut(Acc, Self::Item) -> Acc { unsafe { self.inner.rfold(init, move |acc, ptr| g(acc, &mut *ptr)) } } } impl<'a, A, D> ExactSizeIterator for ElementsBaseMut<'a, A, D> -where - D: Dimension, +where D: Dimension { - fn len(&self) -> usize { + fn len(&self) -> usize + { self.inner.len() } } -impl<'a, A, D: Dimension> Iterator for IndexedIterMut<'a, A, D> { +impl<'a, A, D: Dimension> Iterator for IndexedIterMut<'a, A, D> +{ type Item = (D::Pattern, &'a mut A); #[inline] - fn next(&mut self) -> Option { + fn next(&mut self) -> Option + { let index = match self.0.inner.index { None => return None, Some(ref ix) => ix.clone(), @@ -670,16 +701,17 @@ impl<'a, A, D: Dimension> Iterator for IndexedIterMut<'a, A, D> { } } - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { self.0.size_hint() } } impl<'a, A, D> ExactSizeIterator for IndexedIterMut<'a, A, D> -where - D: Dimension, +where D: Dimension { - fn len(&self) -> usize { + fn len(&self) -> usize + { self.0.inner.len() } } @@ -688,7 +720,8 @@ where /// each lane along that axis. /// /// See [`.lanes()`](ArrayBase::lanes) for more information. -pub struct LanesIter<'a, A, D> { +pub struct LanesIter<'a, A, D> +{ inner_len: Ix, inner_stride: Ixs, iter: Baseiter, @@ -708,36 +741,38 @@ clone_bounds!( ); impl<'a, A, D> Iterator for LanesIter<'a, A, D> -where - D: Dimension, +where D: Dimension { type Item = ArrayView<'a, A, Ix1>; - fn next(&mut self) -> Option { - self.iter.next().map(|ptr| unsafe { - ArrayView::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) - }) + fn next(&mut self) -> Option + { + self.iter + .next() + .map(|ptr| unsafe { ArrayView::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) }) } - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { self.iter.size_hint() } } impl<'a, A, D> ExactSizeIterator for LanesIter<'a, A, D> -where - D: Dimension, +where D: Dimension { - fn len(&self) -> usize { + fn len(&self) -> usize + { self.iter.len() } } impl<'a, A> DoubleEndedIterator for LanesIter<'a, A, Ix1> { - fn next_back(&mut self) -> Option { - self.iter.next_back().map(|ptr| unsafe { - ArrayView::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) - }) + fn next_back(&mut self) -> Option + { + self.iter + .next_back() + .map(|ptr| unsafe { ArrayView::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) }) } } @@ -749,7 +784,8 @@ impl<'a, A> DoubleEndedIterator for LanesIter<'a, A, Ix1> /// /// See [`.lanes_mut()`](ArrayBase::lanes_mut) /// for more information. -pub struct LanesIterMut<'a, A, D> { +pub struct LanesIterMut<'a, A, D> +{ inner_len: Ix, inner_stride: Ixs, iter: Baseiter, @@ -757,41 +793,44 @@ pub struct LanesIterMut<'a, A, D> { } impl<'a, A, D> Iterator for LanesIterMut<'a, A, D> -where - D: Dimension, +where D: Dimension { type Item = ArrayViewMut<'a, A, Ix1>; - fn next(&mut self) -> Option { - self.iter.next().map(|ptr| unsafe { - ArrayViewMut::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) - }) + fn next(&mut self) -> Option + { + self.iter + .next() + .map(|ptr| unsafe { ArrayViewMut::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) }) } - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { self.iter.size_hint() } } impl<'a, A, D> ExactSizeIterator for LanesIterMut<'a, A, D> -where - D: Dimension, +where D: Dimension { - fn len(&self) -> usize { + fn len(&self) -> usize + { self.iter.len() } } impl<'a, A> DoubleEndedIterator for LanesIterMut<'a, A, Ix1> { - fn next_back(&mut self) -> Option { - self.iter.next_back().map(|ptr| unsafe { - ArrayViewMut::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) - }) + fn next_back(&mut self) -> Option + { + self.iter + .next_back() + .map(|ptr| unsafe { ArrayViewMut::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) }) } } #[derive(Debug)] -pub struct AxisIterCore { +pub struct AxisIterCore +{ /// Index along the axis of the value of `.next()`, relative to the start /// of the axis. index: Ix, @@ -822,7 +861,8 @@ clone_bounds!( } ); -impl AxisIterCore { +impl AxisIterCore +{ /// Constructs a new iterator over the specified axis. fn new(v: ArrayBase, axis: Axis) -> Self where @@ -840,7 +880,8 @@ impl AxisIterCore { } #[inline] - unsafe fn offset(&self, index: usize) -> *mut A { + unsafe fn offset(&self, index: usize) -> *mut A + { debug_assert!( index < self.end, "index={}, end={}, stride={}", @@ -859,7 +900,8 @@ impl AxisIterCore { /// **Panics** if `index` is strictly greater than the iterator's remaining /// length. #[track_caller] - fn split_at(self, index: usize) -> (Self, Self) { + fn split_at(self, index: usize) -> (Self, Self) + { assert!(index <= self.len()); let mid = self.index + index; let left = AxisIterCore { @@ -883,25 +925,27 @@ impl AxisIterCore { /// Does the same thing as `.next()` but also returns the index of the item /// relative to the start of the axis. - fn next_with_index(&mut self) -> Option<(usize, *mut A)> { + fn next_with_index(&mut self) -> Option<(usize, *mut A)> + { let index = self.index; self.next().map(|ptr| (index, ptr)) } /// Does the same thing as `.next_back()` but also returns the index of the /// item relative to the start of the axis. - fn next_back_with_index(&mut self) -> Option<(usize, *mut A)> { + fn next_back_with_index(&mut self) -> Option<(usize, *mut A)> + { self.next_back().map(|ptr| (self.end, ptr)) } } impl Iterator for AxisIterCore -where - D: Dimension, +where D: Dimension { type Item = *mut A; - fn next(&mut self) -> Option { + fn next(&mut self) -> Option + { if self.index >= self.end { None } else { @@ -911,17 +955,18 @@ where } } - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { let len = self.len(); (len, Some(len)) } } impl DoubleEndedIterator for AxisIterCore -where - D: Dimension, +where D: Dimension { - fn next_back(&mut self) -> Option { + fn next_back(&mut self) -> Option + { if self.index >= self.end { None } else { @@ -933,10 +978,10 @@ where } impl ExactSizeIterator for AxisIterCore -where - D: Dimension, +where D: Dimension { - fn len(&self) -> usize { + fn len(&self) -> usize + { self.end - self.index } } @@ -956,7 +1001,8 @@ where /// or [`.axis_iter()`](ArrayBase::axis_iter) /// for more information. #[derive(Debug)] -pub struct AxisIter<'a, A, D> { +pub struct AxisIter<'a, A, D> +{ iter: AxisIterCore, life: PhantomData<&'a A>, } @@ -971,11 +1017,11 @@ clone_bounds!( } ); -impl<'a, A, D: Dimension> AxisIter<'a, A, D> { +impl<'a, A, D: Dimension> AxisIter<'a, A, D> +{ /// Creates a new iterator over the specified axis. pub(crate) fn new(v: ArrayView<'a, A, Di>, axis: Axis) -> Self - where - Di: RemoveAxis, + where Di: RemoveAxis { AxisIter { iter: AxisIterCore::new(v, axis), @@ -991,7 +1037,8 @@ impl<'a, A, D: Dimension> AxisIter<'a, A, D> { /// **Panics** if `index` is strictly greater than the iterator's remaining /// length. #[track_caller] - pub fn split_at(self, index: usize) -> (Self, Self) { + pub fn split_at(self, index: usize) -> (Self, Self) + { let (left, right) = self.iter.split_at(index); ( AxisIter { @@ -1007,34 +1054,35 @@ impl<'a, A, D: Dimension> AxisIter<'a, A, D> { } impl<'a, A, D> Iterator for AxisIter<'a, A, D> -where - D: Dimension, +where D: Dimension { type Item = ArrayView<'a, A, D>; - fn next(&mut self) -> Option { + fn next(&mut self) -> Option + { self.iter.next().map(|ptr| unsafe { self.as_ref(ptr) }) } - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { self.iter.size_hint() } } impl<'a, A, D> DoubleEndedIterator for AxisIter<'a, A, D> -where - D: Dimension, +where D: Dimension { - fn next_back(&mut self) -> Option { + fn next_back(&mut self) -> Option + { self.iter.next_back().map(|ptr| unsafe { self.as_ref(ptr) }) } } impl<'a, A, D> ExactSizeIterator for AxisIter<'a, A, D> -where - D: Dimension, +where D: Dimension { - fn len(&self) -> usize { + fn len(&self) -> usize + { self.iter.len() } } @@ -1053,16 +1101,17 @@ where /// See [`.outer_iter_mut()`](ArrayBase::outer_iter_mut) /// or [`.axis_iter_mut()`](ArrayBase::axis_iter_mut) /// for more information. -pub struct AxisIterMut<'a, A, D> { +pub struct AxisIterMut<'a, A, D> +{ iter: AxisIterCore, life: PhantomData<&'a mut A>, } -impl<'a, A, D: Dimension> AxisIterMut<'a, A, D> { +impl<'a, A, D: Dimension> AxisIterMut<'a, A, D> +{ /// Creates a new iterator over the specified axis. pub(crate) fn new(v: ArrayViewMut<'a, A, Di>, axis: Axis) -> Self - where - Di: RemoveAxis, + where Di: RemoveAxis { AxisIterMut { iter: AxisIterCore::new(v, axis), @@ -1078,7 +1127,8 @@ impl<'a, A, D: Dimension> AxisIterMut<'a, A, D> { /// **Panics** if `index` is strictly greater than the iterator's remaining /// length. #[track_caller] - pub fn split_at(self, index: usize) -> (Self, Self) { + pub fn split_at(self, index: usize) -> (Self, Self) + { let (left, right) = self.iter.split_at(index); ( AxisIterMut { @@ -1094,53 +1144,58 @@ impl<'a, A, D: Dimension> AxisIterMut<'a, A, D> { } impl<'a, A, D> Iterator for AxisIterMut<'a, A, D> -where - D: Dimension, +where D: Dimension { type Item = ArrayViewMut<'a, A, D>; - fn next(&mut self) -> Option { + fn next(&mut self) -> Option + { self.iter.next().map(|ptr| unsafe { self.as_ref(ptr) }) } - fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) + { self.iter.size_hint() } } impl<'a, A, D> DoubleEndedIterator for AxisIterMut<'a, A, D> -where - D: Dimension, +where D: Dimension { - fn next_back(&mut self) -> Option { + fn next_back(&mut self) -> Option + { self.iter.next_back().map(|ptr| unsafe { self.as_ref(ptr) }) } } impl<'a, A, D> ExactSizeIterator for AxisIterMut<'a, A, D> -where - D: Dimension, +where D: Dimension { - fn len(&self) -> usize { + fn len(&self) -> usize + { self.iter.len() } } -impl<'a, A, D: Dimension> NdProducer for AxisIter<'a, A, D> { +impl<'a, A, D: Dimension> NdProducer for AxisIter<'a, A, D> +{ type Item = ::Item; type Dim = Ix1; type Ptr = *mut A; type Stride = isize; - fn layout(&self) -> crate::Layout { + fn layout(&self) -> crate::Layout + { crate::Layout::one_dimensional() } - fn raw_dim(&self) -> Self::Dim { + fn raw_dim(&self) -> Self::Dim + { Ix1(self.len()) } - fn as_ptr(&self) -> Self::Ptr { + fn as_ptr(&self) -> Self::Ptr + { if self.len() > 0 { // `self.iter.index` is guaranteed to be in-bounds if any of the // iterator remains (i.e. if `self.len() > 0`). @@ -1153,48 +1208,53 @@ impl<'a, A, D: Dimension> NdProducer for AxisIter<'a, A, D> { } } - fn contiguous_stride(&self) -> isize { + fn contiguous_stride(&self) -> isize + { self.iter.stride } - unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { - ArrayView::new_( - ptr, - self.iter.inner_dim.clone(), - self.iter.inner_strides.clone(), - ) + unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item + { + ArrayView::new_(ptr, self.iter.inner_dim.clone(), self.iter.inner_strides.clone()) } - unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { + unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr + { self.iter.offset(self.iter.index + i[0]) } - fn stride_of(&self, _axis: Axis) -> isize { + fn stride_of(&self, _axis: Axis) -> isize + { self.contiguous_stride() } - fn split_at(self, _axis: Axis, index: usize) -> (Self, Self) { + fn split_at(self, _axis: Axis, index: usize) -> (Self, Self) + { self.split_at(index) } private_impl! {} } -impl<'a, A, D: Dimension> NdProducer for AxisIterMut<'a, A, D> { +impl<'a, A, D: Dimension> NdProducer for AxisIterMut<'a, A, D> +{ type Item = ::Item; type Dim = Ix1; type Ptr = *mut A; type Stride = isize; - fn layout(&self) -> crate::Layout { + fn layout(&self) -> crate::Layout + { crate::Layout::one_dimensional() } - fn raw_dim(&self) -> Self::Dim { + fn raw_dim(&self) -> Self::Dim + { Ix1(self.len()) } - fn as_ptr(&self) -> Self::Ptr { + fn as_ptr(&self) -> Self::Ptr + { if self.len() > 0 { // `self.iter.index` is guaranteed to be in-bounds if any of the // iterator remains (i.e. if `self.len() > 0`). @@ -1207,27 +1267,28 @@ impl<'a, A, D: Dimension> NdProducer for AxisIterMut<'a, A, D> { } } - fn contiguous_stride(&self) -> isize { + fn contiguous_stride(&self) -> isize + { self.iter.stride } - unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { - ArrayViewMut::new_( - ptr, - self.iter.inner_dim.clone(), - self.iter.inner_strides.clone(), - ) + unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item + { + ArrayViewMut::new_(ptr, self.iter.inner_dim.clone(), self.iter.inner_strides.clone()) } - unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { + unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr + { self.iter.offset(self.iter.index + i[0]) } - fn stride_of(&self, _axis: Axis) -> isize { + fn stride_of(&self, _axis: Axis) -> isize + { self.contiguous_stride() } - fn split_at(self, _axis: Axis, index: usize) -> (Self, Self) { + fn split_at(self, _axis: Axis, index: usize) -> (Self, Self) + { self.split_at(index) } @@ -1244,7 +1305,8 @@ impl<'a, A, D: Dimension> NdProducer for AxisIterMut<'a, A, D> { /// Iterator element type is `ArrayView<'a, A, D>`. /// /// See [`.axis_chunks_iter()`](ArrayBase::axis_chunks_iter) for more information. -pub struct AxisChunksIter<'a, A, D> { +pub struct AxisChunksIter<'a, A, D> +{ iter: AxisIterCore, /// Index of the partial chunk (the chunk smaller than the specified chunk /// size due to the axis length not being evenly divisible). If the axis @@ -1277,11 +1339,9 @@ clone_bounds!( /// /// **Panics** if `size == 0`. #[track_caller] -fn chunk_iter_parts( - v: ArrayView<'_, A, D>, - axis: Axis, - size: usize, -) -> (AxisIterCore, usize, D) { +fn chunk_iter_parts(v: ArrayView<'_, A, D>, axis: Axis, size: usize) + -> (AxisIterCore, usize, D) +{ assert_ne!(size, 0, "Chunk size must be nonzero."); let axis_len = v.len_of(axis); let n_whole_chunks = axis_len / size; @@ -1318,8 +1378,10 @@ fn chunk_iter_parts( (iter, partial_chunk_index, partial_chunk_dim) } -impl<'a, A, D: Dimension> AxisChunksIter<'a, A, D> { - pub(crate) fn new(v: ArrayView<'a, A, D>, axis: Axis, size: usize) -> Self { +impl<'a, A, D: Dimension> AxisChunksIter<'a, A, D> +{ + pub(crate) fn new(v: ArrayView<'a, A, D>, axis: Axis, size: usize) -> Self + { let (iter, partial_chunk_index, partial_chunk_dim) = chunk_iter_parts(v, axis, size); AxisChunksIter { iter, @@ -1426,17 +1488,19 @@ macro_rules! chunk_iter_impl { /// /// See [`.axis_chunks_iter_mut()`](ArrayBase::axis_chunks_iter_mut) /// for more information. -pub struct AxisChunksIterMut<'a, A, D> { +pub struct AxisChunksIterMut<'a, A, D> +{ iter: AxisIterCore, partial_chunk_index: usize, partial_chunk_dim: D, life: PhantomData<&'a mut A>, } -impl<'a, A, D: Dimension> AxisChunksIterMut<'a, A, D> { - pub(crate) fn new(v: ArrayViewMut<'a, A, D>, axis: Axis, size: usize) -> Self { - let (iter, partial_chunk_index, partial_chunk_dim) = - chunk_iter_parts(v.into_view(), axis, size); +impl<'a, A, D: Dimension> AxisChunksIterMut<'a, A, D> +{ + pub(crate) fn new(v: ArrayViewMut<'a, A, D>, axis: Axis, size: usize) -> Self + { + let (iter, partial_chunk_index, partial_chunk_dim) = chunk_iter_parts(v.into_view(), axis, size); AxisChunksIterMut { iter, partial_chunk_index, @@ -1495,8 +1559,7 @@ unsafe impl TrustedIterator for IntoIter where D: Dimension {} /// Like Iterator::collect, but only for trusted length iterators pub fn to_vec(iter: I) -> Vec -where - I: TrustedIterator + ExactSizeIterator, +where I: TrustedIterator + ExactSizeIterator { to_vec_mapped(iter, |x| x) } diff --git a/src/iterators/windows.rs b/src/iterators/windows.rs index 9140f43b9..ec1afb634 100644 --- a/src/iterators/windows.rs +++ b/src/iterators/windows.rs @@ -11,17 +11,18 @@ use crate::Slice; /// /// See [`.windows()`](ArrayBase::windows) for more /// information. -pub struct Windows<'a, A, D> { +pub struct Windows<'a, A, D> +{ base: RawArrayView, life: PhantomData<&'a A>, window: D, strides: D, } -impl<'a, A, D: Dimension> Windows<'a, A, D> { +impl<'a, A, D: Dimension> Windows<'a, A, D> +{ pub(crate) fn new(a: ArrayView<'a, A, D>, window_size: E) -> Self - where - E: IntoDimension, + where E: IntoDimension { let window = window_size.into_dimension(); let ndim = window.ndim(); @@ -33,8 +34,7 @@ impl<'a, A, D: Dimension> Windows<'a, A, D> { } pub(crate) fn new_with_stride(a: ArrayView<'a, A, D>, window_size: E, axis_strides: E) -> Self - where - E: IntoDimension, + where E: IntoDimension { let window = window_size.into_dimension(); @@ -112,7 +112,8 @@ where { type Item = ::Item; type IntoIter = WindowsIter<'a, A, D>; - fn into_iter(self) -> Self::IntoIter { + fn into_iter(self) -> Self::IntoIter + { WindowsIter { iter: self.base.into_base_iter(), life: self.life, @@ -126,7 +127,8 @@ where /// /// See [`.windows()`](ArrayBase::windows) for more /// information. -pub struct WindowsIter<'a, A, D> { +pub struct WindowsIter<'a, A, D> +{ iter: Baseiter, life: PhantomData<&'a A>, window: D, diff --git a/src/itertools.rs b/src/itertools.rs index 8edfd75eb..d3562e687 100644 --- a/src/itertools.rs +++ b/src/itertools.rs @@ -23,8 +23,7 @@ use std::iter; /// } /// ``` pub(crate) fn enumerate(iterable: I) -> iter::Enumerate -where - I: IntoIterator, +where I: IntoIterator { iterable.into_iter().enumerate() } diff --git a/src/layout/layoutfmt.rs b/src/layout/layoutfmt.rs index 3d7fad00a..f20f0caaa 100644 --- a/src/layout/layoutfmt.rs +++ b/src/layout/layoutfmt.rs @@ -12,8 +12,10 @@ const LAYOUT_NAMES: &[&str] = &["C", "F", "c", "f"]; use std::fmt; -impl fmt::Debug for Layout { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl fmt::Debug for Layout +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { if self.0 == 0 { write!(f, "Custom")? } else { diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 9eecf016d..026688d63 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -8,72 +8,82 @@ mod layoutfmt; #[derive(Copy, Clone)] pub struct Layout(u32); -impl Layout { +impl Layout +{ pub(crate) const CORDER: u32 = 0b01; pub(crate) const FORDER: u32 = 0b10; pub(crate) const CPREFER: u32 = 0b0100; pub(crate) const FPREFER: u32 = 0b1000; #[inline(always)] - pub(crate) fn is(self, flag: u32) -> bool { + pub(crate) fn is(self, flag: u32) -> bool + { self.0 & flag != 0 } /// Return layout common to both inputs #[inline(always)] - pub(crate) fn intersect(self, other: Layout) -> Layout { + pub(crate) fn intersect(self, other: Layout) -> Layout + { Layout(self.0 & other.0) } /// Return a layout that simultaneously "is" what both of the inputs are #[inline(always)] - pub(crate) fn also(self, other: Layout) -> Layout { + pub(crate) fn also(self, other: Layout) -> Layout + { Layout(self.0 | other.0) } #[inline(always)] - pub(crate) fn one_dimensional() -> Layout { + pub(crate) fn one_dimensional() -> Layout + { Layout::c().also(Layout::f()) } #[inline(always)] - pub(crate) fn c() -> Layout { + pub(crate) fn c() -> Layout + { Layout(Layout::CORDER | Layout::CPREFER) } #[inline(always)] - pub(crate) fn f() -> Layout { + pub(crate) fn f() -> Layout + { Layout(Layout::FORDER | Layout::FPREFER) } #[inline(always)] - pub(crate) fn cpref() -> Layout { + pub(crate) fn cpref() -> Layout + { Layout(Layout::CPREFER) } #[inline(always)] - pub(crate) fn fpref() -> Layout { + pub(crate) fn fpref() -> Layout + { Layout(Layout::FPREFER) } #[inline(always)] - pub(crate) fn none() -> Layout { + pub(crate) fn none() -> Layout + { Layout(0) } /// A simple "score" method which scores positive for preferring C-order, negative for F-order /// Subject to change when we can describe other layouts #[inline] - pub(crate) fn tendency(self) -> i32 { - (self.is(Layout::CORDER) as i32 - self.is(Layout::FORDER) as i32) + - (self.is(Layout::CPREFER) as i32 - self.is(Layout::FPREFER) as i32) - + pub(crate) fn tendency(self) -> i32 + { + (self.is(Layout::CORDER) as i32 - self.is(Layout::FORDER) as i32) + + (self.is(Layout::CPREFER) as i32 - self.is(Layout::FPREFER) as i32) } } - #[cfg(test)] -mod tests { +mod tests +{ use super::*; use crate::imp_prelude::*; use crate::NdProducer; @@ -91,7 +101,7 @@ mod tests { $mat, stringify!($layout)); )* - }} + }}; } macro_rules! assert_not_layouts { @@ -103,11 +113,12 @@ mod tests { $mat, stringify!($layout)); )* - }} + }}; } #[test] - fn contig_layouts() { + fn contig_layouts() + { let a = M::zeros((5, 5)); let b = M::zeros((5, 5).f()); let ac = a.view().layout(); @@ -119,7 +130,8 @@ mod tests { } #[test] - fn contig_cf_layouts() { + fn contig_cf_layouts() + { let a = M::zeros((5, 1)); let b = M::zeros((1, 5).f()); assert_layouts!(a, CORDER, CPREFER, FORDER, FPREFER); @@ -147,7 +159,8 @@ mod tests { } #[test] - fn stride_layouts() { + fn stride_layouts() + { let a = M::zeros((5, 5)); { @@ -174,7 +187,8 @@ mod tests { } #[test] - fn no_layouts() { + fn no_layouts() + { let a = M::zeros((5, 5)); let b = M::zeros((5, 5).f()); @@ -202,7 +216,8 @@ mod tests { } #[test] - fn skip_layouts() { + fn skip_layouts() + { let a = M::zeros((5, 5)); { let v1 = a.slice(s![..;2, ..]).layout(); diff --git a/src/lib.rs b/src/lib.rs index b15b0ea88..37af0adfe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,13 +112,12 @@ //! For conversion between `ndarray`, [`nalgebra`](https://crates.io/crates/nalgebra) and //! [`image`](https://crates.io/crates/image) check out [`nshare`](https://crates.io/crates/nshare). - extern crate alloc; -#[cfg(feature = "std")] -extern crate std; #[cfg(not(feature = "std"))] extern crate core as std; +#[cfg(feature = "std")] +extern crate std; #[cfg(feature = "blas")] extern crate cblas_sys; @@ -126,8 +125,8 @@ extern crate cblas_sys; #[cfg(feature = "docs")] pub mod doc; -use std::marker::PhantomData; use alloc::sync::Arc; +use std::marker::PhantomData; pub use crate::dimension::dim::*; pub use crate::dimension::{Axis, AxisDescription, Dimension, IntoDimension, RemoveAxis}; @@ -138,24 +137,22 @@ pub use crate::dimension::NdIndex; pub use crate::error::{ErrorKind, ShapeError}; pub use crate::indexes::{indices, indices_of}; pub use crate::order::Order; -pub use crate::slice::{ - MultiSliceArg, NewAxis, Slice, SliceArg, SliceInfo, SliceInfoElem, SliceNextDim, -}; +pub use crate::slice::{MultiSliceArg, NewAxis, Slice, SliceArg, SliceInfo, SliceInfoElem, SliceNextDim}; use crate::iterators::Baseiter; use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut}; pub use crate::arraytraits::AsArray; +pub use crate::linalg_traits::LinalgScalar; #[cfg(feature = "std")] pub use crate::linalg_traits::NdFloat; -pub use crate::linalg_traits::LinalgScalar; #[allow(deprecated)] // stack_new_axis pub use crate::stacking::{concatenate, stack, stack_new_axis}; -pub use crate::math_cell::MathCell; pub use crate::impl_views::IndexLonger; -pub use crate::shape_builder::{Shape, ShapeBuilder, ShapeArg, StrideShape}; +pub use crate::math_cell::MathCell; +pub use crate::shape_builder::{Shape, ShapeArg, ShapeBuilder, StrideShape}; #[macro_use] mod macro_utils; @@ -175,10 +172,7 @@ mod data_traits; pub use crate::aliases::*; -pub use crate::data_traits::{ - Data, DataMut, DataOwned, DataShared, RawData, RawDataClone, RawDataMut, - RawDataSubst, -}; +pub use crate::data_traits::{Data, DataMut, DataOwned, DataShared, RawData, RawDataClone, RawDataMut, RawDataSubst}; mod free_functions; pub use crate::free_functions::*; @@ -193,10 +187,10 @@ mod layout; mod linalg_traits; mod linspace; #[cfg(feature = "std")] -pub use crate::linspace::{Linspace, linspace, range}; +pub use crate::linspace::{linspace, range, Linspace}; mod logspace; #[cfg(feature = "std")] -pub use crate::logspace::{Logspace, logspace}; +pub use crate::logspace::{logspace, Logspace}; mod math_cell; mod numeric_util; mod order; @@ -217,13 +211,24 @@ pub use crate::zip::{FoldWhile, IntoNdProducer, NdProducer, Zip}; pub use crate::layout::Layout; /// Implementation's prelude. Common types used everywhere. -mod imp_prelude { +mod imp_prelude +{ pub use crate::dimension::DimensionExt; pub use crate::prelude::*; pub use crate::ArcArray; pub use crate::{ - CowRepr, Data, DataMut, DataOwned, DataShared, Ix, Ixs, RawData, RawDataMut, RawViewRepr, - RemoveAxis, ViewRepr, + CowRepr, + Data, + DataMut, + DataOwned, + DataShared, + Ix, + Ixs, + RawData, + RawDataMut, + RawViewRepr, + RemoveAxis, + ViewRepr, }; } @@ -1271,8 +1276,7 @@ pub type Ixs = isize; // // [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset-1 pub struct ArrayBase -where - S: RawData, +where S: RawData { /// Data buffer / ownership information. (If owned, contains the data /// buffer; if borrowed, contains the lifetime and mutability.) @@ -1432,8 +1436,10 @@ pub use data_repr::OwnedRepr; #[derive(Debug)] pub struct OwnedArcRepr(Arc>); -impl Clone for OwnedArcRepr { - fn clone(&self) -> Self { +impl Clone for OwnedArcRepr +{ + fn clone(&self) -> Self + { OwnedArcRepr(self.0.clone()) } } @@ -1444,13 +1450,16 @@ impl Clone for OwnedArcRepr { /// [`RawArrayView`] / [`RawArrayViewMut`] for the array type!* #[derive(Copy, Clone)] // This is just a marker type, to carry the mutability and element type. -pub struct RawViewRepr { +pub struct RawViewRepr +{ ptr: PhantomData, } -impl RawViewRepr { +impl RawViewRepr +{ #[inline(always)] - const fn new() -> Self { + const fn new() -> Self + { RawViewRepr { ptr: PhantomData } } } @@ -1461,13 +1470,16 @@ impl RawViewRepr { /// [`ArrayView`] / [`ArrayViewMut`] for the array type!* #[derive(Copy, Clone)] // This is just a marker type, to carry the lifetime parameter. -pub struct ViewRepr { +pub struct ViewRepr +{ life: PhantomData, } -impl ViewRepr { +impl ViewRepr +{ #[inline(always)] - const fn new() -> Self { + const fn new() -> Self + { ViewRepr { life: PhantomData } } } @@ -1476,16 +1488,19 @@ impl ViewRepr { /// /// *Don't use this type directly—use the type alias /// [`CowArray`] for the array type!* -pub enum CowRepr<'a, A> { +pub enum CowRepr<'a, A> +{ /// Borrowed data. View(ViewRepr<&'a A>), /// Owned data. Owned(OwnedRepr), } -impl<'a, A> CowRepr<'a, A> { +impl<'a, A> CowRepr<'a, A> +{ /// Returns `true` iff the data is the `View` variant. - pub fn is_view(&self) -> bool { + pub fn is_view(&self) -> bool + { match self { CowRepr::View(_) => true, CowRepr::Owned(_) => false, @@ -1493,7 +1508,8 @@ impl<'a, A> CowRepr<'a, A> { } /// Returns `true` iff the data is the `Owned` variant. - pub fn is_owned(&self) -> bool { + pub fn is_owned(&self) -> bool + { match self { CowRepr::View(_) => false, CowRepr::Owned(_) => true, @@ -1521,8 +1537,7 @@ where { #[inline] fn broadcast_unwrap(&self, dim: E) -> ArrayView<'_, A, E> - where - E: Dimension, + where E: Dimension { #[cold] #[inline(never)] @@ -1548,8 +1563,7 @@ where // (Checked in debug assertions). #[inline] fn broadcast_assume(&self, dim: E) -> ArrayView<'_, A, E> - where - E: Dimension, + where E: Dimension { let dim = dim.into_dimension(); debug_assert_eq!(self.shape(), dim.slice()); @@ -1560,13 +1574,12 @@ where } /// Remove array axis `axis` and return the result. - fn try_remove_axis(self, axis: Axis) -> ArrayBase { + fn try_remove_axis(self, axis: Axis) -> ArrayBase + { let d = self.dim.try_remove_axis(axis); let s = self.strides.try_remove_axis(axis); // safe because new dimension, strides allow access to a subset of old data - unsafe { - self.with_strides_dim(s, d) - } + unsafe { self.with_strides_dim(s, d) } } } @@ -1600,6 +1613,7 @@ mod impl_raw_views; mod impl_cow; /// Returns `true` if the pointer is aligned. -pub(crate) fn is_aligned(ptr: *const T) -> bool { +pub(crate) fn is_aligned(ptr: *const T) -> bool +{ (ptr as usize) % ::std::mem::align_of::() == 0 } diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index cd2f16199..bcfcba94e 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -14,10 +14,10 @@ use crate::numeric_util; use crate::{LinalgScalar, Zip}; -use std::any::TypeId; -use std::mem::MaybeUninit; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +use std::any::TypeId; +use std::mem::MaybeUninit; use num_complex::Complex; use num_complex::{Complex32 as c32, Complex64 as c64}; @@ -45,8 +45,7 @@ const GEMM_BLAS_CUTOFF: usize = 7; type blas_index = c_int; // blas index type impl ArrayBase -where - S: Data, +where S: Data { /// Perform dot product or matrix multiplication of arrays `self` and `rhs`. /// @@ -67,8 +66,7 @@ where /// layout allows. #[track_caller] pub fn dot(&self, rhs: &Rhs) -> >::Output - where - Self: Dot, + where Self: Dot { Dot::dot(self, rhs) } @@ -147,11 +145,8 @@ where /// which agrees with our pointer for non-negative strides, but /// is at the opposite end for negative strides. #[cfg(feature = "blas")] -unsafe fn blas_1d_params( - ptr: *const A, - len: usize, - stride: isize, -) -> (*const A, blas_index, blas_index) { +unsafe fn blas_1d_params(ptr: *const A, len: usize, stride: isize) -> (*const A, blas_index, blas_index) +{ // [x x x x] // ^--ptr // stride = -1 @@ -168,7 +163,8 @@ unsafe fn blas_1d_params( /// /// For two-dimensional arrays, the dot method computes the matrix /// multiplication. -pub trait Dot { +pub trait Dot +{ /// The result of the operation. /// /// For two-dimensional arrays: a rectangular array. @@ -193,7 +189,8 @@ where /// *Note:* If enabled, uses blas `dot` for elements of `f32, f64` when memory /// layout allows. #[track_caller] - fn dot(&self, rhs: &ArrayBase) -> A { + fn dot(&self, rhs: &ArrayBase) -> A + { self.dot_impl(rhs) } } @@ -216,14 +213,14 @@ where /// /// **Panics** if shapes are incompatible. #[track_caller] - fn dot(&self, rhs: &ArrayBase) -> Array { + fn dot(&self, rhs: &ArrayBase) -> Array + { rhs.t().dot(self) } } impl ArrayBase -where - S: Data, +where S: Data { /// Perform matrix multiplication of rectangular arrays `self` and `rhs`. /// @@ -256,8 +253,7 @@ where /// ``` #[track_caller] pub fn dot(&self, rhs: &Rhs) -> >::Output - where - Self: Dot, + where Self: Dot { Dot::dot(self, rhs) } @@ -270,7 +266,8 @@ where A: LinalgScalar, { type Output = Array2; - fn dot(&self, b: &ArrayBase) -> Array2 { + fn dot(&self, b: &ArrayBase) -> Array2 + { let a = self.view(); let b = b.view(); let ((m, k), (k2, n)) = (a.dim(), b.dim()); @@ -296,7 +293,8 @@ where /// Assumes that `m` and `n` are ≤ `isize::MAX`. #[cold] #[inline(never)] -fn dot_shape_error(m: usize, k: usize, k2: usize, n: usize) -> ! { +fn dot_shape_error(m: usize, k: usize, k2: usize, n: usize) -> ! +{ match m.checked_mul(n) { Some(len) if len <= ::std::isize::MAX as usize => {} _ => panic!("ndarray: shape {} × {} overflows isize", m, n), @@ -309,7 +307,8 @@ fn dot_shape_error(m: usize, k: usize, k2: usize, n: usize) -> ! { #[cold] #[inline(never)] -fn general_dot_shape_error(m: usize, k: usize, k2: usize, n: usize, c1: usize, c2: usize) -> ! { +fn general_dot_shape_error(m: usize, k: usize, k2: usize, n: usize, c1: usize, c2: usize) -> ! +{ panic!("ndarray: inputs {} × {}, {} × {}, and output {} × {} are not compatible for matrix multiplication", m, k, k2, n, c1, c2); } @@ -331,7 +330,8 @@ where { type Output = Array; #[track_caller] - fn dot(&self, rhs: &ArrayBase) -> Array { + fn dot(&self, rhs: &ArrayBase) -> Array + { let ((m, a), n) = (self.dim(), rhs.dim()); if a != n { dot_shape_error(m, a, n, 1); @@ -496,13 +496,8 @@ fn mat_mul_impl( /// C ← α A B + β C fn mat_mul_general( - alpha: A, - lhs: &ArrayView2<'_, A>, - rhs: &ArrayView2<'_, A>, - beta: A, - c: &mut ArrayViewMut2<'_, A>, -) where - A: LinalgScalar, + alpha: A, lhs: &ArrayView2<'_, A>, rhs: &ArrayView2<'_, A>, beta: A, c: &mut ArrayViewMut2<'_, A>, +) where A: LinalgScalar { let ((m, k), (_, n)) = (lhs.dim(), rhs.dim()); @@ -607,11 +602,8 @@ fn mat_mul_general( loop { unsafe { let elt = c.uget_mut((i, j)); - *elt = *elt * beta - + alpha - * (0..k).fold(A::zero(), move |s, x| { - s + *lhs.uget((i, x)) * *rhs.uget((x, j)) - }); + *elt = + *elt * beta + alpha * (0..k).fold(A::zero(), move |s, x| s + *lhs.uget((i, x)) * *rhs.uget((x, j))); } j += 1; if j == n { @@ -638,11 +630,7 @@ fn mat_mul_general( /// `f32, f64` for all memory layouts. #[track_caller] pub fn general_mat_mul( - alpha: A, - a: &ArrayBase, - b: &ArrayBase, - beta: A, - c: &mut ArrayBase, + alpha: A, a: &ArrayBase, b: &ArrayBase, beta: A, c: &mut ArrayBase, ) where S1: Data, S2: Data, @@ -671,11 +659,7 @@ pub fn general_mat_mul( #[track_caller] #[allow(clippy::collapsible_if)] pub fn general_mat_vec_mul( - alpha: A, - a: &ArrayBase, - x: &ArrayBase, - beta: A, - y: &mut ArrayBase, + alpha: A, a: &ArrayBase, x: &ArrayBase, beta: A, y: &mut ArrayBase, ) where S1: Data, S2: Data, @@ -695,11 +679,7 @@ pub fn general_mat_vec_mul( /// the destination may be uninitialized iff beta is zero. #[allow(clippy::collapsible_else_if)] unsafe fn general_mat_vec_mul_impl( - alpha: A, - a: &ArrayBase, - x: &ArrayBase, - beta: A, - y: RawArrayViewMut, + alpha: A, a: &ArrayBase, x: &ArrayBase, beta: A, y: RawArrayViewMut, ) where S1: Data, S2: Data, @@ -777,7 +757,6 @@ unsafe fn general_mat_vec_mul_impl( } } - /// Kronecker product of 2D matrices. /// /// The kronecker product of a LxN matrix A and a MxR matrix B is a (L*M)x(N*R) @@ -812,14 +791,16 @@ where #[inline(always)] /// Return `true` if `A` and `B` are the same type -fn same_type() -> bool { +fn same_type() -> bool +{ TypeId::of::() == TypeId::of::() } // Read pointer to type `A` as type `B`. // // **Panics** if `A` and `B` are not the same type -fn cast_as(a: &A) -> B { +fn cast_as(a: &A) -> B +{ assert!(same_type::(), "expect type {} and {} to match", std::any::type_name::(), std::any::type_name::()); unsafe { ::std::ptr::read(a as *const _ as *const B) } @@ -827,7 +808,8 @@ fn cast_as(a: &A) -> B { /// Return the complex in the form of an array [re, im] #[inline] -fn complex_array(z: Complex) -> [A; 2] { +fn complex_array(z: Complex) -> [A; 2] +{ [z.re, z.im] } @@ -845,17 +827,15 @@ where return false; } let stride = a.strides()[0]; - if stride == 0 - || stride > blas_index::max_value() as isize - || stride < blas_index::min_value() as isize - { + if stride == 0 || stride > blas_index::max_value() as isize || stride < blas_index::min_value() as isize { return false; } true } #[cfg(feature = "blas")] -enum MemoryOrder { +enum MemoryOrder +{ C, F, } @@ -887,7 +867,8 @@ where } #[cfg(feature = "blas")] -fn is_blas_2d(dim: &Ix2, stride: &Ix2, order: MemoryOrder) -> bool { +fn is_blas_2d(dim: &Ix2, stride: &Ix2, order: MemoryOrder) -> bool +{ let (m, n) = dim.into_pattern(); let s0 = stride[0] as isize; let s1 = stride[1] as isize; @@ -930,32 +911,37 @@ where #[cfg(test)] #[cfg(feature = "blas")] -mod blas_tests { +mod blas_tests +{ use super::*; #[test] - fn blas_row_major_2d_normal_matrix() { + fn blas_row_major_2d_normal_matrix() + { let m: Array2 = Array2::zeros((3, 5)); assert!(blas_row_major_2d::(&m)); assert!(!blas_column_major_2d::(&m)); } #[test] - fn blas_row_major_2d_row_matrix() { + fn blas_row_major_2d_row_matrix() + { let m: Array2 = Array2::zeros((1, 5)); assert!(blas_row_major_2d::(&m)); assert!(blas_column_major_2d::(&m)); } #[test] - fn blas_row_major_2d_column_matrix() { + fn blas_row_major_2d_column_matrix() + { let m: Array2 = Array2::zeros((5, 1)); assert!(blas_row_major_2d::(&m)); assert!(blas_column_major_2d::(&m)); } #[test] - fn blas_row_major_2d_transposed_row_matrix() { + fn blas_row_major_2d_transposed_row_matrix() + { let m: Array2 = Array2::zeros((1, 5)); let m_t = m.t(); assert!(blas_row_major_2d::(&m_t)); @@ -963,7 +949,8 @@ mod blas_tests { } #[test] - fn blas_row_major_2d_transposed_column_matrix() { + fn blas_row_major_2d_transposed_column_matrix() + { let m: Array2 = Array2::zeros((5, 1)); let m_t = m.t(); assert!(blas_row_major_2d::(&m_t)); @@ -971,7 +958,8 @@ mod blas_tests { } #[test] - fn blas_column_major_2d_normal_matrix() { + fn blas_column_major_2d_normal_matrix() + { let m: Array2 = Array2::zeros((3, 5).f()); assert!(!blas_row_major_2d::(&m)); assert!(blas_column_major_2d::(&m)); diff --git a/src/linalg/mod.rs b/src/linalg/mod.rs index abd7b2b9d..dc6964f9b 100644 --- a/src/linalg/mod.rs +++ b/src/linalg/mod.rs @@ -10,7 +10,7 @@ pub use self::impl_linalg::general_mat_mul; pub use self::impl_linalg::general_mat_vec_mul; -pub use self::impl_linalg::Dot; pub use self::impl_linalg::kron; +pub use self::impl_linalg::Dot; mod impl_linalg; diff --git a/src/linalg_traits.rs b/src/linalg_traits.rs index d52e7f54f..65d264c40 100644 --- a/src/linalg_traits.rs +++ b/src/linalg_traits.rs @@ -24,28 +24,12 @@ use crate::ScalarOperand; /// `'static` for type-based specialization, `Copy` so that they don't need move /// semantics or destructors, and the rest are numerical traits. pub trait LinalgScalar: - 'static - + Copy - + Zero - + One - + Add - + Sub - + Mul - + Div + 'static + Copy + Zero + One + Add + Sub + Mul + Div { } -impl LinalgScalar for T where - T: 'static - + Copy - + Zero - + One - + Add - + Sub - + Mul - + Div -{ -} +impl LinalgScalar for T where T: 'static + Copy + Zero + One + Add + Sub + Mul + Div +{} /// Floating-point element types `f32` and `f64`. /// @@ -78,4 +62,3 @@ pub trait NdFloat: impl NdFloat for f32 {} #[cfg(feature = "std")] impl NdFloat for f64 {} - diff --git a/src/linspace.rs b/src/linspace.rs index 513044e00..411c480db 100644 --- a/src/linspace.rs +++ b/src/linspace.rs @@ -11,7 +11,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, @@ -19,13 +20,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 { @@ -37,18 +38,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 { @@ -72,8 +74,7 @@ impl ExactSizeIterator for Linspace where Linspace: Iterator {} /// **Panics** if converting `n - 1` to type `F` fails. #[inline] pub fn linspace(a: F, b: F, n: usize) -> Linspace -where - F: Float, +where F: Float { let step = if n > 1 { let num_steps = F::from(n - 1).expect("Converting number of steps to `A` must not fail."); @@ -100,8 +101,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 53be034b5..6f8de885d 100644 --- a/src/logspace.rs +++ b/src/logspace.rs @@ -11,7 +11,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, @@ -21,13 +22,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 { @@ -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 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 { @@ -78,8 +80,7 @@ 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, +where F: Float { let step = if n > 1 { let num_steps = F::from(n - 1).expect("Converting number of steps to `A` must not fail."); @@ -98,12 +99,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; @@ -121,7 +124,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))); @@ -136,7 +140,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))); diff --git a/src/low_level_util.rs b/src/low_level_util.rs index b61b06f0d..5a615a187 100644 --- a/src/low_level_util.rs +++ b/src/low_level_util.rs @@ -6,7 +6,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - /// Guard value that will abort if it is dropped. /// To defuse, this value must be forgotten before the end of the scope. /// @@ -14,24 +13,28 @@ #[must_use] pub(crate) struct AbortIfPanic(pub(crate) &'static &'static str); -impl AbortIfPanic { +impl AbortIfPanic +{ /// Defuse the AbortIfPanic guard. This *must* be done when finished. #[inline] - pub(crate) fn defuse(self) { + pub(crate) fn defuse(self) + { std::mem::forget(self); } } -impl Drop for AbortIfPanic { +impl Drop for AbortIfPanic +{ // The compiler should be able to remove this, if it can see through that there // is no panic in the code section. - fn drop(&mut self) { - #[cfg(feature="std")] + fn drop(&mut self) + { + #[cfg(feature = "std")] { eprintln!("ndarray: panic in no-panic section, aborting: {}", self.0); std::process::abort() } - #[cfg(not(feature="std"))] + #[cfg(not(feature = "std"))] { // no-std uses panic-in-panic (should abort) panic!("ndarray: panic in no-panic section, bailing out: {}", self.0); diff --git a/src/macro_utils.rs b/src/macro_utils.rs index 0480b7c91..75360de37 100644 --- a/src/macro_utils.rs +++ b/src/macro_utils.rs @@ -9,7 +9,7 @@ macro_rules! copy_and_clone { }; ($type_:ty) => { copy_and_clone!{ [] $type_ } - } + }; } macro_rules! clone_bounds { @@ -38,7 +38,7 @@ macro_rules! clone_bounds { /// debug assertions are enabled). #[cfg(debug_assertions)] macro_rules! ndassert { - ($e:expr, $($t:tt)*) => { assert!($e, $($t)*) } + ($e:expr, $($t:tt)*) => { assert!($e, $($t)*) }; } #[cfg(not(debug_assertions))] diff --git a/src/math_cell.rs b/src/math_cell.rs index f0f8da40b..6ed1ed71f 100644 --- a/src/math_cell.rs +++ b/src/math_cell.rs @@ -1,4 +1,3 @@ - use std::cell::Cell; use std::cmp::Ordering; use std::fmt; @@ -14,87 +13,119 @@ use std::ops::{Deref, DerefMut}; #[derive(Default)] pub struct MathCell(Cell); -impl MathCell { +impl MathCell +{ /// Create a new cell with the given value #[inline(always)] - pub const fn new(value: T) -> Self { MathCell(Cell::new(value)) } + pub const fn new(value: T) -> Self + { + MathCell(Cell::new(value)) + } /// Return the inner value - pub fn into_inner(self) -> T { Cell::into_inner(self.0) } + pub fn into_inner(self) -> T + { + Cell::into_inner(self.0) + } /// Swap value with another cell - pub fn swap(&self, other: &Self) { + pub fn swap(&self, other: &Self) + { Cell::swap(&self.0, &other.0) } } -impl Deref for MathCell { +impl Deref for MathCell +{ type Target = Cell; #[inline(always)] - fn deref(&self) -> &Self::Target { &self.0 } + fn deref(&self) -> &Self::Target + { + &self.0 + } } -impl DerefMut for MathCell { +impl DerefMut for MathCell +{ #[inline(always)] - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } + fn deref_mut(&mut self) -> &mut Self::Target + { + &mut self.0 + } } impl Clone for MathCell - where T: Copy +where T: Copy { - fn clone(&self) -> Self { + fn clone(&self) -> Self + { MathCell::new(self.get()) } } impl PartialEq for MathCell - where T: Copy + PartialEq +where T: Copy + PartialEq { - fn eq(&self, rhs: &Self) -> bool { + fn eq(&self, rhs: &Self) -> bool + { self.get() == rhs.get() } } -impl Eq for MathCell - where T: Copy + Eq -{ } +impl Eq for MathCell where T: Copy + Eq {} impl PartialOrd for MathCell - where T: Copy + PartialOrd +where T: Copy + PartialOrd { - fn partial_cmp(&self, rhs: &Self) -> Option { + fn partial_cmp(&self, rhs: &Self) -> Option + { self.get().partial_cmp(&rhs.get()) } - fn lt(&self, rhs: &Self) -> bool { self.get().lt(&rhs.get()) } - fn le(&self, rhs: &Self) -> bool { self.get().le(&rhs.get()) } - fn gt(&self, rhs: &Self) -> bool { self.get().gt(&rhs.get()) } - fn ge(&self, rhs: &Self) -> bool { self.get().ge(&rhs.get()) } + fn lt(&self, rhs: &Self) -> bool + { + self.get().lt(&rhs.get()) + } + fn le(&self, rhs: &Self) -> bool + { + self.get().le(&rhs.get()) + } + fn gt(&self, rhs: &Self) -> bool + { + self.get().gt(&rhs.get()) + } + fn ge(&self, rhs: &Self) -> bool + { + self.get().ge(&rhs.get()) + } } impl Ord for MathCell - where T: Copy + Ord +where T: Copy + Ord { - fn cmp(&self, rhs: &Self) -> Ordering { + fn cmp(&self, rhs: &Self) -> Ordering + { self.get().cmp(&rhs.get()) } } impl fmt::Debug for MathCell - where T: Copy + fmt::Debug +where T: Copy + fmt::Debug { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result + { self.get().fmt(f) } } - #[cfg(test)] -mod tests { +mod tests +{ use super::MathCell; #[test] - fn test_basic() { + fn test_basic() + { let c = &MathCell::new(0); c.set(1); assert_eq!(c.get(), 1); diff --git a/src/numeric/impl_float_maths.rs b/src/numeric/impl_float_maths.rs index 4b3208800..54fed49c2 100644 --- a/src/numeric/impl_float_maths.rs +++ b/src/numeric/impl_float_maths.rs @@ -137,7 +137,8 @@ where /// Square (two powers) of each element. #[must_use = "method returns a new array and does not mutate the original value"] - pub fn pow2(&self) -> Array { + pub fn pow2(&self) -> Array + { self.mapv(|v: A| v * v) } } @@ -161,7 +162,8 @@ where /// # Panics /// /// Panics if `!(min <= max)`. - pub fn clamp(&self, min: A, max: A) -> Array { + pub fn clamp(&self, min: A, max: A) -> Array + { assert!(min <= max, "min must be less than or equal to max"); self.mapv(|a| num_traits::clamp(a, min.clone(), max.clone())) } diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 55cd0cdfe..ca6f24bbe 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -30,8 +30,7 @@ where /// assert_eq!(a.sum(), 10.); /// ``` pub fn sum(&self) -> A - where - A: Clone + Add + num_traits::Zero, + where A: Clone + Add + num_traits::Zero { if let Some(slc) = self.as_slice_memory_order() { return numeric_util::unrolled_fold(slc, A::zero, A::add); @@ -50,10 +49,9 @@ where /// Return the sum of all elements in the array. /// /// *This method has been renamed to `.sum()`* - #[deprecated(note="renamed to `sum`", since="0.15.0")] + #[deprecated(note = "renamed to `sum`", since = "0.15.0")] pub fn scalar_sum(&self) -> A - where - A: Clone + Add + num_traits::Zero, + where A: Clone + Add + num_traits::Zero { self.sum() } @@ -72,15 +70,13 @@ where /// /// [arithmetic mean]: https://en.wikipedia.org/wiki/Arithmetic_mean pub fn mean(&self) -> Option - where - A: Clone + FromPrimitive + Add + Div + Zero, + where A: Clone + FromPrimitive + Add + Div + Zero { let n_elements = self.len(); if n_elements == 0 { None } else { - let n_elements = A::from_usize(n_elements) - .expect("Converting number of elements to `A` must not fail."); + let n_elements = A::from_usize(n_elements).expect("Converting number of elements to `A` must not fail."); Some(self.sum() / n_elements) } } @@ -95,8 +91,7 @@ where /// assert_eq!(a.product(), 24.); /// ``` pub fn product(&self) -> A - where - A: Clone + Mul + num_traits::One, + where A: Clone + Mul + num_traits::One { if let Some(slc) = self.as_slice_memory_order() { return numeric_util::unrolled_fold(slc, A::one, A::mul); @@ -154,8 +149,7 @@ where #[track_caller] #[cfg(feature = "std")] pub fn var(&self, ddof: A) -> A - where - A: Float + FromPrimitive, + where A: Float + FromPrimitive { let zero = A::from_usize(0).expect("Converting 0 to `A` must not fail."); let n = A::from_usize(self.len()).expect("Converting length to `A` must not fail."); @@ -220,8 +214,7 @@ where #[track_caller] #[cfg(feature = "std")] pub fn std(&self, ddof: A) -> A - where - A: Float + FromPrimitive, + where A: Float + FromPrimitive { self.var(ddof).sqrt() } @@ -289,8 +282,7 @@ where if axis_length == 0 { None } else { - let axis_length = - A::from_usize(axis_length).expect("Converting axis length to `A` must not fail."); + let axis_length = A::from_usize(axis_length).expect("Converting axis length to `A` must not fail."); let sum = self.sum_axis(axis); Some(sum / aview0(&axis_length)) } diff --git a/src/numeric_util.rs b/src/numeric_util.rs index b06850fd0..9d5ce66c5 100644 --- a/src/numeric_util.rs +++ b/src/numeric_util.rs @@ -20,16 +20,8 @@ where // eightfold unrolled so that floating point can be vectorized // (even with strict floating point accuracy semantics) let mut acc = init(); - let (mut p0, mut p1, mut p2, mut p3, mut p4, mut p5, mut p6, mut p7) = ( - init(), - init(), - init(), - init(), - init(), - init(), - init(), - init(), - ); + let (mut p0, mut p1, mut p2, mut p3, mut p4, mut p5, mut p6, mut p7) = + (init(), init(), init(), init(), init(), init(), init(), init()); while xs.len() >= 8 { p0 = f(p0, xs[0].clone()); p1 = f(p1, xs[1].clone()); @@ -62,8 +54,7 @@ where /// /// `xs` and `ys` must be the same length pub fn unrolled_dot(xs: &[A], ys: &[A]) -> A -where - A: LinalgScalar, +where A: LinalgScalar { debug_assert_eq!(xs.len(), ys.len()); // eightfold unrolled so that floating point can be vectorized @@ -72,16 +63,8 @@ where let mut xs = &xs[..len]; let mut ys = &ys[..len]; let mut sum = A::zero(); - let (mut p0, mut p1, mut p2, mut p3, mut p4, mut p5, mut p6, mut p7) = ( - A::zero(), - A::zero(), - A::zero(), - A::zero(), - A::zero(), - A::zero(), - A::zero(), - A::zero(), - ); + let (mut p0, mut p1, mut p2, mut p3, mut p4, mut p5, mut p6, mut p7) = + (A::zero(), A::zero(), A::zero(), A::zero(), A::zero(), A::zero(), A::zero(), A::zero()); while xs.len() >= 8 { p0 = p0 + xs[0] * ys[0]; p1 = p1 + xs[1] * ys[1]; @@ -113,8 +96,7 @@ where /// /// `xs` and `ys` must be the same length pub fn unrolled_eq(xs: &[A], ys: &[B]) -> bool -where - A: PartialEq, +where A: PartialEq { debug_assert_eq!(xs.len(), ys.len()); // eightfold unrolled for performance (this is not done by llvm automatically) diff --git a/src/order.rs b/src/order.rs index e8d9c8db1..a52a32e2c 100644 --- a/src/order.rs +++ b/src/order.rs @@ -1,4 +1,3 @@ - /// Array order /// /// Order refers to indexing order, or how a linear sequence is translated @@ -31,14 +30,16 @@ /// or "Fortran" order. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[non_exhaustive] -pub enum Order { +pub enum Order +{ /// Row major or "C" order RowMajor, /// Column major or "F" order ColumnMajor, } -impl Order { +impl Order +{ /// "C" is an alias for row major ordering pub const C: Order = Order::RowMajor; @@ -47,7 +48,8 @@ impl Order { /// Return true if input is Order::RowMajor, false otherwise #[inline] - pub fn is_row_major(self) -> bool { + pub fn is_row_major(self) -> bool + { match self { Order::RowMajor => true, Order::ColumnMajor => false, @@ -56,25 +58,33 @@ impl Order { /// Return true if input is Order::ColumnMajor, false otherwise #[inline] - pub fn is_column_major(self) -> bool { + pub fn is_column_major(self) -> bool + { !self.is_row_major() } /// Return Order::RowMajor if the input is true, Order::ColumnMajor otherwise #[inline] - pub fn row_major(row_major: bool) -> Order { - if row_major { Order::RowMajor } else { Order::ColumnMajor } + pub fn row_major(row_major: bool) -> Order + { + if row_major { + Order::RowMajor + } else { + Order::ColumnMajor + } } /// Return Order::ColumnMajor if the input is true, Order::RowMajor otherwise #[inline] - pub fn column_major(column_major: bool) -> Order { + pub fn column_major(column_major: bool) -> Order + { Self::row_major(!column_major) } /// Return the transpose: row major becomes column major and vice versa. #[inline] - pub fn transpose(self) -> Order { + pub fn transpose(self) -> Order + { match self { Order::RowMajor => Order::ColumnMajor, Order::ColumnMajor => Order::RowMajor, diff --git a/src/parallel/impl_par_methods.rs b/src/parallel/impl_par_methods.rs index ed0dcad7a..b3fbdedc8 100644 --- a/src/parallel/impl_par_methods.rs +++ b/src/parallel/impl_par_methods.rs @@ -1,9 +1,9 @@ -use crate::{Array, ArrayBase, DataMut, Dimension, IntoNdProducer, NdProducer, Zip}; use crate::AssignElem; +use crate::{Array, ArrayBase, DataMut, Dimension, IntoNdProducer, NdProducer, Zip}; -use crate::parallel::prelude::*; -use crate::parallel::par::ParallelSplits; use super::send_producer::SendProducer; +use crate::parallel::par::ParallelSplits; +use crate::parallel::prelude::*; use crate::partial::Partial; @@ -22,8 +22,7 @@ where /// /// Elements are visited in arbitrary order. pub fn par_map_inplace(&mut self, f: F) - where - F: Fn(&mut A) + Sync + Send, + where F: Fn(&mut A) + Sync + Send { self.view_mut().into_par_iter().for_each(f) } @@ -230,7 +229,7 @@ macro_rules! zip_impl { ); } )+ - } + }; } zip_impl! { diff --git a/src/parallel/into_impls.rs b/src/parallel/into_impls.rs index c1a5388fd..75bded7de 100644 --- a/src/parallel/into_impls.rs +++ b/src/parallel/into_impls.rs @@ -11,7 +11,8 @@ where { type Item = &'a A; type Iter = Parallel>; - fn into_par_iter(self) -> Self::Iter { + fn into_par_iter(self) -> Self::Iter + { self.view().into_par_iter() } } @@ -25,7 +26,8 @@ where { type Item = &'a A; type Iter = Parallel>; - fn into_par_iter(self) -> Self::Iter { + fn into_par_iter(self) -> Self::Iter + { self.view().into_par_iter() } } @@ -38,7 +40,8 @@ where { type Item = &'a mut A; type Iter = Parallel>; - fn into_par_iter(self) -> Self::Iter { + fn into_par_iter(self) -> Self::Iter + { self.view_mut().into_par_iter() } } @@ -52,7 +55,8 @@ where { type Item = &'a mut A; type Iter = Parallel>; - fn into_par_iter(self) -> Self::Iter { + fn into_par_iter(self) -> Self::Iter + { self.view_mut().into_par_iter() } } diff --git a/src/parallel/mod.rs b/src/parallel/mod.rs index 552515f11..0c84baa91 100644 --- a/src/parallel/mod.rs +++ b/src/parallel/mod.rs @@ -118,28 +118,20 @@ //! ``` #[allow(unused_imports)] // used by rustdoc links -use crate::{ - ArrayBase, - Array, - ArcArray, - ArrayView, - ArrayViewMut, - Zip, -}; +use crate::iter::{AxisChunksIter, AxisChunksIterMut, AxisIter, AxisIterMut}; #[allow(unused_imports)] // used by rustdoc links -use crate::iter::{ - AxisIter, - AxisIterMut, - AxisChunksIter, - AxisChunksIterMut, -}; +use crate::{ArcArray, Array, ArrayBase, ArrayView, ArrayViewMut, Zip}; /// Into- traits for creating parallelized iterators and/or using [`par_azip!`] -pub mod prelude { +pub mod prelude +{ #[doc(no_inline)] pub use rayon::prelude::{ - IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, - IntoParallelRefMutIterator, ParallelIterator, + IndexedParallelIterator, + IntoParallelIterator, + IntoParallelRefIterator, + IntoParallelRefMutIterator, + ParallelIterator, }; pub use super::par_azip; diff --git a/src/parallel/par.rs b/src/parallel/par.rs index cc905b5cf..b59af4c8e 100644 --- a/src/parallel/par.rs +++ b/src/parallel/par.rs @@ -13,13 +13,14 @@ use crate::iter::AxisChunksIter; use crate::iter::AxisChunksIterMut; use crate::iter::AxisIter; use crate::iter::AxisIterMut; +use crate::split_at::SplitPreference; use crate::Dimension; use crate::{ArrayView, ArrayViewMut}; -use crate::split_at::SplitPreference; /// Parallel iterator wrapper. #[derive(Copy, Clone, Debug)] -pub struct Parallel { +pub struct Parallel +{ iter: I, min_len: usize, } @@ -114,7 +115,7 @@ macro_rules! par_iter_wrapper { } } - } + }; } par_iter_wrapper!(AxisIter, [Sync]); @@ -216,7 +217,7 @@ macro_rules! par_iter_view_wrapper { } } - } + }; } par_iter_view_wrapper!(ArrayView, [Sync]); @@ -296,7 +297,7 @@ macro_rules! zip_impl { } } )+ - } + }; } zip_impl! { @@ -309,69 +310,71 @@ zip_impl! { } impl Parallel> -where - D: Dimension, +where D: Dimension { /// Sets the minimum number of elements desired to process in each job. This will not be /// split any smaller than this length, but of course a producer could already be smaller /// to begin with. /// /// ***Panics*** if `min_len` is zero. - pub fn with_min_len(self, min_len: usize) -> Self { + pub fn with_min_len(self, min_len: usize) -> Self + { assert_ne!(min_len, 0, "Minimum number of elements must at least be one to avoid splitting off empty tasks."); - Self { - min_len, - ..self - } + Self { min_len, ..self } } } /// A parallel iterator (unindexed) that produces the splits of the array /// or producer `P`. -pub(crate) struct ParallelSplits

{ +pub(crate) struct ParallelSplits

+{ pub(crate) iter: P, pub(crate) max_splits: usize, } impl

ParallelIterator for ParallelSplits

- where P: SplitPreference + Send, +where P: SplitPreference + Send { type Item = P; fn drive_unindexed(self, consumer: C) -> C::Result - where C: UnindexedConsumer + where C: UnindexedConsumer { bridge_unindexed(self, consumer) } - fn opt_len(&self) -> Option { + fn opt_len(&self) -> Option + { None } } impl

UnindexedProducer for ParallelSplits

- where P: SplitPreference + Send, +where P: SplitPreference + Send { type Item = P; - fn split(self) -> (Self, Option) { + fn split(self) -> (Self, Option) + { if self.max_splits == 0 || !self.iter.can_split() { - return (self, None) + return (self, None); } let (a, b) = self.iter.split(); - (ParallelSplits { - iter: a, - max_splits: self.max_splits - 1, - }, - Some(ParallelSplits { - iter: b, - max_splits: self.max_splits - 1, - })) + ( + ParallelSplits { + iter: a, + max_splits: self.max_splits - 1, + }, + Some(ParallelSplits { + iter: b, + max_splits: self.max_splits - 1, + }), + ) } fn fold_with(self, folder: Fold) -> Fold - where Fold: Folder, + where Fold: Folder { folder.consume(self.iter) } diff --git a/src/parallel/send_producer.rs b/src/parallel/send_producer.rs index 5324b3490..ecfb77af0 100644 --- a/src/parallel/send_producer.rs +++ b/src/parallel/send_producer.rs @@ -1,32 +1,44 @@ - use crate::imp_prelude::*; use crate::{Layout, NdProducer}; use std::ops::{Deref, DerefMut}; /// An NdProducer that is unconditionally `Send`. #[repr(transparent)] -pub(crate) struct SendProducer { - inner: T +pub(crate) struct SendProducer +{ + inner: T, } -impl SendProducer { +impl SendProducer +{ /// Create an unconditionally `Send` ndproducer from the producer - pub(crate) unsafe fn new(producer: T) -> Self { Self { inner: producer } } + pub(crate) unsafe fn new(producer: T) -> Self + { + Self { inner: producer } + } } -unsafe impl

Send for SendProducer

{ } +unsafe impl

Send for SendProducer

{} -impl

Deref for SendProducer

{ +impl

Deref for SendProducer

+{ type Target = P; - fn deref(&self) -> &P { &self.inner } + fn deref(&self) -> &P + { + &self.inner + } } -impl

DerefMut for SendProducer

{ - fn deref_mut(&mut self) -> &mut P { &mut self.inner } +impl

DerefMut for SendProducer

+{ + fn deref_mut(&mut self) -> &mut P + { + &mut self.inner + } } impl NdProducer for SendProducer

- where P: NdProducer, +where P: NdProducer { type Item = P::Item; type Dim = P::Dim; @@ -36,48 +48,56 @@ impl NdProducer for SendProducer

private_impl! {} #[inline(always)] - fn raw_dim(&self) -> Self::Dim { + fn raw_dim(&self) -> Self::Dim + { self.inner.raw_dim() } #[inline(always)] - fn equal_dim(&self, dim: &Self::Dim) -> bool { + fn equal_dim(&self, dim: &Self::Dim) -> bool + { self.inner.equal_dim(dim) } #[inline(always)] - fn as_ptr(&self) -> Self::Ptr { + fn as_ptr(&self) -> Self::Ptr + { self.inner.as_ptr() } #[inline(always)] - fn layout(&self) -> Layout { + fn layout(&self) -> Layout + { self.inner.layout() } #[inline(always)] - unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { + unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item + { self.inner.as_ref(ptr) } #[inline(always)] - unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { + unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr + { self.inner.uget_ptr(i) } #[inline(always)] - fn stride_of(&self, axis: Axis) -> Self::Stride { + fn stride_of(&self, axis: Axis) -> Self::Stride + { self.inner.stride_of(axis) } #[inline(always)] - fn contiguous_stride(&self) -> Self::Stride { + fn contiguous_stride(&self) -> Self::Stride + { self.inner.contiguous_stride() } - fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) + { let (a, b) = self.inner.split_at(axis, index); (Self { inner: a }, Self { inner: b }) } } - diff --git a/src/partial.rs b/src/partial.rs index a835081c4..99aba75a8 100644 --- a/src/partial.rs +++ b/src/partial.rs @@ -12,14 +12,16 @@ use std::ptr; /// it is the owner of the elements, but not the allocation, /// and will drop the elements on drop. #[must_use] -pub(crate) struct Partial { +pub(crate) struct Partial +{ /// Data pointer ptr: *mut T, /// Current length pub(crate) len: usize, } -impl Partial { +impl Partial +{ /// Create an empty partial for this data pointer /// /// ## Safety @@ -29,25 +31,29 @@ impl Partial { /// the `len` elements following it valid. /// /// The Partial has an accessible length field which must only be modified in trusted code. - pub(crate) unsafe fn new(ptr: *mut T) -> Self { - Self { - ptr, - len: 0, - } + pub(crate) unsafe fn new(ptr: *mut T) -> Self + { + Self { ptr, len: 0 } } #[cfg(feature = "rayon")] - pub(crate) fn stub() -> Self { - Self { len: 0, ptr: ptr::null_mut() } + pub(crate) fn stub() -> Self + { + Self { + len: 0, + ptr: ptr::null_mut(), + } } #[cfg(feature = "rayon")] - pub(crate) fn is_stub(&self) -> bool { + pub(crate) fn is_stub(&self) -> bool + { self.ptr.is_null() } /// Release Partial's ownership of the written elements, and return the current length - pub(crate) fn release_ownership(mut self) -> usize { + pub(crate) fn release_ownership(mut self) -> usize + { let ret = self.len; self.len = 0; ret @@ -56,7 +62,8 @@ impl Partial { #[cfg(feature = "rayon")] /// Merge if they are in order (left to right) and contiguous. /// Skips merge if T does not need drop. - pub(crate) fn try_merge(mut left: Self, right: Self) -> Self { + pub(crate) fn try_merge(mut left: Self, right: Self) -> Self + { if !std::mem::needs_drop::() { return left; } @@ -75,10 +82,12 @@ impl Partial { } } -unsafe impl Send for Partial where T: Send { } +unsafe impl Send for Partial where T: Send {} -impl Drop for Partial { - fn drop(&mut self) { +impl Drop for Partial +{ + fn drop(&mut self) + { if !self.ptr.is_null() { unsafe { ptr::drop_in_place(alloc::slice::from_raw_parts_mut(self.ptr, self.len)); diff --git a/src/prelude.rs b/src/prelude.rs index a25fc8780..acf39da1a 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -18,9 +18,7 @@ //! ``` #[doc(no_inline)] -pub use crate::{ - ArcArray, Array, ArrayBase, ArrayView, ArrayViewMut, CowArray, RawArrayView, RawArrayViewMut, -}; +pub use crate::{ArcArray, Array, ArrayBase, ArrayView, ArrayViewMut, CowArray, RawArrayView, RawArrayViewMut}; #[doc(no_inline)] pub use crate::{Axis, Dim, Dimension}; @@ -29,14 +27,18 @@ pub use crate::{Axis, Dim, Dimension}; pub use crate::{Array0, Array1, Array2, Array3, Array4, Array5, Array6, ArrayD}; #[doc(no_inline)] -pub use crate::{ - ArrayView0, ArrayView1, ArrayView2, ArrayView3, ArrayView4, ArrayView5, ArrayView6, ArrayViewD, -}; +pub use crate::{ArrayView0, ArrayView1, ArrayView2, ArrayView3, ArrayView4, ArrayView5, ArrayView6, ArrayViewD}; #[doc(no_inline)] pub use crate::{ - ArrayViewMut0, ArrayViewMut1, ArrayViewMut2, ArrayViewMut3, ArrayViewMut4, ArrayViewMut5, - ArrayViewMut6, ArrayViewMutD, + ArrayViewMut0, + ArrayViewMut1, + ArrayViewMut2, + ArrayViewMut3, + ArrayViewMut4, + ArrayViewMut5, + ArrayViewMut6, + ArrayViewMutD, }; #[doc(no_inline)] diff --git a/src/private.rs b/src/private.rs index 1fa779ff0..9dade0c48 100644 --- a/src/private.rs +++ b/src/private.rs @@ -13,7 +13,7 @@ macro_rules! private_decl { /// impossible to implement outside the crate. #[doc(hidden)] fn __private__(&self) -> crate::private::PrivateMarker; - } + }; } macro_rules! private_impl { diff --git a/src/shape_builder.rs b/src/shape_builder.rs index 877102b5c..8b25f71e7 100644 --- a/src/shape_builder.rs +++ b/src/shape_builder.rs @@ -1,12 +1,13 @@ use crate::dimension::IntoDimension; -use crate::Dimension; use crate::order::Order; +use crate::Dimension; /// A contiguous array shape of n dimensions. /// /// Either c- or f- memory ordered (*c* a.k.a *row major* is the default). #[derive(Copy, Clone, Debug)] -pub struct Shape { +pub struct Shape +{ /// Shape (axis lengths) pub(crate) dim: D, /// Strides can only be C or F here @@ -16,36 +17,41 @@ pub struct Shape { #[derive(Copy, Clone, Debug)] pub(crate) enum Contiguous {} -impl Shape { - pub(crate) fn is_c(&self) -> bool { +impl Shape +{ + pub(crate) fn is_c(&self) -> bool + { matches!(self.strides, Strides::C) } } /// An array shape of n dimensions in c-order, f-order or custom strides. #[derive(Copy, Clone, Debug)] -pub struct StrideShape { +pub struct StrideShape +{ pub(crate) dim: D, pub(crate) strides: Strides, } impl StrideShape -where - D: Dimension, +where D: Dimension { /// Return a reference to the dimension - pub fn raw_dim(&self) -> &D { + pub fn raw_dim(&self) -> &D + { &self.dim } /// Return the size of the shape in number of elements - pub fn size(&self) -> usize { + pub fn size(&self) -> usize + { self.dim.size() } } /// Stride description #[derive(Copy, Clone, Debug)] -pub(crate) enum Strides { +pub(crate) enum Strides +{ /// Row-major ("C"-order) C, /// Column-major ("F"-order) @@ -54,11 +60,11 @@ pub(crate) enum Strides { Custom(D), } -impl Strides { +impl Strides +{ /// Return strides for `dim` (computed from dimension if c/f, else return the custom stride) pub(crate) fn strides_for_dim(self, dim: &D) -> D - where - D: Dimension, + where D: Dimension { match self { Strides::C => dim.default_strides(), @@ -76,7 +82,8 @@ impl Strides { } } - pub(crate) fn is_custom(&self) -> bool { + pub(crate) fn is_custom(&self) -> bool + { matches!(*self, Strides::Custom(_)) } } @@ -86,7 +93,8 @@ impl Strides { /// /// This trait is used together with array constructor methods like /// `Array::from_shape_vec`. -pub trait ShapeBuilder { +pub trait ShapeBuilder +{ type Dim: Dimension; type Strides; @@ -97,11 +105,11 @@ pub trait ShapeBuilder { } impl From for Shape -where - D: Dimension, +where D: Dimension { /// Create a `Shape` from `dimension`, using the default memory layout. - fn from(dimension: D) -> Shape { + fn from(dimension: D) -> Shape + { dimension.into_shape_with_order() } } @@ -111,7 +119,8 @@ where D: Dimension, T: ShapeBuilder, { - fn from(value: T) -> Self { + fn from(value: T) -> Self + { let shape = value.into_shape_with_order(); let st = if shape.is_c() { Strides::C } else { Strides::F }; StrideShape { @@ -122,49 +131,55 @@ where } impl ShapeBuilder for T -where - T: IntoDimension, +where T: IntoDimension { type Dim = T::Dim; type Strides = T; - fn into_shape_with_order(self) -> Shape { + fn into_shape_with_order(self) -> Shape + { Shape { dim: self.into_dimension(), strides: Strides::C, } } - fn f(self) -> Shape { + fn f(self) -> Shape + { self.set_f(true) } - fn set_f(self, is_f: bool) -> Shape { + fn set_f(self, is_f: bool) -> Shape + { self.into_shape_with_order().set_f(is_f) } - fn strides(self, st: T) -> StrideShape { + fn strides(self, st: T) -> StrideShape + { self.into_shape_with_order().strides(st.into_dimension()) } } impl ShapeBuilder for Shape -where - D: Dimension, +where D: Dimension { type Dim = D; type Strides = D; - fn into_shape_with_order(self) -> Shape { + fn into_shape_with_order(self) -> Shape + { self } - fn f(self) -> Self { + fn f(self) -> Self + { self.set_f(true) } - fn set_f(mut self, is_f: bool) -> Self { + fn set_f(mut self, is_f: bool) -> Self + { self.strides = if !is_f { Strides::C } else { Strides::F }; self } - fn strides(self, st: D) -> StrideShape { + fn strides(self, st: D) -> StrideShape + { StrideShape { dim: self.dim, strides: Strides::Custom(st), @@ -173,20 +188,20 @@ where } impl Shape -where - D: Dimension, +where D: Dimension { /// Return a reference to the dimension - pub fn raw_dim(&self) -> &D { + pub fn raw_dim(&self) -> &D + { &self.dim } /// Return the size of the shape in number of elements - pub fn size(&self) -> usize { + pub fn size(&self) -> usize + { self.dim.size() } } - /// Array shape argument with optional order parameter /// /// Shape or array dimension argument, with optional [`Order`] parameter. @@ -195,23 +210,30 @@ where /// (optionally) an ordering argument. /// /// See for example [`.to_shape()`](crate::ArrayBase::to_shape). -pub trait ShapeArg { +pub trait ShapeArg +{ type Dim: Dimension; fn into_shape_and_order(self) -> (Self::Dim, Option); } -impl ShapeArg for T where T: IntoDimension { +impl ShapeArg for T +where T: IntoDimension +{ type Dim = T::Dim; - fn into_shape_and_order(self) -> (Self::Dim, Option) { + fn into_shape_and_order(self) -> (Self::Dim, Option) + { (self.into_dimension(), None) } } -impl ShapeArg for (T, Order) where T: IntoDimension { +impl ShapeArg for (T, Order) +where T: IntoDimension +{ type Dim = T::Dim; - fn into_shape_and_order(self) -> (Self::Dim, Option) { + fn into_shape_and_order(self) -> (Self::Dim, Option) + { (self.0.into_dimension(), Some(self.1)) } } diff --git a/src/slice.rs b/src/slice.rs index 14ab0dd67..9e6acc449 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -36,7 +36,8 @@ use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, Rang /// reverse order. It can also be created with `Slice::from(a..).step_by(-1)`. /// The Python equivalent is `[a::-1]`. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct Slice { +pub struct Slice +{ /// start index; negative are counted from the back of the axis pub start: isize, /// end index; negative are counted from the back of the axis; when not present @@ -46,7 +47,8 @@ pub struct Slice { pub step: isize, } -impl Slice { +impl Slice +{ /// Create a new `Slice` with the given extents. /// /// See also the `From` impls, converting from ranges; for example @@ -54,7 +56,8 @@ impl Slice { /// /// `step` must be nonzero. /// (This method checks with a debug assertion that `step` is not zero.) - pub fn new(start: isize, end: Option, step: isize) -> Slice { + pub fn new(start: isize, end: Option, step: isize) -> Slice + { debug_assert_ne!(step, 0, "Slice::new: step must be nonzero"); Slice { start, end, step } } @@ -65,7 +68,8 @@ impl Slice { /// `step` must be nonzero. /// (This method checks with a debug assertion that `step` is not zero.) #[inline] - pub fn step_by(self, step: isize) -> Self { + pub fn step_by(self, step: isize) -> Self + { debug_assert_ne!(step, 0, "Slice::step_by: step must be nonzero"); Slice { step: self.step * step, @@ -109,11 +113,13 @@ pub struct NewAxis; /// with `SliceInfoElem::from(NewAxis)`. The Python equivalent is /// `[np.newaxis]`. The macro equivalent is `s![NewAxis]`. #[derive(Debug, PartialEq, Eq, Hash)] -pub enum SliceInfoElem { +pub enum SliceInfoElem +{ /// A range with step size. `end` is an exclusive index. Negative `start` /// or `end` indexes are counted from the back of the axis. If `end` is /// `None`, the slice extends to the end of the axis. - Slice { + Slice + { /// start index; negative are counted from the back of the axis start: isize, /// end index; negative are counted from the back of the axis; when not present @@ -130,25 +136,31 @@ pub enum SliceInfoElem { copy_and_clone! {SliceInfoElem} -impl SliceInfoElem { +impl SliceInfoElem +{ /// Returns `true` if `self` is a `Slice` value. - pub fn is_slice(&self) -> bool { + pub fn is_slice(&self) -> bool + { matches!(self, SliceInfoElem::Slice { .. }) } /// Returns `true` if `self` is an `Index` value. - pub fn is_index(&self) -> bool { + pub fn is_index(&self) -> bool + { matches!(self, SliceInfoElem::Index(_)) } /// Returns `true` if `self` is a `NewAxis` value. - pub fn is_new_axis(&self) -> bool { + pub fn is_new_axis(&self) -> bool + { matches!(self, SliceInfoElem::NewAxis) } } -impl fmt::Display for SliceInfoElem { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl fmt::Display for SliceInfoElem +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { match *self { SliceInfoElem::Index(index) => write!(f, "{}", index)?, SliceInfoElem::Slice { start, end, step } => { @@ -236,9 +248,11 @@ impl_slice_variant_from_range!(SliceInfoElem, SliceInfoElem::Slice, isize); impl_slice_variant_from_range!(SliceInfoElem, SliceInfoElem::Slice, usize); impl_slice_variant_from_range!(SliceInfoElem, SliceInfoElem::Slice, i32); -impl From for Slice { +impl From for Slice +{ #[inline] - fn from(_: RangeFull) -> Slice { + fn from(_: RangeFull) -> Slice + { Slice { start: 0, end: None, @@ -247,9 +261,11 @@ impl From for Slice { } } -impl From for SliceInfoElem { +impl From for SliceInfoElem +{ #[inline] - fn from(_: RangeFull) -> SliceInfoElem { + fn from(_: RangeFull) -> SliceInfoElem + { SliceInfoElem::Slice { start: 0, end: None, @@ -258,9 +274,11 @@ impl From for SliceInfoElem { } } -impl From for SliceInfoElem { +impl From for SliceInfoElem +{ #[inline] - fn from(s: Slice) -> SliceInfoElem { + fn from(s: Slice) -> SliceInfoElem + { SliceInfoElem::Slice { start: s.start, end: s.end, @@ -283,9 +301,11 @@ impl_sliceinfoelem_from_index!(isize); impl_sliceinfoelem_from_index!(usize); impl_sliceinfoelem_from_index!(i32); -impl From for SliceInfoElem { +impl From for SliceInfoElem +{ #[inline] - fn from(_: NewAxis) -> SliceInfoElem { + fn from(_: NewAxis) -> SliceInfoElem + { SliceInfoElem::NewAxis } } @@ -297,7 +317,8 @@ impl From for SliceInfoElem { /// consistent with the `&[SliceInfoElem]` returned by `self.as_ref()` and that /// `self.as_ref()` always returns the same value when called multiple times. #[allow(clippy::missing_safety_doc)] // not implementable downstream -pub unsafe trait SliceArg: AsRef<[SliceInfoElem]> { +pub unsafe trait SliceArg: AsRef<[SliceInfoElem]> +{ /// Dimensionality of the output array. type OutDim: Dimension; @@ -317,11 +338,13 @@ where { type OutDim = T::OutDim; - fn in_ndim(&self) -> usize { + fn in_ndim(&self) -> usize + { T::in_ndim(self) } - fn out_ndim(&self) -> usize { + fn out_ndim(&self) -> usize + { T::out_ndim(self) } @@ -365,25 +388,30 @@ where { type OutDim = Dout; - fn in_ndim(&self) -> usize { + fn in_ndim(&self) -> usize + { self.in_ndim() } - fn out_ndim(&self) -> usize { + fn out_ndim(&self) -> usize + { self.out_ndim() } private_impl! {} } -unsafe impl SliceArg for [SliceInfoElem] { +unsafe impl SliceArg for [SliceInfoElem] +{ type OutDim = IxDyn; - fn in_ndim(&self) -> usize { + fn in_ndim(&self) -> usize + { self.iter().filter(|s| !s.is_new_axis()).count() } - fn out_ndim(&self) -> usize { + fn out_ndim(&self) -> usize + { self.iter().filter(|s| !s.is_index()).count() } @@ -401,7 +429,8 @@ unsafe impl SliceArg for [SliceInfoElem] { /// /// [`.slice()`]: crate::ArrayBase::slice #[derive(Debug)] -pub struct SliceInfo { +pub struct SliceInfo +{ in_dim: PhantomData, out_dim: PhantomData, indices: T, @@ -413,7 +442,8 @@ where Dout: Dimension, { type Target = T; - fn deref(&self) -> &Self::Target { + fn deref(&self) -> &Self::Target + { &self.indices } } @@ -453,10 +483,9 @@ where /// when called multiple times. #[doc(hidden)] pub unsafe fn new_unchecked( - indices: T, - in_dim: PhantomData, - out_dim: PhantomData, - ) -> SliceInfo { + indices: T, in_dim: PhantomData, out_dim: PhantomData, + ) -> SliceInfo + { if cfg!(debug_assertions) { check_dims_for_sliceinfo::(indices.as_ref()) .expect("`Din` and `Dout` must be consistent with `indices`."); @@ -478,7 +507,8 @@ where /// /// The caller must ensure `indices.as_ref()` always returns the same value /// when called multiple times. - pub unsafe fn new(indices: T) -> Result, ShapeError> { + pub unsafe fn new(indices: T) -> Result, ShapeError> + { check_dims_for_sliceinfo::(indices.as_ref())?; Ok(SliceInfo { in_dim: PhantomData, @@ -493,7 +523,8 @@ where /// If `Din` is a fixed-size dimension type, then this is equivalent to /// `Din::NDIM.unwrap()`. Otherwise, the value is calculated by iterating /// over the `SliceInfoElem` elements. - pub fn in_ndim(&self) -> usize { + pub fn in_ndim(&self) -> usize + { if let Some(ndim) = Din::NDIM { ndim } else { @@ -508,7 +539,8 @@ where /// If `Dout` is a fixed-size dimension type, then this is equivalent to /// `Dout::NDIM.unwrap()`. Otherwise, the value is calculated by iterating /// over the `SliceInfoElem` elements. - pub fn out_ndim(&self) -> usize { + pub fn out_ndim(&self) -> usize + { if let Some(ndim) = Dout::NDIM { ndim } else { @@ -524,9 +556,8 @@ where { type Error = ShapeError; - fn try_from( - indices: &'a [SliceInfoElem], - ) -> Result, ShapeError> { + fn try_from(indices: &'a [SliceInfoElem]) -> Result, ShapeError> + { unsafe { // This is okay because `&[SliceInfoElem]` always returns the same // value for `.as_ref()`. @@ -542,9 +573,8 @@ where { type Error = ShapeError; - fn try_from( - indices: Vec, - ) -> Result, Din, Dout>, ShapeError> { + fn try_from(indices: Vec) -> Result, Din, Dout>, ShapeError> + { unsafe { // This is okay because `Vec` always returns the same value for // `.as_ref()`. @@ -591,19 +621,20 @@ where Din: Dimension, Dout: Dimension, { - fn as_ref(&self) -> &[SliceInfoElem] { + fn as_ref(&self) -> &[SliceInfoElem] + { self.indices.as_ref() } } -impl<'a, T, Din, Dout> From<&'a SliceInfo> - for SliceInfo<&'a [SliceInfoElem], Din, Dout> +impl<'a, T, Din, Dout> From<&'a SliceInfo> for SliceInfo<&'a [SliceInfoElem], Din, Dout> where T: AsRef<[SliceInfoElem]>, Din: Dimension, Dout: Dimension, { - fn from(info: &'a SliceInfo) -> SliceInfo<&'a [SliceInfoElem], Din, Dout> { + fn from(info: &'a SliceInfo) -> SliceInfo<&'a [SliceInfoElem], Din, Dout> + { SliceInfo { in_dim: info.in_dim, out_dim: info.out_dim, @@ -626,7 +657,8 @@ where Din: Dimension, Dout: Dimension, { - fn clone(&self) -> Self { + fn clone(&self) -> Self + { SliceInfo { in_dim: PhantomData, out_dim: PhantomData, @@ -637,22 +669,21 @@ where /// Trait for determining dimensionality of input and output for [`s!`] macro. #[doc(hidden)] -pub trait SliceNextDim { +pub trait SliceNextDim +{ /// Number of dimensions that this slicing argument consumes in the input array. type InDim: Dimension; /// Number of dimensions that this slicing argument produces in the output array. type OutDim: Dimension; fn next_in_dim(&self, _: PhantomData) -> PhantomData<>::Output> - where - D: Dimension + DimAdd, + where D: Dimension + DimAdd { PhantomData } fn next_out_dim(&self, _: PhantomData) -> PhantomData<>::Output> - where - D: Dimension + DimAdd, + where D: Dimension + DimAdd { PhantomData } @@ -915,7 +946,8 @@ where { type Output = (ArrayViewMut<'a, A, I0::OutDim>,); - fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output { + fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output + { (view.slice_move(&self.0),) } @@ -977,7 +1009,8 @@ where { type Output = T::Output; - fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output { + fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output + { T::multi_slice_move(self, view) } diff --git a/src/split_at.rs b/src/split_at.rs index 50466afdf..4af1403c0 100644 --- a/src/split_at.rs +++ b/src/split_at.rs @@ -1,26 +1,30 @@ - use crate::imp_prelude::*; /// Arrays and similar that can be split along an axis -pub(crate) trait SplitAt { - fn split_at(self, axis: Axis, index: usize) -> (Self, Self) where Self: Sized; +pub(crate) trait SplitAt +{ + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) + where Self: Sized; } -pub(crate) trait SplitPreference : SplitAt { +pub(crate) trait SplitPreference: SplitAt +{ #[allow(dead_code)] // used only when Rayon support is enabled fn can_split(&self) -> bool; fn split_preference(&self) -> (Axis, usize); - fn split(self) -> (Self, Self) where Self: Sized { + fn split(self) -> (Self, Self) + where Self: Sized + { let (axis, index) = self.split_preference(); self.split_at(axis, index) } } impl SplitAt for D -where - D: Dimension, +where D: Dimension { - fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { + fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) + { let mut d1 = self; let mut d2 = d1.clone(); let i = axis.index(); @@ -32,18 +36,19 @@ where } impl<'a, A, D> SplitAt for ArrayViewMut<'a, A, D> - where D: Dimension +where D: Dimension { - fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) + { self.split_at(axis, index) } } - impl SplitAt for RawArrayViewMut - where D: Dimension +where D: Dimension { - fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) + { self.split_at(axis, index) } } diff --git a/src/stacking.rs b/src/stacking.rs index 16058f39d..120baad14 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -37,10 +37,7 @@ use crate::imp_prelude::*; /// ); /// # } /// ``` -pub fn stack( - axis: Axis, - arrays: &[ArrayView], -) -> Result, ShapeError> +pub fn stack(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> where A: Clone, D: Dimension, @@ -109,7 +106,7 @@ where Ok(res) } -#[deprecated(note="Use under the name stack instead.", since="0.15.0")] +#[deprecated(note = "Use under the name stack instead.", since = "0.15.0")] /// Stack arrays along the new axis. /// /// ***Errors*** if the arrays have mismatching shapes. @@ -134,10 +131,7 @@ where /// ); /// # } /// ``` -pub fn stack_new_axis( - axis: Axis, - arrays: &[ArrayView], -) -> Result, ShapeError> +pub fn stack_new_axis(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> where A: Clone, D: Dimension, @@ -292,9 +286,9 @@ macro_rules! concatenate { /// # } /// ``` #[macro_export] -#[deprecated(note="Use under the name stack instead.", since="0.15.0")] +#[deprecated(note = "Use under the name stack instead.", since = "0.15.0")] macro_rules! stack_new_axis { ($axis:expr, $( $array:expr ),+ ) => { $crate::stack_new_axis($axis, &[ $($crate::ArrayView::from(&$array) ),* ]).unwrap() - } + }; } diff --git a/src/zip/mod.rs b/src/zip/mod.rs index a94f74518..aadc93032 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -14,16 +14,16 @@ mod ndproducer; use std::mem::MaybeUninit; use crate::imp_prelude::*; +use crate::partial::Partial; use crate::AssignElem; use crate::IntoDimension; use crate::Layout; -use crate::partial::Partial; -use crate::indexes::{indices, Indices}; -use crate::split_at::{SplitPreference, SplitAt}; use crate::dimension; +use crate::indexes::{indices, Indices}; +use crate::split_at::{SplitAt, SplitPreference}; -pub use self::ndproducer::{NdProducer, IntoNdProducer, Offset}; +pub use self::ndproducer::{IntoNdProducer, NdProducer, Offset}; /// Return if the expression is a break value. macro_rules! fold_while { @@ -39,8 +39,7 @@ macro_rules! fold_while { /// /// See [broadcasting](ArrayBase#broadcasting) for more information. trait Broadcast -where - E: IntoDimension, +where E: IntoDimension { type Output: NdProducer; /// Broadcast the array to the new dimensions `shape`. @@ -52,7 +51,8 @@ where } /// Compute `Layout` hints for array shape dim, strides -fn array_layout(dim: &D, strides: &D) -> Layout { +fn array_layout(dim: &D, strides: &D) -> Layout +{ let n = dim.ndim(); if dimension::is_layout_c(dim, strides) { // effectively one-dimensional => C and F layout compatible @@ -81,7 +81,8 @@ where S: RawData, D: Dimension, { - pub(crate) fn layout_impl(&self) -> Layout { + pub(crate) fn layout_impl(&self) -> Layout + { array_layout(&self.dim, &self.strides) } } @@ -92,7 +93,8 @@ where D: Dimension, { type Output = ArrayView<'a, A, E::Dim>; - fn broadcast_unwrap(self, shape: E) -> Self::Output { + fn broadcast_unwrap(self, shape: E) -> Self::Output + { #[allow(clippy::needless_borrow)] let res: ArrayView<'_, A, E::Dim> = (&self).broadcast_unwrap(shape.into_dimension()); unsafe { ArrayView::new(res.ptr, res.dim, res.strides) } @@ -100,7 +102,8 @@ where private_impl! {} } -trait ZippableTuple: Sized { +trait ZippableTuple: Sized +{ type Item; type Ptr: OffsetTuple + Copy; type Dim: Dimension; @@ -189,7 +192,8 @@ trait ZippableTuple: Sized { /// ``` #[derive(Debug, Clone)] #[must_use = "zipping producers is lazy and does nothing unless consumed"] -pub struct Zip { +pub struct Zip +{ parts: Parts, dimension: D, layout: Layout, @@ -198,7 +202,6 @@ pub struct Zip { layout_tendency: i32, } - impl Zip<(P,), D> where D: Dimension, @@ -209,8 +212,7 @@ where /// The Zip will take the exact dimension of `p` and all inputs /// must have the same dimensions (or be broadcast to them). pub fn from(p: IP) -> Self - where - IP: IntoNdProducer, + where IP: IntoNdProducer { let array = p.into_producer(); let dim = array.raw_dim(); @@ -235,8 +237,7 @@ where /// /// *Note:* Indexed zip has overhead. pub fn indexed(p: IP) -> Self - where - IP: IntoNdProducer, + where IP: IntoNdProducer { let array = p.into_producer(); let dim = array.raw_dim(); @@ -258,13 +259,12 @@ where ); } - impl Zip -where - D: Dimension, +where D: Dimension { /// Return a the number of element tuples in the Zip - pub fn size(&self) -> usize { + pub fn size(&self) -> usize + { self.dimension.size() } @@ -272,30 +272,30 @@ where /// /// ***Panics*** if `axis` is out of bounds. #[track_caller] - fn len_of(&self, axis: Axis) -> usize { + fn len_of(&self, axis: Axis) -> usize + { self.dimension[axis.index()] } - fn prefer_f(&self) -> bool { - !self.layout.is(Layout::CORDER) && - (self.layout.is(Layout::FORDER) || self.layout_tendency < 0) + fn prefer_f(&self) -> bool + { + !self.layout.is(Layout::CORDER) && (self.layout.is(Layout::FORDER) || self.layout_tendency < 0) } /// Return an *approximation* to the max stride axis; if /// component arrays disagree, there may be no choice better than the /// others. - fn max_stride_axis(&self) -> Axis { + fn max_stride_axis(&self) -> Axis + { let i = if self.prefer_f() { - self - .dimension + self.dimension .slice() .iter() .rposition(|&len| len > 1) .unwrap_or(self.dimension.ndim() - 1) } else { /* corder or default */ - self - .dimension + self.dimension .slice() .iter() .position(|&len| len > 1) @@ -306,8 +306,7 @@ where } impl Zip -where - D: Dimension, +where D: Dimension { fn for_each_core(&mut self, acc: Acc, mut function: F) -> FoldWhile where @@ -332,9 +331,7 @@ where let size = self.dimension.size(); let ptrs = self.parts.as_ptr(); let inner_strides = self.parts.contiguous_stride(); - unsafe { - self.inner(acc, ptrs, inner_strides, size, &mut function) - } + unsafe { self.inner(acc, ptrs, inner_strides, size, &mut function) } } /// The innermost loop of the Zip for_each methods @@ -345,11 +342,12 @@ where /// `strides`: strides for the elements in this stretch /// `len`: number of elements /// `function`: closure - unsafe fn inner(&self, mut acc: Acc, ptr: P::Ptr, strides: P::Stride, - len: usize, function: &mut F) -> FoldWhile + unsafe fn inner( + &self, mut acc: Acc, ptr: P::Ptr, strides: P::Stride, len: usize, function: &mut F, + ) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, - P: ZippableTuple + P: ZippableTuple, { let mut i = 0; while i < len { @@ -360,7 +358,6 @@ where FoldWhile::Continue(acc) } - fn for_each_core_strided(&mut self, acc: Acc, function: F) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, @@ -439,13 +436,14 @@ where impl Zip<(P1, P2), D> where D: Dimension, - P1: NdProducer, - P1: NdProducer, + P1: NdProducer, + P1: NdProducer, { /// Debug assert traversal order is like c (including 1D case) // Method placement: only used for binary Zip at the moment. #[inline] - pub(crate) fn debug_assert_c_order(self) -> Self { + pub(crate) fn debug_assert_c_order(self) -> Self + { debug_assert!(self.layout.is(Layout::CORDER) || self.layout_tendency >= 0 || self.dimension.slice().iter().filter(|&&d| d > 1).count() <= 1, "Assertion failed: traversal is not c-order or 1D for \ @@ -455,7 +453,6 @@ where } } - /* trait Offset : Copy { unsafe fn offset(self, off: isize) -> Self; @@ -471,14 +468,17 @@ impl Offset for *mut T { } */ -trait OffsetTuple { +trait OffsetTuple +{ type Args; unsafe fn stride_offset(self, stride: Self::Args, index: usize) -> Self; } -impl OffsetTuple for *mut T { +impl OffsetTuple for *mut T +{ type Args = isize; - unsafe fn stride_offset(self, stride: Self::Args, index: usize) -> Self { + unsafe fn stride_offset(self, stride: Self::Args, index: usize) -> Self + { self.offset(index as isize * stride) } } @@ -496,7 +496,7 @@ macro_rules! offset_impl { } } )+ - } + }; } offset_impl! { @@ -555,7 +555,7 @@ macro_rules! zipt_impl { } } )+ - } + }; } zipt_impl! { @@ -938,7 +938,7 @@ macro_rules! map_impl { } )+ - } + }; } map_impl! { @@ -952,23 +952,27 @@ map_impl! { /// Value controlling the execution of `.fold_while` on `Zip`. #[derive(Debug, Copy, Clone)] -pub enum FoldWhile { +pub enum FoldWhile +{ /// Continue folding with this value Continue(T), /// Fold is complete and will return this value Done(T), } -impl FoldWhile { +impl FoldWhile +{ /// Return the inner value - pub fn into_inner(self) -> T { + pub fn into_inner(self) -> T + { match self { FoldWhile::Continue(x) | FoldWhile::Done(x) => x, } } /// Return true if it is `Done`, false if `Continue` - pub fn is_done(&self) -> bool { + pub fn is_done(&self) -> bool + { match *self { FoldWhile::Continue(_) => false, FoldWhile::Done(_) => true, diff --git a/src/zip/ndproducer.rs b/src/zip/ndproducer.rs index 4eb986d37..1d1b3391b 100644 --- a/src/zip/ndproducer.rs +++ b/src/zip/ndproducer.rs @@ -9,7 +9,8 @@ use alloc::vec::Vec; /// Slices and vectors can be used (equivalent to 1-dimensional array views). /// /// This trait is like `IntoIterator` for `NdProducers` instead of iterators. -pub trait IntoNdProducer { +pub trait IntoNdProducer +{ /// The element produced per iteration. type Item; /// Dimension type of the producer @@ -20,13 +21,13 @@ pub trait IntoNdProducer { } impl

IntoNdProducer for P -where - P: NdProducer, +where P: NdProducer { type Item = P::Item; type Dim = P::Dim; type Output = Self; - fn into_producer(self) -> Self::Output { + fn into_producer(self) -> Self::Output + { self } } @@ -51,7 +52,8 @@ where /// *producing* multidimensional items). /// /// See also [`IntoNdProducer`] -pub trait NdProducer { +pub trait NdProducer +{ /// The element produced per iteration. type Item; // Internal use / Pointee type @@ -74,7 +76,8 @@ pub trait NdProducer { /// Return the shape of the producer. fn raw_dim(&self) -> Self::Dim; #[doc(hidden)] - fn equal_dim(&self, dim: &Self::Dim) -> bool { + fn equal_dim(&self, dim: &Self::Dim) -> bool + { self.raw_dim() == *dim } #[doc(hidden)] @@ -89,29 +92,33 @@ pub trait NdProducer { fn contiguous_stride(&self) -> Self::Stride; #[doc(hidden)] fn split_at(self, axis: Axis, index: usize) -> (Self, Self) - where - Self: Sized; + where Self: Sized; private_decl! {} } -pub trait Offset: Copy { +pub trait Offset: Copy +{ type Stride: Copy; unsafe fn stride_offset(self, s: Self::Stride, index: usize) -> Self; private_decl! {} } -impl Offset for *const T { +impl Offset for *const T +{ type Stride = isize; - unsafe fn stride_offset(self, s: Self::Stride, index: usize) -> Self { + unsafe fn stride_offset(self, s: Self::Stride, index: usize) -> Self + { self.offset(s * (index as isize)) } private_impl! {} } -impl Offset for *mut T { +impl Offset for *mut T +{ type Stride = isize; - unsafe fn stride_offset(self, s: Self::Stride, index: usize) -> Self { + unsafe fn stride_offset(self, s: Self::Stride, index: usize) -> Self + { self.offset(s * (index as isize)) } private_impl! {} @@ -127,7 +134,8 @@ where type Item = &'a A; type Dim = D; type Output = ArrayView<'a, A, D>; - fn into_producer(self) -> Self::Output { + fn into_producer(self) -> Self::Output + { self.view() } } @@ -142,72 +150,86 @@ where type Item = &'a mut A; type Dim = D; type Output = ArrayViewMut<'a, A, D>; - fn into_producer(self) -> Self::Output { + fn into_producer(self) -> Self::Output + { self.view_mut() } } /// A slice is a one-dimensional producer -impl<'a, A: 'a> IntoNdProducer for &'a [A] { +impl<'a, A: 'a> IntoNdProducer for &'a [A] +{ type Item = ::Item; type Dim = Ix1; type Output = ArrayView1<'a, A>; - fn into_producer(self) -> Self::Output { + fn into_producer(self) -> Self::Output + { <_>::from(self) } } /// A mutable slice is a mutable one-dimensional producer -impl<'a, A: 'a> IntoNdProducer for &'a mut [A] { +impl<'a, A: 'a> IntoNdProducer for &'a mut [A] +{ type Item = ::Item; type Dim = Ix1; type Output = ArrayViewMut1<'a, A>; - fn into_producer(self) -> Self::Output { + fn into_producer(self) -> Self::Output + { <_>::from(self) } } /// A one-dimensional array is a one-dimensional producer -impl<'a, A: 'a, const N: usize> IntoNdProducer for &'a [A; N] { +impl<'a, A: 'a, const N: usize> IntoNdProducer for &'a [A; N] +{ type Item = ::Item; type Dim = Ix1; type Output = ArrayView1<'a, A>; - fn into_producer(self) -> Self::Output { + fn into_producer(self) -> Self::Output + { <_>::from(self) } } /// A mutable one-dimensional array is a mutable one-dimensional producer -impl<'a, A: 'a, const N: usize> IntoNdProducer for &'a mut [A; N] { +impl<'a, A: 'a, const N: usize> IntoNdProducer for &'a mut [A; N] +{ type Item = ::Item; type Dim = Ix1; type Output = ArrayViewMut1<'a, A>; - fn into_producer(self) -> Self::Output { + fn into_producer(self) -> Self::Output + { <_>::from(self) } } /// A Vec is a one-dimensional producer -impl<'a, A: 'a> IntoNdProducer for &'a Vec { +impl<'a, A: 'a> IntoNdProducer for &'a Vec +{ type Item = ::Item; type Dim = Ix1; type Output = ArrayView1<'a, A>; - fn into_producer(self) -> Self::Output { + fn into_producer(self) -> Self::Output + { <_>::from(self) } } /// A mutable Vec is a mutable one-dimensional producer -impl<'a, A: 'a> IntoNdProducer for &'a mut Vec { +impl<'a, A: 'a> IntoNdProducer for &'a mut Vec +{ type Item = ::Item; type Dim = Ix1; type Output = ArrayViewMut1<'a, A>; - fn into_producer(self) -> Self::Output { + fn into_producer(self) -> Self::Output + { <_>::from(self) } } -impl<'a, A, D: Dimension> NdProducer for ArrayView<'a, A, D> { +impl<'a, A, D: Dimension> NdProducer for ArrayView<'a, A, D> +{ type Item = &'a A; type Dim = D; type Ptr = *mut A; @@ -215,45 +237,55 @@ impl<'a, A, D: Dimension> NdProducer for ArrayView<'a, A, D> { private_impl! {} - fn raw_dim(&self) -> Self::Dim { + fn raw_dim(&self) -> Self::Dim + { self.raw_dim() } - fn equal_dim(&self, dim: &Self::Dim) -> bool { + fn equal_dim(&self, dim: &Self::Dim) -> bool + { self.dim.equal(dim) } - fn as_ptr(&self) -> *mut A { + fn as_ptr(&self) -> *mut A + { self.as_ptr() as _ } - fn layout(&self) -> Layout { + fn layout(&self) -> Layout + { self.layout_impl() } - unsafe fn as_ref(&self, ptr: *mut A) -> Self::Item { + unsafe fn as_ref(&self, ptr: *mut A) -> Self::Item + { &*ptr } - unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { + unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A + { self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) } - fn stride_of(&self, axis: Axis) -> isize { + fn stride_of(&self, axis: Axis) -> isize + { self.stride_of(axis) } #[inline(always)] - fn contiguous_stride(&self) -> Self::Stride { + fn contiguous_stride(&self) -> Self::Stride + { 1 } - fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) + { self.split_at(axis, index) } } -impl<'a, A, D: Dimension> NdProducer for ArrayViewMut<'a, A, D> { +impl<'a, A, D: Dimension> NdProducer for ArrayViewMut<'a, A, D> +{ type Item = &'a mut A; type Dim = D; type Ptr = *mut A; @@ -261,45 +293,55 @@ impl<'a, A, D: Dimension> NdProducer for ArrayViewMut<'a, A, D> { private_impl! {} - fn raw_dim(&self) -> Self::Dim { + fn raw_dim(&self) -> Self::Dim + { self.raw_dim() } - fn equal_dim(&self, dim: &Self::Dim) -> bool { + fn equal_dim(&self, dim: &Self::Dim) -> bool + { self.dim.equal(dim) } - fn as_ptr(&self) -> *mut A { + fn as_ptr(&self) -> *mut A + { self.as_ptr() as _ } - fn layout(&self) -> Layout { + fn layout(&self) -> Layout + { self.layout_impl() } - unsafe fn as_ref(&self, ptr: *mut A) -> Self::Item { + unsafe fn as_ref(&self, ptr: *mut A) -> Self::Item + { &mut *ptr } - unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { + unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A + { self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) } - fn stride_of(&self, axis: Axis) -> isize { + fn stride_of(&self, axis: Axis) -> isize + { self.stride_of(axis) } #[inline(always)] - fn contiguous_stride(&self) -> Self::Stride { + fn contiguous_stride(&self) -> Self::Stride + { 1 } - fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) + { self.split_at(axis, index) } } -impl NdProducer for RawArrayView { +impl NdProducer for RawArrayView +{ type Item = *const A; type Dim = D; type Ptr = *const A; @@ -307,45 +349,55 @@ impl NdProducer for RawArrayView { private_impl! {} - fn raw_dim(&self) -> Self::Dim { + fn raw_dim(&self) -> Self::Dim + { self.raw_dim() } - fn equal_dim(&self, dim: &Self::Dim) -> bool { + fn equal_dim(&self, dim: &Self::Dim) -> bool + { self.dim.equal(dim) } - fn as_ptr(&self) -> *const A { + fn as_ptr(&self) -> *const A + { self.as_ptr() } - fn layout(&self) -> Layout { + fn layout(&self) -> Layout + { self.layout_impl() } - unsafe fn as_ref(&self, ptr: *const A) -> *const A { + unsafe fn as_ref(&self, ptr: *const A) -> *const A + { ptr } - unsafe fn uget_ptr(&self, i: &Self::Dim) -> *const A { + unsafe fn uget_ptr(&self, i: &Self::Dim) -> *const A + { self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) } - fn stride_of(&self, axis: Axis) -> isize { + fn stride_of(&self, axis: Axis) -> isize + { self.stride_of(axis) } #[inline(always)] - fn contiguous_stride(&self) -> Self::Stride { + fn contiguous_stride(&self) -> Self::Stride + { 1 } - fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) + { self.split_at(axis, index) } } -impl NdProducer for RawArrayViewMut { +impl NdProducer for RawArrayViewMut +{ type Item = *mut A; type Dim = D; type Ptr = *mut A; @@ -353,40 +405,49 @@ impl NdProducer for RawArrayViewMut { private_impl! {} - fn raw_dim(&self) -> Self::Dim { + fn raw_dim(&self) -> Self::Dim + { self.raw_dim() } - fn equal_dim(&self, dim: &Self::Dim) -> bool { + fn equal_dim(&self, dim: &Self::Dim) -> bool + { self.dim.equal(dim) } - fn as_ptr(&self) -> *mut A { + fn as_ptr(&self) -> *mut A + { self.as_ptr() as _ } - fn layout(&self) -> Layout { + fn layout(&self) -> Layout + { self.layout_impl() } - unsafe fn as_ref(&self, ptr: *mut A) -> *mut A { + unsafe fn as_ref(&self, ptr: *mut A) -> *mut A + { ptr } - unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { + unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A + { self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) } - fn stride_of(&self, axis: Axis) -> isize { + fn stride_of(&self, axis: Axis) -> isize + { self.stride_of(axis) } #[inline(always)] - fn contiguous_stride(&self) -> Self::Stride { + fn contiguous_stride(&self) -> Self::Stride + { 1 } - fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) + { self.split_at(axis, index) } } diff --git a/tests/append.rs b/tests/append.rs index cbb10d853..78ea243b2 100644 --- a/tests/append.rs +++ b/tests/append.rs @@ -1,9 +1,9 @@ - use ndarray::prelude::*; -use ndarray::{ShapeError, ErrorKind}; +use ndarray::{ErrorKind, ShapeError}; #[test] -fn push_row() { +fn push_row() +{ let mut a = Array::zeros((0, 4)); a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); @@ -25,7 +25,8 @@ fn push_row() { } #[test] -fn push_row_wrong_layout() { +fn push_row_wrong_layout() +{ let mut a = Array::zeros((0, 4)); a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); @@ -44,7 +45,6 @@ fn push_row_wrong_layout() { [4., 5., 6., 7., 2.]]); assert_eq!(a2.strides(), &[1, 2]); - // Clone the array let mut dim = a.raw_dim(); @@ -58,7 +58,8 @@ fn push_row_wrong_layout() { } #[test] -fn push_row_neg_stride_1() { +fn push_row_neg_stride_1() +{ let mut a = Array::zeros((0, 4)); a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); @@ -101,7 +102,8 @@ fn push_row_neg_stride_1() { } #[test] -fn push_row_neg_stride_2() { +fn push_row_neg_stride_2() +{ let mut a = Array::zeros((0, 4)); a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); @@ -144,7 +146,8 @@ fn push_row_neg_stride_2() { } #[test] -fn push_row_error() { +fn push_row_error() +{ let mut a = Array::zeros((3, 4)); assert_eq!(a.push_row(aview1(&[1.])), @@ -162,7 +165,8 @@ fn push_row_error() { } #[test] -fn push_row_existing() { +fn push_row_existing() +{ let mut a = Array::zeros((1, 4)); a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); @@ -186,7 +190,8 @@ fn push_row_existing() { } #[test] -fn push_row_col_len_1() { +fn push_row_col_len_1() +{ // Test appending 1 row and then cols from shape 1 x 1 let mut a = Array::zeros((1, 1)); a.push_row(aview1(&[1.])).unwrap(); // shape 2 x 1 @@ -203,7 +208,8 @@ fn push_row_col_len_1() { } #[test] -fn push_column() { +fn push_column() +{ let mut a = Array::zeros((4, 0)); a.push_column(aview1(&[0., 1., 2., 3.])).unwrap(); a.push_column(aview1(&[4., 5., 6., 7.])).unwrap(); @@ -215,7 +221,8 @@ fn push_column() { } #[test] -fn append_array1() { +fn append_array1() +{ let mut a = Array::zeros((0, 4)); a.append(Axis(0), aview2(&[[0., 1., 2., 3.]])).unwrap(); println!("{:?}", a); @@ -228,7 +235,8 @@ fn append_array1() { array![[0., 1., 2., 3.], [4., 5., 6., 7.]]); - a.append(Axis(0), aview2(&[[5., 5., 4., 4.], [3., 3., 2., 2.]])).unwrap(); + a.append(Axis(0), aview2(&[[5., 5., 4., 4.], [3., 3., 2., 2.]])) + .unwrap(); println!("{:?}", a); assert_eq!(a, array![[0., 1., 2., 3.], @@ -238,7 +246,8 @@ fn append_array1() { } #[test] -fn append_array_3d() { +fn append_array_3d() +{ let mut a = Array::zeros((0, 2, 2)); a.append(Axis(0), array![[[0, 1], [2, 3]]].view()).unwrap(); println!("{:?}", a); @@ -279,11 +288,16 @@ fn append_array_3d() { } #[test] -fn test_append_2d() { +fn test_append_2d() +{ // create an empty array and append let mut a = Array::zeros((0, 4)); - let ones = ArrayView::from(&[1.; 12]).into_shape_with_order((3, 4)).unwrap(); - let zeros = ArrayView::from(&[0.; 8]).into_shape_with_order((2, 4)).unwrap(); + let ones = ArrayView::from(&[1.; 12]) + .into_shape_with_order((3, 4)) + .unwrap(); + let zeros = ArrayView::from(&[0.; 8]) + .into_shape_with_order((2, 4)) + .unwrap(); a.append(Axis(0), ones).unwrap(); a.append(Axis(0), zeros).unwrap(); a.append(Axis(0), ones).unwrap(); @@ -311,24 +325,54 @@ fn test_append_2d() { } #[test] -fn test_append_middle_axis() { +fn test_append_middle_axis() +{ // ensure we can append to Axis(1) by letting it become outermost let mut a = Array::::zeros((3, 0, 2)); - a.append(Axis(1), Array::from_iter(0..12).into_shape_with_order((3, 2, 2)).unwrap().view()).unwrap(); + a.append( + Axis(1), + Array::from_iter(0..12) + .into_shape_with_order((3, 2, 2)) + .unwrap() + .view(), + ) + .unwrap(); println!("{:?}", a); - a.append(Axis(1), Array::from_iter(12..24).into_shape_with_order((3, 2, 2)).unwrap().view()).unwrap(); + a.append( + Axis(1), + Array::from_iter(12..24) + .into_shape_with_order((3, 2, 2)) + .unwrap() + .view(), + ) + .unwrap(); println!("{:?}", a); // ensure we can append to Axis(1) by letting it become outermost let mut a = Array::::zeros((3, 1, 2)); - a.append(Axis(1), Array::from_iter(0..12).into_shape_with_order((3, 2, 2)).unwrap().view()).unwrap(); + a.append( + Axis(1), + Array::from_iter(0..12) + .into_shape_with_order((3, 2, 2)) + .unwrap() + .view(), + ) + .unwrap(); println!("{:?}", a); - a.append(Axis(1), Array::from_iter(12..24).into_shape_with_order((3, 2, 2)).unwrap().view()).unwrap(); + a.append( + Axis(1), + Array::from_iter(12..24) + .into_shape_with_order((3, 2, 2)) + .unwrap() + .view(), + ) + .unwrap(); println!("{:?}", a); } #[test] -fn test_append_zero_size() { +fn test_append_zero_size() +{ { let mut a = Array::::zeros((0, 0)); a.append(Axis(0), aview2(&[[]])).unwrap(); @@ -339,15 +383,18 @@ fn test_append_zero_size() { { let mut a = Array::::zeros((0, 0)); - a.append(Axis(1), ArrayView::from(&[]).into_shape_with_order((0, 1)).unwrap()).unwrap(); - a.append(Axis(1), ArrayView::from(&[]).into_shape_with_order((0, 1)).unwrap()).unwrap(); + a.append(Axis(1), ArrayView::from(&[]).into_shape_with_order((0, 1)).unwrap()) + .unwrap(); + a.append(Axis(1), ArrayView::from(&[]).into_shape_with_order((0, 1)).unwrap()) + .unwrap(); assert_eq!(a.len(), 0); assert_eq!(a.shape(), &[0, 2]); } } #[test] -fn push_row_neg_stride_3() { +fn push_row_neg_stride_3() +{ let mut a = Array::zeros((0, 4)); a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); a.invert_axis(Axis(1)); @@ -358,13 +405,13 @@ fn push_row_neg_stride_3() { } #[test] -fn push_row_ignore_strides_length_one_axes() { +fn push_row_ignore_strides_length_one_axes() +{ let strides = &[0, 1, 10, 20]; for invert in &[vec![], vec![0], vec![1], vec![0, 1]] { for &stride0 in strides { for &stride1 in strides { - let mut a = - Array::from_shape_vec([1, 1].strides([stride0, stride1]), vec![0.]).unwrap(); + let mut a = Array::from_shape_vec([1, 1].strides([stride0, stride1]), vec![0.]).unwrap(); for &ax in invert { a.invert_axis(Axis(ax)); } @@ -379,20 +426,23 @@ fn push_row_ignore_strides_length_one_axes() { #[test] #[should_panic(expected = "IncompatibleShape")] -fn zero_dimensional_error1() { +fn zero_dimensional_error1() +{ let mut a = Array::zeros(()).into_dyn(); a.append(Axis(0), arr0(0).into_dyn().view()).unwrap(); } #[test] #[should_panic(expected = "IncompatibleShape")] -fn zero_dimensional_error2() { +fn zero_dimensional_error2() +{ let mut a = Array::zeros(()).into_dyn(); a.push(Axis(0), arr0(0).into_dyn().view()).unwrap(); } #[test] -fn zero_dimensional_ok() { +fn zero_dimensional_ok() +{ let mut a = Array::zeros(0); let one = aview0(&1); let two = aview0(&2); diff --git a/tests/array-construct.rs b/tests/array-construct.rs index a3949fcab..f7339dff6 100644 --- a/tests/array-construct.rs +++ b/tests/array-construct.rs @@ -1,26 +1,23 @@ #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] use defmac::defmac; -use ndarray::prelude::*; use ndarray::arr3; +use ndarray::prelude::*; use ndarray::Zip; #[test] -fn test_from_shape_fn() { +fn test_from_shape_fn() +{ let step = 3.1; - let h = Array::from_shape_fn((5, 5), |(i, j)| { - f64::sin(i as f64 / step) * f64::cos(j as f64 / step) - }); + let h = Array::from_shape_fn((5, 5), |(i, j)| f64::sin(i as f64 / step) * f64::cos(j as f64 / step)); assert_eq!(h.shape(), &[5, 5]); } #[test] -fn test_dimension_zero() { +fn test_dimension_zero() +{ let a: Array2 = Array2::from(vec![[], [], []]); assert_eq!(vec![0.; 0], a.into_raw_vec()); let a: Array3 = Array3::from(vec![[[]], [[]], [[]]]); @@ -29,7 +26,8 @@ fn test_dimension_zero() { #[test] #[cfg(feature = "approx")] -fn test_arc_into_owned() { +fn test_arc_into_owned() +{ use approx::assert_abs_diff_ne; let a = Array2::from_elem((5, 5), 1.).into_shared(); @@ -42,7 +40,8 @@ fn test_arc_into_owned() { } #[test] -fn test_arcarray_thread_safe() { +fn test_arcarray_thread_safe() +{ fn is_send(_t: &T) {} fn is_sync(_t: &T) {} let a = Array2::from_elem((5, 5), 1.).into_shared(); @@ -54,7 +53,8 @@ fn test_arcarray_thread_safe() { #[test] #[cfg(feature = "std")] #[allow(deprecated)] // uninitialized -fn test_uninit() { +fn test_uninit() +{ unsafe { let mut a = Array::::uninitialized((3, 4).f()); assert_eq!(a.dim(), (3, 4)); @@ -69,7 +69,8 @@ fn test_uninit() { } #[test] -fn test_from_fn_c0() { +fn test_from_fn_c0() +{ let a = Array::from_shape_fn((), |i| i); assert_eq!(a[()], ()); assert_eq!(a.len(), 1); @@ -77,7 +78,8 @@ fn test_from_fn_c0() { } #[test] -fn test_from_fn_c1() { +fn test_from_fn_c1() +{ let a = Array::from_shape_fn(28, |i| i); for (i, elt) in a.indexed_iter() { assert_eq!(i, *elt); @@ -85,7 +87,8 @@ fn test_from_fn_c1() { } #[test] -fn test_from_fn_c() { +fn test_from_fn_c() +{ let a = Array::from_shape_fn((4, 7), |i| i); for (i, elt) in a.indexed_iter() { assert_eq!(i, *elt); @@ -93,7 +96,8 @@ fn test_from_fn_c() { } #[test] -fn test_from_fn_c3() { +fn test_from_fn_c3() +{ let a = Array::from_shape_fn((4, 3, 7), |i| i); for (i, elt) in a.indexed_iter() { assert_eq!(i, *elt); @@ -101,7 +105,8 @@ fn test_from_fn_c3() { } #[test] -fn test_from_fn_f0() { +fn test_from_fn_f0() +{ let a = Array::from_shape_fn(().f(), |i| i); assert_eq!(a[()], ()); assert_eq!(a.len(), 1); @@ -109,7 +114,8 @@ fn test_from_fn_f0() { } #[test] -fn test_from_fn_f1() { +fn test_from_fn_f1() +{ let a = Array::from_shape_fn(28.f(), |i| i); for (i, elt) in a.indexed_iter() { assert_eq!(i, *elt); @@ -117,7 +123,8 @@ fn test_from_fn_f1() { } #[test] -fn test_from_fn_f() { +fn test_from_fn_f() +{ let a = Array::from_shape_fn((4, 7).f(), |i| i); for (i, elt) in a.indexed_iter() { assert_eq!(i, *elt); @@ -125,7 +132,8 @@ fn test_from_fn_f() { } #[test] -fn test_from_fn_f_with_zero() { +fn test_from_fn_f_with_zero() +{ defmac!(test_from_fn_f_with_zero shape => { let a = Array::from_shape_fn(shape.f(), |i| i); assert_eq!(a.len(), 0); @@ -140,7 +148,8 @@ fn test_from_fn_f_with_zero() { } #[test] -fn test_from_fn_f3() { +fn test_from_fn_f3() +{ let a = Array::from_shape_fn((4, 2, 7).f(), |i| i); for (i, elt) in a.indexed_iter() { assert_eq!(i, *elt); @@ -148,7 +157,8 @@ fn test_from_fn_f3() { } #[test] -fn deny_wraparound_from_vec() { +fn deny_wraparound_from_vec() +{ let five = vec![0; 5]; let five_large = Array::from_shape_vec((3, 7, 29, 36760123, 823996703), five.clone()); println!("{:?}", five_large); @@ -158,7 +168,8 @@ fn deny_wraparound_from_vec() { } #[test] -fn test_ones() { +fn test_ones() +{ let mut a = Array::::zeros((2, 3, 4)); a.fill(1.0); let b = Array::::ones((2, 3, 4)); @@ -166,7 +177,8 @@ fn test_ones() { } #[test] -fn test_from_shape_empty_with_neg_stride() { +fn test_from_shape_empty_with_neg_stride() +{ // Issue #998, negative strides for an axis where it doesn't matter. let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; let v = s[..12].to_vec(); @@ -177,7 +189,8 @@ fn test_from_shape_empty_with_neg_stride() { } #[test] -fn test_from_shape_with_neg_stride() { +fn test_from_shape_with_neg_stride() +{ // Issue #998, negative strides for an axis where it doesn't matter. let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; let v = s[..12].to_vec(); @@ -189,7 +202,8 @@ fn test_from_shape_with_neg_stride() { } #[test] -fn test_from_shape_2_2_2_with_neg_stride() { +fn test_from_shape_2_2_2_with_neg_stride() +{ // Issue #998, negative strides for an axis where it doesn't matter. let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; let v = s[..12].to_vec(); @@ -204,35 +218,42 @@ fn test_from_shape_2_2_2_with_neg_stride() { #[should_panic] #[test] -fn deny_wraparound_zeros() { +fn deny_wraparound_zeros() +{ //2^64 + 5 = 18446744073709551621 = 3×7×29×36760123×823996703 (5 distinct prime factors) let _five_large = Array::::zeros((3, 7, 29, 36760123, 823996703)); } #[should_panic] #[test] -fn deny_wraparound_reshape() { +fn deny_wraparound_reshape() +{ //2^64 + 5 = 18446744073709551621 = 3×7×29×36760123×823996703 (5 distinct prime factors) let five = Array::::zeros(5); - let _five_large = five.into_shape_with_order((3, 7, 29, 36760123, 823996703)).unwrap(); + let _five_large = five + .into_shape_with_order((3, 7, 29, 36760123, 823996703)) + .unwrap(); } #[should_panic] #[test] -fn deny_wraparound_default() { +fn deny_wraparound_default() +{ let _five_large = Array::::default((3, 7, 29, 36760123, 823996703)); } #[should_panic] #[test] -fn deny_wraparound_from_shape_fn() { +fn deny_wraparound_from_shape_fn() +{ let _five_large = Array::::from_shape_fn((3, 7, 29, 36760123, 823996703), |_| 0.); } #[should_panic] #[test] #[allow(deprecated)] // uninitialized -fn deny_wraparound_uninitialized() { +fn deny_wraparound_uninitialized() +{ unsafe { let _five_large = Array::::uninitialized((3, 7, 29, 36760123, 823996703)); } @@ -240,36 +261,42 @@ fn deny_wraparound_uninitialized() { #[should_panic] #[test] -fn deny_wraparound_uninit() { +fn deny_wraparound_uninit() +{ let _five_large = Array::::uninit((3, 7, 29, 36760123, 823996703)); } #[should_panic] #[test] -fn deny_slice_with_too_many_rows_to_arrayview2() { +fn deny_slice_with_too_many_rows_to_arrayview2() +{ let _view = ArrayView2::from(&[[0u8; 0]; usize::MAX][..]); } #[should_panic] #[test] -fn deny_slice_with_too_many_zero_sized_elems_to_arrayview2() { +fn deny_slice_with_too_many_zero_sized_elems_to_arrayview2() +{ let _view = ArrayView2::from(&[[(); isize::MAX as usize]; isize::MAX as usize][..]); } #[should_panic] #[test] -fn deny_slice_with_too_many_rows_to_arrayviewmut2() { +fn deny_slice_with_too_many_rows_to_arrayviewmut2() +{ let _view = ArrayViewMut2::from(&mut [[0u8; 0]; usize::MAX][..]); } #[should_panic] #[test] -fn deny_slice_with_too_many_zero_sized_elems_to_arrayviewmut2() { +fn deny_slice_with_too_many_zero_sized_elems_to_arrayviewmut2() +{ let _view = ArrayViewMut2::from(&mut [[(); isize::MAX as usize]; isize::MAX as usize][..]); } #[test] -fn maybe_uninit_1() { +fn maybe_uninit_1() +{ use std::mem::MaybeUninit; unsafe { @@ -299,13 +326,10 @@ fn maybe_uninit_1() { // RawArrayViewMut let mut a = Mat::uninit((10, 10)); let v = a.raw_view_mut(); - Zip::from(v) - .for_each(|ptr| *(*ptr).as_mut_ptr() = 1.); + Zip::from(v).for_each(|ptr| *(*ptr).as_mut_ptr() = 1.); let u = a.raw_view_mut().assume_init(); - Zip::from(u) - .for_each(|ptr| assert_eq!(*ptr, 1.)); - + Zip::from(u).for_each(|ptr| assert_eq!(*ptr, 1.)); } } diff --git a/tests/array.rs b/tests/array.rs index 561284869..3f2c38a62 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1,9 +1,6 @@ #![allow(non_snake_case)] #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names, + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, clippy::float_cmp )] @@ -11,9 +8,9 @@ use approx::assert_relative_eq; use defmac::defmac; #[allow(deprecated)] use itertools::{zip, Itertools}; +use ndarray::indices; use ndarray::prelude::*; use ndarray::{arr3, rcarr2}; -use ndarray::indices; use ndarray::{Slice, SliceInfo, SliceInfoElem}; use num_complex::Complex; use std::convert::TryFrom; @@ -33,7 +30,8 @@ macro_rules! assert_panics { } #[test] -fn test_matmul_arcarray() { +fn test_matmul_arcarray() +{ let mut A = ArcArray::::zeros((2, 3)); for (i, elt) in A.iter_mut().enumerate() { *elt = i; @@ -49,8 +47,7 @@ fn test_matmul_arcarray() { println!("B = \n{:?}", B); println!("A x B = \n{:?}", c); unsafe { - let result = - ArcArray::from_shape_vec_unchecked((2, 4), vec![20, 23, 26, 29, 56, 68, 80, 92]); + let result = ArcArray::from_shape_vec_unchecked((2, 4), vec![20, 23, 26, 29, 56, 68, 80, 92]); assert_eq!(c.shape(), result.shape()); assert!(c.iter().zip(result.iter()).all(|(a, b)| a == b)); assert!(c == result); @@ -58,23 +55,26 @@ fn test_matmul_arcarray() { } #[allow(unused)] -fn arrayview_shrink_lifetime<'a, 'b: 'a>(view: ArrayView1<'b, f64>) -> ArrayView1<'a, f64> { +fn arrayview_shrink_lifetime<'a, 'b: 'a>(view: ArrayView1<'b, f64>) -> ArrayView1<'a, f64> +{ view.reborrow() } #[allow(unused)] -fn arrayviewmut_shrink_lifetime<'a, 'b: 'a>( - view: ArrayViewMut1<'b, f64>, -) -> ArrayViewMut1<'a, f64> { +fn arrayviewmut_shrink_lifetime<'a, 'b: 'a>(view: ArrayViewMut1<'b, f64>) -> ArrayViewMut1<'a, f64> +{ view.reborrow() } #[test] #[cfg(feature = "std")] -fn test_mat_mul() { +fn test_mat_mul() +{ // smoke test, a big matrix multiplication of uneven size let (n, m) = (45, 33); - let a = ArcArray::linspace(0., ((n * m) - 1) as f32, n as usize * m as usize).into_shape_with_order((n, m)).unwrap(); + let a = ArcArray::linspace(0., ((n * m) - 1) as f32, n as usize * m as usize) + .into_shape_with_order((n, m)) + .unwrap(); let b = ArcArray::eye(m); assert_eq!(a.dot(&b), a); let c = ArcArray::eye(n); @@ -83,7 +83,8 @@ fn test_mat_mul() { #[deny(unsafe_code)] #[test] -fn test_slice() { +fn test_slice() +{ let mut A = ArcArray::::zeros((3, 4, 5)); for (i, elt) in A.iter_mut().enumerate() { *elt = i; @@ -98,13 +99,15 @@ fn test_slice() { #[deny(unsafe_code)] #[test] -fn test_slice_ix0() { +fn test_slice_ix0() +{ let arr = arr0(5); assert_eq!(arr.slice(s![]), aview0(&5)); } #[test] -fn test_slice_edge_cases() { +fn test_slice_edge_cases() +{ let mut arr = Array3::::zeros((3, 4, 5)); arr.slice_collapse(s![0..0;-1, .., ..]); assert_eq!(arr.shape(), &[0, 4, 5]); @@ -114,7 +117,8 @@ fn test_slice_edge_cases() { } #[test] -fn test_slice_inclusive_range() { +fn test_slice_inclusive_range() +{ let arr = array![[1, 2, 3], [4, 5, 6]]; assert_eq!(arr.slice(s![1..=1, 1..=2]), array![[5, 6]]); assert_eq!(arr.slice(s![1..=-1, -2..=2;-1]), array![[6, 5]]); @@ -128,7 +132,8 @@ fn test_slice_inclusive_range() { /// `ArrayView1` and `ArrayView2`, so the compiler needs to determine which /// type is the correct result for the `.slice()` call. #[test] -fn test_slice_infer() { +fn test_slice_infer() +{ let a = array![1., 2.]; let b = array![[3., 4.], [5., 6.]]; b.slice(s![..-1, ..]).dot(&a); @@ -136,7 +141,8 @@ fn test_slice_infer() { } #[test] -fn test_slice_with_many_dim() { +fn test_slice_with_many_dim() +{ let mut A = ArcArray::::zeros(&[3, 1, 4, 1, 3, 2, 1][..]); for (i, elt) in A.iter_mut().enumerate() { *elt = i; @@ -163,14 +169,16 @@ fn test_slice_with_many_dim() { } #[test] -fn test_slice_range_variable() { +fn test_slice_range_variable() +{ let range = 1..4; let arr = array![0, 1, 2, 3, 4]; assert_eq!(arr.slice(s![range]), array![1, 2, 3]); } #[test] -fn test_slice_args_eval_range_once() { +fn test_slice_args_eval_range_once() +{ let mut eval_count = 0; { let mut range = || { @@ -184,7 +192,8 @@ fn test_slice_args_eval_range_once() { } #[test] -fn test_slice_args_eval_step_once() { +fn test_slice_args_eval_step_once() +{ let mut eval_count = 0; { let mut step = || { @@ -198,7 +207,8 @@ fn test_slice_args_eval_step_once() { } #[test] -fn test_slice_array_fixed() { +fn test_slice_array_fixed() +{ let mut arr = Array3::::zeros((5, 2, 5)); let info = s![1.., 1, NewAxis, ..;2]; arr.slice(info); @@ -209,7 +219,8 @@ fn test_slice_array_fixed() { } #[test] -fn test_slice_dyninput_array_fixed() { +fn test_slice_dyninput_array_fixed() +{ let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); let info = s![1.., 1, NewAxis, ..;2]; arr.slice(info); @@ -220,7 +231,8 @@ fn test_slice_dyninput_array_fixed() { } #[test] -fn test_slice_array_dyn() { +fn test_slice_array_dyn() +{ let mut arr = Array3::::zeros((5, 2, 5)); let info = SliceInfo::<_, Ix3, IxDyn>::try_from([ SliceInfoElem::from(1..), @@ -242,7 +254,8 @@ fn test_slice_array_dyn() { } #[test] -fn test_slice_dyninput_array_dyn() { +fn test_slice_dyninput_array_dyn() +{ let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); let info = SliceInfo::<_, Ix3, IxDyn>::try_from([ SliceInfoElem::from(1..), @@ -264,7 +277,8 @@ fn test_slice_dyninput_array_dyn() { } #[test] -fn test_slice_dyninput_vec_fixed() { +fn test_slice_dyninput_vec_fixed() +{ let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); let info = &SliceInfo::<_, Ix3, Ix3>::try_from(vec![ SliceInfoElem::from(1..), @@ -286,7 +300,8 @@ fn test_slice_dyninput_vec_fixed() { } #[test] -fn test_slice_dyninput_vec_dyn() { +fn test_slice_dyninput_vec_dyn() +{ let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); let info = &SliceInfo::<_, Ix3, IxDyn>::try_from(vec![ SliceInfoElem::from(1..), @@ -308,7 +323,8 @@ fn test_slice_dyninput_vec_dyn() { } #[test] -fn test_slice_with_subview_and_new_axis() { +fn test_slice_with_subview_and_new_axis() +{ let mut arr = ArcArray::::zeros((3, 5, 4)); for (i, elt) in arr.iter_mut().enumerate() { *elt = i; @@ -345,7 +361,8 @@ fn test_slice_with_subview_and_new_axis() { } #[test] -fn test_slice_collapse_with_indices() { +fn test_slice_collapse_with_indices() +{ let mut arr = ArcArray::::zeros((3, 5, 4)); for (i, elt) in arr.iter_mut().enumerate() { *elt = i; @@ -384,13 +401,15 @@ fn test_slice_collapse_with_indices() { #[test] #[should_panic] -fn test_slice_collapse_with_newaxis() { +fn test_slice_collapse_with_newaxis() +{ let mut arr = Array2::::zeros((2, 3)); arr.slice_collapse(s![0, 0, NewAxis]); } #[test] -fn test_multislice() { +fn test_multislice() +{ macro_rules! do_test { ($arr:expr, $($s:expr),*) => { { @@ -404,7 +423,9 @@ fn test_multislice() { }; } - let mut arr = Array1::from_iter(0..48).into_shape_with_order((8, 6)).unwrap(); + let mut arr = Array1::from_iter(0..48) + .into_shape_with_order((8, 6)) + .unwrap(); assert_eq!( (arr.clone().view_mut(),), @@ -426,7 +447,8 @@ fn test_multislice() { } #[test] -fn test_multislice_intersecting() { +fn test_multislice_intersecting() +{ assert_panics!({ let mut arr = Array2::::zeros((8, 6)); arr.multi_slice_mut((s![3, .., NewAxis], s![3, ..])); @@ -467,34 +489,39 @@ fn test_multislice_intersecting() { #[should_panic] #[test] -fn index_out_of_bounds() { +fn index_out_of_bounds() +{ let mut a = Array::::zeros((3, 4)); a[[3, 2]] = 1; } #[should_panic] #[test] -fn slice_oob() { +fn slice_oob() +{ let a = ArcArray::::zeros((3, 4)); let _vi = a.slice(s![..10, ..]); } #[should_panic] #[test] -fn slice_axis_oob() { +fn slice_axis_oob() +{ let a = ArcArray::::zeros((3, 4)); let _vi = a.slice_axis(Axis(0), Slice::new(0, Some(10), 1)); } #[should_panic] #[test] -fn slice_wrong_dim() { +fn slice_wrong_dim() +{ let a = ArcArray::::zeros(vec![3, 4, 5]); let _vi = a.slice(s![.., ..]); } #[test] -fn test_index() { +fn test_index() +{ let mut A = ArcArray::::zeros((2, 3)); for (i, elt) in A.iter_mut().enumerate() { *elt = i; @@ -515,7 +542,8 @@ fn test_index() { } #[test] -fn test_index_arrays() { +fn test_index_arrays() +{ let a = Array1::from_iter(0..12); assert_eq!(a[1], a[[1]]); let v = a.view().into_shape_with_order((3, 4)).unwrap(); @@ -526,7 +554,8 @@ fn test_index_arrays() { #[test] #[allow(clippy::assign_op_pattern)] -fn test_add() { +fn test_add() +{ let mut A = ArcArray::::zeros((2, 2)); for (i, elt) in A.iter_mut().enumerate() { *elt = i; @@ -541,8 +570,11 @@ fn test_add() { } #[test] -fn test_multidim() { - let mut mat = ArcArray::zeros(2 * 3 * 4 * 5 * 6).into_shape_with_order((2, 3, 4, 5, 6)).unwrap(); +fn test_multidim() +{ + let mut mat = ArcArray::zeros(2 * 3 * 4 * 5 * 6) + .into_shape_with_order((2, 3, 4, 5, 6)) + .unwrap(); mat[(0, 0, 0, 0, 0)] = 22u8; { for (i, elt) in mat.iter_mut().enumerate() { @@ -564,7 +596,8 @@ array([[[ 7, 6], [ 9, 8]]]) */ #[test] -fn test_negative_stride_arcarray() { +fn test_negative_stride_arcarray() +{ let mut mat = ArcArray::zeros((2, 4, 2)); mat[[0, 0, 0]] = 1.0f32; for (i, elt) in mat.iter_mut().enumerate() { @@ -575,9 +608,7 @@ fn test_negative_stride_arcarray() { let vi = mat.slice(s![.., ..;-1, ..;-1]); assert_eq!(vi.shape(), &[2, 4, 2]); // Test against sequential iterator - let seq = [ - 7f32, 6., 5., 4., 3., 2., 1., 0., 15., 14., 13., 12., 11., 10., 9., 8., - ]; + let seq = [7f32, 6., 5., 4., 3., 2., 1., 0., 15., 14., 13., 12., 11., 10., 9., 8.]; for (a, b) in vi.iter().zip(seq.iter()) { assert_eq!(*a, *b); } @@ -592,7 +623,8 @@ fn test_negative_stride_arcarray() { } #[test] -fn test_cow() { +fn test_cow() +{ let mut mat = ArcArray::zeros((2, 2)); mat[[0, 0]] = 1; let n = mat.clone(); @@ -624,7 +656,8 @@ fn test_cow() { } #[test] -fn test_cow_shrink() { +fn test_cow_shrink() +{ // A test for clone-on-write in the case that // mutation shrinks the array and gives it different strides // @@ -659,41 +692,44 @@ fn test_cow_shrink() { #[test] #[cfg(feature = "std")] -fn test_sub() { - let mat = ArcArray::linspace(0., 15., 16).into_shape_with_order((2, 4, 2)).unwrap(); +fn test_sub() +{ + let mat = ArcArray::linspace(0., 15., 16) + .into_shape_with_order((2, 4, 2)) + .unwrap(); let s1 = mat.index_axis(Axis(0), 0); let s2 = mat.index_axis(Axis(0), 1); assert_eq!(s1.shape(), &[4, 2]); assert_eq!(s2.shape(), &[4, 2]); - let n = ArcArray::linspace(8., 15., 8).into_shape_with_order((4, 2)).unwrap(); + let n = ArcArray::linspace(8., 15., 8) + .into_shape_with_order((4, 2)) + .unwrap(); assert_eq!(n, s2); - let m = ArcArray::from(vec![2., 3., 10., 11.]).into_shape_with_order((2, 2)).unwrap(); + let m = ArcArray::from(vec![2., 3., 10., 11.]) + .into_shape_with_order((2, 2)) + .unwrap(); assert_eq!(m, mat.index_axis(Axis(1), 1)); } #[should_panic] #[test] #[cfg(feature = "std")] -fn test_sub_oob_1() { - let mat = ArcArray::linspace(0., 15., 16).into_shape_with_order((2, 4, 2)).unwrap(); +fn test_sub_oob_1() +{ + let mat = ArcArray::linspace(0., 15., 16) + .into_shape_with_order((2, 4, 2)) + .unwrap(); mat.index_axis(Axis(0), 2); } #[test] #[cfg(feature = "approx")] -fn test_select() { +fn test_select() +{ use approx::assert_abs_diff_eq; // test for 2-d array - let x = arr2(&[ - [0., 1.], - [1., 0.], - [1., 0.], - [1., 0.], - [1., 0.], - [0., 1.], - [0., 1.], - ]); + let x = arr2(&[[0., 1.], [1., 0.], [1., 0.], [1., 0.], [1., 0.], [0., 1.], [0., 1.]]); let r = x.select(Axis(0), &[1, 3, 5]); let c = x.select(Axis(1), &[1]); let r_target = arr2(&[[1., 0.], [1., 0.], [0., 1.]]); @@ -702,10 +738,7 @@ fn test_select() { assert_abs_diff_eq!(c, c_target.t()); // test for 3-d array - let y = arr3(&[ - [[1., 2., 3.], [1.5, 1.5, 3.]], - [[1., 2., 8.], [1., 2.5, 3.]], - ]); + let y = arr3(&[[[1., 2., 3.], [1.5, 1.5, 3.]], [[1., 2., 8.], [1., 2.5, 3.]]]); let r = y.select(Axis(1), &[1]); let c = y.select(Axis(2), &[1]); let r_target = arr3(&[[[1.5, 1.5, 3.]], [[1., 2.5, 3.]]]); @@ -715,7 +748,8 @@ fn test_select() { } #[test] -fn test_select_1d() { +fn test_select_1d() +{ let x = arr1(&[0, 1, 2, 3, 4, 5, 6]); let r1 = x.select(Axis(0), &[1, 3, 4, 2, 2, 5]); assert_eq!(r1, arr1(&[1, 3, 4, 2, 2, 5])); @@ -728,7 +762,8 @@ fn test_select_1d() { } #[test] -fn diag() { +fn diag() +{ let d = arr2(&[[1., 2., 3.0f32]]).into_diag(); assert_eq!(d.dim(), 1); let a = arr2(&[[1., 2., 3.0f32], [0., 0., 0.]]); @@ -745,7 +780,8 @@ fn diag() { /// Note that this does not check the strides in the "merged" case! #[test] #[allow(clippy::cognitive_complexity)] -fn merge_axes() { +fn merge_axes() +{ macro_rules! assert_merged { ($arr:expr, $slice:expr, $take:expr, $into:expr) => { let mut v = $arr.slice($slice); @@ -833,7 +869,8 @@ fn merge_axes() { } #[test] -fn swapaxes() { +fn swapaxes() +{ let mut a = arr2(&[[1., 2.], [3., 4.0f32]]); let b = arr2(&[[1., 3.], [2., 4.0f32]]); assert!(a != b); @@ -846,7 +883,8 @@ fn swapaxes() { } #[test] -fn permuted_axes() { +fn permuted_axes() +{ let a = array![1].index_axis_move(Axis(0), 0); let permuted = a.view().permuted_axes([]); assert_eq!(a, permuted); @@ -855,7 +893,9 @@ fn permuted_axes() { let permuted = a.view().permuted_axes([0]); assert_eq!(a, permuted); - let a = Array::from_iter(0..24).into_shape_with_order((2, 3, 4)).unwrap(); + let a = Array::from_iter(0..24) + .into_shape_with_order((2, 3, 4)) + .unwrap(); let permuted = a.view().permuted_axes([2, 1, 0]); for ((i0, i1, i2), elem) in a.indexed_iter() { assert_eq!(*elem, permuted[(i2, i1, i0)]); @@ -865,7 +905,9 @@ fn permuted_axes() { assert_eq!(*elem, permuted[&[i0, i2, i1][..]]); } - let a = Array::from_iter(0..120).into_shape_with_order((2, 3, 4, 5)).unwrap(); + let a = Array::from_iter(0..120) + .into_shape_with_order((2, 3, 4, 5)) + .unwrap(); let permuted = a.view().permuted_axes([1, 0, 3, 2]); for ((i0, i1, i2, i3), elem) in a.indexed_iter() { assert_eq!(*elem, permuted[(i1, i0, i3, i2)]); @@ -878,14 +920,18 @@ fn permuted_axes() { #[should_panic] #[test] -fn permuted_axes_repeated_axis() { - let a = Array::from_iter(0..24).into_shape_with_order((2, 3, 4)).unwrap(); +fn permuted_axes_repeated_axis() +{ + let a = Array::from_iter(0..24) + .into_shape_with_order((2, 3, 4)) + .unwrap(); a.view().permuted_axes([1, 0, 1]); } #[should_panic] #[test] -fn permuted_axes_missing_axis() { +fn permuted_axes_missing_axis() +{ let a = Array::from_iter(0..24) .into_shape_with_order((2, 3, 4)) .unwrap() @@ -895,13 +941,17 @@ fn permuted_axes_missing_axis() { #[should_panic] #[test] -fn permuted_axes_oob() { - let a = Array::from_iter(0..24).into_shape_with_order((2, 3, 4)).unwrap(); +fn permuted_axes_oob() +{ + let a = Array::from_iter(0..24) + .into_shape_with_order((2, 3, 4)) + .unwrap(); a.view().permuted_axes([1, 0, 3]); } #[test] -fn standard_layout() { +fn standard_layout() +{ let mut a = arr2(&[[1., 2.], [3., 4.0]]); assert!(a.is_standard_layout()); a.swap_axes(0, 1); @@ -919,7 +969,8 @@ fn standard_layout() { } #[test] -fn iter_size_hint() { +fn iter_size_hint() +{ let mut a = arr2(&[[1., 2.], [3., 4.]]); { let mut it = a.iter(); @@ -954,7 +1005,8 @@ fn iter_size_hint() { } #[test] -fn zero_axes() { +fn zero_axes() +{ let mut a = arr1::(&[]); for _ in a.iter() { panic!(); @@ -972,7 +1024,8 @@ fn zero_axes() { } #[test] -fn equality() { +fn equality() +{ let a = arr2(&[[1., 2.], [3., 4.]]); let mut b = arr2(&[[1., 2.], [2., 4.]]); assert!(a != b); @@ -985,7 +1038,8 @@ fn equality() { } #[test] -fn map1() { +fn map1() +{ let a = arr2(&[[1., 2.], [3., 4.]]); let b = a.map(|&x| (x / 3.) as isize); assert_eq!(b, arr2(&[[0, 0], [1, 1]])); @@ -995,21 +1049,24 @@ fn map1() { } #[test] -fn mapv_into_any_same_type() { +fn mapv_into_any_same_type() +{ let a: Array = array![[1., 2., 3.], [4., 5., 6.]]; let a_plus_one: Array = array![[2., 3., 4.], [5., 6., 7.]]; assert_eq!(a.mapv_into_any(|a| a + 1.), a_plus_one); } #[test] -fn mapv_into_any_diff_types() { +fn mapv_into_any_diff_types() +{ let a: Array = array![[1., 2., 3.], [4., 5., 6.]]; let a_even: Array = array![[false, true, false], [true, false, true]]; assert_eq!(a.mapv_into_any(|a| a.round() as i32 % 2 == 0), a_even); } #[test] -fn as_slice_memory_order_mut_arcarray() { +fn as_slice_memory_order_mut_arcarray() +{ // Test that mutation breaks sharing for `ArcArray`. let a = rcarr2(&[[1., 2.], [3., 4.0f32]]); let mut b = a.clone(); @@ -1020,7 +1077,8 @@ fn as_slice_memory_order_mut_arcarray() { } #[test] -fn as_slice_memory_order_mut_cowarray() { +fn as_slice_memory_order_mut_cowarray() +{ // Test that mutation breaks sharing for `CowArray`. let a = arr2(&[[1., 2.], [3., 4.0f32]]); let mut b = CowArray::from(a.view()); @@ -1031,7 +1089,8 @@ fn as_slice_memory_order_mut_cowarray() { } #[test] -fn as_slice_memory_order_mut_contiguous_arcarray() { +fn as_slice_memory_order_mut_contiguous_arcarray() +{ // Test that unsharing preserves the strides in the contiguous case for `ArcArray`. let a = rcarr2(&[[0, 5], [1, 6], [2, 7], [3, 8], [4, 9]]).reversed_axes(); let mut b = a.clone().slice_move(s![.., ..2]); @@ -1041,7 +1100,8 @@ fn as_slice_memory_order_mut_contiguous_arcarray() { } #[test] -fn as_slice_memory_order_mut_contiguous_cowarray() { +fn as_slice_memory_order_mut_contiguous_cowarray() +{ // Test that unsharing preserves the strides in the contiguous case for `CowArray`. let a = arr2(&[[0, 5], [1, 6], [2, 7], [3, 8], [4, 9]]).reversed_axes(); let mut b = CowArray::from(a.slice(s![.., ..2])); @@ -1052,10 +1112,13 @@ fn as_slice_memory_order_mut_contiguous_cowarray() { } #[test] -fn to_slice_memory_order() { +fn to_slice_memory_order() +{ for shape in vec![[2, 0, 3, 5], [2, 1, 3, 5], [2, 4, 3, 5]] { let data: Vec = (0..shape.iter().product()).collect(); - let mut orig = Array1::from(data.clone()).into_shape_with_order(shape).unwrap(); + let mut orig = Array1::from(data.clone()) + .into_shape_with_order(shape) + .unwrap(); for perm in vec![[0, 1, 2, 3], [0, 2, 1, 3], [2, 0, 1, 3]] { let mut a = orig.view_mut().permuted_axes(perm); assert_eq!(a.as_slice_memory_order().unwrap(), &data); @@ -1067,7 +1130,8 @@ fn to_slice_memory_order() { } #[test] -fn to_slice_memory_order_discontiguous() { +fn to_slice_memory_order_discontiguous() +{ let mut orig = Array3::::zeros([3, 2, 4]); assert!(orig .slice(s![.., 1.., ..]) @@ -1088,7 +1152,8 @@ fn to_slice_memory_order_discontiguous() { } #[test] -fn array0_into_scalar() { +fn array0_into_scalar() +{ // With this kind of setup, the `Array`'s pointer is not the same as the // underlying `Vec`'s pointer. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); @@ -1103,7 +1168,8 @@ fn array0_into_scalar() { } #[test] -fn array_view0_into_scalar() { +fn array_view0_into_scalar() +{ // With this kind of setup, the `Array`'s pointer is not the same as the // underlying `Vec`'s pointer. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); @@ -1118,7 +1184,8 @@ fn array_view0_into_scalar() { } #[test] -fn array_view_mut0_into_scalar() { +fn array_view_mut0_into_scalar() +{ // With this kind of setup, the `Array`'s pointer is not the same as the // underlying `Vec`'s pointer. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); @@ -1133,7 +1200,8 @@ fn array_view_mut0_into_scalar() { } #[test] -fn owned_array1() { +fn owned_array1() +{ let mut a = Array::from(vec![1, 2, 3, 4]); for elt in a.iter_mut() { *elt = 2; @@ -1158,7 +1226,8 @@ fn owned_array1() { } #[test] -fn owned_array_with_stride() { +fn owned_array_with_stride() +{ let v: Vec<_> = (0..12).collect(); let dim = (2, 3, 2); let strides = (1, 4, 2); @@ -1168,7 +1237,8 @@ fn owned_array_with_stride() { } #[test] -fn owned_array_discontiguous() { +fn owned_array_discontiguous() +{ use std::iter::repeat; let v: Vec<_> = (0..12).flat_map(|x| repeat(x).take(2)).collect(); let dim = (3, 2, 2); @@ -1181,14 +1251,17 @@ fn owned_array_discontiguous() { } #[test] -fn owned_array_discontiguous_drop() { +fn owned_array_discontiguous_drop() +{ use std::cell::RefCell; use std::collections::BTreeSet; use std::rc::Rc; struct InsertOnDrop(Rc>>, Option); - impl Drop for InsertOnDrop { - fn drop(&mut self) { + impl Drop for InsertOnDrop + { + fn drop(&mut self) + { let InsertOnDrop(ref set, ref mut value) = *self; set.borrow_mut().insert(value.take().expect("double drop!")); } @@ -1222,13 +1295,15 @@ macro_rules! assert_matches { } #[test] -fn from_vec_dim_stride_empty_1d() { +fn from_vec_dim_stride_empty_1d() +{ let empty: [f32; 0] = []; assert_matches!(Array::from_shape_vec(0.strides(1), empty.to_vec()), Ok(_)); } #[test] -fn from_vec_dim_stride_0d() { +fn from_vec_dim_stride_0d() +{ let empty: [f32; 0] = []; let one = [1.]; let two = [1., 2.]; @@ -1244,7 +1319,8 @@ fn from_vec_dim_stride_0d() { } #[test] -fn from_vec_dim_stride_2d_1() { +fn from_vec_dim_stride_2d_1() +{ let two = [1., 2.]; let d = Ix2(2, 1); let s = d.default_strides(); @@ -1252,7 +1328,8 @@ fn from_vec_dim_stride_2d_1() { } #[test] -fn from_vec_dim_stride_2d_2() { +fn from_vec_dim_stride_2d_2() +{ let two = [1., 2.]; let d = Ix2(1, 2); let s = d.default_strides(); @@ -1260,7 +1337,8 @@ fn from_vec_dim_stride_2d_2() { } #[test] -fn from_vec_dim_stride_2d_3() { +fn from_vec_dim_stride_2d_3() +{ let a = arr3(&[[[1]], [[2]], [[3]]]); let d = a.raw_dim(); let s = d.default_strides(); @@ -1271,7 +1349,8 @@ fn from_vec_dim_stride_2d_3() { } #[test] -fn from_vec_dim_stride_2d_4() { +fn from_vec_dim_stride_2d_4() +{ let a = arr3(&[[[1]], [[2]], [[3]]]); let d = a.raw_dim(); let s = d.fortran_strides(); @@ -1282,7 +1361,8 @@ fn from_vec_dim_stride_2d_4() { } #[test] -fn from_vec_dim_stride_2d_5() { +fn from_vec_dim_stride_2d_5() +{ let a = arr3(&[[[1, 2, 3]]]); let d = a.raw_dim(); let s = d.fortran_strides(); @@ -1293,7 +1373,8 @@ fn from_vec_dim_stride_2d_5() { } #[test] -fn from_vec_dim_stride_2d_6() { +fn from_vec_dim_stride_2d_6() +{ let a = [1., 2., 3., 4., 5., 6.]; let d = (2, 1, 1); let s = (2, 2, 1); @@ -1305,7 +1386,8 @@ fn from_vec_dim_stride_2d_6() { } #[test] -fn from_vec_dim_stride_2d_7() { +fn from_vec_dim_stride_2d_7() +{ // empty arrays can have 0 strides let a: [f32; 0] = []; // [[]] shape=[4, 0], strides=[0, 1] @@ -1315,7 +1397,8 @@ fn from_vec_dim_stride_2d_7() { } #[test] -fn from_vec_dim_stride_2d_8() { +fn from_vec_dim_stride_2d_8() +{ // strides of length 1 axes can be zero let a = [1.]; let d = (1, 1); @@ -1324,7 +1407,8 @@ fn from_vec_dim_stride_2d_8() { } #[test] -fn from_vec_dim_stride_2d_rejects() { +fn from_vec_dim_stride_2d_rejects() +{ let two = [1., 2.]; let d = (2, 2); let s = (1, 0); @@ -1336,8 +1420,11 @@ fn from_vec_dim_stride_2d_rejects() { } #[test] -fn views() { - let a = ArcArray::from(vec![1, 2, 3, 4]).into_shape_with_order((2, 2)).unwrap(); +fn views() +{ + let a = ArcArray::from(vec![1, 2, 3, 4]) + .into_shape_with_order((2, 2)) + .unwrap(); let b = a.view(); assert_eq!(a, b); assert_eq!(a.shape(), b.shape()); @@ -1353,8 +1440,11 @@ fn views() { } #[test] -fn view_mut() { - let mut a = ArcArray::from(vec![1, 2, 3, 4]).into_shape_with_order((2, 2)).unwrap(); +fn view_mut() +{ + let mut a = ArcArray::from(vec![1, 2, 3, 4]) + .into_shape_with_order((2, 2)) + .unwrap(); for elt in &mut a.view_mut() { *elt = 0; } @@ -1372,8 +1462,11 @@ fn view_mut() { } #[test] -fn slice_mut() { - let mut a = ArcArray::from(vec![1, 2, 3, 4]).into_shape_with_order((2, 2)).unwrap(); +fn slice_mut() +{ + let mut a = ArcArray::from(vec![1, 2, 3, 4]) + .into_shape_with_order((2, 2)) + .unwrap(); for elt in a.slice_mut(s![.., ..]) { *elt = 0; } @@ -1394,7 +1487,8 @@ fn slice_mut() { } #[test] -fn assign_ops() { +fn assign_ops() +{ let mut a = arr2(&[[1., 2.], [3., 4.]]); let b = arr2(&[[1., 3.], [2., 4.]]); (*&mut a.view_mut()) += &b; @@ -1412,7 +1506,8 @@ fn assign_ops() { } #[test] -fn aview() { +fn aview() +{ let a = arr2(&[[1., 2., 3.], [4., 5., 6.]]); let data = [[1., 2., 3.], [4., 5., 6.]]; let b = aview2(&data); @@ -1421,7 +1516,8 @@ fn aview() { } #[test] -fn aview_mut() { +fn aview_mut() +{ let mut data = [0; 16]; { let mut a = aview_mut1(&mut data).into_shape_with_order((4, 4)).unwrap(); @@ -1434,7 +1530,8 @@ fn aview_mut() { } #[test] -fn transpose_view() { +fn transpose_view() +{ let a = arr2(&[[1, 2], [3, 4]]); let at = a.view().reversed_axes(); assert_eq!(at, arr2(&[[1, 3], [2, 4]])); @@ -1445,7 +1542,8 @@ fn transpose_view() { } #[test] -fn transpose_view_mut() { +fn transpose_view_mut() +{ let mut a = arr2(&[[1, 2], [3, 4]]); let mut at = a.view_mut().reversed_axes(); at[[0, 1]] = 5; @@ -1459,7 +1557,8 @@ fn transpose_view_mut() { #[test] #[allow(clippy::cognitive_complexity)] -fn insert_axis() { +fn insert_axis() +{ defmac!(test_insert orig, index, new => { let res = orig.insert_axis(Axis(index)); assert_eq!(res, new); @@ -1554,7 +1653,8 @@ fn insert_axis() { } #[test] -fn insert_axis_f() { +fn insert_axis_f() +{ defmac!(test_insert_f orig, index, new => { let res = orig.insert_axis(Axis(index)); assert_eq!(res, new); @@ -1601,7 +1701,8 @@ fn insert_axis_f() { } #[test] -fn insert_axis_view() { +fn insert_axis_view() +{ let a = array![[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]]]; assert_eq!( @@ -1619,7 +1720,8 @@ fn insert_axis_view() { } #[test] -fn arithmetic_broadcast() { +fn arithmetic_broadcast() +{ let mut a = arr2(&[[1., 2.], [3., 4.]]); let b = a.clone() * aview0(&1.); assert_eq!(a, b); @@ -1678,14 +1780,18 @@ fn arithmetic_broadcast() { } #[test] -fn char_array() { +fn char_array() +{ // test compilation & basics of non-numerical array - let cc = ArcArray::from_iter("alphabet".chars()).into_shape_with_order((4, 2)).unwrap(); + let cc = ArcArray::from_iter("alphabet".chars()) + .into_shape_with_order((4, 2)) + .unwrap(); assert!(cc.index_axis(Axis(1), 0) == ArcArray::from_iter("apae".chars())); } #[test] -fn scalar_ops() { +fn scalar_ops() +{ let a = Array::::zeros((5, 5)); let b = &a + 1; let c = (&a + &a + 2) - 3; @@ -1723,7 +1829,8 @@ fn scalar_ops() { #[test] #[cfg(feature = "std")] -fn split_at() { +fn split_at() +{ let mut a = arr2(&[[1., 2.], [3., 4.]]); { @@ -1740,7 +1847,9 @@ fn split_at() { } assert_eq!(a, arr2(&[[1., 5.], [8., 4.]])); - let b = ArcArray::linspace(0., 59., 60).into_shape_with_order((3, 4, 5)).unwrap(); + let b = ArcArray::linspace(0., 59., 60) + .into_shape_with_order((3, 4, 5)) + .unwrap(); let (left, right) = b.view().split_at(Axis(2), 2); assert_eq!(left.shape(), [3, 4, 2]); @@ -1761,21 +1870,24 @@ fn split_at() { #[test] #[should_panic] -fn deny_split_at_axis_out_of_bounds() { +fn deny_split_at_axis_out_of_bounds() +{ let a = arr2(&[[1., 2.], [3., 4.]]); a.view().split_at(Axis(2), 0); } #[test] #[should_panic] -fn deny_split_at_index_out_of_bounds() { +fn deny_split_at_index_out_of_bounds() +{ let a = arr2(&[[1., 2.], [3., 4.]]); a.view().split_at(Axis(1), 3); } #[test] #[cfg(feature = "std")] -fn test_range() { +fn test_range() +{ let a = Array::range(0., 5., 1.); assert_eq!(a.len(), 5); assert_eq!(a[0], 0.); @@ -1804,7 +1916,8 @@ fn test_range() { } #[test] -fn test_f_order() { +fn test_f_order() +{ // Test that arrays are logically equal in every way, // even if the underlying memory order is different let c = arr2(&[[1, 2, 3], [4, 5, 6]]); @@ -1826,7 +1939,8 @@ fn test_f_order() { } #[test] -fn to_owned_memory_order() { +fn to_owned_memory_order() +{ // check that .to_owned() makes f-contiguous arrays out of f-contiguous // input. let c = arr2(&[[1, 2, 3], [4, 5, 6]]); @@ -1846,7 +1960,8 @@ fn to_owned_memory_order() { } #[test] -fn to_owned_neg_stride() { +fn to_owned_neg_stride() +{ let mut c = arr2(&[[1, 2, 3], [4, 5, 6]]); c.slice_collapse(s![.., ..;-1]); let co = c.to_owned(); @@ -1855,7 +1970,8 @@ fn to_owned_neg_stride() { } #[test] -fn discontiguous_owned_to_owned() { +fn discontiguous_owned_to_owned() +{ let mut c = arr2(&[[1, 2, 3], [4, 5, 6]]); c.slice_collapse(s![.., ..;2]); @@ -1866,7 +1982,8 @@ fn discontiguous_owned_to_owned() { } #[test] -fn map_memory_order() { +fn map_memory_order() +{ let a = arr3(&[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [0, -1, -2]]]); let mut v = a.view(); v.swap_axes(0, 1); @@ -1876,7 +1993,8 @@ fn map_memory_order() { } #[test] -fn map_mut_with_unsharing() { +fn map_mut_with_unsharing() +{ // Fortran-layout `ArcArray`. let a = rcarr2(&[[0, 5], [1, 6], [2, 7], [3, 8], [4, 9]]).reversed_axes(); assert_eq!(a.shape(), &[2, 5]); @@ -1903,10 +2021,13 @@ fn map_mut_with_unsharing() { } #[test] -fn test_view_from_shape() { +fn test_view_from_shape() +{ let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; let a = ArrayView::from_shape((2, 3, 2), &s).unwrap(); - let mut answer = Array::from(s.to_vec()).into_shape_with_order((2, 3, 2)).unwrap(); + let mut answer = Array::from(s.to_vec()) + .into_shape_with_order((2, 3, 2)) + .unwrap(); assert_eq!(a, answer); // custom strides (row major) @@ -1924,7 +2045,8 @@ fn test_view_from_shape() { } #[test] -fn test_contiguous() { +fn test_contiguous() +{ let c = arr3(&[[[1, 2, 3], [4, 5, 6]], [[4, 5, 6], [7, 7, 7]]]); assert!(c.is_standard_layout()); assert!(c.as_slice_memory_order().is_some()); @@ -1974,7 +2096,8 @@ fn test_contiguous_single_element() } #[test] -fn test_contiguous_neg_strides() { +fn test_contiguous_neg_strides() +{ let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; let a = ArrayView::from_shape((2, 3, 2).strides((1, 4, 2)), &s).unwrap(); assert_eq!( @@ -2032,7 +2155,8 @@ fn test_contiguous_neg_strides() { } #[test] -fn test_swap() { +fn test_swap() +{ let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9]]); let b = a.clone(); @@ -2045,7 +2169,8 @@ fn test_swap() { } #[test] -fn test_uswap() { +fn test_uswap() +{ let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9]]); let b = a.clone(); @@ -2058,7 +2183,8 @@ fn test_uswap() { } #[test] -fn test_shape() { +fn test_shape() +{ let data = [0, 1, 2, 3, 4, 5]; let a = Array::from_shape_vec((1, 2, 3), data.to_vec()).unwrap(); let b = Array::from_shape_vec((1, 2, 3).f(), data.to_vec()).unwrap(); @@ -2072,7 +2198,8 @@ fn test_shape() { } #[test] -fn test_view_from_shape_ptr() { +fn test_view_from_shape_ptr() +{ let data = [0, 1, 2, 3, 4, 5]; let view = unsafe { ArrayView::from_shape_ptr((2, 3), data.as_ptr()) }; assert_eq!(view, aview2(&[[0, 1, 2], [3, 4, 5]])); @@ -2088,45 +2215,42 @@ fn test_view_from_shape_ptr() { #[should_panic(expected = "Unsupported")] #[cfg(debug_assertions)] #[test] -fn test_view_from_shape_ptr_deny_neg_strides() { +fn test_view_from_shape_ptr_deny_neg_strides() +{ let data = [0, 1, 2, 3, 4, 5]; - let _view = unsafe { - ArrayView::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_ptr()) - }; + let _view = unsafe { ArrayView::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_ptr()) }; } #[should_panic(expected = "Unsupported")] #[cfg(debug_assertions)] #[test] -fn test_view_mut_from_shape_ptr_deny_neg_strides() { +fn test_view_mut_from_shape_ptr_deny_neg_strides() +{ let mut data = [0, 1, 2, 3, 4, 5]; - let _view = unsafe { - ArrayViewMut::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_mut_ptr()) - }; + let _view = unsafe { ArrayViewMut::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_mut_ptr()) }; } #[should_panic(expected = "Unsupported")] #[cfg(debug_assertions)] #[test] -fn test_raw_view_from_shape_ptr_deny_neg_strides() { +fn test_raw_view_from_shape_ptr_deny_neg_strides() +{ let data = [0, 1, 2, 3, 4, 5]; - let _view = unsafe { - RawArrayView::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_ptr()) - }; + let _view = unsafe { RawArrayView::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_ptr()) }; } #[should_panic(expected = "Unsupported")] #[cfg(debug_assertions)] #[test] -fn test_raw_view_mut_from_shape_ptr_deny_neg_strides() { +fn test_raw_view_mut_from_shape_ptr_deny_neg_strides() +{ let mut data = [0, 1, 2, 3, 4, 5]; - let _view = unsafe { - RawArrayViewMut::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_mut_ptr()) - }; + let _view = unsafe { RawArrayViewMut::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_mut_ptr()) }; } #[test] -fn test_default() { +fn test_default() +{ let a = as Default>::default(); assert_eq!(a, aview2(&[[0.0; 0]; 0])); @@ -2137,14 +2261,16 @@ fn test_default() { } #[test] -fn test_default_ixdyn() { +fn test_default_ixdyn() +{ let a = as Default>::default(); let b = >::zeros(IxDyn(&[0])); assert_eq!(a, b); } #[test] -fn test_map_axis() { +fn test_map_axis() +{ let a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); let b = a.map_axis(Axis(0), |view| view.sum()); @@ -2177,7 +2303,8 @@ fn test_map_axis() { } #[test] -fn test_accumulate_axis_inplace_noop() { +fn test_accumulate_axis_inplace_noop() +{ let mut a = Array2::::zeros((0, 3)); a.accumulate_axis_inplace(Axis(0), |&prev, curr| *curr += prev); assert_eq!(a, Array2::zeros((0, 3))); @@ -2219,7 +2346,8 @@ fn test_accumulate_axis_inplace_nonstandard_layout() { } #[test] -fn test_to_vec() { +fn test_to_vec() +{ let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); a.slice_collapse(s![..;-1, ..]); @@ -2230,7 +2358,8 @@ fn test_to_vec() { } #[test] -fn test_array_clone_unalias() { +fn test_array_clone_unalias() +{ let a = Array::::zeros((3, 3)); let mut b = a.clone(); b.fill(1); @@ -2239,15 +2368,19 @@ fn test_array_clone_unalias() { } #[test] -fn test_array_clone_same_view() { - let mut a = Array::from_iter(0..9).into_shape_with_order((3, 3)).unwrap(); +fn test_array_clone_same_view() +{ + let mut a = Array::from_iter(0..9) + .into_shape_with_order((3, 3)) + .unwrap(); a.slice_collapse(s![..;-1, ..;-1]); let b = a.clone(); assert_eq!(a, b); } #[test] -fn test_array2_from_diag() { +fn test_array2_from_diag() +{ let diag = arr1(&[0, 1, 2]); let x = Array2::from_diag(&diag); let x_exp = arr2(&[[0, 0, 0], [0, 1, 0], [0, 0, 2]]); @@ -2261,7 +2394,8 @@ fn test_array2_from_diag() { } #[test] -fn array_macros() { +fn array_macros() +{ // array let a1 = array![1, 2, 3]; assert_eq!(a1, arr1(&[1, 2, 3])); @@ -2289,7 +2423,8 @@ fn array_macros() { } #[cfg(test)] -mod as_standard_layout_tests { +mod as_standard_layout_tests +{ use super::*; use ndarray::Data; use std::fmt::Debug; @@ -2308,7 +2443,8 @@ mod as_standard_layout_tests { } #[test] - fn test_f_layout() { + fn test_f_layout() + { let shape = (2, 2).f(); let arr = Array::::from_shape_vec(shape, vec![1, 2, 3, 4]).unwrap(); assert!(!arr.is_standard_layout()); @@ -2316,14 +2452,16 @@ mod as_standard_layout_tests { } #[test] - fn test_c_layout() { + fn test_c_layout() + { let arr = Array::::from_shape_vec((2, 2), vec![1, 2, 3, 4]).unwrap(); assert!(arr.is_standard_layout()); test_as_standard_layout_for(arr); } #[test] - fn test_f_layout_view() { + fn test_f_layout_view() + { let shape = (2, 2).f(); let arr = Array::::from_shape_vec(shape, vec![1, 2, 3, 4]).unwrap(); let arr_view = arr.view(); @@ -2332,7 +2470,8 @@ mod as_standard_layout_tests { } #[test] - fn test_c_layout_view() { + fn test_c_layout_view() + { let arr = Array::::from_shape_vec((2, 2), vec![1, 2, 3, 4]).unwrap(); let arr_view = arr.view(); assert!(arr_view.is_standard_layout()); @@ -2340,14 +2479,16 @@ mod as_standard_layout_tests { } #[test] - fn test_zero_dimensional_array() { + fn test_zero_dimensional_array() + { let arr_view = ArrayView1::::from(&[]); assert!(arr_view.is_standard_layout()); test_as_standard_layout_for(arr_view); } #[test] - fn test_custom_layout() { + fn test_custom_layout() + { let shape = (1, 2, 3, 2).strides((12, 1, 2, 6)); let arr_data: Vec = (0..12).collect(); let arr = Array::::from_shape_vec(shape, arr_data).unwrap(); @@ -2357,11 +2498,13 @@ mod as_standard_layout_tests { } #[cfg(test)] -mod array_cow_tests { +mod array_cow_tests +{ use super::*; #[test] - fn test_is_variant() { + fn test_is_variant() + { let arr: Array = array![[1, 2], [3, 4]]; let arr_cow = CowArray::::from(arr.view()); assert!(arr_cow.is_view()); @@ -2371,7 +2514,8 @@ mod array_cow_tests { assert!(!arr_cow.is_view()); } - fn run_with_various_layouts(mut f: impl FnMut(Array2)) { + fn run_with_various_layouts(mut f: impl FnMut(Array2)) + { for all in vec![ Array2::from_shape_vec((7, 8), (0..7 * 8).collect()).unwrap(), Array2::from_shape_vec((7, 8).f(), (0..7 * 8).collect()).unwrap(), @@ -2389,7 +2533,8 @@ mod array_cow_tests { } #[test] - fn test_element_mutation() { + fn test_element_mutation() + { run_with_various_layouts(|arr: Array2| { let mut expected = arr.clone(); expected[(1, 1)] = 2; @@ -2409,7 +2554,8 @@ mod array_cow_tests { } #[test] - fn test_clone() { + fn test_clone() + { run_with_various_layouts(|arr: Array2| { let arr_cow = CowArray::::from(arr.view()); let arr_cow_clone = arr_cow.clone(); @@ -2428,11 +2574,10 @@ mod array_cow_tests { } #[test] - fn test_clone_from() { - fn assert_eq_contents_and_layout( - arr1: &CowArray<'_, i32, Ix2>, - arr2: &CowArray<'_, i32, Ix2>, - ) { + fn test_clone_from() + { + fn assert_eq_contents_and_layout(arr1: &CowArray<'_, i32, Ix2>, arr2: &CowArray<'_, i32, Ix2>) + { assert_eq!(arr1, arr2); assert_eq!(arr1.dim(), arr2.dim()); assert_eq!(arr1.strides(), arr2.strides()); @@ -2468,7 +2613,8 @@ mod array_cow_tests { } #[test] - fn test_into_owned() { + fn test_into_owned() + { run_with_various_layouts(|arr: Array2| { let before = CowArray::::from(arr.view()); let after = before.into_owned(); @@ -2484,11 +2630,9 @@ mod array_cow_tests { } #[test] -fn test_remove_index() { - let mut a = arr2(&[[1, 2, 3], - [4, 5, 6], - [7, 8, 9], - [10,11,12]]); +fn test_remove_index() +{ + let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); a.remove_index(Axis(0), 1); a.remove_index(Axis(1), 2); assert_eq!(a.shape(), &[3, 2]); @@ -2497,10 +2641,7 @@ fn test_remove_index() { [7, 8], [10,11]]); - let mut a = arr2(&[[1, 2, 3], - [4, 5, 6], - [7, 8, 9], - [10,11,12]]); + let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); a.invert_axis(Axis(0)); a.remove_index(Axis(0), 1); a.remove_index(Axis(1), 2); @@ -2525,19 +2666,18 @@ fn test_remove_index() { []]); } -#[should_panic(expected="must be less")] +#[should_panic(expected = "must be less")] #[test] -fn test_remove_index_oob1() { - let mut a = arr2(&[[1, 2, 3], - [4, 5, 6], - [7, 8, 9], - [10,11,12]]); +fn test_remove_index_oob1() +{ + let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); a.remove_index(Axis(0), 4); } -#[should_panic(expected="must be less")] +#[should_panic(expected = "must be less")] #[test] -fn test_remove_index_oob2() { +fn test_remove_index_oob2() +{ let mut a = array![[10], [4], [1]]; a.remove_index(Axis(1), 0); assert_eq!(a.shape(), &[3, 0]); @@ -2552,41 +2692,37 @@ fn test_remove_index_oob2() { a.remove_index(Axis(1), 0); // oob } -#[should_panic(expected="index out of bounds")] +#[should_panic(expected = "index out of bounds")] #[test] -fn test_remove_index_oob3() { +fn test_remove_index_oob3() +{ let mut a = array![[10], [4], [1]]; a.remove_index(Axis(2), 0); } #[test] -fn test_split_complex_view() { - let a = Array3::from_shape_fn((3, 4, 5), |(i, j, k)| { - Complex::::new(i as f32 * j as f32, k as f32) - }); +fn test_split_complex_view() +{ + let a = Array3::from_shape_fn((3, 4, 5), |(i, j, k)| Complex::::new(i as f32 * j as f32, k as f32)); let Complex { re, im } = a.view().split_complex(); assert_relative_eq!(re.sum(), 90.); assert_relative_eq!(im.sum(), 120.); } #[test] -fn test_split_complex_view_roundtrip() { - let a_re = Array3::from_shape_fn((3,1,5), |(i, j, _k)| { - i * j - }); - let a_im = Array3::from_shape_fn((3,1,5), |(_i, _j, k)| { - k - }); - let a = Array3::from_shape_fn((3,1,5), |(i,j,k)| { - Complex::new(a_re[[i,j,k]], a_im[[i,j,k]]) - }); +fn test_split_complex_view_roundtrip() +{ + let a_re = Array3::from_shape_fn((3, 1, 5), |(i, j, _k)| i * j); + let a_im = Array3::from_shape_fn((3, 1, 5), |(_i, _j, k)| k); + let a = Array3::from_shape_fn((3, 1, 5), |(i, j, k)| Complex::new(a_re[[i, j, k]], a_im[[i, j, k]])); let Complex { re, im } = a.view().split_complex(); assert_eq!(a_re, re); assert_eq!(a_im, im); } #[test] -fn test_split_complex_view_mut() { +fn test_split_complex_view_mut() +{ let eye_scalar = Array2::::eye(4); let eye_complex = Array2::>::eye(4); let mut a = Array2::>::zeros((4, 4)); @@ -2597,7 +2733,8 @@ fn test_split_complex_view_mut() { } #[test] -fn test_split_complex_zerod() { +fn test_split_complex_zerod() +{ let mut a = Array0::from_elem((), Complex::new(42, 32)); let Complex { re, im } = a.view().split_complex(); assert_eq!(re.get(()), Some(&42)); @@ -2608,18 +2745,18 @@ fn test_split_complex_zerod() { } #[test] -fn test_split_complex_permuted() { - let a = Array3::from_shape_fn((3, 4, 5), |(i, j, k)| { - Complex::new(i * k + j, k) - }); - let permuted = a.view().permuted_axes([1,0,2]); +fn test_split_complex_permuted() +{ + let a = Array3::from_shape_fn((3, 4, 5), |(i, j, k)| Complex::new(i * k + j, k)); + let permuted = a.view().permuted_axes([1, 0, 2]); let Complex { re, im } = permuted.split_complex(); assert_eq!(re.get((3,2,4)).unwrap(), &11); assert_eq!(im.get((3,2,4)).unwrap(), &4); } #[test] -fn test_split_complex_invert_axis() { +fn test_split_complex_invert_axis() +{ let mut a = Array::from_shape_fn((2, 3, 2), |(i, j, k)| Complex::new(i as f64 + j as f64, i as f64 + k as f64)); a.invert_axis(Axis(1)); let cmplx = a.view().split_complex(); diff --git a/tests/assign.rs b/tests/assign.rs index 5c300943d..29a6b851a 100644 --- a/tests/assign.rs +++ b/tests/assign.rs @@ -3,7 +3,8 @@ use ndarray::prelude::*; use std::sync::atomic::{AtomicUsize, Ordering}; #[test] -fn assign() { +fn assign() +{ let mut a = arr2(&[[1., 2.], [3., 4.]]); let b = arr2(&[[1., 3.], [2., 4.]]); a.assign(&b); @@ -27,9 +28,9 @@ fn assign() { assert_eq!(a, arr2(&[[0, 0], [3, 4]])); } - #[test] -fn assign_to() { +fn assign_to() +{ let mut a = arr2(&[[1., 2.], [3., 4.]]); let b = arr2(&[[0., 3.], [2., 0.]]); b.assign_to(&mut a); @@ -37,7 +38,8 @@ fn assign_to() { } #[test] -fn move_into_copy() { +fn move_into_copy() +{ let a = arr2(&[[1., 2.], [3., 4.]]); let acopy = a.clone(); let mut b = Array::uninit(a.dim()); @@ -54,13 +56,14 @@ fn move_into_copy() { } #[test] -fn move_into_owned() { +fn move_into_owned() +{ // Test various memory layouts and holes while moving String elements. for &use_f_order in &[false, true] { - for &invert_axis in &[0b00, 0b01, 0b10, 0b11] { // bitmask for axis to invert + for &invert_axis in &[0b00, 0b01, 0b10, 0b11] { + // bitmask for axis to invert for &slice in &[false, true] { - let mut a = Array::from_shape_fn((5, 4).set_f(use_f_order), - |idx| format!("{:?}", idx)); + let mut a = Array::from_shape_fn((5, 4).set_f(use_f_order), |idx| format!("{:?}", idx)); if slice { a.slice_collapse(s![1..-1, ..;2]); } @@ -84,10 +87,12 @@ fn move_into_owned() { } #[test] -fn move_into_slicing() { +fn move_into_slicing() +{ // Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes). for &use_f_order in &[false, true] { - for &invert_axis in &[0b00, 0b01, 0b10, 0b11] { // bitmask for axis to invert + for &invert_axis in &[0b00, 0b01, 0b10, 0b11] { + // bitmask for axis to invert let counter = DropCounter::default(); { let (m, n) = (5, 4); @@ -117,7 +122,8 @@ fn move_into_slicing() { } #[test] -fn move_into_diag() { +fn move_into_diag() +{ // Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes). for &use_f_order in &[false, true] { let counter = DropCounter::default(); @@ -142,7 +148,8 @@ fn move_into_diag() { } #[test] -fn move_into_0dim() { +fn move_into_0dim() +{ // Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes). for &use_f_order in &[false, true] { let counter = DropCounter::default(); @@ -169,7 +176,8 @@ fn move_into_0dim() { } #[test] -fn move_into_empty() { +fn move_into_empty() +{ // Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes). for &use_f_order in &[false, true] { let counter = DropCounter::default(); @@ -195,13 +203,14 @@ fn move_into_empty() { } #[test] -fn move_into() { +fn move_into() +{ // Test various memory layouts and holes while moving String elements with move_into for &use_f_order in &[false, true] { - for &invert_axis in &[0b00, 0b01, 0b10, 0b11] { // bitmask for axis to invert + for &invert_axis in &[0b00, 0b01, 0b10, 0b11] { + // bitmask for axis to invert for &slice in &[false, true] { - let mut a = Array::from_shape_fn((5, 4).set_f(use_f_order), - |idx| format!("{:?}", idx)); + let mut a = Array::from_shape_fn((5, 4).set_f(use_f_order), |idx| format!("{:?}", idx)); if slice { a.slice_collapse(s![1..-1, ..;2]); } @@ -223,32 +232,37 @@ fn move_into() { } } - /// This counter can create elements, and then count and verify /// the number of which have actually been dropped again. #[derive(Default)] -struct DropCounter { +struct DropCounter +{ created: AtomicUsize, dropped: AtomicUsize, } struct Element<'a>(&'a AtomicUsize); -impl DropCounter { - fn created(&self) -> usize { +impl DropCounter +{ + fn created(&self) -> usize + { self.created.load(Ordering::Relaxed) } - fn dropped(&self) -> usize { + fn dropped(&self) -> usize + { self.dropped.load(Ordering::Relaxed) } - fn element(&self) -> Element<'_> { + fn element(&self) -> Element<'_> + { self.created.fetch_add(1, Ordering::Relaxed); Element(&self.dropped) } - fn assert_drop_count(&self) { + fn assert_drop_count(&self) + { assert_eq!( self.created(), self.dropped(), @@ -259,8 +273,10 @@ impl DropCounter { } } -impl<'a> Drop for Element<'a> { - fn drop(&mut self) { +impl<'a> Drop for Element<'a> +{ + fn drop(&mut self) + { self.0.fetch_add(1, Ordering::Relaxed); } } diff --git a/tests/azip.rs b/tests/azip.rs index d41c019dd..a4bb6ffac 100644 --- a/tests/azip.rs +++ b/tests/azip.rs @@ -1,8 +1,5 @@ #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names, + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, clippy::float_cmp )] @@ -14,7 +11,8 @@ use itertools::{assert_equal, cloned}; use std::mem::swap; #[test] -fn test_azip1() { +fn test_azip1() +{ let mut a = Array::zeros(62); let mut x = 0; azip!((a in &mut a) { *a = x; x += 1; }); @@ -22,7 +20,8 @@ fn test_azip1() { } #[test] -fn test_azip2() { +fn test_azip2() +{ let mut a = Array::zeros((5, 7)); let b = Array::from_shape_fn(a.dim(), |(i, j)| 1. / (i + 2 * j) as f32); azip!((a in &mut a, &b in &b) *a = b); @@ -30,7 +29,8 @@ fn test_azip2() { } #[test] -fn test_azip2_1() { +fn test_azip2_1() +{ let mut a = Array::zeros((5, 7)); let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j) as f32); let b = b.slice(s![..;-1, 3..]); @@ -39,7 +39,8 @@ fn test_azip2_1() { } #[test] -fn test_azip2_3() { +fn test_azip2_3() +{ let mut b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j) as f32); let mut c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); let a = b.clone(); @@ -50,7 +51,8 @@ fn test_azip2_3() { #[test] #[cfg(feature = "approx")] -fn test_zip_collect() { +fn test_zip_collect() +{ use approx::assert_abs_diff_eq; // test Zip::map_collect and that it preserves c/f layout. @@ -78,7 +80,8 @@ fn test_zip_collect() { #[test] #[cfg(feature = "approx")] -fn test_zip_assign_into() { +fn test_zip_assign_into() +{ use approx::assert_abs_diff_eq; let mut a = Array::::zeros((5, 10)); @@ -92,7 +95,8 @@ fn test_zip_assign_into() { #[test] #[cfg(feature = "approx")] -fn test_zip_assign_into_cell() { +fn test_zip_assign_into_cell() +{ use approx::assert_abs_diff_eq; use std::cell::Cell; @@ -107,33 +111,40 @@ fn test_zip_assign_into_cell() { } #[test] -fn test_zip_collect_drop() { +fn test_zip_collect_drop() +{ use std::cell::RefCell; use std::panic; struct Recorddrop<'a>((usize, usize), &'a RefCell>); - impl<'a> Drop for Recorddrop<'a> { - fn drop(&mut self) { + impl<'a> Drop for Recorddrop<'a> + { + fn drop(&mut self) + { self.1.borrow_mut().push(self.0); } } #[derive(Copy, Clone)] - enum Config { + enum Config + { CC, CF, FF, } - impl Config { - fn a_is_f(self) -> bool { + impl Config + { + fn a_is_f(self) -> bool + { match self { Config::CC | Config::CF => false, _ => true, } } - fn b_is_f(self) -> bool { + fn b_is_f(self) -> bool + { match self { Config::CC => false, _ => true, @@ -178,9 +189,9 @@ fn test_zip_collect_drop() { } } - #[test] -fn test_azip_syntax_trailing_comma() { +fn test_azip_syntax_trailing_comma() +{ let mut b = Array::::zeros((5, 5)); let mut c = Array::::ones((5, 5)); let a = b.clone(); @@ -191,7 +202,8 @@ fn test_azip_syntax_trailing_comma() { #[test] #[cfg(feature = "approx")] -fn test_azip2_sum() { +fn test_azip2_sum() +{ use approx::assert_abs_diff_eq; let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); @@ -205,7 +217,8 @@ fn test_azip2_sum() { #[test] #[cfg(feature = "approx")] -fn test_azip3_slices() { +fn test_azip3_slices() +{ use approx::assert_abs_diff_eq; let mut a = [0.; 32]; @@ -225,7 +238,8 @@ fn test_azip3_slices() { #[test] #[cfg(feature = "approx")] -fn test_broadcast() { +fn test_broadcast() +{ use approx::assert_abs_diff_eq; let n = 16; @@ -250,7 +264,8 @@ fn test_broadcast() { #[should_panic] #[test] -fn test_zip_dim_mismatch_1() { +fn test_zip_dim_mismatch_1() +{ let mut a = Array::zeros((5, 7)); let mut d = a.raw_dim(); d[0] += 1; @@ -262,8 +277,11 @@ fn test_zip_dim_mismatch_1() { // Zip::from(A).and(B) // where A is F-contiguous and B contiguous but neither F nor C contiguous. #[test] -fn test_contiguous_but_not_c_or_f() { - let a = Array::from_iter(0..27).into_shape_with_order((3, 3, 3)).unwrap(); +fn test_contiguous_but_not_c_or_f() +{ + let a = Array::from_iter(0..27) + .into_shape_with_order((3, 3, 3)) + .unwrap(); // both F order let a = a.reversed_axes(); @@ -286,8 +304,11 @@ fn test_contiguous_but_not_c_or_f() { } #[test] -fn test_clone() { - let a = Array::from_iter(0..27).into_shape_with_order((3, 3, 3)).unwrap(); +fn test_clone() +{ + let a = Array::from_iter(0..27) + .into_shape_with_order((3, 3, 3)) + .unwrap(); let z = Zip::from(&a).and(a.exact_chunks((1, 1, 1))); let w = z.clone(); @@ -303,7 +324,8 @@ fn test_clone() { } #[test] -fn test_indices_0() { +fn test_indices_0() +{ let a1 = arr0(3); let mut count = 0; @@ -316,7 +338,8 @@ fn test_indices_0() { } #[test] -fn test_indices_1() { +fn test_indices_1() +{ let mut a1 = Array::default(12); for (i, elt) in a1.indexed_iter_mut() { *elt = i; @@ -346,7 +369,8 @@ fn test_indices_1() { } #[test] -fn test_indices_2() { +fn test_indices_2() +{ let mut a1 = Array::default((10, 12)); for (i, elt) in a1.indexed_iter_mut() { *elt = i; @@ -376,7 +400,8 @@ fn test_indices_2() { } #[test] -fn test_indices_3() { +fn test_indices_3() +{ let mut a1 = Array::default((4, 5, 6)); for (i, elt) in a1.indexed_iter_mut() { *elt = i; @@ -406,7 +431,8 @@ fn test_indices_3() { } #[test] -fn test_indices_split_1() { +fn test_indices_split_1() +{ for m in (0..4).chain(10..12) { for n in (0..4).chain(10..12) { let a1 = Array::::default((m, n)); @@ -438,7 +464,8 @@ fn test_indices_split_1() { } #[test] -fn test_zip_all() { +fn test_zip_all() +{ let a = Array::::zeros(62); let b = Array::::ones(62); let mut c = Array::::ones(62); @@ -449,7 +476,8 @@ fn test_zip_all() { } #[test] -fn test_zip_all_empty_array() { +fn test_zip_all_empty_array() +{ let a = Array::::zeros(0); let b = Array::::ones(0); assert_eq!(true, Zip::from(&a).and(&b).all(|&_x, &_y| true)); diff --git a/tests/broadcast.rs b/tests/broadcast.rs index 6dee901e2..288ccb38a 100644 --- a/tests/broadcast.rs +++ b/tests/broadcast.rs @@ -2,15 +2,22 @@ use ndarray::prelude::*; #[test] #[cfg(feature = "std")] -fn broadcast_1() { +fn broadcast_1() +{ let a_dim = Dim([2, 4, 2, 2]); let b_dim = Dim([2, 1, 2, 1]); - let a = ArcArray::linspace(0., 1., a_dim.size()).into_shape_with_order(a_dim).unwrap(); - let b = ArcArray::linspace(0., 1., b_dim.size()).into_shape_with_order(b_dim).unwrap(); + let a = ArcArray::linspace(0., 1., a_dim.size()) + .into_shape_with_order(a_dim) + .unwrap(); + let b = ArcArray::linspace(0., 1., b_dim.size()) + .into_shape_with_order(b_dim) + .unwrap(); assert!(b.broadcast(a.dim()).is_some()); let c_dim = Dim([2, 1]); - let c = ArcArray::linspace(0., 1., c_dim.size()).into_shape_with_order(c_dim).unwrap(); + let c = ArcArray::linspace(0., 1., c_dim.size()) + .into_shape_with_order(c_dim) + .unwrap(); assert!(c.broadcast(1).is_none()); assert!(c.broadcast(()).is_none()); assert!(c.broadcast((2, 1)).is_some()); @@ -28,11 +35,16 @@ fn broadcast_1() { #[test] #[cfg(feature = "std")] -fn test_add() { +fn test_add() +{ let a_dim = Dim([2, 4, 2, 2]); let b_dim = Dim([2, 1, 2, 1]); - let mut a = ArcArray::linspace(0.0, 1., a_dim.size()).into_shape_with_order(a_dim).unwrap(); - let b = ArcArray::linspace(0.0, 1., b_dim.size()).into_shape_with_order(b_dim).unwrap(); + let mut a = ArcArray::linspace(0.0, 1., a_dim.size()) + .into_shape_with_order(a_dim) + .unwrap(); + let b = ArcArray::linspace(0.0, 1., b_dim.size()) + .into_shape_with_order(b_dim) + .unwrap(); a += &b; let t = ArcArray::from_elem((), 1.0f32); a += &t; @@ -41,15 +53,19 @@ fn test_add() { #[test] #[should_panic] #[cfg(feature = "std")] -fn test_add_incompat() { +fn test_add_incompat() +{ let a_dim = Dim([2, 4, 2, 2]); - let mut a = ArcArray::linspace(0.0, 1., a_dim.size()).into_shape_with_order(a_dim).unwrap(); + let mut a = ArcArray::linspace(0.0, 1., a_dim.size()) + .into_shape_with_order(a_dim) + .unwrap(); let incompat = ArcArray::from_elem(3, 1.0f32); a += &incompat; } #[test] -fn test_broadcast() { +fn test_broadcast() +{ let (_, n, k) = (16, 16, 16); let x1 = 1.; // b0 broadcast 1 -> n, k @@ -69,7 +85,8 @@ fn test_broadcast() { } #[test] -fn test_broadcast_1d() { +fn test_broadcast_1d() +{ let n = 16; let x1 = 1.; // b0 broadcast 1 -> n diff --git a/tests/clone.rs b/tests/clone.rs index e1914ba7f..4a7e50b8e 100644 --- a/tests/clone.rs +++ b/tests/clone.rs @@ -1,7 +1,8 @@ use ndarray::arr2; #[test] -fn test_clone_from() { +fn test_clone_from() +{ let a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9]]); let b = arr2(&[[7, 7, 7]]); let mut c = b.clone(); diff --git a/tests/complex.rs b/tests/complex.rs index 1b52b2671..824e296a4 100644 --- a/tests/complex.rs +++ b/tests/complex.rs @@ -3,12 +3,14 @@ use ndarray::{arr1, arr2, Axis}; use num_complex::Complex; use num_traits::Num; -fn c(re: T, im: T) -> Complex { +fn c(re: T, im: T) -> Complex +{ Complex::new(re, im) } #[test] -fn complex_mat_mul() { +fn complex_mat_mul() +{ let a = arr2(&[[c(3., 4.), c(2., 0.)], [c(0., -2.), c(3., 0.)]]); let b = (&a * c(3., 0.)).map(|c| 5. * c / c.norm_sqr()); println!("{:>8.2}", b); diff --git a/tests/dimension.rs b/tests/dimension.rs index 5dae5b5a3..6a9207e4c 100644 --- a/tests/dimension.rs +++ b/tests/dimension.rs @@ -7,7 +7,8 @@ use ndarray::{arr2, ArcArray, Array, Axis, Dim, Dimension, IxDyn, RemoveAxis}; use std::hash::{Hash, Hasher}; #[test] -fn insert_axis() { +fn insert_axis() +{ assert_eq!(Dim([]).insert_axis(Axis(0)), Dim([1])); assert_eq!(Dim([3]).insert_axis(Axis(0)), Dim([1, 3])); @@ -41,7 +42,8 @@ fn insert_axis() { } #[test] -fn remove_axis() { +fn remove_axis() +{ assert_eq!(Dim([3]).remove_axis(Axis(0)), Dim([])); assert_eq!(Dim([1, 2]).remove_axis(Axis(0)), Dim([2])); assert_eq!(Dim([4, 5, 6]).remove_axis(Axis(1)), Dim([4, 6])); @@ -55,14 +57,19 @@ fn remove_axis() { let a = ArcArray::::zeros(vec![4, 5, 6]); let _b = a .index_axis_move(Axis(1), 0) - .to_shape((4, 6)).unwrap() - .to_shape(vec![2, 3, 4]).unwrap(); + .to_shape((4, 6)) + .unwrap() + .to_shape(vec![2, 3, 4]) + .unwrap(); } #[test] #[allow(clippy::eq_op)] -fn dyn_dimension() { - let a = arr2(&[[1., 2.], [3., 4.0]]).into_shape_with_order(vec![2, 2]).unwrap(); +fn dyn_dimension() +{ + let a = arr2(&[[1., 2.], [3., 4.0]]) + .into_shape_with_order(vec![2, 2]) + .unwrap(); assert_eq!(&a - &a, Array::zeros(vec![2, 2])); assert_eq!(a[&[0, 0][..]], 1.); assert_eq!(a[[0, 0]], 1.); @@ -75,7 +82,8 @@ fn dyn_dimension() { } #[test] -fn dyn_insert() { +fn dyn_insert() +{ let mut v = vec![2, 3, 4, 5]; let mut dim = Dim(v.clone()); defmac!(test_insert index => { @@ -94,7 +102,8 @@ fn dyn_insert() { } #[test] -fn dyn_remove() { +fn dyn_remove() +{ let mut v = vec![1, 2, 3, 4, 5, 6, 7]; let mut dim = Dim(v.clone()); defmac!(test_remove index => { @@ -113,7 +122,8 @@ fn dyn_remove() { } #[test] -fn fastest_varying_order() { +fn fastest_varying_order() +{ let strides = Dim([2, 8, 4, 1]); let order = strides._fastest_varying_stride_order(); assert_eq!(order.slice(), &[3, 0, 2, 1]); @@ -186,7 +196,8 @@ fn min_stride_axis() { */ #[test] -fn max_stride_axis() { +fn max_stride_axis() +{ let a = ArrayF32::zeros(10); assert_eq!(a.max_stride_axis(), Axis(0)); @@ -213,7 +224,8 @@ fn max_stride_axis() { } #[test] -fn test_indexing() { +fn test_indexing() +{ let mut x = Dim([1, 2]); assert_eq!(x[0], 1); @@ -224,7 +236,8 @@ fn test_indexing() { } #[test] -fn test_operations() { +fn test_operations() +{ let mut x = Dim([1, 2]); let mut y = Dim([1, 1]); @@ -241,8 +254,10 @@ fn test_operations() { #[test] #[allow(clippy::cognitive_complexity)] -fn test_hash() { - fn calc_hash(value: &T) -> u64 { +fn test_hash() +{ + fn calc_hash(value: &T) -> u64 + { let mut hasher = std::collections::hash_map::DefaultHasher::new(); value.hash(&mut hasher); hasher.finish() @@ -277,8 +292,10 @@ fn test_hash() { } #[test] -fn test_generic_operations() { - fn test_dim(d: &D) { +fn test_generic_operations() +{ + fn test_dim(d: &D) + { let mut x = d.clone(); x[0] += 1; assert_eq!(x[0], 3); @@ -292,8 +309,10 @@ fn test_generic_operations() { } #[test] -fn test_array_view() { - fn test_dim(d: &D) { +fn test_array_view() +{ + fn test_dim(d: &D) + { assert_eq!(d.as_array_view().sum(), 7); assert_eq!(d.as_array_view().strides(), &[1]); } @@ -306,10 +325,11 @@ fn test_array_view() { #[test] #[cfg(feature = "std")] #[allow(clippy::cognitive_complexity)] -fn test_all_ndindex() { +fn test_all_ndindex() +{ use ndarray::IntoDimension; macro_rules! ndindex { - ($($i:expr),*) => { + ($($i:expr),*) => { for &rev in &[false, true] { // rev is for C / F order let size = $($i *)* 1; @@ -331,8 +351,8 @@ fn test_all_ndindex() { assert_eq!(elt, b[dim]); } } + }; } -} ndindex!(10); ndindex!(10, 4); ndindex!(10, 4, 3); diff --git a/tests/format.rs b/tests/format.rs index 4b21fe39d..35909871f 100644 --- a/tests/format.rs +++ b/tests/format.rs @@ -2,7 +2,8 @@ use ndarray::prelude::*; use ndarray::rcarr1; #[test] -fn formatting() { +fn formatting() +{ let a = rcarr1::(&[1., 2., 3., 4.]); assert_eq!(format!("{}", a), "[1, 2, 3, 4]"); assert_eq!(format!("{:4}", a), "[ 1, 2, 3, 4]"); @@ -55,7 +56,8 @@ fn formatting() { } #[test] -fn debug_format() { +fn debug_format() +{ let a = Array2::::zeros((3, 4)); assert_eq!( format!("{:?}", a), diff --git a/tests/higher_order_f.rs b/tests/higher_order_f.rs index c567eb3e0..72245412f 100644 --- a/tests/higher_order_f.rs +++ b/tests/higher_order_f.rs @@ -2,7 +2,8 @@ use ndarray::prelude::*; #[test] #[should_panic] -fn test_fold_axis_oob() { +fn test_fold_axis_oob() +{ let a = arr2(&[[1., 2.], [3., 4.]]); a.fold_axis(Axis(2), 0., |x, y| x + y); } diff --git a/tests/indices.rs b/tests/indices.rs index ca6ca9887..a9414f9a7 100644 --- a/tests/indices.rs +++ b/tests/indices.rs @@ -3,7 +3,8 @@ use ndarray::prelude::*; use ndarray::Order; #[test] -fn test_ixdyn_index_iterate() { +fn test_ixdyn_index_iterate() +{ for &order in &[Order::C, Order::F] { let mut a = Array::zeros((2, 3, 4).set_f(order.is_column_major())); let dim = a.shape().to_vec(); diff --git a/tests/into-ixdyn.rs b/tests/into-ixdyn.rs index a9383a0e6..6e7bf9607 100644 --- a/tests/into-ixdyn.rs +++ b/tests/into-ixdyn.rs @@ -1,20 +1,19 @@ #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names, + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, clippy::float_cmp )] use ndarray::prelude::*; #[test] -fn test_arr0_into_dyn() { +fn test_arr0_into_dyn() +{ assert!(arr0(1.234).into_dyn()[IxDyn(&[])] == 1.234); } #[test] -fn test_arr2_into_arrd_nonstandard_strides() { +fn test_arr2_into_arrd_nonstandard_strides() +{ let arr = Array2::from_shape_fn((12, 34).f(), |(i, j)| i * 34 + j).into_dyn(); let brr = ArrayD::from_shape_fn(vec![12, 34], |d| d[0] * 34 + d[1]); diff --git a/tests/iterator_chunks.rs b/tests/iterator_chunks.rs index bf3af2d56..79b5403ef 100644 --- a/tests/iterator_chunks.rs +++ b/tests/iterator_chunks.rs @@ -1,8 +1,5 @@ #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names, + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, clippy::float_cmp )] @@ -10,7 +7,8 @@ use ndarray::prelude::*; #[test] #[cfg(feature = "std")] -fn chunks() { +fn chunks() +{ use ndarray::NdProducer; let a = >::linspace(1., 100., 10 * 10) .into_shape_with_order((10, 10)) @@ -49,13 +47,15 @@ fn chunks() { #[should_panic] #[test] -fn chunks_different_size_1() { +fn chunks_different_size_1() +{ let a = Array::::zeros(vec![2, 3]); a.exact_chunks(vec![2]); } #[test] -fn chunks_ok_size() { +fn chunks_ok_size() +{ let mut a = Array::::zeros(vec![2, 3]); a.fill(1.); let mut c = 0; @@ -69,13 +69,15 @@ fn chunks_ok_size() { #[should_panic] #[test] -fn chunks_different_size_2() { +fn chunks_different_size_2() +{ let a = Array::::zeros(vec![2, 3]); a.exact_chunks(vec![2, 3, 4]); } #[test] -fn chunks_mut() { +fn chunks_mut() +{ let mut a = Array::zeros((7, 8)); for (i, mut chunk) in a.exact_chunks_mut((2, 3)).into_iter().enumerate() { chunk.fill(i); @@ -95,7 +97,8 @@ fn chunks_mut() { #[should_panic] #[test] -fn chunks_different_size_3() { +fn chunks_different_size_3() +{ let mut a = Array::::zeros(vec![2, 3]); a.exact_chunks_mut(vec![2, 3, 4]); } diff --git a/tests/iterators.rs b/tests/iterators.rs index b2cd58ecf..23175fd40 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -1,8 +1,5 @@ #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] use ndarray::prelude::*; @@ -28,7 +25,8 @@ macro_rules! assert_panics { #[test] #[cfg(feature = "std")] -fn double_ended() { +fn double_ended() +{ let a = ArcArray::linspace(0., 7., 8); let mut it = a.iter().cloned(); assert_eq!(it.next(), Some(0.)); @@ -40,7 +38,8 @@ fn double_ended() { } #[test] -fn double_ended_rows() { +fn double_ended_rows() +{ let a = ArcArray::from_iter(0..8).into_shape_clone((4, 2)).unwrap(); let mut row_it = a.rows().into_iter(); assert_equal(row_it.next_back().unwrap(), &[6, 7]); @@ -50,15 +49,23 @@ fn double_ended_rows() { assert!(row_it.next().is_none()); assert!(row_it.next_back().is_none()); - for (row, check) in a.rows().into_iter().rev().zip(&[[6, 7], [4, 5], [2, 3], [0, 1]]) { + for (row, check) in a + .rows() + .into_iter() + .rev() + .zip(&[[6, 7], [4, 5], [2, 3], [0, 1]]) + { assert_equal(row, check); } } #[test] -fn iter_size_hint() { +fn iter_size_hint() +{ // Check that the size hint is correctly computed - let a = ArcArray::from_iter(0..24).into_shape_with_order((2, 3, 4)).unwrap(); + let a = ArcArray::from_iter(0..24) + .into_shape_with_order((2, 3, 4)) + .unwrap(); let mut data = [0; 24]; for (i, elt) in enumerate(&mut data) { *elt = i as i32; @@ -75,7 +82,8 @@ fn iter_size_hint() { #[test] #[cfg(feature = "std")] -fn indexed() { +fn indexed() +{ let a = ArcArray::linspace(0., 7., 8); for (i, elt) in a.indexed_iter() { assert_eq!(i, *elt as usize); @@ -95,7 +103,8 @@ fn indexed() { #[test] #[cfg(feature = "std")] -fn as_slice() { +fn as_slice() +{ use ndarray::Data; fn assert_slice_correct(v: &ArrayBase) @@ -152,7 +161,8 @@ fn as_slice() { } #[test] -fn inner_iter() { +fn inner_iter() +{ let a = ArcArray::from_iter(0..12); let a = a.into_shape_with_order((2, 3, 2)).unwrap(); // [[[0, 1], @@ -161,35 +171,30 @@ fn inner_iter() { // [[6, 7], // [8, 9], // ... - assert_equal( - a.rows(), - vec![ + assert_equal(a.rows(), vec![ aview1(&[0, 1]), aview1(&[2, 3]), aview1(&[4, 5]), aview1(&[6, 7]), aview1(&[8, 9]), aview1(&[10, 11]), - ], - ); + ]); let mut b = ArcArray::zeros((2, 3, 2)); b.swap_axes(0, 2); b.assign(&a); - assert_equal( - b.rows(), - vec![ + assert_equal(b.rows(), vec![ aview1(&[0, 1]), aview1(&[2, 3]), aview1(&[4, 5]), aview1(&[6, 7]), aview1(&[8, 9]), aview1(&[10, 11]), - ], - ); + ]); } #[test] -fn inner_iter_corner_cases() { +fn inner_iter_corner_cases() +{ let a0 = ArcArray::::zeros(()); assert_equal(a0.rows(), vec![aview1(&[0])]); @@ -201,9 +206,12 @@ fn inner_iter_corner_cases() { } #[test] -fn inner_iter_size_hint() { +fn inner_iter_size_hint() +{ // Check that the size hint is correctly computed - let a = ArcArray::from_iter(0..24).into_shape_with_order((2, 3, 4)).unwrap(); + let a = ArcArray::from_iter(0..24) + .into_shape_with_order((2, 3, 4)) + .unwrap(); let mut len = 6; let mut it = a.rows().into_iter(); assert_eq!(it.len(), len); @@ -216,7 +224,8 @@ fn inner_iter_size_hint() { #[allow(deprecated)] // into_outer_iter #[test] -fn outer_iter() { +fn outer_iter() +{ let a = ArcArray::from_iter(0..12); let a = a.into_shape_with_order((2, 3, 2)).unwrap(); // [[[0, 1], @@ -225,17 +234,11 @@ fn outer_iter() { // [[6, 7], // [8, 9], // ... - assert_equal( - a.outer_iter(), - vec![a.index_axis(Axis(0), 0), a.index_axis(Axis(0), 1)], - ); + assert_equal(a.outer_iter(), vec![a.index_axis(Axis(0), 0), a.index_axis(Axis(0), 1)]); let mut b = ArcArray::zeros((2, 3, 2)); b.swap_axes(0, 2); b.assign(&a); - assert_equal( - b.outer_iter(), - vec![a.index_axis(Axis(0), 0), a.index_axis(Axis(0), 1)], - ); + assert_equal(b.outer_iter(), vec![a.index_axis(Axis(0), 0), a.index_axis(Axis(0), 1)]); let mut found_rows = Vec::new(); for sub in b.outer_iter() { @@ -259,10 +262,7 @@ fn outer_iter() { let mut cv = c.slice_mut(s![..;-1, ..;-1, ..;-1]); cv.assign(&a); assert_eq!(&a, &cv); - assert_equal( - cv.outer_iter(), - vec![a.index_axis(Axis(0), 0), a.index_axis(Axis(0), 1)], - ); + assert_equal(cv.outer_iter(), vec![a.index_axis(Axis(0), 0), a.index_axis(Axis(0), 1)]); let mut found_rows = Vec::new(); for sub in cv.outer_iter() { @@ -275,7 +275,8 @@ fn outer_iter() { } #[test] -fn axis_iter() { +fn axis_iter() +{ let a = ArcArray::from_iter(0..12); let a = a.into_shape_with_order((2, 3, 2)).unwrap(); // [[[0, 1], @@ -284,18 +285,16 @@ fn axis_iter() { // [[6, 7], // [8, 9], // ... - assert_equal( - a.axis_iter(Axis(1)), - vec![ + assert_equal(a.axis_iter(Axis(1)), vec![ a.index_axis(Axis(1), 0), a.index_axis(Axis(1), 1), a.index_axis(Axis(1), 2), - ], - ); + ]); } #[test] -fn axis_iter_split_at() { +fn axis_iter_split_at() +{ let a = Array::from_iter(0..5); let iter = a.axis_iter(Axis(0)); let all: Vec<_> = iter.clone().collect(); @@ -307,7 +306,8 @@ fn axis_iter_split_at() { } #[test] -fn axis_iter_split_at_partially_consumed() { +fn axis_iter_split_at_partially_consumed() +{ let a = Array::from_iter(0..5); let mut iter = a.axis_iter(Axis(0)); while iter.next().is_some() { @@ -321,7 +321,8 @@ fn axis_iter_split_at_partially_consumed() { } #[test] -fn axis_iter_zip() { +fn axis_iter_zip() +{ let a = Array::from_iter(0..5); let iter = a.axis_iter(Axis(0)); let mut b = Array::zeros(5); @@ -330,20 +331,24 @@ fn axis_iter_zip() { } #[test] -fn axis_iter_zip_partially_consumed() { +fn axis_iter_zip_partially_consumed() +{ let a = Array::from_iter(0..5); let mut iter = a.axis_iter(Axis(0)); let mut consumed = 0; while iter.next().is_some() { consumed += 1; let mut b = Array::zeros(a.len() - consumed); - Zip::from(&mut b).and(iter.clone()).for_each(|b, a| *b = a[()]); + Zip::from(&mut b) + .and(iter.clone()) + .for_each(|b, a| *b = a[()]); assert_eq!(a.slice(s![consumed..]), b); } } #[test] -fn axis_iter_zip_partially_consumed_discontiguous() { +fn axis_iter_zip_partially_consumed_discontiguous() +{ let a = Array::from_iter(0..5); let mut iter = a.axis_iter(Axis(0)); let mut consumed = 0; @@ -351,13 +356,16 @@ fn axis_iter_zip_partially_consumed_discontiguous() { consumed += 1; let mut b = Array::zeros((a.len() - consumed) * 2); b.slice_collapse(s![..;2]); - Zip::from(&mut b).and(iter.clone()).for_each(|b, a| *b = a[()]); + Zip::from(&mut b) + .and(iter.clone()) + .for_each(|b, a| *b = a[()]); assert_eq!(a.slice(s![consumed..]), b); } } #[test] -fn outer_iter_corner_cases() { +fn outer_iter_corner_cases() +{ let a2 = ArcArray::::zeros((0, 3)); assert_equal(a2.outer_iter(), vec![aview1(&[]); 0]); @@ -367,7 +375,8 @@ fn outer_iter_corner_cases() { #[allow(deprecated)] #[test] -fn outer_iter_mut() { +fn outer_iter_mut() +{ let a = ArcArray::from_iter(0..12); let a = a.into_shape_with_order((2, 3, 2)).unwrap(); // [[[0, 1], @@ -379,10 +388,7 @@ fn outer_iter_mut() { let mut b = ArcArray::zeros((2, 3, 2)); b.swap_axes(0, 2); b.assign(&a); - assert_equal( - b.outer_iter_mut(), - vec![a.index_axis(Axis(0), 0), a.index_axis(Axis(0), 1)], - ); + assert_equal(b.outer_iter_mut(), vec![a.index_axis(Axis(0), 0), a.index_axis(Axis(0), 1)]); let mut found_rows = Vec::new(); for sub in b.outer_iter_mut() { @@ -394,7 +400,8 @@ fn outer_iter_mut() { } #[test] -fn axis_iter_mut() { +fn axis_iter_mut() +{ let a = ArcArray::from_iter(0..12); let a = a.into_shape_with_order((2, 3, 2)).unwrap(); // [[[0, 1], @@ -414,44 +421,36 @@ fn axis_iter_mut() { } #[test] -fn axis_chunks_iter() { +fn axis_chunks_iter() +{ let a = ArcArray::from_iter(0..24); let a = a.into_shape_with_order((2, 6, 2)).unwrap(); let it = a.axis_chunks_iter(Axis(1), 2); - assert_equal( - it, - vec![ + assert_equal(it, vec![ arr3(&[[[0, 1], [2, 3]], [[12, 13], [14, 15]]]), arr3(&[[[4, 5], [6, 7]], [[16, 17], [18, 19]]]), arr3(&[[[8, 9], [10, 11]], [[20, 21], [22, 23]]]), - ], - ); + ]); let a = ArcArray::from_iter(0..28); let a = a.into_shape_with_order((2, 7, 2)).unwrap(); let it = a.axis_chunks_iter(Axis(1), 2); - assert_equal( - it, - vec![ + assert_equal(it, vec![ arr3(&[[[0, 1], [2, 3]], [[14, 15], [16, 17]]]), arr3(&[[[4, 5], [6, 7]], [[18, 19], [20, 21]]]), arr3(&[[[8, 9], [10, 11]], [[22, 23], [24, 25]]]), arr3(&[[[12, 13]], [[26, 27]]]), - ], - ); + ]); let it = a.axis_chunks_iter(Axis(1), 2).rev(); - assert_equal( - it, - vec![ + assert_equal(it, vec![ arr3(&[[[12, 13]], [[26, 27]]]), arr3(&[[[8, 9], [10, 11]], [[22, 23], [24, 25]]]), arr3(&[[[4, 5], [6, 7]], [[18, 19], [20, 21]]]), arr3(&[[[0, 1], [2, 3]], [[14, 15], [16, 17]]]), - ], - ); + ]); let it = a.axis_chunks_iter(Axis(1), 7); assert_equal(it, vec![a.view()]); @@ -461,7 +460,8 @@ fn axis_chunks_iter() { } #[test] -fn axis_iter_mut_split_at() { +fn axis_iter_mut_split_at() +{ let mut a = Array::from_iter(0..5); let mut a_clone = a.clone(); let all: Vec<_> = a_clone.axis_iter_mut(Axis(0)).collect(); @@ -473,7 +473,8 @@ fn axis_iter_mut_split_at() { } #[test] -fn axis_iter_mut_split_at_partially_consumed() { +fn axis_iter_mut_split_at_partially_consumed() +{ let mut a = Array::from_iter(0..5); for consumed in 1..=a.len() { for mid in 0..=(a.len() - consumed) { @@ -499,7 +500,8 @@ fn axis_iter_mut_split_at_partially_consumed() { } #[test] -fn axis_iter_mut_zip() { +fn axis_iter_mut_zip() +{ let orig = Array::from_iter(0..5); let mut cloned = orig.clone(); let iter = cloned.axis_iter_mut(Axis(0)); @@ -513,7 +515,8 @@ fn axis_iter_mut_zip() { } #[test] -fn axis_iter_mut_zip_partially_consumed() { +fn axis_iter_mut_zip_partially_consumed() +{ let mut a = Array::from_iter(0..5); for consumed in 1..=a.len() { let remaining = a.len() - consumed; @@ -528,7 +531,8 @@ fn axis_iter_mut_zip_partially_consumed() { } #[test] -fn axis_iter_mut_zip_partially_consumed_discontiguous() { +fn axis_iter_mut_zip_partially_consumed_discontiguous() +{ let mut a = Array::from_iter(0..5); for consumed in 1..=a.len() { let remaining = a.len() - consumed; @@ -545,27 +549,27 @@ fn axis_iter_mut_zip_partially_consumed_discontiguous() { #[test] #[cfg(feature = "std")] -fn axis_chunks_iter_corner_cases() { +fn axis_chunks_iter_corner_cases() +{ // examples provided by @bluss in PR #65 // these tests highlight corner cases of the axis_chunks_iter implementation // and enable checking if no pointer offsetting is out of bounds. However // checking the absence of of out of bounds offsetting cannot (?) be // done automatically, so one has to launch this test in a debugger. - let a = ArcArray::::linspace(0., 7., 8).into_shape_with_order((8, 1)).unwrap(); + let a = ArcArray::::linspace(0., 7., 8) + .into_shape_with_order((8, 1)) + .unwrap(); let it = a.axis_chunks_iter(Axis(0), 4); assert_equal(it, vec![a.slice(s![..4, ..]), a.slice(s![4.., ..])]); let a = a.slice(s![..;-1,..]); let it = a.axis_chunks_iter(Axis(0), 8); assert_equal(it, vec![a.view()]); let it = a.axis_chunks_iter(Axis(0), 3); - assert_equal( - it, - vec![ + assert_equal(it, vec![ array![[7.], [6.], [5.]], array![[4.], [3.], [2.]], array![[1.], [0.]], - ], - ); + ]); let b = ArcArray::::zeros((8, 2)); let a = b.slice(s![1..;2,..]); @@ -577,10 +581,13 @@ fn axis_chunks_iter_corner_cases() { } #[test] -fn axis_chunks_iter_zero_stride() { +fn axis_chunks_iter_zero_stride() +{ { // stride 0 case - let b = Array::from(vec![0f32; 0]).into_shape_with_order((5, 0, 3)).unwrap(); + let b = Array::from(vec![0f32; 0]) + .into_shape_with_order((5, 0, 3)) + .unwrap(); let shapes: Vec<_> = b .axis_chunks_iter(Axis(0), 2) .map(|v| v.raw_dim()) @@ -590,7 +597,9 @@ fn axis_chunks_iter_zero_stride() { { // stride 0 case reverse - let b = Array::from(vec![0f32; 0]).into_shape_with_order((5, 0, 3)).unwrap(); + let b = Array::from(vec![0f32; 0]) + .into_shape_with_order((5, 0, 3)) + .unwrap(); let shapes: Vec<_> = b .axis_chunks_iter(Axis(0), 2) .rev() @@ -609,19 +618,22 @@ fn axis_chunks_iter_zero_stride() { #[should_panic] #[test] -fn axis_chunks_iter_zero_chunk_size() { +fn axis_chunks_iter_zero_chunk_size() +{ let a = Array::from_iter(0..5); a.axis_chunks_iter(Axis(0), 0); } #[test] -fn axis_chunks_iter_zero_axis_len() { +fn axis_chunks_iter_zero_axis_len() +{ let a = Array::from_iter(0..0); assert!(a.axis_chunks_iter(Axis(0), 5).next().is_none()); } #[test] -fn axis_chunks_iter_split_at() { +fn axis_chunks_iter_split_at() +{ let mut a = Array2::::zeros((11, 3)); a.iter_mut().enumerate().for_each(|(i, elt)| *elt = i); for source in &[ @@ -648,7 +660,8 @@ fn axis_chunks_iter_split_at() { } #[test] -fn axis_chunks_iter_mut() { +fn axis_chunks_iter_mut() +{ let a = ArcArray::from_iter(0..24); let mut a = a.into_shape_with_order((2, 6, 2)).unwrap(); @@ -660,21 +673,26 @@ fn axis_chunks_iter_mut() { #[should_panic] #[test] -fn axis_chunks_iter_mut_zero_chunk_size() { +fn axis_chunks_iter_mut_zero_chunk_size() +{ let mut a = Array::from_iter(0..5); a.axis_chunks_iter_mut(Axis(0), 0); } #[test] -fn axis_chunks_iter_mut_zero_axis_len() { +fn axis_chunks_iter_mut_zero_axis_len() +{ let mut a = Array::from_iter(0..0); assert!(a.axis_chunks_iter_mut(Axis(0), 5).next().is_none()); } #[test] -fn outer_iter_size_hint() { +fn outer_iter_size_hint() +{ // Check that the size hint is correctly computed - let a = ArcArray::from_iter(0..24).into_shape_with_order((4, 3, 2)).unwrap(); + let a = ArcArray::from_iter(0..24) + .into_shape_with_order((4, 3, 2)) + .unwrap(); let mut len = 4; let mut it = a.outer_iter(); assert_eq!(it.len(), len); @@ -705,8 +723,11 @@ fn outer_iter_size_hint() { } #[test] -fn outer_iter_split_at() { - let a = ArcArray::from_iter(0..30).into_shape_with_order((5, 3, 2)).unwrap(); +fn outer_iter_split_at() +{ + let a = ArcArray::from_iter(0..30) + .into_shape_with_order((5, 3, 2)) + .unwrap(); let it = a.outer_iter(); let (mut itl, mut itr) = it.clone().split_at(2); @@ -727,16 +748,22 @@ fn outer_iter_split_at() { #[test] #[should_panic] -fn outer_iter_split_at_panics() { - let a = ArcArray::from_iter(0..30).into_shape_with_order((5, 3, 2)).unwrap(); +fn outer_iter_split_at_panics() +{ + let a = ArcArray::from_iter(0..30) + .into_shape_with_order((5, 3, 2)) + .unwrap(); let it = a.outer_iter(); it.split_at(6); } #[test] -fn outer_iter_mut_split_at() { - let mut a = ArcArray::from_iter(0..30).into_shape_with_order((5, 3, 2)).unwrap(); +fn outer_iter_mut_split_at() +{ + let mut a = ArcArray::from_iter(0..30) + .into_shape_with_order((5, 3, 2)) + .unwrap(); { let it = a.outer_iter_mut(); @@ -755,12 +782,15 @@ fn outer_iter_mut_split_at() { } #[test] -fn iterators_are_send_sync() { +fn iterators_are_send_sync() +{ // When the element type is Send + Sync, then the iterators and views // are too. fn _send_sync(_: &T) {} - let mut a = ArcArray::from_iter(0..30).into_shape_with_order((5, 3, 2)).unwrap(); + let mut a = ArcArray::from_iter(0..30) + .into_shape_with_order((5, 3, 2)) + .unwrap(); _send_sync(&a.view()); _send_sync(&a.view_mut()); @@ -785,7 +815,8 @@ fn iterators_are_send_sync() { #[test] #[allow(clippy::unnecessary_fold)] -fn test_fold() { +fn test_fold() +{ let mut a = Array2::::default((20, 20)); a += 1; let mut iter = a.iter(); @@ -798,7 +829,8 @@ fn test_fold() { } #[test] -fn nth_back_examples() { +fn nth_back_examples() +{ let mut a: Array1 = (0..256).collect(); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); assert_eq!(a.iter().nth_back(0), Some(&a[a.len() - 1])); @@ -811,7 +843,8 @@ fn nth_back_examples() { } #[test] -fn nth_back_zero_n() { +fn nth_back_zero_n() +{ let mut a: Array1 = (0..256).collect(); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); let mut iter1 = a.iter(); @@ -823,7 +856,8 @@ fn nth_back_zero_n() { } #[test] -fn nth_back_nonzero_n() { +fn nth_back_nonzero_n() +{ let mut a: Array1 = (0..256).collect(); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); let mut iter1 = a.iter(); @@ -839,7 +873,8 @@ fn nth_back_nonzero_n() { } #[test] -fn nth_back_past_end() { +fn nth_back_past_end() +{ let mut a: Array1 = (0..256).collect(); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); let mut iter = a.iter(); @@ -848,7 +883,8 @@ fn nth_back_past_end() { } #[test] -fn nth_back_partially_consumed() { +fn nth_back_partially_consumed() +{ let mut a: Array1 = (0..256).collect(); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); let mut iter = a.iter(); @@ -866,7 +902,8 @@ fn nth_back_partially_consumed() { } #[test] -fn test_rfold() { +fn test_rfold() +{ { let mut a = Array1::::default(256); a += 1; @@ -912,32 +949,40 @@ fn test_rfold() { } #[test] -fn test_into_iter() { +fn test_into_iter() +{ let a = Array1::from(vec![1, 2, 3, 4]); let v = a.into_iter().collect::>(); assert_eq!(v, [1, 2, 3, 4]); } #[test] -fn test_into_iter_2d() { - let a = Array1::from(vec![1, 2, 3, 4]).into_shape_with_order((2, 2)).unwrap(); +fn test_into_iter_2d() +{ + let a = Array1::from(vec![1, 2, 3, 4]) + .into_shape_with_order((2, 2)) + .unwrap(); let v = a.into_iter().collect::>(); assert_eq!(v, [1, 2, 3, 4]); - let a = Array1::from(vec![1, 2, 3, 4]).into_shape_with_order((2, 2)).unwrap().reversed_axes(); + let a = Array1::from(vec![1, 2, 3, 4]) + .into_shape_with_order((2, 2)) + .unwrap() + .reversed_axes(); let v = a.into_iter().collect::>(); assert_eq!(v, [1, 3, 2, 4]); } #[test] -fn test_into_iter_sliced() { +fn test_into_iter_sliced() +{ let (m, n) = (4, 5); let drops = Cell::new(0); for i in 0..m - 1 { for j in 0..n - 1 { - for i2 in i + 1 .. m { - for j2 in j + 1 .. n { + for i2 in i + 1..m { + for j2 in j + 1..n { for invert in 0..3 { drops.set(0); let i = i as isize; @@ -946,7 +991,8 @@ fn test_into_iter_sliced() { let j2 = j2 as isize; let mut a = Array1::from_iter(0..(m * n) as i32) .mapv(|v| DropCount::new(v, &drops)) - .into_shape_with_order((m, n)).unwrap(); + .into_shape_with_order((m, n)) + .unwrap(); a.slice_collapse(s![i..i2, j..j2]); if invert < a.ndim() { a.invert_axis(Axis(invert)); @@ -973,26 +1019,37 @@ fn test_into_iter_sliced() { /// /// Compares equal by its "represented value". #[derive(Clone, Debug)] -struct DropCount<'a> { +struct DropCount<'a> +{ value: i32, my_drops: usize, - drops: &'a Cell + drops: &'a Cell, } -impl PartialEq for DropCount<'_> { - fn eq(&self, other: &Self) -> bool { +impl PartialEq for DropCount<'_> +{ + fn eq(&self, other: &Self) -> bool + { self.value == other.value } } -impl<'a> DropCount<'a> { - fn new(value: i32, drops: &'a Cell) -> Self { - DropCount { value, my_drops: 0, drops } +impl<'a> DropCount<'a> +{ + fn new(value: i32, drops: &'a Cell) -> Self + { + DropCount { + value, + my_drops: 0, + drops, + } } } -impl Drop for DropCount<'_> { - fn drop(&mut self) { +impl Drop for DropCount<'_> +{ + fn drop(&mut self) + { assert_eq!(self.my_drops, 0); self.my_drops += 1; self.drops.set(self.drops.get() + 1); diff --git a/tests/ix0.rs b/tests/ix0.rs index c8c6c73aa..f1038556a 100644 --- a/tests/ix0.rs +++ b/tests/ix0.rs @@ -1,8 +1,5 @@ #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names, + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, clippy::float_cmp )] @@ -11,7 +8,8 @@ use ndarray::Ix0; use ndarray::ShapeBuilder; #[test] -fn test_ix0() { +fn test_ix0() +{ let mut a = Array::zeros(Ix0()); assert_eq!(a[()], 0.); a[()] = 1.; @@ -30,7 +28,8 @@ fn test_ix0() { } #[test] -fn test_ix0_add() { +fn test_ix0_add() +{ let mut a = Array::zeros(Ix0()); a += 1.; assert_eq!(a[()], 1.); @@ -39,7 +38,8 @@ fn test_ix0_add() { } #[test] -fn test_ix0_add_add() { +fn test_ix0_add_add() +{ let mut a = Array::zeros(Ix0()); a += 1.; let mut b = Array::zeros(Ix0()); @@ -49,7 +49,8 @@ fn test_ix0_add_add() { } #[test] -fn test_ix0_add_broad() { +fn test_ix0_add_broad() +{ let mut b = Array::from(vec![5., 6.]); let mut a = Array::zeros(Ix0()); a += 1.; diff --git a/tests/ixdyn.rs b/tests/ixdyn.rs index 5b7ef9327..05f123ba1 100644 --- a/tests/ixdyn.rs +++ b/tests/ixdyn.rs @@ -1,19 +1,17 @@ #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names, + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, clippy::float_cmp )] use ndarray::Array; use ndarray::IntoDimension; -use ndarray::ShapeBuilder; use ndarray::Ix3; use ndarray::Order; +use ndarray::ShapeBuilder; #[test] -fn test_ixdyn() { +fn test_ixdyn() +{ // check that we can use fixed size arrays for indexing let mut a = Array::zeros(vec![2, 3, 4]); a[[1, 1, 1]] = 1.; @@ -22,7 +20,8 @@ fn test_ixdyn() { #[should_panic] #[test] -fn test_ixdyn_wrong_dim() { +fn test_ixdyn_wrong_dim() +{ // check that we can use but it panics at runtime, if number of axes is wrong let mut a = Array::zeros(vec![2, 3, 4]); a[[1, 1, 1]] = 1.; @@ -31,7 +30,8 @@ fn test_ixdyn_wrong_dim() { } #[test] -fn test_ixdyn_out_of_bounds() { +fn test_ixdyn_out_of_bounds() +{ // check that we are out of bounds let a = Array::::zeros(vec![2, 3, 4]); let res = a.get([0, 3, 0]); @@ -39,7 +39,8 @@ fn test_ixdyn_out_of_bounds() { } #[test] -fn test_ixdyn_iterate() { +fn test_ixdyn_iterate() +{ for &order in &[Order::C, Order::F] { let mut a = Array::zeros((2, 3, 4).set_f(order.is_column_major())); let dim = a.shape().to_vec(); @@ -59,7 +60,8 @@ fn test_ixdyn_iterate() { } #[test] -fn test_ixdyn_index_iterate() { +fn test_ixdyn_index_iterate() +{ for &order in &[Order::C, Order::F] { let mut a = Array::zeros((2, 3, 4).set_f(order.is_column_major())); let dim = a.shape().to_vec(); @@ -78,7 +80,8 @@ fn test_ixdyn_index_iterate() { } #[test] -fn test_ixdyn_uget() { +fn test_ixdyn_uget() +{ // check that we are out of bounds let mut a = Array::::zeros(vec![2, 3, 4]); @@ -107,7 +110,8 @@ fn test_ixdyn_uget() { } #[test] -fn test_0() { +fn test_0() +{ let mut a = Array::zeros(vec![]); let z = vec![].into_dimension(); assert_eq!(a[z.clone()], 0.); @@ -127,7 +131,8 @@ fn test_0() { } #[test] -fn test_0_add() { +fn test_0_add() +{ let mut a = Array::zeros(vec![]); a += 1.; assert_eq!(a[[]], 1.); @@ -136,7 +141,8 @@ fn test_0_add() { } #[test] -fn test_0_add_add() { +fn test_0_add_add() +{ let mut a = Array::zeros(vec![]); a += 1.; let mut b = Array::zeros(vec![]); @@ -146,7 +152,8 @@ fn test_0_add_add() { } #[test] -fn test_0_add_broad() { +fn test_0_add_broad() +{ let mut b = Array::from(vec![5., 6.]); let mut a = Array::zeros(vec![]); a += 1.; @@ -157,10 +164,13 @@ fn test_0_add_broad() { #[test] #[cfg(feature = "std")] -fn test_into_dimension() { +fn test_into_dimension() +{ use ndarray::{Ix0, Ix1, Ix2, IxDyn}; - let a = Array::linspace(0., 41., 6 * 7).into_shape_with_order((6, 7)).unwrap(); + let a = Array::linspace(0., 41., 6 * 7) + .into_shape_with_order((6, 7)) + .unwrap(); let a2 = a.clone().into_shape_with_order(IxDyn(&[6, 7])).unwrap(); let b = a2.clone().into_dimensionality::().unwrap(); assert_eq!(a, b); diff --git a/tests/numeric.rs b/tests/numeric.rs index e08979d29..4d70d4502 100644 --- a/tests/numeric.rs +++ b/tests/numeric.rs @@ -1,8 +1,5 @@ #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names, + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, clippy::float_cmp )] @@ -11,19 +8,22 @@ use ndarray::{arr0, arr1, arr2, array, aview1, Array, Array1, Array2, Array3, Ax use std::f64; #[test] -fn test_mean_with_nan_values() { +fn test_mean_with_nan_values() +{ let a = array![f64::NAN, 1.]; assert!(a.mean().unwrap().is_nan()); } #[test] -fn test_mean_with_empty_array_of_floats() { +fn test_mean_with_empty_array_of_floats() +{ let a: Array1 = array![]; assert!(a.mean().is_none()); } #[test] -fn test_mean_with_array_of_floats() { +fn test_mean_with_array_of_floats() +{ let a: Array1 = array![ 0.99889651, 0.0150731, 0.28492482, 0.83819218, 0.48413156, 0.80710412, 0.41762936, 0.22879429, 0.43997224, 0.23831807, 0.02416466, 0.6269962, 0.47420614, 0.56275487, @@ -39,7 +39,8 @@ fn test_mean_with_array_of_floats() { } #[test] -fn sum_mean() { +fn sum_mean() +{ let a: Array2 = arr2(&[[1., 2.], [3., 4.]]); assert_eq!(a.sum_axis(Axis(0)), arr1(&[4., 6.])); assert_eq!(a.sum_axis(Axis(1)), arr1(&[3., 7.])); @@ -51,7 +52,8 @@ fn sum_mean() { } #[test] -fn sum_mean_empty() { +fn sum_mean_empty() +{ assert_eq!(Array3::::ones((2, 0, 3)).sum(), 0.); assert_eq!(Array1::::ones(0).sum_axis(Axis(0)), arr0(0.)); assert_eq!( @@ -66,7 +68,8 @@ fn sum_mean_empty() { #[test] #[cfg(feature = "std")] -fn var() { +fn var() +{ let a = array![1., -4.32, 1.14, 0.32]; assert_abs_diff_eq!(a.var(0.), 5.049875, epsilon = 1e-8); } @@ -74,7 +77,8 @@ fn var() { #[test] #[cfg(feature = "std")] #[should_panic] -fn var_negative_ddof() { +fn var_negative_ddof() +{ let a = array![1., 2., 3.]; a.var(-1.); } @@ -82,14 +86,16 @@ fn var_negative_ddof() { #[test] #[cfg(feature = "std")] #[should_panic] -fn var_too_large_ddof() { +fn var_too_large_ddof() +{ let a = array![1., 2., 3.]; a.var(4.); } #[test] #[cfg(feature = "std")] -fn var_nan_ddof() { +fn var_nan_ddof() +{ let a = Array2::::zeros((2, 3)); let v = a.var(::std::f64::NAN); assert!(v.is_nan()); @@ -97,14 +103,16 @@ fn var_nan_ddof() { #[test] #[cfg(feature = "std")] -fn var_empty_arr() { +fn var_empty_arr() +{ let a: Array1 = array![]; assert!(a.var(0.0).is_nan()); } #[test] #[cfg(feature = "std")] -fn std() { +fn std() +{ let a = array![1., -4.32, 1.14, 0.32]; assert_abs_diff_eq!(a.std(0.), 2.24719, epsilon = 1e-5); } @@ -112,7 +120,8 @@ fn std() { #[test] #[cfg(feature = "std")] #[should_panic] -fn std_negative_ddof() { +fn std_negative_ddof() +{ let a = array![1., 2., 3.]; a.std(-1.); } @@ -120,14 +129,16 @@ fn std_negative_ddof() { #[test] #[cfg(feature = "std")] #[should_panic] -fn std_too_large_ddof() { +fn std_too_large_ddof() +{ let a = array![1., 2., 3.]; a.std(4.); } #[test] #[cfg(feature = "std")] -fn std_nan_ddof() { +fn std_nan_ddof() +{ let a = Array2::::zeros((2, 3)); let v = a.std(::std::f64::NAN); assert!(v.is_nan()); @@ -135,14 +146,16 @@ fn std_nan_ddof() { #[test] #[cfg(feature = "std")] -fn std_empty_arr() { +fn std_empty_arr() +{ let a: Array1 = array![]; assert!(a.std(0.0).is_nan()); } #[test] #[cfg(feature = "approx")] -fn var_axis() { +fn var_axis() +{ use ndarray::{aview0, aview2}; let a = array![ @@ -200,7 +213,8 @@ fn var_axis() { #[test] #[cfg(feature = "approx")] -fn std_axis() { +fn std_axis() +{ use ndarray::aview2; let a = array![ @@ -260,7 +274,8 @@ fn std_axis() { #[test] #[should_panic] #[cfg(feature = "std")] -fn var_axis_negative_ddof() { +fn var_axis_negative_ddof() +{ let a = array![1., 2., 3.]; a.var_axis(Axis(0), -1.); } @@ -268,14 +283,16 @@ fn var_axis_negative_ddof() { #[test] #[should_panic] #[cfg(feature = "std")] -fn var_axis_too_large_ddof() { +fn var_axis_too_large_ddof() +{ let a = array![1., 2., 3.]; a.var_axis(Axis(0), 4.); } #[test] #[cfg(feature = "std")] -fn var_axis_nan_ddof() { +fn var_axis_nan_ddof() +{ let a = Array2::::zeros((2, 3)); let v = a.var_axis(Axis(1), ::std::f64::NAN); assert_eq!(v.shape(), &[2]); @@ -284,7 +301,8 @@ fn var_axis_nan_ddof() { #[test] #[cfg(feature = "std")] -fn var_axis_empty_axis() { +fn var_axis_empty_axis() +{ let a = Array2::::zeros((2, 0)); let v = a.var_axis(Axis(1), 0.); assert_eq!(v.shape(), &[2]); @@ -294,14 +312,16 @@ fn var_axis_empty_axis() { #[test] #[should_panic] #[cfg(feature = "std")] -fn std_axis_bad_dof() { +fn std_axis_bad_dof() +{ let a = array![1., 2., 3.]; a.std_axis(Axis(0), 4.); } #[test] #[cfg(feature = "std")] -fn std_axis_empty_axis() { +fn std_axis_empty_axis() +{ let a = Array2::::zeros((2, 0)); let v = a.std_axis(Axis(1), 0.); assert_eq!(v.shape(), &[2]); diff --git a/tests/oper.rs b/tests/oper.rs index 12e822cb7..294a762c6 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -1,8 +1,5 @@ #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] #![cfg(feature = "std")] use ndarray::linalg::general_mat_mul; @@ -16,7 +13,8 @@ use num_traits::Zero; use approx::assert_abs_diff_eq; use defmac::defmac; -fn test_oper(op: &str, a: &[f32], b: &[f32], c: &[f32]) { +fn test_oper(op: &str, a: &[f32], b: &[f32], c: &[f32]) +{ let aa = CowArray::from(arr1(a)); let bb = CowArray::from(arr1(b)); let cc = CowArray::from(arr1(c)); @@ -33,10 +31,8 @@ fn test_oper(op: &str, a: &[f32], b: &[f32], c: &[f32]) { test_oper_arr::(op, aa.clone(), bb.clone(), cc.clone()); } - fn test_oper_arr(op: &str, mut aa: CowArray, bb: CowArray, cc: CowArray) -where - D: Dimension, +where D: Dimension { match op { "+" => { @@ -73,47 +69,19 @@ where } #[test] -fn operations() { - test_oper( - "+", - &[1.0, 2.0, 3.0, 4.0], - &[0.0, 1.0, 2.0, 3.0], - &[1.0, 3.0, 5.0, 7.0], - ); - test_oper( - "-", - &[1.0, 2.0, 3.0, 4.0], - &[0.0, 1.0, 2.0, 3.0], - &[1.0, 1.0, 1.0, 1.0], - ); - test_oper( - "*", - &[1.0, 2.0, 3.0, 4.0], - &[0.0, 1.0, 2.0, 3.0], - &[0.0, 2.0, 6.0, 12.0], - ); - test_oper( - "/", - &[1.0, 2.0, 3.0, 4.0], - &[1.0, 1.0, 2.0, 3.0], - &[1.0, 2.0, 3.0 / 2.0, 4.0 / 3.0], - ); - test_oper( - "%", - &[1.0, 2.0, 3.0, 4.0], - &[1.0, 1.0, 2.0, 3.0], - &[0.0, 0.0, 1.0, 1.0], - ); - test_oper( - "neg", - &[1.0, 2.0, 3.0, 4.0], - &[1.0, 1.0, 2.0, 3.0], - &[-1.0, -2.0, -3.0, -4.0], - ); +fn operations() +{ + test_oper("+", &[1.0, 2.0, 3.0, 4.0], &[0.0, 1.0, 2.0, 3.0], &[1.0, 3.0, 5.0, 7.0]); + test_oper("-", &[1.0, 2.0, 3.0, 4.0], &[0.0, 1.0, 2.0, 3.0], &[1.0, 1.0, 1.0, 1.0]); + test_oper("*", &[1.0, 2.0, 3.0, 4.0], &[0.0, 1.0, 2.0, 3.0], &[0.0, 2.0, 6.0, 12.0]); + test_oper("/", &[1.0, 2.0, 3.0, 4.0], &[1.0, 1.0, 2.0, 3.0], &[1.0, 2.0, 3.0 / 2.0, 4.0 / 3.0]); + test_oper("%", &[1.0, 2.0, 3.0, 4.0], &[1.0, 1.0, 2.0, 3.0], &[0.0, 0.0, 1.0, 1.0]); + test_oper("neg", &[1.0, 2.0, 3.0, 4.0], &[1.0, 1.0, 2.0, 3.0], &[-1.0, -2.0, -3.0, -4.0]); } #[test] -fn scalar_operations() { +fn scalar_operations() +{ let a = arr0::(1.); let b = rcarr1::(&[1., 1.]); let c = rcarr2(&[[1., 1.], [1., 1.]]); @@ -159,7 +127,8 @@ where } #[test] -fn dot_product() { +fn dot_product() +{ let a = Array::range(0., 69., 1.); let b = &a * 2. - 7.; let dot = 197846.; @@ -197,7 +166,8 @@ fn dot_product() { // test that we can dot product with a broadcast array #[test] -fn dot_product_0() { +fn dot_product_0() +{ let a = Array::range(0., 69., 1.); let x = 1.5; let b = aview0(&x); @@ -217,7 +187,8 @@ fn dot_product_0() { } #[test] -fn dot_product_neg_stride() { +fn dot_product_neg_stride() +{ // test that we can dot with negative stride let a = Array::range(0., 69., 1.); let b = &a * 2. - 7.; @@ -236,8 +207,11 @@ fn dot_product_neg_stride() { } #[test] -fn fold_and_sum() { - let a = Array::linspace(0., 127., 128).into_shape_with_order((8, 16)).unwrap(); +fn fold_and_sum() +{ + let a = Array::linspace(0., 127., 128) + .into_shape_with_order((8, 16)) + .unwrap(); assert_abs_diff_eq!(a.fold(0., |acc, &x| acc + x), a.sum(), epsilon = 1e-5); // test different strides @@ -275,8 +249,11 @@ fn fold_and_sum() { } #[test] -fn product() { - let a = Array::linspace(0.5, 2., 128).into_shape_with_order((8, 16)).unwrap(); +fn product() +{ + let a = Array::linspace(0.5, 2., 128) + .into_shape_with_order((8, 16)) + .unwrap(); assert_abs_diff_eq!(a.fold(1., |acc, &x| acc * x), a.product(), epsilon = 1e-5); // test different strides @@ -294,24 +271,28 @@ fn product() { } } -fn range_mat(m: Ix, n: Ix) -> Array2 { +fn range_mat(m: Ix, n: Ix) -> Array2 +{ Array::linspace(0., (m * n) as f32 - 1., m * n) .into_shape_with_order((m, n)) .unwrap() } -fn range_mat64(m: Ix, n: Ix) -> Array2 { +fn range_mat64(m: Ix, n: Ix) -> Array2 +{ Array::linspace(0., (m * n) as f64 - 1., m * n) .into_shape_with_order((m, n)) .unwrap() } #[cfg(feature = "approx")] -fn range1_mat64(m: Ix) -> Array1 { +fn range1_mat64(m: Ix) -> Array1 +{ Array::linspace(0., m as f64 - 1., m) } -fn range_i32(m: Ix, n: Ix) -> Array2 { +fn range_i32(m: Ix, n: Ix) -> Array2 +{ Array::from_iter(0..(m * n) as i32) .into_shape_with_order((m, n)) .unwrap() @@ -336,9 +317,7 @@ where let mut j = 0; for rr in &mut res_elems { unsafe { - *rr = (0..k).fold(A::zero(), move |s, x| { - s + *lhs.uget((i, x)) * *rhs.uget((x, j)) - }); + *rr = (0..k).fold(A::zero(), move |s, x| s + *lhs.uget((i, x)) * *rhs.uget((x, j))); } j += 1; if j == n { @@ -350,7 +329,8 @@ where } #[test] -fn mat_mul() { +fn mat_mul() +{ let (m, n, k) = (8, 8, 8); let a = range_mat(m, n); let b = range_mat(n, k); @@ -412,7 +392,8 @@ fn mat_mul() { // Check that matrix multiplication of contiguous matrices returns a // matrix with the same order #[test] -fn mat_mul_order() { +fn mat_mul_order() +{ let (m, n, k) = (8, 8, 8); let a = range_mat(m, n); let b = range_mat(n, k); @@ -431,7 +412,8 @@ fn mat_mul_order() { // test matrix multiplication shape mismatch #[test] #[should_panic] -fn mat_mul_shape_mismatch() { +fn mat_mul_shape_mismatch() +{ let (m, k, k2, n) = (8, 8, 9, 8); let a = range_mat(m, k); let b = range_mat(k2, n); @@ -441,7 +423,8 @@ fn mat_mul_shape_mismatch() { // test matrix multiplication shape mismatch #[test] #[should_panic] -fn mat_mul_shape_mismatch_2() { +fn mat_mul_shape_mismatch_2() +{ let (m, k, k2, n) = (8, 8, 8, 8); let a = range_mat(m, k); let b = range_mat(k2, n); @@ -452,7 +435,8 @@ fn mat_mul_shape_mismatch_2() { // Check that matrix multiplication // supports broadcast arrays. #[test] -fn mat_mul_broadcast() { +fn mat_mul_broadcast() +{ let (m, n, k) = (16, 16, 16); let a = range_mat(m, n); let x1 = 1.; @@ -471,7 +455,8 @@ fn mat_mul_broadcast() { // Check that matrix multiplication supports reversed axes #[test] -fn mat_mul_rev() { +fn mat_mul_rev() +{ let (m, n, k) = (16, 16, 16); let a = range_mat(m, n); let b = range_mat(n, k); @@ -487,7 +472,8 @@ fn mat_mul_rev() { // Check that matrix multiplication supports arrays with zero rows or columns #[test] -fn mat_mut_zero_len() { +fn mat_mut_zero_len() +{ defmac!(mat_mul_zero_len range_mat_fn => { for n in 0..4 { for m in 0..4 { @@ -508,7 +494,8 @@ fn mat_mut_zero_len() { } #[test] -fn scaled_add() { +fn scaled_add() +{ let a = range_mat(16, 15); let mut b = range_mat(16, 15); b.mapv_inplace(f32::exp); @@ -523,7 +510,8 @@ fn scaled_add() { #[cfg(feature = "approx")] #[test] -fn scaled_add_2() { +fn scaled_add_2() +{ let beta = -2.3; let sizes = vec![ (4, 4, 1, 4), @@ -560,7 +548,8 @@ fn scaled_add_2() { #[cfg(feature = "approx")] #[test] -fn scaled_add_3() { +fn scaled_add_3() +{ use approx::assert_relative_eq; use ndarray::{Slice, SliceInfo, SliceInfoElem}; use std::convert::TryFrom; @@ -611,7 +600,8 @@ fn scaled_add_3() { #[cfg(feature = "approx")] #[test] -fn gen_mat_mul() { +fn gen_mat_mul() +{ let alpha = -2.3; let beta = 3.14; let sizes = vec![ @@ -653,7 +643,8 @@ fn gen_mat_mul() { // Test y = A x where A is f-order #[cfg(feature = "approx")] #[test] -fn gemm_64_1_f() { +fn gemm_64_1_f() +{ let a = range_mat64(64, 64).reversed_axes(); let (m, n) = a.dim(); // m x n times n x 1 == m x 1 @@ -665,7 +656,8 @@ fn gemm_64_1_f() { } #[test] -fn gen_mat_mul_i32() { +fn gen_mat_mul_i32() +{ let alpha = -1; let beta = 2; let sizes = if cfg!(miri) { @@ -696,24 +688,27 @@ fn gen_mat_mul_i32() { #[cfg(feature = "approx")] #[test] -fn gen_mat_vec_mul() { +fn gen_mat_vec_mul() +{ use approx::assert_relative_eq; use ndarray::linalg::general_mat_vec_mul; // simple, slow, correct (hopefully) mat mul - fn reference_mat_vec_mul( - lhs: &ArrayBase, - rhs: &ArrayBase, - ) -> Array1 + fn reference_mat_vec_mul(lhs: &ArrayBase, rhs: &ArrayBase) -> Array1 where A: LinalgScalar, S: Data, S2: Data, { let ((m, _), k) = (lhs.dim(), rhs.dim()); - reference_mat_mul(lhs, &rhs.as_standard_layout().into_shape_with_order((k, 1)).unwrap()) - .into_shape_with_order(m) - .unwrap() + reference_mat_mul( + lhs, + &rhs.as_standard_layout() + .into_shape_with_order((k, 1)) + .unwrap(), + ) + .into_shape_with_order(m) + .unwrap() } let alpha = -2.3; @@ -762,23 +757,26 @@ fn gen_mat_vec_mul() { #[cfg(feature = "approx")] #[test] -fn vec_mat_mul() { +fn vec_mat_mul() +{ use approx::assert_relative_eq; // simple, slow, correct (hopefully) mat mul - fn reference_vec_mat_mul( - lhs: &ArrayBase, - rhs: &ArrayBase, - ) -> Array1 + fn reference_vec_mat_mul(lhs: &ArrayBase, rhs: &ArrayBase) -> Array1 where A: LinalgScalar, S: Data, S2: Data, { let (m, (_, n)) = (lhs.dim(), rhs.dim()); - reference_mat_mul(&lhs.as_standard_layout().into_shape_with_order((1, m)).unwrap(), rhs) - .into_shape_with_order(n) - .unwrap() + reference_mat_mul( + &lhs.as_standard_layout() + .into_shape_with_order((1, m)) + .unwrap(), + rhs, + ) + .into_shape_with_order(n) + .unwrap() } let sizes = vec![ @@ -823,7 +821,8 @@ fn vec_mat_mul() { } #[test] -fn kron_square_f64() { +fn kron_square_f64() +{ let a = arr2(&[[1.0, 0.0], [0.0, 1.0]]); let b = arr2(&[[0.0, 1.0], [1.0, 0.0]]); @@ -849,7 +848,8 @@ fn kron_square_f64() { } #[test] -fn kron_square_i64() { +fn kron_square_i64() +{ let a = arr2(&[[1, 0], [0, 1]]); let b = arr2(&[[0, 1], [1, 0]]); @@ -865,7 +865,8 @@ fn kron_square_i64() { } #[test] -fn kron_i64() { +fn kron_i64() +{ let a = arr2(&[[1, 0]]); let b = arr2(&[[0, 1], [1, 0]]); let r = arr2(&[[0, 1, 0, 0], [1, 0, 0, 0]]); @@ -873,13 +874,6 @@ fn kron_i64() { let a = arr2(&[[1, 0], [0, 0], [0, 1]]); let b = arr2(&[[0, 1], [1, 0]]); - let r = arr2(&[ - [0, 1, 0, 0], - [1, 0, 0, 0], - [0, 0, 0, 0], - [0, 0, 0, 0], - [0, 0, 0, 1], - [0, 0, 1, 0], - ]); + let r = arr2(&[[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]); assert_eq!(kron(&a, &b), r); } diff --git a/tests/par_azip.rs b/tests/par_azip.rs index e5dc02c4e..418c21ef8 100644 --- a/tests/par_azip.rs +++ b/tests/par_azip.rs @@ -7,7 +7,8 @@ use ndarray::prelude::*; use std::sync::atomic::{AtomicUsize, Ordering}; #[test] -fn test_par_azip1() { +fn test_par_azip1() +{ let mut a = Array::zeros(62); let b = Array::from_elem(62, 42); par_azip!((a in &mut a) { *a = 42 }); @@ -15,7 +16,8 @@ fn test_par_azip1() { } #[test] -fn test_par_azip2() { +fn test_par_azip2() +{ let mut a = Array::zeros((5, 7)); let b = Array::from_shape_fn(a.dim(), |(i, j)| 1. / (i + 2 * j) as f32); par_azip!((a in &mut a, &b in &b, ) *a = b ); @@ -24,7 +26,8 @@ fn test_par_azip2() { #[test] #[cfg(feature = "approx")] -fn test_par_azip3() { +fn test_par_azip3() +{ use approx::assert_abs_diff_eq; let mut a = [0.; 32]; @@ -44,7 +47,8 @@ fn test_par_azip3() { #[should_panic] #[test] -fn test_zip_dim_mismatch_1() { +fn test_zip_dim_mismatch_1() +{ let mut a = Array::zeros((5, 7)); let mut d = a.raw_dim(); d[0] += 1; @@ -53,7 +57,8 @@ fn test_zip_dim_mismatch_1() { } #[test] -fn test_indices_1() { +fn test_indices_1() +{ let mut a1 = Array::default(12); for (i, elt) in a1.indexed_iter_mut() { *elt = i; diff --git a/tests/par_rayon.rs b/tests/par_rayon.rs index 40670c6bf..13669763f 100644 --- a/tests/par_rayon.rs +++ b/tests/par_rayon.rs @@ -9,7 +9,8 @@ const CHUNK_SIZE: usize = 100; const N_CHUNKS: usize = (M + CHUNK_SIZE - 1) / CHUNK_SIZE; #[test] -fn test_axis_iter() { +fn test_axis_iter() +{ let mut a = Array2::::zeros((M, N)); for (i, mut v) in a.axis_iter_mut(Axis(0)).enumerate() { v.fill(i as _); @@ -22,7 +23,8 @@ fn test_axis_iter() { #[test] #[cfg(feature = "approx")] -fn test_axis_iter_mut() { +fn test_axis_iter_mut() +{ use approx::assert_abs_diff_eq; let mut a = Array::linspace(0., 1.0f64, M * N) .into_shape_with_order((M, N)) @@ -36,7 +38,8 @@ fn test_axis_iter_mut() { } #[test] -fn test_regular_iter() { +fn test_regular_iter() +{ let mut a = Array2::::zeros((M, N)); for (i, mut v) in a.axis_iter_mut(Axis(0)).enumerate() { v.fill(i as _); @@ -47,7 +50,8 @@ fn test_regular_iter() { } #[test] -fn test_regular_iter_collect() { +fn test_regular_iter_collect() +{ let mut a = Array2::::zeros((M, N)); for (i, mut v) in a.axis_iter_mut(Axis(0)).enumerate() { v.fill(i as _); @@ -57,7 +61,8 @@ fn test_regular_iter_collect() { } #[test] -fn test_axis_chunks_iter() { +fn test_axis_chunks_iter() +{ let mut a = Array2::::zeros((M, N)); for (i, mut v) in a.axis_chunks_iter_mut(Axis(0), CHUNK_SIZE).enumerate() { v.fill(i as _); @@ -74,7 +79,8 @@ fn test_axis_chunks_iter() { #[test] #[cfg(feature = "approx")] -fn test_axis_chunks_iter_mut() { +fn test_axis_chunks_iter_mut() +{ use approx::assert_abs_diff_eq; let mut a = Array::linspace(0., 1.0f64, M * N) .into_shape_with_order((M, N)) diff --git a/tests/par_zip.rs b/tests/par_zip.rs index ee929f36e..9f10d9fd5 100644 --- a/tests/par_zip.rs +++ b/tests/par_zip.rs @@ -8,14 +8,16 @@ const M: usize = 1024 * 10; const N: usize = 100; #[test] -fn test_zip_1() { +fn test_zip_1() +{ let mut a = Array2::::zeros((M, N)); Zip::from(&mut a).par_for_each(|x| *x = x.exp()); } #[test] -fn test_zip_index_1() { +fn test_zip_index_1() +{ let mut a = Array2::default((10, 10)); Zip::indexed(&mut a).par_for_each(|i, x| { @@ -28,7 +30,8 @@ fn test_zip_index_1() { } #[test] -fn test_zip_index_2() { +fn test_zip_index_2() +{ let mut a = Array2::default((M, N)); Zip::indexed(&mut a).par_for_each(|i, x| { @@ -41,7 +44,8 @@ fn test_zip_index_2() { } #[test] -fn test_zip_index_3() { +fn test_zip_index_3() +{ let mut a = Array::default((1, 2, 1, 2, 3)); Zip::indexed(&mut a).par_for_each(|i, x| { @@ -54,14 +58,17 @@ fn test_zip_index_3() { } #[test] -fn test_zip_index_4() { +fn test_zip_index_4() +{ let mut a = Array2::zeros((M, N)); let mut b = Array2::zeros((M, N)); - Zip::indexed(&mut a).and(&mut b).par_for_each(|(i, j), x, y| { - *x = i; - *y = j; - }); + Zip::indexed(&mut a) + .and(&mut b) + .par_for_each(|(i, j), x, y| { + *x = i; + *y = j; + }); for ((i, _), elt) in a.indexed_iter() { assert_eq!(*elt, i); @@ -73,7 +80,8 @@ fn test_zip_index_4() { #[test] #[cfg(feature = "approx")] -fn test_zip_collect() { +fn test_zip_collect() +{ use approx::assert_abs_diff_eq; // test Zip::map_collect and that it preserves c/f layout. @@ -97,12 +105,12 @@ fn test_zip_collect() { assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); assert_eq!(a.strides(), b.strides()); } - } #[test] #[cfg(feature = "approx")] -fn test_zip_small_collect() { +fn test_zip_small_collect() +{ use approx::assert_abs_diff_eq; for m in 0..32 { @@ -126,17 +134,19 @@ fn test_zip_small_collect() { } } - #[test] #[cfg(feature = "approx")] -fn test_zip_assign_into() { +fn test_zip_assign_into() +{ use approx::assert_abs_diff_eq; let mut a = Array::::zeros((M, N)); let b = Array::from_shape_fn((M, N), |(i, j)| 1. / (i + 2 * j + 1) as f32); let c = Array::from_shape_fn((M, N), |(i, j)| f32::ln((1 + i + j) as f32)); - Zip::from(&b).and(&c).par_map_assign_into(&mut a, |x, y| x + y); + Zip::from(&b) + .and(&c) + .par_map_assign_into(&mut a, |x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); } diff --git a/tests/raw_views.rs b/tests/raw_views.rs index bb39547e8..929e969d7 100644 --- a/tests/raw_views.rs +++ b/tests/raw_views.rs @@ -4,7 +4,8 @@ use ndarray::Zip; use std::cell::Cell; #[test] -fn raw_view_cast_cell() { +fn raw_view_cast_cell() +{ // Test .cast() by creating an ArrayView> let mut a = Array::from_shape_fn((10, 5), |(i, j)| (i * j) as f32); @@ -20,7 +21,8 @@ fn raw_view_cast_cell() { } #[test] -fn raw_view_cast_reinterpret() { +fn raw_view_cast_reinterpret() +{ // Test .cast() by reinterpreting u16 as [u8; 2] let a = Array::from_shape_fn((5, 5).f(), |(i, j)| (i as u16) << 8 | j as u16); let answer = a.mapv(u16::to_ne_bytes); @@ -31,7 +33,8 @@ fn raw_view_cast_reinterpret() { } #[test] -fn raw_view_cast_zst() { +fn raw_view_cast_zst() +{ struct Zst; let a = Array::<(), _>::default((250, 250)); @@ -42,14 +45,16 @@ fn raw_view_cast_zst() { #[test] #[should_panic] -fn raw_view_invalid_size_cast() { +fn raw_view_invalid_size_cast() +{ let data = [0i32; 16]; ArrayView::from(&data[..]).raw_view().cast::(); } #[test] #[should_panic] -fn raw_view_mut_invalid_size_cast() { +fn raw_view_mut_invalid_size_cast() +{ let mut data = [0i32; 16]; ArrayViewMut::from(&mut data[..]) .raw_view_mut() @@ -57,7 +62,8 @@ fn raw_view_mut_invalid_size_cast() { } #[test] -fn raw_view_misaligned() { +fn raw_view_misaligned() +{ let data: [u16; 2] = [0x0011, 0x2233]; let ptr: *const u16 = data.as_ptr(); unsafe { @@ -69,8 +75,10 @@ fn raw_view_misaligned() { #[test] #[cfg(debug_assertions)] #[should_panic = "The pointer must be aligned."] -fn raw_view_deref_into_view_misaligned() { - fn misaligned_deref(data: &[u16; 2]) -> ArrayView1<'_, u16> { +fn raw_view_deref_into_view_misaligned() +{ + fn misaligned_deref(data: &[u16; 2]) -> ArrayView1<'_, u16> + { let ptr: *const u16 = data.as_ptr(); unsafe { let misaligned_ptr = (ptr as *const u8).add(1) as *const u16; @@ -85,8 +93,10 @@ fn raw_view_deref_into_view_misaligned() { #[test] #[cfg(debug_assertions)] #[should_panic = "Unsupported"] -fn raw_view_negative_strides() { - fn misaligned_deref(data: &[u16; 2]) -> ArrayView1<'_, u16> { +fn raw_view_negative_strides() +{ + fn misaligned_deref(data: &[u16; 2]) -> ArrayView1<'_, u16> + { let ptr: *const u16 = data.as_ptr(); unsafe { let raw_view = RawArrayView::from_shape_ptr(1.strides((-1isize) as usize), ptr); diff --git a/tests/reshape.rs b/tests/reshape.rs index 24e7d01f8..a13a5c05f 100644 --- a/tests/reshape.rs +++ b/tests/reshape.rs @@ -5,7 +5,8 @@ use itertools::enumerate; use ndarray::Order; #[test] -fn reshape() { +fn reshape() +{ let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); let u = v.into_shape_with_order((3, 3)); @@ -21,7 +22,8 @@ fn reshape() { #[test] #[should_panic(expected = "IncompatibleShape")] -fn reshape_error1() { +fn reshape_error1() +{ let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); let _u = v.into_shape_with_order((2, 5)).unwrap(); @@ -29,7 +31,8 @@ fn reshape_error1() { #[test] #[should_panic(expected = "IncompatibleLayout")] -fn reshape_error2() { +fn reshape_error2() +{ let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); let mut u = v.into_shape_with_order((2, 2, 2)).unwrap(); @@ -38,7 +41,8 @@ fn reshape_error2() { } #[test] -fn reshape_f() { +fn reshape_f() +{ let mut u = Array::zeros((3, 4).f()); for (i, elt) in enumerate(u.as_slice_memory_order_mut().unwrap()) { *elt = i as i32; @@ -62,9 +66,9 @@ fn reshape_f() { assert_eq!(s, aview2(&[[0, 4, 8], [1, 5, 9], [2, 6, 10], [3, 7, 11]])); } - #[test] -fn to_shape_easy() { +fn to_shape_easy() +{ // 1D -> C -> C let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); @@ -103,7 +107,8 @@ fn to_shape_easy() { } #[test] -fn to_shape_copy() { +fn to_shape_copy() +{ // 1D -> C -> F let v = ArrayView::from(&[1, 2, 3, 4, 5, 6, 7, 8]); let u = v.to_shape(((4, 2), Order::RowMajor)).unwrap(); @@ -126,7 +131,8 @@ fn to_shape_copy() { } #[test] -fn to_shape_add_axis() { +fn to_shape_add_axis() +{ // 1D -> C -> C let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); @@ -136,9 +142,9 @@ fn to_shape_add_axis() { assert!(u.to_shape(((1, 4, 2), Order::ColumnMajor)).unwrap().is_view()); } - #[test] -fn to_shape_copy_stride() { +fn to_shape_copy_stride() +{ let v = array![[1, 2, 3, 4], [5, 6, 7, 8]]; let vs = v.slice(s![.., ..3]); let lin1 = vs.to_shape(6).unwrap(); @@ -150,9 +156,9 @@ fn to_shape_copy_stride() { assert!(lin2.is_owned()); } - #[test] -fn to_shape_zero_len() { +fn to_shape_zero_len() +{ let v = array![[1, 2, 3, 4], [5, 6, 7, 8]]; let vs = v.slice(s![.., ..0]); let lin1 = vs.to_shape(0).unwrap(); @@ -162,7 +168,8 @@ fn to_shape_zero_len() { #[test] #[should_panic(expected = "IncompatibleShape")] -fn to_shape_error1() { +fn to_shape_error1() +{ let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); let _u = v.to_shape((2, 5)).unwrap(); @@ -170,7 +177,8 @@ fn to_shape_error1() { #[test] #[should_panic(expected = "IncompatibleShape")] -fn to_shape_error2() { +fn to_shape_error2() +{ // overflow let data = [3, 4, 5, 6, 7, 8]; let v = aview1(&data); @@ -178,7 +186,8 @@ fn to_shape_error2() { } #[test] -fn to_shape_discontig() { +fn to_shape_discontig() +{ for &create_order in &[Order::C, Order::F] { let a = Array::from_iter(0..64); let mut a1 = a.to_shape(((4, 4, 4), create_order)).unwrap(); @@ -205,7 +214,8 @@ fn to_shape_discontig() { } #[test] -fn to_shape_broadcast() { +fn to_shape_broadcast() +{ for &create_order in &[Order::C, Order::F] { let a = Array::from_iter(0..64); let mut a1 = a.to_shape(((4, 4, 4), create_order)).unwrap(); @@ -231,9 +241,9 @@ fn to_shape_broadcast() { } } - #[test] -fn into_shape_with_order() { +fn into_shape_with_order() +{ // 1D -> C -> C let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); @@ -264,13 +274,16 @@ fn into_shape_with_order() { assert_eq!(u.shape(), &[2, 2, 2]); assert_eq!(u, array![[[1, 5], [3, 7]], [[2, 6], [4, 8]]]); - let s = u.into_shape_with_order(((4, 2), Order::ColumnMajor)).unwrap(); + let s = u + .into_shape_with_order(((4, 2), Order::ColumnMajor)) + .unwrap(); assert_eq!(s.shape(), &[4, 2]); assert_eq!(s, array![[1, 5], [2, 6], [3, 7], [4, 8]]); } #[test] -fn into_shape_clone() { +fn into_shape_clone() +{ // 1D -> C -> C { let data = [1, 2, 3, 4, 5, 6, 7, 8]; diff --git a/tests/s.rs b/tests/s.rs index dbc68184a..edb3f071a 100644 --- a/tests/s.rs +++ b/tests/s.rs @@ -1,14 +1,12 @@ #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] use ndarray::{s, Array}; #[test] -fn test_s() { +fn test_s() +{ let a = Array::::zeros((3, 4)); let vi = a.slice(s![1.., ..;2]); assert_eq!(vi.shape(), &[2, 2]); diff --git a/tests/stacking.rs b/tests/stacking.rs index 0c4e79c79..bdfe478b4 100644 --- a/tests/stacking.rs +++ b/tests/stacking.rs @@ -1,7 +1,8 @@ use ndarray::{arr2, arr3, aview1, aview2, concatenate, stack, Array2, Axis, ErrorKind, Ix1}; #[test] -fn concatenating() { +fn concatenating() +{ let a = arr2(&[[2., 2.], [3., 3.]]); let b = ndarray::concatenate(Axis(0), &[a.view(), a.view()]).unwrap(); assert_eq!(b, arr2(&[[2., 2.], [3., 3.], [2., 2.], [3., 3.]])); @@ -33,7 +34,8 @@ fn concatenating() { } #[test] -fn stacking() { +fn stacking() +{ let a = arr2(&[[2., 2.], [3., 3.]]); let b = ndarray::stack(Axis(0), &[a.view(), a.view()]).unwrap(); assert_eq!(b, arr3(&[[[2., 2.], [3., 3.]], [[2., 2.], [3., 3.]]])); diff --git a/tests/views.rs b/tests/views.rs index ecef72fe8..02970b1b7 100644 --- a/tests/views.rs +++ b/tests/views.rs @@ -2,7 +2,8 @@ use ndarray::prelude::*; use ndarray::Zip; #[test] -fn cell_view() { +fn cell_view() +{ let mut a = Array::from_shape_fn((10, 5), |(i, j)| (i * j) as f32); let answer = &a + 1.; diff --git a/tests/windows.rs b/tests/windows.rs index c24a47ef9..d8d5b699e 100644 --- a/tests/windows.rs +++ b/tests/windows.rs @@ -1,8 +1,5 @@ #![allow( - clippy::many_single_char_names, - clippy::deref_addrof, - clippy::unreadable_literal, - clippy::many_single_char_names + clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] use ndarray::prelude::*; @@ -25,26 +22,31 @@ use ndarray::{arr3, Zip}; /// Test that verifies the `Windows` iterator panics on window sizes equal to zero. #[test] #[should_panic] -fn windows_iterator_zero_size() { - let a = Array::from_iter(10..37).into_shape_with_order((3, 3, 3)).unwrap(); +fn windows_iterator_zero_size() +{ + let a = Array::from_iter(10..37) + .into_shape_with_order((3, 3, 3)) + .unwrap(); a.windows(Dim((0, 0, 0))); } /// Test that verifies that no windows are yielded on oversized window sizes. #[test] -fn windows_iterator_oversized() { - let a = Array::from_iter(10..37).into_shape_with_order((3, 3, 3)).unwrap(); +fn windows_iterator_oversized() +{ + let a = Array::from_iter(10..37) + .into_shape_with_order((3, 3, 3)) + .unwrap(); let mut iter = a.windows((4, 3, 2)).into_iter(); // (4,3,2) doesn't fit into (3,3,3) => oversized! assert_eq!(iter.next(), None); } /// Simple test for iterating 1d-arrays via `Windows`. #[test] -fn windows_iterator_1d() { +fn windows_iterator_1d() +{ let a = Array::from_iter(10..20).into_shape_with_order(10).unwrap(); - itertools::assert_equal( - a.windows(Dim(4)), - vec![ + itertools::assert_equal(a.windows(Dim(4)), vec![ arr1(&[10, 11, 12, 13]), arr1(&[11, 12, 13, 14]), arr1(&[12, 13, 14, 15]), @@ -52,17 +54,17 @@ fn windows_iterator_1d() { arr1(&[14, 15, 16, 17]), arr1(&[15, 16, 17, 18]), arr1(&[16, 17, 18, 19]), - ], - ); + ]); } /// Simple test for iterating 2d-arrays via `Windows`. #[test] -fn windows_iterator_2d() { - let a = Array::from_iter(10..30).into_shape_with_order((5, 4)).unwrap(); - itertools::assert_equal( - a.windows(Dim((3, 2))), - vec![ +fn windows_iterator_2d() +{ + let a = Array::from_iter(10..30) + .into_shape_with_order((5, 4)) + .unwrap(); + itertools::assert_equal(a.windows(Dim((3, 2))), vec![ arr2(&[[10, 11], [14, 15], [18, 19]]), arr2(&[[11, 12], [15, 16], [19, 20]]), arr2(&[[12, 13], [16, 17], [20, 21]]), @@ -72,17 +74,17 @@ fn windows_iterator_2d() { arr2(&[[18, 19], [22, 23], [26, 27]]), arr2(&[[19, 20], [23, 24], [27, 28]]), arr2(&[[20, 21], [24, 25], [28, 29]]), - ], - ); + ]); } /// Simple test for iterating 3d-arrays via `Windows`. #[test] -fn windows_iterator_3d() { - let a = Array::from_iter(10..37).into_shape_with_order((3, 3, 3)).unwrap(); - itertools::assert_equal( - a.windows(Dim((2, 2, 2))), - vec![ +fn windows_iterator_3d() +{ + let a = Array::from_iter(10..37) + .into_shape_with_order((3, 3, 3)) + .unwrap(); + itertools::assert_equal(a.windows(Dim((2, 2, 2))), vec![ arr3(&[[[10, 11], [13, 14]], [[19, 20], [22, 23]]]), arr3(&[[[11, 12], [14, 15]], [[20, 21], [23, 24]]]), arr3(&[[[13, 14], [16, 17]], [[22, 23], [25, 26]]]), @@ -91,68 +93,69 @@ fn windows_iterator_3d() { arr3(&[[[20, 21], [23, 24]], [[29, 30], [32, 33]]]), arr3(&[[[22, 23], [25, 26]], [[31, 32], [34, 35]]]), arr3(&[[[23, 24], [26, 27]], [[32, 33], [35, 36]]]), - ], - ); + ]); } /// Test that verifies the `Windows` iterator panics when stride has an axis equal to zero. #[test] #[should_panic] -fn windows_iterator_stride_axis_zero() { - let a = Array::from_iter(10..37).into_shape_with_order((3, 3, 3)).unwrap(); +fn windows_iterator_stride_axis_zero() +{ + let a = Array::from_iter(10..37) + .into_shape_with_order((3, 3, 3)) + .unwrap(); a.windows_with_stride((2, 2, 2), (0, 2, 2)); } /// Test that verifies that only first window is yielded when stride is oversized on every axis. #[test] -fn windows_iterator_only_one_valid_window_for_oversized_stride() { - let a = Array::from_iter(10..135).into_shape_with_order((5, 5, 5)).unwrap(); +fn windows_iterator_only_one_valid_window_for_oversized_stride() +{ + let a = Array::from_iter(10..135) + .into_shape_with_order((5, 5, 5)) + .unwrap(); let mut iter = a.windows_with_stride((2, 2, 2), (8, 8, 8)).into_iter(); // (4,3,2) doesn't fit into (3,3,3) => oversized! - itertools::assert_equal( - iter.next(), - Some(arr3(&[[[10, 11], [15, 16]], [[35, 36], [40, 41]]])), - ); + itertools::assert_equal(iter.next(), Some(arr3(&[[[10, 11], [15, 16]], [[35, 36], [40, 41]]]))); } /// Simple test for iterating 1d-arrays via `Windows` with stride. #[test] -fn windows_iterator_1d_with_stride() { +fn windows_iterator_1d_with_stride() +{ let a = Array::from_iter(10..20).into_shape_with_order(10).unwrap(); - itertools::assert_equal( - a.windows_with_stride(4, 2), - vec![ + itertools::assert_equal(a.windows_with_stride(4, 2), vec![ arr1(&[10, 11, 12, 13]), arr1(&[12, 13, 14, 15]), arr1(&[14, 15, 16, 17]), arr1(&[16, 17, 18, 19]), - ], - ); + ]); } /// Simple test for iterating 2d-arrays via `Windows` with stride. #[test] -fn windows_iterator_2d_with_stride() { - let a = Array::from_iter(10..30).into_shape_with_order((5, 4)).unwrap(); - itertools::assert_equal( - a.windows_with_stride((3, 2), (2, 1)), - vec![ +fn windows_iterator_2d_with_stride() +{ + let a = Array::from_iter(10..30) + .into_shape_with_order((5, 4)) + .unwrap(); + itertools::assert_equal(a.windows_with_stride((3, 2), (2, 1)), vec![ arr2(&[[10, 11], [14, 15], [18, 19]]), arr2(&[[11, 12], [15, 16], [19, 20]]), arr2(&[[12, 13], [16, 17], [20, 21]]), arr2(&[[18, 19], [22, 23], [26, 27]]), arr2(&[[19, 20], [23, 24], [27, 28]]), arr2(&[[20, 21], [24, 25], [28, 29]]), - ], - ); + ]); } /// Simple test for iterating 3d-arrays via `Windows` with stride. #[test] -fn windows_iterator_3d_with_stride() { - let a = Array::from_iter(10..74).into_shape_with_order((4, 4, 4)).unwrap(); - itertools::assert_equal( - a.windows_with_stride((2, 2, 2), (2, 2, 2)), - vec![ +fn windows_iterator_3d_with_stride() +{ + let a = Array::from_iter(10..74) + .into_shape_with_order((4, 4, 4)) + .unwrap(); + itertools::assert_equal(a.windows_with_stride((2, 2, 2), (2, 2, 2)), vec![ arr3(&[[[10, 11], [14, 15]], [[26, 27], [30, 31]]]), arr3(&[[[12, 13], [16, 17]], [[28, 29], [32, 33]]]), arr3(&[[[18, 19], [22, 23]], [[34, 35], [38, 39]]]), @@ -161,13 +164,15 @@ fn windows_iterator_3d_with_stride() { arr3(&[[[44, 45], [48, 49]], [[60, 61], [64, 65]]]), arr3(&[[[50, 51], [54, 55]], [[66, 67], [70, 71]]]), arr3(&[[[52, 53], [56, 57]], [[68, 69], [72, 73]]]), - ], - ); + ]); } #[test] -fn test_window_zip() { - let a = Array::from_iter(0..64).into_shape_with_order((4, 4, 4)).unwrap(); +fn test_window_zip() +{ + let a = Array::from_iter(0..64) + .into_shape_with_order((4, 4, 4)) + .unwrap(); for x in 1..4 { for y in 1..4 { @@ -189,69 +194,77 @@ fn test_window_zip() { /// Test verifies that non existent Axis results in panic #[test] #[should_panic] -fn axis_windows_outofbound() { - let a = Array::from_iter(10..37).into_shape_with_order((3, 3, 3)).unwrap(); +fn axis_windows_outofbound() +{ + let a = Array::from_iter(10..37) + .into_shape_with_order((3, 3, 3)) + .unwrap(); a.axis_windows(Axis(4), 2); } /// Test verifies that zero sizes results in panic #[test] #[should_panic] -fn axis_windows_zero_size() { - let a = Array::from_iter(10..37).into_shape_with_order((3, 3, 3)).unwrap(); +fn axis_windows_zero_size() +{ + let a = Array::from_iter(10..37) + .into_shape_with_order((3, 3, 3)) + .unwrap(); a.axis_windows(Axis(0), 0); } /// Test verifies that over sized windows yield nothing #[test] -fn axis_windows_oversized() { - let a = Array::from_iter(10..37).into_shape_with_order((3, 3, 3)).unwrap(); +fn axis_windows_oversized() +{ + let a = Array::from_iter(10..37) + .into_shape_with_order((3, 3, 3)) + .unwrap(); let mut iter = a.axis_windows(Axis(2), 4).into_iter(); assert_eq!(iter.next(), None); } /// Simple test for iterating 1d-arrays via `Axis Windows`. #[test] -fn test_axis_windows_1d() { +fn test_axis_windows_1d() +{ let a = Array::from_iter(10..20).into_shape_with_order(10).unwrap(); - itertools::assert_equal( - a.axis_windows(Axis(0), 5), - vec![ + itertools::assert_equal(a.axis_windows(Axis(0), 5), vec![ arr1(&[10, 11, 12, 13, 14]), arr1(&[11, 12, 13, 14, 15]), arr1(&[12, 13, 14, 15, 16]), arr1(&[13, 14, 15, 16, 17]), arr1(&[14, 15, 16, 17, 18]), arr1(&[15, 16, 17, 18, 19]), - ], - ); + ]); } /// Simple test for iterating 2d-arrays via `Axis Windows`. #[test] -fn test_axis_windows_2d() { - let a = Array::from_iter(10..30).into_shape_with_order((5, 4)).unwrap(); +fn test_axis_windows_2d() +{ + let a = Array::from_iter(10..30) + .into_shape_with_order((5, 4)) + .unwrap(); - itertools::assert_equal( - a.axis_windows(Axis(0), 2), - vec![ + itertools::assert_equal(a.axis_windows(Axis(0), 2), vec![ arr2(&[[10, 11, 12, 13], [14, 15, 16, 17]]), arr2(&[[14, 15, 16, 17], [18, 19, 20, 21]]), arr2(&[[18, 19, 20, 21], [22, 23, 24, 25]]), arr2(&[[22, 23, 24, 25], [26, 27, 28, 29]]), - ], - ); + ]); } /// Simple test for iterating 3d-arrays via `Axis Windows`. #[test] -fn test_axis_windows_3d() { - let a = Array::from_iter(0..27).into_shape_with_order((3, 3, 3)).unwrap(); +fn test_axis_windows_3d() +{ + let a = Array::from_iter(0..27) + .into_shape_with_order((3, 3, 3)) + .unwrap(); - itertools::assert_equal( - a.axis_windows(Axis(1), 2), - vec![ + itertools::assert_equal(a.axis_windows(Axis(1), 2), vec![ arr3(&[ [[0, 1, 2], [3, 4, 5]], [[9, 10, 11], [12, 13, 14]], @@ -262,71 +275,61 @@ fn test_axis_windows_3d() { [[12, 13, 14], [15, 16, 17]], [[21, 22, 23], [24, 25, 26]], ]), - ], - ); + ]); } - #[test] -fn test_window_neg_stride() { - let array = Array::from_iter(1..10).into_shape_with_order((3, 3)).unwrap(); +fn test_window_neg_stride() +{ + let array = Array::from_iter(1..10) + .into_shape_with_order((3, 3)) + .unwrap(); // window neg/pos stride combinations - + // Make a 2 x 2 array of the windows of the 3 x 3 array // and compute test answers from here let mut answer = Array::from_iter(array.windows((2, 2)).into_iter().map(|a| a.to_owned())) - .into_shape_with_order((2, 2)).unwrap(); + .into_shape_with_order((2, 2)) + .unwrap(); answer.invert_axis(Axis(1)); answer.map_inplace(|a| a.invert_axis(Axis(1))); - itertools::assert_equal( - array.slice(s![.., ..;-1]).windows((2, 2)), - answer.iter() - ); + itertools::assert_equal(array.slice(s![.., ..;-1]).windows((2, 2)), answer.iter()); answer.invert_axis(Axis(0)); answer.map_inplace(|a| a.invert_axis(Axis(0))); - itertools::assert_equal( - array.slice(s![..;-1, ..;-1]).windows((2, 2)), - answer.iter() - ); + itertools::assert_equal(array.slice(s![..;-1, ..;-1]).windows((2, 2)), answer.iter()); answer.invert_axis(Axis(1)); answer.map_inplace(|a| a.invert_axis(Axis(1))); - itertools::assert_equal( - array.slice(s![..;-1, ..]).windows((2, 2)), - answer.iter() - ); + itertools::assert_equal(array.slice(s![..;-1, ..]).windows((2, 2)), answer.iter()); } #[test] -fn test_windows_with_stride_on_inverted_axis() { - let mut array = Array::from_iter(1..17).into_shape_with_order((4, 4)).unwrap(); - +fn test_windows_with_stride_on_inverted_axis() +{ + let mut array = Array::from_iter(1..17) + .into_shape_with_order((4, 4)) + .unwrap(); + // inverting axis results in negative stride array.invert_axis(Axis(0)); - itertools::assert_equal( - array.windows_with_stride((2, 2), (2,2)), - vec![ + itertools::assert_equal(array.windows_with_stride((2, 2), (2, 2)), vec![ arr2(&[[13, 14], [9, 10]]), arr2(&[[15, 16], [11, 12]]), arr2(&[[5, 6], [1, 2]]), arr2(&[[7, 8], [3, 4]]), - ], - ); + ]); array.invert_axis(Axis(1)); - itertools::assert_equal( - array.windows_with_stride((2, 2), (2,2)), - vec![ + itertools::assert_equal(array.windows_with_stride((2, 2), (2, 2)), vec![ arr2(&[[16, 15], [12, 11]]), arr2(&[[14, 13], [10, 9]]), arr2(&[[8, 7], [4, 3]]), arr2(&[[6, 5], [2, 1]]), - ], - ); -} \ No newline at end of file + ]); +} diff --git a/tests/zst.rs b/tests/zst.rs index c3c779d2c..f5f2c8e32 100644 --- a/tests/zst.rs +++ b/tests/zst.rs @@ -2,7 +2,8 @@ use ndarray::arr2; use ndarray::ArcArray; #[test] -fn test_swap() { +fn test_swap() +{ let mut a = arr2(&[[(); 3]; 3]); let b = a.clone(); @@ -16,7 +17,8 @@ fn test_swap() { } #[test] -fn test() { +fn test() +{ let c = ArcArray::<(), _>::default((3, 4)); let mut d = c.clone(); for _ in d.iter_mut() {} diff --git a/xtest-blas/src/lib.rs b/xtest-blas/src/lib.rs index 857b91a3b..fc031eedb 100644 --- a/xtest-blas/src/lib.rs +++ b/xtest-blas/src/lib.rs @@ -1,6 +1,4 @@ - #[cfg(not(feature = "blas-src"))] compile_error!("Missing backend: could not compile. Help: For this testing crate, select one of the blas backend features, for example \ openblas-system"); - diff --git a/xtest-blas/tests/oper.rs b/xtest-blas/tests/oper.rs index 5f11893df..3ed81915e 100644 --- a/xtest-blas/tests/oper.rs +++ b/xtest-blas/tests/oper.rs @@ -17,7 +17,8 @@ use num_complex::Complex32; use num_complex::Complex64; #[test] -fn mat_vec_product_1d() { +fn mat_vec_product_1d() +{ let a = arr2(&[[1.], [2.]]); let b = arr1(&[1., 2.]); let ans = arr1(&[5.]); @@ -25,7 +26,8 @@ fn mat_vec_product_1d() { } #[test] -fn mat_vec_product_1d_broadcast() { +fn mat_vec_product_1d_broadcast() +{ let a = arr2(&[[1.], [2.], [3.]]); let b = arr1(&[1.]); let b = b.broadcast(3).unwrap(); @@ -34,7 +36,8 @@ fn mat_vec_product_1d_broadcast() { } #[test] -fn mat_vec_product_1d_inverted_axis() { +fn mat_vec_product_1d_inverted_axis() +{ let a = arr2(&[[1.], [2.], [3.]]); let mut b = arr1(&[1., 2., 3.]); b.invert_axis(Axis(0)); @@ -43,37 +46,43 @@ fn mat_vec_product_1d_inverted_axis() { assert_eq!(a.t().dot(&b), ans); } -fn range_mat(m: Ix, n: Ix) -> Array2 { +fn range_mat(m: Ix, n: Ix) -> Array2 +{ Array::linspace(0., (m * n) as f32 - 1., m * n) .into_shape_with_order((m, n)) .unwrap() } -fn range_mat64(m: Ix, n: Ix) -> Array2 { +fn range_mat64(m: Ix, n: Ix) -> Array2 +{ Array::linspace(0., (m * n) as f64 - 1., m * n) .into_shape_with_order((m, n)) .unwrap() } -fn range_mat_complex(m: Ix, n: Ix) -> Array2 { +fn range_mat_complex(m: Ix, n: Ix) -> Array2 +{ Array::linspace(0., (m * n) as f32 - 1., m * n) .into_shape_with_order((m, n)) .unwrap() .map(|&f| Complex32::new(f, 0.)) } -fn range_mat_complex64(m: Ix, n: Ix) -> Array2 { +fn range_mat_complex64(m: Ix, n: Ix) -> Array2 +{ Array::linspace(0., (m * n) as f64 - 1., m * n) .into_shape_with_order((m, n)) .unwrap() .map(|&f| Complex64::new(f, 0.)) } -fn range1_mat64(m: Ix) -> Array1 { +fn range1_mat64(m: Ix) -> Array1 +{ Array::linspace(0., m as f64 - 1., m) } -fn range_i32(m: Ix, n: Ix) -> Array2 { +fn range_i32(m: Ix, n: Ix) -> Array2 +{ Array::from_iter(0..(m * n) as i32) .into_shape_with_order((m, n)) .unwrap() @@ -98,9 +107,7 @@ where let mut j = 0; for rr in &mut res_elems { unsafe { - *rr = (0..k).fold(A::zero(), move |s, x| { - s + *lhs.uget((i, x)) * *rhs.uget((x, j)) - }); + *rr = (0..k).fold(A::zero(), move |s, x| s + *lhs.uget((i, x)) * *rhs.uget((x, j))); } j += 1; if j == n { @@ -119,9 +126,14 @@ where S2: Data, { let ((m, _), k) = (lhs.dim(), rhs.dim()); - reference_mat_mul(lhs, &rhs.as_standard_layout().into_shape_with_order((k, 1)).unwrap()) - .into_shape_with_order(m) - .unwrap() + reference_mat_mul( + lhs, + &rhs.as_standard_layout() + .into_shape_with_order((k, 1)) + .unwrap(), + ) + .into_shape_with_order(m) + .unwrap() } // simple, slow, correct (hopefully) mat mul @@ -132,15 +144,21 @@ where S2: Data, { let (m, (_, n)) = (lhs.dim(), rhs.dim()); - reference_mat_mul(&lhs.as_standard_layout().into_shape_with_order((1, m)).unwrap(), rhs) - .into_shape_with_order(n) - .unwrap() + reference_mat_mul( + &lhs.as_standard_layout() + .into_shape_with_order((1, m)) + .unwrap(), + rhs, + ) + .into_shape_with_order(n) + .unwrap() } // Check that matrix multiplication of contiguous matrices returns a // matrix with the same order #[test] -fn mat_mul_order() { +fn mat_mul_order() +{ let (m, n, k) = (50, 50, 50); let a = range_mat(m, n); let b = range_mat(n, k); @@ -159,7 +177,8 @@ fn mat_mul_order() { // Check that matrix multiplication // supports broadcast arrays. #[test] -fn mat_mul_broadcast() { +fn mat_mul_broadcast() +{ let (m, n, k) = (16, 16, 16); let a = range_mat(m, n); let x1 = 1.; @@ -178,7 +197,8 @@ fn mat_mul_broadcast() { // Check that matrix multiplication supports reversed axes #[test] -fn mat_mul_rev() { +fn mat_mul_rev() +{ let (m, n, k) = (16, 16, 16); let a = range_mat(m, n); let b = range_mat(n, k); @@ -194,7 +214,8 @@ fn mat_mul_rev() { // Check that matrix multiplication supports arrays with zero rows or columns #[test] -fn mat_mut_zero_len() { +fn mat_mut_zero_len() +{ defmac!(mat_mul_zero_len range_mat_fn => { for n in 0..4 { for m in 0..4 { @@ -215,7 +236,8 @@ fn mat_mut_zero_len() { } #[test] -fn gen_mat_mul() { +fn gen_mat_mul() +{ let alpha = -2.3; let beta = 3.14; let sizes = vec![ @@ -256,7 +278,8 @@ fn gen_mat_mul() { // Test y = A x where A is f-order #[test] -fn gemm_64_1_f() { +fn gemm_64_1_f() +{ let a = range_mat64(64, 64).reversed_axes(); let (m, n) = a.dim(); // m x n times n x 1 == m x 1 @@ -268,20 +291,15 @@ fn gemm_64_1_f() { } #[test] -fn gemm_c64_1_f() { +fn gemm_c64_1_f() +{ let a = range_mat_complex64(64, 64).reversed_axes(); let (m, n) = a.dim(); // m x n times n x 1 == m x 1 let x = range_mat_complex64(n, 1); let mut y = range_mat_complex64(m, 1); let answer = reference_mat_mul(&a, &x) + &y; - general_mat_mul( - Complex64::new(1.0, 0.), - &a, - &x, - Complex64::new(1.0, 0.), - &mut y, - ); + general_mat_mul(Complex64::new(1.0, 0.), &a, &x, Complex64::new(1.0, 0.), &mut y); assert_relative_eq!( y.mapv(|i| i.norm_sqr()), answer.mapv(|i| i.norm_sqr()), @@ -291,20 +309,15 @@ fn gemm_c64_1_f() { } #[test] -fn gemm_c32_1_f() { +fn gemm_c32_1_f() +{ let a = range_mat_complex(64, 64).reversed_axes(); let (m, n) = a.dim(); // m x n times n x 1 == m x 1 let x = range_mat_complex(n, 1); let mut y = range_mat_complex(m, 1); let answer = reference_mat_mul(&a, &x) + &y; - general_mat_mul( - Complex32::new(1.0, 0.), - &a, - &x, - Complex32::new(1.0, 0.), - &mut y, - ); + general_mat_mul(Complex32::new(1.0, 0.), &a, &x, Complex32::new(1.0, 0.), &mut y); assert_relative_eq!( y.mapv(|i| i.norm_sqr()), answer.mapv(|i| i.norm_sqr()), @@ -314,22 +327,17 @@ fn gemm_c32_1_f() { } #[test] -fn gemm_c64_actually_complex() { - let mut a = range_mat_complex64(4,4); +fn gemm_c64_actually_complex() +{ + let mut a = range_mat_complex64(4, 4); a = a.map(|&i| if i.re > 8. { i.conj() } else { i }); - let mut b = range_mat_complex64(4,6); - b = b.map(|&i| if i.re > 4. { i.conj() } else {i}); - let mut y = range_mat_complex64(4,6); + let mut b = range_mat_complex64(4, 6); + b = b.map(|&i| if i.re > 4. { i.conj() } else { i }); + let mut y = range_mat_complex64(4, 6); let alpha = Complex64::new(0., 1.0); let beta = Complex64::new(1.0, 1.0); let answer = alpha * reference_mat_mul(&a, &b) + beta * &y; - general_mat_mul( - alpha.clone(), - &a, - &b, - beta.clone(), - &mut y, - ); + general_mat_mul(alpha.clone(), &a, &b, beta.clone(), &mut y); assert_relative_eq!( y.mapv(|i| i.norm_sqr()), answer.mapv(|i| i.norm_sqr()), @@ -339,7 +347,8 @@ fn gemm_c64_actually_complex() { } #[test] -fn gen_mat_vec_mul() { +fn gen_mat_vec_mul() +{ let alpha = -2.3; let beta = 3.14; let sizes = vec![ @@ -385,7 +394,8 @@ fn gen_mat_vec_mul() { } #[test] -fn vec_mat_mul() { +fn vec_mat_mul() +{ let sizes = vec![ (4, 4), (8, 8), diff --git a/xtest-numeric/src/lib.rs b/xtest-numeric/src/lib.rs index ba1c3b0d9..79ffc274e 100644 --- a/xtest-numeric/src/lib.rs +++ b/xtest-numeric/src/lib.rs @@ -1,3 +1,2 @@ #[cfg(feature = "test_blas")] extern crate blas_src; - diff --git a/xtest-numeric/tests/accuracy.rs b/xtest-numeric/tests/accuracy.rs index 679267096..e98fb3c4d 100644 --- a/xtest-numeric/tests/accuracy.rs +++ b/xtest-numeric/tests/accuracy.rs @@ -1,32 +1,29 @@ extern crate approx; -extern crate rand_distr; extern crate ndarray; extern crate ndarray_rand; extern crate rand; +extern crate rand_distr; extern crate numeric_tests; use std::fmt; use ndarray_rand::RandomExt; -use rand::{Rng, SeedableRng}; use rand::rngs::SmallRng; +use rand::{Rng, SeedableRng}; -use ndarray::prelude::*; -use ndarray::{ - Data, - LinalgScalar, -}; use ndarray::linalg::general_mat_mul; +use ndarray::prelude::*; +use ndarray::{Data, LinalgScalar}; -use rand_distr::{Normal, StandardNormal, Distribution}; -use num_traits::{Float, AsPrimitive}; use num_complex::Complex; +use num_traits::{AsPrimitive, Float}; +use rand_distr::{Distribution, Normal, StandardNormal}; use approx::{assert_abs_diff_eq, assert_relative_eq}; fn kahan_sum(iter: impl Iterator) -> A - where A: LinalgScalar +where A: LinalgScalar { let mut sum = A::zero(); let mut compensation = A::zero(); @@ -42,11 +39,11 @@ fn kahan_sum(iter: impl Iterator) -> A } // simple, slow, correct (hopefully) mat mul -fn reference_mat_mul(lhs: &ArrayBase, rhs: &ArrayBase) - -> Array - where A: LinalgScalar, - S: Data, - S2: Data, +fn reference_mat_mul(lhs: &ArrayBase, rhs: &ArrayBase) -> Array +where + A: LinalgScalar, + S: Data, + S2: Data, { let ((m, k), (_, n)) = (lhs.dim(), rhs.dim()); let mut res_elems = Array::zeros(m * n); @@ -69,23 +66,26 @@ fn reference_mat_mul(lhs: &ArrayBase, rhs: &ArrayBase } fn gen(d: D, rng: &mut SmallRng) -> Array - where D: Dimension, - A: Float, - StandardNormal: Distribution, +where + D: Dimension, + A: Float, + StandardNormal: Distribution, { Array::random_using(d, Normal::new(A::zero(), A::one()).unwrap(), rng) } fn gen_complex(d: D, rng: &mut SmallRng) -> Array, D> - where D: Dimension, - A: Float, - StandardNormal: Distribution, +where + D: Dimension, + A: Float, + StandardNormal: Distribution, { gen(d.clone(), rng).mapv(Complex::from) + gen(d, rng).mapv(|x| Complex::new(A::zero(), x)) } #[test] -fn accurate_eye_f32() { +fn accurate_eye_f32() +{ let rng = &mut SmallRng::from_entropy(); for i in 0..20 { let eye = Array::eye(i); @@ -112,7 +112,8 @@ fn accurate_eye_f32() { } #[test] -fn accurate_eye_f64() { +fn accurate_eye_f64() +{ let rng = &mut SmallRng::from_entropy(); let abs_tol = 1e-15; for i in 0..20 { @@ -140,32 +141,36 @@ fn accurate_eye_f64() { } #[test] -fn accurate_mul_f32_dot() { +fn accurate_mul_f32_dot() +{ accurate_mul_float_general::(1e-5, false); } #[test] -fn accurate_mul_f32_general() { +fn accurate_mul_f32_general() +{ accurate_mul_float_general::(1e-5, true); } #[test] -fn accurate_mul_f64_dot() { +fn accurate_mul_f64_dot() +{ accurate_mul_float_general::(1e-14, false); } #[test] -fn accurate_mul_f64_general() { +fn accurate_mul_f64_general() +{ accurate_mul_float_general::(1e-14, true); } /// Generate random sized matrices using the given generator function. /// Compute gemm using either .dot() (if use_general is false) otherwise general_mat_mul. /// Return tuple of actual result matrix and reference matrix, which should be equal. -fn random_matrix_mul(rng: &mut SmallRng, use_stride: bool, use_general: bool, - generator: fn(Ix2, &mut SmallRng) -> Array2) - -> (Array2, Array2) - where A: LinalgScalar, +fn random_matrix_mul( + rng: &mut SmallRng, use_stride: bool, use_general: bool, generator: fn(Ix2, &mut SmallRng) -> Array2, +) -> (Array2, Array2) +where A: LinalgScalar { let m = rng.gen_range(15..512); let k = rng.gen_range(15..512); @@ -180,13 +185,9 @@ fn random_matrix_mul(rng: &mut SmallRng, use_stride: bool, use_general: bool, let b = b.t(); let (a, b, mut c) = if use_stride { - (a.slice(s![..;2, ..;2]), - b.slice(s![..;2, ..;2]), - c.map(|c_| c_.slice_move(s![..;2, ..;2]))) + (a.slice(s![..;2, ..;2]), b.slice(s![..;2, ..;2]), c.map(|c_| c_.slice_move(s![..;2, ..;2]))) } else { - (a.view(), - b, - c) + (a.view(), b, c) }; println!("Testing size {} by {} by {}", a.shape()[0], a.shape()[1], b.shape()[1]); @@ -202,9 +203,10 @@ fn random_matrix_mul(rng: &mut SmallRng, use_stride: bool, use_general: bool, } fn accurate_mul_float_general(limit: f64, use_general: bool) - where A: Float + Copy + 'static + AsPrimitive, - StandardNormal: Distribution, - A: fmt::Debug, +where + A: Float + Copy + 'static + AsPrimitive, + StandardNormal: Distribution, + A: fmt::Debug, { // pick a few random sizes let mut rng = SmallRng::from_entropy(); @@ -221,19 +223,22 @@ fn accurate_mul_float_general(limit: f64, use_general: bool) } #[test] -fn accurate_mul_complex32() { +fn accurate_mul_complex32() +{ accurate_mul_complex_general::(1e-5); } #[test] -fn accurate_mul_complex64() { +fn accurate_mul_complex64() +{ accurate_mul_complex_general::(1e-14); } fn accurate_mul_complex_general(limit: f64) - where A: Float + Copy + 'static + AsPrimitive, - StandardNormal: Distribution, - A: fmt::Debug, +where + A: Float + Copy + 'static + AsPrimitive, + StandardNormal: Distribution, + A: fmt::Debug, { // pick a few random sizes let mut rng = SmallRng::from_entropy(); @@ -251,7 +256,8 @@ fn accurate_mul_complex_general(limit: f64) } #[test] -fn accurate_mul_with_column_f64() { +fn accurate_mul_with_column_f64() +{ // pick a few random sizes let rng = &mut SmallRng::from_entropy(); for i in 0..10 { @@ -264,8 +270,8 @@ fn accurate_mul_with_column_f64() { // pick dense square or broadcasted to square matrix match i { - 0 ..= 3 => b_sq = b_owner.view(), - 4 ..= 7 => { + 0..=3 => b_sq = b_owner.view(), + 4..=7 => { b_row_col = b_owner.column(0); b_sq = b_row_col.broadcast((k, k)).unwrap(); } diff --git a/xtest-serialization/tests/serialize.rs b/xtest-serialization/tests/serialize.rs index cea84dd7f..95e93e4fb 100644 --- a/xtest-serialization/tests/serialize.rs +++ b/xtest-serialization/tests/serialize.rs @@ -12,7 +12,8 @@ extern crate ron; use ndarray::{arr0, arr1, arr2, s, ArcArray, ArcArray2, ArrayD, IxDyn}; #[test] -fn serial_many_dim_serde() { +fn serial_many_dim_serde() +{ { let a = arr0::(2.72); let serial = serde_json::to_string(&a).unwrap(); @@ -45,7 +46,9 @@ fn serial_many_dim_serde() { { // Test a sliced array. - let mut a = ArcArray::linspace(0., 31., 32).into_shape_with_order((2, 2, 2, 4)).unwrap(); + let mut a = ArcArray::linspace(0., 31., 32) + .into_shape_with_order((2, 2, 2, 4)) + .unwrap(); a.slice_collapse(s![..;-1, .., .., ..2]); let serial = serde_json::to_string(&a).unwrap(); println!("Encode {:?} => {:?}", a, serial); @@ -56,7 +59,8 @@ fn serial_many_dim_serde() { } #[test] -fn serial_ixdyn_serde() { +fn serial_ixdyn_serde() +{ { let a = arr0::(2.72).into_dyn(); let serial = serde_json::to_string(&a).unwrap(); @@ -95,7 +99,8 @@ fn serial_ixdyn_serde() { } #[test] -fn serial_wrong_count_serde() { +fn serial_wrong_count_serde() +{ // one element too few let text = r##"{"v":1,"dim":[2,3],"data":[3,1,2.2,3.1,4]}"##; let arr = serde_json::from_str::>(text); @@ -110,7 +115,8 @@ fn serial_wrong_count_serde() { } #[test] -fn serial_many_dim_serde_msgpack() { +fn serial_many_dim_serde_msgpack() +{ { let a = arr0::(2.72); @@ -155,7 +161,9 @@ fn serial_many_dim_serde_msgpack() { { // Test a sliced array. - let mut a = ArcArray::linspace(0., 31., 32).into_shape_with_order((2, 2, 2, 4)).unwrap(); + let mut a = ArcArray::linspace(0., 31., 32) + .into_shape_with_order((2, 2, 2, 4)) + .unwrap(); a.slice_collapse(s![..;-1, .., .., ..2]); let mut buf = Vec::new(); @@ -172,7 +180,8 @@ fn serial_many_dim_serde_msgpack() { #[test] #[cfg(feature = "ron")] -fn serial_many_dim_ron() { +fn serial_many_dim_ron() +{ use ron::de::from_str as ron_deserialize; use ron::ser::to_string as ron_serialize; @@ -208,7 +217,9 @@ fn serial_many_dim_ron() { { // Test a sliced array. - let mut a = ArcArray::linspace(0., 31., 32).into_shape_with_order((2, 2, 2, 4)).unwrap(); + let mut a = ArcArray::linspace(0., 31., 32) + .into_shape_with_order((2, 2, 2, 4)) + .unwrap(); a.slice_collapse(s![..;-1, .., .., ..2]); let a_s = ron_serialize(&a).unwrap(); From 46909232fa3e4a6c0f3f72ecc9865eedb92077f0 Mon Sep 17 00:00:00 2001 From: Lucas Colley Date: Wed, 13 Mar 2024 23:01:06 +0000 Subject: [PATCH 552/651] Add rustfmt commit to ignored revisions for git blame --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000..6d0b5bdb0 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# rustfmt codebase (gh-1375) +d07f5f33800e5240e7edb02bdbc4815ab30ef37e From 646de85becb2959bc96c86403a3a5a4d07c18050 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 6 Dec 2021 20:18:12 +0100 Subject: [PATCH 553/651] Add additional array -> array view conversions It is best to be "complete" with implementations when possible, since it can always be a breaking change to add it later. As shown in the test change in this commit, this is a minor type inference breakage due to `ArrayView::from(&[])` being ambiguous. Empty array views should be relatively rare - and one can use `ArrayView1` or other means to disambiguate. --- src/arraytraits.rs | 32 ++++++++++++++++++++++++++++++-- tests/append.rs | 4 ++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 5c376cb0a..ea0b380ed 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -333,7 +333,21 @@ where Slice: AsRef<[A]> } } -/// Implementation of ArrayView2::from(&S) where S is a slice to a 2D array +/// Implementation of ArrayView2::from(&[[A; N]; M]) +/// +/// **Panics** if the product of non-zero axis lengths overflows `isize` (This can only occur if A +/// is zero-sized because slices cannot contain more than `isize::MAX` number of bytes). +/// **Panics** if N == 0 and the number of rows is greater than isize::MAX. +impl<'a, A, const M: usize, const N: usize> From<&'a [[A; N]; M]> for ArrayView<'a, A, Ix2> +{ + /// Create a two-dimensional read-only array view of the data in `slice` + fn from(xs: &'a [[A; N]; M]) -> Self + { + Self::from(&xs[..]) + } +} + +/// Implementation of ArrayView2::from(&[[A; N]]) /// /// **Panics** if the product of non-zero axis lengths overflows `isize`. (This /// can only occur if A is zero-sized or if `N` is zero, because slices cannot @@ -380,7 +394,21 @@ where Slice: AsMut<[A]> } } -/// Implementation of ArrayViewMut2::from(&S) where S is a slice to a 2D array +/// Implementation of ArrayViewMut2::from(&mut [[A; N]; M]) +/// +/// **Panics** if the product of non-zero axis lengths overflows `isize` (This can only occur if A +/// is zero-sized because slices cannot contain more than `isize::MAX` number of bytes). +/// **Panics** if N == 0 and the number of rows is greater than isize::MAX. +impl<'a, A, const M: usize, const N: usize> From<&'a mut [[A; N]; M]> for ArrayViewMut<'a, A, Ix2> +{ + /// Create a two-dimensional read-write array view of the data in `slice` + fn from(xs: &'a mut [[A; N]; M]) -> Self + { + Self::from(&mut xs[..]) + } +} + +/// Implementation of ArrayViewMut2::from(&mut [[A; N]]) /// /// **Panics** if the product of non-zero axis lengths overflows `isize`. (This /// can only occur if `A` is zero-sized or if `N` is zero, because slices diff --git a/tests/append.rs b/tests/append.rs index 78ea243b2..cf5397de1 100644 --- a/tests/append.rs +++ b/tests/append.rs @@ -383,9 +383,9 @@ fn test_append_zero_size() { let mut a = Array::::zeros((0, 0)); - a.append(Axis(1), ArrayView::from(&[]).into_shape_with_order((0, 1)).unwrap()) + a.append(Axis(1), ArrayView1::from(&[]).into_shape_with_order((0, 1)).unwrap()) .unwrap(); - a.append(Axis(1), ArrayView::from(&[]).into_shape_with_order((0, 1)).unwrap()) + a.append(Axis(1), ArrayView1::from(&[]).into_shape_with_order((0, 1)).unwrap()) .unwrap(); assert_eq!(a.len(), 0); assert_eq!(a.shape(), &[0, 2]); From 330e91520a7fdcab9da354544cf20937001d43ff Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sun, 31 Mar 2024 15:18:42 +0200 Subject: [PATCH 554/651] tests: Use mold linker to speed up ci --- .github/workflows/ci.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 35f72acf4..a8194f8ee 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -58,6 +58,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} + - uses: rui314/setup-mold@v1 - uses: Swatinem/rust-cache@v2 - name: Install openblas run: sudo apt-get install libopenblas-dev gfortran @@ -81,6 +82,7 @@ jobs: with: toolchain: ${{ matrix.rust }} targets: ${{ matrix.target }} + - uses: rui314/setup-mold@v1 - uses: Swatinem/rust-cache@v2 - name: Install cross run: cargo install cross From 6d04ebd3715888d9136df1a985d6ccd605cf33d0 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 1 Apr 2024 16:13:13 +0200 Subject: [PATCH 555/651] Update into_raw_vec Without the offset from start of allocation to the logically first element of the array, correctly reinterpreting the results of .into_raw_vec() as an n-D array is tricky. --- examples/sort-axis.rs | 2 +- src/dimension/mod.rs | 4 +- src/impl_owned_array.rs | 90 +++++++++++++++++++++++++++++++++++----- tests/array-construct.rs | 4 +- tests/array.rs | 6 +-- 5 files changed, 88 insertions(+), 18 deletions(-) diff --git a/examples/sort-axis.rs b/examples/sort-axis.rs index 17ce52e3a..e86a45743 100644 --- a/examples/sort-axis.rs +++ b/examples/sort-axis.rs @@ -157,7 +157,7 @@ where D: Dimension }); debug_assert_eq!(result.len(), moved_elements); // forget the old elements but not the allocation - let mut old_storage = self.into_raw_vec(); + let mut old_storage = self.into_raw_vec().0; old_storage.set_len(0); // transfer ownership of the elements into the result diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index e1563613e..0fedf7823 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -409,8 +409,8 @@ fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize) (start, end, step) } -/// Returns the offset from the lowest-address element to the logically first -/// element. +/// This function computes the offset from the lowest address element to the +/// logically first element. The result is always >= 0. pub fn offset_from_low_addr_ptr_to_logical_ptr(dim: &D, strides: &D) -> usize { let offset = izip!(dim.slice(), strides.slice()).fold(0, |_offset, (&d, &s)| { diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 53be9e48c..4774c1b7c 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -59,14 +59,89 @@ impl Array impl Array where D: Dimension { + /// Returns the offset (in units of `A`) from the start of the allocation + /// to the first element, or `None` if the array is empty. + fn offset_from_alloc_to_logical_ptr(&self) -> Option + { + if self.is_empty() { + return None; + } + if std::mem::size_of::() == 0 { + Some(dimension::offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides)) + } else { + let offset = unsafe { self.as_ptr().offset_from(self.data.as_ptr()) }; + debug_assert!(offset >= 0); + Some(offset as usize) + } + } + /// Return a vector of the elements in the array, in the way they are - /// stored internally. + /// stored internally, and the index in the vector corresponding to the + /// logically first element of the array (or `None` if the array is empty). /// /// If the array is in standard memory layout, the logical element order /// of the array (`.iter()` order) and of the returned vector will be the same. - pub fn into_raw_vec(self) -> Vec + /// + /// ``` + /// use ndarray::{array, Array2, Axis}; + /// + /// let mut arr: Array2 = array![[1., 2.], [3., 4.], [5., 6.]]; + /// arr.slice_axis_inplace(Axis(0), (1..).into()); + /// assert_eq!(arr[[0, 0]], 3.); + /// let copy = arr.clone(); + /// + /// let shape = arr.shape().to_owned(); + /// let strides = arr.strides().to_owned(); + /// let (v, offset) = arr.into_raw_vec(); + /// + /// assert_eq!(v, &[1., 2., 3., 4., 5., 6.]); + /// assert_eq!(offset, Some(2)); + /// assert_eq!(v[offset.unwrap()], 3.); + /// for row in 0..shape[0] { + /// for col in 0..shape[1] { + /// let index = ( + /// offset.unwrap() as isize + /// + row as isize * strides[0] + /// + col as isize * strides[1] + /// ) as usize; + /// assert_eq!(v[index], copy[[row, col]]); + /// } + /// } + /// ``` + /// + /// In the case of zero-sized elements, the offset to the logically first + /// element is somewhat meaningless. For convenience, an offset will be + /// returned such that all indices computed using the offset, shape, and + /// strides will be in-bounds for the `Vec`. Note that this offset won't + /// necessarily be the same as the offset for an array of nonzero-sized + /// elements sliced in the same way. + /// + /// ``` + /// use ndarray::{array, Array2, Axis}; + /// + /// let mut arr: Array2<()> = array![[(), ()], [(), ()], [(), ()]]; + /// arr.slice_axis_inplace(Axis(0), (1..).into()); + /// + /// let shape = arr.shape().to_owned(); + /// let strides = arr.strides().to_owned(); + /// let (v, offset) = arr.into_raw_vec(); + /// + /// assert_eq!(v, &[(), (), (), (), (), ()]); + /// for row in 0..shape[0] { + /// for col in 0..shape[1] { + /// let index = ( + /// offset.unwrap() as isize + /// + row as isize * strides[0] + /// + col as isize * strides[1] + /// ) as usize; + /// assert_eq!(v[index], ()); + /// } + /// } + /// ``` + pub fn into_raw_vec(self) -> (Vec, Option) { - self.data.into_vec() + let offset = self.offset_from_alloc_to_logical_ptr(); + (self.data.into_vec(), offset) } } @@ -575,16 +650,11 @@ where D: Dimension unsafe { // grow backing storage and update head ptr - let data_to_array_offset = if std::mem::size_of::() != 0 { - self.as_ptr().offset_from(self.data.as_ptr()) - } else { - 0 - }; - debug_assert!(data_to_array_offset >= 0); + let offset_from_alloc_to_logical = self.offset_from_alloc_to_logical_ptr().unwrap_or(0); self.ptr = self .data .reserve(len_to_append) - .offset(data_to_array_offset); + .add(offset_from_alloc_to_logical); // clone elements from view to the array now // diff --git a/tests/array-construct.rs b/tests/array-construct.rs index f7339dff6..a5fd00dc0 100644 --- a/tests/array-construct.rs +++ b/tests/array-construct.rs @@ -19,9 +19,9 @@ fn test_from_shape_fn() fn test_dimension_zero() { let a: Array2 = Array2::from(vec![[], [], []]); - assert_eq!(vec![0.; 0], a.into_raw_vec()); + assert_eq!(vec![0.; 0], a.into_raw_vec().0); let a: Array3 = Array3::from(vec![[[]], [[]], [[]]]); - assert_eq!(vec![0.; 0], a.into_raw_vec()); + assert_eq!(vec![0.; 0], a.into_raw_vec().0); } #[test] diff --git a/tests/array.rs b/tests/array.rs index 3f2c38a62..3c60aa616 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1157,7 +1157,7 @@ fn array0_into_scalar() // With this kind of setup, the `Array`'s pointer is not the same as the // underlying `Vec`'s pointer. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); - assert_ne!(a.as_ptr(), a.into_raw_vec().as_ptr()); + assert_ne!(a.as_ptr(), a.into_raw_vec().0.as_ptr()); // `.into_scalar()` should still work correctly. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); assert_eq!(a.into_scalar(), 6); @@ -1173,7 +1173,7 @@ fn array_view0_into_scalar() // With this kind of setup, the `Array`'s pointer is not the same as the // underlying `Vec`'s pointer. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); - assert_ne!(a.as_ptr(), a.into_raw_vec().as_ptr()); + assert_ne!(a.as_ptr(), a.into_raw_vec().0.as_ptr()); // `.into_scalar()` should still work correctly. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); assert_eq!(a.view().into_scalar(), &6); @@ -1189,7 +1189,7 @@ fn array_view_mut0_into_scalar() // With this kind of setup, the `Array`'s pointer is not the same as the // underlying `Vec`'s pointer. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); - assert_ne!(a.as_ptr(), a.into_raw_vec().as_ptr()); + assert_ne!(a.as_ptr(), a.into_raw_vec().0.as_ptr()); // `.into_scalar()` should still work correctly. let mut a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); assert_eq!(a.view_mut().into_scalar(), &6); From edfbe8191ac8eac064eaf9a0717244cd7c44d8f8 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 1 Apr 2024 16:56:42 +0200 Subject: [PATCH 556/651] Add .into_raw_vec_with_offset() and deprecate .into_raw_vec() Provide a smoother transition by deprecating the old method and introducing a new one. Return Option, this makes the empty vector case stand out as a separate case that needs to be handled. --- examples/sort-axis.rs | 2 +- src/dimension/mod.rs | 2 +- src/impl_owned_array.rs | 20 ++++++++++++++++---- tests/array-construct.rs | 4 ++-- tests/array.rs | 22 +++++++++++++++++++--- 5 files changed, 39 insertions(+), 11 deletions(-) diff --git a/examples/sort-axis.rs b/examples/sort-axis.rs index e86a45743..4da3a64d5 100644 --- a/examples/sort-axis.rs +++ b/examples/sort-axis.rs @@ -157,7 +157,7 @@ where D: Dimension }); debug_assert_eq!(result.len(), moved_elements); // forget the old elements but not the allocation - let mut old_storage = self.into_raw_vec().0; + let mut old_storage = self.into_raw_vec_and_offset().0; old_storage.set_len(0); // transfer ownership of the elements into the result diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 0fedf7823..d11dd996b 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -410,7 +410,7 @@ fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize) } /// This function computes the offset from the lowest address element to the -/// logically first element. The result is always >= 0. +/// logically first element. pub fn offset_from_low_addr_ptr_to_logical_ptr(dim: &D, strides: &D) -> usize { let offset = izip!(dim.slice(), strides.slice()).fold(0, |_offset, (&d, &s)| { diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 4774c1b7c..c7bf603b5 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -77,7 +77,7 @@ where D: Dimension /// Return a vector of the elements in the array, in the way they are /// stored internally, and the index in the vector corresponding to the - /// logically first element of the array (or `None` if the array is empty). + /// logically first element of the array (or 0 if the array is empty). /// /// If the array is in standard memory layout, the logical element order /// of the array (`.iter()` order) and of the returned vector will be the same. @@ -92,7 +92,7 @@ where D: Dimension /// /// let shape = arr.shape().to_owned(); /// let strides = arr.strides().to_owned(); - /// let (v, offset) = arr.into_raw_vec(); + /// let (v, offset) = arr.into_raw_vec_and_offset(); /// /// assert_eq!(v, &[1., 2., 3., 4., 5., 6.]); /// assert_eq!(offset, Some(2)); @@ -124,7 +124,7 @@ where D: Dimension /// /// let shape = arr.shape().to_owned(); /// let strides = arr.strides().to_owned(); - /// let (v, offset) = arr.into_raw_vec(); + /// let (v, offset) = arr.into_raw_vec_and_offset(); /// /// assert_eq!(v, &[(), (), (), (), (), ()]); /// for row in 0..shape[0] { @@ -138,11 +138,23 @@ where D: Dimension /// } /// } /// ``` - pub fn into_raw_vec(self) -> (Vec, Option) + pub fn into_raw_vec_and_offset(self) -> (Vec, Option) { let offset = self.offset_from_alloc_to_logical_ptr(); (self.data.into_vec(), offset) } + + /// Return a vector of the elements in the array, in the way they are + /// stored internally. + /// + /// Depending on slicing and strides, the logically first element of the + /// array can be located at an offset. Because of this, prefer to use + /// `.into_raw_vec_and_offset()` instead. + #[deprecated(note = "Use .into_raw_vec_and_offset() instead")] + pub fn into_raw_vec(self) -> Vec + { + self.into_raw_vec_and_offset().0 + } } /// Methods specific to `Array2`. diff --git a/tests/array-construct.rs b/tests/array-construct.rs index a5fd00dc0..b4a81fce1 100644 --- a/tests/array-construct.rs +++ b/tests/array-construct.rs @@ -19,9 +19,9 @@ fn test_from_shape_fn() fn test_dimension_zero() { let a: Array2 = Array2::from(vec![[], [], []]); - assert_eq!(vec![0.; 0], a.into_raw_vec().0); + assert_eq!((vec![0.; 0], None), a.into_raw_vec_and_offset()); let a: Array3 = Array3::from(vec![[[]], [[]], [[]]]); - assert_eq!(vec![0.; 0], a.into_raw_vec().0); + assert_eq!((vec![0.; 0], None), a.into_raw_vec_and_offset()); } #[test] diff --git a/tests/array.rs b/tests/array.rs index 3c60aa616..8f01d0636 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1157,7 +1157,10 @@ fn array0_into_scalar() // With this kind of setup, the `Array`'s pointer is not the same as the // underlying `Vec`'s pointer. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); - assert_ne!(a.as_ptr(), a.into_raw_vec().0.as_ptr()); + let a_ptr = a.as_ptr(); + let (raw_vec, offset) = a.into_raw_vec_and_offset(); + assert_ne!(a_ptr, raw_vec.as_ptr()); + assert_eq!(offset, Some(2)); // `.into_scalar()` should still work correctly. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); assert_eq!(a.into_scalar(), 6); @@ -1173,7 +1176,10 @@ fn array_view0_into_scalar() // With this kind of setup, the `Array`'s pointer is not the same as the // underlying `Vec`'s pointer. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); - assert_ne!(a.as_ptr(), a.into_raw_vec().0.as_ptr()); + let a_ptr = a.as_ptr(); + let (raw_vec, offset) = a.into_raw_vec_and_offset(); + assert_ne!(a_ptr, raw_vec.as_ptr()); + assert_eq!(offset, Some(2)); // `.into_scalar()` should still work correctly. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); assert_eq!(a.view().into_scalar(), &6); @@ -1189,7 +1195,7 @@ fn array_view_mut0_into_scalar() // With this kind of setup, the `Array`'s pointer is not the same as the // underlying `Vec`'s pointer. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); - assert_ne!(a.as_ptr(), a.into_raw_vec().0.as_ptr()); + assert_ne!(a.as_ptr(), a.into_raw_vec_and_offset().0.as_ptr()); // `.into_scalar()` should still work correctly. let mut a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); assert_eq!(a.view_mut().into_scalar(), &6); @@ -1199,6 +1205,16 @@ fn array_view_mut0_into_scalar() assert_eq!(a.view_mut().into_scalar(), &()); } +#[test] +fn array1_into_raw_vec() +{ + let data = vec![4, 5, 6, 7]; + let array = Array::from(data.clone()); + let (raw_vec, offset) = array.into_raw_vec_and_offset(); + assert_eq!(data, raw_vec); + assert_eq!(offset, Some(0)); +} + #[test] fn owned_array1() { From 83dba02a7279b61523e0ab9368b7cffca9727cf4 Mon Sep 17 00:00:00 2001 From: Josiah Bills Date: Wed, 1 Sep 2021 23:58:40 -0400 Subject: [PATCH 557/651] Bumped approx's version number. --- Cargo.toml | 5 ++--- src/array_approx.rs | 3 --- xtest-blas/Cargo.toml | 2 +- xtest-numeric/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ae9e33f06..fada9bcee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,8 +34,7 @@ num-complex = { version = "0.4", default-features = false } # Use via the `rayon` crate feature! rayon_ = { version = "1.0.3", optional = true, package = "rayon" } -approx = { version = "0.4", optional = true , default-features = false } -approx-0_5 = { package = "approx", version = "0.5", optional = true , default-features = false } +approx = { version = "0.5", optional = true , default-features = false } # Use via the `blas` crate feature! cblas-sys = { version = "0.1.4", optional = true, default-features = false } @@ -49,7 +48,7 @@ rawpointer = { version = "0.2" } [dev-dependencies] defmac = "0.2" quickcheck = { version = "1.0", default-features = false } -approx = "0.4" +approx = "0.5" itertools = { version = "0.10.0", default-features = false, features = ["use_std"] } [features] diff --git a/src/array_approx.rs b/src/array_approx.rs index 286a1146c..493864c7e 100644 --- a/src/array_approx.rs +++ b/src/array_approx.rs @@ -193,6 +193,3 @@ macro_rules! impl_approx_traits { #[cfg(feature = "approx")] impl_approx_traits!(approx, "**Requires crate feature `\"approx\"`.**"); - -#[cfg(feature = "approx-0_5")] -impl_approx_traits!(approx_0_5, "**Requires crate feature `\"approx-0_5\"`.**"); diff --git a/xtest-blas/Cargo.toml b/xtest-blas/Cargo.toml index 7ad33953c..3724caf14 100644 --- a/xtest-blas/Cargo.toml +++ b/xtest-blas/Cargo.toml @@ -8,7 +8,7 @@ publish = false test = false [dev-dependencies] -approx = "0.4" +approx = "0.5" defmac = "0.2" num-traits = "0.2" num-complex = { version = "0.4", default-features = false } diff --git a/xtest-numeric/Cargo.toml b/xtest-numeric/Cargo.toml index 8558d39ad..8cbceb247 100644 --- a/xtest-numeric/Cargo.toml +++ b/xtest-numeric/Cargo.toml @@ -6,7 +6,7 @@ publish = false edition = "2018" [dependencies] -approx = "0.4" +approx = "0.5" ndarray = { path = "..", features = ["approx"] } ndarray-rand = { path = "../ndarray-rand" } rand_distr = "0.4" From ea62b5dff78abf120e68a89db42845e06f1e7c97 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 6 Apr 2024 15:28:14 +0200 Subject: [PATCH 558/651] API: Remove approx-0_5 feature (replaced by approx feature) --- Cargo.toml | 2 +- README.rst | 4 ---- src/doc/crate_feature_flags.rs | 5 +---- src/lib.rs | 5 ++--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fada9bcee..945dc6aa8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,7 +65,7 @@ serde-1 = ["serde"] test = [] # This feature is used for docs -docs = ["approx", "approx-0_5", "serde", "rayon"] +docs = ["approx", "serde", "rayon"] std = ["num-traits/std", "matrixmultiply/std"] rayon = ["rayon_", "std"] diff --git a/README.rst b/README.rst index 9e3d37247..67c1c025e 100644 --- a/README.rst +++ b/README.rst @@ -85,10 +85,6 @@ your `Cargo.toml`. - ``approx`` - - Implementations of traits from version 0.4 of the [`approx`] crate. - -- ``approx-0_5`` - - Implementations of traits from version 0.5 of the [`approx`] crate. - ``blas`` diff --git a/src/doc/crate_feature_flags.rs b/src/doc/crate_feature_flags.rs index b396755c7..c0fc4c0f5 100644 --- a/src/doc/crate_feature_flags.rs +++ b/src/doc/crate_feature_flags.rs @@ -19,10 +19,7 @@ //! - Implies std //! //! ## `approx` -//! - Enables implementations of traits from version 0.4 of the [`approx`] crate. -//! -//! ## `approx-0_5` -//! - Enables implementations of traits from version 0.5 of the [`approx`] crate. +//! - Enables implementations of traits of the [`approx`] crate. //! //! ## `blas` //! - Enable transparent BLAS support for matrix multiplication. diff --git a/src/lib.rs b/src/lib.rs index 37af0adfe..bfeb6835b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,8 +81,7 @@ //! - `std`: Rust standard library-using functionality (enabled by default) //! - `serde`: serialization support for serde 1.x //! - `rayon`: Parallel iterators, parallelized methods, the [`parallel`] module and [`par_azip!`]. -//! - `approx` Implementations of traits from version 0.4 of the [`approx`] crate. -//! - `approx-0_5`: Implementations of traits from version 0.5 of the [`approx`] crate. +//! - `approx` Implementations of traits from the [`approx`] crate. //! - `blas`: transparent BLAS support for matrix multiplication, needs configuration. //! - `matrixmultiply-threading`: Use threading from `matrixmultiply`. //! @@ -1600,7 +1599,7 @@ pub mod linalg; mod impl_ops; pub use crate::impl_ops::ScalarOperand; -#[cfg(any(feature = "approx", feature = "approx-0_5"))] +#[cfg(feature = "approx")] mod array_approx; // Array view methods From a85c5b94571bc089ddb9d2d40e9ed31228a38ef6 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 1 Jul 2023 11:24:51 +0200 Subject: [PATCH 559/651] Use inline on spit_at and smaller methods --- src/impl_internal_constructors.rs | 2 ++ src/impl_raw_views.rs | 6 ++++++ src/impl_views/constructors.rs | 2 ++ src/impl_views/conversions.rs | 1 + src/impl_views/splitting.rs | 2 ++ src/shape_builder.rs | 1 + 6 files changed, 14 insertions(+) diff --git a/src/impl_internal_constructors.rs b/src/impl_internal_constructors.rs index ebb2e26e0..adb4cbd35 100644 --- a/src/impl_internal_constructors.rs +++ b/src/impl_internal_constructors.rs @@ -22,6 +22,7 @@ where S: RawData /// The caller must ensure that the data storage and pointer is valid. /// /// See ArrayView::from_shape_ptr for general pointer validity documentation. + #[inline] pub(crate) unsafe fn from_data_ptr(data: S, ptr: NonNull) -> Self { let array = ArrayBase { @@ -50,6 +51,7 @@ where /// /// The caller needs to ensure that the new strides and dimensions are correct /// for the array data. + #[inline] pub(crate) unsafe fn with_strides_dim(self, strides: E, dim: E) -> ArrayBase where E: Dimension { diff --git a/src/impl_raw_views.rs b/src/impl_raw_views.rs index aeee75cb2..fca5e7de7 100644 --- a/src/impl_raw_views.rs +++ b/src/impl_raw_views.rs @@ -21,6 +21,7 @@ where D: Dimension RawArrayView::from_data_ptr(RawViewRepr::new(), ptr).with_strides_dim(strides, dim) } + #[inline] unsafe fn new_(ptr: *const A, dim: D, strides: D) -> Self { Self::new(nonnull_debug_checked_from_ptr(ptr as *mut A), dim, strides) @@ -66,6 +67,7 @@ where D: Dimension /// but it's not a complete check. /// /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset + #[inline] pub unsafe fn from_shape_ptr(shape: Sh, ptr: *const A) -> Self where Sh: Into> { @@ -107,6 +109,7 @@ where D: Dimension /// /// **Panics** if `axis` or `index` is out of bounds. #[track_caller] + #[inline] pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { assert!(index <= self.len_of(axis)); @@ -234,6 +237,7 @@ where D: Dimension RawArrayViewMut::from_data_ptr(RawViewRepr::new(), ptr).with_strides_dim(strides, dim) } + #[inline] unsafe fn new_(ptr: *mut A, dim: D, strides: D) -> Self { Self::new(nonnull_debug_checked_from_ptr(ptr), dim, strides) @@ -279,6 +283,7 @@ where D: Dimension /// but it's not a complete check. /// /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset + #[inline] pub unsafe fn from_shape_ptr(shape: Sh, ptr: *mut A) -> Self where Sh: Into> { @@ -345,6 +350,7 @@ where D: Dimension /// /// **Panics** if `axis` or `index` is out of bounds. #[track_caller] + #[inline] pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { let (left, right) = self.into_raw_view().split_at(axis, index); diff --git a/src/impl_views/constructors.rs b/src/impl_views/constructors.rs index 33c7b15be..dcbec991b 100644 --- a/src/impl_views/constructors.rs +++ b/src/impl_views/constructors.rs @@ -110,6 +110,7 @@ where D: Dimension /// but it's not a complete check. /// /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset + #[inline] pub unsafe fn from_shape_ptr(shape: Sh, ptr: *const A) -> Self where Sh: Into> { @@ -212,6 +213,7 @@ where D: Dimension /// but it's not a complete check. /// /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset + #[inline] pub unsafe fn from_shape_ptr(shape: Sh, ptr: *mut A) -> Self where Sh: Into> { diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs index f545ebdd0..18f263691 100644 --- a/src/impl_views/conversions.rs +++ b/src/impl_views/conversions.rs @@ -62,6 +62,7 @@ where D: Dimension } /// Converts to a raw array view. + #[inline] pub(crate) fn into_raw_view(self) -> RawArrayView { unsafe { RawArrayView::new(self.ptr, self.dim, self.strides) } diff --git a/src/impl_views/splitting.rs b/src/impl_views/splitting.rs index e26900984..6d6ea275b 100644 --- a/src/impl_views/splitting.rs +++ b/src/impl_views/splitting.rs @@ -88,6 +88,7 @@ where D: Dimension /// along Axis(1) /// ``` #[track_caller] + #[inline] pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { unsafe { @@ -137,6 +138,7 @@ where D: Dimension /// /// **Panics** if `axis` or `index` is out of bounds. #[track_caller] + #[inline] pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { unsafe { diff --git a/src/shape_builder.rs b/src/shape_builder.rs index 8b25f71e7..cd790a25f 100644 --- a/src/shape_builder.rs +++ b/src/shape_builder.rs @@ -82,6 +82,7 @@ impl Strides } } + #[inline] pub(crate) fn is_custom(&self) -> bool { matches!(*self, Strides::Custom(_)) From 6933dd86281d66b3852258181858eba2b6c5c6ee Mon Sep 17 00:00:00 2001 From: ssande7 <1731652+ssande7@users.noreply.github.com> Date: Thu, 9 Mar 2023 22:54:29 +1000 Subject: [PATCH 560/651] Add reserve method for owned arrays --- src/impl_owned_array.rs | 48 +++++++++++++++++++++++++++++++------ tests/reserve.rs | 52 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 tests/reserve.rs diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index c7bf603b5..3b393a6d7 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -660,14 +660,10 @@ where D: Dimension self.strides.clone() }; - unsafe { - // grow backing storage and update head ptr - let offset_from_alloc_to_logical = self.offset_from_alloc_to_logical_ptr().unwrap_or(0); - self.ptr = self - .data - .reserve(len_to_append) - .add(offset_from_alloc_to_logical); + // grow backing storage and update head ptr + self.reserve(axis, array_dim[axis.index()]); + unsafe { // clone elements from view to the array now // // To be robust for panics and drop the right elements, we want @@ -751,6 +747,44 @@ where D: Dimension Ok(()) } + + /// Reserve capacity to grow array along `axis` by at least `additional` elements. + /// + /// Existing elements of `array` are untouched and the backing storage is grown by + /// calling the underlying `reserve` method of the `OwnedRepr`. + /// + /// This is useful when pushing or appending repeatedly to an array to avoid multiple + /// allocations. + /// + /// ```rust + /// use ndarray::{Array3, Axis}; + /// let mut a = Array3::::zeros((0,2,4)); + /// a.reserve(Axis(0), 1000); + /// assert!(a.into_raw_vec().capacity() >= 2*4*1000); + pub fn reserve(&mut self, axis: Axis, additional: usize) + where D: RemoveAxis + { + if additional == 0 { + return; + } + let self_dim = self.raw_dim(); + let remaining_shape = self_dim.remove_axis(axis); + let len_to_append = remaining_shape.size() * additional; + + unsafe { + // grow backing storage and update head ptr + let data_to_array_offset = if std::mem::size_of::() != 0 { + self.as_ptr().offset_from(self.data.as_ptr()) + } else { + 0 + }; + debug_assert!(data_to_array_offset >= 0); + self.ptr = self + .data + .reserve(len_to_append) + .offset(data_to_array_offset); + } + } } /// This drops all "unreachable" elements in `self_` given the data pointer and data length. diff --git a/tests/reserve.rs b/tests/reserve.rs new file mode 100644 index 000000000..1b701d1f6 --- /dev/null +++ b/tests/reserve.rs @@ -0,0 +1,52 @@ +use ndarray::prelude::*; + +#[test] +fn reserve_1d() +{ + let mut a = Array1::::zeros((4,)); + a.reserve(Axis(0), 1000); + assert_eq!(a.shape(), &[4]); + assert!(a.into_raw_vec().capacity() >= 1004); +} + +#[test] +fn reserve_3d() +{ + let mut a = Array3::::zeros((0, 4, 8)); + a.reserve(Axis(0), 10); + assert_eq!(a.shape(), &[0, 4, 8]); + assert!(a.into_raw_vec().capacity() >= 4 * 8 * 10); +} + +#[test] +fn reserve_empty_3d() +{ + let mut a = Array3::::zeros((0, 0, 0)); + a.reserve(Axis(0), 10); +} + +#[test] +fn reserve_3d_axis1() +{ + let mut a = Array3::::zeros((2, 4, 8)); + a.reserve(Axis(1), 10); + assert!(a.into_raw_vec().capacity() >= 2 * 8 * 10); +} + +#[test] +fn reserve_3d_repeat() +{ + let mut a = Array3::::zeros((2, 4, 8)); + a.reserve(Axis(1), 10); + a.reserve(Axis(2), 30); + assert!(a.into_raw_vec().capacity() >= 2 * 4 * 30); +} + +#[test] +fn reserve_2d_with_data() +{ + let mut a = array![[1, 2], [3, 4], [5, 6]]; + a.reserve(Axis(1), 100); + assert_eq!(a, array![[1, 2], [3, 4], [5, 6]]); + assert!(a.into_raw_vec().capacity() >= 3 * 100); +} From aa077bf56f06bdedf1bace7aa0fe173afc043c0c Mon Sep 17 00:00:00 2001 From: ssande7 <1731652+ssande7@users.noreply.github.com> Date: Sat, 11 Mar 2023 12:06:26 +1000 Subject: [PATCH 561/651] Add benchmark for reserve() --- benches/reserve.rs | 31 +++++++++++++++++++++++++++++++ src/impl_owned_array.rs | 3 --- 2 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 benches/reserve.rs diff --git a/benches/reserve.rs b/benches/reserve.rs new file mode 100644 index 000000000..ea9d52e96 --- /dev/null +++ b/benches/reserve.rs @@ -0,0 +1,31 @@ +#![feature(test)] + +extern crate test; +use test::Bencher; + +use ndarray::prelude::*; + +#[bench] +fn push_reserve(bench: &mut Bencher) +{ + let ones: Array = array![1f32]; + bench.iter(|| { + let mut a: Array = array![]; + a.reserve(Axis(0), 100); + for _ in 0..100 { + a.append(Axis(0), ones.view()).unwrap(); + } + }); +} + +#[bench] +fn push_no_reserve(bench: &mut Bencher) +{ + let ones: Array = array![1f32]; + bench.iter(|| { + let mut a: Array = array![]; + for _ in 0..100 { + a.append(Axis(0), ones.view()).unwrap(); + } + }); +} diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 3b393a6d7..aeb5c7697 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -764,9 +764,6 @@ where D: Dimension pub fn reserve(&mut self, axis: Axis, additional: usize) where D: RemoveAxis { - if additional == 0 { - return; - } let self_dim = self.raw_dim(); let remaining_shape = self_dim.remove_axis(axis); let len_to_append = remaining_shape.size() * additional; From 63062d3bc1a5433e91e2049df423daec33643fa6 Mon Sep 17 00:00:00 2001 From: ssande7 <1731652+ssande7@users.noreply.github.com> Date: Sat, 11 Mar 2023 18:29:51 +1000 Subject: [PATCH 562/651] Add convenience functions for reserving space for rows/columns in Array2 --- src/impl_owned_array.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index aeb5c7697..70f2f229f 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -251,6 +251,44 @@ impl Array { self.append(Axis(1), column.insert_axis(Axis(1))) } + + /// Reserve capacity to grow array by at least `additional` rows. + /// + /// Existing elements of `array` are untouched and the backing storage is grown by + /// calling the underlying `reserve` method of the `OwnedRepr`. + /// + /// This is useful when pushing or appending repeatedly to an array to avoid multiple + /// allocations. + /// + /// ```rust + /// use ndarray::Array2; + /// let mut a = Array2::::zeros((2,4)); + /// a.reserve_rows(1000); + /// assert!(a.into_raw_vec().capacity() >= 4*1002); + /// ``` + pub fn reserve_rows(&mut self, additional: usize) + { + self.reserve(Axis(0), additional); + } + + /// Reserve capacity to grow array by at least `additional` columns. + /// + /// Existing elements of `array` are untouched and the backing storage is grown by + /// calling the underlying `reserve` method of the `OwnedRepr`. + /// + /// This is useful when pushing or appending repeatedly to an array to avoid multiple + /// allocations. + /// + /// ```rust + /// use ndarray::Array2; + /// let mut a = Array2::::zeros((2,4)); + /// a.reserve_columns(1000); + /// assert!(a.into_raw_vec().capacity() >= 2*1002); + /// ``` + pub fn reserve_columns(&mut self, additional: usize) + { + self.reserve(Axis(1), additional); + } } impl Array @@ -761,6 +799,7 @@ where D: Dimension /// let mut a = Array3::::zeros((0,2,4)); /// a.reserve(Axis(0), 1000); /// assert!(a.into_raw_vec().capacity() >= 2*4*1000); + /// ``` pub fn reserve(&mut self, axis: Axis, additional: usize) where D: RemoveAxis { From a16707a4ab125a7e864d4a02a535e0cbeb2774e9 Mon Sep 17 00:00:00 2001 From: ssande7 <1731652+ssande7@users.noreply.github.com> Date: Sun, 7 May 2023 20:21:43 +1000 Subject: [PATCH 563/651] Add bounds checking and error documentation --- benches/reserve.rs | 2 +- src/impl_owned_array.rs | 47 +++++++++++++++++++++++++++++++++-------- tests/reserve.rs | 14 ++++++------ 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/benches/reserve.rs b/benches/reserve.rs index ea9d52e96..14ebf9f1a 100644 --- a/benches/reserve.rs +++ b/benches/reserve.rs @@ -11,7 +11,7 @@ fn push_reserve(bench: &mut Bencher) let ones: Array = array![1f32]; bench.iter(|| { let mut a: Array = array![]; - a.reserve(Axis(0), 100); + a.reserve(Axis(0), 100).unwrap(); for _ in 0..100 { a.append(Axis(0), ones.view()).unwrap(); } diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 70f2f229f..bfaa90531 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -260,15 +260,19 @@ impl Array /// This is useful when pushing or appending repeatedly to an array to avoid multiple /// allocations. /// + /// ***Errors*** with a shape error if the resultant capacity is larger than the addressable + /// bounds; that is, the product of non-zero axis lengths once `axis` has been extended by + /// `additional` exceeds `isize::MAX`. + /// /// ```rust /// use ndarray::Array2; /// let mut a = Array2::::zeros((2,4)); - /// a.reserve_rows(1000); + /// a.reserve_rows(1000).unwrap(); /// assert!(a.into_raw_vec().capacity() >= 4*1002); /// ``` - pub fn reserve_rows(&mut self, additional: usize) + pub fn reserve_rows(&mut self, additional: usize) -> Result<(), ShapeError> { - self.reserve(Axis(0), additional); + self.reserve(Axis(0), additional) } /// Reserve capacity to grow array by at least `additional` columns. @@ -279,15 +283,19 @@ impl Array /// This is useful when pushing or appending repeatedly to an array to avoid multiple /// allocations. /// + /// ***Errors*** with a shape error if the resultant capacity is larger than the addressable + /// bounds; that is, the product of non-zero axis lengths once `axis` has been extended by + /// `additional` exceeds `isize::MAX`. + /// /// ```rust /// use ndarray::Array2; /// let mut a = Array2::::zeros((2,4)); - /// a.reserve_columns(1000); + /// a.reserve_columns(1000).unwrap(); /// assert!(a.into_raw_vec().capacity() >= 2*1002); /// ``` - pub fn reserve_columns(&mut self, additional: usize) + pub fn reserve_columns(&mut self, additional: usize) -> Result<(), ShapeError> { - self.reserve(Axis(1), additional); + self.reserve(Axis(1), additional) } } @@ -699,7 +707,7 @@ where D: Dimension }; // grow backing storage and update head ptr - self.reserve(axis, array_dim[axis.index()]); + self.reserve(axis, array_dim[axis.index()])?; unsafe { // clone elements from view to the array now @@ -788,25 +796,42 @@ where D: Dimension /// Reserve capacity to grow array along `axis` by at least `additional` elements. /// + /// The axis should be in the range `Axis(` 0 .. *n* `)` where *n* is the + /// number of dimensions (axes) of the array. + /// /// Existing elements of `array` are untouched and the backing storage is grown by /// calling the underlying `reserve` method of the `OwnedRepr`. /// /// This is useful when pushing or appending repeatedly to an array to avoid multiple /// allocations. /// + /// ***Panics*** if the axis is out of bounds. + /// + /// ***Errors*** with a shape error if the resultant capacity is larger than the addressable + /// bounds; that is, the product of non-zero axis lengths once `axis` has been extended by + /// `additional` exceeds `isize::MAX`. + /// /// ```rust /// use ndarray::{Array3, Axis}; /// let mut a = Array3::::zeros((0,2,4)); - /// a.reserve(Axis(0), 1000); + /// a.reserve(Axis(0), 1000).unwrap(); /// assert!(a.into_raw_vec().capacity() >= 2*4*1000); /// ``` - pub fn reserve(&mut self, axis: Axis, additional: usize) + /// + pub fn reserve(&mut self, axis: Axis, additional: usize) -> Result<(), ShapeError> where D: RemoveAxis { + debug_assert!(axis.index() < self.ndim()); let self_dim = self.raw_dim(); let remaining_shape = self_dim.remove_axis(axis); let len_to_append = remaining_shape.size() * additional; + // Make sure new capacity is still in bounds + let mut res_dim = self_dim; + res_dim[axis.index()] += additional; + let new_len = dimension::size_of_shape_checked(&res_dim)?; + debug_assert_eq!(self.len() + len_to_append, new_len); + unsafe { // grow backing storage and update head ptr let data_to_array_offset = if std::mem::size_of::() != 0 { @@ -820,6 +845,10 @@ where D: Dimension .reserve(len_to_append) .offset(data_to_array_offset); } + + debug_assert!(self.pointer_is_inbounds()); + + Ok(()) } } diff --git a/tests/reserve.rs b/tests/reserve.rs index 1b701d1f6..cdf74dbf4 100644 --- a/tests/reserve.rs +++ b/tests/reserve.rs @@ -4,7 +4,7 @@ use ndarray::prelude::*; fn reserve_1d() { let mut a = Array1::::zeros((4,)); - a.reserve(Axis(0), 1000); + a.reserve(Axis(0), 1000).unwrap(); assert_eq!(a.shape(), &[4]); assert!(a.into_raw_vec().capacity() >= 1004); } @@ -13,7 +13,7 @@ fn reserve_1d() fn reserve_3d() { let mut a = Array3::::zeros((0, 4, 8)); - a.reserve(Axis(0), 10); + a.reserve(Axis(0), 10).unwrap(); assert_eq!(a.shape(), &[0, 4, 8]); assert!(a.into_raw_vec().capacity() >= 4 * 8 * 10); } @@ -22,14 +22,14 @@ fn reserve_3d() fn reserve_empty_3d() { let mut a = Array3::::zeros((0, 0, 0)); - a.reserve(Axis(0), 10); + a.reserve(Axis(0), 10).unwrap(); } #[test] fn reserve_3d_axis1() { let mut a = Array3::::zeros((2, 4, 8)); - a.reserve(Axis(1), 10); + a.reserve(Axis(1), 10).unwrap(); assert!(a.into_raw_vec().capacity() >= 2 * 8 * 10); } @@ -37,8 +37,8 @@ fn reserve_3d_axis1() fn reserve_3d_repeat() { let mut a = Array3::::zeros((2, 4, 8)); - a.reserve(Axis(1), 10); - a.reserve(Axis(2), 30); + a.reserve(Axis(1), 10).unwrap(); + a.reserve(Axis(2), 30).unwrap(); assert!(a.into_raw_vec().capacity() >= 2 * 4 * 30); } @@ -46,7 +46,7 @@ fn reserve_3d_repeat() fn reserve_2d_with_data() { let mut a = array![[1, 2], [3, 4], [5, 6]]; - a.reserve(Axis(1), 100); + a.reserve(Axis(1), 100).unwrap(); assert_eq!(a, array![[1, 2], [3, 4], [5, 6]]); assert!(a.into_raw_vec().capacity() >= 3 * 100); } From beb14398d29ba9c2608bd5fc3565e53e195f9350 Mon Sep 17 00:00:00 2001 From: ssande7 <1731652+ssande7@users.noreply.github.com> Date: Sun, 7 May 2023 20:34:43 +1000 Subject: [PATCH 564/651] Check for usize overflow of new capacity --- src/impl_owned_array.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index bfaa90531..faef1d9e8 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -260,6 +260,8 @@ impl Array /// This is useful when pushing or appending repeatedly to an array to avoid multiple /// allocations. /// + /// ***Panics*** if the new capacity would exceed `usize::MAX`. + /// /// ***Errors*** with a shape error if the resultant capacity is larger than the addressable /// bounds; that is, the product of non-zero axis lengths once `axis` has been extended by /// `additional` exceeds `isize::MAX`. @@ -283,6 +285,8 @@ impl Array /// This is useful when pushing or appending repeatedly to an array to avoid multiple /// allocations. /// + /// ***Panics*** if the new capacity would exceed `usize::MAX`. + /// /// ***Errors*** with a shape error if the resultant capacity is larger than the addressable /// bounds; that is, the product of non-zero axis lengths once `axis` has been extended by /// `additional` exceeds `isize::MAX`. @@ -805,7 +809,7 @@ where D: Dimension /// This is useful when pushing or appending repeatedly to an array to avoid multiple /// allocations. /// - /// ***Panics*** if the axis is out of bounds. + /// ***Panics*** if the axis is out of bounds or if the new capacity would exceed `usize::MAX`. /// /// ***Errors*** with a shape error if the resultant capacity is larger than the addressable /// bounds; that is, the product of non-zero axis lengths once `axis` has been extended by @@ -830,7 +834,9 @@ where D: Dimension let mut res_dim = self_dim; res_dim[axis.index()] += additional; let new_len = dimension::size_of_shape_checked(&res_dim)?; - debug_assert_eq!(self.len() + len_to_append, new_len); + + // Check whether len_to_append would cause an overflow + debug_assert_eq!(self.len().checked_add(len_to_append).unwrap(), new_len); unsafe { // grow backing storage and update head ptr From 4b022eba7c3f7243233d1c968bbf2b40de4426ab Mon Sep 17 00:00:00 2001 From: ssande7 <1731652+ssande7@users.noreply.github.com> Date: Sun, 7 May 2023 20:47:39 +1000 Subject: [PATCH 565/651] Make sure added capacity doesn't overflow usize::MAX --- src/impl_owned_array.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index faef1d9e8..6f9acb7a2 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -828,6 +828,9 @@ where D: Dimension debug_assert!(axis.index() < self.ndim()); let self_dim = self.raw_dim(); let remaining_shape = self_dim.remove_axis(axis); + + // Make sure added capacity doesn't overflow + debug_assert!(remaining_shape.size().checked_mul(additional).is_some()); let len_to_append = remaining_shape.size() * additional; // Make sure new capacity is still in bounds From 8e030d1075892236e82ba9d040fbd55f56338a7f Mon Sep 17 00:00:00 2001 From: ssande7 <1731652+ssande7@users.noreply.github.com> Date: Sun, 17 Mar 2024 12:16:51 +1000 Subject: [PATCH 566/651] Error on overflow when reserving --- src/impl_owned_array.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 6f9acb7a2..393c2e075 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -260,8 +260,6 @@ impl Array /// This is useful when pushing or appending repeatedly to an array to avoid multiple /// allocations. /// - /// ***Panics*** if the new capacity would exceed `usize::MAX`. - /// /// ***Errors*** with a shape error if the resultant capacity is larger than the addressable /// bounds; that is, the product of non-zero axis lengths once `axis` has been extended by /// `additional` exceeds `isize::MAX`. @@ -285,8 +283,6 @@ impl Array /// This is useful when pushing or appending repeatedly to an array to avoid multiple /// allocations. /// - /// ***Panics*** if the new capacity would exceed `usize::MAX`. - /// /// ***Errors*** with a shape error if the resultant capacity is larger than the addressable /// bounds; that is, the product of non-zero axis lengths once `axis` has been extended by /// `additional` exceeds `isize::MAX`. @@ -809,7 +805,7 @@ where D: Dimension /// This is useful when pushing or appending repeatedly to an array to avoid multiple /// allocations. /// - /// ***Panics*** if the axis is out of bounds or if the new capacity would exceed `usize::MAX`. + /// ***Panics*** if the axis is out of bounds. /// /// ***Errors*** with a shape error if the resultant capacity is larger than the addressable /// bounds; that is, the product of non-zero axis lengths once `axis` has been extended by @@ -829,9 +825,11 @@ where D: Dimension let self_dim = self.raw_dim(); let remaining_shape = self_dim.remove_axis(axis); - // Make sure added capacity doesn't overflow - debug_assert!(remaining_shape.size().checked_mul(additional).is_some()); - let len_to_append = remaining_shape.size() * additional; + // Make sure added capacity doesn't overflow usize::MAX + let len_to_append = remaining_shape + .size() + .checked_mul(additional) + .ok_or(ShapeError::from_kind(ErrorKind::Overflow))?; // Make sure new capacity is still in bounds let mut res_dim = self_dim; From 25d2d04a3e7572251b822fdfa90979a4a7d6e2b6 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 6 Apr 2024 16:35:21 +0200 Subject: [PATCH 567/651] reserve: Add test with inverted axis Testing when the allocation to first element offset is nonzero. --- tests/reserve.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/reserve.rs b/tests/reserve.rs index cdf74dbf4..c220c1715 100644 --- a/tests/reserve.rs +++ b/tests/reserve.rs @@ -50,3 +50,16 @@ fn reserve_2d_with_data() assert_eq!(a, array![[1, 2], [3, 4], [5, 6]]); assert!(a.into_raw_vec().capacity() >= 3 * 100); } + +#[test] +fn reserve_2d_inverted_with_data() +{ + let mut a = array![[1, 2], [3, 4], [5, 6]]; + a.invert_axis(Axis(1)); + assert_eq!(a, array![[2, 1], [4, 3], [6, 5]]); + a.reserve(Axis(1), 100).unwrap(); + assert_eq!(a, array![[2, 1], [4, 3], [6, 5]]); + let (raw_vec, offset) = a.into_raw_vec_and_offset(); + assert!(raw_vec.capacity() >= 3 * 100); + assert_eq!(offset, Some(1)); +} From 7cc7141e23a9f19708ead4395f604d4068b21f89 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 6 Apr 2024 16:38:25 +0200 Subject: [PATCH 568/651] reserve: Update tests to use into_raw_vec_and_offset --- tests/reserve.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/reserve.rs b/tests/reserve.rs index c220c1715..108620014 100644 --- a/tests/reserve.rs +++ b/tests/reserve.rs @@ -1,12 +1,17 @@ use ndarray::prelude::*; +fn into_raw_vec_capacity(a: Array) -> usize +{ + a.into_raw_vec_and_offset().0.capacity() +} + #[test] fn reserve_1d() { let mut a = Array1::::zeros((4,)); a.reserve(Axis(0), 1000).unwrap(); assert_eq!(a.shape(), &[4]); - assert!(a.into_raw_vec().capacity() >= 1004); + assert!(into_raw_vec_capacity(a) >= 1004); } #[test] @@ -15,7 +20,7 @@ fn reserve_3d() let mut a = Array3::::zeros((0, 4, 8)); a.reserve(Axis(0), 10).unwrap(); assert_eq!(a.shape(), &[0, 4, 8]); - assert!(a.into_raw_vec().capacity() >= 4 * 8 * 10); + assert!(into_raw_vec_capacity(a) >= 4 * 8 * 10); } #[test] @@ -30,7 +35,7 @@ fn reserve_3d_axis1() { let mut a = Array3::::zeros((2, 4, 8)); a.reserve(Axis(1), 10).unwrap(); - assert!(a.into_raw_vec().capacity() >= 2 * 8 * 10); + assert!(into_raw_vec_capacity(a) >= 2 * 8 * 10); } #[test] @@ -39,7 +44,7 @@ fn reserve_3d_repeat() let mut a = Array3::::zeros((2, 4, 8)); a.reserve(Axis(1), 10).unwrap(); a.reserve(Axis(2), 30).unwrap(); - assert!(a.into_raw_vec().capacity() >= 2 * 4 * 30); + assert!(into_raw_vec_capacity(a) >= 2 * 4 * 30); } #[test] @@ -48,7 +53,7 @@ fn reserve_2d_with_data() let mut a = array![[1, 2], [3, 4], [5, 6]]; a.reserve(Axis(1), 100).unwrap(); assert_eq!(a, array![[1, 2], [3, 4], [5, 6]]); - assert!(a.into_raw_vec().capacity() >= 3 * 100); + assert!(into_raw_vec_capacity(a) >= 3 * 100); } #[test] From d0ee87e4aebbb53b1e37fbb52a43949ea7bb8dc3 Mon Sep 17 00:00:00 2001 From: Juho N Date: Sun, 14 Apr 2024 20:54:04 +0300 Subject: [PATCH 569/651] Fixed broke continuous integration badge The previous "continuous integration" badge showed failing no matter the current result because of historical tests that failed, see: https://github.com/actions/starter-workflows/issues/1525 Now it shows the up to date test result --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 67c1c025e..4e33d12c4 100644 --- a/README.rst +++ b/README.rst @@ -11,7 +11,7 @@ __ https://docs.rs/ndarray/ |build_status|_ |crates|_ |matrix-chat|_ |irc|_ -.. |build_status| image:: https://github.com/rust-ndarray/ndarray/workflows/Continuous%20integration/badge.svg?branch=master +.. |build_status| image:: https://github.com/rust-ndarray/ndarray/actions/workflows/ci.yaml/badge.svg :alt: CI build status .. _build_status: https://github.com/rust-ndarray/ndarray/actions From cddb2eeadc122f956bab6046b4f4221774e9e3ff Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Sun, 19 May 2024 15:57:33 +0200 Subject: [PATCH 570/651] Avoid legacy numeric constants. --- src/arrayformat.rs | 6 +++--- src/arraytraits.rs | 2 +- src/dimension/mod.rs | 3 +-- src/linalg/impl_linalg.rs | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/arrayformat.rs b/src/arrayformat.rs index 202805604..1a3b714c3 100644 --- a/src/arrayformat.rs +++ b/src/arrayformat.rs @@ -51,9 +51,9 @@ impl FormatOptions fn set_no_limit(mut self, no_limit: bool) -> Self { if no_limit { - self.axis_collapse_limit = std::usize::MAX; - self.axis_collapse_limit_next_last = std::usize::MAX; - self.axis_collapse_limit_last = std::usize::MAX; + self.axis_collapse_limit = usize::MAX; + self.axis_collapse_limit_next_last = usize::MAX; + self.axis_collapse_limit_last = usize::MAX; } self } diff --git a/src/arraytraits.rs b/src/arraytraits.rs index ea0b380ed..2f72cea2f 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -386,7 +386,7 @@ where Slice: AsMut<[A]> let xs = slice.as_mut(); if mem::size_of::() == 0 { assert!( - xs.len() <= ::std::isize::MAX as usize, + xs.len() <= isize::MAX as usize, "Slice length must fit in `isize`.", ); } diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index d11dd996b..6c5cd0e84 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -26,7 +26,6 @@ pub use self::remove_axis::RemoveAxis; pub(crate) use self::axes::axes_of; pub(crate) use self::reshape::reshape_dim; -use std::isize; use std::mem; #[macro_use] @@ -94,7 +93,7 @@ pub fn size_of_shape_checked(dim: &D) -> Result .filter(|&&d| d != 0) .try_fold(1usize, |acc, &d| acc.checked_mul(d)) .ok_or_else(|| from_kind(ErrorKind::Overflow))?; - if size_nonzero > ::std::isize::MAX as usize { + if size_nonzero > isize::MAX as usize { Err(from_kind(ErrorKind::Overflow)) } else { Ok(dim.size()) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index bcfcba94e..6fb850b50 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -296,7 +296,7 @@ where fn dot_shape_error(m: usize, k: usize, k2: usize, n: usize) -> ! { match m.checked_mul(n) { - Some(len) if len <= ::std::isize::MAX as usize => {} + Some(len) if len <= isize::MAX as usize => {} _ => panic!("ndarray: shape {} × {} overflows isize", m, n), } panic!( From 778ee689f092b26e8f1145220a934c2c769ae32e Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Sun, 19 May 2024 16:10:18 +0200 Subject: [PATCH 571/651] Allow for rawpointer::PointerExt being unused as the inherent methods are stable on nightly. --- src/data_repr.rs | 1 + src/data_traits.rs | 1 + src/impl_constructors.rs | 1 + src/impl_methods.rs | 1 + src/impl_owned_array.rs | 1 + src/impl_views/conversions.rs | 1 + 6 files changed, 6 insertions(+) diff --git a/src/data_repr.rs b/src/data_repr.rs index c64cbcfcf..a24cd7789 100644 --- a/src/data_repr.rs +++ b/src/data_repr.rs @@ -8,6 +8,7 @@ use std::mem; use std::mem::ManuallyDrop; use std::ptr::NonNull; +#[allow(unused_imports)] use rawpointer::PointerExt; /// Array's representation. diff --git a/src/data_traits.rs b/src/data_traits.rs index a2784b8d3..960c561da 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -8,6 +8,7 @@ //! The data (inner representation) traits for ndarray +#[allow(unused_imports)] use rawpointer::PointerExt; use alloc::sync::Arc; diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index e5f19a837..acd7f8167 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -34,6 +34,7 @@ use crate::iterators::TrustedIterator; use crate::StrideShape; #[cfg(feature = "std")] use crate::{geomspace, linspace, logspace}; +#[allow(unused_imports)] use rawpointer::PointerExt; /// # Constructor Methods for Owned Arrays diff --git a/src/impl_methods.rs b/src/impl_methods.rs index d1250ec28..f21b407e1 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -10,6 +10,7 @@ use alloc::slice; use alloc::vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +#[allow(unused_imports)] use rawpointer::PointerExt; use std::mem::{size_of, ManuallyDrop}; diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 393c2e075..e7bf6d3a1 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -3,6 +3,7 @@ use alloc::vec::Vec; use std::mem; use std::mem::MaybeUninit; +#[allow(unused_imports)] use rawpointer::PointerExt; use crate::imp_prelude::*; diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs index 18f263691..ef6923a56 100644 --- a/src/impl_views/conversions.rs +++ b/src/impl_views/conversions.rs @@ -7,6 +7,7 @@ // except according to those terms. use alloc::slice; +#[allow(unused_imports)] use rawpointer::PointerExt; use std::mem::MaybeUninit; From 17a628e53ee2a1de93be7705dcb5c38a402ec175 Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Sat, 18 May 2024 10:47:18 -0400 Subject: [PATCH 572/651] Implements and tests `product_axis`. --- src/numeric/impl_numeric.rs | 38 +++++++++++++++++++++++++++++++++++++ tests/numeric.rs | 13 +++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index ca6f24bbe..5cdd255d7 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -8,6 +8,7 @@ #[cfg(feature = "std")] use num_traits::Float; +use num_traits::One; use num_traits::{FromPrimitive, Zero}; use std::ops::{Add, Div, Mul}; @@ -253,6 +254,43 @@ where } } + /// Return product along `axis`. + /// + /// The product of an empty array is 1. + /// + /// ``` + /// use ndarray::{aview0, aview1, arr2, Axis}; + /// + /// let a = arr2(&[[1., 2., 3.], + /// [4., 5., 6.]]); + /// + /// assert!( + /// a.product_axis(Axis(0)) == aview1(&[4., 10., 18.]) && + /// a.product_axis(Axis(1)) == aview1(&[6., 120.]) && + /// + /// a.product_axis(Axis(0)).product_axis(Axis(0)) == aview0(&720.) + /// ); + /// ``` + /// + /// **Panics** if `axis` is out of bounds. + #[track_caller] + pub fn product_axis(&self, axis: Axis) -> Array + where + A: Clone + One + Mul, + D: RemoveAxis, + { + let min_stride_axis = self.dim.min_stride_axis(&self.strides); + if axis == min_stride_axis { + crate::Zip::from(self.lanes(axis)).map_collect(|lane| lane.product()) + } else { + let mut res = Array::ones(self.raw_dim().remove_axis(axis)); + for subview in self.axis_iter(axis) { + res = res * &subview; + } + res + } + } + /// Return mean along `axis`. /// /// Return `None` if the length of the axis is zero. diff --git a/tests/numeric.rs b/tests/numeric.rs index 4d70d4502..f6de146c9 100644 --- a/tests/numeric.rs +++ b/tests/numeric.rs @@ -39,27 +39,36 @@ fn test_mean_with_array_of_floats() } #[test] -fn sum_mean() +fn sum_mean_prod() { let a: Array2 = arr2(&[[1., 2.], [3., 4.]]); assert_eq!(a.sum_axis(Axis(0)), arr1(&[4., 6.])); assert_eq!(a.sum_axis(Axis(1)), arr1(&[3., 7.])); + assert_eq!(a.product_axis(Axis(0)), arr1(&[3., 8.])); + assert_eq!(a.product_axis(Axis(1)), arr1(&[2., 12.])); assert_eq!(a.mean_axis(Axis(0)), Some(arr1(&[2., 3.]))); assert_eq!(a.mean_axis(Axis(1)), Some(arr1(&[1.5, 3.5]))); assert_eq!(a.sum_axis(Axis(1)).sum_axis(Axis(0)), arr0(10.)); + assert_eq!(a.product_axis(Axis(1)).product_axis(Axis(0)), arr0(24.)); assert_eq!(a.view().mean_axis(Axis(1)).unwrap(), aview1(&[1.5, 3.5])); assert_eq!(a.sum(), 10.); } #[test] -fn sum_mean_empty() +fn sum_mean_prod_empty() { assert_eq!(Array3::::ones((2, 0, 3)).sum(), 0.); + assert_eq!(Array3::::ones((2, 0, 3)).product(), 1.); assert_eq!(Array1::::ones(0).sum_axis(Axis(0)), arr0(0.)); + assert_eq!(Array1::::ones(0).product_axis(Axis(0)), arr0(1.)); assert_eq!( Array3::::ones((2, 0, 3)).sum_axis(Axis(1)), Array::zeros((2, 3)), ); + assert_eq!( + Array3::::ones((2, 0, 3)).product_axis(Axis(1)), + Array::ones((2, 3)), + ); let a = Array1::::ones(0).mean_axis(Axis(0)); assert_eq!(a, None); let a = Array3::::ones((2, 0, 3)).mean_axis(Axis(1)); From 654db57755a2bf930c010969d18cbbd827f01d1e Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Sun, 19 May 2024 23:02:55 -0400 Subject: [PATCH 573/651] Expands the `array!` macro. Allows the creation of arrays up to and including 6 dimensions, and causes a compiler error on 7 or more dimensions. --- src/dimension/ndindex.rs | 16 ++++++ src/free_functions.rs | 121 +++++++++++++++++++++------------------ 2 files changed, 81 insertions(+), 56 deletions(-) diff --git a/src/dimension/ndindex.rs b/src/dimension/ndindex.rs index e27e68c99..7bc2c54ef 100644 --- a/src/dimension/ndindex.rs +++ b/src/dimension/ndindex.rs @@ -113,6 +113,22 @@ unsafe impl NdIndex for (Ix, Ix, Ix, Ix, Ix) } } +unsafe impl NdIndex for (Ix, Ix, Ix, Ix, Ix, Ix) +{ + #[inline] + fn index_checked(&self, dim: &Ix6, strides: &Ix6) -> Option + { + dim.stride_offset_checked(strides, &self.into_dimension()) + } + #[inline] + fn index_unchecked(&self, strides: &Ix6) -> isize + { + zip(strides.ix(), self.into_dimension().ix()) + .map(|(&s, &i)| stride_offset(i, s)) + .sum() + } +} + unsafe impl NdIndex for Ix { #[inline] diff --git a/src/free_functions.rs b/src/free_functions.rs index 3adf2d8f3..a31e68062 100644 --- a/src/free_functions.rs +++ b/src/free_functions.rs @@ -11,12 +11,13 @@ use alloc::vec; use alloc::vec::Vec; use std::mem::{forget, size_of}; use std::ptr::NonNull; +#[allow(unused_imports)] +use std::compile_error; use crate::imp_prelude::*; use crate::{dimension, ArcArray1, ArcArray2}; -/// Create an **[`Array`]** with one, two or -/// three dimensions. +/// Create an **[`Array`]** with one, two, three, four, five, or six dimensions. /// /// ``` /// use ndarray::array; @@ -27,18 +28,50 @@ use crate::{dimension, ArcArray1, ArcArray2}; /// /// let a3 = array![[[1, 2], [3, 4]], /// [[5, 6], [7, 8]]]; +/// +/// let a4 = array![[[[1, 2, 3, 4]]]]; +/// +/// let a5 = array![[[[[1, 2, 3, 4, 5]]]]]; +/// +/// let a6 = array![[[[[[1, 2, 3, 4, 5, 6]]]]]]; /// /// assert_eq!(a1.shape(), &[4]); /// assert_eq!(a2.shape(), &[2, 2]); /// assert_eq!(a3.shape(), &[2, 2, 2]); +/// assert_eq!(a4.shape(), &[1, 1, 1, 4]); +/// assert_eq!(a5.shape(), &[1, 1, 1, 1, 5]); +/// assert_eq!(a6.shape(), &[1, 1, 1, 1, 1, 6]); /// ``` /// /// This macro uses `vec![]`, and has the same ownership semantics; /// elements are moved into the resulting `Array`. /// /// Use `array![...].into_shared()` to create an `ArcArray`. +/// +/// Attempts to crate 7D+ arrays with this macro will lead to +/// a compiler error, since the difference between a 7D array +/// of i32 and a 6D array of `[i32; 3]` is ambiguous. Higher-dim +/// arrays can be created with [`ArrayD`]. +/// +/// ```compile_fail +/// use ndarray::array; +/// let a7 = array![[[[[[[1, 2, 3]]]]]]]; +/// // error: Arrays of 7 dimensions or more (or ndarrays of Rust arrays) cannot be constructed with the array! macro. +/// ``` #[macro_export] macro_rules! array { + ($([$([$([$([$([$([$($x:expr),* $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*) => {{ + compile_error!("Arrays of 7 dimensions or more (or ndarrays of Rust arrays) cannot be constructed with the array! macro."); + }}; + ($([$([$([$([$([$($x:expr),* $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*) => {{ + $crate::Array6::from(vec![$([$([$([$([$([$($x,)*],)*],)*],)*],)*],)*]) + }}; + ($([$([$([$([$($x:expr),* $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*) => {{ + $crate::Array5::from(vec![$([$([$([$([$($x,)*],)*],)*],)*],)*]) + }}; + ($([$([$([$($x:expr),* $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*) => {{ + $crate::Array4::from(vec![$([$([$([$($x,)*],)*],)*],)*]) + }}; ($([$([$($x:expr),* $(,)*]),+ $(,)*]),+ $(,)*) => {{ $crate::Array3::from(vec![$([$([$($x,)*],)*],)*]) }}; @@ -233,63 +266,39 @@ pub fn arr2(xs: &[[A; N]]) -> Array2 Array2::from(xs.to_vec()) } -impl From> for Array2 -{ - /// Converts the `Vec` of arrays to an owned 2-D array. - /// - /// **Panics** if the product of non-zero axis lengths overflows `isize`. - fn from(mut xs: Vec<[A; N]>) -> Self - { - let dim = Ix2(xs.len(), N); - let ptr = xs.as_mut_ptr(); - let cap = xs.capacity(); - let expand_len = - dimension::size_of_shape_checked(&dim).expect("Product of non-zero axis lengths must not overflow isize."); - forget(xs); - unsafe { - let v = if size_of::() == 0 { - Vec::from_raw_parts(ptr as *mut A, expand_len, expand_len) - } else if N == 0 { - Vec::new() - } else { - // Guaranteed not to overflow in this case since A is non-ZST - // and Vec never allocates more than isize bytes. - let expand_cap = cap * N; - Vec::from_raw_parts(ptr as *mut A, expand_len, expand_cap) - }; - ArrayBase::from_shape_vec_unchecked(dim, v) +macro_rules! impl_from_nested_vec { + ($arr_type:ty, $ix_type:tt, $($n:ident),+) => { + impl From> for Array + { + fn from(mut xs: Vec<$arr_type>) -> Self + { + let dim = $ix_type(xs.len(), $($n),+); + let ptr = xs.as_mut_ptr(); + let cap = xs.capacity(); + let expand_len = + dimension::size_of_shape_checked(&dim).expect("Product of non-zero axis lengths must not overflow isize."); + forget(xs); + unsafe { + let v = if size_of::() == 0 { + Vec::from_raw_parts(ptr as *mut A, expand_len, expand_len) + } else if $($n == 0 ||)+ false { + Vec::new() + } else { + let expand_cap = cap $(* $n)+; + Vec::from_raw_parts(ptr as *mut A, expand_len, expand_cap) + }; + ArrayBase::from_shape_vec_unchecked(dim, v) + } + } } - } + }; } -impl From> for Array3 -{ - /// Converts the `Vec` of arrays to an owned 3-D array. - /// - /// **Panics** if the product of non-zero axis lengths overflows `isize`. - fn from(mut xs: Vec<[[A; M]; N]>) -> Self - { - let dim = Ix3(xs.len(), N, M); - let ptr = xs.as_mut_ptr(); - let cap = xs.capacity(); - let expand_len = - dimension::size_of_shape_checked(&dim).expect("Product of non-zero axis lengths must not overflow isize."); - forget(xs); - unsafe { - let v = if size_of::() == 0 { - Vec::from_raw_parts(ptr as *mut A, expand_len, expand_len) - } else if N == 0 || M == 0 { - Vec::new() - } else { - // Guaranteed not to overflow in this case since A is non-ZST - // and Vec never allocates more than isize bytes. - let expand_cap = cap * N * M; - Vec::from_raw_parts(ptr as *mut A, expand_len, expand_cap) - }; - ArrayBase::from_shape_vec_unchecked(dim, v) - } - } -} +impl_from_nested_vec!([A; N], Ix2, N); +impl_from_nested_vec!([[A; M]; N], Ix3, N, M); +impl_from_nested_vec!([[[A; L]; M]; N], Ix4, N, M, L); +impl_from_nested_vec!([[[[A; K]; L]; M]; N], Ix5, N, M, L, K); +impl_from_nested_vec!([[[[[A; J]; K]; L]; M]; N], Ix6, N, M, L, K, J); /// Create a two-dimensional array with elements from `xs`. /// From 98b1a3357555dd2ffd8d36d0c3537e19815977d6 Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Sun, 19 May 2024 23:30:13 -0400 Subject: [PATCH 574/651] Hopefully fixes fmt CI error --- src/free_functions.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/free_functions.rs b/src/free_functions.rs index a31e68062..3b65883ef 100644 --- a/src/free_functions.rs +++ b/src/free_functions.rs @@ -275,8 +275,8 @@ macro_rules! impl_from_nested_vec { let dim = $ix_type(xs.len(), $($n),+); let ptr = xs.as_mut_ptr(); let cap = xs.capacity(); - let expand_len = - dimension::size_of_shape_checked(&dim).expect("Product of non-zero axis lengths must not overflow isize."); + let expand_len = dimension::size_of_shape_checked(&dim) + .expect("Product of non-zero axis lengths must not overflow isize."); forget(xs); unsafe { let v = if size_of::() == 0 { From 3986824d1ed73a5c0763d2075410c087464ec74e Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Tue, 21 May 2024 23:13:48 -0400 Subject: [PATCH 575/651] Adds `triu` and `tril` methods that mimic NumPy. Includes branched implementations for f- and c-order arrays. --- src/lib.rs | 3 + src/tri.rs | 288 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 src/tri.rs diff --git a/src/lib.rs b/src/lib.rs index bfeb6835b..d75d65faa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1616,3 +1616,6 @@ pub(crate) fn is_aligned(ptr: *const T) -> bool { (ptr as usize) % ::std::mem::align_of::() == 0 } + +// Triangular constructors +mod tri; diff --git a/src/tri.rs b/src/tri.rs new file mode 100644 index 000000000..94846add4 --- /dev/null +++ b/src/tri.rs @@ -0,0 +1,288 @@ +// Copyright 2014-2024 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::cmp::{max, min}; + +use num_traits::Zero; + +use crate::{dimension::is_layout_f, Array, ArrayBase, Axis, Data, Dimension, IntoDimension, Zip}; + +impl ArrayBase +where + S: Data, + D: Dimension, + A: Clone + Zero, + D::Smaller: Copy, +{ + /// Upper triangular of an array. + /// + /// Return a copy of the array with elements below the *k*-th diagonal zeroed. + /// For arrays with `ndim` exceeding 2, `triu` will apply to the final two axes. + /// For 0D and 1D arrays, `triu` will return an unchanged clone. + /// + /// See also [`ArrayBase::tril`] + /// + /// ``` + /// use ndarray::array; + /// + /// let arr = array![[1, 2, 3], [4, 5, 6], [7, 8, 9]]; + /// let res = arr.triu(0); + /// assert_eq!(res, array![[1, 2, 3], [0, 5, 6], [0, 0, 9]]); + /// ``` + pub fn triu(&self, k: isize) -> Array + { + match self.ndim() > 1 && is_layout_f(&self.dim, &self.strides) { + true => { + let n = self.ndim(); + let mut x = self.view(); + x.swap_axes(n - 2, n - 1); + let mut tril = x.tril(-k); + tril.swap_axes(n - 2, n - 1); + + tril + } + false => { + let mut res = Array::zeros(self.raw_dim()); + Zip::indexed(self.rows()) + .and(res.rows_mut()) + .for_each(|i, src, mut dst| { + let row_num = i.into_dimension().last_elem(); + let lower = max(row_num as isize + k, 0); + dst.slice_mut(s![lower..]).assign(&src.slice(s![lower..])); + }); + + res + } + } + } + + /// Lower triangular of an array. + /// + /// Return a copy of the array with elements above the *k*-th diagonal zeroed. + /// For arrays with `ndim` exceeding 2, `tril` will apply to the final two axes. + /// For 0D and 1D arrays, `tril` will return an unchanged clone. + /// + /// See also [`ArrayBase::triu`] + /// + /// ``` + /// use ndarray::array; + /// + /// let arr = array![[1, 2, 3], [4, 5, 6], [7, 8, 9]]; + /// let res = arr.tril(0); + /// assert_eq!(res, array![[1, 0, 0], [4, 5, 0], [7, 8, 9]]); + /// ``` + pub fn tril(&self, k: isize) -> Array + { + match self.ndim() > 1 && is_layout_f(&self.dim, &self.strides) { + true => { + let n = self.ndim(); + let mut x = self.view(); + x.swap_axes(n - 2, n - 1); + let mut tril = x.triu(-k); + tril.swap_axes(n - 2, n - 1); + + tril + } + false => { + let mut res = Array::zeros(self.raw_dim()); + Zip::indexed(self.rows()) + .and(res.rows_mut()) + .for_each(|i, src, mut dst| { + // This ncols must go inside the loop to avoid panic on 1D arrays. + // Statistically-neglible difference in performance vs defining ncols at top. + let ncols = src.len_of(Axis(src.ndim() - 1)) as isize; + let row_num = i.into_dimension().last_elem(); + let upper = min(row_num as isize + k, ncols) + 1; + dst.slice_mut(s![..upper]).assign(&src.slice(s![..upper])); + }); + + res + } + } + } +} + +#[cfg(test)] +mod tests +{ + use crate::{array, dimension, Array0, Array1, Array2, Array3, ShapeBuilder}; + use std::vec; + + #[test] + fn test_keep_order() + { + let x = Array2::::ones((3, 3).f()); + let res = x.triu(0); + assert!(dimension::is_layout_f(&res.dim, &res.strides)); + + let res = x.tril(0); + assert!(dimension::is_layout_f(&res.dim, &res.strides)); + } + + #[test] + fn test_0d() + { + let x = Array0::::ones(()); + let res = x.triu(0); + assert_eq!(res, x); + + let res = x.tril(0); + assert_eq!(res, x); + + let x = Array0::::ones(().f()); + let res = x.triu(0); + assert_eq!(res, x); + + let res = x.tril(0); + assert_eq!(res, x); + } + + #[test] + fn test_1d() + { + let x = array![1, 2, 3]; + let res = x.triu(0); + assert_eq!(res, x); + + let res = x.triu(0); + assert_eq!(res, x); + + let x = Array1::::ones(3.f()); + let res = x.triu(0); + assert_eq!(res, x); + + let res = x.triu(0); + assert_eq!(res, x); + } + + #[test] + fn test_2d() + { + let x = array![[1, 2, 3], [4, 5, 6], [7, 8, 9]]; + + // Upper + let res = x.triu(0); + assert_eq!(res, array![[1, 2, 3], [0, 5, 6], [0, 0, 9]]); + + // Lower + let res = x.tril(0); + assert_eq!(res, array![[1, 0, 0], [4, 5, 0], [7, 8, 9]]); + + let x = Array2::from_shape_vec((3, 3).f(), vec![1, 4, 7, 2, 5, 8, 3, 6, 9]).unwrap(); + + // Upper + let res = x.triu(0); + assert_eq!(res, array![[1, 2, 3], [0, 5, 6], [0, 0, 9]]); + + // Lower + let res = x.tril(0); + assert_eq!(res, array![[1, 0, 0], [4, 5, 0], [7, 8, 9]]); + } + + #[test] + fn test_3d() + { + let x = array![ + [[1, 2, 3], [4, 5, 6], [7, 8, 9]], + [[10, 11, 12], [13, 14, 15], [16, 17, 18]], + [[19, 20, 21], [22, 23, 24], [25, 26, 27]] + ]; + + // Upper + let res = x.triu(0); + assert_eq!( + res, + array![ + [[1, 2, 3], [0, 5, 6], [0, 0, 9]], + [[10, 11, 12], [0, 14, 15], [0, 0, 18]], + [[19, 20, 21], [0, 23, 24], [0, 0, 27]] + ] + ); + + // Lower + let res = x.tril(0); + assert_eq!( + res, + array![ + [[1, 0, 0], [4, 5, 0], [7, 8, 9]], + [[10, 0, 0], [13, 14, 0], [16, 17, 18]], + [[19, 0, 0], [22, 23, 0], [25, 26, 27]] + ] + ); + + let x = Array3::from_shape_vec( + (3, 3, 3).f(), + vec![1, 10, 19, 4, 13, 22, 7, 16, 25, 2, 11, 20, 5, 14, 23, 8, 17, 26, 3, 12, 21, 6, 15, 24, 9, 18, 27], + ) + .unwrap(); + + // Upper + let res = x.triu(0); + assert_eq!( + res, + array![ + [[1, 2, 3], [0, 5, 6], [0, 0, 9]], + [[10, 11, 12], [0, 14, 15], [0, 0, 18]], + [[19, 20, 21], [0, 23, 24], [0, 0, 27]] + ] + ); + + // Lower + let res = x.tril(0); + assert_eq!( + res, + array![ + [[1, 0, 0], [4, 5, 0], [7, 8, 9]], + [[10, 0, 0], [13, 14, 0], [16, 17, 18]], + [[19, 0, 0], [22, 23, 0], [25, 26, 27]] + ] + ); + } + + #[test] + fn test_off_axis() + { + let x = array![ + [[1, 2, 3], [4, 5, 6], [7, 8, 9]], + [[10, 11, 12], [13, 14, 15], [16, 17, 18]], + [[19, 20, 21], [22, 23, 24], [25, 26, 27]] + ]; + + let res = x.triu(1); + assert_eq!( + res, + array![ + [[0, 2, 3], [0, 0, 6], [0, 0, 0]], + [[0, 11, 12], [0, 0, 15], [0, 0, 0]], + [[0, 20, 21], [0, 0, 24], [0, 0, 0]] + ] + ); + + let res = x.triu(-1); + assert_eq!( + res, + array![ + [[1, 2, 3], [4, 5, 6], [0, 8, 9]], + [[10, 11, 12], [13, 14, 15], [0, 17, 18]], + [[19, 20, 21], [22, 23, 24], [0, 26, 27]] + ] + ); + } + + #[test] + fn test_odd_shape() + { + let x = array![[1, 2, 3], [4, 5, 6]]; + let res = x.triu(0); + assert_eq!(res, array![[1, 2, 3], [0, 5, 6]]); + + let x = array![[1, 2], [3, 4], [5, 6]]; + let res = x.triu(0); + assert_eq!(res, array![[1, 2], [0, 4], [0, 0]]); + } +} From e2facb7d5f3bb154ddc2173c4c985c63be15a69c Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Tue, 21 May 2024 23:16:25 -0400 Subject: [PATCH 576/651] Actually fixes formatting --- src/free_functions.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/free_functions.rs b/src/free_functions.rs index 3b65883ef..5659d7024 100644 --- a/src/free_functions.rs +++ b/src/free_functions.rs @@ -9,10 +9,10 @@ use alloc::vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; -use std::mem::{forget, size_of}; -use std::ptr::NonNull; #[allow(unused_imports)] use std::compile_error; +use std::mem::{forget, size_of}; +use std::ptr::NonNull; use crate::imp_prelude::*; use crate::{dimension, ArcArray1, ArcArray2}; @@ -28,11 +28,11 @@ use crate::{dimension, ArcArray1, ArcArray2}; /// /// let a3 = array![[[1, 2], [3, 4]], /// [[5, 6], [7, 8]]]; -/// +/// /// let a4 = array![[[[1, 2, 3, 4]]]]; -/// +/// /// let a5 = array![[[[[1, 2, 3, 4, 5]]]]]; -/// +/// /// let a6 = array![[[[[[1, 2, 3, 4, 5, 6]]]]]]; /// /// assert_eq!(a1.shape(), &[4]); @@ -47,12 +47,12 @@ use crate::{dimension, ArcArray1, ArcArray2}; /// elements are moved into the resulting `Array`. /// /// Use `array![...].into_shared()` to create an `ArcArray`. -/// +/// /// Attempts to crate 7D+ arrays with this macro will lead to /// a compiler error, since the difference between a 7D array /// of i32 and a 6D array of `[i32; 3]` is ambiguous. Higher-dim /// arrays can be created with [`ArrayD`]. -/// +/// /// ```compile_fail /// use ndarray::array; /// let a7 = array![[[[[[[1, 2, 3]]]]]]]; From 449ad0e08aab60d1a574bf844d9da41ded2d2729 Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Tue, 21 May 2024 23:20:05 -0400 Subject: [PATCH 577/651] Uses alloc:: instead of std:: for vec! import --- src/tri.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tri.rs b/src/tri.rs index 94846add4..bccc5ec87 100644 --- a/src/tri.rs +++ b/src/tri.rs @@ -111,7 +111,7 @@ where mod tests { use crate::{array, dimension, Array0, Array1, Array2, Array3, ShapeBuilder}; - use std::vec; + use alloc::vec; #[test] fn test_keep_order() From 9e9c3f9710c718c68eb382d6ef7032bea91618bb Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Fri, 24 May 2024 23:06:13 -0400 Subject: [PATCH 578/651] Uses initial check to clarify logic --- src/tri.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/tri.rs b/src/tri.rs index bccc5ec87..0d51bc255 100644 --- a/src/tri.rs +++ b/src/tri.rs @@ -36,7 +36,10 @@ where /// ``` pub fn triu(&self, k: isize) -> Array { - match self.ndim() > 1 && is_layout_f(&self.dim, &self.strides) { + if self.ndim() <= 1 { + return self.to_owned(); + } + match is_layout_f(&self.dim, &self.strides) { true => { let n = self.ndim(); let mut x = self.view(); @@ -78,7 +81,10 @@ where /// ``` pub fn tril(&self, k: isize) -> Array { - match self.ndim() > 1 && is_layout_f(&self.dim, &self.strides) { + if self.ndim() <= 1 { + return self.to_owned(); + } + match is_layout_f(&self.dim, &self.strides) { true => { let n = self.ndim(); let mut x = self.view(); @@ -90,12 +96,12 @@ where } false => { let mut res = Array::zeros(self.raw_dim()); + let ncols = self.len_of(Axis(self.ndim() - 1)) as isize; Zip::indexed(self.rows()) .and(res.rows_mut()) .for_each(|i, src, mut dst| { // This ncols must go inside the loop to avoid panic on 1D arrays. // Statistically-neglible difference in performance vs defining ncols at top. - let ncols = src.len_of(Axis(src.ndim() - 1)) as isize; let row_num = i.into_dimension().last_elem(); let upper = min(row_num as isize + k, ncols) + 1; dst.slice_mut(s![..upper]).assign(&src.slice(s![..upper])); From 84f0c80d9d95d47eff6a28f4dd1cc4c4a5cf8171 Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Sat, 25 May 2024 15:26:19 -0400 Subject: [PATCH 579/651] Removes unecessary comment --- src/tri.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tri.rs b/src/tri.rs index 0d51bc255..4eab9e105 100644 --- a/src/tri.rs +++ b/src/tri.rs @@ -100,8 +100,6 @@ where Zip::indexed(self.rows()) .and(res.rows_mut()) .for_each(|i, src, mut dst| { - // This ncols must go inside the loop to avoid panic on 1D arrays. - // Statistically-neglible difference in performance vs defining ncols at top. let row_num = i.into_dimension().last_elem(); let upper = min(row_num as isize + k, ncols) + 1; dst.slice_mut(s![..upper]).assign(&src.slice(s![..upper])); From 75386440e1c28c8b44c2bd94f8caff52fd6bf203 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Mon, 20 May 2024 14:34:37 +0200 Subject: [PATCH 580/651] Fix styling of the BLAS integration heading. --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 4e33d12c4..90ed47932 100644 --- a/README.rst +++ b/README.rst @@ -105,8 +105,8 @@ How to use with cargo [dependencies] ndarray = "0.15.0" -How to enable blas integration ------------------------------ +How to enable BLAS integration +------------------------------ Blas integration is an optional add-on. Without BLAS, ndarray uses the ``matrixmultiply`` crate for matrix multiplication for ``f64`` and ``f32`` From bd13f6bdd9d9494790147539c974b16ba20c141d Mon Sep 17 00:00:00 2001 From: Nil Goyette Date: Mon, 22 Jul 2024 21:12:14 -0400 Subject: [PATCH 581/651] Use the stable toolchain to run clippy --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a8194f8ee..81eb99513 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -16,7 +16,7 @@ jobs: strategy: matrix: rust: - - beta + - stable name: clippy/${{ matrix.rust }} steps: - uses: actions/checkout@v4 From 5d37dfb586e9a816201adef4438d3e1bc12ca3ab Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 26 Jul 2024 19:38:28 +0200 Subject: [PATCH 582/651] Fix clippy lints - list indentation - ignore false positive for PointerExt trait methods (there's already an issue on this in clippy). --- src/dimension/reshape.rs | 4 ++-- src/impl_owned_array.rs | 4 ++-- src/lib.rs | 23 ++++++++++++----------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/dimension/reshape.rs b/src/dimension/reshape.rs index 52d9e719a..abcec4993 100644 --- a/src/dimension/reshape.rs +++ b/src/dimension/reshape.rs @@ -31,11 +31,11 @@ where /// Preconditions: /// /// 1. from_dim and to_dim are valid dimensions (product of all non-zero axes -/// fits in isize::MAX). +/// fits in isize::MAX). /// 2. from_dim and to_dim are don't have any axes that are zero (that should be handled before /// this function). /// 3. `to_strides` should be an all-zeros or all-ones dimension of the right dimensionality -/// (but it will be overwritten after successful exit of this function). +/// (but it will be overwritten after successful exit of this function). /// /// This function returns: /// diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index e7bf6d3a1..f36ce3c2e 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -513,7 +513,7 @@ where D: Dimension /// "growing axis" for the array, i.e. one of these is true: /// /// - The axis is the longest stride axis, for example the 0th axis in a C-layout or the - /// *n-1*th axis in an F-layout array. + /// *n-1*th axis in an F-layout array. /// - The axis has length 0 or 1 (It is converted to the new growing axis) /// /// Ensure appending is efficient by for example starting from an empty array and/or always @@ -569,7 +569,7 @@ where D: Dimension /// "growing axis" for the array, i.e. one of these is true: /// /// - The axis is the longest stride axis, for example the 0th axis in a C-layout or the - /// *n-1*th axis in an F-layout array. + /// *n-1*th axis in an F-layout array. /// - The axis has length 0 or 1 (It is converted to the new growing axis) /// /// Ensure appending is efficient by for example starting from an empty array and/or always diff --git a/src/lib.rs b/src/lib.rs index d75d65faa..380256692 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,7 @@ clippy::while_let_on_iterator, // is not an error clippy::from_iter_instead_of_collect, // using from_iter is good style clippy::redundant_closure, // false positives clippy #7812 + clippy::incompatible_msrv, // false positive PointerExt::offset )] #![doc(test(attr(deny(warnings))))] #![doc(test(attr(allow(unused_variables))))] @@ -36,7 +37,7 @@ //! It is used to implement both the owned arrays and the views; see its docs //! for an overview of all array features.
//! - The main specific array type is **[`Array`]**, which owns -//! its elements. +//! its elements. //! //! ## Highlights //! @@ -376,14 +377,14 @@ pub type Ixs = isize; /// /// - A [`struct@Dim`] value represents a dimensionality or index. /// - Trait [`Dimension`] is implemented by all -/// dimensionalities. It defines many operations for dimensions and indices. +/// dimensionalities. It defines many operations for dimensions and indices. /// - Trait [`IntoDimension`] is used to convert into a -/// `Dim` value. +/// `Dim` value. /// - Trait [`ShapeBuilder`] is an extension of -/// `IntoDimension` and is used when constructing an array. A shape describes -/// not just the extent of each axis but also their strides. +/// `IntoDimension` and is used when constructing an array. A shape describes +/// not just the extent of each axis but also their strides. /// - Trait [`NdIndex`] is an extension of `Dimension` and is -/// for values that can be used with indexing syntax. +/// for values that can be used with indexing syntax. /// /// /// The default memory order of an array is *row major* order (a.k.a “c” order), @@ -1329,11 +1330,11 @@ pub type ArcArray = ArrayBase, D>; /// + [Constructor Methods for Owned Arrays](ArrayBase#constructor-methods-for-owned-arrays) /// + [Methods For All Array Types](ArrayBase#methods-for-all-array-types) /// + Dimensionality-specific type alises -/// [`Array1`], -/// [`Array2`], -/// [`Array3`], ..., -/// [`ArrayD`], -/// and so on. +/// [`Array1`], +/// [`Array2`], +/// [`Array3`], ..., +/// [`ArrayD`], +/// and so on. pub type Array = ArrayBase, D>; /// An array with copy-on-write behavior. From e86ebff64c89e759e3568511045006b51726b4b0 Mon Sep 17 00:00:00 2001 From: daniellga <43149605+daniellga@users.noreply.github.com> Date: Mon, 29 Jul 2024 17:46:08 -0300 Subject: [PATCH 583/651] Add `is_unique` for `ArcArray` (#1399) --- src/impl_arc_array.rs | 25 +++++++++++++++++++++++++ src/lib.rs | 3 +++ 2 files changed, 28 insertions(+) create mode 100644 src/impl_arc_array.rs diff --git a/src/impl_arc_array.rs b/src/impl_arc_array.rs new file mode 100644 index 000000000..0225c71ac --- /dev/null +++ b/src/impl_arc_array.rs @@ -0,0 +1,25 @@ +// Copyright 2019 ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::imp_prelude::*; +use alloc::sync::Arc; + +/// Methods specific to `ArcArray`. +/// +/// ***See also all methods for [`ArrayBase`]*** +impl ArcArray +where D: Dimension +{ + /// Returns `true` iff the inner `Arc` is not shared. + /// If you want to ensure the `Arc` is not concurrently cloned, you need to provide a `&mut self` to this function. + pub fn is_unique(&self) -> bool + { + // Only strong pointers are used in this crate. + Arc::strong_count(&self.data.0) == 1 + } +} diff --git a/src/lib.rs b/src/lib.rs index 380256692..838c70497 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1612,6 +1612,9 @@ mod impl_raw_views; // Copy-on-write array methods mod impl_cow; +// Arc array methods +mod impl_arc_array; + /// Returns `true` if the pointer is aligned. pub(crate) fn is_aligned(ptr: *const T) -> bool { From 587a1113e43a30013d418c767da5dccdeb4e5300 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 29 Jul 2024 22:54:54 +0200 Subject: [PATCH 584/651] Increase MSRV to Rust 1.64.0 --- .github/workflows/ci.yaml | 2 +- Cargo.toml | 2 +- scripts/all-tests.sh | 19 ------------------- src/lib.rs | 3 ++- xtest-serialization/Cargo.toml | 9 +++++++-- 5 files changed, 11 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 81eb99513..06e11241c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -50,7 +50,7 @@ jobs: - stable - beta - nightly - - 1.57.0 # MSRV + - 1.64.0 # MSRV name: tests/${{ matrix.rust }} steps: diff --git a/Cargo.toml b/Cargo.toml index 945dc6aa8..776cfca77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "ndarray" version = "0.15.6" edition = "2018" -rust-version = "1.57" +rust-version = "1.64" authors = [ "Ulrik Sverdrup \"bluss\"", "Jim Turner" diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index cbea6dba7..03f0729f4 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -6,25 +6,6 @@ set -e FEATURES=$1 CHANNEL=$2 -if [ "$CHANNEL" = "1.57.0" ]; then - cargo update --package openblas-src --precise 0.10.5 - cargo update --package openblas-build --precise 0.10.5 - cargo update --package once_cell --precise 1.14.0 - cargo update --package byteorder --precise 1.4.3 - cargo update --package rayon --precise 1.5.3 - cargo update --package rayon-core --precise 1.9.3 - cargo update --package crossbeam-channel --precise 0.5.8 - cargo update --package crossbeam-deque --precise 0.8.3 - cargo update --package crossbeam-epoch --precise 0.9.15 - cargo update --package crossbeam-utils --precise 0.8.16 - cargo update --package rmp --precise 0.8.11 - cargo update --package serde_json --precise 1.0.99 - cargo update --package serde --precise 1.0.156 - cargo update --package thiserror --precise 1.0.39 - cargo update --package quote --precise 1.0.30 - cargo update --package proc-macro2 --precise 1.0.65 -fi - cargo build --verbose --no-default-features # Testing both dev and release profiles helps find bugs, especially in low level code cargo test --verbose --no-default-features diff --git a/src/lib.rs b/src/lib.rs index 838c70497..0329162df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,7 +72,8 @@ //! needs matching memory layout to be efficient (with some exceptions). //! + Efficient floating point matrix multiplication even for very large //! matrices; can optionally use BLAS to improve it further. -//! - **Requires Rust 1.57 or later** +//! +//! - **MSRV: Requires Rust 1.64 or later** //! //! ## Crate Feature Flags //! diff --git a/xtest-serialization/Cargo.toml b/xtest-serialization/Cargo.toml index 857e31fe6..9b377abc6 100644 --- a/xtest-serialization/Cargo.toml +++ b/xtest-serialization/Cargo.toml @@ -20,9 +20,14 @@ default-features = false [dev-dependencies.serde_json] version = "1.0.40" +[dev-dependencies.rmp] +# Old version to work with Rust 1.64+ +version = "=0.8.10" + [dev-dependencies.rmp-serde] -version = "0.14.0" +# Old version to work with Rust 1.64+ +version = "0.14" [dependencies.ron] -version = "0.5.1" +version = "0.8.1" optional = true From 77cc6c87ec28960ebc3fc227e9bc525f8304be93 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 29 Jul 2024 22:59:21 +0200 Subject: [PATCH 585/651] Bump itertools to 0.13 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 776cfca77..943621de2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ rawpointer = { version = "0.2" } defmac = "0.2" quickcheck = { version = "1.0", default-features = false } approx = "0.5" -itertools = { version = "0.10.0", default-features = false, features = ["use_std"] } +itertools = { version = "0.13.0", default-features = false, features = ["use_std"] } [features] default = ["std"] From 3330cbfa1de2b6f30eccdcc0cfee9adb760056d9 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 29 Jul 2024 23:02:32 +0200 Subject: [PATCH 586/651] Bump blas-src to 0.10 --- xtest-blas/Cargo.toml | 3 ++- xtest-numeric/Cargo.toml | 2 +- xtest-serialization/Cargo.toml | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/xtest-blas/Cargo.toml b/xtest-blas/Cargo.toml index 3724caf14..b53919e9a 100644 --- a/xtest-blas/Cargo.toml +++ b/xtest-blas/Cargo.toml @@ -3,6 +3,7 @@ name = "blas-tests" version = "0.1.0" authors = ["bluss"] publish = false +edition = "2018" [lib] test = false @@ -16,7 +17,7 @@ num-complex = { version = "0.4", default-features = false } [dependencies] ndarray = { path = "..", features = ["approx", "blas"] } -blas-src = { version = "0.8", optional = true } +blas-src = { version = "0.10", optional = true } openblas-src = { version = "0.10", optional = true } netlib-src = { version = "0.8", optional = true } diff --git a/xtest-numeric/Cargo.toml b/xtest-numeric/Cargo.toml index 8cbceb247..843fbfefe 100644 --- a/xtest-numeric/Cargo.toml +++ b/xtest-numeric/Cargo.toml @@ -11,7 +11,7 @@ ndarray = { path = "..", features = ["approx"] } ndarray-rand = { path = "../ndarray-rand" } rand_distr = "0.4" -blas-src = { optional = true, version = "0.8", default-features = false, features = ["openblas"] } +blas-src = { optional = true, version = "0.10", default-features = false, features = ["openblas"] } openblas-src = { optional = true, version = "0.10", default-features = false, features = ["cblas", "system"] } [dependencies.rand] diff --git a/xtest-serialization/Cargo.toml b/xtest-serialization/Cargo.toml index 9b377abc6..fbc1a7cbb 100644 --- a/xtest-serialization/Cargo.toml +++ b/xtest-serialization/Cargo.toml @@ -3,6 +3,7 @@ name = "serialization-tests" version = "0.1.0" authors = ["bluss"] publish = false +edition = "2018" [lib] test = false From 99e3f31e7b0b4780c98287256659c8c096c0dd36 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 29 Jul 2024 23:12:31 +0200 Subject: [PATCH 587/651] Fix a few clippy lints Fix the min_value() / max_value() lints. Also remove clippy::many_single_char_names, clippy::unreadable_literal from the allow list since they are not needed anymore. --- src/impl_methods.rs | 2 +- src/lib.rs | 3 --- src/linalg/impl_linalg.rs | 10 +++++----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index f21b407e1..076e155ba 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2623,7 +2623,7 @@ where let dim = self.raw_dim(); Zip::from(LanesMut::new(self.view_mut(), Axis(n - 1))) .and(Lanes::new(rhs.broadcast_assume(dim), Axis(n - 1))) - .for_each(move |s_row, r_row| Zip::from(s_row).and(r_row).for_each(|a, b| f(a, b))); + .for_each(move |s_row, r_row| Zip::from(s_row).and(r_row).for_each(&mut f)); } fn zip_mut_with_elem(&mut self, rhs_elem: &B, mut f: F) diff --git a/src/lib.rs b/src/lib.rs index 0329162df..58ff0e167 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,13 +10,10 @@ #![doc(html_logo_url = "https://rust-ndarray.github.io/images/rust-ndarray_logo.svg")] #![allow( unstable_name_collisions, // our `PointerExt` collides with upcoming inherent methods on `NonNull` - clippy::many_single_char_names, clippy::deref_addrof, - clippy::unreadable_literal, clippy::manual_map, // is not an error clippy::while_let_on_iterator, // is not an error clippy::from_iter_instead_of_collect, // using from_iter is good style - clippy::redundant_closure, // false positives clippy #7812 clippy::incompatible_msrv, // false positive PointerExt::offset )] #![doc(test(attr(deny(warnings))))] diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index 6fb850b50..f3bedae71 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -823,11 +823,11 @@ where if !same_type::() { return false; } - if a.len() > blas_index::max_value() as usize { + if a.len() > blas_index::MAX as usize { return false; } let stride = a.strides()[0]; - if stride == 0 || stride > blas_index::max_value() as isize || stride < blas_index::min_value() as isize { + if stride == 0 || stride > blas_index::MAX as isize || stride < blas_index::MIN as isize { return false; } true @@ -882,12 +882,12 @@ fn is_blas_2d(dim: &Ix2, stride: &Ix2, order: MemoryOrder) -> bool if s0 < 1 || s1 < 1 { return false; } - if (s0 > blas_index::max_value() as isize || s0 < blas_index::min_value() as isize) - || (s1 > blas_index::max_value() as isize || s1 < blas_index::min_value() as isize) + if (s0 > blas_index::MAX as isize || s0 < blas_index::MIN as isize) + || (s1 > blas_index::MAX as isize || s1 < blas_index::MIN as isize) { return false; } - if m > blas_index::max_value() as usize || n > blas_index::max_value() as usize { + if m > blas_index::MAX as usize || n > blas_index::MAX as usize { return false; } true From 8a4d3a054b010caf1de90c3f18d286b91220e313 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Thu, 1 Aug 2024 19:26:48 +0200 Subject: [PATCH 588/651] Organize the workspace of test crates a bit better Using workspace = true (Rust 1.64+) --- Cargo.toml | 13 ++++++++++++- extra-tests/README.md | 5 +++++ {xtest-blas => extra-tests/blas}/Cargo.toml | 6 +++--- {xtest-blas => extra-tests/blas}/src/lib.rs | 0 {xtest-blas => extra-tests/blas}/tests/oper.rs | 0 {xtest-numeric => extra-tests/numeric}/Cargo.toml | 8 ++++---- {xtest-numeric => extra-tests/numeric}/src/lib.rs | 0 .../numeric}/tests/accuracy.rs | 0 .../serialization}/Cargo.toml | 2 +- .../serialization}/src/lib.rs | 0 .../serialization}/tests/serialize.rs | 0 scripts/all-tests.sh | 14 ++++++++------ scripts/cross-tests.sh | 6 +++--- 13 files changed, 36 insertions(+), 18 deletions(-) create mode 100644 extra-tests/README.md rename {xtest-blas => extra-tests/blas}/Cargo.toml (86%) rename {xtest-blas => extra-tests/blas}/src/lib.rs (100%) rename {xtest-blas => extra-tests/blas}/tests/oper.rs (100%) rename {xtest-numeric => extra-tests/numeric}/Cargo.toml (71%) rename {xtest-numeric => extra-tests/numeric}/src/lib.rs (100%) rename {xtest-numeric => extra-tests/numeric}/tests/accuracy.rs (100%) rename {xtest-serialization => extra-tests/serialization}/Cargo.toml (90%) rename {xtest-serialization => extra-tests/serialization}/src/lib.rs (100%) rename {xtest-serialization => extra-tests/serialization}/tests/serialize.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 943621de2..f2a6f7fd4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,7 +80,18 @@ opt-level = 2 opt-level = 2 [workspace] -members = ["ndarray-rand", "xtest-serialization", "xtest-blas", "xtest-numeric"] +members = [ + "ndarray-rand", + "extra-tests/serialization", + "extra-tests/blas", + "extra-tests/numeric", +] + +[workspace.dependencies] +ndarray = { path = "." } +num-traits = { version = "0.2", default-features = false } +num-complex = { version = "0.4", default-features = false } +ndarray-rand = { path = "./ndarray-rand" } [package.metadata.release] no-dev-version = true diff --git a/extra-tests/README.md b/extra-tests/README.md new file mode 100644 index 000000000..5b5b78b58 --- /dev/null +++ b/extra-tests/README.md @@ -0,0 +1,5 @@ + +# Extra Tests + +These are test crates whose settings, features and dependencies should be +separate from the main crate. diff --git a/xtest-blas/Cargo.toml b/extra-tests/blas/Cargo.toml similarity index 86% rename from xtest-blas/Cargo.toml rename to extra-tests/blas/Cargo.toml index b53919e9a..a41efa57f 100644 --- a/xtest-blas/Cargo.toml +++ b/extra-tests/blas/Cargo.toml @@ -11,11 +11,11 @@ test = false [dev-dependencies] approx = "0.5" defmac = "0.2" -num-traits = "0.2" -num-complex = { version = "0.4", default-features = false } +num-traits = { workspace = true } +num-complex = { workspace = true } [dependencies] -ndarray = { path = "..", features = ["approx", "blas"] } +ndarray = { workspace = true, features = ["approx"] } blas-src = { version = "0.10", optional = true } diff --git a/xtest-blas/src/lib.rs b/extra-tests/blas/src/lib.rs similarity index 100% rename from xtest-blas/src/lib.rs rename to extra-tests/blas/src/lib.rs diff --git a/xtest-blas/tests/oper.rs b/extra-tests/blas/tests/oper.rs similarity index 100% rename from xtest-blas/tests/oper.rs rename to extra-tests/blas/tests/oper.rs diff --git a/xtest-numeric/Cargo.toml b/extra-tests/numeric/Cargo.toml similarity index 71% rename from xtest-numeric/Cargo.toml rename to extra-tests/numeric/Cargo.toml index 843fbfefe..3af9538ed 100644 --- a/xtest-numeric/Cargo.toml +++ b/extra-tests/numeric/Cargo.toml @@ -7,8 +7,8 @@ edition = "2018" [dependencies] approx = "0.5" -ndarray = { path = "..", features = ["approx"] } -ndarray-rand = { path = "../ndarray-rand" } +ndarray = { workspace = true, features = ["approx"] } +ndarray-rand = { workspace = true } rand_distr = "0.4" blas-src = { optional = true, version = "0.10", default-features = false, features = ["openblas"] } @@ -19,8 +19,8 @@ version = "0.8.0" features = ["small_rng"] [dev-dependencies] -num-traits = { version = "0.2.14", default-features = false } -num-complex = { version = "0.4", default-features = false } +num-traits = { workspace = true } +num-complex = { workspace = true } [lib] test = false diff --git a/xtest-numeric/src/lib.rs b/extra-tests/numeric/src/lib.rs similarity index 100% rename from xtest-numeric/src/lib.rs rename to extra-tests/numeric/src/lib.rs diff --git a/xtest-numeric/tests/accuracy.rs b/extra-tests/numeric/tests/accuracy.rs similarity index 100% rename from xtest-numeric/tests/accuracy.rs rename to extra-tests/numeric/tests/accuracy.rs diff --git a/xtest-serialization/Cargo.toml b/extra-tests/serialization/Cargo.toml similarity index 90% rename from xtest-serialization/Cargo.toml rename to extra-tests/serialization/Cargo.toml index fbc1a7cbb..bdfcaf907 100644 --- a/xtest-serialization/Cargo.toml +++ b/extra-tests/serialization/Cargo.toml @@ -9,7 +9,7 @@ edition = "2018" test = false [dependencies] -ndarray = { path = "..", features = ["serde"] } +ndarray = { workspace = true, features = ["serde"] } [features] default = ["ron"] diff --git a/xtest-serialization/src/lib.rs b/extra-tests/serialization/src/lib.rs similarity index 100% rename from xtest-serialization/src/lib.rs rename to extra-tests/serialization/src/lib.rs diff --git a/xtest-serialization/tests/serialize.rs b/extra-tests/serialization/tests/serialize.rs similarity index 100% rename from xtest-serialization/tests/serialize.rs rename to extra-tests/serialization/tests/serialize.rs diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index 03f0729f4..ca40c2103 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -12,11 +12,13 @@ cargo test --verbose --no-default-features cargo test --release --verbose --no-default-features cargo build --verbose --features "$FEATURES" cargo test --verbose --features "$FEATURES" -cargo test --manifest-path=ndarray-rand/Cargo.toml --no-default-features --verbose -cargo test --manifest-path=ndarray-rand/Cargo.toml --features quickcheck --verbose -cargo test --manifest-path=xtest-serialization/Cargo.toml --verbose -cargo test --manifest-path=xtest-blas/Cargo.toml --verbose --features openblas-system +cargo test -p ndarray-rand --no-default-features --verbose +cargo test -p ndarray-rand --features ndarray-rand/quickcheck --verbose + +cargo test -p serialization-tests -v +cargo test -p blas-tests -v --features blas-tests/openblas-system +cargo test -p numeric-tests -v +cargo test -p numeric-tests -v --features numeric-tests/test_blas + cargo test --examples -cargo test --manifest-path=xtest-numeric/Cargo.toml --verbose -cargo test --manifest-path=xtest-numeric/Cargo.toml --verbose --features test_blas ([ "$CHANNEL" != "nightly" ] || cargo bench --no-run --verbose --features "$FEATURES") diff --git a/scripts/cross-tests.sh b/scripts/cross-tests.sh index dc27058f8..f28623b46 100755 --- a/scripts/cross-tests.sh +++ b/scripts/cross-tests.sh @@ -9,6 +9,6 @@ TARGET=$3 cross build -v --features="$FEATURES" --target=$TARGET cross test -v --no-fail-fast --features="$FEATURES" --target=$TARGET -cross test -v --no-fail-fast --target=$TARGET --manifest-path=ndarray-rand/Cargo.toml --features quickcheck -cross test -v --no-fail-fast --target=$TARGET --manifest-path=xtest-serialization/Cargo.toml --verbose -cross test -v --no-fail-fast --target=$TARGET --manifest-path=xtest-numeric/Cargo.toml --release +cross test -v --no-fail-fast --target=$TARGET -p ndarray-rand --features ndarray-rand/quickcheck +cross test -v --no-fail-fast --target=$TARGET -p serialization-tests --verbose +cross test -v --no-fail-fast --target=$TARGET -p numeric-tests --release From f4e424a1cd00768f8c09bc0616bf0b424e17c6b3 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Thu, 1 Aug 2024 19:56:05 +0200 Subject: [PATCH 589/651] extra-tests: Reduce matrix sizes in test These tests are far too slow, and we need to reduce the size. --- extra-tests/numeric/tests/accuracy.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extra-tests/numeric/tests/accuracy.rs b/extra-tests/numeric/tests/accuracy.rs index e98fb3c4d..c594f020d 100644 --- a/extra-tests/numeric/tests/accuracy.rs +++ b/extra-tests/numeric/tests/accuracy.rs @@ -172,9 +172,9 @@ fn random_matrix_mul
( ) -> (Array2, Array2) where A: LinalgScalar { - let m = rng.gen_range(15..512); - let k = rng.gen_range(15..512); - let n = rng.gen_range(15..1560); + let m = rng.gen_range(15..128); + let k = rng.gen_range(15..128); + let n = rng.gen_range(15..512); let a = generator(Ix2(m, k), rng); let b = generator(Ix2(n, k), rng); let c = if use_general { @@ -261,7 +261,7 @@ fn accurate_mul_with_column_f64() // pick a few random sizes let rng = &mut SmallRng::from_entropy(); for i in 0..10 { - let m = rng.gen_range(1..350); + let m = rng.gen_range(1..128); let k = rng.gen_range(1..350); let a = gen::(Ix2(m, k), rng); let b_owner = gen::(Ix2(k, k), rng); From 71e359a12301e1daea715477b7218fa44438a053 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Thu, 1 Aug 2024 20:46:42 +0200 Subject: [PATCH 590/651] Update to use dep: for features From Rust 1.60, now we can use this to bring order to the rayon situation (previously renamed to rayon_). --- Cargo.toml | 12 ++++++------ src/lib.rs | 2 -- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f2a6f7fd4..adae2de29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,12 +31,11 @@ num-integer = { version = "0.1.39", default-features = false } num-traits = { version = "0.2", default-features = false } num-complex = { version = "0.4", default-features = false } -# Use via the `rayon` crate feature! -rayon_ = { version = "1.0.3", optional = true, package = "rayon" } +rayon = { version = "1.10.0", optional = true } approx = { version = "0.5", optional = true , default-features = false } -# Use via the `blas` crate feature! +# Use via the `blas` crate feature cblas-sys = { version = "0.1.4", optional = true, default-features = false } libc = { version = "0.2.82", optional = true } @@ -56,10 +55,11 @@ default = ["std"] # Enable blas usage # See README for more instructions -blas = ["cblas-sys", "libc"] +blas = ["dep:cblas-sys", "dep:libc"] +serde = ["dep:serde"] # Old name for the serde feature -serde-1 = ["serde"] +serde-1 = ["dep:serde"] # These features are used for testing test = [] @@ -68,7 +68,7 @@ test = [] docs = ["approx", "serde", "rayon"] std = ["num-traits/std", "matrixmultiply/std"] -rayon = ["rayon_", "std"] +rayon = ["dep:rayon", "std"] matrixmultiply-threading = ["matrixmultiply/threading"] diff --git a/src/lib.rs b/src/lib.rs index 58ff0e167..d6d6efcc2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1583,8 +1583,6 @@ where // parallel methods #[cfg(feature = "rayon")] -extern crate rayon_ as rayon; -#[cfg(feature = "rayon")] pub mod parallel; mod impl_1d; From 2aec8192427e0c59356467ee882085640cdfe757 Mon Sep 17 00:00:00 2001 From: Jonas Bosse Date: Thu, 13 Jul 2023 00:03:02 +0200 Subject: [PATCH 591/651] Add AxisWindows Move core logic of creating the Windows logic into seperate fn. fix some bugs, make the tests pass Reduce code duplication Specialize the `Windows` struct with a `variant` field to replace the `AxisWindows` struct --- src/impl_methods.rs | 8 +- src/iterators/iter.rs | 1 + src/iterators/mod.rs | 2 +- src/iterators/windows.rs | 199 ++++++++++++++++++++++++++++++++------- 4 files changed, 169 insertions(+), 41 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 076e155ba..53e06f2ce 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -46,6 +46,7 @@ use crate::iter::{ AxisChunksIterMut, AxisIter, AxisIterMut, + AxisWindows, ExactChunks, ExactChunksMut, IndexedIter, @@ -1521,7 +1522,7 @@ where /// assert_eq!(window.shape(), &[4, 3, 2]); /// } /// ``` - pub fn axis_windows(&self, axis: Axis, window_size: usize) -> Windows<'_, A, D> + pub fn axis_windows(&self, axis: Axis, window_size: usize) -> AxisWindows<'_, A, D> where S: Data { let axis_index = axis.index(); @@ -1537,10 +1538,7 @@ where self.shape() ); - let mut size = self.raw_dim(); - size[axis_index] = window_size; - - Windows::new(self.view(), size) + AxisWindows::new(self.view(), axis, window_size) } // Return (length, stride) for diagonal diff --git a/src/iterators/iter.rs b/src/iterators/iter.rs index 5c5acb9d7..478987ee0 100644 --- a/src/iterators/iter.rs +++ b/src/iterators/iter.rs @@ -13,6 +13,7 @@ pub use crate::iterators::{ AxisChunksIterMut, AxisIter, AxisIterMut, + AxisWindows, ExactChunks, ExactChunksIter, ExactChunksIterMut, diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 4851b2827..d49ffe2d0 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -28,7 +28,7 @@ use super::{Dimension, Ix, Ixs}; pub use self::chunks::{ExactChunks, ExactChunksIter, ExactChunksIterMut, ExactChunksMut}; pub use self::into_iter::IntoIter; pub use self::lanes::{Lanes, LanesMut}; -pub use self::windows::Windows; +pub use self::windows::{AxisWindows, Windows}; use std::slice::{self, Iter as SliceIter, IterMut as SliceIterMut}; diff --git a/src/iterators/windows.rs b/src/iterators/windows.rs index ec1afb634..453ef5024 100644 --- a/src/iterators/windows.rs +++ b/src/iterators/windows.rs @@ -41,41 +41,7 @@ impl<'a, A, D: Dimension> Windows<'a, A, D> let strides = axis_strides.into_dimension(); let window_strides = a.strides.clone(); - ndassert!( - a.ndim() == window.ndim(), - concat!( - "Window dimension {} does not match array dimension {} ", - "(with array of shape {:?})" - ), - window.ndim(), - a.ndim(), - a.shape() - ); - - ndassert!( - a.ndim() == strides.ndim(), - concat!( - "Stride dimension {} does not match array dimension {} ", - "(with array of shape {:?})" - ), - strides.ndim(), - a.ndim(), - a.shape() - ); - - let mut base = a; - base.slice_each_axis_inplace(|ax_desc| { - let len = ax_desc.len; - let wsz = window[ax_desc.axis.index()]; - let stride = strides[ax_desc.axis.index()]; - - if len < wsz { - Slice::new(0, Some(0), 1) - } else { - Slice::new(0, Some((len - wsz + 1) as isize), stride as isize) - } - }); - + let base = build_base(a, window.clone(), strides); Windows { base: base.into_raw_view(), life: PhantomData, @@ -160,3 +126,166 @@ impl_iterator! { send_sync_read_only!(Windows); send_sync_read_only!(WindowsIter); + +/// Window producer and iterable +/// +/// See [`.axis_windows()`](ArrayBase::axis_windows) for more +/// information. +pub struct AxisWindows<'a, A, D> +{ + base: ArrayView<'a, A, D>, + axis_idx: usize, + window: D, + strides: D, +} + +impl<'a, A, D: Dimension> AxisWindows<'a, A, D> +{ + pub(crate) fn new(a: ArrayView<'a, A, D>, axis: Axis, window_size: usize) -> Self + { + let window_strides = a.strides.clone(); + let axis_idx = axis.index(); + + let mut window = a.raw_dim(); + window[axis_idx] = window_size; + + let ndim = window.ndim(); + let mut unit_stride = D::zeros(ndim); + unit_stride.slice_mut().fill(1); + + let base = build_base(a, window.clone(), unit_stride); + AxisWindows { + base, + axis_idx, + window, + strides: window_strides, + } + } +} + +impl<'a, A, D: Dimension> NdProducer for AxisWindows<'a, A, D> +{ + type Item = ArrayView<'a, A, D>; + type Dim = Ix1; + type Ptr = *mut A; + type Stride = isize; + + fn raw_dim(&self) -> Ix1 + { + Ix1(self.base.raw_dim()[self.axis_idx]) + } + + fn layout(&self) -> Layout + { + self.base.layout() + } + + fn as_ptr(&self) -> *mut A + { + self.base.as_ptr() as *mut _ + } + + fn contiguous_stride(&self) -> isize + { + self.base.contiguous_stride() + } + + unsafe fn as_ref(&self, ptr: *mut A) -> Self::Item + { + ArrayView::new_(ptr, self.window.clone(), self.strides.clone()) + } + + unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A + { + let mut d = D::zeros(self.base.ndim()); + d[self.axis_idx] = i[0]; + self.base.uget_ptr(&d) + } + + fn stride_of(&self, axis: Axis) -> isize + { + assert_eq!(axis, Axis(0)); + self.base.stride_of(Axis(self.axis_idx)) + } + + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) + { + assert_eq!(axis, Axis(0)); + let (a, b) = self.base.split_at(Axis(self.axis_idx), index); + ( + AxisWindows { + base: a, + axis_idx: self.axis_idx, + window: self.window.clone(), + strides: self.strides.clone(), + }, + AxisWindows { + base: b, + axis_idx: self.axis_idx, + window: self.window, + strides: self.strides, + }, + ) + } + + private_impl!{} +} + +impl<'a, A, D> IntoIterator for AxisWindows<'a, A, D> +where + D: Dimension, + A: 'a, +{ + type Item = ::Item; + type IntoIter = WindowsIter<'a, A, D>; + fn into_iter(self) -> Self::IntoIter + { + WindowsIter { + iter: self.base.into_base_iter(), + life: PhantomData, + window: self.window, + strides: self.strides, + } + } +} + +/// build the base array of the `Windows` and `AxisWindows` structs +fn build_base(a: ArrayView, window: D, strides: D) -> ArrayView +where D: Dimension +{ + ndassert!( + a.ndim() == window.ndim(), + concat!( + "Window dimension {} does not match array dimension {} ", + "(with array of shape {:?})" + ), + window.ndim(), + a.ndim(), + a.shape() + ); + + ndassert!( + a.ndim() == strides.ndim(), + concat!( + "Stride dimension {} does not match array dimension {} ", + "(with array of shape {:?})" + ), + strides.ndim(), + a.ndim(), + a.shape() + ); + + let mut base = a; + base.slice_each_axis_inplace(|ax_desc| { + let len = ax_desc.len; + let wsz = window[ax_desc.axis.index()]; + let stride = strides[ax_desc.axis.index()]; + + if len < wsz { + Slice::new(0, Some(0), 1) + } else { + Slice::new(0, Some((len - wsz + 1) as isize), stride as isize) + } + }); + base +} From 21fb8171715bcb6c879a870a3a939a714673cb3d Mon Sep 17 00:00:00 2001 From: Jonas Bosse Date: Thu, 1 Aug 2024 21:07:38 +0200 Subject: [PATCH 592/651] add a test for zipping axis_windows with a 1d array --- tests/windows.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/windows.rs b/tests/windows.rs index d8d5b699e..6506d8301 100644 --- a/tests/windows.rs +++ b/tests/windows.rs @@ -278,6 +278,22 @@ fn test_axis_windows_3d() ]); } +#[test] +fn tests_axis_windows_3d_zips_with_1d() +{ + let a = Array::from_iter(0..27) + .into_shape_with_order((3, 3, 3)) + .unwrap(); + let mut b = Array::zeros(2); + + Zip::from(b.view_mut()) + .and(a.axis_windows(Axis(1), 2)) + .for_each(|b, a| { + *b = a.sum(); + }); + assert_eq!(b,arr1(&[207, 261])); +} + #[test] fn test_window_neg_stride() { From 7dd36384e35d7851d98471cccc4f49c5d682caa3 Mon Sep 17 00:00:00 2001 From: Bjorn <75190918+BjornTheProgrammer@users.noreply.github.com> Date: Thu, 25 Apr 2024 13:24:38 -0700 Subject: [PATCH 593/651] Made compatible with thumbv6m-none-eabi --- .github/workflows/ci.yaml | 21 +++++++++++++++++++++ Cargo.toml | 7 +++++++ README.rst | 4 ++++ src/data_traits.rs | 5 +++++ src/impl_arc_array.rs | 5 +++++ src/lib.rs | 5 +++++ 6 files changed, 47 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 06e11241c..16228b6bd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -42,6 +42,27 @@ jobs: components: rustfmt - run: cargo fmt --all --check + nostd-build: + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental }} + strategy: + matrix: + include: + - rust: stable + experimental: false + target: thumbv6m-none-eabi + + name: nostd-build/${{ matrix.target }}/${{ matrix.rust }} + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ matrix.rust }} + targets: ${{ matrix.target }} + - name: Tests + run: | + cargo rustc "--target=${{ matrix.target }}" --no-default-features --features portable-atomic-critical-section + tests: runs-on: ubuntu-latest strategy: diff --git a/Cargo.toml b/Cargo.toml index adae2de29..5921ebe05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,7 @@ matrixmultiply = { version = "0.3.2", default-features = false, features=["cgemm serde = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } rawpointer = { version = "0.2" } + [dev-dependencies] defmac = "0.2" quickcheck = { version = "1.0", default-features = false } @@ -70,8 +71,14 @@ docs = ["approx", "serde", "rayon"] std = ["num-traits/std", "matrixmultiply/std"] rayon = ["dep:rayon", "std"] +portable-atomic-critical-section = ["portable-atomic/critical-section"] + matrixmultiply-threading = ["matrixmultiply/threading"] +[target.'cfg(not(target_has_atomic = "ptr"))'.dependencies] +portable-atomic = { version = "1.6.0" } +portable-atomic-util = { version = "0.2.0", features = [ "alloc" ] } + [profile.bench] debug = true [profile.dev.package.numeric-tests] diff --git a/README.rst b/README.rst index 90ed47932..3fb692b68 100644 --- a/README.rst +++ b/README.rst @@ -97,6 +97,10 @@ your `Cargo.toml`. - Enable the ``threading`` feature in the matrixmultiply package +- ``portable-atomic-critical-section`` + + - Whether ``portable-atomic`` should use ``critical-section`` + How to use with cargo --------------------- diff --git a/src/data_traits.rs b/src/data_traits.rs index 960c561da..5ed856f4d 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -11,7 +11,12 @@ #[allow(unused_imports)] use rawpointer::PointerExt; +#[cfg(target_has_atomic = "ptr")] use alloc::sync::Arc; + +#[cfg(not(target_has_atomic = "ptr"))] +use portable_atomic_util::Arc; + #[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::mem::MaybeUninit; diff --git a/src/impl_arc_array.rs b/src/impl_arc_array.rs index 0225c71ac..619ae2506 100644 --- a/src/impl_arc_array.rs +++ b/src/impl_arc_array.rs @@ -7,8 +7,13 @@ // except according to those terms. use crate::imp_prelude::*; + +#[cfg(target_has_atomic = "ptr")] use alloc::sync::Arc; +#[cfg(not(target_has_atomic = "ptr"))] +use portable_atomic_util::Arc; + /// Methods specific to `ArcArray`. /// /// ***See also all methods for [`ArrayBase`]*** diff --git a/src/lib.rs b/src/lib.rs index d6d6efcc2..a13224695 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -123,7 +123,12 @@ extern crate cblas_sys; #[cfg(feature = "docs")] pub mod doc; +#[cfg(target_has_atomic = "ptr")] use alloc::sync::Arc; + +#[cfg(not(target_has_atomic = "ptr"))] +use portable_atomic_util::Arc; + use std::marker::PhantomData; pub use crate::dimension::dim::*; From 65c2a22f6f50239d9dba850de5ec517be7fc8a72 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 2 Aug 2024 09:48:56 +0200 Subject: [PATCH 594/651] Organize dependencies with workspace = true (cont.) Continued cleanup using workspace = true and more uniformity in dependency listings among all crates. No functional changes. --- Cargo.toml | 43 ++++++++++++++++------------ extra-tests/blas/Cargo.toml | 13 ++++----- extra-tests/numeric/Cargo.toml | 16 +++++------ extra-tests/serialization/Cargo.toml | 26 ++++++----------- ndarray-rand/Cargo.toml | 12 ++++---- 5 files changed, 51 insertions(+), 59 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5921ebe05..c0c8f8772 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,14 +27,13 @@ bench = false test = true [dependencies] -num-integer = { version = "0.1.39", default-features = false } -num-traits = { version = "0.2", default-features = false } -num-complex = { version = "0.4", default-features = false } +num-integer = { workspace = true } +num-traits = { workspace = true } +num-complex = { workspace = true } +approx = { workspace = true, optional = true } rayon = { version = "1.10.0", optional = true } -approx = { version = "0.5", optional = true , default-features = false } - # Use via the `blas` crate feature cblas-sys = { version = "0.1.4", optional = true, default-features = false } libc = { version = "0.2.82", optional = true } @@ -44,11 +43,10 @@ matrixmultiply = { version = "0.3.2", default-features = false, features=["cgemm serde = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } rawpointer = { version = "0.2" } - [dev-dependencies] defmac = "0.2" -quickcheck = { version = "1.0", default-features = false } -approx = "0.5" +quickcheck = { workspace = true } +approx = { workspace = true, default-features = true } itertools = { version = "0.13.0", default-features = false, features = ["use_std"] } [features] @@ -71,21 +69,14 @@ docs = ["approx", "serde", "rayon"] std = ["num-traits/std", "matrixmultiply/std"] rayon = ["dep:rayon", "std"] -portable-atomic-critical-section = ["portable-atomic/critical-section"] - matrixmultiply-threading = ["matrixmultiply/threading"] +portable-atomic-critical-section = ["portable-atomic/critical-section"] + [target.'cfg(not(target_has_atomic = "ptr"))'.dependencies] portable-atomic = { version = "1.6.0" } portable-atomic-util = { version = "0.2.0", features = [ "alloc" ] } -[profile.bench] -debug = true -[profile.dev.package.numeric-tests] -opt-level = 2 -[profile.test.package.numeric-tests] -opt-level = 2 - [workspace] members = [ "ndarray-rand", @@ -95,10 +86,24 @@ members = [ ] [workspace.dependencies] -ndarray = { path = "." } +ndarray = { version = "0.15", path = "." } +ndarray-rand = { path = "ndarray-rand" } + +num-integer = { version = "0.1.39", default-features = false } num-traits = { version = "0.2", default-features = false } num-complex = { version = "0.4", default-features = false } -ndarray-rand = { path = "./ndarray-rand" } +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" } + +[profile.bench] +debug = true + +[profile.test.package.numeric-tests] +opt-level = 2 +[profile.test.package.blas-tests] +opt-level = 2 [package.metadata.release] no-dev-version = true diff --git a/extra-tests/blas/Cargo.toml b/extra-tests/blas/Cargo.toml index a41efa57f..33323ceac 100644 --- a/extra-tests/blas/Cargo.toml +++ b/extra-tests/blas/Cargo.toml @@ -8,21 +8,20 @@ edition = "2018" [lib] test = false -[dev-dependencies] -approx = "0.5" -defmac = "0.2" -num-traits = { workspace = true } -num-complex = { workspace = true } - [dependencies] ndarray = { workspace = true, features = ["approx"] } blas-src = { version = "0.10", optional = true } - openblas-src = { version = "0.10", optional = true } netlib-src = { version = "0.8", optional = true } blis-src = { version = "0.2", features = ["system"], optional = true } +[dev-dependencies] +defmac = "0.2" +approx = { workspace = true } +num-traits = { workspace = true } +num-complex = { workspace = true } + [features] # Just for making an example and to help testing, , multiple different possible # configurations are selectable here. diff --git a/extra-tests/numeric/Cargo.toml b/extra-tests/numeric/Cargo.toml index 3af9538ed..09fe14dbb 100644 --- a/extra-tests/numeric/Cargo.toml +++ b/extra-tests/numeric/Cargo.toml @@ -5,25 +5,23 @@ authors = ["bluss"] publish = false edition = "2018" +[lib] +test = false + [dependencies] -approx = "0.5" ndarray = { workspace = true, features = ["approx"] } ndarray-rand = { workspace = true } -rand_distr = "0.4" + +approx = { workspace = true } +rand = { workspace = true } +rand_distr = { workspace = true } blas-src = { optional = true, version = "0.10", default-features = false, features = ["openblas"] } openblas-src = { optional = true, version = "0.10", default-features = false, features = ["cblas", "system"] } -[dependencies.rand] -version = "0.8.0" -features = ["small_rng"] - [dev-dependencies] num-traits = { workspace = true } num-complex = { workspace = true } -[lib] -test = false - [features] test_blas = ["ndarray/blas", "blas-src", "openblas-src"] diff --git a/extra-tests/serialization/Cargo.toml b/extra-tests/serialization/Cargo.toml index bdfcaf907..76bf66bf0 100644 --- a/extra-tests/serialization/Cargo.toml +++ b/extra-tests/serialization/Cargo.toml @@ -11,24 +11,16 @@ test = false [dependencies] ndarray = { workspace = true, features = ["serde"] } -[features] -default = ["ron"] - -[dev-dependencies.serde] -version = "1.0.100" -default-features = false - -[dev-dependencies.serde_json] -version = "1.0.40" +serde = { version = "1.0.100", default-features = false } +ron = { version = "0.8.1", optional = true } -[dev-dependencies.rmp] +[dev-dependencies] +serde_json = { version = "1.0.40" } # Old version to work with Rust 1.64+ -version = "=0.8.10" - -[dev-dependencies.rmp-serde] +rmp = { version = "=0.8.10" } # Old version to work with Rust 1.64+ -version = "0.14" +rmp-serde = { version = "0.14" } + +[features] +default = ["ron"] -[dependencies.ron] -version = "0.8.1" -optional = true diff --git a/ndarray-rand/Cargo.toml b/ndarray-rand/Cargo.toml index 4e9bf265f..2824dbd07 100644 --- a/ndarray-rand/Cargo.toml +++ b/ndarray-rand/Cargo.toml @@ -14,17 +14,15 @@ description = "Constructors for randomized arrays. `rand` integration for `ndarr keywords = ["multidimensional", "matrix", "rand", "ndarray"] [dependencies] -ndarray = { version = "0.15", path = ".." } -rand_distr = "0.4.0" -quickcheck = { version = "1.0", default-features = false, optional = true } +ndarray = { workspace = true } -[dependencies.rand] -version = "0.8.0" -features = ["small_rng"] +rand = { workspace = true } +rand_distr = { workspace = true } +quickcheck = { workspace = true, optional = true } [dev-dependencies] rand_isaac = "0.3.0" -quickcheck = { version = "1.0", default-features = false } +quickcheck = { workspace = true } [package.metadata.release] no-dev-version = true From a7fbf3853debb75e6e311091af81d29a5acd8c7e Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 24 Jul 2024 20:02:48 +0200 Subject: [PATCH 595/651] Prepare changelog for 0.16.0 --- RELEASES.md | 78 ++++++++++++++++++++++++++++++++++++++++ scripts/makechangelog.sh | 16 +++++++++ 2 files changed, 94 insertions(+) create mode 100755 scripts/makechangelog.sh diff --git a/RELEASES.md b/RELEASES.md index 364166718..1d310a835 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,81 @@ +Version 0.16.0 (Not yet released) +================================= + +Featured Changes +---------------- + +- Better shape: Deprecate reshape, into_shape by [@bluss](https://github.com/bluss) [#1310](https://github.com/rust-ndarray/ndarray/pull/1310)
+ `.into_shape()` **is now deprecated**. + Use `.into_shape_with_order()` or `.to_shape()` instead, which don't have `into_shape`'s drawbacks. + +New Features and Improvements +----------------------------- + +- Make compatible with thumbv6m-none-eabi by [@BjornTheProgrammer](https://github.com/BjornTheProgrammer) [#1384](https://github.com/rust-ndarray/ndarray/pull/1384) +- `is_unique` for `ArcArray` by [@daniellga](https://github.com/daniellga) [#1399](https://github.com/rust-ndarray/ndarray/pull/1399) +- Add `triu` and `tril` methods directly to ArrayBase by [@akern40](https://github.com/akern40) [#1386](https://github.com/rust-ndarray/ndarray/pull/1386) +- Fix styling of the BLAS integration heading. by [@adamreichold](https://github.com/adamreichold) [#1390](https://github.com/rust-ndarray/ndarray/pull/1390) +- Implement `product_axis` by [@akern40](https://github.com/akern40) [#1387](https://github.com/rust-ndarray/ndarray/pull/1387) +- Add reserve method for owned arrays by [@ssande7](https://github.com/ssande7) [#1268](https://github.com/rust-ndarray/ndarray/pull/1268) +- Use inline on spit_at and smaller methods by [@bluss](https://github.com/bluss) [#1381](https://github.com/rust-ndarray/ndarray/pull/1381) +- Update to Approx 0.5 by [@bluss](https://github.com/bluss) [#1380](https://github.com/rust-ndarray/ndarray/pull/1380) +- Add .into_raw_vec_with_offset() and deprecate .into_raw_vec() by [@bluss](https://github.com/bluss) [#1379](https://github.com/rust-ndarray/ndarray/pull/1379) +- Add additional array -> array view conversions by [@bluss](https://github.com/bluss) [#1130](https://github.com/rust-ndarray/ndarray/pull/1130) +- implement DoubleEndedIterator for 1d `LanesIter` by [@Muthsera](https://github.com/Muthsera) [#1237](https://github.com/rust-ndarray/ndarray/pull/1237) +- Add Zip::any by [@nilgoyette](https://github.com/nilgoyette) [#1228](https://github.com/rust-ndarray/ndarray/pull/1228) +- Make the aview0, aview1, and aview2 free functions be const fns by [@jturner314](https://github.com/jturner314) [#1132](https://github.com/rust-ndarray/ndarray/pull/1132) +- Add missing safety checks to `From<&[[A; N]]> for ArrayView` and `From<&mut [[A; N]]> for ArrayViewMut` by [@jturner314](https://github.com/jturner314) [#1131](https://github.com/rust-ndarray/ndarray/pull/1131) +- derived Debug for Iter and IterMut by [@biskwikman](https://github.com/biskwikman) [#1353](https://github.com/rust-ndarray/ndarray/pull/1353) +- Fix Miri errors for WindowsIter and ExactChunksIter/Mut by [@jturner314](https://github.com/jturner314) [#1142](https://github.com/rust-ndarray/ndarray/pull/1142) +- Fix Miri failure with -Zmiri-tag-raw-pointers by [@jturner314](https://github.com/jturner314) [#1138](https://github.com/rust-ndarray/ndarray/pull/1138) +- Track-caller panics by [@xd009642](https://github.com/xd009642) [#975](https://github.com/rust-ndarray/ndarray/pull/975) +- Add slice_axis_move method by [@jturner314](https://github.com/jturner314) [#1211](https://github.com/rust-ndarray/ndarray/pull/1211) +- iterators: Re-export IntoIter by [@bluss](https://github.com/bluss) [#1370](https://github.com/rust-ndarray/ndarray/pull/1370) +- Fix unsafe blocks in `s![]` macro by [@jturner314](https://github.com/jturner314) [#1196](https://github.com/rust-ndarray/ndarray/pull/1196) +- Fix comparison with NumPy of slicing with negative step by [@venkat0791](https://github.com/venkat0791) [#1319](https://github.com/rust-ndarray/ndarray/pull/1319) +- Updated Windows `base` Computations to be Safer by [@LazaroHurtado](https://github.com/LazaroHurtado) [#1297](https://github.com/rust-ndarray/ndarray/pull/1297) +- Update README-quick-start.md by [@fumseckk](https://github.com/fumseckk) [#1246](https://github.com/rust-ndarray/ndarray/pull/1246) +- Added stride support to `Windows` by [@LazaroHurtado](https://github.com/LazaroHurtado) [#1249](https://github.com/rust-ndarray/ndarray/pull/1249) +- Added select example to numpy user docs by [@WillAyd](https://github.com/WillAyd) [#1294](https://github.com/rust-ndarray/ndarray/pull/1294) +- Add both approx features to the readme by [@nilgoyette](https://github.com/nilgoyette) [#1289](https://github.com/rust-ndarray/ndarray/pull/1289) +- Add NumPy examples combining slicing and assignment by [@jturner314](https://github.com/jturner314) [#1210](https://github.com/rust-ndarray/ndarray/pull/1210) +- Fix contig check for single element arrays by [@bluss](https://github.com/bluss) [#1362](https://github.com/rust-ndarray/ndarray/pull/1362) +- Export Linspace and Logspace iterators by [@johann-cm](https://github.com/johann-cm) [#1348](https://github.com/rust-ndarray/ndarray/pull/1348) +- Use `clone_from()` in two places by [@ChayimFriedman2](https://github.com/ChayimFriedman2) [#1347](https://github.com/rust-ndarray/ndarray/pull/1347) +- Update README-quick-start.md by [@joelchen](https://github.com/joelchen) [#1344](https://github.com/rust-ndarray/ndarray/pull/1344) +- Provide element-wise math functions for floats by [@KmolYuan](https://github.com/KmolYuan) [#1042](https://github.com/rust-ndarray/ndarray/pull/1042) +- Improve example in doc for columns method by [@gkobeaga](https://github.com/gkobeaga) [#1221](https://github.com/rust-ndarray/ndarray/pull/1221) +- Fix description of stack! in quick start by [@jturner314](https://github.com/jturner314) [#1156](https://github.com/rust-ndarray/ndarray/pull/1156) + +Tests, CI and Maintainer tasks +------------------------------ + +- Prepare changelog for 0.16.0 by [@bluss](https://github.com/bluss) [#1401](https://github.com/rust-ndarray/ndarray/pull/1401) +- Organize dependencies with workspace = true (cont.) by [@bluss](https://github.com/bluss) [#1407](https://github.com/rust-ndarray/ndarray/pull/1407) +- Update to use dep: for features by [@bluss](https://github.com/bluss) [#1406](https://github.com/rust-ndarray/ndarray/pull/1406) +- Organize the workspace of test crates a bit better by [@bluss](https://github.com/bluss) [#1405](https://github.com/rust-ndarray/ndarray/pull/1405) +- Add rustfmt commit to ignored revisions for git blame by [@lucascolley](https://github.com/lucascolley) [#1376](https://github.com/rust-ndarray/ndarray/pull/1376) +- The minimum amount of work required to fix our CI by [@adamreichold](https://github.com/adamreichold) [#1388](https://github.com/rust-ndarray/ndarray/pull/1388) +- Fixed broke continuous integration badge by [@juhotuho10](https://github.com/juhotuho10) [#1382](https://github.com/rust-ndarray/ndarray/pull/1382) +- Use mold linker to speed up ci by [@bluss](https://github.com/bluss) [#1378](https://github.com/rust-ndarray/ndarray/pull/1378) +- Add rustformat config and CI by [@bluss](https://github.com/bluss) [#1375](https://github.com/rust-ndarray/ndarray/pull/1375) +- Add docs to CI by [@jturner314](https://github.com/jturner314) [#925](https://github.com/rust-ndarray/ndarray/pull/925) +- Test using cargo-careful by [@bluss](https://github.com/bluss) [#1371](https://github.com/rust-ndarray/ndarray/pull/1371) +- Further ci updates - numeric tests, and run all tests on PRs by [@bluss](https://github.com/bluss) [#1369](https://github.com/rust-ndarray/ndarray/pull/1369) +- Setup ci so that most checks run in merge queue only by [@bluss](https://github.com/bluss) [#1368](https://github.com/rust-ndarray/ndarray/pull/1368) +- Use merge queue by [@bluss](https://github.com/bluss) [#1367](https://github.com/rust-ndarray/ndarray/pull/1367) +- Try to make the master branch shipshape by [@adamreichold](https://github.com/adamreichold) [#1286](https://github.com/rust-ndarray/ndarray/pull/1286) +- Update ci - run cross tests only on master by [@bluss](https://github.com/bluss) [#1366](https://github.com/rust-ndarray/ndarray/pull/1366) +- ndarray_for_numpy_users some example to code not pointed out to clippy by [@higumachan](https://github.com/higumachan) [#1360](https://github.com/rust-ndarray/ndarray/pull/1360) +- Fix minimum rust version mismatch in lib.rs by [@HoKim98](https://github.com/HoKim98) [#1352](https://github.com/rust-ndarray/ndarray/pull/1352) +- Fix MSRV build by pinning crossbeam crates. by [@adamreichold](https://github.com/adamreichold) [#1345](https://github.com/rust-ndarray/ndarray/pull/1345) +- Fix new rustc lints to make the CI pass. by [@adamreichold](https://github.com/adamreichold) [#1337](https://github.com/rust-ndarray/ndarray/pull/1337) +- Make Clippy happy and fix MSRV build by [@adamreichold](https://github.com/adamreichold) [#1320](https://github.com/rust-ndarray/ndarray/pull/1320) +- small formatting fix in README.rst by [@podusowski](https://github.com/podusowski) [#1199](https://github.com/rust-ndarray/ndarray/pull/1199) +- Fix CI failures (mostly linting with clippy) by [@aganders3](https://github.com/aganders3) [#1171](https://github.com/rust-ndarray/ndarray/pull/1171) +- Remove doc(hidden) attr from items in trait impls by [@jturner314](https://github.com/jturner314) [#1165](https://github.com/rust-ndarray/ndarray/pull/1165) + + Version 0.15.6 (2022-07-30) =========================== diff --git a/scripts/makechangelog.sh b/scripts/makechangelog.sh new file mode 100755 index 000000000..8bb6f2c2f --- /dev/null +++ b/scripts/makechangelog.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# Usage: makechangelog +# +# This script depends on and uses the github `gh` binary +# which needs to be authenticated to use. +# +# Will produce some duplicates for PRs integrated using rebase, +# but those will not occur with current merge queue. + +git log --first-parent --pretty="format:%H" "$@" | while read commit_sha +do + gh api "/repos/:owner/:repo/commits/${commit_sha}/pulls" \ + -q ".[] | \"- \(.title) by [@\(.user.login)](\(.user.html_url)) [#\(.number)](\(.html_url))\"" +done | uniq + From a828db6e08cec61f75615f1377eb4a933d7c24d7 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 2 Aug 2024 13:56:18 +0200 Subject: [PATCH 596/651] Update readme for 0.16 --- README.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 3fb692b68..a95ca3205 100644 --- a/README.rst +++ b/README.rst @@ -107,7 +107,7 @@ How to use with cargo :: [dependencies] - ndarray = "0.15.0" + ndarray = "0.16.0" How to enable BLAS integration ------------------------------ @@ -127,8 +127,8 @@ An example configuration using system openblas is shown below. Note that only end-user projects (not libraries) should select provider:: [dependencies] - ndarray = { version = "0.15.0", features = ["blas"] } - blas-src = { version = "0.8", features = ["openblas"] } + ndarray = { version = "0.16.0", features = ["blas"] } + blas-src = { version = "0.10", features = ["openblas"] } openblas-src = { version = "0.10", features = ["cblas", "system"] } Using system-installed dependencies can save a long time building dependencies. @@ -149,6 +149,7 @@ there is no tight coupling to the ``blas-src`` version, so version selection is =========== ============ ================ ============== ``ndarray`` ``blas-src`` ``openblas-src`` ``netlib-src`` =========== ============ ================ ============== +0.16 0.10 0.10 0.8 0.15 0.8 0.10 0.8 0.15 0.7 0.9 0.8 0.14 0.6.1 0.9.0 From 874f6b40b25bac9d2867a6b7fc093770b38fcf98 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 2 Aug 2024 14:17:29 +0200 Subject: [PATCH 597/651] Organize tests in workspace (cont.) Further normalization, using crates/ and naming directories exactly as their respective crates. Also exclude blas-tests from default members, because it depends on selecting a blas backend. --- Cargo.toml | 11 ++++++++--- {extra-tests/blas => crates/blas-tests}/Cargo.toml | 0 {extra-tests/blas => crates/blas-tests}/src/lib.rs | 0 {extra-tests/blas => crates/blas-tests}/tests/oper.rs | 0 .../numeric => crates/numeric-tests}/Cargo.toml | 0 .../numeric => crates/numeric-tests}/src/lib.rs | 0 .../numeric-tests}/tests/accuracy.rs | 0 .../serialization-tests}/Cargo.toml | 0 .../serialization-tests}/src/lib.rs | 0 .../serialization-tests}/tests/serialize.rs | 0 extra-tests/README.md | 5 ----- 11 files changed, 8 insertions(+), 8 deletions(-) rename {extra-tests/blas => crates/blas-tests}/Cargo.toml (100%) rename {extra-tests/blas => crates/blas-tests}/src/lib.rs (100%) rename {extra-tests/blas => crates/blas-tests}/tests/oper.rs (100%) rename {extra-tests/numeric => crates/numeric-tests}/Cargo.toml (100%) rename {extra-tests/numeric => crates/numeric-tests}/src/lib.rs (100%) rename {extra-tests/numeric => crates/numeric-tests}/tests/accuracy.rs (100%) rename {extra-tests/serialization => crates/serialization-tests}/Cargo.toml (100%) rename {extra-tests/serialization => crates/serialization-tests}/src/lib.rs (100%) rename {extra-tests/serialization => crates/serialization-tests}/tests/serialize.rs (100%) delete mode 100644 extra-tests/README.md diff --git a/Cargo.toml b/Cargo.toml index c0c8f8772..f1a7d4287 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,9 +80,14 @@ portable-atomic-util = { version = "0.2.0", features = [ "alloc" ] } [workspace] members = [ "ndarray-rand", - "extra-tests/serialization", - "extra-tests/blas", - "extra-tests/numeric", + "crates/*", +] +default-members = [ + ".", + "ndarray-rand", + "crates/numeric-tests", + "crates/serialization-tests", + # exclude blas-tests that depends on BLAS install ] [workspace.dependencies] diff --git a/extra-tests/blas/Cargo.toml b/crates/blas-tests/Cargo.toml similarity index 100% rename from extra-tests/blas/Cargo.toml rename to crates/blas-tests/Cargo.toml diff --git a/extra-tests/blas/src/lib.rs b/crates/blas-tests/src/lib.rs similarity index 100% rename from extra-tests/blas/src/lib.rs rename to crates/blas-tests/src/lib.rs diff --git a/extra-tests/blas/tests/oper.rs b/crates/blas-tests/tests/oper.rs similarity index 100% rename from extra-tests/blas/tests/oper.rs rename to crates/blas-tests/tests/oper.rs diff --git a/extra-tests/numeric/Cargo.toml b/crates/numeric-tests/Cargo.toml similarity index 100% rename from extra-tests/numeric/Cargo.toml rename to crates/numeric-tests/Cargo.toml diff --git a/extra-tests/numeric/src/lib.rs b/crates/numeric-tests/src/lib.rs similarity index 100% rename from extra-tests/numeric/src/lib.rs rename to crates/numeric-tests/src/lib.rs diff --git a/extra-tests/numeric/tests/accuracy.rs b/crates/numeric-tests/tests/accuracy.rs similarity index 100% rename from extra-tests/numeric/tests/accuracy.rs rename to crates/numeric-tests/tests/accuracy.rs diff --git a/extra-tests/serialization/Cargo.toml b/crates/serialization-tests/Cargo.toml similarity index 100% rename from extra-tests/serialization/Cargo.toml rename to crates/serialization-tests/Cargo.toml diff --git a/extra-tests/serialization/src/lib.rs b/crates/serialization-tests/src/lib.rs similarity index 100% rename from extra-tests/serialization/src/lib.rs rename to crates/serialization-tests/src/lib.rs diff --git a/extra-tests/serialization/tests/serialize.rs b/crates/serialization-tests/tests/serialize.rs similarity index 100% rename from extra-tests/serialization/tests/serialize.rs rename to crates/serialization-tests/tests/serialize.rs diff --git a/extra-tests/README.md b/extra-tests/README.md deleted file mode 100644 index 5b5b78b58..000000000 --- a/extra-tests/README.md +++ /dev/null @@ -1,5 +0,0 @@ - -# Extra Tests - -These are test crates whose settings, features and dependencies should be -separate from the main crate. From 7f93048448d94e4996171d717896cf71c7b94870 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 2 Aug 2024 14:29:44 +0200 Subject: [PATCH 598/651] Simplify all-tests.sh Use workspace features better in all-tests.sh --- crates/serialization-tests/Cargo.toml | 6 +---- crates/serialization-tests/tests/serialize.rs | 2 -- scripts/all-tests.sh | 27 +++++++++++-------- scripts/cross-tests.sh | 9 +++---- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/crates/serialization-tests/Cargo.toml b/crates/serialization-tests/Cargo.toml index 76bf66bf0..8e7056b88 100644 --- a/crates/serialization-tests/Cargo.toml +++ b/crates/serialization-tests/Cargo.toml @@ -12,7 +12,7 @@ test = false ndarray = { workspace = true, features = ["serde"] } serde = { version = "1.0.100", default-features = false } -ron = { version = "0.8.1", optional = true } +ron = { version = "0.8.1" } [dev-dependencies] serde_json = { version = "1.0.40" } @@ -20,7 +20,3 @@ serde_json = { version = "1.0.40" } rmp = { version = "=0.8.10" } # Old version to work with Rust 1.64+ rmp-serde = { version = "0.14" } - -[features] -default = ["ron"] - diff --git a/crates/serialization-tests/tests/serialize.rs b/crates/serialization-tests/tests/serialize.rs index 95e93e4fb..6e6fb4d64 100644 --- a/crates/serialization-tests/tests/serialize.rs +++ b/crates/serialization-tests/tests/serialize.rs @@ -6,7 +6,6 @@ extern crate serde_json; extern crate rmp_serde; -#[cfg(feature = "ron")] extern crate ron; use ndarray::{arr0, arr1, arr2, s, ArcArray, ArcArray2, ArrayD, IxDyn}; @@ -179,7 +178,6 @@ fn serial_many_dim_serde_msgpack() } #[test] -#[cfg(feature = "ron")] fn serial_many_dim_ron() { use ron::de::from_str as ron_deserialize; diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index ca40c2103..6f1fdf73a 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -6,19 +6,24 @@ set -e FEATURES=$1 CHANNEL=$2 -cargo build --verbose --no-default-features -# Testing both dev and release profiles helps find bugs, especially in low level code -cargo test --verbose --no-default-features -cargo test --release --verbose --no-default-features -cargo build --verbose --features "$FEATURES" -cargo test --verbose --features "$FEATURES" -cargo test -p ndarray-rand --no-default-features --verbose -cargo test -p ndarray-rand --features ndarray-rand/quickcheck --verbose - -cargo test -p serialization-tests -v +QC_FEAT=--features=ndarray-rand/quickcheck + +# build check with no features +cargo build -v --no-default-features + +# ndarray with no features +cargo test -p ndarray -v --no-default-features +# all with features +cargo test -v --features "$FEATURES" $QC_FEAT +# all with features and release (ignore test crates which is already optimized) +cargo test -v -p ndarray -p ndarray-rand --release --features "$FEATURES" $QC_FEAT --lib --tests + +# BLAS tests cargo test -p blas-tests -v --features blas-tests/openblas-system -cargo test -p numeric-tests -v cargo test -p numeric-tests -v --features numeric-tests/test_blas +# Examples cargo test --examples + +# Benchmarks ([ "$CHANNEL" != "nightly" ] || cargo bench --no-run --verbose --features "$FEATURES") diff --git a/scripts/cross-tests.sh b/scripts/cross-tests.sh index f28623b46..683a901d8 100755 --- a/scripts/cross-tests.sh +++ b/scripts/cross-tests.sh @@ -7,8 +7,7 @@ FEATURES=$1 CHANNEL=$2 TARGET=$3 -cross build -v --features="$FEATURES" --target=$TARGET -cross test -v --no-fail-fast --features="$FEATURES" --target=$TARGET -cross test -v --no-fail-fast --target=$TARGET -p ndarray-rand --features ndarray-rand/quickcheck -cross test -v --no-fail-fast --target=$TARGET -p serialization-tests --verbose -cross test -v --no-fail-fast --target=$TARGET -p numeric-tests --release +QC_FEAT=--features=ndarray-rand/quickcheck + +cross build -v --features="$FEATURES" $QC_FEAT --target=$TARGET +cross test -v --no-fail-fast --features="$FEATURES" $QC_FEAT --target=$TARGET From de604c9dcce13ec9863f0ae7b24ba6ba1c7429ec Mon Sep 17 00:00:00 2001 From: Barak Ugav Date: Tue, 9 Jul 2024 13:42:06 +0300 Subject: [PATCH 599/651] Add flatten methods --- src/impl_methods.rs | 98 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 2 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 076e155ba..0c585792d 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2017,8 +2017,6 @@ where /// possible, otherwise they are copied to create a new array. /// /// If an index ordering is not specified, the default is `RowMajor`. - /// The operation will only succeed if the array's memory layout is compatible with - /// the index ordering, so that the array elements can be rearranged in place. /// /// # `.to_shape` vs `.into_shape_clone` /// @@ -2131,6 +2129,69 @@ where } } + /// Flatten the array to a one-dimensional array. + /// + /// The array is returned as a `CowArray`; a view if possible, otherwise an owned array. + /// + /// ``` + /// use ndarray::{arr1, arr3}; + /// + /// let array = arr3(&[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); + /// let flattened = array.flatten(); + /// assert_eq!(flattened, arr1(&[1, 2, 3, 4, 5, 6, 7, 8])); + /// ``` + pub fn flatten(&self) -> CowArray<'_, A, Ix1> + where + A: Clone, + S: Data, + { + self.flatten_with_order(Order::RowMajor) + } + + /// Flatten the array to a one-dimensional array. + /// + /// `order` specifies the *logical* order in which the array is to be read and reshaped. + /// The array is returned as a `CowArray`; a view if possible, otherwise an owned array. + /// + /// ``` + /// use ndarray::{arr1, arr2}; + /// use ndarray::Order; + /// + /// let array = arr2(&[[1, 2], [3, 4], [5, 6], [7, 8]]); + /// let flattened = array.flatten_with_order(Order::RowMajor); + /// assert_eq!(flattened, arr1(&[1, 2, 3, 4, 5, 6, 7, 8])); + /// let flattened = array.flatten_with_order(Order::ColumnMajor); + /// assert_eq!(flattened, arr1(&[1, 3, 5, 7, 2, 4, 6, 8])); + /// ``` + pub fn flatten_with_order(&self, order: Order) -> CowArray<'_, A, Ix1> + where + A: Clone, + S: Data, + { + self.to_shape((self.len(), order)).unwrap() + } + + /// Flatten the array to a one-dimensional array, consuming the array. + /// + /// If possible, no copy is made, and the new array use the same memory as the original array. + /// Otherwise, a new array is allocated and the elements are copied. + /// + /// ``` + /// use ndarray::{arr1, arr3}; + /// + /// let array = arr3(&[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); + /// let flattened = array.into_flat(); + /// assert_eq!(flattened, arr1(&[1, 2, 3, 4, 5, 6, 7, 8])); + /// ``` + pub fn into_flat(self) -> ArrayBase + where + A: Clone, + S: DataOwned, + { + let len = self.len(); + self.into_shape_clone(Ix1(len)).unwrap() + } + /// Convert any array or array view to a dynamic dimensional array or /// array view (respectively). /// @@ -3065,3 +3126,36 @@ unsafe fn unlimited_transmute(data: A) -> B } type DimMaxOf =
>::Output; + +#[cfg(test)] +mod tests +{ + use super::*; + use crate::arr3; + + #[test] + fn test_flatten() + { + let array = arr3(&[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); + let flattened = array.flatten(); + assert_eq!(flattened, arr1(&[1, 2, 3, 4, 5, 6, 7, 8])); + } + + #[test] + fn test_flatten_with_order() + { + let array = arr2(&[[1, 2], [3, 4], [5, 6], [7, 8]]); + let flattened = array.flatten_with_order(Order::RowMajor); + assert_eq!(flattened, arr1(&[1, 2, 3, 4, 5, 6, 7, 8])); + let flattened = array.flatten_with_order(Order::ColumnMajor); + assert_eq!(flattened, arr1(&[1, 3, 5, 7, 2, 4, 6, 8])); + } + + #[test] + fn test_into_flat() + { + let array = arr3(&[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); + let flattened = array.into_flat(); + assert_eq!(flattened, arr1(&[1, 2, 3, 4, 5, 6, 7, 8])); + } +} From a5543000e8e9f4b4a182eeb73010a5cfb7b67db1 Mon Sep 17 00:00:00 2001 From: Barak Ugav Date: Wed, 3 Jul 2024 10:20:15 +0300 Subject: [PATCH 600/651] Add `squeeze()` to dynamic arrays --- src/impl_dyn.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/impl_dyn.rs b/src/impl_dyn.rs index 836234cec..b86c5dd69 100644 --- a/src/impl_dyn.rs +++ b/src/impl_dyn.rs @@ -58,4 +58,60 @@ where S: Data self.dim = self.dim.remove_axis(axis); self.strides = self.strides.remove_axis(axis); } + + /// Remove axes of length 1 and return the modified array. + /// + /// If the array has more the one dimension, the result array will always + /// have at least one dimension, even if it has a length of 1. + /// + /// ``` + /// use ndarray::{arr1, arr2, arr3}; + /// + /// let a = arr3(&[[[1, 2, 3]], [[4, 5, 6]]]).into_dyn(); + /// assert_eq!(a.shape(), &[2, 1, 3]); + /// let b = a.squeeze(); + /// assert_eq!(b, arr2(&[[1, 2, 3], [4, 5, 6]]).into_dyn()); + /// assert_eq!(b.shape(), &[2, 3]); + /// + /// let c = arr2(&[[1]]).into_dyn(); + /// assert_eq!(c.shape(), &[1, 1]); + /// let d = c.squeeze(); + /// assert_eq!(d, arr1(&[1]).into_dyn()); + /// assert_eq!(d.shape(), &[1]); + /// ``` + #[track_caller] + pub fn squeeze(self) -> Self + { + let mut out = self; + for axis in (0..out.shape().len()).rev() { + if out.shape()[axis] == 1 && out.shape().len() > 1 { + out = out.remove_axis(Axis(axis)); + } + } + out + } +} + +#[cfg(test)] +mod tests +{ + use crate::{arr1, arr2, arr3}; + + #[test] + fn test_squeeze() + { + let a = arr3(&[[[1, 2, 3]], [[4, 5, 6]]]).into_dyn(); + assert_eq!(a.shape(), &[2, 1, 3]); + + let b = a.squeeze(); + assert_eq!(b, arr2(&[[1, 2, 3], [4, 5, 6]]).into_dyn()); + assert_eq!(b.shape(), &[2, 3]); + + let c = arr2(&[[1]]).into_dyn(); + assert_eq!(c.shape(), &[1, 1]); + + let d = c.squeeze(); + assert_eq!(d, arr1(&[1]).into_dyn()); + assert_eq!(d.shape(), &[1]); + } } From be3aa20f837d8a247cfea417e80c379b63cb7b78 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sat, 5 Jun 2021 17:27:51 -0400 Subject: [PATCH 601/651] Add A: Clone bound to into_shared --- src/arraytraits.rs | 10 ++++++++-- src/data_traits.rs | 22 ++++++++++++++++------ src/impl_methods.rs | 14 +++++++++----- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 2f72cea2f..e68b5d56a 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -10,18 +10,22 @@ use alloc::boxed::Box; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +use std::hash; use std::mem; +use std::mem::size_of; use std::ops::{Index, IndexMut}; -use std::{hash, mem::size_of}; use std::{iter::FromIterator, slice}; use crate::imp_prelude::*; +use crate::Arc; + use crate::{ dimension, iter::{Iter, IterMut}, numeric_util, FoldWhile, NdIndex, + OwnedArcRepr, Zip, }; @@ -457,7 +461,9 @@ where D: Dimension { fn from(arr: Array) -> ArcArray { - arr.into_shared() + let data = OwnedArcRepr(Arc::new(arr.data)); + // safe because: equivalent unmoved data, ptr and dims remain valid + unsafe { ArrayBase::from_data_ptr(data, arr.ptr).with_strides_dim(arr.strides, arr.dim) } } } diff --git a/src/data_traits.rs b/src/data_traits.rs index 5ed856f4d..8ac978046 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -553,9 +553,13 @@ pub unsafe trait DataOwned: Data fn new(elements: Vec) -> Self; /// Converts the data representation to a shared (copy on write) - /// representation, without any copying. + /// representation, cloning the array elements if necessary. #[doc(hidden)] - fn into_shared(self) -> OwnedArcRepr; + #[allow(clippy::wrong_self_convention)] + fn into_shared(self_: ArrayBase) -> ArcArray + where + Self::Elem: Clone, + D: Dimension; } /// Array representation trait. @@ -578,9 +582,12 @@ unsafe impl DataOwned for OwnedRepr OwnedRepr::from(elements) } - fn into_shared(self) -> OwnedArcRepr + fn into_shared(self_: ArrayBase) -> ArcArray + where + A: Clone, + D: Dimension, { - OwnedArcRepr(Arc::new(self)) + ArcArray::from(self_) } } @@ -593,9 +600,12 @@ unsafe impl DataOwned for OwnedArcRepr OwnedArcRepr(Arc::new(OwnedRepr::from(elements))) } - fn into_shared(self) -> OwnedArcRepr + fn into_shared(self_: ArrayBase) -> ArcArray + where + A: Clone, + D: Dimension, { - self + self_ } } diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 076e155ba..1fe633e10 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -292,13 +292,17 @@ where } /// Turn the array into a shared ownership (copy on write) array, - /// without any copying. + /// cloning the array elements if necessary. + /// + /// If you want to generalize over `Array` and `ArcArray` inputs but avoid + /// an `A: Clone` bound, use `Into::>::into` instead of this + /// method. pub fn into_shared(self) -> ArcArray - where S: DataOwned + where + A: Clone, + S: DataOwned, { - let data = self.data.into_shared(); - // safe because: equivalent unmoved data, ptr and dims remain valid - unsafe { ArrayBase::from_data_ptr(data, self.ptr).with_strides_dim(self.strides, self.dim) } + S::into_shared(self) } /// Returns a reference to the first element of the array, or `None` if it From 9681ababca251c4aa8c88bfe069dfce74f48ffcb Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 31 May 2021 21:59:21 -0400 Subject: [PATCH 602/651] Implement DataOwned for CowRepr --- src/data_traits.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index 8ac978046..5dfb90e37 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -534,7 +534,7 @@ unsafe impl<'a, A> DataMut for ViewRepr<&'a mut A> {} /// Array representation trait. /// -/// A representation that is a unique or shared owner of its data. +/// A representation which can be the owner of its data. /// /// ***Internal trait, see `Data`.*** // The owned storage represents the ownership and allocation of the array's elements. @@ -730,6 +730,24 @@ unsafe impl<'a, A> Data for CowRepr<'a, A> unsafe impl<'a, A> DataMut for CowRepr<'a, A> where A: Clone {} +unsafe impl<'a, A> DataOwned for CowRepr<'a, A> +{ + type MaybeUninit = CowRepr<'a, MaybeUninit>; + + fn new(elements: Vec) -> Self + { + CowRepr::Owned(OwnedRepr::new(elements)) + } + + fn into_shared(self_: ArrayBase) -> ArcArray + where + A: Clone, + D: Dimension, + { + self_.into_owned().into_shared() + } +} + /// Array representation trait. /// /// The RawDataSubst trait maps the element type of array storage, while From 6fc3f24100395576f200c638dca7c2b0a345094d Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 2 Aug 2024 22:02:49 +0200 Subject: [PATCH 603/651] Remove deprecated uninitialized and maybe_uninit Replaced by Array::uninit already in 0.15.x --- src/impl_constructors.rs | 62 ---------------------------------------- tests/array-construct.rs | 28 ------------------ 2 files changed, 90 deletions(-) diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index acd7f8167..3bdde09b5 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -625,66 +625,4 @@ where } array } - - #[deprecated( - note = "This method is hard to use correctly. Use `uninit` instead.", - since = "0.15.0" - )] - #[allow(clippy::uninit_vec)] // this is explicitly intended to create uninitialized memory - /// Create an array with uninitialized elements, shape `shape`. - /// - /// Prefer to use [`uninit()`](ArrayBase::uninit) if possible, because it is - /// easier to use correctly. - /// - /// **Panics** if the number of elements in `shape` would overflow isize. - /// - /// ### Safety - /// - /// Accessing uninitialized values is undefined behaviour. You must overwrite *all* the elements - /// in the array after it is created; for example using - /// [`raw_view_mut`](ArrayBase::raw_view_mut) or other low-level element access. - /// - /// The contents of the array is indeterminate before initialization and it - /// is an error to perform operations that use the previous values. For - /// example it would not be legal to use `a += 1.;` on such an array. - /// - /// This constructor is limited to elements where `A: Copy` (no destructors) - /// to avoid users shooting themselves too hard in the foot. - /// - /// (Also note that the constructors `from_shape_vec` and - /// `from_shape_vec_unchecked` allow the user yet more control, in the sense - /// that Arrays can be created from arbitrary vectors.) - pub unsafe fn uninitialized(shape: Sh) -> Self - where - A: Copy, - Sh: ShapeBuilder, - { - let shape = shape.into_shape_with_order(); - let size = size_of_shape_checked_unwrap!(&shape.dim); - let mut v = Vec::with_capacity(size); - v.set_len(size); - Self::from_shape_vec_unchecked(shape, v) - } -} - -impl ArrayBase -where - S: DataOwned>, - D: Dimension, -{ - /// Create an array with uninitialized elements, shape `shape`. - /// - /// This method has been renamed to `uninit` - #[deprecated(note = "Renamed to `uninit`", since = "0.15.0")] - pub fn maybe_uninit(shape: Sh) -> Self - where Sh: ShapeBuilder - { - unsafe { - let shape = shape.into_shape_with_order(); - let size = size_of_shape_checked_unwrap!(&shape.dim); - let mut v = Vec::with_capacity(size); - v.set_len(size); - Self::from_shape_vec_unchecked(shape, v) - } - } } diff --git a/tests/array-construct.rs b/tests/array-construct.rs index b4a81fce1..9f8418467 100644 --- a/tests/array-construct.rs +++ b/tests/array-construct.rs @@ -50,24 +50,6 @@ fn test_arcarray_thread_safe() is_sync(&a); } -#[test] -#[cfg(feature = "std")] -#[allow(deprecated)] // uninitialized -fn test_uninit() -{ - unsafe { - let mut a = Array::::uninitialized((3, 4).f()); - assert_eq!(a.dim(), (3, 4)); - assert_eq!(a.strides(), &[1, 3]); - let b = Array::::linspace(0., 25., a.len()) - .into_shape_with_order(a.dim()) - .unwrap(); - a.assign(&b); - assert_eq!(&a, &b); - assert_eq!(a.t(), b.t()); - } -} - #[test] fn test_from_fn_c0() { @@ -249,16 +231,6 @@ fn deny_wraparound_from_shape_fn() let _five_large = Array::::from_shape_fn((3, 7, 29, 36760123, 823996703), |_| 0.); } -#[should_panic] -#[test] -#[allow(deprecated)] // uninitialized -fn deny_wraparound_uninitialized() -{ - unsafe { - let _five_large = Array::::uninitialized((3, 7, 29, 36760123, 823996703)); - } -} - #[should_panic] #[test] fn deny_wraparound_uninit() From ddc827179b34cb3f6c1634b0deee32c730aacfcd Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 2 Aug 2024 22:06:04 +0200 Subject: [PATCH 604/651] Remove deprecated visit and genrows/gencolumns/_mut Visit replaced by map, genrows/gencolumns by rows/columns. --- src/impl_methods.rs | 50 --------------------------------------------- 1 file changed, 50 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 0c585792d..e3ca02235 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1119,13 +1119,6 @@ where Lanes::new(self.view(), Axis(n - 1)) } - #[deprecated(note = "Renamed to .rows()", since = "0.15.0")] - pub fn genrows(&self) -> Lanes<'_, A, D::Smaller> - where S: Data - { - self.rows() - } - /// Return a producer and iterable that traverses over the *generalized* /// rows of the array and yields mutable array views. /// @@ -1140,13 +1133,6 @@ where LanesMut::new(self.view_mut(), Axis(n - 1)) } - #[deprecated(note = "Renamed to .rows_mut()", since = "0.15.0")] - pub fn genrows_mut(&mut self) -> LanesMut<'_, A, D::Smaller> - where S: DataMut - { - self.rows_mut() - } - /// Return a producer and iterable that traverses over the *generalized* /// columns of the array. For a 2D array these are the regular columns. /// @@ -1179,17 +1165,6 @@ where Lanes::new(self.view(), Axis(0)) } - /// Return a producer and iterable that traverses over the *generalized* - /// columns of the array. For a 2D array these are the regular columns. - /// - /// Renamed to `.columns()` - #[deprecated(note = "Renamed to .columns()", since = "0.15.0")] - pub fn gencolumns(&self) -> Lanes<'_, A, D::Smaller> - where S: Data - { - self.columns() - } - /// Return a producer and iterable that traverses over the *generalized* /// columns of the array and yields mutable array views. /// @@ -1200,17 +1175,6 @@ where LanesMut::new(self.view_mut(), Axis(0)) } - /// Return a producer and iterable that traverses over the *generalized* - /// columns of the array and yields mutable array views. - /// - /// Renamed to `.columns_mut()` - #[deprecated(note = "Renamed to .columns_mut()", since = "0.15.0")] - pub fn gencolumns_mut(&mut self) -> LanesMut<'_, A, D::Smaller> - where S: DataMut - { - self.columns_mut() - } - /// Return a producer and iterable that traverses over all 1D lanes /// pointing in the direction of `axis`. /// @@ -2942,20 +2906,6 @@ where self.fold((), move |(), elt| f(elt)) } - /// Visit each element in the array by calling `f` by reference - /// on each element. - /// - /// Elements are visited in arbitrary order. - #[deprecated(note = "Renamed to .for_each()", since = "0.15.0")] - pub fn visit<'a, F>(&'a self, f: F) - where - F: FnMut(&'a A), - A: 'a, - S: Data, - { - self.for_each(f) - } - /// Fold along an axis. /// /// Combine the elements of each subview with the previous using the `fold` From 41090f311c10701b9c43dece98be5347d36ee21f Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 2 Aug 2024 22:06:33 +0200 Subject: [PATCH 605/651] Correct the deprecation since= versions for 0.16 --- src/impl_methods.rs | 7 ++----- src/impl_owned_array.rs | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index e3ca02235..87521b07e 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1955,7 +1955,7 @@ where /// [3., 4.]]) /// ); /// ``` - #[deprecated = "Use `.into_shape_with_order()` or `.to_shape()`"] + #[deprecated(note = "Use `.into_shape_with_order()` or `.to_shape()`", since = "0.16.0")] pub fn into_shape(self, shape: E) -> Result, ShapeError> where E: IntoDimension { @@ -2064,10 +2064,7 @@ where /// ); /// ``` #[track_caller] - #[deprecated( - note = "Obsolete, use `to_shape` or `into_shape_with_order` instead.", - since = "0.15.2" - )] + #[deprecated(note = "Use `.into_shape_with_order()` or `.to_shape()`", since = "0.16.0")] pub fn reshape(&self, shape: E) -> ArrayBase where S: DataShared + DataOwned, diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index f36ce3c2e..db176210c 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -151,7 +151,7 @@ where D: Dimension /// Depending on slicing and strides, the logically first element of the /// array can be located at an offset. Because of this, prefer to use /// `.into_raw_vec_and_offset()` instead. - #[deprecated(note = "Use .into_raw_vec_and_offset() instead")] + #[deprecated(note = "Use .into_raw_vec_and_offset() instead", since = "0.16.0")] pub fn into_raw_vec(self) -> Vec { self.into_raw_vec_and_offset().0 From 708de9453f47f31f6dad4e7962fd83900779e0e4 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 2 Aug 2024 22:09:30 +0200 Subject: [PATCH 606/651] Remove deprecated Zip apply methods - apply -> for_each - apply_collect -> map_collect - apply_assign_into -> map_assign_into - par_apply -> par_for_each - par_apply_collect -> par_map_collect - par_apply_assign_into -> par_map_assign_into --- src/parallel/impl_par_methods.rs | 39 -------------------------------- src/zip/mod.rs | 32 -------------------------- 2 files changed, 71 deletions(-) diff --git a/src/parallel/impl_par_methods.rs b/src/parallel/impl_par_methods.rs index b3fbdedc8..c6af4e8f3 100644 --- a/src/parallel/impl_par_methods.rs +++ b/src/parallel/impl_par_methods.rs @@ -70,19 +70,6 @@ macro_rules! zip_impl { self.into_par_iter().for_each(move |($($p,)*)| function($($p),*)) } - /// The `par_apply` method for `Zip`. - /// - /// This is a shorthand for using `.into_par_iter().for_each()` on - /// `Zip`. - /// - /// Requires crate feature `rayon`. - #[deprecated(note="Renamed to .par_for_each()", since="0.15.0")] - pub fn par_apply(self, function: F) - where F: Fn($($p::Item),*) + Sync + Send - { - self.into_par_iter().for_each(move |($($p,)*)| function($($p),*)) - } - expand_if!(@bool [$notlast] /// Map and collect the results into a new array, which has the same size as the @@ -134,18 +121,6 @@ macro_rules! zip_impl { } } - /// Map and collect the results into a new array, which has the same size as the - /// inputs. - /// - /// If all inputs are c- or f-order respectively, that is preserved in the output. - #[deprecated(note="Renamed to .par_map_collect()", since="0.15.0")] - pub fn par_apply_collect(self, f: impl Fn($($p::Item,)* ) -> R + Sync + Send) - -> Array - where R: Send - { - self.par_map_collect(f) - } - /// Map and assign the results into the producer `into`, which should have the same /// size as the other inputs. /// @@ -162,20 +137,6 @@ macro_rules! zip_impl { }); } - /// Apply and assign the results into the producer `into`, which should have the same - /// size as the other inputs. - /// - /// The producer should have assignable items as dictated by the `AssignElem` trait, - /// for example `&mut R`. - #[deprecated(note="Renamed to .par_map_assign_into()", since="0.15.0")] - pub fn par_apply_assign_into(self, into: Q, f: impl Fn($($p::Item,)* ) -> R + Sync + Send) - where Q: IntoNdProducer, - Q::Item: AssignElem + Send, - Q::Output: Send, - { - self.par_map_assign_into(into, f) - } - /// Parallel version of `fold`. /// /// Splits the producer in multiple tasks which each accumulate a single value diff --git a/src/zip/mod.rs b/src/zip/mod.rs index aadc93032..b58752f66 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -586,15 +586,6 @@ macro_rules! map_impl { }); } - /// Apply a function to all elements of the input arrays, - /// visiting elements in lock step. - #[deprecated(note="Renamed to .for_each()", since="0.15.0")] - pub fn apply(self, function: F) - where F: FnMut($($p::Item),*) - { - self.for_each(function) - } - /// Apply a fold function to all elements of the input arrays, /// visiting elements in lock step. /// @@ -791,15 +782,6 @@ macro_rules! map_impl { } } - /// Map and collect the results into a new array, which has the same size as the - /// inputs. - /// - /// If all inputs are c- or f-order respectively, that is preserved in the output. - #[deprecated(note="Renamed to .map_collect()", since="0.15.0")] - pub fn apply_collect(self, f: impl FnMut($($p::Item,)* ) -> R) -> Array { - self.map_collect(f) - } - /// Map and assign the results into the producer `into`, which should have the same /// size as the other inputs. /// @@ -815,20 +797,6 @@ macro_rules! map_impl { }); } - /// Map and assign the results into the producer `into`, which should have the same - /// size as the other inputs. - /// - /// The producer should have assignable items as dictated by the `AssignElem` trait, - /// for example `&mut R`. - #[deprecated(note="Renamed to .map_assign_into()", since="0.15.0")] - pub fn apply_assign_into(self, into: Q, f: impl FnMut($($p::Item,)* ) -> R) - where Q: IntoNdProducer, - Q::Item: AssignElem - { - self.map_assign_into(into, f) - } - - ); /// Split the `Zip` evenly in two. From 99766f6536a76377f2000bb1cdac734d3bd6124e Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 2 Aug 2024 22:11:45 +0200 Subject: [PATCH 607/651] ndarray-rand: Remove deprecated F32 --- ndarray-rand/src/lib.rs | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/ndarray-rand/src/lib.rs b/ndarray-rand/src/lib.rs index 027198538..6671ab334 100644 --- a/ndarray-rand/src/lib.rs +++ b/ndarray-rand/src/lib.rs @@ -310,32 +310,3 @@ fn get_rng() -> SmallRng { SmallRng::from_rng(thread_rng()).expect("create SmallRng from thread_rng failed") } - -/// A wrapper type that allows casting f64 distributions to f32 -/// -/// ``` -/// use ndarray::Array; -/// use ndarray_rand::{RandomExt, F32}; -/// use ndarray_rand::rand_distr::Normal; -/// -/// # fn main() { -/// let distribution_f64 = Normal::new(0., 1.).expect("Failed to create normal distribution"); -/// let a = Array::random((2, 5), F32(distribution_f64)); -/// println!("{:8.4}", a); -/// // Example Output: -/// // [[ -0.6910, 1.1730, 1.0902, -0.4092, -1.7340], -/// // [ -0.6810, 0.1678, -0.9487, 0.3150, 1.2981]] -/// # } -#[derive(Copy, Clone, Debug)] -#[deprecated(since = "0.14.0", note = "Redundant with rand 0.8")] -pub struct F32(pub S); - -#[allow(deprecated)] -impl Distribution for F32 -where S: Distribution -{ - fn sample(&self, rng: &mut R) -> f32 - { - self.0.sample(rng) as f32 - } -} From ca7d8bb0a50add4ef6544eb4909f97103e95a42e Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 2 Aug 2024 22:13:02 +0200 Subject: [PATCH 608/651] Remove deprecated AxisDescription methods --- src/dimension/axes.rs | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/src/dimension/axes.rs b/src/dimension/axes.rs index 925b257a7..45b7a75f0 100644 --- a/src/dimension/axes.rs +++ b/src/dimension/axes.rs @@ -1,4 +1,4 @@ -use crate::{Axis, Dimension, Ix, Ixs}; +use crate::{Axis, Dimension, Ixs}; /// Create a new Axes iterator pub(crate) fn axes_of<'a, D>(d: &'a D, strides: &'a D) -> Axes<'a, D> @@ -58,35 +58,6 @@ pub struct AxisDescription } copy_and_clone!(AxisDescription); - -// AxisDescription can't really be empty -// https://github.com/rust-ndarray/ndarray/pull/642#discussion_r296051702 -#[allow(clippy::len_without_is_empty)] -impl AxisDescription -{ - /// Return axis - #[deprecated(note = "Use .axis field instead", since = "0.15.0")] - #[inline(always)] - pub fn axis(self) -> Axis - { - self.axis - } - /// Return length - #[deprecated(note = "Use .len field instead", since = "0.15.0")] - #[inline(always)] - pub fn len(self) -> Ix - { - self.len - } - /// Return stride - #[deprecated(note = "Use .stride field instead", since = "0.15.0")] - #[inline(always)] - pub fn stride(self) -> Ixs - { - self.stride - } -} - copy_and_clone!(['a, D] Axes<'a, D>); impl<'a, D> Iterator for Axes<'a, D> From 2ed283ab64f82c301e29818fc955ff8ba24e4f7f Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 2 Aug 2024 22:13:21 +0200 Subject: [PATCH 609/651] Remove deprecated scalar_sum --- src/numeric/impl_numeric.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 5cdd255d7..7306fc727 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -47,16 +47,6 @@ where sum } - /// Return the sum of all elements in the array. - /// - /// *This method has been renamed to `.sum()`* - #[deprecated(note = "renamed to `sum`", since = "0.15.0")] - pub fn scalar_sum(&self) -> A - where A: Clone + Add + num_traits::Zero - { - self.sum() - } - /// Returns the [arithmetic mean] x̅ of all elements in the array: /// /// ```text From 7dd3054ac8d6becc8ac4a0effb78c555cf9f5f1e Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 2 Aug 2024 22:16:43 +0200 Subject: [PATCH 610/651] Remove deprecated stack_new_axis Replaced by stack since 0.15.x --- src/lib.rs | 3 +- src/stacking.rs | 74 ++----------------------------------------------- 2 files changed, 4 insertions(+), 73 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a13224695..f52f25e5e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -150,8 +150,7 @@ pub use crate::linalg_traits::LinalgScalar; #[cfg(feature = "std")] pub use crate::linalg_traits::NdFloat; -#[allow(deprecated)] // stack_new_axis -pub use crate::stacking::{concatenate, stack, stack_new_axis}; +pub use crate::stacking::{concatenate, stack}; pub use crate::impl_views::IndexLonger; pub use crate::math_cell::MathCell; diff --git a/src/stacking.rs b/src/stacking.rs index 120baad14..8737d6f60 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -13,40 +13,6 @@ use crate::dimension; use crate::error::{from_kind, ErrorKind, ShapeError}; use crate::imp_prelude::*; -/// Stack arrays along the new axis. -/// -/// ***Errors*** if the arrays have mismatching shapes. -/// ***Errors*** if `arrays` is empty, if `axis` is out of bounds, -/// if the result is larger than is possible to represent. -/// -/// ``` -/// extern crate ndarray; -/// -/// use ndarray::{arr2, arr3, stack, Axis}; -/// -/// # fn main() { -/// -/// let a = arr2(&[[2., 2.], -/// [3., 3.]]); -/// assert!( -/// stack(Axis(0), &[a.view(), a.view()]) -/// == Ok(arr3(&[[[2., 2.], -/// [3., 3.]], -/// [[2., 2.], -/// [3., 3.]]])) -/// ); -/// # } -/// ``` -pub fn stack(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> -where - A: Clone, - D: Dimension, - D::Larger: RemoveAxis, -{ - #[allow(deprecated)] - stack_new_axis(axis, arrays) -} - /// Concatenate arrays along the given axis. /// /// ***Errors*** if the arrays have mismatching shapes, apart from along `axis`. @@ -106,7 +72,6 @@ where Ok(res) } -#[deprecated(note = "Use under the name stack instead.", since = "0.15.0")] /// Stack arrays along the new axis. /// /// ***Errors*** if the arrays have mismatching shapes. @@ -116,14 +81,14 @@ where /// ``` /// extern crate ndarray; /// -/// use ndarray::{arr2, arr3, stack_new_axis, Axis}; +/// use ndarray::{arr2, arr3, stack, Axis}; /// /// # fn main() { /// /// let a = arr2(&[[2., 2.], /// [3., 3.]]); /// assert!( -/// stack_new_axis(Axis(0), &[a.view(), a.view()]) +/// stack(Axis(0), &[a.view(), a.view()]) /// == Ok(arr3(&[[[2., 2.], /// [3., 3.]], /// [[2., 2.], @@ -131,7 +96,7 @@ where /// ); /// # } /// ``` -pub fn stack_new_axis(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> +pub fn stack(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> where A: Clone, D: Dimension, @@ -259,36 +224,3 @@ macro_rules! concatenate { $crate::concatenate($axis, &[ $($crate::ArrayView::from(&$array) ),* ]).unwrap() }; } - -/// Stack arrays along the new axis. -/// -/// Uses the [`stack_new_axis()`] function, calling `ArrayView::from(&a)` on each -/// argument `a`. -/// -/// ***Panics*** if the `stack` function would return an error. -/// -/// ``` -/// extern crate ndarray; -/// -/// use ndarray::{arr2, arr3, stack_new_axis, Axis}; -/// -/// # fn main() { -/// -/// let a = arr2(&[[2., 2.], -/// [3., 3.]]); -/// assert!( -/// stack_new_axis![Axis(0), a, a] -/// == arr3(&[[[2., 2.], -/// [3., 3.]], -/// [[2., 2.], -/// [3., 3.]]]) -/// ); -/// # } -/// ``` -#[macro_export] -#[deprecated(note = "Use under the name stack instead.", since = "0.15.0")] -macro_rules! stack_new_axis { - ($axis:expr, $( $array:expr ),+ ) => { - $crate::stack_new_axis($axis, &[ $($crate::ArrayView::from(&$array) ),* ]).unwrap() - }; -} From b5bbe0ed8e878a6ba560009e99f43c341475a28e Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 2 Aug 2024 22:18:22 +0200 Subject: [PATCH 611/651] Remove deprecated (internal) _data_slice This method was already useless/unused, visible but in practice unused by users. --- src/data_traits.rs | 47 ---------------------------------------------- 1 file changed, 47 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index 5ed856f4d..821bc2911 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -40,11 +40,6 @@ pub unsafe trait RawData: Sized /// The array element type. type Elem; - #[doc(hidden)] - // This method is only used for debugging - #[deprecated(note = "Unused", since = "0.15.2")] - fn _data_slice(&self) -> Option<&[Self::Elem]>; - #[doc(hidden)] fn _is_pointer_inbounds(&self, ptr: *const Self::Elem) -> bool; @@ -177,12 +172,6 @@ unsafe impl RawData for RawViewRepr<*const A> { type Elem = A; - #[inline] - fn _data_slice(&self) -> Option<&[A]> - { - None - } - #[inline(always)] fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { @@ -204,12 +193,6 @@ unsafe impl RawData for RawViewRepr<*mut A> { type Elem = A; - #[inline] - fn _data_slice(&self) -> Option<&[A]> - { - None - } - #[inline(always)] fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { @@ -247,10 +230,6 @@ unsafe impl RawDataClone for RawViewRepr<*mut A> unsafe impl RawData for OwnedArcRepr { type Elem = A; - fn _data_slice(&self) -> Option<&[A]> - { - Some(self.0.as_slice()) - } fn _is_pointer_inbounds(&self, self_ptr: *const Self::Elem) -> bool { @@ -353,11 +332,6 @@ unsafe impl RawData for OwnedRepr { type Elem = A; - fn _data_slice(&self) -> Option<&[A]> - { - Some(self.as_slice()) - } - fn _is_pointer_inbounds(&self, self_ptr: *const Self::Elem) -> bool { let slc = self.as_slice(); @@ -437,12 +411,6 @@ unsafe impl<'a, A> RawData for ViewRepr<&'a A> { type Elem = A; - #[inline] - fn _data_slice(&self) -> Option<&[A]> - { - None - } - #[inline(always)] fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { @@ -481,12 +449,6 @@ unsafe impl<'a, A> RawData for ViewRepr<&'a mut A> { type Elem = A; - #[inline] - fn _data_slice(&self) -> Option<&[A]> - { - None - } - #[inline(always)] fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { @@ -603,15 +565,6 @@ unsafe impl<'a, A> RawData for CowRepr<'a, A> { type Elem = A; - fn _data_slice(&self) -> Option<&[A]> - { - #[allow(deprecated)] - match self { - CowRepr::View(view) => view._data_slice(), - CowRepr::Owned(data) => data._data_slice(), - } - } - #[inline] fn _is_pointer_inbounds(&self, ptr: *const Self::Elem) -> bool { From 516a5047c20102871b88b8ae7aa785fe857cb392 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 2 Aug 2024 23:26:46 +0200 Subject: [PATCH 612/651] Allow aliasing in ArrayView::from_shape Changes the checks in the ArrayView::from_shape constructor so that it allows a few more cases: custom strides that lead to overlapping are allowed. Before, both ArrayViewMut and ArrayView applied the same check, that the dimensions and strides must be such that no elements can be reached by more than one index. However, this rule only applies for mutable data, for ArrayView we can allow this kind of aliasing. This is in fact how broadcasting works, where we use strides to repeat the same array data multiple times. --- src/dimension/mod.rs | 123 ++++++++++++++++++++------------- src/impl_constructors.rs | 6 +- src/impl_views/constructors.rs | 6 +- tests/array.rs | 17 +++++ 4 files changed, 99 insertions(+), 53 deletions(-) diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 6c5cd0e84..2ae851d1c 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -100,6 +100,21 @@ pub fn size_of_shape_checked(dim: &D) -> Result } } +/// Select how aliasing is checked +/// +/// For owned or mutable data: +/// +/// The strides must not allow any element to be referenced by two different indices. +/// +#[derive(Copy, Clone, PartialEq)] +pub(crate) enum CanIndexCheckMode +{ + /// Owned or mutable: No aliasing + OwnedMutable, + /// Aliasing + ReadOnly, +} + /// Checks whether the given data and dimension meet the invariants of the /// `ArrayBase` type, assuming the strides are created using /// `dim.default_strides()` or `dim.fortran_strides()`. @@ -125,12 +140,13 @@ pub fn size_of_shape_checked(dim: &D) -> Result /// `A` and in units of bytes between the least address and greatest address /// accessible by moving along all axes does not exceed `isize::MAX`. pub(crate) fn can_index_slice_with_strides( - data: &[A], dim: &D, strides: &Strides, + data: &[A], dim: &D, strides: &Strides, mode: CanIndexCheckMode, ) -> Result<(), ShapeError> { if let Strides::Custom(strides) = strides { - can_index_slice(data, dim, strides) + can_index_slice(data, dim, strides, mode) } else { + // contiguous shapes: never aliasing, mode does not matter can_index_slice_not_custom(data.len(), dim) } } @@ -239,15 +255,19 @@ where D: Dimension /// allocation. (In other words, the pointer to the first element of the array /// must be computed using `offset_from_low_addr_ptr_to_logical_ptr` so that /// negative strides are correctly handled.) -pub(crate) fn can_index_slice(data: &[A], dim: &D, strides: &D) -> Result<(), ShapeError> +/// +/// Note, condition (4) is guaranteed to be checked last +pub(crate) fn can_index_slice( + data: &[A], dim: &D, strides: &D, mode: CanIndexCheckMode, +) -> Result<(), ShapeError> { // Check conditions 1 and 2 and calculate `max_offset`. let max_offset = max_abs_offset_check_overflow::(dim, strides)?; - can_index_slice_impl(max_offset, data.len(), dim, strides) + can_index_slice_impl(max_offset, data.len(), dim, strides, mode) } fn can_index_slice_impl( - max_offset: usize, data_len: usize, dim: &D, strides: &D, + max_offset: usize, data_len: usize, dim: &D, strides: &D, mode: CanIndexCheckMode, ) -> Result<(), ShapeError> { // Check condition 3. @@ -260,7 +280,7 @@ fn can_index_slice_impl( } // Check condition 4. - if !is_empty && dim_stride_overlap(dim, strides) { + if !is_empty && mode != CanIndexCheckMode::ReadOnly && dim_stride_overlap(dim, strides) { return Err(from_kind(ErrorKind::Unsupported)); } @@ -782,6 +802,7 @@ mod test slice_min_max, slices_intersect, solve_linear_diophantine_eq, + CanIndexCheckMode, IntoDimension, }; use crate::error::{from_kind, ErrorKind}; @@ -796,11 +817,11 @@ mod test let v: alloc::vec::Vec<_> = (0..12).collect(); let dim = (2, 3, 2).into_dimension(); let strides = (1, 2, 6).into_dimension(); - assert!(super::can_index_slice(&v, &dim, &strides).is_ok()); + assert!(super::can_index_slice(&v, &dim, &strides, CanIndexCheckMode::OwnedMutable).is_ok()); let strides = (2, 4, 12).into_dimension(); assert_eq!( - super::can_index_slice(&v, &dim, &strides), + super::can_index_slice(&v, &dim, &strides, CanIndexCheckMode::OwnedMutable), Err(from_kind(ErrorKind::OutOfBounds)) ); } @@ -848,71 +869,79 @@ mod test #[test] fn can_index_slice_ix0() { - can_index_slice::(&[1], &Ix0(), &Ix0()).unwrap(); - can_index_slice::(&[], &Ix0(), &Ix0()).unwrap_err(); + can_index_slice::(&[1], &Ix0(), &Ix0(), CanIndexCheckMode::OwnedMutable).unwrap(); + can_index_slice::(&[], &Ix0(), &Ix0(), CanIndexCheckMode::OwnedMutable).unwrap_err(); } #[test] fn can_index_slice_ix1() { - can_index_slice::(&[], &Ix1(0), &Ix1(0)).unwrap(); - can_index_slice::(&[], &Ix1(0), &Ix1(1)).unwrap(); - can_index_slice::(&[], &Ix1(1), &Ix1(0)).unwrap_err(); - can_index_slice::(&[], &Ix1(1), &Ix1(1)).unwrap_err(); - can_index_slice::(&[1], &Ix1(1), &Ix1(0)).unwrap(); - can_index_slice::(&[1], &Ix1(1), &Ix1(2)).unwrap(); - can_index_slice::(&[1], &Ix1(1), &Ix1(-1isize as usize)).unwrap(); - can_index_slice::(&[1], &Ix1(2), &Ix1(1)).unwrap_err(); - can_index_slice::(&[1, 2], &Ix1(2), &Ix1(0)).unwrap_err(); - can_index_slice::(&[1, 2], &Ix1(2), &Ix1(1)).unwrap(); - can_index_slice::(&[1, 2], &Ix1(2), &Ix1(-1isize as usize)).unwrap(); + let mode = CanIndexCheckMode::OwnedMutable; + can_index_slice::(&[], &Ix1(0), &Ix1(0), mode).unwrap(); + can_index_slice::(&[], &Ix1(0), &Ix1(1), mode).unwrap(); + can_index_slice::(&[], &Ix1(1), &Ix1(0), mode).unwrap_err(); + can_index_slice::(&[], &Ix1(1), &Ix1(1), mode).unwrap_err(); + can_index_slice::(&[1], &Ix1(1), &Ix1(0), mode).unwrap(); + can_index_slice::(&[1], &Ix1(1), &Ix1(2), mode).unwrap(); + can_index_slice::(&[1], &Ix1(1), &Ix1(-1isize as usize), mode).unwrap(); + can_index_slice::(&[1], &Ix1(2), &Ix1(1), mode).unwrap_err(); + can_index_slice::(&[1, 2], &Ix1(2), &Ix1(0), mode).unwrap_err(); + can_index_slice::(&[1, 2], &Ix1(2), &Ix1(1), mode).unwrap(); + can_index_slice::(&[1, 2], &Ix1(2), &Ix1(-1isize as usize), mode).unwrap(); } #[test] fn can_index_slice_ix2() { - can_index_slice::(&[], &Ix2(0, 0), &Ix2(0, 0)).unwrap(); - can_index_slice::(&[], &Ix2(0, 0), &Ix2(2, 1)).unwrap(); - can_index_slice::(&[], &Ix2(0, 1), &Ix2(0, 0)).unwrap(); - can_index_slice::(&[], &Ix2(0, 1), &Ix2(2, 1)).unwrap(); - can_index_slice::(&[], &Ix2(0, 2), &Ix2(0, 0)).unwrap(); - can_index_slice::(&[], &Ix2(0, 2), &Ix2(2, 1)).unwrap_err(); - can_index_slice::(&[1], &Ix2(1, 2), &Ix2(5, 1)).unwrap_err(); - can_index_slice::(&[1, 2], &Ix2(1, 2), &Ix2(5, 1)).unwrap(); - can_index_slice::(&[1, 2], &Ix2(1, 2), &Ix2(5, 2)).unwrap_err(); - can_index_slice::(&[1, 2, 3, 4, 5], &Ix2(2, 2), &Ix2(3, 1)).unwrap(); - can_index_slice::(&[1, 2, 3, 4], &Ix2(2, 2), &Ix2(3, 1)).unwrap_err(); + let mode = CanIndexCheckMode::OwnedMutable; + can_index_slice::(&[], &Ix2(0, 0), &Ix2(0, 0), mode).unwrap(); + can_index_slice::(&[], &Ix2(0, 0), &Ix2(2, 1), mode).unwrap(); + can_index_slice::(&[], &Ix2(0, 1), &Ix2(0, 0), mode).unwrap(); + can_index_slice::(&[], &Ix2(0, 1), &Ix2(2, 1), mode).unwrap(); + can_index_slice::(&[], &Ix2(0, 2), &Ix2(0, 0), mode).unwrap(); + can_index_slice::(&[], &Ix2(0, 2), &Ix2(2, 1), mode).unwrap_err(); + can_index_slice::(&[1], &Ix2(1, 2), &Ix2(5, 1), mode).unwrap_err(); + can_index_slice::(&[1, 2], &Ix2(1, 2), &Ix2(5, 1), mode).unwrap(); + can_index_slice::(&[1, 2], &Ix2(1, 2), &Ix2(5, 2), mode).unwrap_err(); + can_index_slice::(&[1, 2, 3, 4, 5], &Ix2(2, 2), &Ix2(3, 1), mode).unwrap(); + can_index_slice::(&[1, 2, 3, 4], &Ix2(2, 2), &Ix2(3, 1), mode).unwrap_err(); + + // aliasing strides: ok when readonly + can_index_slice::(&[0; 4], &Ix2(2, 2), &Ix2(1, 1), CanIndexCheckMode::OwnedMutable).unwrap_err(); + can_index_slice::(&[0; 4], &Ix2(2, 2), &Ix2(1, 1), CanIndexCheckMode::ReadOnly).unwrap(); } #[test] fn can_index_slice_ix3() { - can_index_slice::(&[], &Ix3(0, 0, 1), &Ix3(2, 1, 3)).unwrap(); - can_index_slice::(&[], &Ix3(1, 1, 1), &Ix3(2, 1, 3)).unwrap_err(); - can_index_slice::(&[1], &Ix3(1, 1, 1), &Ix3(2, 1, 3)).unwrap(); - can_index_slice::(&[1; 11], &Ix3(2, 2, 3), &Ix3(6, 3, 1)).unwrap_err(); - can_index_slice::(&[1; 12], &Ix3(2, 2, 3), &Ix3(6, 3, 1)).unwrap(); + let mode = CanIndexCheckMode::OwnedMutable; + can_index_slice::(&[], &Ix3(0, 0, 1), &Ix3(2, 1, 3), mode).unwrap(); + can_index_slice::(&[], &Ix3(1, 1, 1), &Ix3(2, 1, 3), mode).unwrap_err(); + can_index_slice::(&[1], &Ix3(1, 1, 1), &Ix3(2, 1, 3), mode).unwrap(); + can_index_slice::(&[1; 11], &Ix3(2, 2, 3), &Ix3(6, 3, 1), mode).unwrap_err(); + can_index_slice::(&[1; 12], &Ix3(2, 2, 3), &Ix3(6, 3, 1), mode).unwrap(); } #[test] fn can_index_slice_zero_size_elem() { - can_index_slice::<(), _>(&[], &Ix1(0), &Ix1(1)).unwrap(); - can_index_slice::<(), _>(&[()], &Ix1(1), &Ix1(1)).unwrap(); - can_index_slice::<(), _>(&[(), ()], &Ix1(2), &Ix1(1)).unwrap(); + let mode = CanIndexCheckMode::OwnedMutable; + can_index_slice::<(), _>(&[], &Ix1(0), &Ix1(1), mode).unwrap(); + can_index_slice::<(), _>(&[()], &Ix1(1), &Ix1(1), mode).unwrap(); + can_index_slice::<(), _>(&[(), ()], &Ix1(2), &Ix1(1), mode).unwrap(); // These might seem okay because the element type is zero-sized, but // there could be a zero-sized type such that the number of instances // in existence are carefully controlled. - can_index_slice::<(), _>(&[], &Ix1(1), &Ix1(1)).unwrap_err(); - can_index_slice::<(), _>(&[()], &Ix1(2), &Ix1(1)).unwrap_err(); + can_index_slice::<(), _>(&[], &Ix1(1), &Ix1(1), mode).unwrap_err(); + can_index_slice::<(), _>(&[()], &Ix1(2), &Ix1(1), mode).unwrap_err(); - can_index_slice::<(), _>(&[(), ()], &Ix2(2, 1), &Ix2(1, 0)).unwrap(); - can_index_slice::<(), _>(&[], &Ix2(0, 2), &Ix2(0, 0)).unwrap(); + can_index_slice::<(), _>(&[(), ()], &Ix2(2, 1), &Ix2(1, 0), mode).unwrap(); + can_index_slice::<(), _>(&[], &Ix2(0, 2), &Ix2(0, 0), mode).unwrap(); // This case would be probably be sound, but that's not entirely clear // and it's not worth the special case code. - can_index_slice::<(), _>(&[], &Ix2(0, 2), &Ix2(2, 1)).unwrap_err(); + can_index_slice::<(), _>(&[], &Ix2(0, 2), &Ix2(2, 1), mode).unwrap_err(); } quickcheck! { @@ -923,8 +952,8 @@ mod test // Avoid overflow `dim.default_strides()` or `dim.fortran_strides()`. result.is_err() } else { - result == can_index_slice(&data, &dim, &dim.default_strides()) && - result == can_index_slice(&data, &dim, &dim.fortran_strides()) + result == can_index_slice(&data, &dim, &dim.default_strides(), CanIndexCheckMode::OwnedMutable) && + result == can_index_slice(&data, &dim, &dim.fortran_strides(), CanIndexCheckMode::OwnedMutable) } } } diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 3bdde09b5..260937a90 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -20,8 +20,8 @@ use num_traits::{One, Zero}; use std::mem; use std::mem::MaybeUninit; -use crate::dimension; use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr; +use crate::dimension::{self, CanIndexCheckMode}; use crate::error::{self, ShapeError}; use crate::extension::nonnull::nonnull_from_vec_data; use crate::imp_prelude::*; @@ -466,7 +466,7 @@ where { let dim = shape.dim; let is_custom = shape.strides.is_custom(); - dimension::can_index_slice_with_strides(&v, &dim, &shape.strides)?; + dimension::can_index_slice_with_strides(&v, &dim, &shape.strides, dimension::CanIndexCheckMode::OwnedMutable)?; if !is_custom && dim.size() != v.len() { return Err(error::incompatible_shapes(&Ix1(v.len()), &dim)); } @@ -510,7 +510,7 @@ where 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).is_ok()); + debug_assert!(dimension::can_index_slice(&v, &dim, &strides, CanIndexCheckMode::OwnedMutable).is_ok()); let ptr = nonnull_from_vec_data(&mut v).add(offset_from_low_addr_ptr_to_logical_ptr(&dim, &strides)); ArrayBase::from_data_ptr(DataOwned::new(v), ptr).with_strides_dim(strides, dim) diff --git a/src/impl_views/constructors.rs b/src/impl_views/constructors.rs index dcbec991b..15f2b9b6b 100644 --- a/src/impl_views/constructors.rs +++ b/src/impl_views/constructors.rs @@ -8,8 +8,8 @@ use std::ptr::NonNull; -use crate::dimension; use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr; +use crate::dimension::{self, CanIndexCheckMode}; use crate::error::ShapeError; use crate::extension::nonnull::nonnull_debug_checked_from_ptr; use crate::imp_prelude::*; @@ -54,7 +54,7 @@ where D: Dimension fn from_shape_impl(shape: StrideShape, xs: &'a [A]) -> Result { let dim = shape.dim; - dimension::can_index_slice_with_strides(xs, &dim, &shape.strides)?; + dimension::can_index_slice_with_strides(xs, &dim, &shape.strides, CanIndexCheckMode::ReadOnly)?; let strides = shape.strides.strides_for_dim(&dim); unsafe { Ok(Self::new_( @@ -157,7 +157,7 @@ where D: Dimension fn from_shape_impl(shape: StrideShape, xs: &'a mut [A]) -> Result { let dim = shape.dim; - dimension::can_index_slice_with_strides(xs, &dim, &shape.strides)?; + dimension::can_index_slice_with_strides(xs, &dim, &shape.strides, CanIndexCheckMode::OwnedMutable)?; let strides = shape.strides.strides_for_dim(&dim); unsafe { Ok(Self::new_( diff --git a/tests/array.rs b/tests/array.rs index 8f01d0636..4de22794c 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -10,6 +10,7 @@ use defmac::defmac; use itertools::{zip, Itertools}; use ndarray::indices; use ndarray::prelude::*; +use ndarray::ErrorKind; use ndarray::{arr3, rcarr2}; use ndarray::{Slice, SliceInfo, SliceInfoElem}; use num_complex::Complex; @@ -2060,6 +2061,22 @@ fn test_view_from_shape() assert_eq!(a, answer); } +#[test] +fn test_view_from_shape_allow_overlap() +{ + let data = [0, 1, 2]; + let view = ArrayView::from_shape((2, 3).strides((0, 1)), &data).unwrap(); + assert_eq!(view, aview2(&[data; 2])); +} + +#[test] +fn test_view_mut_from_shape_deny_overlap() +{ + let mut data = [0, 1, 2]; + let result = ArrayViewMut::from_shape((2, 3).strides((0, 1)), &mut data); + assert_matches!(result.map_err(|e| e.kind()), Err(ErrorKind::Unsupported)); +} + #[test] fn test_contiguous() { From 8cbc3487d815c08a81c92029121327ab28677c43 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 3 Aug 2024 09:21:14 +0200 Subject: [PATCH 613/651] ci: Remove duplicated test in cargo-careful ndarray-rand test now included in workspace test on line before. --- .github/workflows/ci.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 16228b6bd..761f6b1cd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -122,7 +122,6 @@ jobs: - name: Install cargo-careful run: cargo install cargo-careful - run: cargo careful test -Zcareful-sanitizer --features="$FEATURES" - - run: cargo careful test -Zcareful-sanitizer -p ndarray-rand docs: if: ${{ github.event_name == 'merge_group' }} From 98c2f25f4a0b45f23e50a47f60df636695a1a071 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 3 Aug 2024 09:25:42 +0200 Subject: [PATCH 614/651] ci: Require nostd build Require this for ci to pass, and shorten the name. --- .github/workflows/ci.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 761f6b1cd..6771fc24b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -42,7 +42,7 @@ jobs: components: rustfmt - run: cargo fmt --all --check - nostd-build: + nostd: runs-on: ubuntu-latest continue-on-error: ${{ matrix.experimental }} strategy: @@ -52,7 +52,7 @@ jobs: experimental: false target: thumbv6m-none-eabi - name: nostd-build/${{ matrix.target }}/${{ matrix.rust }} + name: nostd/${{ matrix.target }}/${{ matrix.rust }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable @@ -142,6 +142,7 @@ jobs: needs: - clippy # - format # should format be required? + - nostd - tests - cross_test - cargo-careful From 3de33e4f5a3a0335d7957347864f8c69a8dc073d Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 3 Aug 2024 09:27:17 +0200 Subject: [PATCH 615/651] ci: Require rustfmt check --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6771fc24b..88c9a6c2d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -141,7 +141,7 @@ jobs: conclusion: needs: - clippy - # - format # should format be required? + - format # should format be required? - nostd - tests - cross_test From b03953aafd7d28dc4e098be1bb1610dfc97dede0 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 3 Aug 2024 19:26:49 +0200 Subject: [PATCH 616/651] Check for aliasing in RawViewMut::from_shape_ptr It should deny overlapping indexes. Because it's an unsafe function and we only do these checks on best-effort basis (caller has to ensure they are correct), it's a debug assertion. Removed the small addition to a doc comment that was done in a previous PR (it was an unfinished idea). Similar to pr #1410 Finishes and closes #919 --- src/dimension/mod.rs | 4 +--- src/impl_raw_views.rs | 2 ++ tests/array.rs | 23 +++++++++++++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 2ae851d1c..601f0dc43 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -55,7 +55,7 @@ pub fn stride_offset(n: Ix, stride: Ix) -> isize /// There is overlap if, when iterating through the dimensions in order of /// increasing stride, the current stride is less than or equal to the maximum /// possible offset along the preceding axes. (Axes of length ≤1 are ignored.) -pub fn dim_stride_overlap(dim: &D, strides: &D) -> bool +pub(crate) fn dim_stride_overlap(dim: &D, strides: &D) -> bool { let order = strides._fastest_varying_stride_order(); let mut sum_prev_offsets = 0; @@ -255,8 +255,6 @@ where D: Dimension /// allocation. (In other words, the pointer to the first element of the array /// must be computed using `offset_from_low_addr_ptr_to_logical_ptr` so that /// negative strides are correctly handled.) -/// -/// Note, condition (4) is guaranteed to be checked last pub(crate) fn can_index_slice( data: &[A], dim: &D, strides: &D, mode: CanIndexCheckMode, ) -> Result<(), ShapeError> diff --git a/src/impl_raw_views.rs b/src/impl_raw_views.rs index fca5e7de7..5132b1158 100644 --- a/src/impl_raw_views.rs +++ b/src/impl_raw_views.rs @@ -294,6 +294,8 @@ where D: Dimension if let Strides::Custom(strides) = &shape.strides { dimension::strides_non_negative(strides).unwrap(); dimension::max_abs_offset_check_overflow::(&dim, strides).unwrap(); + assert!(!dimension::dim_stride_overlap(&dim, strides), + "The strides must not allow any element to be referenced by two different indices"); } else { dimension::size_of_shape_checked(&dim).unwrap(); } diff --git a/tests/array.rs b/tests/array.rs index 4de22794c..696904dab 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -2281,6 +2281,29 @@ fn test_raw_view_mut_from_shape_ptr_deny_neg_strides() let _view = unsafe { RawArrayViewMut::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_mut_ptr()) }; } +#[test] +fn test_raw_view_from_shape_allow_overlap() +{ + let data = [0, 1, 2]; + let view; + unsafe { + let raw_view = RawArrayView::from_shape_ptr((2, 3).strides((0, 1)), data.as_ptr()); + view = raw_view.deref_into_view(); + } + assert_eq!(view, aview2(&[data, data])); +} + +#[should_panic(expected = "strides must not allow any element")] +#[cfg(debug_assertions)] +#[test] +fn test_raw_view_mut_from_shape_deny_overlap() +{ + let mut data = [0, 1, 2]; + unsafe { + RawArrayViewMut::from_shape_ptr((2, 3).strides((0, 1)), data.as_mut_ptr()); + } +} + #[test] fn test_default() { From 825e80b3b31fb2f0e48e41d5817fe9b539655d32 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 3 Aug 2024 19:42:29 +0200 Subject: [PATCH 617/651] Update changelog for ndarray 0.16 and ndarray-rand 0.15 --- README.rst | 4 ++-- RELEASES.md | 12 ++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index a95ca3205..abac4c18e 100644 --- a/README.rst +++ b/README.rst @@ -135,8 +135,8 @@ Using system-installed dependencies can save a long time building dependencies. An example configuration using (compiled) netlib is shown below anyway:: [dependencies] - ndarray = { version = "0.15.0", features = ["blas"] } - blas-src = { version = "0.8.0", default-features = false, features = ["netlib"] } + ndarray = { version = "0.16.0", features = ["blas"] } + blas-src = { version = "0.10.0", default-features = false, features = ["netlib"] } When this is done, your program must also link to ``blas_src`` by using it or explicitly including it in your code:: diff --git a/RELEASES.md b/RELEASES.md index 1d310a835..04c7c9250 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,5 @@ -Version 0.16.0 (Not yet released) -================================= +Version 0.16.0 (2024-08-03) +=========================== Featured Changes ---------------- @@ -11,6 +11,13 @@ Featured Changes New Features and Improvements ----------------------------- +- Check for aliasing in `RawViewMut::from_shape_ptr` with a debug assertion by [@bluss](https://github.com/bluss) [#1413](https://github.com/rust-ndarray/ndarray/pull/1413) +- Allow aliasing in ArrayView::from_shape by [@bluss](https://github.com/bluss) [#1410](https://github.com/rust-ndarray/ndarray/pull/1410) +- Remove deprecations from 0.15.x by [@bluss](https://github.com/bluss) [#1409](https://github.com/rust-ndarray/ndarray/pull/1409) +- Make `CowArray` an owned storage array, require Clone bound for `into_shared` by [@jturner314](https://github.com/jturner314) [#1028](https://github.com/rust-ndarray/ndarray/pull/1028) +- Change `NdProducer::Dim` of `axis_windows()` to `Ix1` by [@jonasBoss](https://github.com/jonasBoss) [#1305](https://github.com/rust-ndarray/ndarray/pull/1305) +- Add `squeeze()` to dynamic dimension arrays by [@barakugav](https://github.com/barakugav) [#1396](https://github.com/rust-ndarray/ndarray/pull/1396) +- Add `flatten`, `flatten_with_order` and `into_flat` to arrays by [@barakugav](https://github.com/barakugav) [#1397](https://github.com/rust-ndarray/ndarray/pull/1397) - Make compatible with thumbv6m-none-eabi by [@BjornTheProgrammer](https://github.com/BjornTheProgrammer) [#1384](https://github.com/rust-ndarray/ndarray/pull/1384) - `is_unique` for `ArcArray` by [@daniellga](https://github.com/daniellga) [#1399](https://github.com/rust-ndarray/ndarray/pull/1399) - Add `triu` and `tril` methods directly to ArrayBase by [@akern40](https://github.com/akern40) [#1386](https://github.com/rust-ndarray/ndarray/pull/1386) @@ -50,6 +57,7 @@ New Features and Improvements Tests, CI and Maintainer tasks ------------------------------ +- CI: require rustfmt, nostd by [@bluss](https://github.com/bluss) [#1411](https://github.com/rust-ndarray/ndarray/pull/1411) - Prepare changelog for 0.16.0 by [@bluss](https://github.com/bluss) [#1401](https://github.com/rust-ndarray/ndarray/pull/1401) - Organize dependencies with workspace = true (cont.) by [@bluss](https://github.com/bluss) [#1407](https://github.com/rust-ndarray/ndarray/pull/1407) - Update to use dep: for features by [@bluss](https://github.com/bluss) [#1406](https://github.com/rust-ndarray/ndarray/pull/1406) From b592995181dcbe11a2a2746edc61540cf480af4a Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 3 Aug 2024 19:43:42 +0200 Subject: [PATCH 618/651] Bump to ndarray 0.16 and ndarray-rand 0.15 --- Cargo.toml | 4 ++-- ndarray-rand/Cargo.toml | 2 +- ndarray-rand/RELEASES.md | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f1a7d4287..4c34a11bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ndarray" -version = "0.15.6" +version = "0.16.0" edition = "2018" rust-version = "1.64" authors = [ @@ -91,7 +91,7 @@ default-members = [ ] [workspace.dependencies] -ndarray = { version = "0.15", path = "." } +ndarray = { version = "0.16", path = "." } ndarray-rand = { path = "ndarray-rand" } num-integer = { version = "0.1.39", default-features = false } diff --git a/ndarray-rand/Cargo.toml b/ndarray-rand/Cargo.toml index 2824dbd07..b58e752a5 100644 --- a/ndarray-rand/Cargo.toml +++ b/ndarray-rand/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ndarray-rand" -version = "0.14.0" +version = "0.15.0" edition = "2018" authors = ["bluss"] license = "MIT OR Apache-2.0" diff --git a/ndarray-rand/RELEASES.md b/ndarray-rand/RELEASES.md index 8d7586fbe..cff9acd96 100644 --- a/ndarray-rand/RELEASES.md +++ b/ndarray-rand/RELEASES.md @@ -1,6 +1,11 @@ Recent Changes -------------- +- 0.15.0 + + - Require ndarray 0.16 + - Remove deprecated F32 by [@bluss](https://github.com/bluss) [#1409](https://github.com/rust-ndarray/ndarray/pull/1409) + - 0.14.0 - Require ndarray 0.15 From a9605dc251776985f1e6a49f12f91b61dbe9daa1 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 6 Aug 2024 12:01:51 +0200 Subject: [PATCH 619/651] Make iterators covariant in element type The internal Baseiter type underlies most of the ndarray iterators, and it used `*mut A` for element type A. Update it to use `NonNull` which behaves identically except it's guaranteed to be non-null and is covariant w.r.t the parameter A. Add compile test from the issue. Fixes #1290 --- src/impl_owned_array.rs | 4 ++-- src/impl_views/conversions.rs | 8 ++++---- src/iterators/into_iter.rs | 5 ++--- src/iterators/mod.rs | 23 ++++++++++++++--------- tests/iterators.rs | 34 +++++++++++++++++++++++++++++++--- 5 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index db176210c..97ed43a47 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -3,7 +3,7 @@ use alloc::vec::Vec; use std::mem; use std::mem::MaybeUninit; -#[allow(unused_imports)] +#[allow(unused_imports)] // Needed for Rust 1.64 use rawpointer::PointerExt; use crate::imp_prelude::*; @@ -907,7 +907,7 @@ where D: Dimension // iter is a raw pointer iterator traversing the array in memory order now with the // sorted axes. - let mut iter = Baseiter::new(self_.ptr.as_ptr(), self_.dim, self_.strides); + let mut iter = Baseiter::new(self_.ptr, self_.dim, self_.strides); let mut dropped_elements = 0; let mut last_ptr = data_ptr; diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs index ef6923a56..1dd7d97f2 100644 --- a/src/impl_views/conversions.rs +++ b/src/impl_views/conversions.rs @@ -199,7 +199,7 @@ where D: Dimension #[inline] pub(crate) fn into_base_iter(self) -> Baseiter { - unsafe { Baseiter::new(self.ptr.as_ptr(), self.dim, self.strides) } + unsafe { Baseiter::new(self.ptr, self.dim, self.strides) } } } @@ -209,7 +209,7 @@ where D: Dimension #[inline] pub(crate) fn into_base_iter(self) -> Baseiter { - unsafe { Baseiter::new(self.ptr.as_ptr(), self.dim, self.strides) } + unsafe { Baseiter::new(self.ptr, self.dim, self.strides) } } } @@ -220,7 +220,7 @@ where D: Dimension #[inline] pub(crate) fn into_base_iter(self) -> Baseiter { - unsafe { Baseiter::new(self.ptr.as_ptr(), self.dim, self.strides) } + unsafe { Baseiter::new(self.ptr, self.dim, self.strides) } } #[inline] @@ -262,7 +262,7 @@ where D: Dimension #[inline] pub(crate) fn into_base_iter(self) -> Baseiter { - unsafe { Baseiter::new(self.ptr.as_ptr(), self.dim, self.strides) } + unsafe { Baseiter::new(self.ptr, self.dim, self.strides) } } #[inline] diff --git a/src/iterators/into_iter.rs b/src/iterators/into_iter.rs index fcc2e4b8c..e03c642ba 100644 --- a/src/iterators/into_iter.rs +++ b/src/iterators/into_iter.rs @@ -33,16 +33,15 @@ impl IntoIter where D: Dimension { /// Create a new by-value iterator that consumes `array` - pub(crate) fn new(mut array: Array) -> Self + pub(crate) fn new(array: Array) -> Self { unsafe { let array_head_ptr = array.ptr; - let ptr = array.as_mut_ptr(); let mut array_data = array.data; let data_len = array_data.release_all_elements(); debug_assert!(data_len >= array.dim.size()); let has_unreachable_elements = array.dim.size() != data_len; - let inner = Baseiter::new(ptr, array.dim, array.strides); + let inner = Baseiter::new(array_head_ptr, array.dim, array.strides); IntoIter { array_data, diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index d49ffe2d0..6978117ca 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -19,6 +19,10 @@ use alloc::vec::Vec; use std::iter::FromIterator; use std::marker::PhantomData; use std::ptr; +use std::ptr::NonNull; + +#[allow(unused_imports)] // Needed for Rust 1.64 +use rawpointer::PointerExt; use crate::Ix1; @@ -38,7 +42,7 @@ use std::slice::{self, Iter as SliceIter, IterMut as SliceIterMut}; #[derive(Debug)] pub struct Baseiter { - ptr: *mut A, + ptr: NonNull, dim: D, strides: D, index: Option, @@ -50,7 +54,7 @@ impl Baseiter /// to be correct to avoid performing an unsafe pointer offset while /// iterating. #[inline] - pub unsafe fn new(ptr: *mut A, len: D, stride: D) -> Baseiter + pub unsafe fn new(ptr: NonNull, len: D, stride: D) -> Baseiter { Baseiter { ptr, @@ -74,7 +78,7 @@ impl Iterator for Baseiter }; let offset = D::stride_offset(&index, &self.strides); self.index = self.dim.next_for(index); - unsafe { Some(self.ptr.offset(offset)) } + unsafe { Some(self.ptr.offset(offset).as_ptr()) } } fn size_hint(&self) -> (usize, Option) @@ -99,7 +103,7 @@ impl Iterator for Baseiter let mut i = 0; let i_end = len - elem_index; while i < i_end { - accum = g(accum, row_ptr.offset(i as isize * stride)); + accum = g(accum, row_ptr.offset(i as isize * stride).as_ptr()); i += 1; } } @@ -140,12 +144,12 @@ impl DoubleEndedIterator for Baseiter Some(ix) => ix, }; self.dim[0] -= 1; - let offset = <_>::stride_offset(&self.dim, &self.strides); + let offset = Ix1::stride_offset(&self.dim, &self.strides); if index == self.dim { self.index = None; } - unsafe { Some(self.ptr.offset(offset)) } + unsafe { Some(self.ptr.offset(offset).as_ptr()) } } fn nth_back(&mut self, n: usize) -> Option<*mut A> @@ -154,11 +158,11 @@ impl DoubleEndedIterator for Baseiter let len = self.dim[0] - index[0]; if n < len { self.dim[0] -= n + 1; - let offset = <_>::stride_offset(&self.dim, &self.strides); + let offset = Ix1::stride_offset(&self.dim, &self.strides); if index == self.dim { self.index = None; } - unsafe { Some(self.ptr.offset(offset)) } + unsafe { Some(self.ptr.offset(offset).as_ptr()) } } else { self.index = None; None @@ -178,7 +182,8 @@ impl DoubleEndedIterator for Baseiter accum = g( accum, self.ptr - .offset(Ix1::stride_offset(&self.dim, &self.strides)), + .offset(Ix1::stride_offset(&self.dim, &self.strides)) + .as_ptr(), ); } } diff --git a/tests/iterators.rs b/tests/iterators.rs index 23175fd40..908b64d15 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -1,6 +1,4 @@ -#![allow( - clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names -)] +#![allow(clippy::deref_addrof, clippy::unreadable_literal)] use ndarray::prelude::*; use ndarray::{arr3, indices, s, Slice, Zip}; @@ -1055,3 +1053,33 @@ impl Drop for DropCount<'_> self.drops.set(self.drops.get() + 1); } } + +#[test] +fn test_impl_iter_compiles() +{ + // Requires that the iterators are covariant in the element type + + // base case: std + fn slice_iter_non_empty_indices<'s, 'a>(array: &'a Vec<&'s str>) -> impl Iterator + 'a + { + array + .iter() + .enumerate() + .filter(|(_index, elem)| !elem.is_empty()) + .map(|(index, _elem)| index) + } + + let _ = slice_iter_non_empty_indices; + + // ndarray case + fn array_iter_non_empty_indices<'s, 'a>(array: &'a Array<&'s str, Ix1>) -> impl Iterator + 'a + { + array + .iter() + .enumerate() + .filter(|(_index, elem)| !elem.is_empty()) + .map(|(index, _elem)| index) + } + + let _ = array_iter_non_empty_indices; +} From 00e15460ad6d99077f882e92857a9b431478244e Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 6 Aug 2024 12:26:23 +0200 Subject: [PATCH 620/651] Convert Baseiter to use NonNull throughout Complete the transition to using NonNull as the raw pointer type by using it as Baseiter's iterator element type. --- src/data_repr.rs | 5 ---- src/impl_owned_array.rs | 14 +++++----- src/iterators/chunks.rs | 4 +-- src/iterators/into_iter.rs | 4 +-- src/iterators/mod.rs | 55 +++++++++++++++++++++----------------- src/iterators/windows.rs | 2 +- 6 files changed, 43 insertions(+), 41 deletions(-) diff --git a/src/data_repr.rs b/src/data_repr.rs index a24cd7789..4041c192b 100644 --- a/src/data_repr.rs +++ b/src/data_repr.rs @@ -59,11 +59,6 @@ impl OwnedRepr self.ptr.as_ptr() } - pub(crate) fn as_ptr_mut(&self) -> *mut A - { - self.ptr.as_ptr() - } - pub(crate) fn as_nonnull_mut(&mut self) -> NonNull { self.ptr diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 97ed43a47..44ac12dd4 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -1,5 +1,6 @@ #[cfg(not(feature = "std"))] use alloc::vec::Vec; +use core::ptr::NonNull; use std::mem; use std::mem::MaybeUninit; @@ -435,7 +436,7 @@ where D: Dimension // "deconstruct" self; the owned repr releases ownership of all elements and we // carry on with raw view methods let data_len = self.data.len(); - let data_ptr = self.data.as_nonnull_mut().as_ptr(); + let data_ptr = self.data.as_nonnull_mut(); unsafe { // Safety: self.data releases ownership of the elements. Any panics below this point @@ -866,8 +867,9 @@ where D: Dimension /// /// This is an internal function for use by move_into and IntoIter only, safety invariants may need /// to be upheld across the calls from those implementations. -pub(crate) unsafe fn drop_unreachable_raw(mut self_: RawArrayViewMut, data_ptr: *mut A, data_len: usize) -where D: Dimension +pub(crate) unsafe fn drop_unreachable_raw( + mut self_: RawArrayViewMut, data_ptr: NonNull, data_len: usize, +) where D: Dimension { let self_len = self_.len(); @@ -878,7 +880,7 @@ where D: Dimension } sort_axes_in_default_order(&mut self_); // with uninverted axes this is now the element with lowest address - let array_memory_head_ptr = self_.ptr.as_ptr(); + let array_memory_head_ptr = self_.ptr; let data_end_ptr = data_ptr.add(data_len); debug_assert!(data_ptr <= array_memory_head_ptr); debug_assert!(array_memory_head_ptr <= data_end_ptr); @@ -917,7 +919,7 @@ where D: Dimension // should now be dropped. This interval may be empty, then we just skip this loop. while last_ptr != elem_ptr { debug_assert!(last_ptr < data_end_ptr); - std::ptr::drop_in_place(last_ptr); + std::ptr::drop_in_place(last_ptr.as_mut()); last_ptr = last_ptr.add(1); dropped_elements += 1; } @@ -926,7 +928,7 @@ where D: Dimension } while last_ptr < data_end_ptr { - std::ptr::drop_in_place(last_ptr); + std::ptr::drop_in_place(last_ptr.as_mut()); last_ptr = last_ptr.add(1); dropped_elements += 1; } diff --git a/src/iterators/chunks.rs b/src/iterators/chunks.rs index 465428968..9e2f08e1e 100644 --- a/src/iterators/chunks.rs +++ b/src/iterators/chunks.rs @@ -204,7 +204,7 @@ impl_iterator! { fn item(&mut self, ptr) { unsafe { - ArrayView::new_( + ArrayView::new( ptr, self.chunk.clone(), self.inner_strides.clone()) @@ -226,7 +226,7 @@ impl_iterator! { fn item(&mut self, ptr) { unsafe { - ArrayViewMut::new_( + ArrayViewMut::new( ptr, self.chunk.clone(), self.inner_strides.clone()) diff --git a/src/iterators/into_iter.rs b/src/iterators/into_iter.rs index e03c642ba..9374608cb 100644 --- a/src/iterators/into_iter.rs +++ b/src/iterators/into_iter.rs @@ -61,7 +61,7 @@ impl Iterator for IntoIter #[inline] fn next(&mut self) -> Option { - self.inner.next().map(|p| unsafe { p.read() }) + self.inner.next().map(|p| unsafe { p.as_ptr().read() }) } fn size_hint(&self) -> (usize, Option) @@ -91,7 +91,7 @@ where D: Dimension while let Some(_) = self.next() {} unsafe { - let data_ptr = self.array_data.as_ptr_mut(); + let data_ptr = self.array_data.as_nonnull_mut(); let view = RawArrayViewMut::new(self.array_head_ptr, self.inner.dim.clone(), self.inner.strides.clone()); debug_assert!(self.inner.dim.size() < self.data_len, "data_len {} and dim size {}", self.data_len, self.inner.dim.size()); diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 6978117ca..e7321d15b 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -38,7 +38,7 @@ use std::slice::{self, Iter as SliceIter, IterMut as SliceIterMut}; /// Base for iterators over all axes. /// -/// Iterator element type is `*mut A`. +/// Iterator element type is `NonNull`. #[derive(Debug)] pub struct Baseiter { @@ -67,10 +67,10 @@ impl Baseiter impl Iterator for Baseiter { - type Item = *mut A; + type Item = NonNull; #[inline] - fn next(&mut self) -> Option<*mut A> + fn next(&mut self) -> Option { let index = match self.index { None => return None, @@ -78,7 +78,7 @@ impl Iterator for Baseiter }; let offset = D::stride_offset(&index, &self.strides); self.index = self.dim.next_for(index); - unsafe { Some(self.ptr.offset(offset).as_ptr()) } + unsafe { Some(self.ptr.offset(offset)) } } fn size_hint(&self) -> (usize, Option) @@ -88,7 +88,7 @@ impl Iterator for Baseiter } fn fold(mut self, init: Acc, mut g: G) -> Acc - where G: FnMut(Acc, *mut A) -> Acc + where G: FnMut(Acc, Self::Item) -> Acc { let ndim = self.dim.ndim(); debug_assert_ne!(ndim, 0); @@ -103,7 +103,7 @@ impl Iterator for Baseiter let mut i = 0; let i_end = len - elem_index; while i < i_end { - accum = g(accum, row_ptr.offset(i as isize * stride).as_ptr()); + accum = g(accum, row_ptr.offset(i as isize * stride)); i += 1; } } @@ -137,7 +137,7 @@ impl ExactSizeIterator for Baseiter impl DoubleEndedIterator for Baseiter { #[inline] - fn next_back(&mut self) -> Option<*mut A> + fn next_back(&mut self) -> Option { let index = match self.index { None => return None, @@ -149,10 +149,10 @@ impl DoubleEndedIterator for Baseiter self.index = None; } - unsafe { Some(self.ptr.offset(offset).as_ptr()) } + unsafe { Some(self.ptr.offset(offset)) } } - fn nth_back(&mut self, n: usize) -> Option<*mut A> + fn nth_back(&mut self, n: usize) -> Option { let index = self.index?; let len = self.dim[0] - index[0]; @@ -162,7 +162,7 @@ impl DoubleEndedIterator for Baseiter if index == self.dim { self.index = None; } - unsafe { Some(self.ptr.offset(offset).as_ptr()) } + unsafe { Some(self.ptr.offset(offset)) } } else { self.index = None; None @@ -170,7 +170,7 @@ impl DoubleEndedIterator for Baseiter } fn rfold(mut self, init: Acc, mut g: G) -> Acc - where G: FnMut(Acc, *mut A) -> Acc + where G: FnMut(Acc, Self::Item) -> Acc { let mut accum = init; if let Some(index) = self.index { @@ -182,8 +182,7 @@ impl DoubleEndedIterator for Baseiter accum = g( accum, self.ptr - .offset(Ix1::stride_offset(&self.dim, &self.strides)) - .as_ptr(), + .offset(Ix1::stride_offset(&self.dim, &self.strides)), ); } } @@ -231,7 +230,7 @@ impl<'a, A, D: Dimension> Iterator for ElementsBase<'a, A, D> #[inline] fn next(&mut self) -> Option<&'a A> { - self.inner.next().map(|p| unsafe { &*p }) + self.inner.next().map(|p| unsafe { p.as_ref() }) } fn size_hint(&self) -> (usize, Option) @@ -242,7 +241,7 @@ impl<'a, A, D: Dimension> Iterator for ElementsBase<'a, A, D> fn fold(self, init: Acc, mut g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc { - unsafe { self.inner.fold(init, move |acc, ptr| g(acc, &*ptr)) } + unsafe { self.inner.fold(init, move |acc, ptr| g(acc, ptr.as_ref())) } } } @@ -251,13 +250,13 @@ impl<'a, A> DoubleEndedIterator for ElementsBase<'a, A, Ix1> #[inline] fn next_back(&mut self) -> Option<&'a A> { - self.inner.next_back().map(|p| unsafe { &*p }) + self.inner.next_back().map(|p| unsafe { p.as_ref() }) } fn rfold(self, init: Acc, mut g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc { - unsafe { self.inner.rfold(init, move |acc, ptr| g(acc, &*ptr)) } + unsafe { self.inner.rfold(init, move |acc, ptr| g(acc, ptr.as_ref())) } } } @@ -651,7 +650,7 @@ impl<'a, A, D: Dimension> Iterator for ElementsBaseMut<'a, A, D> #[inline] fn next(&mut self) -> Option<&'a mut A> { - self.inner.next().map(|p| unsafe { &mut *p }) + self.inner.next().map(|mut p| unsafe { p.as_mut() }) } fn size_hint(&self) -> (usize, Option) @@ -662,7 +661,10 @@ impl<'a, A, D: Dimension> Iterator for ElementsBaseMut<'a, A, D> fn fold(self, init: Acc, mut g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc { - unsafe { self.inner.fold(init, move |acc, ptr| g(acc, &mut *ptr)) } + unsafe { + self.inner + .fold(init, move |acc, mut ptr| g(acc, ptr.as_mut())) + } } } @@ -671,13 +673,16 @@ impl<'a, A> DoubleEndedIterator for ElementsBaseMut<'a, A, Ix1> #[inline] fn next_back(&mut self) -> Option<&'a mut A> { - self.inner.next_back().map(|p| unsafe { &mut *p }) + self.inner.next_back().map(|mut p| unsafe { p.as_mut() }) } fn rfold(self, init: Acc, mut g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc { - unsafe { self.inner.rfold(init, move |acc, ptr| g(acc, &mut *ptr)) } + unsafe { + self.inner + .rfold(init, move |acc, mut ptr| g(acc, ptr.as_mut())) + } } } @@ -753,7 +758,7 @@ where D: Dimension { self.iter .next() - .map(|ptr| unsafe { ArrayView::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) }) + .map(|ptr| unsafe { ArrayView::new(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) }) } fn size_hint(&self) -> (usize, Option) @@ -777,7 +782,7 @@ impl<'a, A> DoubleEndedIterator for LanesIter<'a, A, Ix1> { self.iter .next_back() - .map(|ptr| unsafe { ArrayView::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) }) + .map(|ptr| unsafe { ArrayView::new(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) }) } } @@ -805,7 +810,7 @@ where D: Dimension { self.iter .next() - .map(|ptr| unsafe { ArrayViewMut::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) }) + .map(|ptr| unsafe { ArrayViewMut::new(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) }) } fn size_hint(&self) -> (usize, Option) @@ -829,7 +834,7 @@ impl<'a, A> DoubleEndedIterator for LanesIterMut<'a, A, Ix1> { self.iter .next_back() - .map(|ptr| unsafe { ArrayViewMut::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) }) + .map(|ptr| unsafe { ArrayViewMut::new(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) }) } } diff --git a/src/iterators/windows.rs b/src/iterators/windows.rs index 453ef5024..1c2ab6a85 100644 --- a/src/iterators/windows.rs +++ b/src/iterators/windows.rs @@ -115,7 +115,7 @@ impl_iterator! { fn item(&mut self, ptr) { unsafe { - ArrayView::new_( + ArrayView::new( ptr, self.window.clone(), self.strides.clone()) From 7e5762b984faa4ecf25520eb92ec4ef53126f73a Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Thu, 8 Aug 2024 18:22:19 +0200 Subject: [PATCH 621/651] ci: Run all checks as pull request tests --- .github/workflows/ci.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 88c9a6c2d..5a2807284 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,6 +1,9 @@ on: pull_request: merge_group: + push: + branches: + - master name: Continuous integration @@ -86,7 +89,7 @@ jobs: - run: ./scripts/all-tests.sh "$FEATURES" ${{ matrix.rust }} cross_test: - if: ${{ github.event_name == 'merge_group' }} + #if: ${{ github.event_name == 'merge_group' }} runs-on: ubuntu-latest strategy: matrix: @@ -110,7 +113,7 @@ jobs: - run: ./scripts/cross-tests.sh "docs" ${{ matrix.rust }} ${{ matrix.target }} cargo-careful: - if: ${{ github.event_name == 'merge_group' }} + #if: ${{ github.event_name == 'merge_group' }} runs-on: ubuntu-latest name: cargo-careful steps: @@ -124,7 +127,7 @@ jobs: - run: cargo careful test -Zcareful-sanitizer --features="$FEATURES" docs: - if: ${{ github.event_name == 'merge_group' }} + #if: ${{ github.event_name == 'merge_group' }} runs-on: ubuntu-latest strategy: matrix: From 6a8fb964d9152bfa108a7fba5d41b5a060f8b36c Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Thu, 8 Aug 2024 18:24:08 +0200 Subject: [PATCH 622/651] ci: Check for warnings in cargo doc --- .github/workflows/ci.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5a2807284..f36591741 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -134,12 +134,14 @@ jobs: rust: - stable name: docs/${{ matrix.rust }} + env: + RUSTDOCFLAGS: "-Dwarnings" steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - - run: cargo doc + - run: cargo doc --no-deps --all-features conclusion: needs: From 2d258bc605cdde8cde516e471a997b5f9c713b7a Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Thu, 8 Aug 2024 18:28:49 +0200 Subject: [PATCH 623/651] Fix rustdoc warnings everywhere --- src/dimension/broadcast.rs | 4 ++-- src/doc/crate_feature_flags.rs | 3 +++ src/doc/ndarray_for_numpy_users/mod.rs | 4 +++- src/doc/ndarray_for_numpy_users/rk_step.rs | 1 + src/slice.rs | 3 +++ 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/dimension/broadcast.rs b/src/dimension/broadcast.rs index d277cfea2..fb9fc1a0c 100644 --- a/src/dimension/broadcast.rs +++ b/src/dimension/broadcast.rs @@ -41,8 +41,8 @@ pub trait DimMax } /// Dimensions of the same type remain unchanged when co_broadcast. -/// So you can directly use D as the resulting type. -/// (Instead of >::BroadcastOutput) +/// So you can directly use `D` as the resulting type. +/// (Instead of `>::BroadcastOutput`) impl DimMax for D { type Output = D; diff --git a/src/doc/crate_feature_flags.rs b/src/doc/crate_feature_flags.rs index c0fc4c0f5..fc2c2bd49 100644 --- a/src/doc/crate_feature_flags.rs +++ b/src/doc/crate_feature_flags.rs @@ -30,3 +30,6 @@ //! - Enable the ``threading`` feature in the matrixmultiply package //! //! [`parallel`]: crate::parallel + +#[cfg(doc)] +use crate::parallel::par_azip; diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index 5ac15e300..eba96cdd0 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -654,7 +654,7 @@ //! convert `f32` array to `i32` array with ["saturating" conversion][sat_conv]; care needed because it can be a lossy conversion or result in non-finite values! See [the reference for information][as_typecast]. //! //!

(&self, to: P) where S: Data, @@ -2631,6 +2667,7 @@ where /// If their shapes disagree, `rhs` is broadcast to the shape of `self`. /// /// **Panics** if broadcasting isn’t possible. + #[track_caller] #[inline] pub fn zip_mut_with(&mut self, rhs: &ArrayBase, f: F) where @@ -2892,6 +2929,7 @@ where /// Return the result as an `Array`. /// /// **Panics** if `axis` is out of bounds. + #[track_caller] pub fn fold_axis(&self, axis: Axis, init: B, mut fold: F) -> Array where D: RemoveAxis, @@ -2914,6 +2952,7 @@ where /// Return the result as an `Array`. /// /// **Panics** if `axis` is out of bounds. + #[track_caller] pub fn map_axis<'a, B, F>(&'a self, axis: Axis, mut mapping: F) -> Array where D: RemoveAxis, @@ -2939,6 +2978,7 @@ where /// Return the result as an `Array`. /// /// **Panics** if `axis` is out of bounds. + #[track_caller] pub fn map_axis_mut<'a, B, F>(&'a mut self, axis: Axis, mut mapping: F) -> Array where D: RemoveAxis, @@ -3042,6 +3082,7 @@ where /// using regular transmute in some cases. /// /// **Panics** if the size of A and B are different. +#[track_caller] #[inline] unsafe fn unlimited_transmute(data: A) -> B { // safe when sizes are equal and caller guarantees that representations are equal diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 9eacd6b69..8d02364d1 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -72,6 +72,7 @@ where E: Dimension, { type Output = ArrayBase>::Output>; + #[track_caller] fn $mth(self, rhs: ArrayBase) -> Self::Output { self.$mth(&rhs) @@ -99,6 +100,7 @@ where E: Dimension, { type Output = ArrayBase>::Output>; + #[track_caller] fn $mth(self, rhs: &ArrayBase) -> Self::Output { if self.ndim() == rhs.ndim() && self.shape() == rhs.shape() { @@ -139,6 +141,7 @@ where E: Dimension + DimMax, { type Output = ArrayBase>::Output>; + #[track_caller] fn $mth(self, rhs: ArrayBase) -> Self::Output where { @@ -178,6 +181,7 @@ where E: Dimension, { type Output = Array>::Output>; + #[track_caller] fn $mth(self, rhs: &'a ArrayBase) -> Self::Output { let (lhs, rhs) = if self.ndim() == rhs.ndim() && self.shape() == rhs.shape() { let lhs = self.view().into_dimensionality::<>::Output>().unwrap(); @@ -447,6 +451,7 @@ mod assign_ops { D: Dimension, E: Dimension, { + #[track_caller] fn $method(&mut self, rhs: &ArrayBase) { self.zip_mut_with(rhs, |x, y| { x.$method(y.clone()); diff --git a/src/impl_raw_views.rs b/src/impl_raw_views.rs index 64a5c4c8d..9f026318c 100644 --- a/src/impl_raw_views.rs +++ b/src/impl_raw_views.rs @@ -106,6 +106,7 @@ where /// before the split and one array pointer after the split. /// /// **Panics** if `axis` or `index` is out of bounds. + #[track_caller] pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { assert!(index <= self.len_of(axis)); let left_ptr = self.ptr.as_ptr(); @@ -139,6 +140,7 @@ where /// While this method is safe, for the same reason as regular raw pointer /// casts are safe, access through the produced raw view is only possible /// in an unsafe block or function. + #[track_caller] pub fn cast(self) -> RawArrayView { assert_eq!( mem::size_of::(), @@ -266,9 +268,9 @@ where /// address by moving along all axes must not exceed `isize::MAX`. This /// constraint prevents overflow when calculating the `count` parameter to /// [`.offset()`] regardless of the starting point due to past offsets. - /// - /// * The product of non-zero axis lengths must not exceed `isize::MAX`. /// + /// * The product of non-zero axis lengths must not exceed `isize::MAX`. + /// /// * Strides must be non-negative. /// /// This function can use debug assertions to check some of these requirements, @@ -338,6 +340,7 @@ where /// before the split and one array pointer after the split. /// /// **Panics** if `axis` or `index` is out of bounds. + #[track_caller] pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { let (left, right) = self.into_raw_view().split_at(axis, index); unsafe { @@ -358,6 +361,7 @@ where /// While this method is safe, for the same reason as regular raw pointer /// casts are safe, access through the produced raw view is only possible /// in an unsafe block or function. + #[track_caller] pub fn cast(self) -> RawArrayViewMut { assert_eq!( mem::size_of::(), diff --git a/src/impl_views/indexing.rs b/src/impl_views/indexing.rs index c30f97e01..3494b91db 100644 --- a/src/impl_views/indexing.rs +++ b/src/impl_views/indexing.rs @@ -62,6 +62,7 @@ pub trait IndexLonger { /// [1]: ArrayBase::get /// /// **Panics** if index is out of bounds. + #[track_caller] fn index(self, index: I) -> Self::Output; /// Get a reference of a element through the view. @@ -77,6 +78,7 @@ pub trait IndexLonger { /// [2]: ArrayBase::get_mut /// /// **Panics** if index is out of bounds. + #[track_caller] fn get(self, index: I) -> Option; /// Get a reference of a element through the view without boundary check @@ -116,6 +118,7 @@ where /// [1]: ArrayBase::get /// /// **Panics** if index is out of bounds. + #[track_caller] fn index(self, index: I) -> &'a A { debug_bounds_check!(self, index); unsafe { &*self.get_ptr(index).unwrap_or_else(|| array_out_of_bounds()) } @@ -161,6 +164,7 @@ where /// [1]: ArrayBase::get_mut /// /// **Panics** if index is out of bounds. + #[track_caller] fn index(mut self, index: I) -> &'a mut A { debug_bounds_check!(self, index); unsafe { diff --git a/src/impl_views/splitting.rs b/src/impl_views/splitting.rs index 028148dde..2ccc3ee91 100644 --- a/src/impl_views/splitting.rs +++ b/src/impl_views/splitting.rs @@ -88,6 +88,7 @@ where /// 0 1 2 3 4 indices → /// along Axis(1) /// ``` + #[track_caller] pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { unsafe { let (left, right) = self.into_raw_view().split_at(axis, index); @@ -136,6 +137,7 @@ where /// before the split and one mutable view after the split. /// /// **Panics** if `axis` or `index` is out of bounds. + #[track_caller] pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { unsafe { let (left, right) = self.into_raw_view_mut().split_at(axis, index); @@ -160,6 +162,7 @@ where /// * if any of the views would intersect (i.e. if any element would appear in multiple slices) /// * if an index is out of bounds or step size is zero /// * if `D` is `IxDyn` and `info` does not match the number of array axes + #[track_caller] pub fn multi_slice_move(self, info: M) -> M::Output where M: MultiSliceArg<'a, A, D>, diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 35b2d725f..3e1d6fe43 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -858,6 +858,7 @@ impl AxisIterCore { /// /// **Panics** if `index` is strictly greater than the iterator's remaining /// length. + #[track_caller] fn split_at(self, index: usize) -> (Self, Self) { assert!(index <= self.len()); let mid = self.index + index; @@ -989,6 +990,7 @@ impl<'a, A, D: Dimension> AxisIter<'a, A, D> { /// /// **Panics** if `index` is strictly greater than the iterator's remaining /// length. + #[track_caller] pub fn split_at(self, index: usize) -> (Self, Self) { let (left, right) = self.iter.split_at(index); ( @@ -1075,6 +1077,7 @@ impl<'a, A, D: Dimension> AxisIterMut<'a, A, D> { /// /// **Panics** if `index` is strictly greater than the iterator's remaining /// length. + #[track_caller] pub fn split_at(self, index: usize) -> (Self, Self) { let (left, right) = self.iter.split_at(index); ( @@ -1273,6 +1276,7 @@ clone_bounds!( /// the number of chunks, and the shape of the last chunk. /// /// **Panics** if `size == 0`. +#[track_caller] fn chunk_iter_parts( v: ArrayView<'_, A, D>, axis: Axis, @@ -1359,6 +1363,7 @@ macro_rules! chunk_iter_impl { /// /// **Panics** if `index` is strictly greater than the iterator's remaining /// length. + #[track_caller] pub fn split_at(self, index: usize) -> (Self, Self) { let (left, right) = self.iter.split_at(index); ( diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index 6a4542edd..dc8ef3428 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -65,6 +65,7 @@ where /// **Panics** if the array shapes are incompatible.
/// *Note:* If enabled, uses blas `dot` for elements of `f32, f64` when memory /// layout allows. + #[track_caller] pub fn dot(&self, rhs: &Rhs) -> >::Output where Self: Dot, @@ -191,6 +192,7 @@ where /// **Panics** if the arrays are not of the same length.
/// *Note:* If enabled, uses blas `dot` for elements of `f32, f64` when memory /// layout allows. + #[track_caller] fn dot(&self, rhs: &ArrayBase) -> A { self.dot_impl(rhs) } @@ -213,6 +215,7 @@ where /// Return a result array with shape *N*. /// /// **Panics** if shapes are incompatible. + #[track_caller] fn dot(&self, rhs: &ArrayBase) -> Array { rhs.t().dot(self) } @@ -251,6 +254,7 @@ where /// [2., 3.]]) /// ); /// ``` + #[track_caller] pub fn dot(&self, rhs: &Rhs) -> >::Output where Self: Dot, @@ -326,6 +330,7 @@ where A: LinalgScalar, { type Output = Array; + #[track_caller] fn dot(&self, rhs: &ArrayBase) -> Array { let ((m, a), n) = (self.dim(), rhs.dim()); if a != n { @@ -353,6 +358,7 @@ where /// If their shapes disagree, `rhs` is broadcast to the shape of `self`. /// /// **Panics** if broadcasting isn’t possible. + #[track_caller] pub fn scaled_add(&mut self, alpha: A, rhs: &ArrayBase) where S: DataMut, @@ -629,6 +635,7 @@ fn mat_mul_general
( /// *Note:* If enabled, uses blas `gemm` for elements of `f32, f64` when memory /// layout allows. The default matrixmultiply backend is otherwise used for /// `f32, f64` for all memory layouts. +#[track_caller] pub fn general_mat_mul( alpha: A, a: &ArrayBase, @@ -660,6 +667,7 @@ pub fn general_mat_mul( /// ***Panics*** if array shapes are not compatible
/// *Note:* If enabled, uses blas `gemv` for elements of `f32, f64` when memory /// layout allows. +#[track_caller] #[allow(clippy::collapsible_if)] pub fn general_mat_vec_mul( alpha: A, diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 0a720cb3e..55cd0cdfe 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -151,6 +151,7 @@ where /// let var = a.var(1.); /// assert_abs_diff_eq!(var, 6.7331, epsilon = 1e-4); /// ``` + #[track_caller] #[cfg(feature = "std")] pub fn var(&self, ddof: A) -> A where @@ -216,6 +217,7 @@ where /// let stddev = a.std(1.); /// assert_abs_diff_eq!(stddev, 2.59483, epsilon = 1e-4); /// ``` + #[track_caller] #[cfg(feature = "std")] pub fn std(&self, ddof: A) -> A where @@ -240,6 +242,7 @@ where /// ``` /// /// **Panics** if `axis` is out of bounds. + #[track_caller] pub fn sum_axis(&self, axis: Axis) -> Array where A: Clone + Zero + Add, @@ -276,6 +279,7 @@ where /// a.mean_axis(Axis(0)).unwrap().mean_axis(Axis(0)).unwrap() == aview0(&3.5) /// ); /// ``` + #[track_caller] pub fn mean_axis(&self, axis: Axis) -> Option> where A: Clone + Zero + FromPrimitive + Add + Div, @@ -334,6 +338,7 @@ where /// let var = a.var_axis(Axis(0), 1.); /// assert_eq!(var, aview1(&[4., 4.])); /// ``` + #[track_caller] #[cfg(feature = "std")] pub fn var_axis(&self, axis: Axis, ddof: A) -> Array where @@ -403,6 +408,7 @@ where /// let stddev = a.std_axis(Axis(0), 1.); /// assert_eq!(stddev, aview1(&[2., 2.])); /// ``` + #[track_caller] #[cfg(feature = "std")] pub fn std_axis(&self, axis: Axis, ddof: A) -> Array where diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 7c3f10b10..a94f74518 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -46,6 +46,7 @@ where /// Broadcast the array to the new dimensions `shape`. /// /// ***Panics*** if broadcasting isn’t possible. + #[track_caller] fn broadcast_unwrap(self, shape: E) -> Self::Output; private_decl! {} } @@ -270,6 +271,7 @@ where /// Return the length of `axis` /// /// ***Panics*** if `axis` is out of bounds. + #[track_caller] fn len_of(&self, axis: Axis) -> usize { self.dimension[axis.index()] } @@ -702,6 +704,7 @@ macro_rules! map_impl { /// Include the producer `p` in the Zip. /// /// ***Panics*** if `p`’s shape doesn’t match the Zip’s exactly. + #[track_caller] pub fn and

+//!
//! //! [as_conv]: https://doc.rust-lang.org/rust-by-example/types/cast.html //! [sat_conv]: https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#fixing-unsoundness-in-casts @@ -677,6 +677,8 @@ //! [.column()]: ArrayBase::column //! [.column_mut()]: ArrayBase::column_mut //! [concatenate()]: crate::concatenate() +//! [concatenate!]: crate::concatenate! +//! [stack!]: crate::stack! //! [::default()]: ArrayBase::default //! [.diag()]: ArrayBase::diag //! [.dim()]: ArrayBase::dim diff --git a/src/doc/ndarray_for_numpy_users/rk_step.rs b/src/doc/ndarray_for_numpy_users/rk_step.rs index 0448e0705..c882a3d00 100644 --- a/src/doc/ndarray_for_numpy_users/rk_step.rs +++ b/src/doc/ndarray_for_numpy_users/rk_step.rs @@ -169,6 +169,7 @@ //! ``` //! //! [`.scaled_add()`]: crate::ArrayBase::scaled_add +//! [`azip!()`]: crate::azip! //! //! ### SciPy license //! diff --git a/src/slice.rs b/src/slice.rs index 9e6acc449..e6c237a92 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -7,7 +7,10 @@ // except according to those terms. use crate::dimension::slices_intersect; use crate::error::{ErrorKind, ShapeError}; +#[cfg(doc)] +use crate::s; use crate::{ArrayViewMut, DimAdd, Dimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn}; + #[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::convert::TryFrom; From f07b2fe6f6b7c2bdc6a859f57ad1b51d6025eafd Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Thu, 8 Aug 2024 08:17:14 +0200 Subject: [PATCH 624/651] Set doc, doctest = false for test crates --- crates/blas-tests/Cargo.toml | 2 ++ crates/numeric-tests/Cargo.toml | 2 ++ crates/serialization-tests/Cargo.toml | 2 ++ 3 files changed, 6 insertions(+) diff --git a/crates/blas-tests/Cargo.toml b/crates/blas-tests/Cargo.toml index 33323ceac..0dbd9fd12 100644 --- a/crates/blas-tests/Cargo.toml +++ b/crates/blas-tests/Cargo.toml @@ -7,6 +7,8 @@ edition = "2018" [lib] test = false +doc = false +doctest = false [dependencies] ndarray = { workspace = true, features = ["approx"] } diff --git a/crates/numeric-tests/Cargo.toml b/crates/numeric-tests/Cargo.toml index 09fe14dbb..214612258 100644 --- a/crates/numeric-tests/Cargo.toml +++ b/crates/numeric-tests/Cargo.toml @@ -7,6 +7,8 @@ edition = "2018" [lib] test = false +doc = false +doctest = false [dependencies] ndarray = { workspace = true, features = ["approx"] } diff --git a/crates/serialization-tests/Cargo.toml b/crates/serialization-tests/Cargo.toml index 8e7056b88..be7c4c17b 100644 --- a/crates/serialization-tests/Cargo.toml +++ b/crates/serialization-tests/Cargo.toml @@ -7,6 +7,8 @@ edition = "2018" [lib] test = false +doc = false +doctest = false [dependencies] ndarray = { workspace = true, features = ["serde"] } From 9f1b35dd81cfd5341517259bc3ac65883fadf21a Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 7 Aug 2024 11:18:44 +0200 Subject: [PATCH 625/651] blas-tests: Fix to use blas feature Lost in the recent workspace refactor. --- crates/blas-tests/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/blas-tests/Cargo.toml b/crates/blas-tests/Cargo.toml index 0dbd9fd12..91c6daaa6 100644 --- a/crates/blas-tests/Cargo.toml +++ b/crates/blas-tests/Cargo.toml @@ -11,7 +11,7 @@ doc = false doctest = false [dependencies] -ndarray = { workspace = true, features = ["approx"] } +ndarray = { workspace = true, features = ["approx", "blas"] } blas-src = { version = "0.10", optional = true } openblas-src = { version = "0.10", optional = true } From 2ca801c1c3c66159acafb41fad9c7ad6fb375402 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 7 Aug 2024 11:18:44 +0200 Subject: [PATCH 626/651] ndarray-gen: Add simple internal interface for building matrices --- Cargo.toml | 2 + crates/ndarray-gen/Cargo.toml | 9 +++ crates/ndarray-gen/README.md | 4 + crates/ndarray-gen/src/array_builder.rs | 97 +++++++++++++++++++++++++ crates/ndarray-gen/src/lib.rs | 11 +++ 5 files changed, 123 insertions(+) create mode 100644 crates/ndarray-gen/Cargo.toml create mode 100644 crates/ndarray-gen/README.md create mode 100644 crates/ndarray-gen/src/array_builder.rs create mode 100644 crates/ndarray-gen/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 4c34a11bc..a19ae00a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,6 +85,7 @@ members = [ default-members = [ ".", "ndarray-rand", + "crates/ndarray-gen", "crates/numeric-tests", "crates/serialization-tests", # exclude blas-tests that depends on BLAS install @@ -93,6 +94,7 @@ default-members = [ [workspace.dependencies] ndarray = { version = "0.16", path = "." } ndarray-rand = { path = "ndarray-rand" } +ndarray-gen = { path = "crates/ndarray-gen" } num-integer = { version = "0.1.39", default-features = false } num-traits = { version = "0.2", default-features = false } diff --git a/crates/ndarray-gen/Cargo.toml b/crates/ndarray-gen/Cargo.toml new file mode 100644 index 000000000..f06adc48a --- /dev/null +++ b/crates/ndarray-gen/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ndarray-gen" +version = "0.1.0" +edition = "2018" +publish = false + +[dependencies] +ndarray = { workspace = true } +num-traits = { workspace = true } diff --git a/crates/ndarray-gen/README.md b/crates/ndarray-gen/README.md new file mode 100644 index 000000000..7dd02320c --- /dev/null +++ b/crates/ndarray-gen/README.md @@ -0,0 +1,4 @@ + +## ndarray-gen + +Array generation functions, used for testing. diff --git a/crates/ndarray-gen/src/array_builder.rs b/crates/ndarray-gen/src/array_builder.rs new file mode 100644 index 000000000..a021e5252 --- /dev/null +++ b/crates/ndarray-gen/src/array_builder.rs @@ -0,0 +1,97 @@ +// Copyright 2024 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ndarray::Array; +use ndarray::Dimension; +use ndarray::IntoDimension; +use ndarray::Order; + +use num_traits::Num; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct ArrayBuilder +{ + dim: D, + memory_order: Order, + generator: ElementGenerator, +} + +/// How to generate elements +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ElementGenerator +{ + Sequential, + Zero, +} + +impl Default for ArrayBuilder +{ + fn default() -> Self + { + Self::new(D::zeros(D::NDIM.unwrap_or(1))) + } +} + +impl ArrayBuilder +where D: Dimension +{ + pub fn new(dim: impl IntoDimension) -> Self + { + ArrayBuilder { + dim: dim.into_dimension(), + memory_order: Order::C, + generator: ElementGenerator::Sequential, + } + } + + pub fn memory_order(mut self, order: Order) -> Self + { + self.memory_order = order; + self + } + + pub fn generator(mut self, generator: ElementGenerator) -> Self + { + self.generator = generator; + self + } + + pub fn build(self) -> Array + where T: Num + Clone + { + let mut current = T::zero(); + let size = self.dim.size(); + let use_zeros = self.generator == ElementGenerator::Zero; + Array::from_iter((0..size).map(|_| { + let ret = current.clone(); + if !use_zeros { + current = ret.clone() + T::one(); + } + ret + })) + .into_shape_with_order((self.dim, self.memory_order)) + .unwrap() + } +} + +#[test] +fn test_order() +{ + let (m, n) = (12, 13); + let c = ArrayBuilder::new((m, n)) + .memory_order(Order::C) + .build::(); + let f = ArrayBuilder::new((m, n)) + .memory_order(Order::F) + .build::(); + + assert_eq!(c.shape(), &[m, n]); + assert_eq!(f.shape(), &[m, n]); + assert_eq!(c.strides(), &[n as isize, 1]); + assert_eq!(f.strides(), &[1, m as isize]); +} diff --git a/crates/ndarray-gen/src/lib.rs b/crates/ndarray-gen/src/lib.rs new file mode 100644 index 000000000..ceecf2fae --- /dev/null +++ b/crates/ndarray-gen/src/lib.rs @@ -0,0 +1,11 @@ +// Copyright 2024 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// Build ndarray arrays for test purposes + +pub mod array_builder; From 27e347c010896cb6e6dde5205af2fbec5c756694 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 7 Aug 2024 11:18:44 +0200 Subject: [PATCH 627/651] blas: Update layout logic for gemm We compute A B -> C with matrices A, B, C With the blas (cblas) interface it supports matrices that adhere to certain criteria. They should be contiguous on one dimension (stride=1). We glance a little at how numpy does this to try to catch all cases. In short, we accept A, B contiguous on either axis (row or column major). We use the case where C is (weakly) row major, but if it is column major we transpose A, B, C => A^t, B^t, C^t so that we are back to the C row major case. (Weakly = contiguous with stride=1 on that inner dimension, but stride for the other dimension can be larger; to differentiate from strictly whole array contiguous.) Minor change to the gemv function, no functional change, only updating due to the refactoring of blas layout functions. Fixes #1278 --- Cargo.toml | 4 +- crates/blas-tests/Cargo.toml | 2 + crates/blas-tests/tests/oper.rs | 53 +++-- src/linalg/impl_linalg.rs | 358 +++++++++++++++++++++----------- 4 files changed, 278 insertions(+), 139 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a19ae00a4..ac1960242 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ rawpointer = { version = "0.2" } defmac = "0.2" quickcheck = { workspace = true } approx = { workspace = true, default-features = true } -itertools = { version = "0.13.0", default-features = false, features = ["use_std"] } +itertools = { workspace = true } [features] default = ["std"] @@ -73,6 +73,7 @@ matrixmultiply-threading = ["matrixmultiply/threading"] portable-atomic-critical-section = ["portable-atomic/critical-section"] + [target.'cfg(not(target_has_atomic = "ptr"))'.dependencies] portable-atomic = { version = "1.6.0" } portable-atomic-util = { version = "0.2.0", features = [ "alloc" ] } @@ -103,6 +104,7 @@ 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" } +itertools = { version = "0.13.0", default-features = false, features = ["use_std"] } [profile.bench] debug = true diff --git a/crates/blas-tests/Cargo.toml b/crates/blas-tests/Cargo.toml index 91c6daaa6..05a656000 100644 --- a/crates/blas-tests/Cargo.toml +++ b/crates/blas-tests/Cargo.toml @@ -12,6 +12,7 @@ doctest = false [dependencies] ndarray = { workspace = true, features = ["approx", "blas"] } +ndarray-gen = { workspace = true } blas-src = { version = "0.10", optional = true } openblas-src = { version = "0.10", optional = true } @@ -23,6 +24,7 @@ defmac = "0.2" approx = { workspace = true } num-traits = { workspace = true } num-complex = { workspace = true } +itertools = { workspace = true } [features] # Just for making an example and to help testing, , multiple different possible diff --git a/crates/blas-tests/tests/oper.rs b/crates/blas-tests/tests/oper.rs index 3ed81915e..931aabea9 100644 --- a/crates/blas-tests/tests/oper.rs +++ b/crates/blas-tests/tests/oper.rs @@ -9,10 +9,13 @@ use ndarray::prelude::*; use ndarray::linalg::general_mat_mul; use ndarray::linalg::general_mat_vec_mul; +use ndarray::Order; use ndarray::{Data, Ix, LinalgScalar}; +use ndarray_gen::array_builder::ArrayBuilder; use approx::assert_relative_eq; use defmac::defmac; +use itertools::iproduct; use num_complex::Complex32; use num_complex::Complex64; @@ -243,7 +246,14 @@ fn gen_mat_mul() let sizes = vec![ (4, 4, 4), (8, 8, 8), - (17, 15, 16), + (10, 10, 10), + (8, 8, 1), + (1, 10, 10), + (10, 1, 10), + (10, 10, 1), + (1, 10, 1), + (10, 1, 1), + (1, 1, 10), (4, 17, 3), (17, 3, 22), (19, 18, 2), @@ -251,24 +261,41 @@ fn gen_mat_mul() (15, 16, 17), (67, 63, 62), ]; - // test different strides - for &s1 in &[1, 2, -1, -2] { - for &s2 in &[1, 2, -1, -2] { - for &(m, k, n) in &sizes { - let a = range_mat64(m, k); - let b = range_mat64(k, n); - let mut c = range_mat64(m, n); + let strides = &[1, 2, -1, -2]; + let cf_order = [Order::C, Order::F]; + + // test different strides and memory orders + for (&s1, &s2) in iproduct!(strides, strides) { + for &(m, k, n) in &sizes { + for (ord1, ord2, ord3) in iproduct!(cf_order, cf_order, cf_order) { + println!("Case s1={}, s2={}, orders={:?}, {:?}, {:?}", s1, s2, ord1, ord2, ord3); + let a = ArrayBuilder::new((m, k)).memory_order(ord1).build(); + let b = ArrayBuilder::new((k, n)).memory_order(ord2).build(); + let mut c = ArrayBuilder::new((m, n)).memory_order(ord3).build(); + let mut answer = c.clone(); { - let a = a.slice(s![..;s1, ..;s2]); - let b = b.slice(s![..;s2, ..;s2]); - let mut cv = c.slice_mut(s![..;s1, ..;s2]); + let av; + let bv; + let mut cv; + + if s1 != 1 || s2 != 1 { + av = a.slice(s![..;s1, ..;s2]); + bv = b.slice(s![..;s2, ..;s2]); + cv = c.slice_mut(s![..;s1, ..;s2]); + } else { + // different stride cases for slicing versus not sliced (for axes of + // len=1); so test not sliced here. + av = a.view(); + bv = b.view(); + cv = c.view_mut(); + } - let answer_part = alpha * reference_mat_mul(&a, &b) + beta * &cv; + let answer_part = alpha * reference_mat_mul(&av, &bv) + beta * &cv; answer.slice_mut(s![..;s1, ..;s2]).assign(&answer_part); - general_mat_mul(alpha, &a, &b, beta, &mut cv); + general_mat_mul(alpha, &av, &bv, beta, &mut cv); } assert_relative_eq!(c, answer, epsilon = 1e-12, max_relative = 1e-7); } diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index f3bedae71..e7813455d 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -25,8 +25,6 @@ use num_complex::{Complex32 as c32, Complex64 as c64}; #[cfg(feature = "blas")] use libc::c_int; #[cfg(feature = "blas")] -use std::cmp; -#[cfg(feature = "blas")] use std::mem::swap; #[cfg(feature = "blas")] @@ -388,8 +386,9 @@ fn mat_mul_impl( { // size cutoff for using BLAS let cut = GEMM_BLAS_CUTOFF; - let ((mut m, a), (_, mut n)) = (lhs.dim(), rhs.dim()); - if !(m > cut || n > cut || a > cut) + let ((mut m, k), (k2, mut n)) = (lhs.dim(), rhs.dim()); + debug_assert_eq!(k, k2); + if !(m > cut || n > cut || k > cut) || !(same_type::() || same_type::() || same_type::() @@ -397,32 +396,74 @@ fn mat_mul_impl( { return mat_mul_general(alpha, lhs, rhs, beta, c); } - { - // Use `c` for c-order and `f` for an f-order matrix - // We can handle c * c, f * f generally and - // c * f and f * c if the `f` matrix is square. - let mut lhs_ = lhs.view(); - let mut rhs_ = rhs.view(); - let mut c_ = c.view_mut(); - let lhs_s0 = lhs_.strides()[0]; - let rhs_s0 = rhs_.strides()[0]; - let both_f = lhs_s0 == 1 && rhs_s0 == 1; - let mut lhs_trans = CblasNoTrans; - let mut rhs_trans = CblasNoTrans; - if both_f { - // A^t B^t = C^t => B A = C - let lhs_t = lhs_.reversed_axes(); - lhs_ = rhs_.reversed_axes(); - rhs_ = lhs_t; - c_ = c_.reversed_axes(); - swap(&mut m, &mut n); - } else if lhs_s0 == 1 && m == a { - lhs_ = lhs_.reversed_axes(); - lhs_trans = CblasTrans; - } else if rhs_s0 == 1 && a == n { - rhs_ = rhs_.reversed_axes(); - rhs_trans = CblasTrans; + + #[allow(clippy::never_loop)] // MSRV Rust 1.64 does not have break from block + 'blas_block: loop { + let mut a = lhs.view(); + let mut b = rhs.view(); + let mut c = c.view_mut(); + + let c_layout = get_blas_compatible_layout(&c); + let c_layout_is_c = matches!(c_layout, Some(MemoryOrder::C)); + let c_layout_is_f = matches!(c_layout, Some(MemoryOrder::F)); + + // Compute A B -> C + // we require for BLAS compatibility that: + // A, B are contiguous (stride=1) in their fastest dimension. + // C is c-contiguous in one dimension (stride=1 in Axis(1)) + // + // If C is f-contiguous, use transpose equivalency + // to translate to the C-contiguous case: + // A^t B^t = C^t => B A = C + + let (a_layout, b_layout) = + match (get_blas_compatible_layout(&a), get_blas_compatible_layout(&b)) { + (Some(a_layout), Some(b_layout)) if c_layout_is_c => { + // normal case + (a_layout, b_layout) + }, + (Some(a_layout), Some(b_layout)) if c_layout_is_f => { + // Transpose equivalency + // A^t B^t = C^t => B A = C + // + // A^t becomes the new B + // B^t becomes the new A + let a_t = a.reversed_axes(); + a = b.reversed_axes(); + b = a_t; + c = c.reversed_axes(); + // Assign (n, k, m) -> (m, k, n) effectively + swap(&mut m, &mut n); + + // Continue using the already computed memory layouts + (b_layout.opposite(), a_layout.opposite()) + }, + _otherwise => { + break 'blas_block; + } + }; + + let a_trans; + let b_trans; + let lda; // Stride of a + let ldb; // Stride of b + + if let MemoryOrder::C = a_layout { + lda = blas_stride(&a, 0); + a_trans = CblasNoTrans; + } else { + lda = blas_stride(&a, 1); + a_trans = CblasTrans; + } + + if let MemoryOrder::C = b_layout { + ldb = blas_stride(&b, 0); + b_trans = CblasNoTrans; + } else { + ldb = blas_stride(&b, 1); + b_trans = CblasTrans; } + let ldc = blas_stride(&c, 0); macro_rules! gemm_scalar_cast { (f32, $var:ident) => { @@ -441,44 +482,25 @@ fn mat_mul_impl( macro_rules! gemm { ($ty:tt, $gemm:ident) => { - if blas_row_major_2d::<$ty, _>(&lhs_) - && blas_row_major_2d::<$ty, _>(&rhs_) - && blas_row_major_2d::<$ty, _>(&c_) - { - let (m, k) = match lhs_trans { - CblasNoTrans => lhs_.dim(), - _ => { - let (rows, cols) = lhs_.dim(); - (cols, rows) - } - }; - let n = match rhs_trans { - CblasNoTrans => rhs_.raw_dim()[1], - _ => rhs_.raw_dim()[0], - }; - // adjust strides, these may [1, 1] for column matrices - let lhs_stride = cmp::max(lhs_.strides()[0] as blas_index, k as blas_index); - let rhs_stride = cmp::max(rhs_.strides()[0] as blas_index, n as blas_index); - let c_stride = cmp::max(c_.strides()[0] as blas_index, n as blas_index); - + if same_type::() { // gemm is C ← αA^Op B^Op + βC // Where Op is notrans/trans/conjtrans unsafe { blas_sys::$gemm( CblasRowMajor, - lhs_trans, - rhs_trans, + a_trans, + b_trans, m as blas_index, // m, rows of Op(a) n as blas_index, // n, cols of Op(b) k as blas_index, // k, cols of Op(a) gemm_scalar_cast!($ty, alpha), // alpha - lhs_.ptr.as_ptr() as *const _, // a - lhs_stride, // lda - rhs_.ptr.as_ptr() as *const _, // b - rhs_stride, // ldb + a.ptr.as_ptr() as *const _, // a + lda, // lda + b.ptr.as_ptr() as *const _, // b + ldb, // ldb gemm_scalar_cast!($ty, beta), // beta - c_.ptr.as_ptr() as *mut _, // c - c_stride, // ldc + c.ptr.as_ptr() as *mut _, // c + ldc, // ldc ); } return; @@ -490,6 +512,7 @@ fn mat_mul_impl( gemm!(c32, cblas_cgemm); gemm!(c64, cblas_zgemm); + break 'blas_block; } mat_mul_general(alpha, lhs, rhs, beta, c) } @@ -693,46 +716,51 @@ unsafe fn general_mat_vec_mul_impl( #[cfg(feature = "blas")] macro_rules! gemv { ($ty:ty, $gemv:ident) => { - if let Some(layout) = blas_layout::<$ty, _>(&a) { - if blas_compat_1d::<$ty, _>(&x) && blas_compat_1d::<$ty, _>(&y) { - // Determine stride between rows or columns. Note that the stride is - // adjusted to at least `k` or `m` to handle the case of a matrix with a - // trivial (length 1) dimension, since the stride for the trivial dimension - // may be arbitrary. - let a_trans = CblasNoTrans; - let a_stride = match layout { - CBLAS_LAYOUT::CblasRowMajor => { - a.strides()[0].max(k as isize) as blas_index - } - CBLAS_LAYOUT::CblasColMajor => { - a.strides()[1].max(m as isize) as blas_index - } - }; - - // Low addr in memory pointers required for x, y - let x_offset = offset_from_low_addr_ptr_to_logical_ptr(&x.dim, &x.strides); - let x_ptr = x.ptr.as_ptr().sub(x_offset); - let y_offset = offset_from_low_addr_ptr_to_logical_ptr(&y.dim, &y.strides); - let y_ptr = y.ptr.as_ptr().sub(y_offset); - - let x_stride = x.strides()[0] as blas_index; - let y_stride = y.strides()[0] as blas_index; - - blas_sys::$gemv( - layout, - a_trans, - m as blas_index, // m, rows of Op(a) - k as blas_index, // n, cols of Op(a) - cast_as(&alpha), // alpha - a.ptr.as_ptr() as *const _, // a - a_stride, // lda - x_ptr as *const _, // x - x_stride, - cast_as(&beta), // beta - y_ptr as *mut _, // y - y_stride, - ); - return; + if same_type::() { + if let Some(layout) = get_blas_compatible_layout(&a) { + if blas_compat_1d::<$ty, _>(&x) && blas_compat_1d::<$ty, _>(&y) { + // Determine stride between rows or columns. Note that the stride is + // adjusted to at least `k` or `m` to handle the case of a matrix with a + // trivial (length 1) dimension, since the stride for the trivial dimension + // may be arbitrary. + let a_trans = CblasNoTrans; + + let (a_stride, cblas_layout) = match layout { + MemoryOrder::C => { + (a.strides()[0].max(k as isize) as blas_index, + CBLAS_LAYOUT::CblasRowMajor) + } + MemoryOrder::F => { + (a.strides()[1].max(m as isize) as blas_index, + CBLAS_LAYOUT::CblasColMajor) + } + }; + + // Low addr in memory pointers required for x, y + let x_offset = offset_from_low_addr_ptr_to_logical_ptr(&x.dim, &x.strides); + let x_ptr = x.ptr.as_ptr().sub(x_offset); + let y_offset = offset_from_low_addr_ptr_to_logical_ptr(&y.dim, &y.strides); + let y_ptr = y.ptr.as_ptr().sub(y_offset); + + let x_stride = x.strides()[0] as blas_index; + let y_stride = y.strides()[0] as blas_index; + + blas_sys::$gemv( + cblas_layout, + a_trans, + m as blas_index, // m, rows of Op(a) + k as blas_index, // n, cols of Op(a) + cast_as(&alpha), // alpha + a.ptr.as_ptr() as *const _, // a + a_stride, // lda + x_ptr as *const _, // x + x_stride, + cast_as(&beta), // beta + y_ptr as *mut _, // y + y_stride, + ); + return; + } } } }; @@ -834,6 +862,7 @@ where } #[cfg(feature = "blas")] +#[derive(Copy, Clone)] enum MemoryOrder { C, @@ -841,29 +870,15 @@ enum MemoryOrder } #[cfg(feature = "blas")] -fn blas_row_major_2d(a: &ArrayBase) -> bool -where - S: Data, - A: 'static, - S::Elem: 'static, -{ - if !same_type::() { - return false; - } - is_blas_2d(&a.dim, &a.strides, MemoryOrder::C) -} - -#[cfg(feature = "blas")] -fn blas_column_major_2d(a: &ArrayBase) -> bool -where - S: Data, - A: 'static, - S::Elem: 'static, +impl MemoryOrder { - if !same_type::() { - return false; + fn opposite(self) -> Self + { + match self { + MemoryOrder::C => MemoryOrder::F, + MemoryOrder::F => MemoryOrder::C, + } } - is_blas_2d(&a.dim, &a.strides, MemoryOrder::F) } #[cfg(feature = "blas")] @@ -893,20 +908,71 @@ fn is_blas_2d(dim: &Ix2, stride: &Ix2, order: MemoryOrder) -> bool true } +/// Get BLAS compatible layout if any (C or F, preferring the former) +#[cfg(feature = "blas")] +fn get_blas_compatible_layout(a: &ArrayBase) -> Option +where S: Data +{ + if is_blas_2d(&a.dim, &a.strides, MemoryOrder::C) { + Some(MemoryOrder::C) + } else if is_blas_2d(&a.dim, &a.strides, MemoryOrder::F) { + Some(MemoryOrder::F) + } else { + None + } +} + +/// `a` should be blas compatible. +/// axis: 0 or 1. +/// +/// Return leading stride (lda, ldb, ldc) of array #[cfg(feature = "blas")] -fn blas_layout(a: &ArrayBase) -> Option +fn blas_stride(a: &ArrayBase, axis: usize) -> blas_index +where S: Data +{ + debug_assert!(axis <= 1); + let other_axis = 1 - axis; + let len_this = a.shape()[axis]; + let len_other = a.shape()[other_axis]; + let stride = a.strides()[axis]; + + // if current axis has length == 1, then stride does not matter for ndarray + // but for BLAS we need a stride that makes sense, i.e. it's >= the other axis + + // cast: a should already be blas compatible + (if len_this <= 1 { + Ord::max(stride, len_other as isize) + } else { + stride + }) as blas_index +} + +#[cfg(test)] +#[cfg(feature = "blas")] +fn blas_row_major_2d(a: &ArrayBase) -> bool where S: Data, A: 'static, S::Elem: 'static, { - if blas_row_major_2d::(a) { - Some(CBLAS_LAYOUT::CblasRowMajor) - } else if blas_column_major_2d::(a) { - Some(CBLAS_LAYOUT::CblasColMajor) - } else { - None + if !same_type::() { + return false; + } + is_blas_2d(&a.dim, &a.strides, MemoryOrder::C) +} + +#[cfg(test)] +#[cfg(feature = "blas")] +fn blas_column_major_2d(a: &ArrayBase) -> bool +where + S: Data, + A: 'static, + S::Elem: 'static, +{ + if !same_type::() { + return false; } + is_blas_2d(&a.dim, &a.strides, MemoryOrder::F) } #[cfg(test)] @@ -964,4 +1030,46 @@ mod blas_tests assert!(!blas_row_major_2d::(&m)); assert!(blas_column_major_2d::(&m)); } + + #[test] + fn blas_row_major_2d_skip_rows_ok() + { + let m: Array2 = Array2::zeros((5, 5)); + let mv = m.slice(s![..;2, ..]); + assert!(blas_row_major_2d::(&mv)); + assert!(!blas_column_major_2d::(&mv)); + } + + #[test] + fn blas_row_major_2d_skip_columns_fail() + { + let m: Array2 = Array2::zeros((5, 5)); + let mv = m.slice(s![.., ..;2]); + assert!(!blas_row_major_2d::(&mv)); + assert!(!blas_column_major_2d::(&mv)); + } + + #[test] + fn blas_col_major_2d_skip_columns_ok() + { + let m: Array2 = Array2::zeros((5, 5).f()); + let mv = m.slice(s![.., ..;2]); + assert!(blas_column_major_2d::(&mv)); + assert!(!blas_row_major_2d::(&mv)); + } + + #[test] + fn blas_col_major_2d_skip_rows_fail() + { + let m: Array2 = Array2::zeros((5, 5).f()); + let mv = m.slice(s![..;2, ..]); + assert!(!blas_column_major_2d::(&mv)); + assert!(!blas_row_major_2d::(&mv)); + } + + #[test] + fn test() + { + //WIP test that stride is larger than other dimension + } } From 01bb218ada456c80d937710ce2d3c997db96bb18 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 7 Aug 2024 11:18:44 +0200 Subject: [PATCH 628/651] blas: Fix to skip array with too short stride If we have a matrix of dimension say 5 x 5, BLAS requires the leading stride to be >= 5. Smaller cases are possible for read-only array views in ndarray(broadcasting and custom strides). In this case we mark the array as not BLAS compatible --- src/linalg/impl_linalg.rs | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index e7813455d..778fcaabd 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -863,6 +863,7 @@ where #[cfg(feature = "blas")] #[derive(Copy, Clone)] +#[cfg_attr(test, derive(PartialEq, Eq, Debug))] enum MemoryOrder { C, @@ -887,24 +888,34 @@ fn is_blas_2d(dim: &Ix2, stride: &Ix2, order: MemoryOrder) -> bool let (m, n) = dim.into_pattern(); let s0 = stride[0] as isize; let s1 = stride[1] as isize; - let (inner_stride, outer_dim) = match order { - MemoryOrder::C => (s1, n), - MemoryOrder::F => (s0, m), + let (inner_stride, outer_stride, inner_dim, outer_dim) = match order { + MemoryOrder::C => (s1, s0, m, n), + MemoryOrder::F => (s0, s1, n, m), }; + if !(inner_stride == 1 || outer_dim == 1) { return false; } + if s0 < 1 || s1 < 1 { return false; } + if (s0 > blas_index::MAX as isize || s0 < blas_index::MIN as isize) || (s1 > blas_index::MAX as isize || s1 < blas_index::MIN as isize) { return false; } + + // leading stride must >= the dimension (no broadcasting/aliasing) + if inner_dim > 1 && (outer_stride as usize) < outer_dim { + return false; + } + if m > blas_index::MAX as usize || n > blas_index::MAX as usize { return false; } + true } @@ -1068,8 +1079,26 @@ mod blas_tests } #[test] - fn test() + fn blas_too_short_stride() { - //WIP test that stride is larger than other dimension + // leading stride must be longer than the other dimension + // Example, in a 5 x 5 matrix, the leading stride must be >= 5 for BLAS. + + const N: usize = 5; + const MAXSTRIDE: usize = N + 2; + let mut data = [0; MAXSTRIDE * N]; + let mut iter = 0..data.len(); + data.fill_with(|| iter.next().unwrap()); + + for stride in 1..=MAXSTRIDE { + let m = ArrayView::from_shape((N, N).strides((stride, 1)), &data).unwrap(); + eprintln!("{:?}", m); + + if stride < N { + assert_eq!(get_blas_compatible_layout(&m), None); + } else { + assert_eq!(get_blas_compatible_layout(&m), Some(MemoryOrder::C)); + } + } } } From 56cac34de7ec11705b4c76721ab4290e25e601d7 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 7 Aug 2024 11:18:44 +0200 Subject: [PATCH 629/651] ci: Run ndarray tests with feature blas --- scripts/all-tests.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index 6f1fdf73a..b8c9b5849 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -19,6 +19,7 @@ cargo test -v --features "$FEATURES" $QC_FEAT cargo test -v -p ndarray -p ndarray-rand --release --features "$FEATURES" $QC_FEAT --lib --tests # BLAS tests +cargo test -p ndarray --lib -v --features blas cargo test -p blas-tests -v --features blas-tests/openblas-system cargo test -p numeric-tests -v --features numeric-tests/test_blas From e65bd0d43d716d60b69e6942a314cb5a012e322b Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 7 Aug 2024 15:49:07 +0200 Subject: [PATCH 630/651] tests: Refactor to use ArrayBuilder more places --- Cargo.toml | 3 +- crates/blas-tests/tests/oper.rs | 64 ++++++++-------------- crates/ndarray-gen/Cargo.toml | 2 +- crates/ndarray-gen/src/lib.rs | 1 + tests/oper.rs | 95 ++++++++++++++------------------- 5 files changed, 66 insertions(+), 99 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ac1960242..34d298dda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ defmac = "0.2" quickcheck = { workspace = true } approx = { workspace = true, default-features = true } itertools = { workspace = true } +ndarray-gen = { workspace = true } [features] default = ["std"] @@ -93,7 +94,7 @@ default-members = [ ] [workspace.dependencies] -ndarray = { version = "0.16", path = "." } +ndarray = { version = "0.16", path = ".", default-features = false } ndarray-rand = { path = "ndarray-rand" } ndarray-gen = { path = "crates/ndarray-gen" } diff --git a/crates/blas-tests/tests/oper.rs b/crates/blas-tests/tests/oper.rs index 931aabea9..9361a59a5 100644 --- a/crates/blas-tests/tests/oper.rs +++ b/crates/blas-tests/tests/oper.rs @@ -18,6 +18,7 @@ use defmac::defmac; use itertools::iproduct; use num_complex::Complex32; use num_complex::Complex64; +use num_traits::Num; #[test] fn mat_vec_product_1d() @@ -49,46 +50,29 @@ fn mat_vec_product_1d_inverted_axis() assert_eq!(a.t().dot(&b), ans); } -fn range_mat(m: Ix, n: Ix) -> Array2 +fn range_mat(m: Ix, n: Ix) -> Array2 { - Array::linspace(0., (m * n) as f32 - 1., m * n) - .into_shape_with_order((m, n)) - .unwrap() -} - -fn range_mat64(m: Ix, n: Ix) -> Array2 -{ - Array::linspace(0., (m * n) as f64 - 1., m * n) - .into_shape_with_order((m, n)) - .unwrap() + ArrayBuilder::new((m, n)).build() } fn range_mat_complex(m: Ix, n: Ix) -> Array2 { - Array::linspace(0., (m * n) as f32 - 1., m * n) - .into_shape_with_order((m, n)) - .unwrap() - .map(|&f| Complex32::new(f, 0.)) + ArrayBuilder::new((m, n)).build() } fn range_mat_complex64(m: Ix, n: Ix) -> Array2 { - Array::linspace(0., (m * n) as f64 - 1., m * n) - .into_shape_with_order((m, n)) - .unwrap() - .map(|&f| Complex64::new(f, 0.)) + ArrayBuilder::new((m, n)).build() } fn range1_mat64(m: Ix) -> Array1 { - Array::linspace(0., m as f64 - 1., m) + ArrayBuilder::new(m).build() } fn range_i32(m: Ix, n: Ix) -> Array2 { - Array::from_iter(0..(m * n) as i32) - .into_shape_with_order((m, n)) - .unwrap() + ArrayBuilder::new((m, n)).build() } // simple, slow, correct (hopefully) mat mul @@ -163,8 +147,8 @@ where fn mat_mul_order() { let (m, n, k) = (50, 50, 50); - let a = range_mat(m, n); - let b = range_mat(n, k); + let a = range_mat::(m, n); + let b = range_mat::(n, k); let mut af = Array::zeros(a.dim().f()); let mut bf = Array::zeros(b.dim().f()); af.assign(&a); @@ -183,7 +167,7 @@ fn mat_mul_order() fn mat_mul_broadcast() { let (m, n, k) = (16, 16, 16); - let a = range_mat(m, n); + let a = range_mat::(m, n); let x1 = 1.; let x = Array::from(vec![x1]); let b0 = x.broadcast((n, k)).unwrap(); @@ -203,8 +187,8 @@ fn mat_mul_broadcast() fn mat_mul_rev() { let (m, n, k) = (16, 16, 16); - let a = range_mat(m, n); - let b = range_mat(n, k); + let a = range_mat::(m, n); + let b = range_mat::(n, k); let mut rev = Array::zeros(b.dim()); let mut rev = rev.slice_mut(s![..;-1, ..]); rev.assign(&b); @@ -233,8 +217,8 @@ fn mat_mut_zero_len() } } }); - mat_mul_zero_len!(range_mat); - mat_mul_zero_len!(range_mat64); + mat_mul_zero_len!(range_mat::); + mat_mul_zero_len!(range_mat::); mat_mul_zero_len!(range_i32); } @@ -307,11 +291,11 @@ fn gen_mat_mul() #[test] fn gemm_64_1_f() { - let a = range_mat64(64, 64).reversed_axes(); + let a = range_mat::(64, 64).reversed_axes(); let (m, n) = a.dim(); // m x n times n x 1 == m x 1 - let x = range_mat64(n, 1); - let mut y = range_mat64(m, 1); + let x = range_mat::(n, 1); + let mut y = range_mat::(m, 1); let answer = reference_mat_mul(&a, &x) + &y; general_mat_mul(1.0, &a, &x, 1.0, &mut y); assert_relative_eq!(y, answer, epsilon = 1e-12, max_relative = 1e-7); @@ -393,11 +377,8 @@ fn gen_mat_vec_mul() for &s1 in &[1, 2, -1, -2] { for &s2 in &[1, 2, -1, -2] { for &(m, k) in &sizes { - for &rev in &[false, true] { - let mut a = range_mat64(m, k); - if rev { - a = a.reversed_axes(); - } + for order in [Order::C, Order::F] { + let a = ArrayBuilder::new((m, k)).memory_order(order).build(); let (m, k) = a.dim(); let b = range1_mat64(k); let mut c = range1_mat64(m); @@ -438,11 +419,8 @@ fn vec_mat_mul() for &s1 in &[1, 2, -1, -2] { for &s2 in &[1, 2, -1, -2] { for &(m, n) in &sizes { - for &rev in &[false, true] { - let mut b = range_mat64(m, n); - if rev { - b = b.reversed_axes(); - } + for order in [Order::C, Order::F] { + let b = ArrayBuilder::new((m, n)).memory_order(order).build(); let (m, n) = b.dim(); let a = range1_mat64(m); let mut c = range1_mat64(n); diff --git a/crates/ndarray-gen/Cargo.toml b/crates/ndarray-gen/Cargo.toml index f06adc48a..6818e4b65 100644 --- a/crates/ndarray-gen/Cargo.toml +++ b/crates/ndarray-gen/Cargo.toml @@ -5,5 +5,5 @@ edition = "2018" publish = false [dependencies] -ndarray = { workspace = true } +ndarray = { workspace = true, default-features = false } num-traits = { workspace = true } diff --git a/crates/ndarray-gen/src/lib.rs b/crates/ndarray-gen/src/lib.rs index ceecf2fae..7f9ca89fc 100644 --- a/crates/ndarray-gen/src/lib.rs +++ b/crates/ndarray-gen/src/lib.rs @@ -1,3 +1,4 @@ +#![no_std] // Copyright 2024 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 Array2 +fn range_mat(m: Ix, n: Ix) -> Array2 { - Array::linspace(0., (m * n) as f32 - 1., m * n) - .into_shape_with_order((m, n)) - .unwrap() -} - -fn range_mat64(m: Ix, n: Ix) -> Array2 -{ - Array::linspace(0., (m * n) as f64 - 1., m * n) - .into_shape_with_order((m, n)) - .unwrap() + ArrayBuilder::new((m, n)).build() } #[cfg(feature = "approx")] fn range1_mat64(m: Ix) -> Array1 { - Array::linspace(0., m as f64 - 1., m) + ArrayBuilder::new(m).build() } fn range_i32(m: Ix, n: Ix) -> Array2 { - Array::from_iter(0..(m * n) as i32) - .into_shape_with_order((m, n)) - .unwrap() + ArrayBuilder::new((m, n)).build() } // simple, slow, correct (hopefully) mat mul @@ -332,8 +325,8 @@ where fn mat_mul() { let (m, n, k) = (8, 8, 8); - let a = range_mat(m, n); - let b = range_mat(n, k); + let a = range_mat::(m, n); + let b = range_mat::(n, k); let mut b = b / 4.; { let mut c = b.column_mut(0); @@ -351,8 +344,8 @@ fn mat_mul() assert_eq!(ab, af.dot(&bf)); let (m, n, k) = (10, 5, 11); - let a = range_mat(m, n); - let b = range_mat(n, k); + let a = range_mat::(m, n); + let b = range_mat::(n, k); let mut b = b / 4.; { let mut c = b.column_mut(0); @@ -370,8 +363,8 @@ fn mat_mul() assert_eq!(ab, af.dot(&bf)); let (m, n, k) = (10, 8, 1); - let a = range_mat(m, n); - let b = range_mat(n, k); + let a = range_mat::(m, n); + let b = range_mat::(n, k); let mut b = b / 4.; { let mut c = b.column_mut(0); @@ -395,8 +388,8 @@ fn mat_mul() fn mat_mul_order() { let (m, n, k) = (8, 8, 8); - let a = range_mat(m, n); - let b = range_mat(n, k); + let a = range_mat::(m, n); + let b = range_mat::(n, k); let mut af = Array::zeros(a.dim().f()); let mut bf = Array::zeros(b.dim().f()); af.assign(&a); @@ -415,8 +408,8 @@ fn mat_mul_order() fn mat_mul_shape_mismatch() { let (m, k, k2, n) = (8, 8, 9, 8); - let a = range_mat(m, k); - let b = range_mat(k2, n); + let a = range_mat::(m, k); + let b = range_mat::(k2, n); a.dot(&b); } @@ -426,9 +419,9 @@ fn mat_mul_shape_mismatch() fn mat_mul_shape_mismatch_2() { let (m, k, k2, n) = (8, 8, 8, 8); - let a = range_mat(m, k); - let b = range_mat(k2, n); - let mut c = range_mat(m, n + 1); + let a = range_mat::(m, k); + let b = range_mat::(k2, n); + let mut c = range_mat::(m, n + 1); general_mat_mul(1., &a, &b, 1., &mut c); } @@ -438,7 +431,7 @@ fn mat_mul_shape_mismatch_2() fn mat_mul_broadcast() { let (m, n, k) = (16, 16, 16); - let a = range_mat(m, n); + let a = range_mat::(m, n); let x1 = 1.; let x = Array::from(vec![x1]); let b0 = x.broadcast((n, k)).unwrap(); @@ -458,8 +451,8 @@ fn mat_mul_broadcast() fn mat_mul_rev() { let (m, n, k) = (16, 16, 16); - let a = range_mat(m, n); - let b = range_mat(n, k); + let a = range_mat::(m, n); + let b = range_mat::(n, k); let mut rev = Array::zeros(b.dim()); let mut rev = rev.slice_mut(s![..;-1, ..]); rev.assign(&b); @@ -488,8 +481,8 @@ fn mat_mut_zero_len() } } }); - mat_mul_zero_len!(range_mat); - mat_mul_zero_len!(range_mat64); + mat_mul_zero_len!(range_mat::); + mat_mul_zero_len!(range_mat::); mat_mul_zero_len!(range_i32); } @@ -528,9 +521,9 @@ fn scaled_add_2() for &s1 in &[1, 2, -1, -2] { for &s2 in &[1, 2, -1, -2] { for &(m, k, n, q) in &sizes { - let mut a = range_mat64(m, k); + let mut a = range_mat::(m, k); let mut answer = a.clone(); - let c = range_mat64(n, q); + let c = range_mat::(n, q); { let mut av = a.slice_mut(s![..;s1, ..;s2]); @@ -570,7 +563,7 @@ fn scaled_add_3() for &s1 in &[1, 2, -1, -2] { for &s2 in &[1, 2, -1, -2] { for &(m, k, n, q) in &sizes { - let mut a = range_mat64(m, k); + let mut a = range_mat::(m, k); let mut answer = a.clone(); let cdim = if n == 1 { vec![q] } else { vec![n, q] }; let cslice: Vec = if n == 1 { @@ -582,7 +575,7 @@ fn scaled_add_3() ] }; - let c = range_mat64(n, q).into_shape_with_order(cdim).unwrap(); + let c = range_mat::(n, q).into_shape_with_order(cdim).unwrap(); { let mut av = a.slice_mut(s![..;s1, ..;s2]); @@ -619,9 +612,9 @@ fn gen_mat_mul() for &s1 in &[1, 2, -1, -2] { for &s2 in &[1, 2, -1, -2] { for &(m, k, n) in &sizes { - let a = range_mat64(m, k); - let b = range_mat64(k, n); - let mut c = range_mat64(m, n); + let a = range_mat::(m, k); + let b = range_mat::(k, n); + let mut c = range_mat::(m, n); let mut answer = c.clone(); { @@ -645,11 +638,11 @@ fn gen_mat_mul() #[test] fn gemm_64_1_f() { - let a = range_mat64(64, 64).reversed_axes(); + let a = range_mat::(64, 64).reversed_axes(); let (m, n) = a.dim(); // m x n times n x 1 == m x 1 - let x = range_mat64(n, 1); - let mut y = range_mat64(m, 1); + let x = range_mat::(n, 1); + let mut y = range_mat::(m, 1); let answer = reference_mat_mul(&a, &x) + &y; general_mat_mul(1.0, &a, &x, 1.0, &mut y); approx::assert_relative_eq!(y, answer, epsilon = 1e-12, max_relative = 1e-7); @@ -728,11 +721,8 @@ fn gen_mat_vec_mul() for &s1 in &[1, 2, -1, -2] { for &s2 in &[1, 2, -1, -2] { for &(m, k) in &sizes { - for &rev in &[false, true] { - let mut a = range_mat64(m, k); - if rev { - a = a.reversed_axes(); - } + for order in [Order::C, Order::F] { + let a = ArrayBuilder::new((m, k)).memory_order(order).build(); let (m, k) = a.dim(); let b = range1_mat64(k); let mut c = range1_mat64(m); @@ -794,11 +784,8 @@ fn vec_mat_mul() for &s1 in &[1, 2, -1, -2] { for &s2 in &[1, 2, -1, -2] { for &(m, n) in &sizes { - for &rev in &[false, true] { - let mut b = range_mat64(m, n); - if rev { - b = b.reversed_axes(); - } + for order in [Order::C, Order::F] { + let b = ArrayBuilder::new((m, n)).memory_order(order).build(); let (m, n) = b.dim(); let a = range1_mat64(m); let mut c = range1_mat64(n); From b2955cb9ebd647963b285e1a3e31e7bedc8beacc Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Thu, 8 Aug 2024 16:05:14 +0200 Subject: [PATCH 631/651] blas: Simplify layout logic for gemm Using cblas we can simplify this further to a more satisfying translation (from ndarray to BLAS), much simpler logic. Avoids creating and handling an extra layer of array views. --- crates/blas-tests/tests/oper.rs | 2 +- src/linalg/impl_linalg.rs | 133 ++++++++++++++++---------------- 2 files changed, 67 insertions(+), 68 deletions(-) diff --git a/crates/blas-tests/tests/oper.rs b/crates/blas-tests/tests/oper.rs index 9361a59a5..f1e1bc42b 100644 --- a/crates/blas-tests/tests/oper.rs +++ b/crates/blas-tests/tests/oper.rs @@ -253,7 +253,7 @@ fn gen_mat_mul() for &(m, k, n) in &sizes { for (ord1, ord2, ord3) in iproduct!(cf_order, cf_order, cf_order) { println!("Case s1={}, s2={}, orders={:?}, {:?}, {:?}", s1, s2, ord1, ord2, ord3); - let a = ArrayBuilder::new((m, k)).memory_order(ord1).build(); + let a = ArrayBuilder::new((m, k)).memory_order(ord1).build() * 0.5; let b = ArrayBuilder::new((k, n)).memory_order(ord2).build(); let mut c = ArrayBuilder::new((m, n)).memory_order(ord3).build(); diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index 778fcaabd..243dc783b 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -24,13 +24,11 @@ use num_complex::{Complex32 as c32, Complex64 as c64}; #[cfg(feature = "blas")] use libc::c_int; -#[cfg(feature = "blas")] -use std::mem::swap; #[cfg(feature = "blas")] use cblas_sys as blas_sys; #[cfg(feature = "blas")] -use cblas_sys::{CblasNoTrans, CblasRowMajor, CblasTrans, CBLAS_LAYOUT}; +use cblas_sys::{CblasNoTrans, CblasTrans, CBLAS_LAYOUT}; /// len of vector before we use blas #[cfg(feature = "blas")] @@ -377,8 +375,8 @@ use self::mat_mul_general as mat_mul_impl; #[cfg(feature = "blas")] fn mat_mul_impl( alpha: A, - lhs: &ArrayView2<'_, A>, - rhs: &ArrayView2<'_, A>, + a: &ArrayView2<'_, A>, + b: &ArrayView2<'_, A>, beta: A, c: &mut ArrayViewMut2<'_, A>, ) where @@ -386,7 +384,7 @@ fn mat_mul_impl( { // size cutoff for using BLAS let cut = GEMM_BLAS_CUTOFF; - let ((mut m, k), (k2, mut n)) = (lhs.dim(), rhs.dim()); + let ((m, k), (k2, n)) = (a.dim(), b.dim()); debug_assert_eq!(k, k2); if !(m > cut || n > cut || k > cut) || !(same_type::() @@ -394,76 +392,48 @@ fn mat_mul_impl( || same_type::() || same_type::()) { - return mat_mul_general(alpha, lhs, rhs, beta, c); + return mat_mul_general(alpha, a, b, beta, c); } #[allow(clippy::never_loop)] // MSRV Rust 1.64 does not have break from block 'blas_block: loop { - let mut a = lhs.view(); - let mut b = rhs.view(); - let mut c = c.view_mut(); - - let c_layout = get_blas_compatible_layout(&c); - let c_layout_is_c = matches!(c_layout, Some(MemoryOrder::C)); - let c_layout_is_f = matches!(c_layout, Some(MemoryOrder::F)); - // Compute A B -> C - // we require for BLAS compatibility that: - // A, B are contiguous (stride=1) in their fastest dimension. - // C is c-contiguous in one dimension (stride=1 in Axis(1)) + // We require for BLAS compatibility that: + // A, B, C are contiguous (stride=1) in their fastest dimension, + // but it can be either first or second axis (either rowmajor/"c" or colmajor/"f"). // - // If C is f-contiguous, use transpose equivalency - // to translate to the C-contiguous case: - // A^t B^t = C^t => B A = C - - let (a_layout, b_layout) = - match (get_blas_compatible_layout(&a), get_blas_compatible_layout(&b)) { - (Some(a_layout), Some(b_layout)) if c_layout_is_c => { - // normal case - (a_layout, b_layout) + // The "normal case" is CblasRowMajor for cblas. + // Select CblasRowMajor, CblasColMajor to fit C's memory order. + // + // Apply transpose to A, B as needed if they differ from the normal case. + // If C is CblasColMajor then transpose both A, B (again!) + + let (a_layout, a_axis, b_layout, b_axis, c_layout) = + match (get_blas_compatible_layout(a), + get_blas_compatible_layout(b), + get_blas_compatible_layout(c)) + { + (Some(a_layout), Some(b_layout), Some(c_layout @ MemoryOrder::C)) => { + (a_layout, a_layout.lead_axis(), + b_layout, b_layout.lead_axis(), c_layout) }, - (Some(a_layout), Some(b_layout)) if c_layout_is_f => { - // Transpose equivalency - // A^t B^t = C^t => B A = C - // - // A^t becomes the new B - // B^t becomes the new A - let a_t = a.reversed_axes(); - a = b.reversed_axes(); - b = a_t; - c = c.reversed_axes(); - // Assign (n, k, m) -> (m, k, n) effectively - swap(&mut m, &mut n); - - // Continue using the already computed memory layouts - (b_layout.opposite(), a_layout.opposite()) + (Some(a_layout), Some(b_layout), Some(c_layout @ MemoryOrder::F)) => { + // CblasColMajor is the "other case" + // Mark a, b as having layouts opposite of what they were detected as, which + // ends up with the correct transpose setting w.r.t col major + (a_layout.opposite(), a_layout.lead_axis(), + b_layout.opposite(), b_layout.lead_axis(), c_layout) }, - _otherwise => { - break 'blas_block; - } + _ => break 'blas_block, }; - let a_trans; - let b_trans; - let lda; // Stride of a - let ldb; // Stride of b + let a_trans = a_layout.to_cblas_transpose(); + let lda = blas_stride(&a, a_axis); - if let MemoryOrder::C = a_layout { - lda = blas_stride(&a, 0); - a_trans = CblasNoTrans; - } else { - lda = blas_stride(&a, 1); - a_trans = CblasTrans; - } + let b_trans = b_layout.to_cblas_transpose(); + let ldb = blas_stride(&b, b_axis); - if let MemoryOrder::C = b_layout { - ldb = blas_stride(&b, 0); - b_trans = CblasNoTrans; - } else { - ldb = blas_stride(&b, 1); - b_trans = CblasTrans; - } - let ldc = blas_stride(&c, 0); + let ldc = blas_stride(&c, c_layout.lead_axis()); macro_rules! gemm_scalar_cast { (f32, $var:ident) => { @@ -487,7 +457,7 @@ fn mat_mul_impl( // Where Op is notrans/trans/conjtrans unsafe { blas_sys::$gemm( - CblasRowMajor, + c_layout.to_cblas_layout(), a_trans, b_trans, m as blas_index, // m, rows of Op(a) @@ -507,14 +477,15 @@ fn mat_mul_impl( } }; } + gemm!(f32, cblas_sgemm); gemm!(f64, cblas_dgemm); - gemm!(c32, cblas_cgemm); gemm!(c64, cblas_zgemm); + break 'blas_block; } - mat_mul_general(alpha, lhs, rhs, beta, c) + mat_mul_general(alpha, a, b, beta, c) } /// C ← α A B + β C @@ -873,6 +844,18 @@ enum MemoryOrder #[cfg(feature = "blas")] impl MemoryOrder { + #[inline] + /// Axis of leading stride (opposite of contiguous axis) + fn lead_axis(self) -> usize + { + match self { + MemoryOrder::C => 0, + MemoryOrder::F => 1, + } + } + + /// Get opposite memory order + #[inline] fn opposite(self) -> Self { match self { @@ -880,6 +863,22 @@ impl MemoryOrder MemoryOrder::F => MemoryOrder::C, } } + + fn to_cblas_transpose(self) -> cblas_sys::CBLAS_TRANSPOSE + { + match self { + MemoryOrder::C => CblasNoTrans, + MemoryOrder::F => CblasTrans, + } + } + + fn to_cblas_layout(self) -> CBLAS_LAYOUT + { + match self { + MemoryOrder::C => CBLAS_LAYOUT::CblasRowMajor, + MemoryOrder::F => CBLAS_LAYOUT::CblasColMajor, + } + } } #[cfg(feature = "blas")] From 844cfcb601f61b7fea35c0611149f85d3aef32f9 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Thu, 8 Aug 2024 19:32:59 +0200 Subject: [PATCH 632/651] blas: Test that matrix multiply calls BLAS Add a crate with a mock blas implementation, so that we can assert that cblas_sgemm etc are called (depending on memory layout). --- Cargo.toml | 5 +- crates/blas-mock-tests/Cargo.toml | 18 ++++ crates/blas-mock-tests/src/lib.rs | 100 +++++++++++++++++++++++ crates/blas-mock-tests/tests/use-blas.rs | 88 ++++++++++++++++++++ scripts/all-tests.sh | 1 + 5 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 crates/blas-mock-tests/Cargo.toml create mode 100644 crates/blas-mock-tests/src/lib.rs create mode 100644 crates/blas-mock-tests/tests/use-blas.rs diff --git a/Cargo.toml b/Cargo.toml index 34d298dda..50faacf19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ approx = { workspace = true, optional = true } rayon = { version = "1.10.0", optional = true } # Use via the `blas` crate feature -cblas-sys = { version = "0.1.4", optional = true, default-features = false } +cblas-sys = { workspace = true, optional = true } libc = { version = "0.2.82", optional = true } matrixmultiply = { version = "0.3.2", default-features = false, features=["cgemm"] } @@ -90,7 +90,7 @@ default-members = [ "crates/ndarray-gen", "crates/numeric-tests", "crates/serialization-tests", - # exclude blas-tests that depends on BLAS install + # exclude blas-tests and blas-mock-tests that activate "blas" feature ] [workspace.dependencies] @@ -106,6 +106,7 @@ quickcheck = { version = "1.0", default-features = false } rand = { version = "0.8.0", features = ["small_rng"] } rand_distr = { version = "0.4.0" } itertools = { version = "0.13.0", default-features = false, features = ["use_std"] } +cblas-sys = { version = "0.1.4", default-features = false } [profile.bench] debug = true diff --git a/crates/blas-mock-tests/Cargo.toml b/crates/blas-mock-tests/Cargo.toml new file mode 100644 index 000000000..a12b78580 --- /dev/null +++ b/crates/blas-mock-tests/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "blas-mock-tests" +version = "0.1.0" +edition = "2018" +publish = false + +[lib] +test = false +doc = false +doctest = false + +[dependencies] +ndarray = { workspace = true, features = ["approx", "blas"] } +ndarray-gen = { workspace = true } +cblas-sys = { workspace = true } + +[dev-dependencies] +itertools = { workspace = true } diff --git a/crates/blas-mock-tests/src/lib.rs b/crates/blas-mock-tests/src/lib.rs new file mode 100644 index 000000000..11fc5975e --- /dev/null +++ b/crates/blas-mock-tests/src/lib.rs @@ -0,0 +1,100 @@ +//! Mock interfaces to BLAS + +use core::cell::RefCell; +use core::ffi::{c_double, c_float, c_int}; +use std::thread_local; + +use cblas_sys::{c_double_complex, c_float_complex, CBLAS_LAYOUT, CBLAS_TRANSPOSE}; + +thread_local! { + /// This counter is incremented every time a gemm function is called + pub static CALL_COUNT: RefCell = RefCell::new(0); +} + +#[rustfmt::skip] +#[no_mangle] +#[allow(unused)] +pub unsafe extern "C" fn cblas_sgemm( + layout: CBLAS_LAYOUT, + transa: CBLAS_TRANSPOSE, + transb: CBLAS_TRANSPOSE, + m: c_int, + n: c_int, + k: c_int, + alpha: c_float, + a: *const c_float, + lda: c_int, + b: *const c_float, + ldb: c_int, + beta: c_float, + c: *mut c_float, + ldc: c_int +) { + CALL_COUNT.with(|ctx| *ctx.borrow_mut() += 1); +} + +#[rustfmt::skip] +#[no_mangle] +#[allow(unused)] +pub unsafe extern "C" fn cblas_dgemm( + layout: CBLAS_LAYOUT, + transa: CBLAS_TRANSPOSE, + transb: CBLAS_TRANSPOSE, + m: c_int, + n: c_int, + k: c_int, + alpha: c_double, + a: *const c_double, + lda: c_int, + b: *const c_double, + ldb: c_int, + beta: c_double, + c: *mut c_double, + ldc: c_int +) { + CALL_COUNT.with(|ctx| *ctx.borrow_mut() += 1); +} + +#[rustfmt::skip] +#[no_mangle] +#[allow(unused)] +pub unsafe extern "C" fn cblas_cgemm( + layout: CBLAS_LAYOUT, + transa: CBLAS_TRANSPOSE, + transb: CBLAS_TRANSPOSE, + m: c_int, + n: c_int, + k: c_int, + alpha: *const c_float_complex, + a: *const c_float_complex, + lda: c_int, + b: *const c_float_complex, + ldb: c_int, + beta: *const c_float_complex, + c: *mut c_float_complex, + ldc: c_int +) { + CALL_COUNT.with(|ctx| *ctx.borrow_mut() += 1); +} + +#[rustfmt::skip] +#[no_mangle] +#[allow(unused)] +pub unsafe extern "C" fn cblas_zgemm( + layout: CBLAS_LAYOUT, + transa: CBLAS_TRANSPOSE, + transb: CBLAS_TRANSPOSE, + m: c_int, + n: c_int, + k: c_int, + alpha: *const c_double_complex, + a: *const c_double_complex, + lda: c_int, + b: *const c_double_complex, + ldb: c_int, + beta: *const c_double_complex, + c: *mut c_double_complex, + ldc: c_int +) { + CALL_COUNT.with(|ctx| *ctx.borrow_mut() += 1); +} diff --git a/crates/blas-mock-tests/tests/use-blas.rs b/crates/blas-mock-tests/tests/use-blas.rs new file mode 100644 index 000000000..217508af6 --- /dev/null +++ b/crates/blas-mock-tests/tests/use-blas.rs @@ -0,0 +1,88 @@ +extern crate ndarray; + +use ndarray::prelude::*; + +use blas_mock_tests::CALL_COUNT; +use ndarray::linalg::general_mat_mul; +use ndarray::Order; +use ndarray_gen::array_builder::ArrayBuilder; + +use itertools::iproduct; + +#[test] +fn test_gen_mat_mul_uses_blas() +{ + let alpha = 1.0; + let beta = 0.0; + + let sizes = vec![ + (8, 8, 8), + (10, 10, 10), + (8, 8, 1), + (1, 10, 10), + (10, 1, 10), + (10, 10, 1), + (1, 10, 1), + (10, 1, 1), + (1, 1, 10), + (4, 17, 3), + (17, 3, 22), + (19, 18, 2), + (16, 17, 15), + (15, 16, 17), + (67, 63, 62), + ]; + let strides = &[1, 2, -1, -2]; + let cf_order = [Order::C, Order::F]; + + // test different strides and memory orders + for &(m, k, n) in &sizes { + for (&s1, &s2) in iproduct!(strides, strides) { + for (ord1, ord2, ord3) in iproduct!(cf_order, cf_order, cf_order) { + println!("Case s1={}, s2={}, orders={:?}, {:?}, {:?}", s1, s2, ord1, ord2, ord3); + + let a = ArrayBuilder::new((m, k)).memory_order(ord1).build(); + let b = ArrayBuilder::new((k, n)).memory_order(ord2).build(); + let mut c = ArrayBuilder::new((m, n)).memory_order(ord3).build(); + + { + let av; + let bv; + let mut cv; + + if s1 != 1 || s2 != 1 { + av = a.slice(s![..;s1, ..;s2]); + bv = b.slice(s![..;s2, ..;s2]); + cv = c.slice_mut(s![..;s1, ..;s2]); + } else { + // different stride cases for slicing versus not sliced (for axes of + // len=1); so test not sliced here. + av = a.view(); + bv = b.view(); + cv = c.view_mut(); + } + + let pre_count = CALL_COUNT.with(|ctx| *ctx.borrow()); + general_mat_mul(alpha, &av, &bv, beta, &mut cv); + let after_count = CALL_COUNT.with(|ctx| *ctx.borrow()); + let ncalls = after_count - pre_count; + debug_assert!(ncalls <= 1); + + let always_uses_blas = s1 == 1 && s2 == 1; + + if always_uses_blas { + assert_eq!(ncalls, 1, "Contiguous arrays should use blas, orders={:?}", (ord1, ord2, ord3)); + } + + let should_use_blas = av.strides().iter().all(|&s| s > 0) + && bv.strides().iter().all(|&s| s > 0) + && cv.strides().iter().all(|&s| s > 0) + && av.strides().iter().any(|&s| s == 1) + && bv.strides().iter().any(|&s| s == 1) + && cv.strides().iter().any(|&s| s == 1); + assert_eq!(should_use_blas, ncalls > 0); + } + } + } + } +} diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index b8c9b5849..4ececbcbd 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -20,6 +20,7 @@ cargo test -v -p ndarray -p ndarray-rand --release --features "$FEATURES" $QC_FE # BLAS tests cargo test -p ndarray --lib -v --features blas +cargo test -p blas-mock-tests -v cargo test -p blas-tests -v --features blas-tests/openblas-system cargo test -p numeric-tests -v --features numeric-tests/test_blas From 700b4ddaae1b97551781f7fb8924cef2f2eb50db Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 9 Aug 2024 21:46:25 +0200 Subject: [PATCH 633/651] scripts: Fix off by one in makechangelog Use git's tformat to get a newline on the last entry, and then we include the last commit hash in the listing too. --- scripts/makechangelog.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/makechangelog.sh b/scripts/makechangelog.sh index 8bb6f2c2f..535280804 100755 --- a/scripts/makechangelog.sh +++ b/scripts/makechangelog.sh @@ -8,7 +8,7 @@ # Will produce some duplicates for PRs integrated using rebase, # but those will not occur with current merge queue. -git log --first-parent --pretty="format:%H" "$@" | while read commit_sha +git log --first-parent --pretty="tformat:%H" "$@" | while IFS= read -r commit_sha do gh api "/repos/:owner/:repo/commits/${commit_sha}/pulls" \ -q ".[] | \"- \(.title) by [@\(.user.login)](\(.user.html_url)) [#\(.number)](\(.html_url))\"" From 05789c1e6d2b7a61c24e8cbfff593a88aa688869 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 9 Aug 2024 21:46:25 +0200 Subject: [PATCH 634/651] Use resolver=2 in the workspace It separates feature selection for different dependency classes (dev, build, regular), which makes sense even if it seems to have no impact at the moment. --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 50faacf19..77f8a01b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ keywords = ["array", "data-structure", "multidimensional", "matrix", "blas"] categories = ["data-structures", "science"] exclude = ["docgen/images/*"] +resolver = "2" [lib] name = "ndarray" From 7226d3983f1bce5ad2149c315b96089d07ef467f Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 9 Aug 2024 21:46:25 +0200 Subject: [PATCH 635/651] blas: Run blas-mock-tests in cross compiler tests --- crates/blas-mock-tests/Cargo.toml | 4 ++-- scripts/cross-tests.sh | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/blas-mock-tests/Cargo.toml b/crates/blas-mock-tests/Cargo.toml index a12b78580..39ef9cf99 100644 --- a/crates/blas-mock-tests/Cargo.toml +++ b/crates/blas-mock-tests/Cargo.toml @@ -10,9 +10,9 @@ doc = false doctest = false [dependencies] -ndarray = { workspace = true, features = ["approx", "blas"] } -ndarray-gen = { workspace = true } cblas-sys = { workspace = true } [dev-dependencies] +ndarray = { workspace = true, features = ["approx", "blas"] } +ndarray-gen = { workspace = true } itertools = { workspace = true } diff --git a/scripts/cross-tests.sh b/scripts/cross-tests.sh index 683a901d8..80b37c339 100755 --- a/scripts/cross-tests.sh +++ b/scripts/cross-tests.sh @@ -11,3 +11,4 @@ QC_FEAT=--features=ndarray-rand/quickcheck cross build -v --features="$FEATURES" $QC_FEAT --target=$TARGET cross test -v --no-fail-fast --features="$FEATURES" $QC_FEAT --target=$TARGET +cross test -v -p blas-mock-tests From 453eae38a4ca63b1cb6c6d3a435a10e69b97974c Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 9 Aug 2024 21:46:25 +0200 Subject: [PATCH 636/651] blas: Refactor and simplify gemm call further Further clarify transpose logic by putting it into BlasOrder methods. --- src/linalg/impl_linalg.rs | 124 ++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 67 deletions(-) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index 243dc783b..965cefc4d 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -28,7 +28,7 @@ use libc::c_int; #[cfg(feature = "blas")] use cblas_sys as blas_sys; #[cfg(feature = "blas")] -use cblas_sys::{CblasNoTrans, CblasTrans, CBLAS_LAYOUT}; +use cblas_sys::{CblasNoTrans, CblasTrans, CBLAS_LAYOUT, CBLAS_TRANSPOSE}; /// len of vector before we use blas #[cfg(feature = "blas")] @@ -400,40 +400,33 @@ fn mat_mul_impl( // Compute A B -> C // We require for BLAS compatibility that: // A, B, C are contiguous (stride=1) in their fastest dimension, - // but it can be either first or second axis (either rowmajor/"c" or colmajor/"f"). + // but they can be either row major/"c" or col major/"f". // // The "normal case" is CblasRowMajor for cblas. - // Select CblasRowMajor, CblasColMajor to fit C's memory order. + // Select CblasRowMajor / CblasColMajor to fit C's memory order. // - // Apply transpose to A, B as needed if they differ from the normal case. + // Apply transpose to A, B as needed if they differ from the row major case. // If C is CblasColMajor then transpose both A, B (again!) - let (a_layout, a_axis, b_layout, b_axis, c_layout) = - match (get_blas_compatible_layout(a), - get_blas_compatible_layout(b), - get_blas_compatible_layout(c)) + let (a_layout, b_layout, c_layout) = + if let (Some(a_layout), Some(b_layout), Some(c_layout)) = + (get_blas_compatible_layout(a), + get_blas_compatible_layout(b), + get_blas_compatible_layout(c)) { - (Some(a_layout), Some(b_layout), Some(c_layout @ MemoryOrder::C)) => { - (a_layout, a_layout.lead_axis(), - b_layout, b_layout.lead_axis(), c_layout) - }, - (Some(a_layout), Some(b_layout), Some(c_layout @ MemoryOrder::F)) => { - // CblasColMajor is the "other case" - // Mark a, b as having layouts opposite of what they were detected as, which - // ends up with the correct transpose setting w.r.t col major - (a_layout.opposite(), a_layout.lead_axis(), - b_layout.opposite(), b_layout.lead_axis(), c_layout) - }, - _ => break 'blas_block, + (a_layout, b_layout, c_layout) + } else { + break 'blas_block; }; - let a_trans = a_layout.to_cblas_transpose(); - let lda = blas_stride(&a, a_axis); + let cblas_layout = c_layout.to_cblas_layout(); + let a_trans = a_layout.to_cblas_transpose_for(cblas_layout); + let lda = blas_stride(&a, a_layout); - let b_trans = b_layout.to_cblas_transpose(); - let ldb = blas_stride(&b, b_axis); + let b_trans = b_layout.to_cblas_transpose_for(cblas_layout); + let ldb = blas_stride(&b, b_layout); - let ldc = blas_stride(&c, c_layout.lead_axis()); + let ldc = blas_stride(&c, c_layout); macro_rules! gemm_scalar_cast { (f32, $var:ident) => { @@ -457,7 +450,7 @@ fn mat_mul_impl( // Where Op is notrans/trans/conjtrans unsafe { blas_sys::$gemm( - c_layout.to_cblas_layout(), + cblas_layout, a_trans, b_trans, m as blas_index, // m, rows of Op(a) @@ -696,16 +689,8 @@ unsafe fn general_mat_vec_mul_impl( // may be arbitrary. let a_trans = CblasNoTrans; - let (a_stride, cblas_layout) = match layout { - MemoryOrder::C => { - (a.strides()[0].max(k as isize) as blas_index, - CBLAS_LAYOUT::CblasRowMajor) - } - MemoryOrder::F => { - (a.strides()[1].max(m as isize) as blas_index, - CBLAS_LAYOUT::CblasColMajor) - } - }; + let a_stride = blas_stride(&a, layout); + let cblas_layout = layout.to_cblas_layout(); // Low addr in memory pointers required for x, y let x_offset = offset_from_low_addr_ptr_to_logical_ptr(&x.dim, &x.strides); @@ -835,61 +820,66 @@ where #[cfg(feature = "blas")] #[derive(Copy, Clone)] #[cfg_attr(test, derive(PartialEq, Eq, Debug))] -enum MemoryOrder +enum BlasOrder { C, F, } #[cfg(feature = "blas")] -impl MemoryOrder +impl BlasOrder { - #[inline] - /// Axis of leading stride (opposite of contiguous axis) - fn lead_axis(self) -> usize + fn transpose(self) -> Self { match self { - MemoryOrder::C => 0, - MemoryOrder::F => 1, + Self::C => Self::F, + Self::F => Self::C, } } - /// Get opposite memory order #[inline] - fn opposite(self) -> Self + /// Axis of leading stride (opposite of contiguous axis) + fn get_blas_lead_axis(self) -> usize { match self { - MemoryOrder::C => MemoryOrder::F, - MemoryOrder::F => MemoryOrder::C, + Self::C => 0, + Self::F => 1, } } - fn to_cblas_transpose(self) -> cblas_sys::CBLAS_TRANSPOSE + fn to_cblas_layout(self) -> CBLAS_LAYOUT { match self { - MemoryOrder::C => CblasNoTrans, - MemoryOrder::F => CblasTrans, + Self::C => CBLAS_LAYOUT::CblasRowMajor, + Self::F => CBLAS_LAYOUT::CblasColMajor, } } - fn to_cblas_layout(self) -> CBLAS_LAYOUT + /// When using cblas_sgemm (etc) with C matrix using `for_layout`, + /// how should this `self` matrix be transposed + fn to_cblas_transpose_for(self, for_layout: CBLAS_LAYOUT) -> CBLAS_TRANSPOSE { - match self { - MemoryOrder::C => CBLAS_LAYOUT::CblasRowMajor, - MemoryOrder::F => CBLAS_LAYOUT::CblasColMajor, + let effective_order = match for_layout { + CBLAS_LAYOUT::CblasRowMajor => self, + CBLAS_LAYOUT::CblasColMajor => self.transpose(), + }; + + match effective_order { + Self::C => CblasNoTrans, + Self::F => CblasTrans, } } } #[cfg(feature = "blas")] -fn is_blas_2d(dim: &Ix2, stride: &Ix2, order: MemoryOrder) -> bool +fn is_blas_2d(dim: &Ix2, stride: &Ix2, order: BlasOrder) -> bool { let (m, n) = dim.into_pattern(); let s0 = stride[0] as isize; let s1 = stride[1] as isize; let (inner_stride, outer_stride, inner_dim, outer_dim) = match order { - MemoryOrder::C => (s1, s0, m, n), - MemoryOrder::F => (s0, s1, n, m), + BlasOrder::C => (s1, s0, m, n), + BlasOrder::F => (s0, s1, n, m), }; if !(inner_stride == 1 || outer_dim == 1) { @@ -920,13 +910,13 @@ fn is_blas_2d(dim: &Ix2, stride: &Ix2, order: MemoryOrder) -> bool /// Get BLAS compatible layout if any (C or F, preferring the former) #[cfg(feature = "blas")] -fn get_blas_compatible_layout(a: &ArrayBase) -> Option +fn get_blas_compatible_layout(a: &ArrayBase) -> Option where S: Data { - if is_blas_2d(&a.dim, &a.strides, MemoryOrder::C) { - Some(MemoryOrder::C) - } else if is_blas_2d(&a.dim, &a.strides, MemoryOrder::F) { - Some(MemoryOrder::F) + if is_blas_2d(&a.dim, &a.strides, BlasOrder::C) { + Some(BlasOrder::C) + } else if is_blas_2d(&a.dim, &a.strides, BlasOrder::F) { + Some(BlasOrder::F) } else { None } @@ -937,10 +927,10 @@ where S: Data /// /// Return leading stride (lda, ldb, ldc) of array #[cfg(feature = "blas")] -fn blas_stride(a: &ArrayBase, axis: usize) -> blas_index +fn blas_stride(a: &ArrayBase, order: BlasOrder) -> blas_index where S: Data { - debug_assert!(axis <= 1); + let axis = order.get_blas_lead_axis(); let other_axis = 1 - axis; let len_this = a.shape()[axis]; let len_other = a.shape()[other_axis]; @@ -968,7 +958,7 @@ where if !same_type::() { return false; } - is_blas_2d(&a.dim, &a.strides, MemoryOrder::C) + is_blas_2d(&a.dim, &a.strides, BlasOrder::C) } #[cfg(test)] @@ -982,7 +972,7 @@ where if !same_type::() { return false; } - is_blas_2d(&a.dim, &a.strides, MemoryOrder::F) + is_blas_2d(&a.dim, &a.strides, BlasOrder::F) } #[cfg(test)] @@ -1096,7 +1086,7 @@ mod blas_tests if stride < N { assert_eq!(get_blas_compatible_layout(&m), None); } else { - assert_eq!(get_blas_compatible_layout(&m), Some(MemoryOrder::C)); + assert_eq!(get_blas_compatible_layout(&m), Some(BlasOrder::C)); } } } From 0153a37c5a832a99d694131464803617540c841d Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 9 Aug 2024 21:46:25 +0200 Subject: [PATCH 637/651] blas: Simplify control flow in matrix multiply --- src/linalg/impl_linalg.rs | 148 ++++++++++++++++---------------------- 1 file changed, 62 insertions(+), 86 deletions(-) diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index 965cefc4d..7472d8292 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -371,32 +371,15 @@ where #[cfg(not(feature = "blas"))] use self::mat_mul_general as mat_mul_impl; -#[rustfmt::skip] #[cfg(feature = "blas")] -fn mat_mul_impl( - alpha: A, - a: &ArrayView2<'_, A>, - b: &ArrayView2<'_, A>, - beta: A, - c: &mut ArrayViewMut2<'_, A>, -) where - A: LinalgScalar, +fn mat_mul_impl(alpha: A, a: &ArrayView2<'_, A>, b: &ArrayView2<'_, A>, beta: A, c: &mut ArrayViewMut2<'_, A>) +where A: LinalgScalar { - // size cutoff for using BLAS - let cut = GEMM_BLAS_CUTOFF; let ((m, k), (k2, n)) = (a.dim(), b.dim()); debug_assert_eq!(k, k2); - if !(m > cut || n > cut || k > cut) - || !(same_type::() - || same_type::() - || same_type::() - || same_type::()) + if (m > GEMM_BLAS_CUTOFF || n > GEMM_BLAS_CUTOFF || k > GEMM_BLAS_CUTOFF) + && (same_type::() || same_type::() || same_type::() || same_type::()) { - return mat_mul_general(alpha, a, b, beta, c); - } - - #[allow(clippy::never_loop)] // MSRV Rust 1.64 does not have break from block - 'blas_block: loop { // Compute A B -> C // We require for BLAS compatibility that: // A, B, C are contiguous (stride=1) in their fastest dimension, @@ -408,75 +391,68 @@ fn mat_mul_impl( // Apply transpose to A, B as needed if they differ from the row major case. // If C is CblasColMajor then transpose both A, B (again!) - let (a_layout, b_layout, c_layout) = - if let (Some(a_layout), Some(b_layout), Some(c_layout)) = - (get_blas_compatible_layout(a), - get_blas_compatible_layout(b), - get_blas_compatible_layout(c)) - { - (a_layout, b_layout, c_layout) - } else { - break 'blas_block; - }; - - let cblas_layout = c_layout.to_cblas_layout(); - let a_trans = a_layout.to_cblas_transpose_for(cblas_layout); - let lda = blas_stride(&a, a_layout); - - let b_trans = b_layout.to_cblas_transpose_for(cblas_layout); - let ldb = blas_stride(&b, b_layout); - - let ldc = blas_stride(&c, c_layout); - - macro_rules! gemm_scalar_cast { - (f32, $var:ident) => { - cast_as(&$var) - }; - (f64, $var:ident) => { - cast_as(&$var) - }; - (c32, $var:ident) => { - &$var as *const A as *const _ - }; - (c64, $var:ident) => { - &$var as *const A as *const _ - }; - } + if let (Some(a_layout), Some(b_layout), Some(c_layout)) = + (get_blas_compatible_layout(a), get_blas_compatible_layout(b), get_blas_compatible_layout(c)) + { + let cblas_layout = c_layout.to_cblas_layout(); + let a_trans = a_layout.to_cblas_transpose_for(cblas_layout); + let lda = blas_stride(&a, a_layout); + + let b_trans = b_layout.to_cblas_transpose_for(cblas_layout); + let ldb = blas_stride(&b, b_layout); + + let ldc = blas_stride(&c, c_layout); + + macro_rules! gemm_scalar_cast { + (f32, $var:ident) => { + cast_as(&$var) + }; + (f64, $var:ident) => { + cast_as(&$var) + }; + (c32, $var:ident) => { + &$var as *const A as *const _ + }; + (c64, $var:ident) => { + &$var as *const A as *const _ + }; + } - macro_rules! gemm { - ($ty:tt, $gemm:ident) => { - if same_type::() { - // gemm is C ← αA^Op B^Op + βC - // Where Op is notrans/trans/conjtrans - unsafe { - blas_sys::$gemm( - cblas_layout, - a_trans, - b_trans, - m as blas_index, // m, rows of Op(a) - n as blas_index, // n, cols of Op(b) - k as blas_index, // k, cols of Op(a) - gemm_scalar_cast!($ty, alpha), // alpha - a.ptr.as_ptr() as *const _, // a - lda, // lda - b.ptr.as_ptr() as *const _, // b - ldb, // ldb - gemm_scalar_cast!($ty, beta), // beta - c.ptr.as_ptr() as *mut _, // c - ldc, // ldc - ); + macro_rules! gemm { + ($ty:tt, $gemm:ident) => { + if same_type::() { + // gemm is C ← αA^Op B^Op + βC + // Where Op is notrans/trans/conjtrans + unsafe { + blas_sys::$gemm( + cblas_layout, + a_trans, + b_trans, + m as blas_index, // m, rows of Op(a) + n as blas_index, // n, cols of Op(b) + k as blas_index, // k, cols of Op(a) + gemm_scalar_cast!($ty, alpha), // alpha + a.ptr.as_ptr() as *const _, // a + lda, // lda + b.ptr.as_ptr() as *const _, // b + ldb, // ldb + gemm_scalar_cast!($ty, beta), // beta + c.ptr.as_ptr() as *mut _, // c + ldc, // ldc + ); + } + return; } - return; - } - }; - } + }; + } - gemm!(f32, cblas_sgemm); - gemm!(f64, cblas_dgemm); - gemm!(c32, cblas_cgemm); - gemm!(c64, cblas_zgemm); + gemm!(f32, cblas_sgemm); + gemm!(f64, cblas_dgemm); + gemm!(c32, cblas_cgemm); + gemm!(c64, cblas_zgemm); - break 'blas_block; + unreachable!() // we checked above that A is one of f32, f64, c32, c64 + } } mat_mul_general(alpha, a, b, beta, c) } From 876ad012f048bd2063746499102d045e11fdb8e5 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 9 Aug 2024 21:46:25 +0200 Subject: [PATCH 638/651] blas: test with more than one pattern in data Implement a checkerboard pattern in input data just to test with some another kind of input. --- crates/blas-tests/tests/oper.rs | 16 ++++++++++------ crates/ndarray-gen/src/array_builder.rs | 17 ++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/crates/blas-tests/tests/oper.rs b/crates/blas-tests/tests/oper.rs index f1e1bc42b..a9dca7e83 100644 --- a/crates/blas-tests/tests/oper.rs +++ b/crates/blas-tests/tests/oper.rs @@ -12,6 +12,7 @@ use ndarray::linalg::general_mat_vec_mul; use ndarray::Order; use ndarray::{Data, Ix, LinalgScalar}; use ndarray_gen::array_builder::ArrayBuilder; +use ndarray_gen::array_builder::ElementGenerator; use approx::assert_relative_eq; use defmac::defmac; @@ -230,7 +231,6 @@ fn gen_mat_mul() let sizes = vec![ (4, 4, 4), (8, 8, 8), - (10, 10, 10), (8, 8, 1), (1, 10, 10), (10, 1, 10), @@ -241,19 +241,23 @@ fn gen_mat_mul() (4, 17, 3), (17, 3, 22), (19, 18, 2), - (16, 17, 15), (15, 16, 17), - (67, 63, 62), + (67, 50, 62), ]; let strides = &[1, 2, -1, -2]; let cf_order = [Order::C, Order::F]; + let generator = [ElementGenerator::Sequential, ElementGenerator::Checkerboard]; // test different strides and memory orders - for (&s1, &s2) in iproduct!(strides, strides) { + for (&s1, &s2, &gen) in iproduct!(strides, strides, &generator) { for &(m, k, n) in &sizes { for (ord1, ord2, ord3) in iproduct!(cf_order, cf_order, cf_order) { - println!("Case s1={}, s2={}, orders={:?}, {:?}, {:?}", s1, s2, ord1, ord2, ord3); - let a = ArrayBuilder::new((m, k)).memory_order(ord1).build() * 0.5; + println!("Case s1={}, s2={}, gen={:?}, orders={:?}, {:?}, {:?}", s1, s2, gen, ord1, ord2, ord3); + let a = ArrayBuilder::new((m, k)) + .memory_order(ord1) + .generator(gen) + .build() + * 0.5; let b = ArrayBuilder::new((k, n)).memory_order(ord2).build(); let mut c = ArrayBuilder::new((m, n)).memory_order(ord3).build(); diff --git a/crates/ndarray-gen/src/array_builder.rs b/crates/ndarray-gen/src/array_builder.rs index a021e5252..9351aadc5 100644 --- a/crates/ndarray-gen/src/array_builder.rs +++ b/crates/ndarray-gen/src/array_builder.rs @@ -26,6 +26,7 @@ pub struct ArrayBuilder pub enum ElementGenerator { Sequential, + Checkerboard, Zero, } @@ -64,16 +65,14 @@ where D: Dimension pub fn build(self) -> Array where T: Num + Clone { - let mut current = T::zero(); + let zero = T::zero(); let size = self.dim.size(); - let use_zeros = self.generator == ElementGenerator::Zero; - Array::from_iter((0..size).map(|_| { - let ret = current.clone(); - if !use_zeros { - current = ret.clone() + T::one(); - } - ret - })) + (match self.generator { + ElementGenerator::Sequential => + Array::from_iter(core::iter::successors(Some(zero), |elt| Some(elt.clone() + T::one())).take(size)), + ElementGenerator::Checkerboard => Array::from_iter([T::one(), zero].iter().cycle().take(size).cloned()), + ElementGenerator::Zero => Array::zeros(size), + }) .into_shape_with_order((self.dim, self.memory_order)) .unwrap() } From 1df6c32d73d148df1e9477e0f1d7a45dad4c4de8 Mon Sep 17 00:00:00 2001 From: akern40 Date: Sun, 11 Aug 2024 11:15:36 -0400 Subject: [PATCH 639/651] Fix infinite recursion, overflow, and off-by-one error in triu/tril (#1418) * Fixes infinite recursion and off-by-one error * Avoids overflow using saturating arithmetic * Removes unused import * Fixes bug for isize::MAX for triu * Fix formatting * Uses broadcast indices to remove D::Smaller: Copy trait bound --- src/tri.rs | 183 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 129 insertions(+), 54 deletions(-) diff --git a/src/tri.rs b/src/tri.rs index 4eab9e105..b7d297fcc 100644 --- a/src/tri.rs +++ b/src/tri.rs @@ -6,18 +6,25 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use core::cmp::{max, min}; +use core::cmp::min; use num_traits::Zero; -use crate::{dimension::is_layout_f, Array, ArrayBase, Axis, Data, Dimension, IntoDimension, Zip}; +use crate::{ + dimension::{is_layout_c, is_layout_f}, + Array, + ArrayBase, + Axis, + Data, + Dimension, + Zip, +}; impl ArrayBase where S: Data, D: Dimension, A: Clone + Zero, - D::Smaller: Copy, { /// Upper triangular of an array. /// @@ -30,38 +37,56 @@ where /// ``` /// use ndarray::array; /// - /// let arr = array![[1, 2, 3], [4, 5, 6], [7, 8, 9]]; - /// let res = arr.triu(0); - /// assert_eq!(res, array![[1, 2, 3], [0, 5, 6], [0, 0, 9]]); + /// let arr = array![ + /// [1, 2, 3], + /// [4, 5, 6], + /// [7, 8, 9] + /// ]; + /// assert_eq!( + /// arr.triu(0), + /// array![ + /// [1, 2, 3], + /// [0, 5, 6], + /// [0, 0, 9] + /// ] + /// ); /// ``` pub fn triu(&self, k: isize) -> Array { if self.ndim() <= 1 { return self.to_owned(); } - match is_layout_f(&self.dim, &self.strides) { - true => { - let n = self.ndim(); - let mut x = self.view(); - x.swap_axes(n - 2, n - 1); - let mut tril = x.tril(-k); - tril.swap_axes(n - 2, n - 1); - - tril - } - false => { - let mut res = Array::zeros(self.raw_dim()); - Zip::indexed(self.rows()) - .and(res.rows_mut()) - .for_each(|i, src, mut dst| { - let row_num = i.into_dimension().last_elem(); - let lower = max(row_num as isize + k, 0); - dst.slice_mut(s![lower..]).assign(&src.slice(s![lower..])); - }); - - res - } + + // Performance optimization for F-order arrays. + // C-order array check prevents infinite recursion in edge cases like [[1]]. + // k-size check prevents underflow when k == isize::MIN + let n = self.ndim(); + if is_layout_f(&self.dim, &self.strides) && !is_layout_c(&self.dim, &self.strides) && k > isize::MIN { + let mut x = self.view(); + x.swap_axes(n - 2, n - 1); + let mut tril = x.tril(-k); + tril.swap_axes(n - 2, n - 1); + + return tril; } + + let mut res = Array::zeros(self.raw_dim()); + let ncols = self.len_of(Axis(n - 1)); + let nrows = self.len_of(Axis(n - 2)); + let indices = Array::from_iter(0..nrows); + Zip::from(self.rows()) + .and(res.rows_mut()) + .and_broadcast(&indices) + .for_each(|src, mut dst, row_num| { + let mut lower = match k >= 0 { + true => row_num.saturating_add(k as usize), // Avoid overflow + false => row_num.saturating_sub(k.unsigned_abs()), // Avoid underflow, go to 0 + }; + lower = min(lower, ncols); + dst.slice_mut(s![lower..]).assign(&src.slice(s![lower..])); + }); + + res } /// Lower triangular of an array. @@ -75,45 +100,65 @@ where /// ``` /// use ndarray::array; /// - /// let arr = array![[1, 2, 3], [4, 5, 6], [7, 8, 9]]; - /// let res = arr.tril(0); - /// assert_eq!(res, array![[1, 0, 0], [4, 5, 0], [7, 8, 9]]); + /// let arr = array![ + /// [1, 2, 3], + /// [4, 5, 6], + /// [7, 8, 9] + /// ]; + /// assert_eq!( + /// arr.tril(0), + /// array![ + /// [1, 0, 0], + /// [4, 5, 0], + /// [7, 8, 9] + /// ] + /// ); /// ``` pub fn tril(&self, k: isize) -> Array { if self.ndim() <= 1 { return self.to_owned(); } - match is_layout_f(&self.dim, &self.strides) { - true => { - let n = self.ndim(); - let mut x = self.view(); - x.swap_axes(n - 2, n - 1); - let mut tril = x.triu(-k); - tril.swap_axes(n - 2, n - 1); - - tril - } - false => { - let mut res = Array::zeros(self.raw_dim()); - let ncols = self.len_of(Axis(self.ndim() - 1)) as isize; - Zip::indexed(self.rows()) - .and(res.rows_mut()) - .for_each(|i, src, mut dst| { - let row_num = i.into_dimension().last_elem(); - let upper = min(row_num as isize + k, ncols) + 1; - dst.slice_mut(s![..upper]).assign(&src.slice(s![..upper])); - }); - - res - } + + // Performance optimization for F-order arrays. + // C-order array check prevents infinite recursion in edge cases like [[1]]. + // k-size check prevents underflow when k == isize::MIN + let n = self.ndim(); + if is_layout_f(&self.dim, &self.strides) && !is_layout_c(&self.dim, &self.strides) && k > isize::MIN { + let mut x = self.view(); + x.swap_axes(n - 2, n - 1); + let mut tril = x.triu(-k); + tril.swap_axes(n - 2, n - 1); + + return tril; } + + let mut res = Array::zeros(self.raw_dim()); + let ncols = self.len_of(Axis(n - 1)); + let nrows = self.len_of(Axis(n - 2)); + let indices = Array::from_iter(0..nrows); + Zip::from(self.rows()) + .and(res.rows_mut()) + .and_broadcast(&indices) + .for_each(|src, mut dst, row_num| { + // let row_num = i.into_dimension().last_elem(); + let mut upper = match k >= 0 { + true => row_num.saturating_add(k as usize).saturating_add(1), // Avoid overflow + false => row_num.saturating_sub((k + 1).unsigned_abs()), // Avoid underflow + }; + upper = min(upper, ncols); + dst.slice_mut(s![..upper]).assign(&src.slice(s![..upper])); + }); + + res } } #[cfg(test)] mod tests { + use core::isize; + use crate::{array, dimension, Array0, Array1, Array2, Array3, ShapeBuilder}; use alloc::vec; @@ -188,6 +233,19 @@ mod tests assert_eq!(res, array![[1, 0, 0], [4, 5, 0], [7, 8, 9]]); } + #[test] + fn test_2d_single() + { + let x = array![[1]]; + + assert_eq!(x.triu(0), array![[1]]); + assert_eq!(x.tril(0), array![[1]]); + assert_eq!(x.triu(1), array![[0]]); + assert_eq!(x.tril(1), array![[1]]); + assert_eq!(x.triu(-1), array![[1]]); + assert_eq!(x.tril(-1), array![[0]]); + } + #[test] fn test_3d() { @@ -285,8 +343,25 @@ mod tests let res = x.triu(0); assert_eq!(res, array![[1, 2, 3], [0, 5, 6]]); + let res = x.tril(0); + assert_eq!(res, array![[1, 0, 0], [4, 5, 0]]); + let x = array![[1, 2], [3, 4], [5, 6]]; let res = x.triu(0); assert_eq!(res, array![[1, 2], [0, 4], [0, 0]]); + + let res = x.tril(0); + assert_eq!(res, array![[1, 0], [3, 4], [5, 6]]); + } + + #[test] + fn test_odd_k() + { + let x = array![[1, 2, 3], [4, 5, 6], [7, 8, 9]]; + let z = Array2::zeros([3, 3]); + assert_eq!(x.triu(isize::MIN), x); + assert_eq!(x.tril(isize::MIN), z); + assert_eq!(x.triu(isize::MAX), z); + assert_eq!(x.tril(isize::MAX), x); } } From 6f77377d7d508550bf516e54c142cee3ab243aeb Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 14 Aug 2024 19:29:05 +0200 Subject: [PATCH 640/651] 0.16.1 --- Cargo.toml | 2 +- RELEASES.md | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 77f8a01b7..5c7217025 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ndarray" -version = "0.16.0" +version = "0.16.1" edition = "2018" rust-version = "1.64" authors = [ diff --git a/RELEASES.md b/RELEASES.md index 04c7c9250..8b4786666 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,12 @@ +Version 0.16.1 (2024-08-14) +=========================== + +- Refactor and simplify BLAS gemm call further by [@bluss](https://github.com/bluss) [#1421](https://github.com/rust-ndarray/ndarray/pull/1421) +- Fix infinite recursion and off-by-one error in triu/tril by [@akern40](https://github.com/akern40) [#1418](https://github.com/rust-ndarray/ndarray/pull/1418) +- Fix using BLAS for all compatible cases of memory layout by [@bluss](https://github.com/bluss) [#1419](https://github.com/rust-ndarray/ndarray/pull/1419) +- Use PR check instead of Merge Queue, and check rustdoc by [@bluss](https://github.com/bluss) [#1420](https://github.com/rust-ndarray/ndarray/pull/1420) +- Make iterators covariant in element type by [@bluss](https://github.com/bluss) [#1417](https://github.com/rust-ndarray/ndarray/pull/1417) + Version 0.16.0 (2024-08-03) =========================== From 1304f9d1bfa26f9c85da2c2ac192435fa9fed16b Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 21 Aug 2024 17:36:56 +0200 Subject: [PATCH 641/651] Fix uniqueness in last_mut() Last mut did not ensure the array was unique before calling uget_mut. The required properties were protected by a debug assertion, but a clear bug in release mode. Adding tests that would have caught this. --- src/impl_methods.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 589a5c83c..f506204b6 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -406,6 +406,7 @@ where if self.is_empty() { None } else { + self.ensure_unique(); let mut index = self.raw_dim(); for ax in 0..index.ndim() { index[ax] -= 1; @@ -3081,6 +3082,7 @@ mod tests { use super::*; use crate::arr3; + use defmac::defmac; #[test] fn test_flatten() @@ -3107,4 +3109,45 @@ mod tests let flattened = array.into_flat(); assert_eq!(flattened, arr1(&[1, 2, 3, 4, 5, 6, 7, 8])); } + + #[test] + fn test_first_last() + { + let first = 2; + let last = 3; + + defmac!(assert_first mut array => { + assert_eq!(array.first().copied(), Some(first)); + assert_eq!(array.first_mut().copied(), Some(first)); + }); + defmac!(assert_last mut array => { + assert_eq!(array.last().copied(), Some(last)); + assert_eq!(array.last_mut().copied(), Some(last)); + }); + + let base = Array::from_vec(vec![first, last]); + let a = base.clone(); + assert_first!(a); + + let a = base.clone(); + assert_last!(a); + + let a = CowArray::from(base.view()); + assert_first!(a); + let a = CowArray::from(base.view()); + assert_last!(a); + + let a = CowArray::from(base.clone()); + assert_first!(a); + let a = CowArray::from(base.clone()); + assert_last!(a); + + let a = ArcArray::from(base.clone()); + let _a2 = a.clone(); + assert_last!(a); + + let a = ArcArray::from(base.clone()); + let _a2 = a.clone(); + assert_first!(a); + } } From 7843a3bc3b1f00c4804346c6b637754c26bac1cf Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Wed, 21 Aug 2024 23:12:19 -0400 Subject: [PATCH 642/651] Adds vscode editor settings to gitignore Necessary for allowing format-on-save to use nightly for this repo only. --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 1e7caa9ea..dd9ffd9fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ +# Rust items Cargo.lock target/ + +# Editor settings +.vscode From 5dc62e60f4477592f22bca4e9cfc166602a91051 Mon Sep 17 00:00:00 2001 From: benliepert Date: Fri, 6 Sep 2024 08:45:37 -0400 Subject: [PATCH 643/651] Tweak documentation for into_raw_vec_and_offset (#1432) --- src/impl_owned_array.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index 44ac12dd4..bb970f876 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -79,7 +79,7 @@ where D: Dimension /// Return a vector of the elements in the array, in the way they are /// stored internally, and the index in the vector corresponding to the - /// logically first element of the array (or 0 if the array is empty). + /// logically first element of the array (or None if the array is empty). /// /// If the array is in standard memory layout, the logical element order /// of the array (`.iter()` order) and of the returned vector will be the same. From c7ebd35b79e82074977da2c67c2a25cba8ae6bcc Mon Sep 17 00:00:00 2001 From: Philip Trauth Date: Mon, 16 Sep 2024 16:52:45 +0200 Subject: [PATCH 644/651] Removed typo Just removed double and --- src/impl_methods.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index f506204b6..4a00ea000 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1030,7 +1030,7 @@ where } /// Along `axis`, select arbitrary subviews corresponding to `indices` - /// and and copy them into a new array. + /// and copy them into a new array. /// /// **Panics** if `axis` or an element of `indices` is out of bounds. /// From fce60345ddbf576d6e5cd8c4b0c8104d00c9e326 Mon Sep 17 00:00:00 2001 From: Johann Carl Meyer <32302462+johann-cm@users.noreply.github.com> Date: Thu, 26 Sep 2024 15:10:33 +0200 Subject: [PATCH 645/651] Add `diff` method as an equivalent to `numpy.diff` (#1437) * implement forward finite differneces on arrays * implement tests for the method * remove some heap allocations --- src/numeric/impl_numeric.rs | 58 ++++++++++++++++++++++++++++++- tests/numeric.rs | 68 +++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 7306fc727..6c67b9135 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -10,10 +10,11 @@ use num_traits::Float; use num_traits::One; use num_traits::{FromPrimitive, Zero}; -use std::ops::{Add, Div, Mul}; +use std::ops::{Add, Div, Mul, Sub}; use crate::imp_prelude::*; use crate::numeric_util; +use crate::Slice; /// # Numerical Methods for Arrays impl ArrayBase @@ -437,4 +438,59 @@ where { self.var_axis(axis, ddof).mapv_into(|x| x.sqrt()) } + + /// Calculates the (forward) finite differences of order `n`, along the `axis`. + /// For the 1D-case, `n==1`, this means: `diff[i] == arr[i+1] - arr[i]` + /// + /// For `n>=2`, the process is iterated: + /// ``` + /// use ndarray::{array, Axis}; + /// let arr = array![1.0, 2.0, 5.0]; + /// assert_eq!(arr.diff(2, Axis(0)), arr.diff(1, Axis(0)).diff(1, Axis(0))) + /// ``` + /// **Panics** if `axis` is out of bounds + /// + /// **Panics** if `n` is too big / the array is to short: + /// ```should_panic + /// use ndarray::{array, Axis}; + /// array![1.0, 2.0, 3.0].diff(10, Axis(0)); + /// ``` + pub fn diff(&self, n: usize, axis: Axis) -> Array + where A: Sub + Zero + Clone + { + if n == 0 { + return self.to_owned(); + } + assert!(axis.0 < self.ndim(), "The array has only ndim {}, but `axis` {:?} is given.", self.ndim(), axis); + assert!( + n < self.shape()[axis.0], + "The array must have length at least `n+1`=={} in the direction of `axis`. It has length {}", + n + 1, + self.shape()[axis.0] + ); + + let mut inp = self.to_owned(); + let mut out = Array::zeros({ + let mut inp_dim = self.raw_dim(); + // inp_dim[axis.0] >= 1 as per the 2nd assertion. + inp_dim[axis.0] -= 1; + inp_dim + }); + for _ in 0..n { + let head = inp.slice_axis(axis, Slice::from(..-1)); + let tail = inp.slice_axis(axis, Slice::from(1..)); + + azip!((o in &mut out, h in head, t in tail) *o = t.clone() - h.clone()); + + // feed the output as the input to the next iteration + std::mem::swap(&mut inp, &mut out); + + // adjust the new output array width along `axis`. + // Current situation: width of `inp`: k, `out`: k+1 + // needed width: `inp`: k, `out`: k-1 + // slice is possible, since k >= 1. + out.slice_axis_inplace(axis, Slice::from(..-2)); + } + inp + } } diff --git a/tests/numeric.rs b/tests/numeric.rs index f6de146c9..2395366b0 100644 --- a/tests/numeric.rs +++ b/tests/numeric.rs @@ -336,3 +336,71 @@ fn std_axis_empty_axis() assert_eq!(v.shape(), &[2]); v.mapv(|x| assert!(x.is_nan())); } + +#[test] +fn diff_1d_order1() +{ + let data = array![1.0, 2.0, 4.0, 7.0]; + let expected = array![1.0, 2.0, 3.0]; + assert_eq!(data.diff(1, Axis(0)), expected); +} + +#[test] +fn diff_1d_order2() +{ + let data = array![1.0, 2.0, 4.0, 7.0]; + assert_eq!( + data.diff(2, Axis(0)), + data.diff(1, Axis(0)).diff(1, Axis(0)) + ); +} + +#[test] +fn diff_1d_order3() +{ + let data = array![1.0, 2.0, 4.0, 7.0]; + assert_eq!( + data.diff(3, Axis(0)), + data.diff(1, Axis(0)).diff(1, Axis(0)).diff(1, Axis(0)) + ); +} + +#[test] +fn diff_2d_order1_ax0() +{ + let data = array![ + [1.0, 2.0, 4.0, 7.0], + [1.0, 3.0, 6.0, 6.0], + [1.5, 3.5, 5.5, 5.5] + ]; + let expected = array![[0.0, 1.0, 2.0, -1.0], [0.5, 0.5, -0.5, -0.5]]; + assert_eq!(data.diff(1, Axis(0)), expected); +} + +#[test] +fn diff_2d_order1_ax1() +{ + let data = array![ + [1.0, 2.0, 4.0, 7.0], + [1.0, 3.0, 6.0, 6.0], + [1.5, 3.5, 5.5, 5.5] + ]; + let expected = array![[1.0, 2.0, 3.0], [2.0, 3.0, 0.0], [2.0, 2.0, 0.0]]; + assert_eq!(data.diff(1, Axis(1)), expected); +} + +#[test] +#[should_panic] +fn diff_panic_n_too_big() +{ + let data = array![1.0, 2.0, 4.0, 7.0]; + data.diff(10, Axis(0)); +} + +#[test] +#[should_panic] +fn diff_panic_axis_out_of_bounds() +{ + let data = array![1, 2, 4, 7]; + data.diff(1, Axis(2)); +} From f1153bfc31639398c2e013bd6c6fb885eab83d1c Mon Sep 17 00:00:00 2001 From: XXMA16 Date: Sun, 6 Oct 2024 21:47:23 +0300 Subject: [PATCH 646/651] Ignore Jetbrains IDE config folder Signed-off-by: XXMA16 --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index dd9ffd9fe..e9b5ca25b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ target/ # Editor settings .vscode +.idea From 492b2742073cf531635d701ced4e01a827038f69 Mon Sep 17 00:00:00 2001 From: akern40 Date: Thu, 24 Oct 2024 08:26:53 -0400 Subject: [PATCH 647/651] Fixes no_std + approx combination (#1448) Fixes no_std + approx combination These two features can coexist; fixing them included: - Slightly altering tests to avoid `std` fns - Adding `feature = "std"` on some "approx" tests - Adding a line to the test script to catch this in the future --- crates/serialization-tests/tests/serialize.rs | 12 ++++++------ scripts/all-tests.sh | 2 ++ tests/azip.rs | 2 +- tests/numeric.rs | 2 ++ 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/crates/serialization-tests/tests/serialize.rs b/crates/serialization-tests/tests/serialize.rs index 6e6fb4d64..478eb20ef 100644 --- a/crates/serialization-tests/tests/serialize.rs +++ b/crates/serialization-tests/tests/serialize.rs @@ -45,13 +45,13 @@ fn serial_many_dim_serde() { // Test a sliced array. - let mut a = ArcArray::linspace(0., 31., 32) + let mut a = ArcArray::from_iter(0..32) .into_shape_with_order((2, 2, 2, 4)) .unwrap(); a.slice_collapse(s![..;-1, .., .., ..2]); let serial = serde_json::to_string(&a).unwrap(); println!("Encode {:?} => {:?}", a, serial); - let res = serde_json::from_str::>(&serial); + let res = serde_json::from_str::>(&serial); println!("{:?}", res); assert_eq!(a, res.unwrap()); } @@ -160,7 +160,7 @@ fn serial_many_dim_serde_msgpack() { // Test a sliced array. - let mut a = ArcArray::linspace(0., 31., 32) + let mut a = ArcArray::from_iter(0..32) .into_shape_with_order((2, 2, 2, 4)) .unwrap(); a.slice_collapse(s![..;-1, .., .., ..2]); @@ -171,7 +171,7 @@ fn serial_many_dim_serde_msgpack() .unwrap(); let mut deserializer = rmp_serde::Deserializer::new(&buf[..]); - let a_de: ArcArray = serde::Deserialize::deserialize(&mut deserializer).unwrap(); + let a_de: ArcArray = serde::Deserialize::deserialize(&mut deserializer).unwrap(); assert_eq!(a, a_de); } @@ -215,14 +215,14 @@ fn serial_many_dim_ron() { // Test a sliced array. - let mut a = ArcArray::linspace(0., 31., 32) + let mut a = ArcArray::from_iter(0..32) .into_shape_with_order((2, 2, 2, 4)) .unwrap(); a.slice_collapse(s![..;-1, .., .., ..2]); let a_s = ron_serialize(&a).unwrap(); - let a_de: ArcArray = ron_deserialize(&a_s).unwrap(); + let a_de: ArcArray = ron_deserialize(&a_s).unwrap(); assert_eq!(a, a_de); } diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index 4ececbcbd..b9af6b65a 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -13,6 +13,8 @@ cargo build -v --no-default-features # ndarray with no features cargo test -p ndarray -v --no-default-features +# ndarray with no_std-compatible features +cargo test -p ndarray -v --no-default-features --features approx # all with features cargo test -v --features "$FEATURES" $QC_FEAT # all with features and release (ignore test crates which is already optimized) diff --git a/tests/azip.rs b/tests/azip.rs index a4bb6ffac..d1ab5ba2a 100644 --- a/tests/azip.rs +++ b/tests/azip.rs @@ -232,7 +232,7 @@ fn test_azip3_slices() *a += b / 10.; *c = a.sin(); }); - let res = Array::linspace(0., 3.1, 32).mapv_into(f32::sin); + let res = Array::from_iter(0..32).mapv(|x| f32::sin(x as f32 / 10.)); assert_abs_diff_eq!(res, ArrayView::from(&c), epsilon = 1e-4); } diff --git a/tests/numeric.rs b/tests/numeric.rs index 2395366b0..839aba58e 100644 --- a/tests/numeric.rs +++ b/tests/numeric.rs @@ -163,6 +163,7 @@ fn std_empty_arr() #[test] #[cfg(feature = "approx")] +#[cfg(feature = "std")] fn var_axis() { use ndarray::{aview0, aview2}; @@ -222,6 +223,7 @@ fn var_axis() #[test] #[cfg(feature = "approx")] +#[cfg(feature = "std")] fn std_axis() { use ndarray::aview2; From fd3ce5d5ee8f2774d445d9b7071821a8bc8e30f2 Mon Sep 17 00:00:00 2001 From: akern40 Date: Tue, 29 Oct 2024 22:51:52 -0400 Subject: [PATCH 648/651] Adds Miri to CI/CD (#1446) This is carefully constructed to allow Miri to test most of `ndarray` without slowing down CI/CD very badly; as a result, it skips a number of slow tests. See #1446 for a list. It also excludes `blas` because Miri cannot call `cblas_gemm`, and it excludes `rayon` because it considers the still-running thread pool to be a leak. `rayon` can be re-added when rust-lang/miri#1371 is resolved. --- .github/workflows/ci.yaml | 12 ++++++++++++ ndarray-rand/tests/tests.rs | 2 ++ scripts/miri-tests.sh | 18 ++++++++++++++++++ src/dimension/mod.rs | 1 + tests/array.rs | 1 + tests/azip.rs | 2 +- tests/dimension.rs | 1 + tests/iterators.rs | 1 + tests/oper.rs | 5 +++++ 9 files changed, 42 insertions(+), 1 deletion(-) create mode 100755 scripts/miri-tests.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f36591741..0f517fac9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -88,6 +88,17 @@ jobs: run: sudo apt-get install libopenblas-dev gfortran - run: ./scripts/all-tests.sh "$FEATURES" ${{ matrix.rust }} + miri: + runs-on: ubuntu-latest + name: miri + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + components: miri + - uses: Swatinem/rust-cache@v2 + - run: ./scripts/miri-tests.sh + cross_test: #if: ${{ github.event_name == 'merge_group' }} runs-on: ubuntu-latest @@ -149,6 +160,7 @@ jobs: - format # should format be required? - nostd - tests + - miri - cross_test - cargo-careful - docs diff --git a/ndarray-rand/tests/tests.rs b/ndarray-rand/tests/tests.rs index 2db040310..e39347c0c 100644 --- a/ndarray-rand/tests/tests.rs +++ b/ndarray-rand/tests/tests.rs @@ -57,6 +57,7 @@ fn oversampling_without_replacement_should_panic() } 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.)); @@ -86,6 +87,7 @@ quickcheck! { #[cfg(feature = "quickcheck")] 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.)); diff --git a/scripts/miri-tests.sh b/scripts/miri-tests.sh new file mode 100755 index 000000000..0100f3e6a --- /dev/null +++ b/scripts/miri-tests.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +set -x +set -e + +# We rely on layout-dependent casts, which should be covered with #[repr(transparent)] +# This should catch if we missed that +RUSTFLAGS="-Zrandomize-layout" + +# Miri reports a stacked borrow violation deep within rayon, in a crate called crossbeam-epoch +# The crate has a PR to fix this: https://github.com/crossbeam-rs/crossbeam/pull/871 +# but using Miri's tree borrow mode may resolve it for now. +# Disabled until we can figure out a different rayon issue: https://github.com/rust-lang/miri/issues/1371 +# MIRIFLAGS="-Zmiri-tree-borrows" + +# General tests +# Note that we exclude blas feature because Miri can't do cblas_gemm +cargo miri test -v -p ndarray -p ndarray-rand --features approx,serde diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 601f0dc43..eb07252b2 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -1020,6 +1020,7 @@ mod test } quickcheck! { + #[cfg_attr(miri, ignore)] // Very slow on CI/CD machines // FIXME: This test is extremely slow, even with i16 values, investigate fn arith_seq_intersect_correct( first1: i8, len1: i8, step1: i8, diff --git a/tests/array.rs b/tests/array.rs index 696904dab..ac38fdd03 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -2629,6 +2629,7 @@ mod array_cow_tests }); } + #[cfg_attr(miri, ignore)] // Very slow on CI/CD machines #[test] fn test_clone_from() { diff --git a/tests/azip.rs b/tests/azip.rs index d1ab5ba2a..96be9d913 100644 --- a/tests/azip.rs +++ b/tests/azip.rs @@ -216,7 +216,7 @@ fn test_azip2_sum() } #[test] -#[cfg(feature = "approx")] +#[cfg(all(feature = "approx", feature = "std"))] fn test_azip3_slices() { use approx::assert_abs_diff_eq; diff --git a/tests/dimension.rs b/tests/dimension.rs index 6a9207e4c..fe53d96b3 100644 --- a/tests/dimension.rs +++ b/tests/dimension.rs @@ -323,6 +323,7 @@ fn test_array_view() } #[test] +#[cfg_attr(miri, ignore)] // Very slow on CI/CD machines #[cfg(feature = "std")] #[allow(clippy::cognitive_complexity)] fn test_all_ndindex() diff --git a/tests/iterators.rs b/tests/iterators.rs index 908b64d15..bdfd3ee50 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -971,6 +971,7 @@ fn test_into_iter_2d() assert_eq!(v, [1, 3, 2, 4]); } +#[cfg_attr(miri, ignore)] // Very slow on CI/CD machines #[test] fn test_into_iter_sliced() { diff --git a/tests/oper.rs b/tests/oper.rs index 5e3e669d0..401913e2b 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -502,6 +502,7 @@ fn scaled_add() } #[cfg(feature = "approx")] +#[cfg_attr(miri, ignore)] // Very slow on CI/CD machines #[test] fn scaled_add_2() { @@ -540,6 +541,7 @@ fn scaled_add_2() } #[cfg(feature = "approx")] +#[cfg_attr(miri, ignore)] // Very slow on CI/CD machines #[test] fn scaled_add_3() { @@ -592,6 +594,7 @@ fn scaled_add_3() } #[cfg(feature = "approx")] +#[cfg_attr(miri, ignore)] #[test] fn gen_mat_mul() { @@ -681,6 +684,7 @@ fn gen_mat_mul_i32() #[cfg(feature = "approx")] #[test] +#[cfg_attr(miri, ignore)] // Takes too long fn gen_mat_vec_mul() { use approx::assert_relative_eq; @@ -746,6 +750,7 @@ fn gen_mat_vec_mul() } #[cfg(feature = "approx")] +#[cfg_attr(miri, ignore)] // Very slow on CI/CD machines #[test] fn vec_mat_mul() { From 9c703ac8a7f86dce8b0b5949731b2bf364230851 Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Sat, 30 Nov 2024 12:57:46 -0500 Subject: [PATCH 649/651] Fixing lifetime elisions and minor clippy complaints --- crates/ndarray-gen/src/lib.rs | 1 - examples/bounds_check_elim.rs | 2 +- ndarray-rand/tests/tests.rs | 2 +- src/argument_traits.rs | 8 +++---- src/array_serde.rs | 4 ++-- src/arraytraits.rs | 4 ++-- src/data_traits.rs | 24 ++++++++++---------- src/dimension/axes.rs | 4 ++-- src/dimension/ndindex.rs | 6 ++--- src/impl_cow.rs | 2 +- src/impl_views/constructors.rs | 4 ++-- src/impl_views/indexing.rs | 2 +- src/impl_views/splitting.rs | 2 +- src/iterators/mod.rs | 40 +++++++++++++++++----------------- src/lib.rs | 2 +- src/split_at.rs | 2 +- tests/azip.rs | 12 +++++----- 17 files changed, 60 insertions(+), 61 deletions(-) diff --git a/crates/ndarray-gen/src/lib.rs b/crates/ndarray-gen/src/lib.rs index 7f9ca89fc..09440e68d 100644 --- a/crates/ndarray-gen/src/lib.rs +++ b/crates/ndarray-gen/src/lib.rs @@ -8,5 +8,4 @@ // except according to those terms. /// Build ndarray arrays for test purposes - pub mod array_builder; diff --git a/examples/bounds_check_elim.rs b/examples/bounds_check_elim.rs index e6b57c719..f1a91cca0 100644 --- a/examples/bounds_check_elim.rs +++ b/examples/bounds_check_elim.rs @@ -57,7 +57,7 @@ pub fn test1d_single_mut(a: &mut Array1, i: usize) -> f64 #[no_mangle] pub fn test1d_len_of(a: &Array1) -> f64 { - let a = &*a; + let a = a; let mut sum = 0.; for i in 0..a.len_of(Axis(0)) { sum += a[i]; diff --git a/ndarray-rand/tests/tests.rs b/ndarray-rand/tests/tests.rs index e39347c0c..d38e8636e 100644 --- a/ndarray-rand/tests/tests.rs +++ b/ndarray-rand/tests/tests.rs @@ -122,7 +122,7 @@ fn sampling_works(a: &Array2, strategy: SamplingStrategy, axis: Axis, n_sam let samples = a.sample_axis(axis, n_samples, strategy); samples .axis_iter(axis) - .all(|lane| is_subset(&a, &lane, axis)) + .all(|lane| is_subset(a, &lane, axis)) } // Check if, when sliced along `axis`, there is at least one lane in `a` equal to `b` diff --git a/src/argument_traits.rs b/src/argument_traits.rs index de8ac7f99..c4e85186a 100644 --- a/src/argument_traits.rs +++ b/src/argument_traits.rs @@ -11,7 +11,7 @@ pub trait AssignElem } /// Assignable element, simply `*self = input`. -impl<'a, T> AssignElem for &'a mut T +impl AssignElem for &mut T { fn assign_elem(self, input: T) { @@ -20,7 +20,7 @@ impl<'a, T> AssignElem for &'a mut T } /// Assignable element, simply `self.set(input)`. -impl<'a, T> AssignElem for &'a Cell +impl AssignElem for &Cell { fn assign_elem(self, input: T) { @@ -29,7 +29,7 @@ impl<'a, T> AssignElem for &'a Cell } /// Assignable element, simply `self.set(input)`. -impl<'a, T> AssignElem for &'a MathCell +impl AssignElem for &MathCell { fn assign_elem(self, input: T) { @@ -39,7 +39,7 @@ impl<'a, T> AssignElem for &'a MathCell /// Assignable element, the item in the MaybeUninit is overwritten (prior value, if any, is not /// read or dropped). -impl<'a, T> AssignElem for &'a mut MaybeUninit +impl AssignElem for &mut MaybeUninit { fn assign_elem(self, input: T) { diff --git a/src/array_serde.rs b/src/array_serde.rs index 31b613d4c..50d9c2905 100644 --- a/src/array_serde.rs +++ b/src/array_serde.rs @@ -98,7 +98,7 @@ where // private iterator wrapper struct Sequence<'a, A, D>(Iter<'a, A, D>); -impl<'a, A, D> Serialize for Sequence<'a, A, D> +impl Serialize for Sequence<'_, A, D> where A: Serialize, D: Dimension + Serialize, @@ -162,7 +162,7 @@ impl<'de> Deserialize<'de> for ArrayField { struct ArrayFieldVisitor; - impl<'de> Visitor<'de> for ArrayFieldVisitor + impl Visitor<'_> for ArrayFieldVisitor { type Value = ArrayField; diff --git a/src/arraytraits.rs b/src/arraytraits.rs index e68b5d56a..d7a00fcfe 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -128,7 +128,7 @@ where /// Return `true` if the array shapes and all elements of `self` and /// `rhs` are equal. Return `false` otherwise. #[allow(clippy::unconditional_recursion)] // false positive -impl<'a, A, B, S, S2, D> PartialEq<&'a ArrayBase> for ArrayBase +impl PartialEq<&ArrayBase> for ArrayBase where A: PartialEq, S: Data, @@ -144,7 +144,7 @@ where /// Return `true` if the array shapes and all elements of `self` and /// `rhs` are equal. Return `false` otherwise. #[allow(clippy::unconditional_recursion)] // false positive -impl<'a, A, B, S, S2, D> PartialEq> for &'a ArrayBase +impl PartialEq> for &ArrayBase where A: PartialEq, S: Data, diff --git a/src/data_traits.rs b/src/data_traits.rs index f43bfb4ef..fc2fe4bfa 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -407,7 +407,7 @@ where A: Clone } } -unsafe impl<'a, A> RawData for ViewRepr<&'a A> +unsafe impl RawData for ViewRepr<&A> { type Elem = A; @@ -420,7 +420,7 @@ unsafe impl<'a, A> RawData for ViewRepr<&'a A> private_impl! {} } -unsafe impl<'a, A> Data for ViewRepr<&'a A> +unsafe impl Data for ViewRepr<&A> { fn into_owned(self_: ArrayBase) -> Array where @@ -437,7 +437,7 @@ unsafe impl<'a, A> Data for ViewRepr<&'a A> } } -unsafe impl<'a, A> RawDataClone for ViewRepr<&'a A> +unsafe impl RawDataClone for ViewRepr<&A> { unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) { @@ -445,7 +445,7 @@ unsafe impl<'a, A> RawDataClone for ViewRepr<&'a A> } } -unsafe impl<'a, A> RawData for ViewRepr<&'a mut A> +unsafe impl RawData for ViewRepr<&mut A> { type Elem = A; @@ -458,7 +458,7 @@ unsafe impl<'a, A> RawData for ViewRepr<&'a mut A> private_impl! {} } -unsafe impl<'a, A> RawDataMut for ViewRepr<&'a mut A> +unsafe impl RawDataMut for ViewRepr<&mut A> { #[inline] fn try_ensure_unique(_: &mut ArrayBase) @@ -475,7 +475,7 @@ unsafe impl<'a, A> RawDataMut for ViewRepr<&'a mut A> } } -unsafe impl<'a, A> Data for ViewRepr<&'a mut A> +unsafe impl Data for ViewRepr<&mut A> { fn into_owned(self_: ArrayBase) -> Array where @@ -492,7 +492,7 @@ unsafe impl<'a, A> Data for ViewRepr<&'a mut A> } } -unsafe impl<'a, A> DataMut for ViewRepr<&'a mut A> {} +unsafe impl DataMut for ViewRepr<&mut A> {} /// Array representation trait. /// @@ -533,7 +533,7 @@ pub unsafe trait DataOwned: Data pub unsafe trait DataShared: Clone + Data + RawDataClone {} unsafe impl DataShared for OwnedArcRepr {} -unsafe impl<'a, A> DataShared for ViewRepr<&'a A> {} +unsafe impl DataShared for ViewRepr<&A> {} unsafe impl DataOwned for OwnedRepr { @@ -571,7 +571,7 @@ unsafe impl DataOwned for OwnedArcRepr } } -unsafe impl<'a, A> RawData for CowRepr<'a, A> +unsafe impl RawData for CowRepr<'_, A> { type Elem = A; @@ -587,7 +587,7 @@ unsafe impl<'a, A> RawData for CowRepr<'a, A> private_impl! {} } -unsafe impl<'a, A> RawDataMut for CowRepr<'a, A> +unsafe impl RawDataMut for CowRepr<'_, A> where A: Clone { #[inline] @@ -615,7 +615,7 @@ where A: Clone } } -unsafe impl<'a, A> RawDataClone for CowRepr<'a, A> +unsafe impl RawDataClone for CowRepr<'_, A> where A: Clone { unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) @@ -681,7 +681,7 @@ unsafe impl<'a, A> Data for CowRepr<'a, A> } } -unsafe impl<'a, A> DataMut for CowRepr<'a, A> where A: Clone {} +unsafe impl DataMut for CowRepr<'_, A> where A: Clone {} unsafe impl<'a, A> DataOwned for CowRepr<'a, A> { diff --git a/src/dimension/axes.rs b/src/dimension/axes.rs index 45b7a75f0..c7aaff149 100644 --- a/src/dimension/axes.rs +++ b/src/dimension/axes.rs @@ -60,7 +60,7 @@ pub struct AxisDescription copy_and_clone!(AxisDescription); copy_and_clone!(['a, D] Axes<'a, D>); -impl<'a, D> Iterator for Axes<'a, D> +impl Iterator for Axes<'_, D> where D: Dimension { /// Description of the axis, its length and its stride. @@ -99,7 +99,7 @@ where D: Dimension } } -impl<'a, D> DoubleEndedIterator for Axes<'a, D> +impl DoubleEndedIterator for Axes<'_, D> where D: Dimension { fn next_back(&mut self) -> Option diff --git a/src/dimension/ndindex.rs b/src/dimension/ndindex.rs index 7bc2c54ef..ca2a3ea69 100644 --- a/src/dimension/ndindex.rs +++ b/src/dimension/ndindex.rs @@ -255,7 +255,7 @@ unsafe impl NdIndex for [Ix; N] } } -impl<'a> IntoDimension for &'a [Ix] +impl IntoDimension for &[Ix] { type Dim = IxDyn; fn into_dimension(self) -> Self::Dim @@ -264,7 +264,7 @@ impl<'a> IntoDimension for &'a [Ix] } } -unsafe impl<'a> NdIndex for &'a IxDyn +unsafe impl NdIndex for &IxDyn { fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { @@ -276,7 +276,7 @@ unsafe impl<'a> NdIndex for &'a IxDyn } } -unsafe impl<'a> NdIndex for &'a [Ix] +unsafe impl NdIndex for &[Ix] { fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { diff --git a/src/impl_cow.rs b/src/impl_cow.rs index f064ce7bd..4843e305b 100644 --- a/src/impl_cow.rs +++ b/src/impl_cow.rs @@ -11,7 +11,7 @@ use crate::imp_prelude::*; /// Methods specific to `CowArray`. /// /// ***See also all methods for [`ArrayBase`]*** -impl<'a, A, D> CowArray<'a, A, D> +impl CowArray<'_, A, D> where D: Dimension { /// Returns `true` iff the array is the view (borrowed) variant. diff --git a/src/impl_views/constructors.rs b/src/impl_views/constructors.rs index 15f2b9b6b..d0089057d 100644 --- a/src/impl_views/constructors.rs +++ b/src/impl_views/constructors.rs @@ -230,7 +230,7 @@ where D: Dimension } /// Private array view methods -impl<'a, A, D> ArrayView<'a, A, D> +impl ArrayView<'_, A, D> where D: Dimension { /// Create a new `ArrayView` @@ -254,7 +254,7 @@ where D: Dimension } } -impl<'a, A, D> ArrayViewMut<'a, A, D> +impl ArrayViewMut<'_, A, D> where D: Dimension { /// Create a new `ArrayView` diff --git a/src/impl_views/indexing.rs b/src/impl_views/indexing.rs index 2b72c2142..827313478 100644 --- a/src/impl_views/indexing.rs +++ b/src/impl_views/indexing.rs @@ -100,7 +100,7 @@ pub trait IndexLonger unsafe fn uget(self, index: I) -> Self::Output; } -impl<'a, 'b, I, A, D> IndexLonger for &'b ArrayView<'a, A, D> +impl<'a, I, A, D> IndexLonger for &ArrayView<'a, A, D> where I: NdIndex, D: Dimension, diff --git a/src/impl_views/splitting.rs b/src/impl_views/splitting.rs index 6d6ea275b..58d0a7556 100644 --- a/src/impl_views/splitting.rs +++ b/src/impl_views/splitting.rs @@ -11,7 +11,7 @@ use crate::slice::MultiSliceArg; use num_complex::Complex; /// Methods for read-only array views. -impl<'a, A, D> ArrayView<'a, A, D> +impl ArrayView<'_, A, D> where D: Dimension { /// Split the array view along `axis` and return one view strictly before the diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index e7321d15b..01fff14f5 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -260,7 +260,7 @@ impl<'a, A> DoubleEndedIterator for ElementsBase<'a, A, Ix1> } } -impl<'a, A, D> ExactSizeIterator for ElementsBase<'a, A, D> +impl ExactSizeIterator for ElementsBase<'_, A, D> where D: Dimension { fn len(&self) -> usize @@ -503,7 +503,7 @@ impl<'a, A> DoubleEndedIterator for Iter<'a, A, Ix1> } } -impl<'a, A, D> ExactSizeIterator for Iter<'a, A, D> +impl ExactSizeIterator for Iter<'_, A, D> where D: Dimension { fn len(&self) -> usize @@ -534,7 +534,7 @@ impl<'a, A, D: Dimension> Iterator for IndexedIter<'a, A, D> } } -impl<'a, A, D> ExactSizeIterator for IndexedIter<'a, A, D> +impl ExactSizeIterator for IndexedIter<'_, A, D> where D: Dimension { fn len(&self) -> usize @@ -635,7 +635,7 @@ impl<'a, A> DoubleEndedIterator for IterMut<'a, A, Ix1> } } -impl<'a, A, D> ExactSizeIterator for IterMut<'a, A, D> +impl ExactSizeIterator for IterMut<'_, A, D> where D: Dimension { fn len(&self) -> usize @@ -686,7 +686,7 @@ impl<'a, A> DoubleEndedIterator for ElementsBaseMut<'a, A, Ix1> } } -impl<'a, A, D> ExactSizeIterator for ElementsBaseMut<'a, A, D> +impl ExactSizeIterator for ElementsBaseMut<'_, A, D> where D: Dimension { fn len(&self) -> usize @@ -717,7 +717,7 @@ impl<'a, A, D: Dimension> Iterator for IndexedIterMut<'a, A, D> } } -impl<'a, A, D> ExactSizeIterator for IndexedIterMut<'a, A, D> +impl ExactSizeIterator for IndexedIterMut<'_, A, D> where D: Dimension { fn len(&self) -> usize @@ -767,7 +767,7 @@ where D: Dimension } } -impl<'a, A, D> ExactSizeIterator for LanesIter<'a, A, D> +impl ExactSizeIterator for LanesIter<'_, A, D> where D: Dimension { fn len(&self) -> usize @@ -776,7 +776,7 @@ where D: Dimension } } -impl<'a, A> DoubleEndedIterator for LanesIter<'a, A, Ix1> +impl DoubleEndedIterator for LanesIter<'_, A, Ix1> { fn next_back(&mut self) -> Option { @@ -819,7 +819,7 @@ where D: Dimension } } -impl<'a, A, D> ExactSizeIterator for LanesIterMut<'a, A, D> +impl ExactSizeIterator for LanesIterMut<'_, A, D> where D: Dimension { fn len(&self) -> usize @@ -828,7 +828,7 @@ where D: Dimension } } -impl<'a, A> DoubleEndedIterator for LanesIterMut<'a, A, Ix1> +impl DoubleEndedIterator for LanesIterMut<'_, A, Ix1> { fn next_back(&mut self) -> Option { @@ -1079,7 +1079,7 @@ where D: Dimension } } -impl<'a, A, D> DoubleEndedIterator for AxisIter<'a, A, D> +impl DoubleEndedIterator for AxisIter<'_, A, D> where D: Dimension { fn next_back(&mut self) -> Option @@ -1088,7 +1088,7 @@ where D: Dimension } } -impl<'a, A, D> ExactSizeIterator for AxisIter<'a, A, D> +impl ExactSizeIterator for AxisIter<'_, A, D> where D: Dimension { fn len(&self) -> usize @@ -1169,7 +1169,7 @@ where D: Dimension } } -impl<'a, A, D> DoubleEndedIterator for AxisIterMut<'a, A, D> +impl DoubleEndedIterator for AxisIterMut<'_, A, D> where D: Dimension { fn next_back(&mut self) -> Option @@ -1178,7 +1178,7 @@ where D: Dimension } } -impl<'a, A, D> ExactSizeIterator for AxisIterMut<'a, A, D> +impl ExactSizeIterator for AxisIterMut<'_, A, D> where D: Dimension { fn len(&self) -> usize @@ -1187,7 +1187,7 @@ where D: Dimension } } -impl<'a, A, D: Dimension> NdProducer for AxisIter<'a, A, D> +impl NdProducer for AxisIter<'_, A, D> { type Item = ::Item; type Dim = Ix1; @@ -1246,7 +1246,7 @@ impl<'a, A, D: Dimension> NdProducer for AxisIter<'a, A, D> private_impl! {} } -impl<'a, A, D: Dimension> NdProducer for AxisIterMut<'a, A, D> +impl NdProducer for AxisIterMut<'_, A, D> { type Item = ::Item; type Dim = Ix1; @@ -1555,12 +1555,12 @@ unsafe impl TrustedIterator for Linspace {} unsafe impl TrustedIterator for Geomspace {} #[cfg(feature = "std")] unsafe impl TrustedIterator for Logspace {} -unsafe impl<'a, A, D> TrustedIterator for Iter<'a, A, D> {} -unsafe impl<'a, A, D> TrustedIterator for IterMut<'a, A, D> {} +unsafe impl TrustedIterator for Iter<'_, A, D> {} +unsafe impl TrustedIterator for IterMut<'_, A, D> {} unsafe impl TrustedIterator for std::iter::Cloned where I: TrustedIterator {} unsafe impl TrustedIterator for std::iter::Map where I: TrustedIterator {} -unsafe impl<'a, A> TrustedIterator for slice::Iter<'a, A> {} -unsafe impl<'a, A> TrustedIterator for slice::IterMut<'a, A> {} +unsafe impl TrustedIterator for slice::Iter<'_, A> {} +unsafe impl TrustedIterator for slice::IterMut<'_, A> {} unsafe impl TrustedIterator for ::std::ops::Range {} // FIXME: These indices iter are dubious -- size needs to be checked up front. unsafe impl TrustedIterator for IndicesIter where D: Dimension {} diff --git a/src/lib.rs b/src/lib.rs index f52f25e5e..b163f16a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1498,7 +1498,7 @@ pub enum CowRepr<'a, A> Owned(OwnedRepr), } -impl<'a, A> CowRepr<'a, A> +impl CowRepr<'_, A> { /// Returns `true` iff the data is the `View` variant. pub fn is_view(&self) -> bool diff --git a/src/split_at.rs b/src/split_at.rs index 4af1403c0..5dee44b63 100644 --- a/src/split_at.rs +++ b/src/split_at.rs @@ -35,7 +35,7 @@ where D: Dimension } } -impl<'a, A, D> SplitAt for ArrayViewMut<'a, A, D> +impl SplitAt for ArrayViewMut<'_, A, D> where D: Dimension { fn split_at(self, axis: Axis, index: usize) -> (Self, Self) diff --git a/tests/azip.rs b/tests/azip.rs index 96be9d913..9d8bebab7 100644 --- a/tests/azip.rs +++ b/tests/azip.rs @@ -118,7 +118,7 @@ fn test_zip_collect_drop() struct Recorddrop<'a>((usize, usize), &'a RefCell>); - impl<'a> Drop for Recorddrop<'a> + impl Drop for Recorddrop<'_> { fn drop(&mut self) { @@ -470,9 +470,9 @@ fn test_zip_all() let b = Array::::ones(62); let mut c = Array::::ones(62); c[5] = 0.0; - assert_eq!(true, Zip::from(&a).and(&b).all(|&x, &y| x + y == 1.0)); - assert_eq!(false, Zip::from(&a).and(&b).all(|&x, &y| x == y)); - assert_eq!(false, Zip::from(&a).and(&c).all(|&x, &y| x + y == 1.0)); + assert!(Zip::from(&a).and(&b).all(|&x, &y| x + y == 1.0)); + assert!(!Zip::from(&a).and(&b).all(|&x, &y| x == y)); + assert!(!Zip::from(&a).and(&c).all(|&x, &y| x + y == 1.0)); } #[test] @@ -480,6 +480,6 @@ fn test_zip_all_empty_array() { let a = Array::::zeros(0); let b = Array::::ones(0); - assert_eq!(true, Zip::from(&a).and(&b).all(|&_x, &_y| true)); - assert_eq!(true, Zip::from(&a).and(&b).all(|&_x, &_y| false)); + assert!(Zip::from(&a).and(&b).all(|&_x, &_y| true)); + assert!(Zip::from(&a).and(&b).all(|&_x, &_y| false)); } From 4e61c87a7dcefac784deae749e0dd982800a9379 Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Sat, 30 Nov 2024 12:58:36 -0500 Subject: [PATCH 650/651] Changing CI to account for BLAS requiring MSRV > 1.64 --- .github/workflows/ci.yaml | 14 ++++++++++++++ README.rst | 8 ++++++++ scripts/all-tests.sh | 5 +++-- scripts/blas-integ-tests.sh | 11 +++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100755 scripts/blas-integ-tests.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0f517fac9..c910b32e0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -88,6 +88,20 @@ jobs: run: sudo apt-get install libopenblas-dev gfortran - run: ./scripts/all-tests.sh "$FEATURES" ${{ matrix.rust }} + blas-msrv: + runs-on: ubuntu-latest + name: blas-msrv + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.67.0 # BLAS MSRV + - uses: rui314/setup-mold@v1 + - uses: Swatinem/rust-cache@v2 + - name: Install openblas + run: sudo apt-get install libopenblas-dev gfortran + - run: ./scripts/blas-integ-tests.sh "$FEATURES" 1.67.0 + miri: runs-on: ubuntu-latest name: miri diff --git a/README.rst b/README.rst index abac4c18e..ef6577f13 100644 --- a/README.rst +++ b/README.rst @@ -156,6 +156,14 @@ there is no tight coupling to the ``blas-src`` version, so version selection is 0.13 0.2.0 0.6.0 =========== ============ ================ ============== +------------ +BLAS on MSRV +------------ + +Although ``ndarray`` currently maintains an MSRV of 1.64.0, this is separate from the MSRV (either stated or real) of the various BLAS providers. +As of the time of writing, ``openblas`` currently supports MSRV of 1.67.0. +So, while ``ndarray`` and ``openblas-src`` are compatible, they can only work together with toolchains 1.67.0 or above. + Recent Changes -------------- diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index b9af6b65a..e98b90df1 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -23,8 +23,9 @@ cargo test -v -p ndarray -p ndarray-rand --release --features "$FEATURES" $QC_FE # BLAS tests cargo test -p ndarray --lib -v --features blas cargo test -p blas-mock-tests -v -cargo test -p blas-tests -v --features blas-tests/openblas-system -cargo test -p numeric-tests -v --features numeric-tests/test_blas +if [ "$CHANNEL" != "1.64.0" ]; then + ./scripts/blas-integ-tests.sh "$FEATURES" $CHANNEL +fi # Examples cargo test --examples diff --git a/scripts/blas-integ-tests.sh b/scripts/blas-integ-tests.sh new file mode 100755 index 000000000..5192d67e3 --- /dev/null +++ b/scripts/blas-integ-tests.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +set -x +set -e + +FEATURES=$1 +CHANNEL=$2 + +# BLAS tests +cargo test -p blas-tests -v --features blas-tests/openblas-system +cargo test -p numeric-tests -v --features numeric-tests/test_blas From d5f32ec06e27d8705dcf5da4e6596abf51188909 Mon Sep 17 00:00:00 2001 From: akern40 Date: Thu, 19 Dec 2024 21:28:07 -0800 Subject: [PATCH 651/651] Pin openblas to >=0.10.11 in order to fix blas-compatible MSRV to 0.71.1 (#1465) --- .github/workflows/ci.yaml | 8 ++++++-- README.rst | 4 ++-- crates/blas-tests/Cargo.toml | 2 +- crates/numeric-tests/Cargo.toml | 2 +- scripts/blas-integ-tests.sh | 3 +-- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c910b32e0..ae74aeb45 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,6 +12,8 @@ env: HOST: x86_64-unknown-linux-gnu FEATURES: "test docs" RUSTFLAGS: "-D warnings" + MSRV: 1.64.0 + BLAS_MSRV: 1.71.1 jobs: clippy: @@ -95,12 +97,14 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.67.0 # BLAS MSRV + toolchain: 1.71.1 # BLAS MSRV - uses: rui314/setup-mold@v1 - uses: Swatinem/rust-cache@v2 - name: Install openblas run: sudo apt-get install libopenblas-dev gfortran - - run: ./scripts/blas-integ-tests.sh "$FEATURES" 1.67.0 + - run: cargo tree -p blas-tests -i openblas-src -F blas-tests/openblas-system + - run: cargo tree -p blas-tests -i openblas-build -F blas-tests/openblas-system + - run: ./scripts/blas-integ-tests.sh $BLAS_MSRV miri: runs-on: ubuntu-latest diff --git a/README.rst b/README.rst index ef6577f13..49558b1c1 100644 --- a/README.rst +++ b/README.rst @@ -161,8 +161,8 @@ BLAS on MSRV ------------ Although ``ndarray`` currently maintains an MSRV of 1.64.0, this is separate from the MSRV (either stated or real) of the various BLAS providers. -As of the time of writing, ``openblas`` currently supports MSRV of 1.67.0. -So, while ``ndarray`` and ``openblas-src`` are compatible, they can only work together with toolchains 1.67.0 or above. +As of the time of writing, ``openblas`` currently supports MSRV of 1.71.1. +So, while ``ndarray`` and ``openblas-src`` are compatible, they can only work together with toolchains 1.71.1 or above. Recent Changes -------------- diff --git a/crates/blas-tests/Cargo.toml b/crates/blas-tests/Cargo.toml index 05a656000..ff556873a 100644 --- a/crates/blas-tests/Cargo.toml +++ b/crates/blas-tests/Cargo.toml @@ -15,7 +15,7 @@ ndarray = { workspace = true, features = ["approx", "blas"] } ndarray-gen = { workspace = true } blas-src = { version = "0.10", optional = true } -openblas-src = { version = "0.10", optional = true } +openblas-src = { version = ">=0.10.11", optional = true } netlib-src = { version = "0.8", optional = true } blis-src = { version = "0.2", features = ["system"], optional = true } diff --git a/crates/numeric-tests/Cargo.toml b/crates/numeric-tests/Cargo.toml index 214612258..93a182e66 100644 --- a/crates/numeric-tests/Cargo.toml +++ b/crates/numeric-tests/Cargo.toml @@ -19,7 +19,7 @@ rand = { workspace = true } rand_distr = { workspace = true } blas-src = { optional = true, version = "0.10", default-features = false, features = ["openblas"] } -openblas-src = { optional = true, version = "0.10", default-features = false, features = ["cblas", "system"] } +openblas-src = { optional = true, version = ">=0.10.11", default-features = false, features = ["cblas", "system"] } [dev-dependencies] num-traits = { workspace = true } diff --git a/scripts/blas-integ-tests.sh b/scripts/blas-integ-tests.sh index 5192d67e3..fec938b83 100755 --- a/scripts/blas-integ-tests.sh +++ b/scripts/blas-integ-tests.sh @@ -3,8 +3,7 @@ set -x set -e -FEATURES=$1 -CHANNEL=$2 +CHANNEL=$1 # BLAS tests cargo test -p blas-tests -v --features blas-tests/openblas-system

(self, part: P) -> Zip<($($p,)* P, ), D> + where P: NdProducer, + { + let part_layout = part.layout(); let ($($p,)*) = self.parts; Zip { - parts: ($($p,)* array, ), + parts: ($($p,)* part, ), layout: self.layout.and(part_layout), dimension: self.dimension, } From f6239c07b3b5a3ef8def1b5fec789e7ac59efa05 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 22 Apr 2020 15:31:09 +0200 Subject: [PATCH 046/651] FIX: Layout, make all the constructors pub(crate) They were previously pub, just because we didn't have the pub(crate) feature yet. --- src/layout/mod.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 57983cc3b..3804a160b 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -25,27 +25,24 @@ impl Layout { pub(crate) fn flag(self) -> u32 { self.0 } -} -impl Layout { - #[doc(hidden)] #[inline(always)] - pub fn one_dimensional() -> Layout { + pub(crate) fn one_dimensional() -> Layout { Layout(CORDER | FORDER) } - #[doc(hidden)] + #[inline(always)] - pub fn c() -> Layout { + pub(crate) fn c() -> Layout { Layout(CORDER) } - #[doc(hidden)] + #[inline(always)] - pub fn f() -> Layout { + pub(crate) fn f() -> Layout { Layout(FORDER) } + #[inline(always)] - #[doc(hidden)] - pub fn none() -> Layout { + pub(crate) fn none() -> Layout { Layout(0) } } From 2a44fb663c665ff2ba1b45f273ac08cda0288465 Mon Sep 17 00:00:00 2001 From: Nil Goyette Date: Tue, 21 Apr 2020 22:00:44 +0200 Subject: [PATCH 047/651] TEST: Zip vs Zip::indexed etc benchmarks --- benches/zip.rs | 109 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 benches/zip.rs diff --git a/benches/zip.rs b/benches/zip.rs new file mode 100644 index 000000000..d44003802 --- /dev/null +++ b/benches/zip.rs @@ -0,0 +1,109 @@ +#![feature(test)] +extern crate test; +use test::{black_box, Bencher}; +use ndarray::{Array3, ShapeBuilder, Zip}; + +pub fn zip_copy(data: &Array3, out: &mut Array3) { + Zip::from(data).and(out).apply(|&i, o| { + *o = i; + }); +} + +pub fn zip_indexed(data: &Array3, out: &mut Array3) { + Zip::indexed(data).and(out).apply(|idx, &i, o| { + *o = i; + }); +} + +pub fn zip_mut_with(data: &Array3, out: &mut Array3) { + out.zip_mut_with(&data, |o, &i| { + *o = i; + }); +} + +// array size in benchmarks +const SZ3: (usize, usize, usize) = (137, 171, 151); + +#[bench] +fn zip_cf(b: &mut Bencher) { + let data: Array3 = Array3::zeros(SZ3); + let mut out = Array3::zeros(data.dim().f()); + b.iter(|| black_box(zip_copy(&data, &mut out))); +} + +#[bench] +fn zip_cc(b: &mut Bencher) { + let data: Array3 = Array3::zeros(SZ3); + let mut out = Array3::zeros(data.dim()); + b.iter(|| black_box(zip_copy(&data, &mut out))); +} + +#[bench] +fn zip_fc(b: &mut Bencher) { + let data: Array3 = Array3::zeros(SZ3.f()); + let mut out = Array3::zeros(data.dim()); + b.iter(|| black_box(zip_copy(&data, &mut out))); +} + +#[bench] +fn zip_ff(b: &mut Bencher) { + let data: Array3 = Array3::zeros(SZ3.f()); + let mut out = Array3::zeros(data.dim().f()); + b.iter(|| black_box(zip_copy(&data, &mut out))); +} + +#[bench] +fn zip_indexed_cf(b: &mut Bencher) { + let data: Array3 = Array3::zeros(SZ3); + let mut out = Array3::zeros(data.dim().f()); + b.iter(|| black_box(zip_indexed(&data, &mut out))); +} + +#[bench] +fn zip_indexed_cc(b: &mut Bencher) { + let data: Array3 = Array3::zeros(SZ3); + let mut out = Array3::zeros(data.dim()); + b.iter(|| black_box(zip_indexed(&data, &mut out))); +} + +#[bench] +fn zip_indexed_fc(b: &mut Bencher) { + let data: Array3 = Array3::zeros(SZ3.f()); + let mut out = Array3::zeros(data.dim()); + b.iter(|| black_box(zip_indexed(&data, &mut out))); +} + +#[bench] +fn zip_indexed_ff(b: &mut Bencher) { + let data: Array3 = Array3::zeros(SZ3.f()); + let mut out = Array3::zeros(data.dim().f()); + b.iter(|| black_box(zip_indexed(&data, &mut out))); +} + +#[bench] +fn zip_mut_with_cf(b: &mut Bencher) { + let data: Array3 = Array3::zeros(SZ3); + let mut out = Array3::zeros(data.dim().f()); + b.iter(|| black_box(zip_mut_with(&data, &mut out))); +} + +#[bench] +fn zip_mut_with_cc(b: &mut Bencher) { + let data: Array3 = Array3::zeros(SZ3); + let mut out = Array3::zeros(data.dim()); + b.iter(|| black_box(zip_mut_with(&data, &mut out))); +} + +#[bench] +fn zip_mut_with_fc(b: &mut Bencher) { + let data: Array3 = Array3::zeros(SZ3.f()); + let mut out = Array3::zeros(data.dim()); + b.iter(|| black_box(zip_mut_with(&data, &mut out))); +} + +#[bench] +fn zip_mut_with_ff(b: &mut Bencher) { + let data: Array3 = Array3::zeros(SZ3.f()); + let mut out = Array3::zeros(data.dim().f()); + b.iter(|| black_box(zip_mut_with(&data, &mut out))); +} From 55998bb8d2c40f42510265e723472cb20480fd4e Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 21 Apr 2020 22:30:44 +0200 Subject: [PATCH 048/651] TEST: Test non-contig arrays in Zip benchmarks, also with splits Using split tests performance of the Zip in parallelization, so that we can see if there are benefits to splitting arrays better. --- benches/iter.rs | 4 +- benches/zip.rs | 102 ++++++++++++++++++++++++++---------------------- 2 files changed, 58 insertions(+), 48 deletions(-) diff --git a/benches/iter.rs b/benches/iter.rs index 24bda95b9..8b89e721b 100644 --- a/benches/iter.rs +++ b/benches/iter.rs @@ -381,12 +381,12 @@ pub fn zip_mut_with(data: &Array3, out: &mut Array3) { fn zip_mut_with_cc(b: &mut Bencher) { let data: Array3 = Array3::zeros((ISZ, ISZ, ISZ)); let mut out = Array3::zeros(data.dim()); - b.iter(|| black_box(zip_mut_with(&data, &mut out))); + b.iter(|| zip_mut_with(&data, &mut out)); } #[bench] fn zip_mut_with_ff(b: &mut Bencher) { let data: Array3 = Array3::zeros((ISZ, ISZ, ISZ).f()); let mut out = Array3::zeros(data.dim().f()); - b.iter(|| black_box(zip_mut_with(&data, &mut out))); + b.iter(|| zip_mut_with(&data, &mut out)); } diff --git a/benches/zip.rs b/benches/zip.rs index d44003802..1fe74b970 100644 --- a/benches/zip.rs +++ b/benches/zip.rs @@ -1,109 +1,119 @@ #![feature(test)] extern crate test; -use test::{black_box, Bencher}; +use test::{Bencher}; use ndarray::{Array3, ShapeBuilder, Zip}; - -pub fn zip_copy(data: &Array3, out: &mut Array3) { +use ndarray::s; +use ndarray::IntoNdProducer; + +pub fn zip_copy<'a, A, P, Q>(data: P, out: Q) + where P: IntoNdProducer, + Q: IntoNdProducer, + A: Copy + 'a +{ Zip::from(data).and(out).apply(|&i, o| { *o = i; }); } -pub fn zip_indexed(data: &Array3, out: &mut Array3) { - Zip::indexed(data).and(out).apply(|idx, &i, o| { - *o = i; - }); +pub fn zip_copy_split<'a, A, P, Q>(data: P, out: Q) + where P: IntoNdProducer, + Q: IntoNdProducer, + A: Copy + 'a +{ + let z = Zip::from(data).and(out); + let (z1, z2) = z.split(); + let (z11, z12) = z1.split(); + let (z21, z22) = z2.split(); + let f = |&i: &A, o: &mut A| *o = i; + z11.apply(f); + z12.apply(f); + z21.apply(f); + z22.apply(f); } -pub fn zip_mut_with(data: &Array3, out: &mut Array3) { - out.zip_mut_with(&data, |o, &i| { +pub fn zip_indexed(data: &Array3, out: &mut Array3) { + Zip::indexed(data).and(out).apply(|idx, &i, o| { *o = i; }); } // array size in benchmarks -const SZ3: (usize, usize, usize) = (137, 171, 151); +const SZ3: (usize, usize, usize) = (100, 110, 100); #[bench] -fn zip_cf(b: &mut Bencher) { +fn zip_cc(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3); - let mut out = Array3::zeros(data.dim().f()); - b.iter(|| black_box(zip_copy(&data, &mut out))); + let mut out = Array3::zeros(data.dim()); + b.iter(|| zip_copy(&data, &mut out)); } #[bench] -fn zip_cc(b: &mut Bencher) { +fn zip_cf(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3); - let mut out = Array3::zeros(data.dim()); - b.iter(|| black_box(zip_copy(&data, &mut out))); + let mut out = Array3::zeros(data.dim().f()); + b.iter(|| zip_copy(&data, &mut out)); } #[bench] fn zip_fc(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3.f()); let mut out = Array3::zeros(data.dim()); - b.iter(|| black_box(zip_copy(&data, &mut out))); + b.iter(|| zip_copy(&data, &mut out)); } #[bench] fn zip_ff(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3.f()); let mut out = Array3::zeros(data.dim().f()); - b.iter(|| black_box(zip_copy(&data, &mut out))); -} - -#[bench] -fn zip_indexed_cf(b: &mut Bencher) { - let data: Array3 = Array3::zeros(SZ3); - let mut out = Array3::zeros(data.dim().f()); - b.iter(|| black_box(zip_indexed(&data, &mut out))); + b.iter(|| zip_copy(&data, &mut out)); } #[bench] fn zip_indexed_cc(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3); let mut out = Array3::zeros(data.dim()); - b.iter(|| black_box(zip_indexed(&data, &mut out))); -} - -#[bench] -fn zip_indexed_fc(b: &mut Bencher) { - let data: Array3 = Array3::zeros(SZ3.f()); - let mut out = Array3::zeros(data.dim()); - b.iter(|| black_box(zip_indexed(&data, &mut out))); + b.iter(|| zip_indexed(&data, &mut out)); } #[bench] fn zip_indexed_ff(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3.f()); let mut out = Array3::zeros(data.dim().f()); - b.iter(|| black_box(zip_indexed(&data, &mut out))); + b.iter(|| zip_indexed(&data, &mut out)); } #[bench] -fn zip_mut_with_cf(b: &mut Bencher) { +fn slice_zip_cc(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3); - let mut out = Array3::zeros(data.dim().f()); - b.iter(|| black_box(zip_mut_with(&data, &mut out))); + let mut out = Array3::zeros(data.dim()); + let data = data.slice(s![1.., 1.., 1..]); + let mut out = out.slice_mut(s![1.., 1.., 1..]); + b.iter(|| zip_copy(&data, &mut out)); } #[bench] -fn zip_mut_with_cc(b: &mut Bencher) { - let data: Array3 = Array3::zeros(SZ3); - let mut out = Array3::zeros(data.dim()); - b.iter(|| black_box(zip_mut_with(&data, &mut out))); +fn slice_zip_ff(b: &mut Bencher) { + let data: Array3 = Array3::zeros(SZ3.f()); + let mut out = Array3::zeros(data.dim().f()); + let data = data.slice(s![1.., 1.., 1..]); + let mut out = out.slice_mut(s![1.., 1.., 1..]); + b.iter(|| zip_copy(&data, &mut out)); } #[bench] -fn zip_mut_with_fc(b: &mut Bencher) { - let data: Array3 = Array3::zeros(SZ3.f()); +fn slice_split_zip_cc(b: &mut Bencher) { + let data: Array3 = Array3::zeros(SZ3); let mut out = Array3::zeros(data.dim()); - b.iter(|| black_box(zip_mut_with(&data, &mut out))); + let data = data.slice(s![1.., 1.., 1..]); + let mut out = out.slice_mut(s![1.., 1.., 1..]); + b.iter(|| zip_copy_split(&data, &mut out)); } #[bench] -fn zip_mut_with_ff(b: &mut Bencher) { +fn slice_split_zip_ff(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3.f()); let mut out = Array3::zeros(data.dim().f()); - b.iter(|| black_box(zip_mut_with(&data, &mut out))); + let data = data.slice(s![1.., 1.., 1..]); + let mut out = out.slice_mut(s![1.., 1.., 1..]); + b.iter(|| zip_copy_split(&data, &mut out)); } From 90ef196e784b164bfc49234925738455716fbe5f Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 22 Apr 2020 22:42:56 +0200 Subject: [PATCH 049/651] TEST: In Zip benchmarks, use the index Using the index shows more directly the overhead of indexed zip --- benches/zip.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/benches/zip.rs b/benches/zip.rs index 1fe74b970..ac7bef589 100644 --- a/benches/zip.rs +++ b/benches/zip.rs @@ -1,6 +1,6 @@ #![feature(test)] extern crate test; -use test::{Bencher}; +use test::{black_box, Bencher}; use ndarray::{Array3, ShapeBuilder, Zip}; use ndarray::s; use ndarray::IntoNdProducer; @@ -33,6 +33,7 @@ pub fn zip_copy_split<'a, A, P, Q>(data: P, out: Q) pub fn zip_indexed(data: &Array3, out: &mut Array3) { Zip::indexed(data).and(out).apply(|idx, &i, o| { + let _ = black_box(idx); *o = i; }); } From a659235cd5d325500e3227570664f13e0d6da14e Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 21 Apr 2020 22:03:08 +0200 Subject: [PATCH 050/651] FEAT: Keep track of "layout tendency" in Zip for better performance Support both unroll over c- and f-layout preferred axis in Zip inner loop (the fallback when inputs are not all contiguous and same layout). Keep a tendency score when building the Zip, so that we know if the inputs are tending to be c- or f- layout. This improves performance on the just added zip_indexed_ff benchmark, so that it seems to match its (already fast) cc counterpart. --- src/layout/layoutfmt.rs | 2 +- src/layout/mod.rs | 46 +++++++++++++----- src/zip/mod.rs | 104 +++++++++++++++++++++++++++++++++------- 3 files changed, 120 insertions(+), 32 deletions(-) diff --git a/src/layout/layoutfmt.rs b/src/layout/layoutfmt.rs index d5049512e..3d7fad00a 100644 --- a/src/layout/layoutfmt.rs +++ b/src/layout/layoutfmt.rs @@ -8,7 +8,7 @@ use super::Layout; -const LAYOUT_NAMES: &[&str] = &["C", "F"]; +const LAYOUT_NAMES: &[&str] = &["C", "F", "c", "f"]; use std::fmt; diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 3804a160b..850c0fe8a 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -1,51 +1,71 @@ mod layoutfmt; -// public struct but users don't interact with it +// Layout it a bitset used for internal layout description of +// arrays, producers and sets of producers. +// The type is public but users don't interact with it. #[doc(hidden)] /// Memory layout description #[derive(Copy, Clone)] pub struct Layout(u32); impl Layout { - #[inline(always)] - pub(crate) fn new(x: u32) -> Self { - Layout(x) - } - #[inline(always)] pub(crate) fn is(self, flag: u32) -> bool { self.0 & flag != 0 } + + /// Return layout common to both inputs #[inline(always)] - pub(crate) fn and(self, flag: Layout) -> Layout { - Layout(self.0 & flag.0) + pub(crate) fn intersect(self, other: Layout) -> Layout { + Layout(self.0 & other.0) } + /// Return a layout that simultaneously "is" what both of the inputs are #[inline(always)] - pub(crate) fn flag(self) -> u32 { - self.0 + pub(crate) fn also(self, other: Layout) -> Layout { + Layout(self.0 | other.0) } #[inline(always)] pub(crate) fn one_dimensional() -> Layout { - Layout(CORDER | FORDER) + Layout::c().also(Layout::f()) } #[inline(always)] pub(crate) fn c() -> Layout { - Layout(CORDER) + Layout(CORDER | CPREFER) } #[inline(always)] pub(crate) fn f() -> Layout { - Layout(FORDER) + Layout(FORDER | FPREFER) + } + + #[inline(always)] + pub(crate) fn cpref() -> Layout { + Layout(CPREFER) + } + + #[inline(always)] + pub(crate) fn fpref() -> Layout { + Layout(FPREFER) } #[inline(always)] pub(crate) fn none() -> Layout { Layout(0) } + + /// A simple "score" method which scores positive for preferring C-order, negative for F-order + /// Subject to change when we can describe other layouts + pub(crate) fn tendency(self) -> i32 { + (self.is(CORDER) as i32 - self.is(FORDER) as i32) + + (self.is(CPREFER) as i32 - self.is(FPREFER) as i32) + + } } pub const CORDER: u32 = 0b01; pub const FORDER: u32 = 0b10; +pub const CPREFER: u32 = 0b0100; +pub const FPREFER: u32 = 0b1000; diff --git a/src/zip/mod.rs b/src/zip/mod.rs index b2e24a62e..d8ebc4faa 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -53,17 +53,26 @@ where D: Dimension, { pub(crate) fn layout_impl(&self) -> Layout { - Layout::new(if self.is_standard_layout() { - if self.ndim() <= 1 { - FORDER | CORDER + let n = self.ndim(); + if self.is_standard_layout() { + if n <= 1 { + Layout::one_dimensional() } else { - CORDER + Layout::c() + } + } else if n > 1 && self.raw_view().reversed_axes().is_standard_layout() { + Layout::f() + } else if n > 1 { + if self.stride_of(Axis(0)) == 1 { + Layout::fpref() + } else if self.stride_of(Axis(n - 1)) == 1 { + Layout::cpref() + } else { + Layout::none() } - } else if self.ndim() > 1 && self.raw_view().reversed_axes().is_standard_layout() { - FORDER } else { - 0 - }) + Layout::none() + } } } @@ -587,6 +596,9 @@ pub struct Zip { parts: Parts, dimension: D, layout: Layout, + /// The sum of the layout tendencies of the parts; + /// positive for c- and negative for f-layout preference. + layout_tendency: i32, } @@ -605,10 +617,12 @@ where { let array = p.into_producer(); let dim = array.raw_dim(); + let layout = array.layout(); Zip { dimension: dim, - layout: array.layout(), + layout, parts: (array,), + layout_tendency: layout.tendency(), } } } @@ -661,24 +675,29 @@ where self.dimension[axis.index()] } + fn prefer_f(&self) -> bool { + !self.layout.is(CORDER) && (self.layout.is(FORDER) || self.layout_tendency < 0) + } + /// Return an *approximation* to the max stride axis; if /// component arrays disagree, there may be no choice better than the /// others. fn max_stride_axis(&self) -> Axis { - let i = match self.layout.flag() { - FORDER => self + let i = if self.prefer_f() { + self .dimension .slice() .iter() .rposition(|&len| len > 1) - .unwrap_or(self.dimension.ndim() - 1), + .unwrap_or(self.dimension.ndim() - 1) + } else { /* corder or default */ - _ => self + self .dimension .slice() .iter() .position(|&len| len > 1) - .unwrap_or(0), + .unwrap_or(0) }; Axis(i) } @@ -699,6 +718,7 @@ where self.apply_core_strided(acc, function) } } + fn apply_core_contiguous(&mut self, mut acc: Acc, mut function: F) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, @@ -717,7 +737,7 @@ where FoldWhile::Continue(acc) } - fn apply_core_strided(&mut self, mut acc: Acc, mut function: F) -> FoldWhile + fn apply_core_strided(&mut self, acc: Acc, function: F) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, P: ZippableTuple, @@ -726,13 +746,27 @@ where if n == 0 { panic!("Unreachable: ndim == 0 is contiguous") } + if n == 1 || self.layout_tendency >= 0 { + self.apply_core_strided_c(acc, function) + } else { + self.apply_core_strided_f(acc, function) + } + } + + // Non-contiguous but preference for C - unroll over Axis(ndim - 1) + fn apply_core_strided_c(&mut self, mut acc: Acc, mut function: F) -> FoldWhile + where + F: FnMut(Acc, P::Item) -> FoldWhile, + P: ZippableTuple, + { + let n = self.dimension.ndim(); let unroll_axis = n - 1; let inner_len = self.dimension[unroll_axis]; self.dimension[unroll_axis] = 1; let mut index_ = self.dimension.first_index(); let inner_strides = self.parts.stride_of(unroll_axis); + // Loop unrolled over closest axis while let Some(index) = index_ { - // Let's “unroll” the loop over the innermost axis unsafe { let ptr = self.parts.uget_ptr(&index); for i in 0..inner_len { @@ -747,9 +781,40 @@ where FoldWhile::Continue(acc) } + // Non-contiguous but preference for F - unroll over Axis(0) + fn apply_core_strided_f(&mut self, mut acc: Acc, mut function: F) -> FoldWhile + where + F: FnMut(Acc, P::Item) -> FoldWhile, + P: ZippableTuple, + { + let unroll_axis = 0; + let inner_len = self.dimension[unroll_axis]; + self.dimension[unroll_axis] = 1; + let index_ = self.dimension.first_index(); + let inner_strides = self.parts.stride_of(unroll_axis); + // Loop unrolled over closest axis + if let Some(mut index) = index_ { + loop { + unsafe { + let ptr = self.parts.uget_ptr(&index); + for i in 0..inner_len { + let p = ptr.stride_offset(inner_strides, i); + acc = fold_while!(function(acc, self.parts.as_ref(p))); + } + } + + if !self.dimension.next_for_f(&mut index) { + break; + } + } + } + self.dimension[unroll_axis] = inner_len; + FoldWhile::Continue(acc) + } + pub(crate) fn uninitalized_for_current_layout(&self) -> Array, D> { - let is_f = !self.layout.is(CORDER) && self.layout.is(FORDER); + let is_f = self.prefer_f(); Array::maybe_uninit(self.dimension.clone().set_f(is_f)) } } @@ -995,8 +1060,9 @@ macro_rules! map_impl { let ($($p,)*) = self.parts; Zip { parts: ($($p,)* part, ), - layout: self.layout.and(part_layout), + layout: self.layout.intersect(part_layout), dimension: self.dimension, + layout_tendency: self.layout_tendency + part_layout.tendency(), } } @@ -1052,11 +1118,13 @@ macro_rules! map_impl { dimension: d1, layout: self.layout, parts: p1, + layout_tendency: self.layout_tendency, }, Zip { dimension: d2, layout: self.layout, parts: p2, + layout_tendency: self.layout_tendency, }) } } From a3d53d25080232089231830846ccb8ca2cc77765 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 22 Apr 2020 18:11:54 +0200 Subject: [PATCH 051/651] TEST: Add tests for Layout --- src/layout/mod.rs | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 850c0fe8a..24dd09958 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -69,3 +69,77 @@ pub const CORDER: u32 = 0b01; pub const FORDER: u32 = 0b10; pub const CPREFER: u32 = 0b0100; pub const FPREFER: u32 = 0b1000; + + +#[cfg(test)] +mod tests { + use super::*; + use crate::imp_prelude::*; + use crate::NdProducer; + + type M = Array2; + + #[test] + fn contig_layouts() { + let a = M::zeros((5, 5)); + let b = M::zeros((5, 5).f()); + let ac = a.view().layout(); + let af = b.view().layout(); + assert!(ac.is(CORDER) && ac.is(CPREFER)); + assert!(!ac.is(FORDER) && !ac.is(FPREFER)); + assert!(!af.is(CORDER) && !af.is(CPREFER)); + assert!(af.is(FORDER) && af.is(FPREFER)); + } + + #[test] + fn stride_layouts() { + let a = M::zeros((5, 5)); + + { + let v1 = a.slice(s![1.., ..]).layout(); + let v2 = a.slice(s![.., 1..]).layout(); + + assert!(v1.is(CORDER) && v1.is(CPREFER)); + assert!(!v1.is(FORDER) && !v1.is(FPREFER)); + assert!(!v2.is(CORDER) && v2.is(CPREFER)); + assert!(!v2.is(FORDER) && !v2.is(FPREFER)); + } + + let b = M::zeros((5, 5).f()); + + { + let v1 = b.slice(s![1.., ..]).layout(); + let v2 = b.slice(s![.., 1..]).layout(); + + assert!(!v1.is(CORDER) && !v1.is(CPREFER)); + assert!(!v1.is(FORDER) && v1.is(FPREFER)); + assert!(!v2.is(CORDER) && !v2.is(CPREFER)); + assert!(v2.is(FORDER) && v2.is(FPREFER)); + } + } + + #[test] + fn skip_layouts() { + let a = M::zeros((5, 5)); + { + let v1 = a.slice(s![..;2, ..]).layout(); + let v2 = a.slice(s![.., ..;2]).layout(); + + assert!(!v1.is(CORDER) && v1.is(CPREFER)); + assert!(!v1.is(FORDER) && !v1.is(FPREFER)); + assert!(!v2.is(CORDER) && !v2.is(CPREFER)); + assert!(!v2.is(FORDER) && !v2.is(FPREFER)); + } + + let b = M::zeros((5, 5).f()); + { + let v1 = b.slice(s![..;2, ..]).layout(); + let v2 = b.slice(s![.., ..;2]).layout(); + + assert!(!v1.is(CORDER) && !v1.is(CPREFER)); + assert!(!v1.is(FORDER) && !v1.is(FPREFER)); + assert!(!v2.is(CORDER) && !v2.is(CPREFER)); + assert!(!v2.is(FORDER) && v2.is(FPREFER)); + } + } +} From 47b36546e29cd6cf5e5b467151a7f58b240ee489 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 22 Apr 2020 19:03:08 +0200 Subject: [PATCH 052/651] TEST: update format tests for layout changes --- tests/format.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/format.rs b/tests/format.rs index 5c2e2b6f4..422fa2957 100644 --- a/tests/format.rs +++ b/tests/format.rs @@ -62,13 +62,13 @@ fn debug_format() { "\ [[0, 0, 0, 0], [0, 0, 0, 0], - [0, 0, 0, 0]], shape=[3, 4], strides=[4, 1], layout=C (0x1), const ndim=2" + [0, 0, 0, 0]], shape=[3, 4], strides=[4, 1], layout=Cc (0x5), const ndim=2" ); assert_eq!( format!("{:?}", a.into_dyn()), "\ [[0, 0, 0, 0], [0, 0, 0, 0], - [0, 0, 0, 0]], shape=[3, 4], strides=[4, 1], layout=C (0x1), dynamic ndim=2" + [0, 0, 0, 0]], shape=[3, 4], strides=[4, 1], layout=Cc (0x5), dynamic ndim=2" ); } From 83a9c6c92acfe310dceeda75e9b9364efd5fad3d Mon Sep 17 00:00:00 2001 From: Bastien Orivel Date: Sat, 25 Apr 2020 09:43:02 +0200 Subject: [PATCH 053/651] Bump all dependencies * Update blas-src to 0.6 * Update itertools to 0.9 * Update openblas-src to 0.9 --- Cargo.toml | 4 ++-- blas-tests/Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 86eabb48a..48fad0822 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ approx = { version = "0.3.2", optional = true } # Use via the `blas` crate feature! cblas-sys = { version = "0.1.4", optional = true, default-features = false } -blas-src = { version = "0.2.0", optional = true, default-features = false } +blas-src = { version = "0.6.1", optional = true, default-features = false } matrixmultiply = { version = "0.2.0" } serde = { version = "1.0", optional = true } @@ -48,7 +48,7 @@ rawpointer = { version = "0.2" } defmac = "0.2" quickcheck = { version = "0.9", default-features = false } approx = "0.3.2" -itertools = { version = "0.8.0", default-features = false, features = ["use_std"] } +itertools = { version = "0.9.0", default-features = false, features = ["use_std"] } [features] # Enable blas usage diff --git a/blas-tests/Cargo.toml b/blas-tests/Cargo.toml index 9853ac634..d7b701084 100644 --- a/blas-tests/Cargo.toml +++ b/blas-tests/Cargo.toml @@ -10,7 +10,7 @@ test = false [dev-dependencies] approx = "0.3.2" ndarray = { path = "../", features = ["approx", "blas"] } -blas-src = { version = "0.2.0", default-features = false, features = ["openblas"] } -openblas-src = { version = "0.6.0", default-features = false, features = ["cblas", "system"] } +blas-src = { version = "0.6.1", default-features = false, features = ["openblas"] } +openblas-src = { version = "0.9.0", default-features = false, features = ["cblas", "system"] } defmac = "0.2" num-traits = "0.2" From 48292bc4b796d948b7571dd4d5741b7db21e3e27 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 25 Apr 2020 12:38:12 +0200 Subject: [PATCH 054/651] FIX: Clippy lints The clippy.toml setting is a workaround for the currently broken single char variable lint (a fix is pending in clippy). --- build.rs | 2 +- clippy.toml | 1 + src/lib.rs | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 clippy.toml diff --git a/build.rs b/build.rs index ceeeff389..dd06c0b81 100644 --- a/build.rs +++ b/build.rs @@ -5,6 +5,6 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); if cfg!(feature = "test-blas-openblas-sys") { - println!("cargo:rustc-link-lib={}=openblas", "dylib"); + println!("cargo:rustc-link-lib=dylib=openblas"); } } diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 000000000..5ef4300ee --- /dev/null +++ b/clippy.toml @@ -0,0 +1 @@ +single-char-binding-names-threshold = 1000 diff --git a/src/lib.rs b/src/lib.rs index eb09d31ea..fc9a475b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,6 @@ clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, - clippy::many_single_char_names )] //! The `ndarray` crate provides an *n*-dimensional container for general elements From d02b6da6bd7057be49a2aabec9c6efde77cf3355 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 25 Apr 2020 12:48:52 +0200 Subject: [PATCH 055/651] DOC: Remove 'extern crate' from all examples Prefer Rust 2018 syntax now, no extern crate needed. --- src/doc/ndarray_for_numpy_users/coord_transform.rs | 4 ---- src/doc/ndarray_for_numpy_users/mod.rs | 2 -- src/doc/ndarray_for_numpy_users/rk_step.rs | 4 ---- src/doc/ndarray_for_numpy_users/simple_math.rs | 2 -- src/parallel/mod.rs | 8 -------- src/parallel/zipmacro.rs | 2 -- src/slice.rs | 4 ---- src/stacking.rs | 2 -- src/zip/zipmacro.rs | 2 -- 9 files changed, 30 deletions(-) diff --git a/src/doc/ndarray_for_numpy_users/coord_transform.rs b/src/doc/ndarray_for_numpy_users/coord_transform.rs index ef922f2ad..e019fd1b1 100644 --- a/src/doc/ndarray_for_numpy_users/coord_transform.rs +++ b/src/doc/ndarray_for_numpy_users/coord_transform.rs @@ -49,8 +49,6 @@ //! This is a direct translation to `ndarray`: //! //! ``` -//! extern crate ndarray; -//! //! use ndarray::prelude::*; //! //! fn main() { @@ -96,8 +94,6 @@ //! this: //! //! ``` -//! extern crate ndarray; -//! //! use ndarray::prelude::*; //! //! fn main() { diff --git a/src/doc/ndarray_for_numpy_users/mod.rs b/src/doc/ndarray_for_numpy_users/mod.rs index b061d8e67..6469e4f5d 100644 --- a/src/doc/ndarray_for_numpy_users/mod.rs +++ b/src/doc/ndarray_for_numpy_users/mod.rs @@ -177,8 +177,6 @@ //! and `ndarray` like this: //! //! ``` -//! extern crate ndarray; -//! //! use ndarray::prelude::*; //! # //! # fn main() {} diff --git a/src/doc/ndarray_for_numpy_users/rk_step.rs b/src/doc/ndarray_for_numpy_users/rk_step.rs index a68f6b171..2c5ce2362 100644 --- a/src/doc/ndarray_for_numpy_users/rk_step.rs +++ b/src/doc/ndarray_for_numpy_users/rk_step.rs @@ -71,8 +71,6 @@ //! A direct translation to `ndarray` looks like this: //! //! ``` -//! extern crate ndarray; -//! //! use ndarray::prelude::*; //! //! fn rk_step( @@ -129,8 +127,6 @@ //! some places, but that's not demonstrated in the example below. //! //! ``` -//! extern crate ndarray; -//! //! use ndarray::prelude::*; //! //! fn rk_step( diff --git a/src/doc/ndarray_for_numpy_users/simple_math.rs b/src/doc/ndarray_for_numpy_users/simple_math.rs index b6549d3c2..1bba0c286 100644 --- a/src/doc/ndarray_for_numpy_users/simple_math.rs +++ b/src/doc/ndarray_for_numpy_users/simple_math.rs @@ -51,8 +51,6 @@ //! //! //! ``` -//! extern crate ndarray; -//! //! use ndarray::prelude::*; //! //! # fn main() { diff --git a/src/parallel/mod.rs b/src/parallel/mod.rs index 3583d342f..2296e2110 100644 --- a/src/parallel/mod.rs +++ b/src/parallel/mod.rs @@ -39,8 +39,6 @@ //! Compute the exponential of each element in an array, parallelized. //! //! ``` -//! extern crate ndarray; -//! //! use ndarray::Array2; //! use ndarray::parallel::prelude::*; //! @@ -61,8 +59,6 @@ //! Use the parallel `.axis_iter()` to compute the sum of each row. //! //! ``` -//! extern crate ndarray; -//! //! use ndarray::Array; //! use ndarray::Axis; //! use ndarray::parallel::prelude::*; @@ -84,8 +80,6 @@ //! Use the parallel `.axis_chunks_iter()` to process your data in chunks. //! //! ``` -//! extern crate ndarray; -//! //! use ndarray::Array; //! use ndarray::Axis; //! use ndarray::parallel::prelude::*; @@ -107,8 +101,6 @@ //! Use zip for lock step function application across several arrays //! //! ``` -//! extern crate ndarray; -//! //! use ndarray::Array3; //! use ndarray::Zip; //! diff --git a/src/parallel/zipmacro.rs b/src/parallel/zipmacro.rs index 99c3807f8..eadd36a84 100644 --- a/src/parallel/zipmacro.rs +++ b/src/parallel/zipmacro.rs @@ -34,8 +34,6 @@ /// ## Examples /// /// ```rust -/// extern crate ndarray; -/// /// use ndarray::Array2; /// use ndarray::parallel::par_azip; /// diff --git a/src/slice.rs b/src/slice.rs index 58bff48b5..15765c61d 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -496,8 +496,6 @@ impl_slicenextdim_larger!((), Slice); /// # Example /// /// ``` -/// extern crate ndarray; -/// /// use ndarray::{s, Array2, ArrayView2}; /// /// fn laplacian(v: &ArrayView2) -> Array2 { @@ -528,8 +526,6 @@ impl_slicenextdim_larger!((), Slice); /// For example, /// /// ``` -/// # extern crate ndarray; -/// # /// # use ndarray::prelude::*; /// # /// # fn main() { diff --git a/src/stacking.rs b/src/stacking.rs index e998b6d15..0f6161a3c 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -86,8 +86,6 @@ where /// ***Panics*** if the `stack` function would return an error. /// /// ``` -/// extern crate ndarray; -/// /// use ndarray::{arr2, stack, Axis}; /// /// # fn main() { diff --git a/src/zip/zipmacro.rs b/src/zip/zipmacro.rs index 3ac73b8fe..ba2b3da22 100644 --- a/src/zip/zipmacro.rs +++ b/src/zip/zipmacro.rs @@ -38,8 +38,6 @@ /// ## Examples /// /// ```rust -/// extern crate ndarray; -/// /// use ndarray::{azip, Array1, Array2, Axis}; /// /// type M = Array2; From 15e6973397794384058da8abfe42b3ba230f4361 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 25 Apr 2020 13:00:59 +0200 Subject: [PATCH 056/651] DOC: Fix method links in ndarray::parallel --- src/parallel/mod.rs | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/parallel/mod.rs b/src/parallel/mod.rs index 2296e2110..82cd1cba6 100644 --- a/src/parallel/mod.rs +++ b/src/parallel/mod.rs @@ -10,11 +10,11 @@ //! The following types implement parallel iterators, accessed using these //! methods: //! -//! - [`Array`], [`ArcArray`]: `.par_iter()` and `.par_iter_mut()` -//! - [`ArrayView`](ArrayView): `.into_par_iter()` -//! - [`ArrayViewMut`](ArrayViewMut): `.into_par_iter()` -//! - [`AxisIter`](iter::AxisIter), [`AxisIterMut`](iter::AxisIterMut): `.into_par_iter()` -//! - [`AxisChunksIter`](iter::AxisChunksIter), [`AxisChunksIterMut`](iter::AxisChunksIterMut): `.into_par_iter()` +//! - [`Array`], [`ArcArray`] `.par_iter()` and `.par_iter_mut()` +//! - [`ArrayView`] `.into_par_iter()` +//! - [`ArrayViewMut`] `.into_par_iter()` +//! - [`AxisIter`], [`AxisIterMut`] `.into_par_iter()` +//! - [`AxisChunksIter`], [`AxisChunksIterMut`] `.into_par_iter()` //! - [`Zip`] `.into_par_iter()` //! //! The following other parallelized methods exist: @@ -121,6 +121,23 @@ //! } //! ``` +#[allow(unused_imports)] // used by rustdoc links +use crate::{ + ArrayBase, + Array, + ArcArray, + ArrayView, + ArrayViewMut, + Zip, +}; +#[allow(unused_imports)] // used by rustdoc links +use crate::iter::{ + AxisIter, + AxisIterMut, + AxisChunksIter, + AxisChunksIterMut, +}; + /// Into- traits for creating parallelized iterators and/or using [`par_azip!`] pub mod prelude { #[doc(no_inline)] From 778575f7756ebff52d430e2be89e9edfad54d4ff Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 25 Apr 2020 13:09:00 +0200 Subject: [PATCH 057/651] DOC: Link some more methods in docs --- src/lib.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index eb09d31ea..270e3fe38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,8 +53,9 @@ //! - Performance: //! + Prefer higher order methods and arithmetic operations on arrays first, //! then iteration, and as a last priority using indexed algorithms. -//! + The higher order functions like ``.map()``, ``.map_inplace()``, -//! ``.zip_mut_with()``, ``Zip`` and ``azip!()`` are the most efficient ways +//! + The higher order functions like [`.map()`](ArrayBase::map), +//! [`.map_inplace()`](ArrayBase::map_inplace), [`.zip_mut_with()`](ArrayBase::zip_mut_with), +//! [`Zip`] and [`azip!()`](azip) are the most efficient ways //! to perform single traversal and lock step traversal respectively. //! + Performance of an operation depends on the memory layout of the array //! or array view. Especially if it's a binary operation, which @@ -300,9 +301,10 @@ pub type Ixs = isize; /// Please see the documentation for the respective array view for an overview /// of methods specific to array views: [`ArrayView`], [`ArrayViewMut`]. /// -/// A view is created from an array using `.view()`, `.view_mut()`, using -/// slicing (`.slice()`, `.slice_mut()`) or from one of the many iterators -/// that yield array views. +/// A view is created from an array using [`.view()`](ArrayBase::view), +/// [`.view_mut()`](ArrayBase::view_mut), using +/// slicing ([`.slice()`](ArrayBase::slice), [`.slice_mut()`](ArrayBase::slice_mut)) or from one of +/// the many iterators that yield array views. /// /// You can also create an array view from a regular slice of data not /// allocated with `Array` — see array view methods or their `From` impls. From 3a767c0660ba4cdddca24ac26a618ae63cc8de9f Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 29 Apr 2020 14:03:09 +0200 Subject: [PATCH 058/651] TEST: Add benchmarks for collect Test both String (with drop) and f64 (Copy type), compare Array to Vec::from_iter. --- benches/bench1.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/benches/bench1.rs b/benches/bench1.rs index d7fa9f163..8cd04c458 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -286,6 +286,38 @@ fn add_2d_alloc_zip_collect(bench: &mut test::Bencher) { }); } +#[bench] +fn vec_string_collect(bench: &mut test::Bencher) { + let v = vec![""; 10240]; + bench.iter(|| { + v.iter().map(|s| s.to_owned()).collect::>() + }); +} + +#[bench] +fn array_string_collect(bench: &mut test::Bencher) { + let v = Array::from(vec![""; 10240]); + bench.iter(|| { + Zip::from(&v).apply_collect(|s| s.to_owned()) + }); +} + +#[bench] +fn vec_f64_collect(bench: &mut test::Bencher) { + let v = vec![1.; 10240]; + bench.iter(|| { + v.iter().map(|s| s + 1.).collect::>() + }); +} + +#[bench] +fn array_f64_collect(bench: &mut test::Bencher) { + let v = Array::from(vec![1.; 10240]); + bench.iter(|| { + Zip::from(&v).apply_collect(|s| s + 1.) + }); +} + #[bench] fn add_2d_assign_ops(bench: &mut test::Bencher) { From fabea291cfc06e5d780001fa85235ee175e834ef Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 29 Apr 2020 14:03:09 +0200 Subject: [PATCH 059/651] FEAT: Implement Zip::apply_collect for non-Copy elements --- src/zip/mod.rs | 26 +++++-- src/zip/partial_array.rs | 144 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 7 deletions(-) create mode 100644 src/zip/partial_array.rs diff --git a/src/zip/mod.rs b/src/zip/mod.rs index d8ebc4faa..2b6612b54 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -8,6 +8,7 @@ #[macro_use] mod zipmacro; +mod partial_array; use std::mem::MaybeUninit; @@ -20,6 +21,8 @@ use crate::NdIndex; use crate::indexes::{indices, Indices}; use crate::layout::{CORDER, FORDER}; +use partial_array::PartialArray; + /// Return if the expression is a break value. macro_rules! fold_while { ($e:expr) => { @@ -195,6 +198,7 @@ pub trait NdProducer { fn split_at(self, axis: Axis, index: usize) -> (Self, Self) where Self: Sized; + private_decl! {} } @@ -1070,16 +1074,24 @@ macro_rules! map_impl { /// inputs. /// /// If all inputs are c- or f-order respectively, that is preserved in the output. - /// - /// Restricted to functions that produce copyable results for technical reasons; other - /// cases are not yet implemented. pub fn apply_collect(self, f: impl FnMut($($p::Item,)* ) -> R) -> Array - where R: Copy, { - // To support non-Copy elements, implementation of dropping partial array (on - // panic) is needed + // Make uninit result let mut output = self.uninitalized_for_current_layout::(); - self.apply_assign_into(&mut output, f); + if !std::mem::needs_drop::() { + // For elements with no drop glue, just overwrite into the array + self.apply_assign_into(&mut output, f); + } else { + // For generic elements, use a proxy that counts the number of filled elements, + // and can drop the right number of elements on unwinding + unsafe { + PartialArray::scope(output.view_mut(), move |partial| { + debug_assert_eq!(partial.layout().tendency() >= 0, self.layout_tendency >= 0); + self.apply_assign_into(partial, f); + }); + } + } + unsafe { output.assume_init() } diff --git a/src/zip/partial_array.rs b/src/zip/partial_array.rs new file mode 100644 index 000000000..d6a2bb9cd --- /dev/null +++ b/src/zip/partial_array.rs @@ -0,0 +1,144 @@ +// Copyright 2020 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::imp_prelude::*; +use crate::{ + AssignElem, + Layout, + NdProducer, + Zip, + FoldWhile, +}; + +use std::cell::Cell; +use std::mem; +use std::mem::MaybeUninit; +use std::ptr; + +/// An assignable element reference that increments a counter when assigned +pub(crate) struct ProxyElem<'a, 'b, A> { + item: &'a mut MaybeUninit, + filled: &'b Cell +} + +impl<'a, 'b, A> AssignElem for ProxyElem<'a, 'b, A> { + fn assign_elem(self, item: A) { + self.filled.set(self.filled.get() + 1); + *self.item = MaybeUninit::new(item); + } +} + +/// Handles progress of assigning to a part of an array, for elements that need +/// to be dropped on unwinding. See Self::scope. +pub(crate) struct PartialArray<'a, 'b, A, D> + where D: Dimension +{ + data: ArrayViewMut<'a, MaybeUninit, D>, + filled: &'b Cell, +} + +impl<'a, 'b, A, D> PartialArray<'a, 'b, A, D> + where D: Dimension +{ + /// Create a temporary PartialArray that wraps the array view `data`; + /// if the end of the scope is reached, the partial array is marked complete; + /// if execution unwinds at any time before them, the elements written until then + /// are dropped. + /// + /// Safety: the caller *must* ensure that elements will be written in `data`'s preferred order. + /// PartialArray can not handle arbitrary writes, only in the memory order. + pub(crate) unsafe fn scope(data: ArrayViewMut<'a, MaybeUninit, D>, + scope_fn: impl FnOnce(&mut PartialArray)) + { + let filled = Cell::new(0); + let mut partial = PartialArray::new(data, &filled); + scope_fn(&mut partial); + filled.set(0); // mark complete + } + + unsafe fn new(data: ArrayViewMut<'a, MaybeUninit, D>, + filled: &'b Cell) -> Self + { + debug_assert_eq!(filled.get(), 0); + Self { data, filled } + } +} + +impl<'a, 'b, A, D> Drop for PartialArray<'a, 'b, A, D> + where D: Dimension +{ + fn drop(&mut self) { + if !mem::needs_drop::() { + return; + } + + let mut count = self.filled.get(); + if count == 0 { + return; + } + + Zip::from(self).fold_while((), move |(), elt| { + if count > 0 { + count -= 1; + unsafe { + ptr::drop_in_place::(elt.item.as_mut_ptr()); + } + FoldWhile::Continue(()) + } else { + FoldWhile::Done(()) + } + }); + } +} + +impl<'a: 'c, 'b: 'c, 'c, A, D: Dimension> NdProducer for &'c mut PartialArray<'a, 'b, A, D> { + // This just wraps ArrayViewMut as NdProducer and maps the item + type Item = ProxyElem<'a, 'b, A>; + type Dim = D; + type Ptr = *mut MaybeUninit; + type Stride = isize; + + private_impl! {} + fn raw_dim(&self) -> Self::Dim { + self.data.raw_dim() + } + + fn equal_dim(&self, dim: &Self::Dim) -> bool { + self.data.equal_dim(dim) + } + + fn as_ptr(&self) -> Self::Ptr { + NdProducer::as_ptr(&self.data) + } + + fn layout(&self) -> Layout { + self.data.layout() + } + + unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { + ProxyElem { filled: self.filled, item: &mut *ptr } + } + + unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { + self.data.uget_ptr(i) + } + + fn stride_of(&self, axis: Axis) -> Self::Stride { + self.data.stride_of(axis) + } + + #[inline(always)] + fn contiguous_stride(&self) -> Self::Stride { + self.data.contiguous_stride() + } + + fn split_at(self, _axis: Axis, _index: usize) -> (Self, Self) { + unimplemented!(); + } +} + From 624fd753f80548f1700e3fd7362839daae771dbe Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 29 Apr 2020 14:03:09 +0200 Subject: [PATCH 060/651] TEST: Test both panic and non-panic cases for apply_collect --- tests/azip.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/tests/azip.rs b/tests/azip.rs index 90da41853..9027927ff 100644 --- a/tests/azip.rs +++ b/tests/azip.rs @@ -107,6 +107,78 @@ fn test_zip_assign_into_cell() { assert_abs_diff_eq!(a2, &b + &c, epsilon = 1e-6); } +#[test] +fn test_zip_collect_drop() { + use std::cell::RefCell; + use std::panic; + + struct Recorddrop<'a>((usize, usize), &'a RefCell>); + + impl<'a> Drop for Recorddrop<'a> { + fn drop(&mut self) { + self.1.borrow_mut().push(self.0); + } + } + + #[derive(Copy, Clone)] + enum Config { + CC, + CF, + FF, + } + + impl Config { + fn a_is_f(self) -> bool { + match self { + Config::CC | Config::CF => false, + _ => true, + } + } + fn b_is_f(self) -> bool { + match self { + Config::CC => false, + _ => true, + } + } + } + + let test_collect_panic = |config: Config, will_panic: bool, slice: bool| { + let mut inserts = RefCell::new(Vec::new()); + let mut drops = RefCell::new(Vec::new()); + + let mut a = Array::from_shape_fn((5, 10).set_f(config.a_is_f()), |idx| idx); + let mut b = Array::from_shape_fn((5, 10).set_f(config.b_is_f()), |_| 0); + if slice { + a = a.slice_move(s![.., ..-1]); + b = b.slice_move(s![.., ..-1]); + } + + let _result = panic::catch_unwind(panic::AssertUnwindSafe(|| { + Zip::from(&a).and(&b).apply_collect(|&elt, _| { + if elt.0 > 3 && will_panic { + panic!(); + } + inserts.borrow_mut().push(elt); + Recorddrop(elt, &drops) + }); + })); + + println!("{:?}", inserts.get_mut()); + println!("{:?}", drops.get_mut()); + + assert_eq!(inserts.get_mut().len(), drops.get_mut().len(), "Incorrect number of drops"); + assert_eq!(inserts.get_mut(), drops.get_mut(), "Incorrect order of drops"); + }; + + for &should_panic in &[true, false] { + for &should_slice in &[false, true] { + test_collect_panic(Config::CC, should_panic, should_slice); + test_collect_panic(Config::CF, should_panic, should_slice); + test_collect_panic(Config::FF, should_panic, should_slice); + } + } +} + #[test] fn test_azip_syntax_trailing_comma() { From f998a62bb36e6bc95d3ae11fe93ca62b0d6e676c Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 29 Apr 2020 21:54:40 +0200 Subject: [PATCH 061/651] DOC: Fix index in example for ArrayView::split_at --- src/impl_views/splitting.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impl_views/splitting.rs b/src/impl_views/splitting.rs index dcfb04b86..38d07594a 100644 --- a/src/impl_views/splitting.rs +++ b/src/impl_views/splitting.rs @@ -51,7 +51,7 @@ where /// ```rust /// # use ndarray::prelude::*; /// # let a = aview2(&[[0; 4]; 3]); - /// let (v1, v2) = a.split_at(Axis(0), 1); + /// let (v1, v2) = a.split_at(Axis(0), 2); /// ``` /// ```text /// ┌─────┬─────┬─────┬─────┐ 0 ↓ indices From 4bac2c1487b4bf25617ce624985b23d41e0974d8 Mon Sep 17 00:00:00 2001 From: Jonathan Woollett-Light Date: Sat, 16 May 2020 19:09:31 +0100 Subject: [PATCH 062/651] Added examples for indexing 2d arrays (#818) * Added examples for indexing 2d arrays --- src/impl_2d.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/impl_2d.rs b/src/impl_2d.rs index f44990ed7..7b919eb3f 100644 --- a/src/impl_2d.rs +++ b/src/impl_2d.rs @@ -17,6 +17,12 @@ where /// Return an array view of row `index`. /// /// **Panics** if `index` is out of bounds. + /// + /// ``` + /// use ndarray::array; + /// let array = array![[1., 2.], [3., 4.]]; + /// assert_eq!(array.row(0), array![1., 2.]); + /// ``` pub fn row(&self, index: Ix) -> ArrayView1<'_, A> where S: Data, @@ -27,6 +33,13 @@ where /// Return a mutable array view of row `index`. /// /// **Panics** if `index` is out of bounds. + /// + /// ``` + /// use ndarray::array; + /// let mut array = array![[1., 2.], [3., 4.]]; + /// array.row_mut(0)[1] = 5.; + /// assert_eq!(array, array![[1., 5.], [3., 4.]]); + /// ``` pub fn row_mut(&mut self, index: Ix) -> ArrayViewMut1<'_, A> where S: DataMut, @@ -35,6 +48,12 @@ where } /// Return the number of rows (length of `Axis(0)`) in the two-dimensional array. + /// + /// ``` + /// use ndarray::array; + /// let array = array![[1., 2.], [3., 4.]]; + /// assert_eq!(array.nrows(), 2usize); + /// ``` pub fn nrows(&self) -> usize { self.len_of(Axis(0)) } @@ -48,6 +67,12 @@ where /// Return an array view of column `index`. /// /// **Panics** if `index` is out of bounds. + /// + /// ``` + /// use ndarray::array; + /// let array = array![[1., 2.], [3., 4.]]; + /// assert_eq!(array.column(0), array![1., 3.]); + /// ``` pub fn column(&self, index: Ix) -> ArrayView1<'_, A> where S: Data, @@ -58,6 +83,13 @@ where /// Return a mutable array view of column `index`. /// /// **Panics** if `index` is out of bounds. + /// + /// ``` + /// use ndarray::array; + /// let mut array = array![[1., 2.], [3., 4.]]; + /// array.column_mut(0)[1] = 5.; + /// assert_eq!(array, array![[1., 2.], [5., 4.]]); + /// ``` pub fn column_mut(&mut self, index: Ix) -> ArrayViewMut1<'_, A> where S: DataMut, @@ -66,6 +98,12 @@ where } /// Return the number of columns (length of `Axis(1)`) in the two-dimensional array. + /// + /// ``` + /// use ndarray::array; + /// let array = array![[1., 2.], [3., 4.]]; + /// assert_eq!(array.ncols(), 2usize); + /// ``` pub fn ncols(&self) -> usize { self.len_of(Axis(1)) } @@ -77,6 +115,20 @@ where } /// Return true if the array is square, false otherwise. + /// + /// # Examples + /// Sqaure: + /// ``` + /// use ndarray::array; + /// let array = array![[1., 2.], [3., 4.]]; + /// assert!(array.is_square()); + /// ``` + /// Not sqaure: + /// ``` + /// use ndarray::array; + /// let array = array![[1., 2., 5.], [3., 4., 6.]]; + /// assert!(!array.is_square()); + /// ``` pub fn is_square(&self) -> bool { self.nrows() == self.ncols() } From ab52a98845cd778df8b68ebcc802509acece254e Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 16 May 2020 23:09:25 +0200 Subject: [PATCH 063/651] FIX: Split shape check functions into less generic parts Split these shape check functions into parts that don't require using the element type as a generic parameter. This means that less instantiations of this code is needed for a typical user of ndarray. --- src/dimension/mod.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 28d2e9b2c..212197fed 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -139,6 +139,14 @@ pub fn can_index_slice_not_custom(data: &[A], dim: &D) -> Resul /// also implies that the length of any individual axis does not exceed /// `isize::MAX`.) pub fn max_abs_offset_check_overflow(dim: &D, strides: &D) -> Result +where + D: Dimension, +{ + max_abs_offset_check_overflow_impl(mem::size_of::(), dim, strides) +} + +fn max_abs_offset_check_overflow_impl(elem_size: usize, dim: &D, strides: &D) + -> Result where D: Dimension, { @@ -168,7 +176,7 @@ where // Determine absolute difference in units of bytes between least and // greatest address accessible by moving along all axes let max_offset_bytes = max_offset - .checked_mul(mem::size_of::()) + .checked_mul(elem_size) .ok_or_else(|| from_kind(ErrorKind::Overflow))?; // Condition 2b. if max_offset_bytes > isize::MAX as usize { @@ -216,13 +224,21 @@ pub fn can_index_slice( ) -> Result<(), ShapeError> { // Check conditions 1 and 2 and calculate `max_offset`. let max_offset = max_abs_offset_check_overflow::(dim, strides)?; + can_index_slice_impl(max_offset, data.len(), dim, strides) +} +fn can_index_slice_impl( + max_offset: usize, + data_len: usize, + dim: &D, + strides: &D, +) -> Result<(), ShapeError> { // Check condition 4. let is_empty = dim.slice().iter().any(|&d| d == 0); - if is_empty && max_offset > data.len() { + if is_empty && max_offset > data_len { return Err(from_kind(ErrorKind::OutOfBounds)); } - if !is_empty && max_offset >= data.len() { + if !is_empty && max_offset >= data_len { return Err(from_kind(ErrorKind::OutOfBounds)); } From b457aec16992b0c278396cc41b08a3adebf4ecee Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 16 May 2020 23:09:25 +0200 Subject: [PATCH 064/651] FIX: Simplify code in Baseiter::fold Remove redundant .clone() and simplify inner loop --- src/iterators/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 091119361..621c141ff 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -79,15 +79,18 @@ impl Iterator for Baseiter { let ndim = self.dim.ndim(); debug_assert_ne!(ndim, 0); let mut accum = init; - while let Some(mut index) = self.index.clone() { + while let Some(mut index) = self.index { let stride = self.strides.last_elem() as isize; let elem_index = index.last_elem(); let len = self.dim.last_elem(); let offset = D::stride_offset(&index, &self.strides); unsafe { let row_ptr = self.ptr.offset(offset); - for i in 0..(len - elem_index) { + let mut i = 0; + let i_end = len - elem_index; + while i < i_end { accum = g(accum, row_ptr.offset(i as isize * stride)); + i += 1; } } index.set_last_elem(len - 1); From eb792938727e0ba1ae18b1eaf603b54e8776be8d Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 16 May 2020 23:09:25 +0200 Subject: [PATCH 065/651] FIX: Remove unused dimension edit in apply_core_strided_[cf] The original design was that the apply cores were "resumable", and thus we'd put the dimension "back in order" when exiting the method. However, this is not in use today (and loop break from FoldWhile also bypasses this tail code, anyway), so just remove this unused line. --- src/zip/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 2b6612b54..12d5ace12 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -781,7 +781,6 @@ where index_ = self.dimension.next_for(index); } - self.dimension[unroll_axis] = inner_len; FoldWhile::Continue(acc) } @@ -812,7 +811,6 @@ where } } } - self.dimension[unroll_axis] = inner_len; FoldWhile::Continue(acc) } From a76bb9238013a9bc59bd40bcfb5d575cdca5e8a9 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 16 May 2020 23:09:25 +0200 Subject: [PATCH 066/651] FIX: Factor out the inner loops in Zip apply_core_* The innermost loop is the stretch of constant stride (for the contiguous case, this is the whole loop); with a lot of function arguments, this part is actually commmon to the three loops (contig, c, and f). Also make a small simplification of this inner loop; use a while loop to have less code to compile in this much instantiated function. --- src/zip/mod.rs | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 12d5ace12..c23791f62 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -723,7 +723,7 @@ where } } - fn apply_core_contiguous(&mut self, mut acc: Acc, mut function: F) -> FoldWhile + fn apply_core_contiguous(&mut self, acc: Acc, mut function: F) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, P: ZippableTuple, @@ -732,15 +732,35 @@ where let size = self.dimension.size(); let ptrs = self.parts.as_ptr(); let inner_strides = self.parts.contiguous_stride(); - for i in 0..size { - unsafe { - let ptr_i = ptrs.stride_offset(inner_strides, i); - acc = fold_while![function(acc, self.parts.as_ref(ptr_i))]; - } + unsafe { + self.inner(acc, ptrs, inner_strides, size, &mut function) + } + } + + /// The innermost loop of the Zip apply methods + /// + /// Run the fold while operation on a stretch of elements with constant strides + /// + /// `ptr`: base pointer for the first element in this stretch + /// `strides`: strides for the elements in this stretch + /// `len`: number of elements + /// `function`: closure + unsafe fn inner(&self, mut acc: Acc, ptr: P::Ptr, strides: P::Stride, + len: usize, function: &mut F) -> FoldWhile + where + F: FnMut(Acc, P::Item) -> FoldWhile, + P: ZippableTuple + { + let mut i = 0; + while i < len { + let p = ptr.stride_offset(strides, i); + acc = fold_while!(function(acc, self.parts.as_ref(p))); + i += 1; } FoldWhile::Continue(acc) } + fn apply_core_strided(&mut self, acc: Acc, function: F) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, @@ -773,10 +793,7 @@ where while let Some(index) = index_ { unsafe { let ptr = self.parts.uget_ptr(&index); - for i in 0..inner_len { - let p = ptr.stride_offset(inner_strides, i); - acc = fold_while!(function(acc, self.parts.as_ref(p))); - } + acc = fold_while![self.inner(acc, ptr, inner_strides, inner_len, &mut function)]; } index_ = self.dimension.next_for(index); @@ -800,10 +817,7 @@ where loop { unsafe { let ptr = self.parts.uget_ptr(&index); - for i in 0..inner_len { - let p = ptr.stride_offset(inner_strides, i); - acc = fold_while!(function(acc, self.parts.as_ref(p))); - } + acc = fold_while![self.inner(acc, ptr, inner_strides, inner_len, &mut function)]; } if !self.dimension.next_for_f(&mut index) { From ad856bd06f995f097ef8fe473c09a296c79616ab Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 29 Apr 2020 14:26:18 +0200 Subject: [PATCH 067/651] FEAT: Special case D::from_dimension for Ix1 --- src/dimension/dimension_trait.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 4f03d3aac..b2ef92e43 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -540,6 +540,14 @@ impl Dimension for Dim<[Ix; 1]> { fn try_remove_axis(&self, axis: Axis) -> Self::Smaller { self.remove_axis(axis) } + + fn from_dimension(d: &D2) -> Option { + if 1 == d.ndim() { + Some(Ix1(d[0])) + } else { + None + } + } private_impl! {} } From 511f8b2cc97bb12ce75b8d94945cef5ed80dfad6 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 1 May 2020 22:50:26 +0200 Subject: [PATCH 068/651] FIX: Use Zip::fold_while for final reduction in parallel array view When fold_with is used, use Zip::fold_while to fold the array view's parallel iterator. Note that in some cases, the IntoIterator of the view is used instead. --- src/parallel/par.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/parallel/par.rs b/src/parallel/par.rs index efd761acf..6713952b7 100644 --- a/src/parallel/par.rs +++ b/src/parallel/par.rs @@ -170,7 +170,14 @@ macro_rules! par_iter_view_wrapper { fn fold_with(self, folder: F) -> F where F: Folder, { - self.into_iter().fold(folder, move |f, elt| f.consume(elt)) + Zip::from(self.0).fold_while(folder, |mut folder, elt| { + folder = folder.consume(elt); + if folder.full() { + FoldWhile::Done(folder) + } else { + FoldWhile::Continue(folder) + } + }).into_inner() } } From 192a166e479cb68bb480a7126fd1233722f73c5c Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 11 May 2020 23:31:18 +0200 Subject: [PATCH 069/651] FIX: Make is_contiguous pub(crate) The reason this method is not yet public, is that it's not accurate (false negatives) for less common layouts. It's correct for C/F i.e row/col major layouts. --- src/impl_methods.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index db502663f..8a906be3d 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1293,7 +1293,11 @@ where is_standard_layout(&self.dim, &self.strides) } - fn is_contiguous(&self) -> bool { + /// Return true if the array is known to be contiguous. + /// + /// Will detect c- and f-contig arrays correctly, but otherwise + /// There are some false negatives. + pub(crate) fn is_contiguous(&self) -> bool { D::is_contiguous(&self.dim, &self.strides) } From 35e89f878adcaa4899b567d8deccbe6e426c11a2 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 11 May 2020 23:31:18 +0200 Subject: [PATCH 070/651] TEST: Add benchmarks for parallel collect --- benches/par_rayon.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/benches/par_rayon.rs b/benches/par_rayon.rs index e8c4cfef3..cca35e00b 100644 --- a/benches/par_rayon.rs +++ b/benches/par_rayon.rs @@ -136,3 +136,39 @@ fn rayon_add(bench: &mut Bencher) { }); }); } + +const COLL_STRING_N: usize = 64; +const COLL_F64_N: usize = 128; + +#[bench] +fn vec_string_collect(bench: &mut test::Bencher) { + let v = vec![""; COLL_STRING_N * COLL_STRING_N]; + bench.iter(|| { + v.iter().map(|s| s.to_owned()).collect::>() + }); +} + +#[bench] +fn array_string_collect(bench: &mut test::Bencher) { + let v = Array::from_elem((COLL_STRING_N, COLL_STRING_N), ""); + bench.iter(|| { + Zip::from(&v).par_apply_collect(|s| s.to_owned()) + }); +} + +#[bench] +fn vec_f64_collect(bench: &mut test::Bencher) { + let v = vec![1.; COLL_F64_N * COLL_F64_N]; + bench.iter(|| { + v.iter().map(|s| s + 1.).collect::>() + }); +} + +#[bench] +fn array_f64_collect(bench: &mut test::Bencher) { + let v = Array::from_elem((COLL_F64_N, COLL_F64_N), 1.); + bench.iter(|| { + Zip::from(&v).par_apply_collect(|s| s + 1.) + }); +} + From 84295c4e893f2ee7e1cd4d95f2ef46bfde9ce0f8 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 11 May 2020 23:31:18 +0200 Subject: [PATCH 071/651] FEAT: Factor out traits SplitAt and SplitPreference To be used by Zip and parallel Zip --- src/indexes.rs | 3 ++- src/lib.rs | 1 + src/split_at.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ src/zip/mod.rs | 44 +++++++++++++++++++++++++------------------- 4 files changed, 77 insertions(+), 20 deletions(-) create mode 100644 src/split_at.rs diff --git a/src/indexes.rs b/src/indexes.rs index a0831b0e7..6f45fb3de 100644 --- a/src/indexes.rs +++ b/src/indexes.rs @@ -7,7 +7,8 @@ // except according to those terms. use super::Dimension; use crate::dimension::IntoDimension; -use crate::zip::{Offset, Splittable}; +use crate::zip::Offset; +use crate::split_at::SplitAt; use crate::Axis; use crate::Layout; use crate::NdProducer; diff --git a/src/lib.rs b/src/lib.rs index a5c1c94ac..b40eee01a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -179,6 +179,7 @@ mod numeric_util; mod shape_builder; #[macro_use] mod slice; +mod split_at; mod stacking; #[macro_use] mod zip; diff --git a/src/split_at.rs b/src/split_at.rs new file mode 100644 index 000000000..da15dfcf3 --- /dev/null +++ b/src/split_at.rs @@ -0,0 +1,49 @@ + +use crate::imp_prelude::*; + +/// Arrays and similar that can be split along an axis +pub(crate) trait SplitAt { + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) where Self: Sized; +} + +pub(crate) trait SplitPreference : SplitAt { + fn can_split(&self) -> bool; + fn size(&self) -> usize; + fn split_preference(&self) -> (Axis, usize); + fn split(self) -> (Self, Self) where Self: Sized { + let (axis, index) = self.split_preference(); + self.split_at(axis, index) + } +} + +impl SplitAt for D +where + D: Dimension, +{ + fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { + let mut d1 = self; + let mut d2 = d1.clone(); + let i = axis.index(); + let len = d1[i]; + d1[i] = index; + d2[i] = len - index; + (d1, d2) + } +} + +impl<'a, A, D> SplitAt for ArrayViewMut<'a, A, D> + where D: Dimension +{ + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { + self.split_at(axis, index) + } +} + + +impl SplitAt for RawArrayViewMut + where D: Dimension +{ + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { + self.split_at(axis, index) + } +} diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 2b6612b54..f319afd4b 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -20,6 +20,7 @@ use crate::NdIndex; use crate::indexes::{indices, Indices}; use crate::layout::{CORDER, FORDER}; +use crate::split_at::{SplitPreference, SplitAt}; use partial_array::PartialArray; @@ -92,25 +93,6 @@ where private_impl! {} } -pub trait Splittable: Sized { - fn split_at(self, axis: Axis, index: Ix) -> (Self, Self); -} - -impl Splittable for D -where - D: Dimension, -{ - fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { - let mut d1 = self; - let mut d2 = d1.clone(); - let i = axis.index(); - let len = d1[i]; - d1[i] = index; - d2[i] = len - index; - (d1, d2) - } -} - /// Argument conversion into a producer. /// /// Slices and vectors can be used (equivalent to 1-dimensional array views). @@ -1121,9 +1103,31 @@ macro_rules! map_impl { pub fn split(self) -> (Self, Self) { debug_assert_ne!(self.size(), 0, "Attempt to split empty zip"); debug_assert_ne!(self.size(), 1, "Attempt to split zip with 1 elem"); + SplitPreference::split(self) + } + } + + impl SplitPreference for Zip<($($p,)*), D> + where D: Dimension, + $($p: NdProducer ,)* + { + fn can_split(&self) -> bool { self.size() > 1 } + + fn size(&self) -> usize { self.size() } + + fn split_preference(&self) -> (Axis, usize) { // Always split in a way that preserves layout (if any) let axis = self.max_stride_axis(); let index = self.len_of(axis) / 2; + (axis, index) + } + } + + impl SplitAt for Zip<($($p,)*), D> + where D: Dimension, + $($p: NdProducer ,)* + { + fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { let (p1, p2) = self.parts.split_at(axis, index); let (d1, d2) = self.dimension.split_at(axis, index); (Zip { @@ -1139,7 +1143,9 @@ macro_rules! map_impl { layout_tendency: self.layout_tendency, }) } + } + )+ } } From 3ab67eb062d5317edfe35ced0d5e24022c6fe2fa Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 11 May 2020 23:31:18 +0200 Subject: [PATCH 072/651] FEAT: Add ParalleIterator ParallelSplits This iterator is for internal use; it produces the splits of a Zip (it splits the Zip the same way as the regular parallel iterator for Zip, but here the whole Zip is the produced item of the iterator.) This is helpful as a building block for other operations. --- src/parallel/par.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/parallel/par.rs b/src/parallel/par.rs index 6713952b7..c7a0fcb3a 100644 --- a/src/parallel/par.rs +++ b/src/parallel/par.rs @@ -15,6 +15,7 @@ use crate::iter::AxisIter; use crate::iter::AxisIterMut; use crate::Dimension; use crate::{ArrayView, ArrayViewMut}; +use crate::split_at::SplitPreference; /// Parallel iterator wrapper. #[derive(Copy, Clone, Debug)] @@ -250,7 +251,7 @@ macro_rules! zip_impl { type Item = ($($p::Item ,)*); fn split(self) -> (Self, Option) { - if self.0.size() <= 1 { + if !self.0.can_split() { return (self, None) } let (a, b) = self.0.split(); @@ -282,3 +283,53 @@ zip_impl! { [P1 P2 P3 P4 P5], [P1 P2 P3 P4 P5 P6], } + +/// A parallel iterator (unindexed) that produces the splits of the array +/// or producer `P`. +pub(crate) struct ParallelSplits