From d2f25ad539a3a8d9e3ce9fa17bc2224c44e093d8 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 17 Mar 2016 15:59:07 +0100 Subject: [PATCH 1/6] Add methods .rows() .cols() --- src/impl_linalg.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/impl_linalg.rs b/src/impl_linalg.rs index b2a4edf9d..34e89e6f3 100644 --- a/src/impl_linalg.rs +++ b/src/impl_linalg.rs @@ -140,6 +140,11 @@ impl ArrayBase self.subview_mut(Axis(0), index) } + /// Return the number of rows (length of `Axis(0)`) in the two-dimensional array. + pub fn rows(&self) -> Ix { + self.shape().axis(Axis(0)) + } + /// Return an array view of column `index`. /// /// **Panics** if `index` is out of bounds. @@ -157,6 +162,11 @@ impl ArrayBase self.subview_mut(Axis(1), index) } + /// Return the number of columns (length of `Axis(1)`) in the two-dimensional array. + pub fn cols(&self) -> Ix { + self.shape().axis(Axis(1)) + } + /// Perform matrix multiplication of rectangular arrays `self` and `rhs`. /// /// The array shapes must agree in the way that From 93c87d18032b8bd4c2d79c8ce5f89cf32624f725 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 17 Mar 2016 15:59:07 +0100 Subject: [PATCH 2/6] Add method .scaled_add() This is known as axpy in blas --- src/impl_linalg.rs | 21 +++++++++++++++++++++ tests/oper.rs | 15 +++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/impl_linalg.rs b/src/impl_linalg.rs index 34e89e6f3..104e97703 100644 --- a/src/impl_linalg.rs +++ b/src/impl_linalg.rs @@ -238,6 +238,27 @@ impl ArrayBase } } +impl ArrayBase + where S: Data, + D: Dimension, +{ + /// Perform the operation `self += alpha * rhs` efficiently, where + /// `alpha` is a scalar and `rhs` is another array. This operation is + /// also known as `axpy` in BLAS. + /// + /// If their shapes disagree, `rhs` is broadcast to the shape of `self`. + /// + /// **Panics** if broadcasting isn’t possible. + pub fn scaled_add(&mut self, alpha: A, rhs: &ArrayBase) + where S: DataMut, + S2: Data, + A: LinalgScalar, + E: Dimension, + { + self.zip_mut_with(rhs, move |y, &x| *y = *y + (alpha * x)); + } +} + #[cfg(not(feature="blas"))] use self::mat_mul_general as mat_mul_impl; diff --git a/tests/oper.rs b/tests/oper.rs index 5690be95c..128260f6c 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -216,3 +216,18 @@ fn mat_mul_order() { assert_eq!(cc.strides()[1], 1); assert_eq!(ff.strides()[0], 1); } + +#[test] +fn scaled_add() { + let a = range_mat(16, 15); + let mut b = range_mat(16, 15); + b.applyv(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); + +} From 3191f347f6bfe11235df8f071adca3d8dc38df92 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 17 Mar 2016 15:59:07 +0100 Subject: [PATCH 3/6] Small cleanup of tests --- tests/array.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/array.rs b/tests/array.rs index 4f156a7db..37e64c165 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -115,7 +115,7 @@ fn test_add() } let B = A.clone(); - A.iadd(&B); + A = A + &B; assert_eq!(A[[0, 0]], 0); assert_eq!(A[[0, 1]], 2); assert_eq!(A[[1, 0]], 4); @@ -132,7 +132,6 @@ fn test_multidim() *elt = i as u8; } } - //println!("shape={:?}, strides={:?}", mat.shape(), mat.strides); assert_eq!(mat.dim(), (2,3,4,5,6)); } @@ -152,7 +151,7 @@ array([[[ 7, 6], fn test_negative_stride_rcarray() { let mut mat = RcArray::zeros((2, 4, 2)); - mat[(0, 0, 0)] = 1.0f32; + mat[[0, 0, 0]] = 1.0f32; for (i, elt) in mat.iter_mut().enumerate() { *elt = i as f32; } @@ -167,8 +166,8 @@ fn test_negative_stride_rcarray() } } { - let vi = mat.slice(&[S, Si(0, None, -5), S]); - let seq = [6_f32, 7., 14., 15.]; + let vi = mat.slice(s![.., ..;-5, ..]); + let seq = [6., 7., 14., 15.]; for (a, b) in vi.iter().zip(seq.iter()) { assert_eq!(*a, *b); } @@ -178,7 +177,7 @@ fn test_negative_stride_rcarray() #[test] fn test_cow() { - let mut mat = RcArray::::zeros((2,2)); + let mut mat = RcArray::zeros((2,2)); mat[[0, 0]] = 1; let n = mat.clone(); mat[[0, 1]] = 2; @@ -343,10 +342,13 @@ fn iter_size_hint() #[test] fn zero_axes() { - let a = arr1::(&[]); + let mut a = arr1::(&[]); for _ in a.iter() { assert!(false); } + a.map(|_| assert!(false)); + a.apply(|_| assert!(false)); + a.visit(|_| assert!(false)); println!("{:?}", a); let b = arr2::(&[[], [], [], []]); println!("{:?}\n{:?}", b.shape(), b); @@ -377,7 +379,7 @@ fn map1() let b = a.map(|&x| (x / 3.) as isize); assert_eq!(b, arr2(&[[0, 0], [1, 1]])); // test map to reference with array's lifetime. - let c = a.map(|x| x).into_shared(); + let c = a.map(|x| x); assert_eq!(a[(0, 0)], *c[(0, 0)]); } From 52b8140448912a6d5e064dc380a99ae4c517f088 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 17 Mar 2016 15:59:07 +0100 Subject: [PATCH 4/6] Document errors in stack() --- src/stacking.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/stacking.rs b/src/stacking.rs index 9d83093fb..4165ed4f4 100644 --- a/src/stacking.rs +++ b/src/stacking.rs @@ -11,6 +11,11 @@ use error::{ShapeError, ErrorKind, from_kind}; /// Stack arrays along the given axis. /// +/// ***Errors*** if the arrays don't all have the same dimension +/// (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, stack}; /// From 9fecfa4875abbeff2e97eae024d69089fc6f57c7 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 17 Mar 2016 15:59:07 +0100 Subject: [PATCH 5/6] Use Fn(X) closures in map and apply for now In current unstable rust, specializing on Fn() which we may want to do down the line requires that we receive an Fn() closure explicitly here. Using Fn() for a mapping function is no big restriction, and it can be loosened backwards compatibly later if wanted. --- 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 39e8084a4..33b19cb62 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1154,8 +1154,8 @@ impl ArrayBase where S: Data, D: Dimension /// [1., 2.]]) /// ); /// ``` - pub fn mapv(&self, mut f: F) -> OwnedArray - where F: FnMut(A) -> B, + pub fn mapv(&self, f: F) -> OwnedArray + where F: Fn(A) -> B, A: Clone, { self.map(move |x| f(x.clone())) @@ -1167,7 +1167,7 @@ impl ArrayBase where S: Data, D: Dimension /// Elements are visited in arbitrary order. pub fn mapv_into(mut self, f: F) -> Self where S: DataMut, - F: FnMut(A) -> A, + F: Fn(A) -> A, A: Clone, { self.applyv(f); @@ -1179,7 +1179,7 @@ impl ArrayBase where S: Data, D: Dimension /// Elements are visited in arbitrary order. pub fn apply(&mut self, f: F) where S: DataMut, - F: FnMut(&mut A), + F: Fn(&mut A), { self.unordered_foreach_mut(f); } @@ -1200,9 +1200,9 @@ impl ArrayBase where S: Data, D: Dimension /// [0.36788, 7.38906]]), 1e-5) /// ); /// ``` - pub fn applyv(&mut self, mut f: F) + pub fn applyv(&mut self, f: F) where S: DataMut, - F: FnMut(A) -> A, + F: Fn(A) -> A, A: Clone, { self.unordered_foreach_mut(move |x| *x = f(x.clone())); From ee32e849ef1a4abc1834b0e2acf56ec16495c601 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 17 Mar 2016 15:59:07 +0100 Subject: [PATCH 6/6] Add from_vec_dim_f --- src/impl_constructors.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 927022ea6..0fa617970 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -199,6 +199,17 @@ impl ArrayBase unsafe { Ok(Self::from_vec_dim_unchecked(dim, v)) } } + /// Create an array from a vector (no copying needed) using fortran + /// memory order to interpret the data. + /// + /// **Errors** if `dim` does not correspond to the number of elements in `v`. + pub fn from_vec_dim_f(dim: D, v: Vec) -> Result, ShapeError> { + if dim.size_checked() != Some(v.len()) { + return Err(error::incompatible_shapes(&v.len(), &dim)); + } + unsafe { Ok(Self::from_vec_dim_unchecked_f(dim, v)) } + } + /// Create an array from a vector (no copying needed). /// /// Unsafe because dimension is unchecked, and must be correct. @@ -216,14 +227,10 @@ impl ArrayBase /// using fortran memory order to interpret the data. /// /// Unsafe because dimension is unchecked, and must be correct. - pub unsafe fn from_vec_dim_unchecked_f(dim: D, mut v: Vec) -> ArrayBase { + pub unsafe fn from_vec_dim_unchecked_f(dim: D, v: Vec) -> ArrayBase { debug_assert!(dim.size_checked() == Some(v.len())); - ArrayBase { - ptr: v.as_mut_ptr(), - data: DataOwned::new(v), - strides: dim.fortran_strides(), - dim: dim, - } + let strides = dim.fortran_strides(); + Self::from_vec_dim_stride_unchecked(dim, strides, v) } /// Create an array from a vector and interpret it according to the