From 0968a710ad343930b7b24a5f632367062ffa2cd1 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 7 Jun 2021 00:53:58 -0400 Subject: [PATCH 1/7] 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 2/7] 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 3/7] 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 f2f3a44d2c0da5caedccc0c54d695cc2e3949720 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Sun, 7 Nov 2021 03:13:16 -0800 Subject: [PATCH 4/7] 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 5/7] 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 533466f0d9ff4db36b6858bc00b803471da551a1 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Sun, 7 Nov 2021 05:48:34 -0800 Subject: [PATCH 6/7] 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 7/7] 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; } } }