From 0386ef37de680ebca856094f3cbb48c6f344bcf9 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 1 May 2021 21:39:56 +0200 Subject: [PATCH 1/3] 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 2/3] 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 3/3] 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);