diff --git a/Cargo.toml b/Cargo.toml index d5f02a648..f16f30577 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ version = "0.1.32" default-features = false [dependencies.itertools] -version = "0.5.0" +version = "0.6.0" [dependencies.rustc-serialize] version = "0.3.20" diff --git a/benches/iter.rs b/benches/iter.rs index 106bb245c..06570b3d4 100644 --- a/benches/iter.rs +++ b/benches/iter.rs @@ -2,6 +2,7 @@ extern crate test; use test::Bencher; +use test::black_box; #[macro_use(s, azip)] extern crate ndarray; @@ -205,6 +206,70 @@ fn vector_sum_3_zip_unchecked(bench: &mut Bencher) // index iterator size const ISZ: usize = 16; +const I2DSZ: usize = 64; + +#[bench] +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 _; + } + + bench.iter(|| { + for (i, &_elt) in a.indexed_iter() { + //assert!(a[i] == elt); + black_box(i); + } + }) +} + +#[bench] +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 _; + } + + bench.iter(|| { + Zip::indexed(&a) + .apply(|i, &_elt| { + black_box(i); + //assert!(a[i] == elt); + }); + }) +} + +#[bench] +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 _; + } + + bench.iter(|| { + for (i, &_elt) in a.indexed_iter() { + //assert!(a[i] == elt); + black_box(i); + } + }) +} +#[bench] +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 _; + } + + bench.iter(|| { + Zip::indexed(&a) + .apply(|i, &_elt| { + black_box(i); + //assert!(a[i] == elt); + }); + }) +} + + #[bench] fn indexed_iter_3d_ix3(bench: &mut Bencher) { @@ -214,12 +279,29 @@ fn indexed_iter_3d_ix3(bench: &mut Bencher) { } bench.iter(|| { - for (i, &elt) in a.indexed_iter() { - assert!(a[i] == elt); + for (i, &_elt) in a.indexed_iter() { + //assert!(a[i] == elt); + black_box(i); } }) } +#[bench] +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 _; + } + + bench.iter(|| { + Zip::indexed(&a) + .apply(|i, &_elt| { + black_box(i); + //assert!(a[i] == elt); + }); + }) +} + #[bench] fn indexed_iter_3d_dyn(bench: &mut Bencher) { let mut a = Array::::zeros((ISZ, ISZ, ISZ)); @@ -229,8 +311,9 @@ fn indexed_iter_3d_dyn(bench: &mut Bencher) { let a = a.into_shape(&[ISZ; 3][..]).unwrap(); bench.iter(|| { - for (i, &elt) in a.indexed_iter() { - assert!(a[i] == elt); + for (i, &_elt) in a.indexed_iter() { + //assert!(a[i] == elt); + black_box(i); } }) } diff --git a/examples/linalg.rs b/examples/linalg.rs deleted file mode 100644 index ec864809b..000000000 --- a/examples/linalg.rs +++ /dev/null @@ -1,293 +0,0 @@ -#![allow(non_snake_case)] - -//! A few linear algebra operations on two-dimensional arrays, just for demo -//! purposes. - -extern crate ndarray; -extern crate num_traits; -extern crate num_complex; - -use num_traits::{Num, Zero, One}; -use num_traits::Float; -use num_complex::Complex; -use std::ops::{Add, Sub, Mul, Div}; - -use ndarray::{RcArray, Ix1, Ix2}; -use ndarray::{rcarr1, rcarr2}; -use ndarray::LinalgScalar; - -/// Column vector. -pub type Col = RcArray; -/// Rectangular matrix. -pub type Mat = RcArray; - -/// Trait union for a ring with 1. -pub trait Ring : Clone + Zero + Add + Sub - + One + Mul { } -impl + Sub + One + Mul> Ring for A { } - -/// Trait union for a field. -pub trait Field : Ring + Div { } -impl> Field for A { } - -/// A real or complex number. -pub trait ComplexField : LinalgScalar -{ - #[inline] - fn conjugate(self) -> Self { self } - fn sqrt_real(self) -> Self; - #[inline] - fn is_complex() -> bool { false } -} - -impl ComplexField for f32 -{ - #[inline] - fn sqrt_real(self) -> f32 { self.sqrt() } -} - -impl ComplexField for f64 -{ - #[inline] - fn sqrt_real(self) -> f64 { self.sqrt() } -} - -impl ComplexField for Complex -{ - #[inline] - fn conjugate(self) -> Complex { self.conj() } - fn sqrt_real(self) -> Complex { Complex::new(self.re.sqrt(), A::zero()) } - #[inline] - fn is_complex() -> bool { true } -} - -// Some examples. -fn main() { - chol(); - subst(); - lst_squares(); -} - -fn chol() -{ - let _ = rcarr2(&[[1., 2.], [3., 4.]]); // not pos. def. - let a = rcarr2(&[[10., 14.], [14., 20.]]); // aT a is pos def - - let chol = cholesky(a); - let ans = - rcarr2(&[[3.16227770, 0.00000000], - [4.42718887, 0.63245525]]); - - assert!(ans.all_close(&chol, 0.001)); - - // Compute bT b for a pos def matrix - let b = RcArray::linspace(0f32, 8., 9).reshape((3, 3)); - let mut bt = b.clone(); - bt.swap_axes(0, 1); - let bpd = bt.dot(&b).into_shared(); - println!("bpd=\n{:?}", bpd); - let chol = cholesky(bpd); - println!("chol=\n{:.8?}", chol); - - let ans = - rcarr2(&[[6.70820379, 0.00000000, 0.00000000], - [8.04984474, 1.09544373, 0.00000000], - [9.39148617, 2.19088745, 0.00000000]]); - assert!(ans.all_close(&chol, 0.001)); - - let a = - rcarr2(&[[ 0.05201001, 0.22982409, 0.1014132 ], - [ 0.22982409, 1.105822 , 0.37946544], - [ 0.1014132 , 0.37946544, 1.16199134]]); - let chol = cholesky(a); - - let ans = - rcarr2(&[[ 0.22805704, 0. , 0. ], - [ 1.00774829, 0.30044197, 0. ], - [ 0.44468348, -0.2285419 , 0.95499557]]); - assert!(ans.all_close(&chol, 0.001)); -} - -fn subst() -{ - let lll = - rcarr2(&[[ 0.22805704, 0. , 0. ], - [ 1.00774829, 0.30044197, 0. ], - [ 0.44468348, -0.2285419 , 0.95499557]]); - let ans = rcarr1::(&[4.384868, -8.050947, -0.827078]); - - assert!(ans.all_close(&subst_fw(&lll, &rcarr1(&[1., 2., 3.])), - 0.001)); -} - -fn lst_squares() -{ - let xs = - rcarr2(&[[ 2., 3.], - [-2., -1.], - [ 1., 5.], - [-1., 2.]]); - let b = rcarr1(&[1., -1., 2., 1.]); - let x_lstsq = least_squares(&xs, &b); - let ans = rcarr1(&[0.070632, 0.390335]); - assert!(x_lstsq.all_close(&ans, 0.001)); -} - -/// Solve *a x = b* with linear least squares approximation. -/// -/// It is used to find the best fit for an overdetermined system, -/// i.e. the number of rows in *a* is larger than the number of -/// unknowns *x*. -/// -/// Return best fit for *x*. -pub fn least_squares(a: &Mat, b: &Col) -> Col -{ - // Using transpose: a.T a x = a.T b; - // a.T a being square gives naive solution - // x_lstsq = inv(a.T a) a.T b - // - // Solve using cholesky decomposition - // aT a x = aT b - // - // Factor aT a into L L.T - // - // L L.T x = aT b - // - // => L z = aT b - // fw subst for z - // => L.T x = z - // bw subst for x estimate - // - let mut aT = a.clone(); - aT.swap_axes(0, 1); - if ::is_complex() { - // conjugate transpose - for elt in aT.iter_mut() { - *elt = elt.conjugate(); - } - } - - let aT_a = aT.dot(a).into_shared(); - let mut L = cholesky(aT_a); - let rhs = aT.dot(b).into_shared(); - - // Solve L z = aT b - let z = subst_fw(&L, &rhs); - - // Solve L.T x = z - if ::is_complex() { - // conjugate transpose - // only elements below the diagonal have imag part - let (m, _) = L.dim(); - for i in 1..m { - for j in 0..i { - let elt = &mut L[[i, j]]; - *elt = elt.conjugate(); - } - } - } - L.swap_axes(0, 1); - - // => x_lstsq - subst_bw(&L, &z) -} - -/// Factor *a = L LT*. -/// -/// *a* should be a square matrix, hermitian and positive definite. -/// -/// https://en.wikipedia.org/wiki/Cholesky_decomposition -/// -/// “The Cholesky decomposition is mainly used for the numerical solution of -/// linear equations Ax = b. -/// -/// If A is symmetric and positive definite, then we can solve Ax = b by first -/// computing the Cholesky decomposition A = LL*, then solving Ly = b for y by -/// forward substitution, and finally solving L*x = y for x by back -/// substitution.” -/// -/// Return L. -pub fn cholesky(a: Mat) -> Mat -{ - let z = A::zero(); - let (m, n) = a.dim(); - assert!(m == n); - // Perform the operation in-place on `a` - let mut L = a; - for i in 0..m { - // Entries 0 .. i before the diagonal - for j in 0..i { - // A = ( - // L²_1,1 - // L_2,1 L_1,1 L²_2,1 + L²_2,2 - // L_3,1 L_1,1 L_3,1 L_2,1 + L_3,2 L_2,2 L²_3,1 + L²_3,2 + L²_3,3 - // .. ) - let mut lik_ljk_sum = z; - { - // L_ik for k = 0 .. j - // L_jk for k = 0 .. j - let Lik = L.row(i).into_iter(); - let Ljk = L.row(j).into_iter(); - for (&lik, &ljk) in Lik.zip(Ljk).take(j as usize) { - lik_ljk_sum = lik_ljk_sum + lik * ljk.conjugate(); - } - } - - // L_ij = [ A_ij - Sum(k = 1 .. j) L_ik L_jk ] / L_jj - L[[i, j]] = (L[[i, j]] - lik_ljk_sum) / L[[j, j]]; - } - - // Diagonal where i == j - // L_jj = Sqrt[ A_jj - Sum(k = 1 .. j) L_jk L_jk ] - let j = i; - let mut ljk_sum = z; - // L_jk for k = 0 .. j - for &ljk in L.row(j).into_iter().take(j as usize) { - ljk_sum = ljk_sum + ljk * ljk.conjugate(); - } - L[[j, j]] = (L[[j, j]] - ljk_sum).sqrt_real(); - - // After the diagonal - // L_ij = 0 for j > i - for j in i + 1..n { - L[[i, j]] = z; - } - } - L -} - -/// Solve *L x = b* where *L* is a lower triangular matrix. -pub fn subst_fw(l: &Mat, b: &Col) -> Col -{ - let (m, n) = l.dim(); - assert!(m == n); - assert!(m == b.len()); - let mut x = Col::zeros(m); - for i in 0..m { - // b_lx_sum = b[i] - Sum(for j = 0 .. i) L_ij x_j - let mut b_lx_sum = b[i]; - for j in 0..i { - b_lx_sum = b_lx_sum - l[[i, j]] * x[j]; - } - x[i] = b_lx_sum / l[[i, i]]; - } - x -} - -/// Solve *U x = b* where *U* is an upper triangular matrix. -pub fn subst_bw(u: &Mat, b: &Col) -> Col -{ - let (m, n) = u.dim(); - assert!(m == n); - assert!(m == b.len()); - let mut x = Col::zeros(m); - for i in (0..m).rev() { - // b_ux_sum = b[i] - Sum(for j = i .. m) U_ij x_j - let mut b_ux_sum = b[i]; - for j in i..m { - b_ux_sum = b_ux_sum - u[[i, j]] * x[j]; - } - x[i] = b_ux_sum / u[[i, i]]; - } - x -} diff --git a/examples/zip_many.rs b/examples/zip_many.rs index e7a0166c2..778f4bffa 100644 --- a/examples/zip_many.rs +++ b/examples/zip_many.rs @@ -3,6 +3,7 @@ extern crate ndarray; use ndarray::prelude::*; +use ndarray::Zip; fn main() { let n = 16; @@ -33,5 +34,19 @@ fn main() { let chunk_sz = (2, 2); let nchunks = (n / chunk_sz.0, n / chunk_sz.1); let mut sums = Array::zeros(nchunks); - azip!(mut sums, ref a (a.whole_chunks(chunk_sz)) in { *sums = a.scalar_sum() }); + azip!(mut sums, ref a (a.exact_chunks(chunk_sz)) in { *sums = a.scalar_sum() }); + + + // Let's imagine we split to parallelize + { + let (x, y) = Zip::indexed(&mut a).split(); + x.apply(|(_, j), elt| { + *elt = elt.powi(j as i32); + }); + + y.apply(|(_, j), elt| { + *elt = elt.powi(j as i32); + }); + } + println!("{:8.3?}", a); } diff --git a/parallel/src/par.rs b/parallel/src/par.rs index 3efcc8119..0afa2d5b4 100644 --- a/parallel/src/par.rs +++ b/parallel/src/par.rs @@ -10,8 +10,8 @@ use rayon::par_iter::internal::Producer; use rayon::par_iter::internal::UnindexedProducer; use rayon::par_iter::internal::bridge_unindexed; -use ndarray::AxisIter; -use ndarray::AxisIterMut; +use ndarray::iter::AxisIter; +use ndarray::iter::AxisIterMut; use ndarray::{Dimension}; use ndarray::{ArrayView, ArrayViewMut}; diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 4968c63be..354b2b691 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -15,9 +15,11 @@ use std::ops::{ }; use imp_prelude::*; -use { +use iter::{ Iter, IterMut, +}; +use { NdIndex, }; diff --git a/src/dimension/axes.rs b/src/dimension/axes.rs index 4b2913d22..2877600bd 100644 --- a/src/dimension/axes.rs +++ b/src/dimension/axes.rs @@ -15,7 +15,7 @@ pub fn axes_of<'a, D>(d: &'a D, strides: &'a D) -> Axes<'a, D> /// An iterator over the length and stride of each axis of an array. /// -/// See [`.axes()`](struct.ArrayBase.html#method.axes) for more information. +/// See [`.axes()`](../struct.ArrayBase.html#method.axes) for more information. /// /// Iterator element type is `AxisDescription`. /// diff --git a/src/dimension/axis.rs b/src/dimension/axis.rs index e222912f3..f09ee95d8 100644 --- a/src/dimension/axis.rs +++ b/src/dimension/axis.rs @@ -22,9 +22,6 @@ impl Axis { /// Return the index of the axis. #[inline(always)] pub fn index(&self) -> usize { self.0 } - #[deprecated(note = "Renamed to .index()")] - #[inline(always)] - pub fn axis(&self) -> usize { self.0 } } copy_and_clone!{Axis} diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index f2e7b5fe1..3612c1066 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -146,6 +146,12 @@ pub trait Dimension : Clone + Eq + Debug + Send + Sync + Default + strides } + #[doc(hidden)] + // Return an index of same dimensionality + fn zero_index(&self) -> Self { + Self::default() + } + #[doc(hidden)] #[inline] fn first_index(&self) -> Option { @@ -758,6 +764,17 @@ impl Dimension for IxDyn fn into_pattern(self) -> Self::Pattern { self } + + fn zero_index(&self) -> Self { + const ZEROS: &'static [usize] = &[0; 4]; + let n = self.ndim(); + if n <= ZEROS.len() { + Dim(&ZEROS[..n]) + } else { + Dim(vec![0; n]) + } + } + #[inline] fn try_remove_axis(&self, axis: Axis) -> Self::Smaller { if self.ndim() > 0 { diff --git a/src/dimension/ndindex.rs b/src/dimension/ndindex.rs index a33ac37e9..b95ae2df4 100644 --- a/src/dimension/ndindex.rs +++ b/src/dimension/ndindex.rs @@ -204,20 +204,20 @@ impl<'a> IntoDimension for &'a [Ix] { } } -unsafe impl<'a> NdIndex for &'a [Ix] { +unsafe impl<'a> NdIndex for &'a IxDyn { fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { - stride_offset_checked(dim.ix(), strides.ix(), *self) + (**self).index_checked(dim, strides) } fn index_unchecked(&self, strides: &IxDyn) -> isize { - zip(strides.ix(), *self).map(|(&s, &i)| stride_offset(i, s)).sum() + (**self).index_unchecked(strides) } } -unsafe impl NdIndex for IxDynImpl { +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).map(|(&s, &i)| stride_offset(i, s)).sum() + zip(strides.ix(), *self).map(|(&s, &i)| stride_offset(i, s)).sum() } } diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 624bfc11d..6d44c2452 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -185,7 +185,7 @@ impl ArrayBase F: FnMut(D::Pattern) -> A, { let shape = shape.into_shape(); - let v = to_vec_mapped(indices(shape.dim.clone()), f); + let v = to_vec_mapped(indices(shape.dim.clone()).into_iter(), f); unsafe { Self::from_shape_vec_unchecked(shape, v) } } diff --git a/src/impl_methods.rs b/src/impl_methods.rs index ae4a6563a..17cc21b51 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -25,27 +25,30 @@ use dimension::{axes_of, Axes, merge_axes, stride_offset}; use iterators::{ new_inners, new_inners_mut, - whole_chunks_of, - whole_chunks_mut_of, - Inners, - InnersMut, + exact_chunks_of, + exact_chunks_mut_of, }; use zip::Zip; use { NdIndex, +}; +use iter::{ AxisChunksIter, AxisChunksIterMut, Iter, IterMut, IndexedIter, IndexedIterMut, + Inners, + InnersMut, AxisIter, AxisIterMut, - WholeChunks, - WholeChunksMut, + ExactChunks, + ExactChunksMut, }; use stacking::stack; +use PrivateNew; /// # Methods For All Array Types impl ArrayBase where S: Data, D: Dimension @@ -181,7 +184,7 @@ impl ArrayBase where S: Data, D: Dimension /// /// Iterator element type is `(D::Pattern, &A)`. pub fn indexed_iter(&self) -> IndexedIter { - IndexedIter(self.view().into_elements_base()) + IndexedIter::new(self.view().into_elements_base()) } /// Return an iterator of indexes and mutable references to the elements of the array. @@ -193,7 +196,7 @@ impl ArrayBase where S: Data, D: Dimension pub fn indexed_iter_mut(&mut self) -> IndexedIterMut where S: DataMut, { - IndexedIterMut(self.view_mut().into_elements_base()) + IndexedIterMut::new(self.view_mut().into_elements_base()) } @@ -458,52 +461,10 @@ impl ArrayBase where S: Data, D: Dimension } } - /// Return a producer and iterable that traverses over all lanes - /// pointing in the direction of `axis`. - /// - /// For example, in a 2 × 2 × 3 array, the iterator element - /// is a row of 3 elements (and there are 2 × 2 = 4 rows in total). - /// - /// Iterator element is `ArrayView1` (1D array view); note that it is - /// always 1D. - /// - /// ``` - /// use ndarray::{arr3, aview1, Axis}; - /// - /// let a = arr3(&[[[ 0, 1, 2], - /// [ 3, 4, 5]], - /// [[ 6, 7, 8], - /// [ 9, 10, 11]]]); - /// - /// let inner0 = a.inners(Axis(0)); - /// let inner1 = a.inners(Axis(1)); - /// let inner2 = a.inners(Axis(2)); - /// - /// // The first lane for axis 0 is [0, 6] - /// assert_eq!(inner0.into_iter().next().unwrap(), aview1(&[0, 6])); - /// // The first lane for axis 1 is [0, 3] - /// assert_eq!(inner1.into_iter().next().unwrap(), aview1(&[0, 3])); - /// // The first lane for axis 2 is [0, 1, 2] - /// assert_eq!(inner2.into_iter().next().unwrap(), aview1(&[0, 1, 2])); - /// ``` - pub fn inners(&self, axis: Axis) -> Inners { - new_inners(self.view(), axis) - } - - /// Return a producer and iterable that traverses over all axes but the - /// selected axis. - /// - /// Iterator element is `ArrayViewMut1` (1D read-write array view). - pub fn inners_mut(&mut self, axis: Axis) -> InnersMut - where S: DataMut - { - new_inners_mut(self.view_mut(), axis) - } - /// Return a producer and iterable that traverses over the *generalized* /// rows of the array. For a 2D array these are the regular rows. /// - /// This is equivalent to `.inners(Axis(n - 1))` where *n* is `self.ndim()`. + /// This is equivalent to `.lanes(Axis(n - 1))` where *n* is `self.ndim()`. /// /// For an array of dimensions *a* × *b* × *c* × ... × *l* × *m* /// it has *a* × *b* × *c* × ... × *l* rows each of length *m*. @@ -547,7 +508,7 @@ impl ArrayBase where S: Data, D: Dimension /// Return a producer and iterable that traverses over the *generalized* /// columns of the array. For a 2D array these are the regular columns. /// - /// This is equivalent to `.inners(Axis(0))`. + /// This is equivalent to `.lanes(Axis(0))`. /// /// For an array of dimensions *a* × *b* × *c* × ... × *l* × *m* /// it has *b* × *c* × ... × *l* × *m* columns each of length *a*. @@ -584,6 +545,49 @@ impl ArrayBase where S: Data, D: Dimension new_inners_mut(self.view_mut(), Axis(0)) } + /// Return a producer and iterable that traverses over all 1D lanes + /// pointing in the direction of `axis`. + /// + /// When the point 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. + /// + /// Iterator element is `ArrayView1` (1D array view). + /// + /// ``` + /// use ndarray::{arr3, aview1, Axis}; + /// + /// let a = arr3(&[[[ 0, 1, 2], + /// [ 3, 4, 5]], + /// [[ 6, 7, 8], + /// [ 9, 10, 11]]]); + /// + /// let inner0 = a.lanes(Axis(0)); + /// let inner1 = a.lanes(Axis(1)); + /// let inner2 = a.lanes(Axis(2)); + /// + /// // The first lane for axis 0 is [0, 6] + /// assert_eq!(inner0.into_iter().next().unwrap(), aview1(&[0, 6])); + /// // The first lane for axis 1 is [0, 3] + /// assert_eq!(inner1.into_iter().next().unwrap(), aview1(&[0, 3])); + /// // The first lane for axis 2 is [0, 1, 2] + /// assert_eq!(inner2.into_iter().next().unwrap(), aview1(&[0, 1, 2])); + /// ``` + pub fn lanes(&self, axis: Axis) -> Inners { + new_inners(self.view(), axis) + } + + /// Return a producer and iterable that traverses over all 1D lanes + /// pointing in the direction of `axis`. + /// + /// Iterator element is `ArrayViewMut1` (1D read-write array view). + pub fn lanes_mut(&mut self, axis: Axis) -> InnersMut + where S: DataMut + { + new_inners_mut(self.view_mut(), axis) + } + + /// Return an iterator that traverses over the outermost dimension /// and yields each subview. /// @@ -691,7 +695,7 @@ impl ArrayBase where S: Data, D: Dimension iterators::new_chunk_iter_mut(self.view_mut(), axis.index(), size) } - /// Return a whole chunks producer (and iterable). + /// Return an exact chunks producer (and iterable). /// /// It produces the whole chunks of a given n-dimensional chunk size, /// skipping the remainder along each dimension that doesn't fit evenly. @@ -702,13 +706,21 @@ impl ArrayBase where S: Data, D: Dimension /// **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.) - pub fn whole_chunks(&self, chunk_size: E) -> WholeChunks + pub fn exact_chunks(&self, chunk_size: E) -> ExactChunks where E: IntoDimension, { - whole_chunks_of(self.view(), chunk_size) + exact_chunks_of(self.view(), chunk_size) } - /// Return a whole chunks producer (and iterable). + #[doc(hidden)] + #[deprecated(note="Renamed to exact_chunks")] + pub fn whole_chunks(&self, chunk_size: E) -> ExactChunks + where E: IntoDimension, + { + self.exact_chunks(chunk_size) + } + + /// Return an exact chunks producer (and iterable). /// /// It produces the whole chunks of a given n-dimensional chunk size, /// skipping the remainder along each dimension that doesn't fit evenly. @@ -726,7 +738,7 @@ impl ArrayBase where S: Data, D: Dimension /// let mut a = Array::zeros((6, 7)); /// /// // Fill each 2 × 2 chunk with the index of where it appeared in iteration - /// for (i, mut chunk) in a.whole_chunks_mut((2, 2)).into_iter().enumerate() { + /// for (i, mut chunk) in a.exact_chunks_mut((2, 2)).into_iter().enumerate() { /// chunk.fill(i); /// } /// @@ -740,11 +752,20 @@ impl ArrayBase where S: Data, D: Dimension /// [6, 6, 7, 7, 8, 8, 0], /// [6, 6, 7, 7, 8, 8, 0]])); /// ``` - pub fn whole_chunks_mut(&mut self, chunk_size: E) -> WholeChunksMut + pub fn exact_chunks_mut(&mut self, chunk_size: E) -> ExactChunksMut + where E: IntoDimension, + S: DataMut + { + exact_chunks_mut_of(self.view_mut(), chunk_size) + } + + #[doc(hidden)] + #[deprecated(note="Renamed to exact_chunks_mut")] + pub fn whole_chunks_mut(&mut self, chunk_size: E) -> ExactChunksMut where E: IntoDimension, S: DataMut { - whole_chunks_mut_of(self.view_mut(), chunk_size) + self.exact_chunks_mut(chunk_size) } // Return (length, stride) for diagonal diff --git a/src/indexes.rs b/src/indexes.rs index 030e0d3ab..b03e8a906 100644 --- a/src/indexes.rs +++ b/src/indexes.rs @@ -8,17 +8,21 @@ use {ArrayBase, Data}; use super::Dimension; use dimension::IntoDimension; +use Axis; +use Layout; +use NdProducer; +use zip::{Offset, Splittable}; /// An iterator over the indexes of an array shape. /// /// Iterator element type is `D`. #[derive(Clone)] -pub struct Indices { +pub struct IndicesIter { dim: D, index: Option, } -/// Create an iterator over the array shape `shape`. +/// Create an iterable of the array shape `shape`. /// /// *Note:* prefer higher order methods, arithmetic operations and /// non-indexed iteration before using indices. @@ -27,12 +31,12 @@ pub fn indices(shape: E) -> Indices { let dim = shape.into_dimension(); Indices { - index: dim.first_index(), + start: dim.zero_index(), dim: dim, } } -/// Create an iterator over the indices of the passed-in array. +/// Return an iterable of the indices of the passed-in array. /// /// *Note:* prefer higher order methods, arithmetic operations and /// non-indexed iteration before using indices. @@ -42,7 +46,7 @@ pub fn indices_of(array: &ArrayBase) -> Indices indices(array.dim()) } -impl Iterator for Indices +impl Iterator for IndicesIter where D: Dimension, { type Item = D::Pattern; @@ -73,6 +77,122 @@ impl Iterator for Indices } } -impl ExactSizeIterator for Indices +impl ExactSizeIterator for IndicesIter where D: Dimension {} + +impl IntoIterator for Indices + where D: Dimension +{ + type Item = D::Pattern; + type IntoIter = IndicesIter; + fn into_iter(self) -> Self::IntoIter { + let sz = self.dim.size(); + let index = if sz != 0 { Some(self.start) } else { None }; + IndicesIter { + index: index, + dim: self.dim, + } + } +} + +/// Indices producer and iterable. +/// +/// `Indices` is an `NdProducer` that produces the indices of an array shape. +#[derive(Copy, Clone, Debug)] +pub struct Indices + where D: Dimension +{ + start: D, + dim: D, +} + +#[derive(Copy, Clone, Debug)] +pub struct IndexPtr { + index: D, +} + +impl Offset for IndexPtr + where D: Dimension + Copy, +{ + // stride: The axis to increment + type Stride = usize; + + unsafe fn stride_offset(mut self, stride: Self::Stride, index: usize) -> Self { + self.index[stride] += index; + self + } + private_impl!{} +} + +impl NdProducer for Indices { + type Item = D::Pattern; + type Dim = D; + type Ptr = IndexPtr; + type Stride = usize; + + private_impl!{} + + #[doc(hidden)] + fn raw_dim(&self) -> Self::Dim { + self.dim.clone() + } + + #[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() + } else { + Layout::none() + } + } + + #[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: index } + } + + #[doc(hidden)] + fn stride_of(&self, axis: Axis) -> Self::Stride { + axis.index() + } + + #[inline(always)] + fn contiguous_stride(&self) -> Self::Stride { 0 } + + #[doc(hidden)] + 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, + }) + } +} + diff --git a/src/iterators/chunks.rs b/src/iterators/chunks.rs index 04ca80431..5c7471db6 100644 --- a/src/iterators/chunks.rs +++ b/src/iterators/chunks.rs @@ -8,12 +8,12 @@ use ::ElementsBaseMut; impl_ndproducer! { ['a, A, D: Dimension] [Clone => 'a, A, D: Clone ] - WholeChunks { + ExactChunks { base, chunk, inner_strides, } - WholeChunks<'a, A, D> { + ExactChunks<'a, A, D> { type Dim = D; type Item = ArrayView<'a, A, D>; @@ -27,19 +27,19 @@ impl_ndproducer! { type BaseProducerRef<'a, A, D> = ArrayView<'a, A, D>; type BaseProducerMut<'a, A, D> = ArrayViewMut<'a, A, D>; -/// Whole chunks producer and iterable. +/// Exact chunks producer and iterable. /// -/// See [`.whole_chunks()`](struct.ArrayBase.html#method.whole_chunks) for more +/// See [`.exact_chunks()`](../struct.ArrayBase.html#method.exact_chunks) for more /// information. //#[derive(Debug)] -pub struct WholeChunks<'a, A: 'a, D> { +pub struct ExactChunks<'a, A: 'a, D> { base: BaseProducerRef<'a, A, D>, chunk: D, inner_strides: D, } /// **Panics** if any chunk dimension is zero
-pub fn whole_chunks_of(mut a: ArrayView, chunk: E) -> WholeChunks +pub fn exact_chunks_of(mut a: ArrayView, chunk: E) -> ExactChunks where D: Dimension, E: IntoDimension, { @@ -54,21 +54,21 @@ pub fn whole_chunks_of(mut a: ArrayView, chunk: E) -> WholeChunks let inner_strides = a.raw_strides(); a.strides *= &chunk; - WholeChunks { + ExactChunks { base: a, chunk: chunk, inner_strides: inner_strides, } } -impl<'a, A, D> IntoIterator for WholeChunks<'a, A, D> +impl<'a, A, D> IntoIterator for ExactChunks<'a, A, D> where D: Dimension, A: 'a, { type Item = ::Item; - type IntoIter = WholeChunksIter<'a, A, D>; + type IntoIter = ExactChunksIter<'a, A, D>; fn into_iter(self) -> Self::IntoIter { - WholeChunksIter { + ExactChunksIter { iter: self.base.into_elements_base(), chunk: self.chunk, inner_strides: self.inner_strides, @@ -76,11 +76,11 @@ impl<'a, A, D> IntoIterator for WholeChunks<'a, A, D> } } -/// Whole chunks iterator. +/// Exact chunks iterator. /// -/// See [`.whole_chunks()`](struct.ArrayBase.html#method.whole_chunks) for more +/// See [`.exact_chunks()`](../struct.ArrayBase.html#method.exact_chunks) for more /// information. -pub struct WholeChunksIter<'a, A: 'a, D> { +pub struct ExactChunksIter<'a, A: 'a, D> { iter: ElementsBase<'a, A, D>, chunk: D, inner_strides: D, @@ -89,12 +89,12 @@ pub struct WholeChunksIter<'a, A: 'a, D> { impl_ndproducer! { ['a, A, D: Dimension] [Clone => ] - WholeChunksMut { + ExactChunksMut { base, chunk, inner_strides, } - WholeChunksMut<'a, A, D> { + ExactChunksMut<'a, A, D> { type Dim = D; type Item = ArrayViewMut<'a, A, D>; @@ -106,20 +106,20 @@ impl_ndproducer! { } } -/// Whole chunks producer and iterable. +/// Exact chunks producer and iterable. /// -/// See [`.whole_chunks_mut()`](struct.ArrayBase.html#method.whole_chunks_mut) +/// See [`.exact_chunks_mut()`](../struct.ArrayBase.html#method.exact_chunks_mut) /// for more information. //#[derive(Debug)] -pub struct WholeChunksMut<'a, A: 'a, D> { +pub struct ExactChunksMut<'a, A: 'a, D> { base: BaseProducerMut<'a, A, D>, chunk: D, inner_strides: D, } /// **Panics** if any chunk dimension is zero
-pub fn whole_chunks_mut_of(mut a: ArrayViewMut, chunk: E) - -> WholeChunksMut +pub fn exact_chunks_mut_of(mut a: ArrayViewMut, chunk: E) + -> ExactChunksMut where D: Dimension, E: IntoDimension, { @@ -134,21 +134,21 @@ pub fn whole_chunks_mut_of(mut a: ArrayViewMut, chunk: E) let inner_strides = a.raw_strides(); a.strides *= &chunk; - WholeChunksMut { + ExactChunksMut { base: a, chunk: chunk, inner_strides: inner_strides, } } -impl<'a, A, D> IntoIterator for WholeChunksMut<'a, A, D> +impl<'a, A, D> IntoIterator for ExactChunksMut<'a, A, D> where D: Dimension, A: 'a, { type Item = ::Item; - type IntoIter = WholeChunksIterMut<'a, A, D>; + type IntoIter = ExactChunksIterMut<'a, A, D>; fn into_iter(self) -> Self::IntoIter { - WholeChunksIterMut { + ExactChunksIterMut { iter: self.base.into_elements_base(), chunk: self.chunk, inner_strides: self.inner_strides, @@ -206,12 +206,12 @@ macro_rules! impl_iterator { impl_iterator!{ ['a, A, D: Dimension] [Clone => 'a, A, D: Clone] - WholeChunksIter { + ExactChunksIter { iter, chunk, inner_strides, } - WholeChunksIter<'a, A, D> { + ExactChunksIter<'a, A, D> { type Item = ArrayView<'a, A, D>; fn item(&mut self, elt) { @@ -228,12 +228,12 @@ impl_iterator!{ impl_iterator!{ ['a, A, D: Dimension] [Clone => ] - WholeChunksIterMut { + ExactChunksIterMut { iter, chunk, inner_strides, } - WholeChunksIterMut<'a, A, D> { + ExactChunksIterMut<'a, A, D> { type Item = ArrayViewMut<'a, A, D>; fn item(&mut self, elt) { @@ -247,11 +247,11 @@ impl_iterator!{ } } -/// Whole chunks iterator. +/// Exact chunks iterator. /// -/// See [`.whole_chunks_mut()`](struct.ArrayBase.html#method.whole_chunks_mut) +/// See [`.exact_chunks_mut()`](../struct.ArrayBase.html#method.exact_chunks_mut) /// for more information. -pub struct WholeChunksIterMut<'a, A: 'a, D> { +pub struct ExactChunksIterMut<'a, A: 'a, D> { iter: ElementsBaseMut<'a, A, D>, chunk: D, inner_strides: D, diff --git a/src/iterators/iter.rs b/src/iterators/iter.rs new file mode 100644 index 000000000..2ca30b07b --- /dev/null +++ b/src/iterators/iter.rs @@ -0,0 +1,35 @@ + +//! Producers, iterables and iterators. +//! +//! This module collects all concrete producer, iterable and iterator +//! implementation structs. +//! +//! +//! See also [`NdProducer`](../trait.NdProducer.html). + + +pub use dimension::{ + Axes, +}; +pub use indexes::{ + Indices, + IndicesIter, +}; +pub use iterators::{ + Iter, + IterMut, + IndexedIter, + IndexedIterMut, + Inners, + InnersMut, + LaneIter, + LaneIterMut, + AxisIter, + AxisIterMut, + AxisChunksIter, + AxisChunksIterMut, + ExactChunks, + ExactChunksIter, + ExactChunksMut, + ExactChunksIterMut, +}; diff --git a/src/iterators/inners.rs b/src/iterators/lanes.rs similarity index 90% rename from src/iterators/inners.rs rename to src/iterators/lanes.rs index 7513d558b..e8dd18792 100644 --- a/src/iterators/inners.rs +++ b/src/iterators/lanes.rs @@ -1,8 +1,8 @@ use imp_prelude::*; use {NdProducer, Layout}; -use super::InnerIter; -use super::InnerIterMut; +use super::LaneIter; +use super::LaneIterMut; impl_ndproducer! { ['a, A, D: Dimension] @@ -22,7 +22,7 @@ impl_ndproducer! { } } -/// See [`.inners()`](struct.ArrayBase.html#method.inners) +/// See [`.lanes()`](../struct.ArrayBase.html#method.lanes) /// for more information. pub struct Inners<'a, A: 'a, D> { base: ArrayView<'a, A, D>, @@ -78,9 +78,9 @@ impl<'a, A, D> IntoIterator for Inners<'a, A, D> where D: Dimension, { type Item = ::Item; - type IntoIter = InnerIter<'a, A, D>; + type IntoIter = LaneIter<'a, A, D>; fn into_iter(self) -> Self::IntoIter { - InnerIter { + LaneIter { iter: self.base.into_base_iter(), inner_len: self.inner_len, inner_stride: self.inner_stride, @@ -88,7 +88,7 @@ impl<'a, A, D> IntoIterator for Inners<'a, A, D> } } -/// See [`.inners_mut()`](struct.ArrayBase.html#method.inners_mut) +/// See [`.lanes_mut()`](../struct.ArrayBase.html#method.lanes_mut) /// for more information. pub struct InnersMut<'a, A: 'a, D> { base: ArrayViewMut<'a, A, D>, @@ -126,9 +126,9 @@ impl<'a, A, D> IntoIterator for InnersMut<'a, A, D> where D: Dimension, { type Item = ::Item; - type IntoIter = InnerIterMut<'a, A, D>; + type IntoIter = LaneIterMut<'a, A, D>; fn into_iter(self) -> Self::IntoIter { - InnerIterMut { + LaneIterMut { iter: self.base.into_base_iter(), inner_len: self.inner_len, inner_stride: self.inner_stride, diff --git a/src/iterators/macros.rs b/src/iterators/macros.rs index bcc5bb4f2..dcd4ee753 100644 --- a/src/iterators/macros.rs +++ b/src/iterators/macros.rs @@ -38,7 +38,8 @@ macro_rules! impl_ndproducer { } }) => { impl<$($typarm)*> NdProducer for $fulltype { - type Elem = A; + type Ptr = *mut A; + type Stride = isize; $( type $atyn = $atyv; )* diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 80c2a9a07..917236299 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -9,15 +9,17 @@ #[macro_use] mod macros; mod chunks; -mod inners; +mod lanes; +pub mod iter; use std::marker::PhantomData; use std::ptr; use Ix1; +use PrivateNew; + use super::{Dimension, Ix, Ixs}; -use super::{Iter, ElementsRepr, ElementsBase, ElementsBaseMut, IterMut, IndexedIter, IndexedIterMut}; use super::{ ArrayBase, Data, @@ -29,20 +31,22 @@ use super::{ }; pub use self::chunks::{ - WholeChunks, - WholeChunksIter, - whole_chunks_of, - WholeChunksMut, - WholeChunksIterMut, - whole_chunks_mut_of, + ExactChunks, + ExactChunksIter, + exact_chunks_of, + ExactChunksMut, + ExactChunksIterMut, + exact_chunks_mut_of, }; -pub use self::inners::{ +pub use self::lanes::{ new_inners, new_inners_mut, Inners, InnersMut, }; +use std::slice::{self, Iter as SliceIter, IterMut as SliceIterMut}; + /// Base for array iterators /// /// Iterator element type is `&'a A`. @@ -254,6 +258,100 @@ clone_bounds!( } ); +impl<'a, A, D> PrivateNew> for Iter<'a, A, D> + where D: Dimension +{ + fn new(self_: ArrayView<'a, A, D>) -> Self { + Iter { + inner: if let Some(slc) = self_.into_slice() { + ElementsRepr::Slice(slc.iter()) + } else { + ElementsRepr::Counted(self_.into_elements_base()) + }, + } + } +} + + + +impl<'a, A, D> PrivateNew> for IterMut<'a, A, D> + where D: Dimension +{ + fn new(self_: ArrayViewMut<'a, A, D>) -> Self { + IterMut { + inner: + match self_.into_slice_() { + Ok(x) => ElementsRepr::Slice(x.into_iter()), + Err(self_) => ElementsRepr::Counted(self_.into_elements_base()), + } + } + } +} + +#[derive(Clone)] +pub enum ElementsRepr { + Slice(S), + Counted(C), +} + +/// An iterator over the elements of an array. +/// +/// Iterator element type is `&'a A`. +/// +/// See [`.iter()`](../struct.ArrayBase.html#method.iter) for more information. +pub struct Iter<'a, A: 'a, D> { + inner: ElementsRepr, ElementsBase<'a, A, D>>, +} + +/// Counted read only iterator +pub struct ElementsBase<'a, A: 'a, D> { + pub inner: Baseiter<'a, A, D>, +} + +/// An iterator over the elements of an array (mutable). +/// +/// Iterator element type is `&'a mut A`. +/// +/// See [`.iter_mut()`](../struct.ArrayBase.html#method.iter_mut) for more information. +pub struct IterMut<'a, A: 'a, D> { + inner: ElementsRepr, ElementsBaseMut<'a, A, D>>, +} + +/// An iterator over the elements of an array. +/// +/// Iterator element type is `&'a mut A`. +pub struct ElementsBaseMut<'a, A: 'a, D> { + pub inner: Baseiter<'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. +#[derive(Clone)] +pub struct IndexedIter<'a, 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. +pub struct IndexedIterMut<'a, A: 'a, D>(ElementsBaseMut<'a, A, D>); + +impl<'a, A, D> PrivateNew> for IndexedIter<'a, A, D> + where D: Dimension +{ + fn new(x: ElementsBase<'a, A, D>) -> Self { + IndexedIter(x) + } +} + +impl<'a, A, D> PrivateNew> for IndexedIterMut<'a, A, D> + where D: Dimension +{ + fn new(x: ElementsBaseMut<'a, A, D>) -> Self { + IndexedIterMut(x) + } +} + + impl<'a, A, D: Dimension> Iterator for Iter<'a, A, D> { type Item = &'a A; #[inline] @@ -417,14 +515,14 @@ impl<'a, A, D> ExactSizeIterator for IndexedIterMut<'a, A, D> /// An iterator that traverses over all dimensions but the innermost, /// and yields each inner row. /// -/// See [`.inners()`](struct.ArrayBase.html#method.inners) for more information. -pub struct InnerIter<'a, A: 'a, D> { +/// See [`.lanes()`](../struct.ArrayBase.html#method.lanes) for more information. +pub struct LaneIter<'a, A: 'a, D> { inner_len: Ix, inner_stride: Ixs, iter: Baseiter<'a, A, D>, } -impl<'a, A, D> Iterator for InnerIter<'a, A, D> +impl<'a, A, D> Iterator for LaneIter<'a, A, D> where D: Dimension { type Item = ArrayView<'a, A, Ix1>; @@ -440,7 +538,7 @@ impl<'a, A, D> Iterator for InnerIter<'a, A, D> } } -impl<'a, A, D> ExactSizeIterator for InnerIter<'a, A, D> +impl<'a, A, D> ExactSizeIterator for LaneIter<'a, A, D> where D: Dimension { fn len(&self) -> usize { @@ -448,21 +546,21 @@ impl<'a, A, D> ExactSizeIterator for InnerIter<'a, A, D> } } -// NOTE: InnerIterMut is a mutable iterator and must not expose aliasing +// NOTE: LaneIterMut 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). /// An iterator that traverses over all dimensions but the innermost, /// and yields each inner row (mutable). /// -/// See [`.inners_mut()`](struct.ArrayBase.html#method.inners_mut) +/// See [`.lanes_mut()`](../struct.ArrayBase.html#method.lanes_mut) /// for more information. -pub struct InnerIterMut<'a, A: 'a, D> { +pub struct LaneIterMut<'a, A: 'a, D> { inner_len: Ix, inner_stride: Ixs, iter: Baseiter<'a, A, D>, } -impl<'a, A, D> Iterator for InnerIterMut<'a, A, D> +impl<'a, A, D> Iterator for LaneIterMut<'a, A, D> where D: Dimension, { type Item = ArrayViewMut<'a, A, Ix1>; @@ -480,7 +578,7 @@ impl<'a, A, D> Iterator for InnerIterMut<'a, A, D> } } -impl<'a, A, D> ExactSizeIterator for InnerIterMut<'a, A, D> +impl<'a, A, D> ExactSizeIterator for LaneIterMut<'a, A, D> where D: Dimension, { fn len(&self) -> usize { @@ -584,8 +682,8 @@ impl DoubleEndedIterator for OuterIterCore /// /// 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()`](../struct.ArrayBase.html#method.outer_iter) +/// or [`.axis_iter()`](../struct.ArrayBase.html#method.axis_iter) /// for more information. #[derive(Debug)] pub struct AxisIter<'a, A: 'a, D> { @@ -721,8 +819,8 @@ pub fn new_axis_iter(v: ArrayView, axis: usize) /// /// 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()`](../struct.ArrayBase.html#method.outer_iter_mut) +/// or [`.axis_iter_mut()`](../struct.ArrayBase.html#method.axis_iter_mut) /// for more information. pub struct AxisIterMut<'a, A: 'a, D> { iter: OuterIterCore, @@ -788,12 +886,12 @@ pub fn new_axis_iter_mut(v: ArrayViewMut, axis: usize) } } -impl<'a, A, D> NdProducer for AxisIter<'a, A, D> - where D: Dimension +impl<'a, A, D: Dimension> NdProducer for AxisIter<'a, A, D> { type Item = ::Item; type Dim = Ix1; - type Elem = A; + type Ptr = *mut A; + type Stride = isize; #[doc(hidden)] fn layout(&self) -> ::Layout { @@ -804,7 +902,7 @@ impl<'a, A, D> NdProducer for AxisIter<'a, A, D> Ix1(self.len()) } #[doc(hidden)] - fn as_ptr(&self) -> *mut Self::Elem { + fn as_ptr(&self) -> Self::Ptr { self.iter.ptr } @@ -813,13 +911,13 @@ impl<'a, A, D> NdProducer for AxisIter<'a, A, D> } #[doc(hidden)] - unsafe fn as_ref(&self, ptr: *mut Self::Elem) -> Self::Item { + unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { ArrayView::new_(ptr, self.iter.inner_dim.clone(), self.iter.inner_strides.clone()) } #[doc(hidden)] - unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut Self::Elem { + unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { self.iter.ptr.offset(self.iter.stride * i[0] as isize) } @@ -835,12 +933,12 @@ impl<'a, A, D> NdProducer for AxisIter<'a, A, D> private_impl!{} } -impl<'a, A, D> NdProducer for AxisIterMut<'a, A, D> - where D: Dimension +impl<'a, A, D: Dimension> NdProducer for AxisIterMut<'a, A, D> { type Item = ::Item; type Dim = Ix1; - type Elem = A; + type Ptr = *mut A; + type Stride = isize; #[doc(hidden)] fn layout(&self) -> ::Layout { @@ -851,7 +949,7 @@ impl<'a, A, D> NdProducer for AxisIterMut<'a, A, D> Ix1(self.len()) } #[doc(hidden)] - fn as_ptr(&self) -> *mut Self::Elem { + fn as_ptr(&self) -> Self::Ptr { self.iter.ptr } @@ -860,13 +958,13 @@ impl<'a, A, D> NdProducer for AxisIterMut<'a, A, D> } #[doc(hidden)] - unsafe fn as_ref(&self, ptr: *mut Self::Elem) -> Self::Item { + unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { ArrayViewMut::new_(ptr, self.iter.inner_dim.clone(), self.iter.inner_strides.clone()) } #[doc(hidden)] - unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut Self::Elem { + unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { self.iter.ptr.offset(self.iter.stride * i[0] as isize) } @@ -891,7 +989,7 @@ impl<'a, A, D> 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()`](../struct.ArrayBase.html#method.axis_chunks_iter) for more information. pub struct AxisChunksIter<'a, A: 'a, D> { iter: OuterIterCore, last_ptr: *mut A, @@ -1027,7 +1125,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()`](../struct.ArrayBase.html#method.axis_chunks_iter_mut) /// for more information. pub struct AxisChunksIterMut<'a, A: 'a, D> { iter: OuterIterCore, @@ -1056,14 +1154,14 @@ chunk_iter_impl!(AxisChunksIterMut, ArrayViewMut); send_sync_read_only!(Iter); send_sync_read_only!(IndexedIter); -send_sync_read_only!(InnerIter); +send_sync_read_only!(LaneIter); send_sync_read_only!(AxisIter); send_sync_read_only!(AxisChunksIter); send_sync_read_only!(ElementsBase); send_sync_read_write!(IterMut); send_sync_read_write!(IndexedIterMut); -send_sync_read_write!(InnerIterMut); +send_sync_read_write!(LaneIterMut); send_sync_read_write!(AxisIterMut); send_sync_read_write!(AxisChunksIterMut); send_sync_read_write!(ElementsBaseMut); @@ -1072,18 +1170,17 @@ send_sync_read_write!(ElementsBaseMut); /// to deliver exactly as many items as it said it would. pub unsafe trait TrustedIterator { } -use std::slice; -use std::iter; +use std; use linspace::Linspace; -use indexes::Indices; +use iter::IndicesIter; unsafe impl TrustedIterator for Linspace { } unsafe impl<'a, A, D> TrustedIterator for Iter<'a, A, D> { } -unsafe impl TrustedIterator for iter::Map +unsafe impl TrustedIterator for std::iter::Map where I: TrustedIterator { } unsafe impl<'a, A> TrustedIterator for slice::Iter<'a, A> { } unsafe impl TrustedIterator for ::std::ops::Range { } -unsafe impl TrustedIterator for Indices where D: Dimension { } +unsafe impl TrustedIterator for IndicesIter where D: Dimension { } /// Like Iterator::collect, but only for trusted length iterators diff --git a/src/lib.rs b/src/lib.rs index 5b9f97453..a14b1449c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,33 +89,18 @@ pub use dimension::{ IntoDimension, RemoveAxis, Axis, - Axes, AxisDescription, }; pub use dimension::dim::*; pub use dimension::NdIndex; pub use dimension::IxDynImpl; -pub use indexes::Indices; pub use indexes::{indices, indices_of}; pub use error::{ShapeError, ErrorKind}; pub use si::{Si, S}; use iterators::Baseiter; -pub use iterators::{ - Inners, - InnersMut, - InnerIter, - InnerIterMut, - AxisIter, - AxisIterMut, - AxisChunksIter, - AxisChunksIterMut, - WholeChunks, - WholeChunksIter, - WholeChunksMut, - WholeChunksIterMut, -}; +use iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut}; pub use arraytraits::AsArray; pub use linalg_traits::{LinalgScalar, NdFloat}; @@ -148,6 +133,7 @@ mod dimension; mod free_functions; pub use free_functions::*; +pub use iterators::iter; mod layout; mod indexes; @@ -218,6 +204,7 @@ pub type Ixs = isize; /// + [RcArray](#rcarray) /// + [Array Views](#array-views) /// + [Indexing and Dimension](#indexing-and-dimension) +/// + [Loops, Producers and Iterators](#loops-producers-and-iterators) /// + [Slicing](#slicing) /// + [Subviews](#subviews) /// + [Arithmetic Operations](#arithmetic-operations) @@ -330,6 +317,90 @@ pub type Ixs = isize; /// (the rightmost index is varying the fastest). /// The iterators `.iter(), .iter_mut()` always adhere to this order, for example. /// +/// ## Loops, Producers and Iterators +/// +/// Using [`Zip`](struct.Zip.html) 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 +/// multidimensional data. All producers have dimensions and axes, like an +/// array view, and they can be split and used with parallelization using `Zip`. +/// +/// For example, `ArrayView` is a producer, it has the same dimensions +/// as the array view and for each iteration it produces a reference to +/// the array element (`&A` in this case). +/// +/// Another example, if we have a 10 × 10 array and use `.exact_chunks((2, 2))` +/// we get a producer of chunks which has the dimensions 5 × 5 (because +/// there are *10 / 2 = 5* chunks in either direction). The 5 × 5 chunks producer +/// can be paired with any other producers of the same dimension with `Zip`, for +/// example 5 × 5 arrays. +/// +/// ### `.iter()` and `.iter_mut()` +/// +/// These are the element iterators of arrays and they produce an element +/// sequence in the logical order of the array, that means that the elements +/// will be visited in the sequence that corresponds to increasing the +/// last index first: *0, ..., 0, 0*; *0, ..., 0, 1*; *0, ...0, 2* and so on. +/// +/// ### `.outer_iter()` and `.axis_iter()` +/// +/// These iterators produce array views of one smaller dimension. +/// +/// For example, for a 2D array, `.outer_iter()` will produce the 1D rows. +/// For a 3D array, `.outer_iter()` produces 2D subviews. +/// +/// `.axis_iter()` is like `outer_iter()` but allows you to pick which +/// axis to traverse. +/// +/// The `outer_iter` and `axis_iter` are one dimensional producers. +/// +/// ## `.genrows()`, `.gencolumns()` and `.lanes()` +/// +/// [`.genrows()`][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() { +/// row.fill(1.); +/// } +/// +/// // 2. Use Zip to pair each row in 2D `a` with elements in 1D `b` +/// use ndarray::Zip; +/// let mut b = Array::zeros(a.rows()); +/// +/// Zip::from(a.genrows()) +/// .and(&mut b) +/// .apply(|a_row, b_elt| { +/// *b_elt = a_row[a.cols() - 1] - a_row[0]; +/// }); +/// ``` +/// +/// The *lanes* of an array are 1D segments along an axis and when pointed +/// along the last axis they are *rows*, when pointed along the first axis +/// they are *columns*. +/// +/// A *m* × *n* array has *m* rows each of length *n* and conversely +/// *n* columns each of length *m*. +/// +/// To generalize this, we say that an array of dimension *a* × *m* × *n* +/// 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], +/// [`.lanes(axis)`][l], [`.lanes_mut(axis)`][lm]. +/// +/// [gr]: #method.genrows +/// [grm]: #method.genrows_mut +/// [gc]: #method.gencolumns +/// [gcm]: #method.gencolumns_mut +/// [l]: #method.lanes +/// [lm]: #method.lanes_mut +/// /// ## Slicing /// /// You can use slicing to create a view of a subset of the data in @@ -746,19 +817,13 @@ impl<'a, A, D> ArrayBase, D> } fn into_iter_(self) -> Iter<'a, A, D> { - Iter { - inner: if let Some(slc) = self.into_slice() { - ElementsRepr::Slice(slc.iter()) - } else { - ElementsRepr::Counted(self.into_elements_base()) - }, - } + Iter::new(self) } /// Return an outer iterator for this view. #[doc(hidden)] // not official #[deprecated(note="This method will be replaced.")] - pub fn into_outer_iter(self) -> AxisIter<'a, A, D::Smaller> + pub fn into_outer_iter(self) -> iter::AxisIter<'a, A, D::Smaller> where D: RemoveAxis, { iterators::new_outer_iter(self) @@ -812,66 +877,23 @@ impl<'a, A, D> ArrayBase, D> } fn into_iter_(self) -> IterMut<'a, A, D> { - IterMut { - inner: - match self.into_slice_() { - Ok(x) => ElementsRepr::Slice(x.into_iter()), - Err(self_) => ElementsRepr::Counted(self_.into_elements_base()), - } - } + IterMut::new(self) } /// Return an outer iterator for this view. #[doc(hidden)] // not official #[deprecated(note="This method will be replaced.")] - pub fn into_outer_iter(self) -> AxisIterMut<'a, A, D::Smaller> + pub fn into_outer_iter(self) -> iter::AxisIterMut<'a, A, D::Smaller> where D: RemoveAxis, { iterators::new_outer_iter_mut(self) } } - -/// An iterator over the elements of an array. -/// -/// Iterator element type is `&'a A`. -/// -/// See [`.iter()`](struct.ArrayBase.html#method.iter) for more information. -pub struct Iter<'a, A: 'a, D> { - inner: ElementsRepr, ElementsBase<'a, A, D>>, +trait PrivateNew { + fn new(x: T) -> Self; } -/// Counted read only iterator -struct ElementsBase<'a, A: 'a, D> { - inner: Baseiter<'a, A, D>, -} - -/// An iterator over the elements of an array (mutable). -/// -/// Iterator element type is `&'a mut A`. -/// -/// See [`.iter_mut()`](struct.ArrayBase.html#method.iter_mut) for more information. -pub struct IterMut<'a, A: 'a, D> { - inner: ElementsRepr, ElementsBaseMut<'a, A, D>>, -} - -/// An iterator over the elements of an array. -/// -/// Iterator element type is `&'a mut A`. -struct ElementsBaseMut<'a, A: 'a, D> { - inner: Baseiter<'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. -#[derive(Clone)] -pub struct IndexedIter<'a, 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. -pub struct IndexedIterMut<'a, A: 'a, D>(ElementsBaseMut<'a, A, D>); - fn zipsl<'a, 'b, A, B>(t: &'a [A], u: &'b [B]) -> ZipIter, SliceIter<'b, B>> { t.iter().zip(u) @@ -894,12 +916,6 @@ trait ZipExt : Iterator { impl ZipExt for I where I: Iterator { } -#[derive(Clone)] -enum ElementsRepr { - Slice(S), - Counted(C), -} - /// A contiguous array shape of n dimensions. /// diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 4f23ab0a4..3892991f7 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -126,8 +126,7 @@ impl ArrayBase S2: Data, E: Dimension, { - let result = - Zip::from(self) + !Zip::from(self) .and(rhs.broadcast_unwrap(self.raw_dim())) .fold_while((), |_, x, y| { if (*x - *y).abs() <= tol { @@ -135,11 +134,7 @@ impl ArrayBase } else { FoldWhile::Done(()) } - }); - match result { - FoldWhile::Continue(_) => true, - FoldWhile::Done(_) => false, - } + }).is_done() } } diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 0aedfd0fb..91f666afd 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -15,6 +15,7 @@ use Layout; use layout::{CORDER, FORDER}; use layout::LayoutPriv; +use indexes::{Indices, indices}; /// Return if the expression is a break value. macro_rules! fold_while { @@ -96,7 +97,7 @@ impl<'a, A, D, E> Broadcast for ArrayView<'a, A, D> private_impl!{} } -trait Splittable : Sized { +pub trait Splittable : Sized { fn split_at(self, Axis, Ix) -> (Self, Self); } @@ -138,14 +139,38 @@ impl

IntoNdProducer for P where P: NdProducer { /// that yields chunks. /// /// Producers are used as a arguments to `Zip` and `azip!()`. +/// +/// # 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 - #[doc(hidden)] - type Elem; /// 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)] @@ -155,23 +180,35 @@ pub trait NdProducer { self.raw_dim() == *dim } #[doc(hidden)] - fn as_ptr(&self) -> *mut Self::Elem; + fn as_ptr(&self) -> Self::Ptr; #[doc(hidden)] - unsafe fn as_ref(&self, *mut Self::Elem) -> Self::Item; + unsafe fn as_ref(&self, Self::Ptr) -> Self::Item; #[doc(hidden)] - unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut Self::Elem; + unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr; #[doc(hidden)] - fn stride_of(&self, axis: Axis) -> isize; + fn stride_of(&self, axis: Axis) -> ::Stride; #[doc(hidden)] #[inline(always)] - fn contiguous_stride(&self) -> isize { - 1 - } + 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 *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; @@ -253,12 +290,12 @@ impl<'a, A: 'a> IntoNdProducer for &'a mut Vec { } } -impl<'a, A, D> NdProducer for ArrayView<'a, A, D> - where D: Dimension, +impl<'a, A, D: Dimension> NdProducer for ArrayView<'a, A, D> { type Item = &'a A; type Dim = D; - type Elem = A; + type Ptr = *mut A; + type Stride = isize; private_impl!{} #[doc(hidden)] @@ -295,6 +332,9 @@ impl<'a, A, D> NdProducer for ArrayView<'a, A, D> fn stride_of(&self, axis: Axis) -> isize { self.strides()[axis.index()] } + + #[inline(always)] + fn contiguous_stride(&self) -> Self::Stride { 1 } #[doc(hidden)] fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { @@ -302,12 +342,11 @@ impl<'a, A, D> NdProducer for ArrayView<'a, A, D> } } -impl<'a, A, D> NdProducer for ArrayViewMut<'a, A, D> - where D: Dimension, -{ +impl<'a, A, D: Dimension> NdProducer for ArrayViewMut<'a, A, D> { type Item = &'a mut A; type Dim = D; - type Elem = A; + type Ptr = *mut A; + type Stride = isize; private_impl!{} #[doc(hidden)] @@ -345,6 +384,9 @@ impl<'a, A, D> NdProducer for ArrayViewMut<'a, A, D> self.strides()[axis.index()] } + #[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) @@ -352,7 +394,6 @@ impl<'a, A, D> NdProducer for ArrayViewMut<'a, A, D> } - /// Lock step function application across several arrays or other producers. /// /// Zip allows matching several arrays to each other elementwise and applying @@ -396,8 +437,8 @@ impl<'a, A, D> NdProducer for ArrayViewMut<'a, A, D> /// .and(&c) /// .and(&d) /// .apply(|w, &x, &y, &z| { -/// *w += x + y * z; -/// }); +/// *w += x + y * z; +/// }); /// ``` #[derive(Debug, Clone)] pub struct Zip { @@ -426,6 +467,24 @@ impl Zip<(P, ), D> } } } +impl Zip<(Indices, P), D> + where D: Dimension + Copy, + P: NdProducer, +{ + /// Create a new `Zip` with an index producer and the producer `p`. + /// + /// The Zip will take the exact dimension of `p` and all inputs + /// must have the same dimensions (or be broadcast to them). + /// + /// *Note:* Indexed zip has overhead. + pub fn indexed(p: IP) -> Self + where IP: IntoNdProducer + { + let array = p.into_producer(); + let dim = array.raw_dim(); + Zip::from(indices(dim)).and(array) + } +} impl Zip where D: Dimension, @@ -456,11 +515,11 @@ impl Zip /// others. fn max_stride_axis(&self) -> Axis { let i = match self.layout.flag() { - CORDER => self.dimension.slice().iter() - .position(|&len| len > 1).unwrap_or(0), FORDER => self.dimension.slice().iter() .rposition(|&len| len > 1).unwrap_or(self.dimension.ndim() - 1), - _ => 0, + /* corder or default */ + _ => self.dimension.slice().iter() + .position(|&len| len > 1).unwrap_or(0), }; Axis(i) } @@ -490,7 +549,7 @@ impl Zip let inner_strides = self.parts.contiguous_stride(); for i in 0..size { unsafe { - let ptr_i = ptrs.stride_offset(i, inner_strides); + let ptr_i = ptrs.stride_offset(inner_strides, i); acc = fold_while![function(acc, self.parts.as_ref(ptr_i))]; } } @@ -515,7 +574,7 @@ impl Zip unsafe { let ptr = self.parts.uget_ptr(&index); for i in 0..inner_len { - let p = ptr.stride_offset(i, inner_strides); + let p = ptr.stride_offset(inner_strides, i); acc = fold_while!(function(acc, self.parts.as_ref(p))); } } @@ -527,6 +586,7 @@ impl Zip } } +/* trait Offset : Copy { unsafe fn offset(self, off: isize) -> Self; unsafe fn stride_offset(self, index: usize, stride: isize) -> Self { @@ -539,21 +599,17 @@ impl Offset for *mut T { self.offset(off) } } +*/ trait OffsetTuple { type Args; - unsafe fn offset(self, off: isize) -> Self; - unsafe fn stride_offset(self, index: usize, stride: Self::Args) -> Self; + unsafe fn stride_offset(self, stride: Self::Args, index: usize) -> Self; } impl OffsetTuple for *mut T { type Args = isize; - unsafe fn offset(self, off: isize) -> Self { - self.offset(off) - } - - unsafe fn stride_offset(self, index: usize, stride: isize) -> Self { + unsafe fn stride_offset(self, stride: Self::Args, index: usize) -> Self { self.offset(index as isize * stride) } } @@ -568,16 +624,11 @@ macro_rules! offset_impl { $( #[allow(non_snake_case)] impl<$($param: Offset),*> OffsetTuple for ($($param, )*) { - type Args = ($(sub!($param [isize]),)*); - unsafe fn offset(self, off: isize) -> Self { - let ($($param, )*) = self; - ($($param . offset(off),)*) - } - - unsafe fn stride_offset(self, index: usize, stride: Self::Args) -> Self { + type Args = ($($param::Stride,)*); + unsafe fn stride_offset(self, stride: Self::Args, index: usize) -> Self { let ($($param, )*) = self; let ($($q, )*) = stride; - ($(Offset::stride_offset($param, index, $q),)*) + ($(Offset::stride_offset($param, $q, index),)*) } } )+ @@ -599,9 +650,9 @@ macro_rules! zipt_impl { #[allow(non_snake_case)] impl),*> ZippableTuple for ($($p, )*) { type Item = ($($p::Item, )*); - type Ptr = ($(*mut $p::Elem, )*); + type Ptr = ($($p::Ptr, )*); type Dim = Dim; - type Stride = ($(sub!($p [isize]),)* ); + type Stride = ($($p::Stride,)* ); fn stride_of(&self, index: usize) -> Self::Stride { let ($(ref $p,)*) = *self; @@ -727,6 +778,8 @@ macro_rules! map_impl { /// /// It will be split in the way that best preserves element locality. 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"); // Always split in a way that preserves layout (if any) let axis = self.max_stride_axis(); let index = self.len_of(axis) / 2; @@ -772,4 +825,12 @@ impl FoldWhile { FoldWhile::Continue(x) | FoldWhile::Done(x) => x } } + + /// Return true if it is `Done`, false if `Continue + pub fn is_done(&self) -> bool { + match *self { + FoldWhile::Continue(_) => false, + FoldWhile::Done(_) => true, + } + } } diff --git a/tests/array.rs b/tests/array.rs index ec614238b..5b850916c 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -11,7 +11,7 @@ use ndarray::{ arr3, }; use ndarray::indices; -use itertools::free::enumerate; +use itertools::{enumerate, zip}; #[test] fn test_matmul_rcarray() @@ -96,13 +96,13 @@ fn test_index() *elt = i; } - for ((i, j), a) in indices((2, 3)).zip(A.iter()) { + for ((i, j), a) in zip(indices((2, 3)), &A) { assert_eq!(*a, A[[i, j]]); } let vi = A.slice(&[Si(1, None, 1), Si(0, None, 2)]); let mut it = vi.iter(); - for ((i, j), x) in indices((1, 2)).zip(it.by_ref()) { + for ((i, j), x) in zip(indices((1, 2)), &mut it) { assert_eq!(*x, vi[[i, j]]); } assert!(it.next().is_none()); diff --git a/tests/azip.rs b/tests/azip.rs index 4586f1a2d..6e5cd300f 100644 --- a/tests/azip.rs +++ b/tests/azip.rs @@ -86,7 +86,7 @@ fn test_broadcast() { let e = Array::from_elem((), 2.); { - let mut z = Zip::from(a.view_mut()) + let z = Zip::from(a.view_mut()) .and_broadcast(&b) .and_broadcast(&d) .and_broadcast(&e); @@ -137,7 +137,7 @@ fn test_contiguous_but_not_c_or_f() { fn test_clone() { let a = Array::from_iter(0..27).into_shape((3, 3, 3)).unwrap(); - let z = Zip::from(&a).and(a.whole_chunks((1, 1, 1))); + 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| { @@ -149,3 +149,128 @@ fn test_clone() { i += 1; }); } + +#[test] +fn test_indices_1() { + let mut a1 = Array::default(12); + for (i, elt) in a1.indexed_iter_mut() { + *elt = i; + } + + let mut count = 0; + Zip::indexed(&a1) + .apply(|i, elt| { + count += 1; + assert_eq!(*elt, i); + }); + assert_eq!(count, a1.len()); + + let mut count = 0; + let len = a1.len(); + let (x, y) = Zip::indexed(&mut a1).split(); + + x.apply(|i, elt| { + count += 1; + assert_eq!(*elt, i); + }); + assert_eq!(count, len / 2); + y.apply(|i, elt| { + count += 1; + assert_eq!(*elt, i); + }); + assert_eq!(count, len); +} + +#[test] +fn test_indices_2() { + let mut a1 = Array::default((10, 12)); + for (i, elt) in a1.indexed_iter_mut() { + *elt = i; + } + + let mut count = 0; + Zip::indexed(&a1) + .apply(|i, elt| { + count += 1; + assert_eq!(*elt, i); + }); + assert_eq!(count, a1.len()); + + let mut count = 0; + let len = a1.len(); + let (x, y) = Zip::indexed(&mut a1).split(); + + x.apply(|i, elt| { + count += 1; + assert_eq!(*elt, i); + }); + assert_eq!(count, len / 2); + y.apply(|i, elt| { + count += 1; + assert_eq!(*elt, i); + }); + assert_eq!(count, len); +} + +#[test] +fn test_indices_3() { + let mut a1 = Array::default((4, 5, 6)); + for (i, elt) in a1.indexed_iter_mut() { + *elt = i; + } + + let mut count = 0; + Zip::indexed(&a1) + .apply(|i, elt| { + count += 1; + assert_eq!(*elt, i); + }); + assert_eq!(count, a1.len()); + + let mut count = 0; + let len = a1.len(); + let (x, y) = Zip::indexed(&mut a1).split(); + + x.apply(|i, elt| { + count += 1; + assert_eq!(*elt, i); + }); + assert_eq!(count, len / 2); + y.apply(|i, elt| { + count += 1; + assert_eq!(*elt, i); + }); + assert_eq!(count, len); +} + +#[test] +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)); + if a1.len() <= 1 { + continue; + } + let (a, b) = Zip::indexed(&a1).split(); + let mut seen = Vec::new(); + + let mut ac = 0; + a.apply(|i, _| { + ac += 1; + seen.push(i); + }); + let mut bc = 0; + b.apply(|i, _| { + bc += 1; + seen.push(i); + }); + + assert_eq!(a1.len(), ac + bc); + + seen.sort(); + assert_eq!(seen.len(), a1.len()); + seen.dedup(); + assert_eq!(seen.len(), a1.len()); + } + } +} diff --git a/tests/dimension.rs b/tests/dimension.rs index ea2c527ea..ec1f312a1 100644 --- a/tests/dimension.rs +++ b/tests/dimension.rs @@ -213,10 +213,11 @@ macro_rules! ndindex { } let dim = a.shape().to_vec(); let b = a.broadcast(dim).unwrap(); - for (i, &elt) in a.indexed_iter() { + for (i, &elt) in b.indexed_iter() { let dim = i.into_dimension(); - assert_eq!(elt, b[dim]); assert_eq!(elt, b[dim.slice()]); + assert_eq!(elt, b[&dim]); + assert_eq!(elt, b[dim]); } } } diff --git a/tests/indices.rs b/tests/indices.rs new file mode 100644 index 000000000..9e47075a4 --- /dev/null +++ b/tests/indices.rs @@ -0,0 +1,25 @@ +extern crate ndarray; + +use ndarray::prelude::*; +use ndarray::indices_of; + +#[test] +fn test_ixdyn_index_iterate() { + for &rev in &[false, true] { + let mut a = Array::zeros((2, 3, 4).set_f(rev)); + 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(); + println!("{:?}", a.dim()); + let mut c = 0; + for i in indices_of(&a) { + let ans = i[0] + 10 * i[1] + 100 * i[2]; + println!("{:?}", i); + assert_eq!(a[i], ans); + c += 1; + } + assert_eq!(c, a.len()); + } +} diff --git a/tests/iterator_chunks.rs b/tests/iterator_chunks.rs index 286d643d5..65202fa42 100644 --- a/tests/iterator_chunks.rs +++ b/tests/iterator_chunks.rs @@ -12,7 +12,7 @@ fn chunks() { let (m, n) = a.dim(); for i in 1..m + 1 { for j in 1..n + 1 { - let c = a.whole_chunks((i, j)); + let c = a.exact_chunks((i, j)); let ly = n / j; for (index, elt) in c.into_iter().enumerate() { @@ -22,16 +22,16 @@ fn chunks() { let cy = (cindex.1 * j) as isize; assert_eq!(elt, a.slice(s![cx.., cy..]).slice(s![..i as isize, ..j as isize])); } - let c = a.whole_chunks((i, j)); + let c = a.exact_chunks((i, j)); assert_eq!(c.into_iter().count(), (m / i) * (n / j)); - let c = a.whole_chunks((i, j)); + let c = a.exact_chunks((i, j)); let (c1, c2) = c.split_at(Axis(0), (m / i) / 2); assert_eq!(c1.into_iter().count(), ((m / i) / 2) * (n / j)); assert_eq!(c2.into_iter().count(), (m / i - (m / i) / 2) * (n / j)); } } - let c = a.whole_chunks((m + 1, n)); + let c = a.exact_chunks((m + 1, n)); assert_eq!(c.raw_dim().size(), 0); assert_eq!(c.into_iter().count(), 0); } @@ -40,7 +40,7 @@ fn chunks() { #[test] fn chunks_different_size_1() { let a = Array::::zeros(vec![2, 3]); - a.whole_chunks(vec![2]); + a.exact_chunks(vec![2]); } #[test] @@ -48,7 +48,7 @@ fn chunks_ok_size() { let mut a = Array::::zeros(vec![2, 3]); a.fill(1.); let mut c = 0; - for elt in a.whole_chunks(vec![2, 1]) { + for elt in a.exact_chunks(vec![2, 1]) { assert!(elt.iter().all(|&x| x == 1.)); assert_eq!(elt.shape(), &[2, 1]); c += 1; @@ -60,13 +60,13 @@ fn chunks_ok_size() { #[test] fn chunks_different_size_2() { let a = Array::::zeros(vec![2, 3]); - a.whole_chunks(vec![2, 3, 4]); + a.exact_chunks(vec![2, 3, 4]); } #[test] fn chunks_mut() { let mut a = Array::zeros((7, 8)); - for (i, mut chunk) in a.whole_chunks_mut((2, 3)).into_iter().enumerate() { + for (i, mut chunk) in a.exact_chunks_mut((2, 3)).into_iter().enumerate() { chunk.fill(i); } println!("{:?}", a); @@ -85,5 +85,5 @@ fn chunks_mut() { #[test] fn chunks_different_size_3() { let mut a = Array::::zeros(vec![2, 3]); - a.whole_chunks_mut(vec![2, 3, 4]); + a.exact_chunks_mut(vec![2, 3, 4]); } diff --git a/tests/iterators.rs b/tests/iterators.rs index 8333d07b2..a75eefbb8 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -480,10 +480,10 @@ fn iterators_are_send_sync() { _send_sync(&a.axis_chunks_iter(Axis(1), 1)); _send_sync(&a.axis_chunks_iter_mut(Axis(1), 1)); _send_sync(&indices(a.dim())); - _send_sync(&a.whole_chunks((1, 1, 1))); - _send_sync(&a.whole_chunks_mut((1, 1, 1))); - _send_sync(&a.whole_chunks((1, 1, 1)).into_iter()); - _send_sync(&a.whole_chunks_mut((1, 1, 1)).into_iter()); + _send_sync(&a.exact_chunks((1, 1, 1))); + _send_sync(&a.exact_chunks_mut((1, 1, 1))); + _send_sync(&a.exact_chunks((1, 1, 1)).into_iter()); + _send_sync(&a.exact_chunks_mut((1, 1, 1)).into_iter()); } #[test]