diff --git a/benches/bench1.rs b/benches/bench1.rs index bd0bb82f7..4f1b2d6bd 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -9,6 +9,8 @@ use ndarray::{ Array, Axis, Ix, + Array1, + Array2, }; use ndarray::{arr0, arr1, arr2}; @@ -291,8 +293,8 @@ fn add_2d_cutout(bench: &mut test::Bencher) #[bench] fn add_2d_broadcast_1_to_2(bench: &mut test::Bencher) { - let mut a = Array::::zeros((64, 64)); - let b = Array::::zeros(64); + let mut a = Array2::::zeros((64, 64)); + let b = Array1::::zeros(64); let bv = b.view(); bench.iter(|| { a += &bv; @@ -717,7 +719,7 @@ fn dot_extended(bench: &mut test::Bencher) { const MEAN_SUM_N: usize = 127; -fn range_mat(m: Ix, n: Ix) -> Array { +fn range_mat(m: Ix, n: Ix) -> Array2 { assert!(m * n != 0); Array::linspace(0., (m * n - 1) as f32, m * n).into_shape((m, n)).unwrap() } diff --git a/benches/higher-order.rs b/benches/higher-order.rs index a3f3c9098..7df304d11 100644 --- a/benches/higher-order.rs +++ b/benches/higher-order.rs @@ -7,6 +7,7 @@ use test::Bencher; #[macro_use(s)] extern crate ndarray; use ndarray::prelude::*; +use ndarray::ArrayViewMut2; const N: usize = 1024; const X: usize = 64; @@ -22,12 +23,38 @@ fn map_regular(bench: &mut Bencher) } +pub fn double_array(mut a: ArrayViewMut2) { + a *= 2.0; +} + #[bench] -fn map_stride(bench: &mut Bencher) +fn map_stride_double_f64(bench: &mut Bencher) { - let a = Array::linspace(0., 127., N * 2).into_shape((X, Y * 2)).unwrap(); + let mut a = Array::linspace(0., 127., N * 2).into_shape([X, Y * 2]).unwrap(); + let mut av = a.slice_mut(s![.., ..;2]); + bench.iter(|| { + double_array(av.view_mut()); + + }); +} + +#[bench] +fn map_stride_f64(bench: &mut Bencher) +{ + let a = Array::linspace(0., 127., N * 2).into_shape([X, Y * 2]).unwrap(); let av = a.slice(s![.., ..;2]); bench.iter(|| { av.map(|&x| 2. * x) }); } + +#[bench] +fn map_stride_u32(bench: &mut Bencher) +{ + let a = Array::linspace(0., 127., N * 2).into_shape([X, Y * 2]).unwrap(); + let b = a.mapv(|x| x as u32); + let av = b.slice(s![.., ..;2]); + bench.iter(|| { + av.map(|&x| 2 * x) + }); +} diff --git a/benches/iter.rs b/benches/iter.rs index 720ba117f..0f64f4437 100644 --- a/benches/iter.rs +++ b/benches/iter.rs @@ -6,6 +6,7 @@ use test::Bencher; #[macro_use(s)] extern crate ndarray; use ndarray::prelude::*; +use ndarray::Array2; #[bench] fn iter_sum_2d_regular(bench: &mut Bencher) @@ -47,3 +48,45 @@ fn iter_sum_2d_transpose(bench: &mut Bencher) a.iter().fold(0, |acc, &x| acc + x) }); } + +#[bench] +fn iter_filter_sum_2d_u32(bench: &mut Bencher) +{ + let a = Array::linspace(0., 1., 256).into_shape((16, 16)).unwrap(); + let b = a.mapv(|x| (x * 100.) as u32); + bench.iter(|| { + b.iter().filter(|&&x| x < 75).fold(0, |acc, &x| acc + x) + }); +} + +#[bench] +fn iter_filter_sum_2d_f32(bench: &mut Bencher) +{ + let a = Array::linspace(0., 1., 256).into_shape((16, 16)).unwrap(); + let b = a * 100.; + bench.iter(|| { + b.iter().filter(|&&x| x < 75.).fold(0., |acc, &x| acc + x) + }); +} + +#[bench] +fn iter_filter_sum_2d_stride_u32(bench: &mut Bencher) +{ + let a = Array::linspace(0., 1., 256).into_shape((16, 16)).unwrap(); + let b = a.mapv(|x| (x * 100.) as u32); + let b = b.slice(s![.., ..;2]); + bench.iter(|| { + b.iter().filter(|&&x| x < 75).fold(0, |acc, &x| acc + x) + }); +} + +#[bench] +fn iter_filter_sum_2d_stride_f32(bench: &mut Bencher) +{ + let a = Array::linspace(0., 1., 256).into_shape((16, 16)).unwrap(); + let b = a * 100.; + let b = b.slice(s![.., ..;2]); + bench.iter(|| { + b.iter().filter(|&&x| x < 75.).fold(0., |acc, &x| acc + x) + }); +} diff --git a/docgen/images/axis_iter.svg b/docgen/images/axis_iter.svg index ecb73209f..ea4157b0e 100644 --- a/docgen/images/axis_iter.svg +++ b/docgen/images/axis_iter.svg @@ -96,7 +96,7 @@ image/svg+xml - + @@ -737,11 +737,11 @@ sodipodi:role="line" x="602.84332" y="185.57912" - id="tspan8614-6">Input shape: (3, 5, 5)Input shape: (3, 4, 5)Output shapes: (3, 5) + id="tspan8640">Output shapes: (3, 4) 4 + diff --git a/docgen/images/split_at.svg b/docgen/images/split_at.svg index 4e68f1157..57c429546 100644 --- a/docgen/images/split_at.svg +++ b/docgen/images/split_at.svg @@ -125,7 +125,7 @@ + style="opacity:0.95;fill:#006eaf;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.62353659;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="opacity:0.95;fill:#006eaf;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.62353659;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="opacity:0.95;fill:#006eaf;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.62353659;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="opacity:0.95;fill:#006eaf;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.62353659;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="opacity:1;fill:#006eaf;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.62353659;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="opacity:1;fill:#006eaf;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.62353659;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="opacity:0.95;fill:#006eaf;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.62353659;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="opacity:0.95;fill:#006eaf;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.62353659;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="opacity:0.95;fill:#006eaf;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.62353659;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + transform="matrix(0.62353657,0,0,0.62353657,546.73664,101.89416)" + style="fill:#006eaf;fill-opacity:1"> + transform="matrix(0.46312941,-0.46312941,0,1.000596,-151.14269,-102.52316)" + style="fill:#006eaf;fill-opacity:1"> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#008aff;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + style="opacity:1;fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + height="100%" + style="fill:#006eaf;fill-opacity:1" /> + style="opacity:1;fill:#006eaf;fill-opacity:1" /> + style="opacity:1;fill:#006eaf;fill-opacity:1" /> + style="opacity:1;fill:#006eaf;fill-opacity:1" /> Output shapes: (3, 5, 2) and (3, 5, 3) = [[A; 3]; 3]; fn conv_3x3(a: &ArrayView2, out: &mut ArrayViewMut2, kernel: &Kernel3x3) where F: Float, { - let (n, m) = a.dim(); - let (np, mp) = out.dim(); + let (n, m) = a.dim_pattern(); + let (np, mp) = out.dim_pattern(); if n < 3 || m < 3 { return; } diff --git a/examples/life.rs b/examples/life.rs index 7d9a3d503..292caad30 100644 --- a/examples/life.rs +++ b/examples/life.rs @@ -58,7 +58,8 @@ fn iterate(z: &mut Board, scratch: &mut Board) { } fn turn_on_corners(z: &mut Board) { - let (n, m) = z.dim(); + let n = z.rows(); + let m = z.cols(); z[[1 , 1 ]] = 1; z[[1 , m - 2]] = 1; z[[n - 2, 1 ]] = 1; diff --git a/examples/linalg.rs b/examples/linalg.rs index 358f537a4..3d602dd80 100644 --- a/examples/linalg.rs +++ b/examples/linalg.rs @@ -12,14 +12,14 @@ use num_traits::Float; use num_complex::Complex; use std::ops::{Add, Sub, Mul, Div}; -use ndarray::{RcArray, Ix}; +use ndarray::{RcArray, Ix1, Ix2}; use ndarray::{rcarr1, rcarr2}; use ndarray::LinalgScalar; /// Column vector. -pub type Col = RcArray; +pub type Col = RcArray; /// Rectangular matrix. -pub type Mat = RcArray; +pub type Mat = RcArray; /// Trait union for a ring with 1. pub trait Ring : Clone + Zero + Add + Sub @@ -178,7 +178,7 @@ pub fn least_squares(a: &Mat, b: &Col) -> Col if ::is_complex() { // conjugate transpose // only elements below the diagonal have imag part - let (m, _) = L.dim(); + let (m, _) = L.dim_pattern(); for i in 1..m { for j in 0..i { let elt = &mut L[[i, j]]; @@ -210,7 +210,7 @@ pub fn least_squares(a: &Mat, b: &Col) -> Col pub fn cholesky(a: Mat) -> Mat { let z = A::zero(); - let (m, n) = a.dim(); + let (m, n) = a.dim_pattern(); assert!(m == n); // Perform the operation in-place on `a` let mut L = a; @@ -259,9 +259,9 @@ pub fn cholesky(a: Mat) -> Mat /// 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(); + let (m, n) = l.dim_pattern(); assert!(m == n); - assert!(m == b.dim()); + 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 @@ -277,9 +277,9 @@ pub fn subst_fw(l: &Mat, b: &Col) -> Col /// 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(); + let (m, n) = u.dim_pattern(); assert!(m == n); - assert!(m == b.dim()); + 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 diff --git a/ndarray-rand/src/lib.rs b/ndarray-rand/src/lib.rs index 11cccbf76..eee86d7d8 100644 --- a/ndarray-rand/src/lib.rs +++ b/ndarray-rand/src/lib.rs @@ -21,6 +21,7 @@ use ndarray::{ Dimension, DataOwned, }; +use ndarray::ShapeBuilder; /// Constructors for n-dimensional arrays with random elements. /// @@ -53,34 +54,39 @@ pub trait RandomExt /// // [[ 8.6900, 6.9824, 3.8922, 6.5861, 2.4890], /// // [ 0.0914, 5.5186, 5.8135, 5.2361, 3.1879]] /// # } - fn random(dim: D, distribution: IdS) -> ArrayBase - where IdS: IndependentSample; + fn random(shape: Sh, distribution: IdS) -> ArrayBase + where IdS: IndependentSample, + Sh: ShapeBuilder; /// Create an array with shape `dim` with elements drawn from /// `distribution`, using a specific Rng `rng`. /// /// ***Panics*** if the number of elements overflows usize. - fn random_using(dim: D, distribution: IdS, rng: &mut R) -> ArrayBase + fn random_using(shape: Sh, distribution: IdS, rng: &mut R) -> ArrayBase where IdS: IndependentSample, - R: Rng; + R: Rng, + Sh: ShapeBuilder; } impl RandomExt for ArrayBase where S: DataOwned, D: Dimension, { - fn random(dim: D, dist: IdS) -> ArrayBase - where IdS: IndependentSample + fn random(shape: Sh, dist: IdS) -> ArrayBase + where IdS: IndependentSample, + Sh: ShapeBuilder, { - Self::random_using(dim, dist, &mut rand::weak_rng()) + Self::random_using(shape, dist, &mut rand::weak_rng()) } - fn random_using(dim: D, dist: IdS, rng: &mut R) -> ArrayBase + fn random_using(shape: Sh, dist: IdS, rng: &mut R) -> ArrayBase where IdS: IndependentSample, - R: Rng + R: Rng, + Sh: ShapeBuilder, { - let elements = Vec::from_iter((0..dim.size()).map(move |_| dist.ind_sample(rng))); - Self::from_shape_vec(dim, elements).unwrap() + let shape = shape.into_shape(); + let elements = Vec::from_iter((0..shape.size()).map(move |_| dist.ind_sample(rng))); + Self::from_shape_vec(shape, elements).unwrap() } } diff --git a/src/aliases.rs b/src/aliases.rs index aaec281c3..302c6f853 100644 --- a/src/aliases.rs +++ b/src/aliases.rs @@ -1,20 +1,59 @@ //! Type aliases for common array sizes //! -use ::{Ix, Array, ArrayView, ArrayViewMut}; +use ::{Ix, Array, ArrayView, ArrayViewMut, RcArray}; +use ::dimension::Dim; +use dimension::DimPrivate; + +/// Create a zero-dimensional index +#[allow(non_snake_case)] +#[inline(always)] +pub fn Ix0() -> Ix0 { Dim::new([]) } +/// Create a one-dimensional index +#[allow(non_snake_case)] +#[inline(always)] +pub fn Ix1(i0: Ix) -> Ix1 { Dim::new([i0]) } +/// Create a two-dimensional index +#[allow(non_snake_case)] +#[inline(always)] +pub fn Ix2(i0: Ix, i1: Ix) -> Ix2 { Dim::new([i0, i1]) } +/// Create a three-dimensional index +#[allow(non_snake_case)] +#[inline(always)] +pub fn Ix3(i0: Ix, i1: Ix, i2: Ix) -> Ix3 { Dim::new([i0, i1, i2]) } +/// Create a four-dimensional index +#[allow(non_snake_case)] +#[inline(always)] +pub fn Ix4(i0: Ix, i1: Ix, i2: Ix, i3: Ix) -> Ix4 { Dim::new([i0, i1, i2, i3]) } +/// Create a five-dimensional index +#[allow(non_snake_case)] +#[inline(always)] +pub fn Ix5(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix) -> Ix5 { + Dim::new([i0, i1, i2, i3, i4]) +} +/// Create a six-dimensional index +#[allow(non_snake_case)] +#[inline(always)] +pub fn Ix6(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix, i5: Ix) -> Ix6 { + Dim::new([i0, i1, i2, i3, i4, i5]) +} /// zero-dimensionial -pub type Ix0 = (); +pub type Ix0 = Dim<[Ix; 0]>; /// one-dimensional -pub type Ix1 = Ix; +pub type Ix1 = Dim<[Ix; 1]>; /// two-dimensional -pub type Ix2 = (Ix, Ix); +pub type Ix2 = Dim<[Ix; 2]>; /// three-dimensional -pub type Ix3 = (Ix, Ix, Ix); +pub type Ix3 = Dim<[Ix; 3]>; /// four-dimensional -pub type Ix4 = (Ix, Ix, Ix, Ix); +pub type Ix4 = Dim<[Ix; 4]>; +/// five-dimensional +pub type Ix5 = Dim<[Ix; 5]>; +/// six-dimensional +pub type Ix6 = Dim<[Ix; 6]>; /// dynamic-dimensional -pub type IxDyn = Vec; +pub type IxDyn = Dim>; /// zero-dimensional array pub type Array0 = Array; @@ -26,6 +65,10 @@ pub type Array2 = Array; pub type Array3 = Array; /// four-dimensional array pub type Array4 = Array; +/// five-dimensional array +pub type Array5 = Array; +/// six-dimensional array +pub type Array6 = Array; /// dynamic-dimensional array pub type ArrayD = Array; @@ -39,6 +82,10 @@ pub type ArrayView2<'a, A> = ArrayView<'a, A, Ix2>; pub type ArrayView3<'a, A> = ArrayView<'a, A, Ix3>; /// four-dimensional array view pub type ArrayView4<'a, A> = ArrayView<'a, A, Ix4>; +/// five-dimensional array view +pub type ArrayView5<'a, A> = ArrayView<'a, A, Ix5>; +/// six-dimensional array view +pub type ArrayView6<'a, A> = ArrayView<'a, A, Ix6>; /// dynamic-dimensional array view pub type ArrayViewD<'a, A> = ArrayView<'a, A, IxDyn>; @@ -52,5 +99,14 @@ pub type ArrayViewMut2<'a, A> = ArrayViewMut<'a, A, Ix2>; pub type ArrayViewMut3<'a, A> = ArrayViewMut<'a, A, Ix3>; /// four-dimensional read-write array view pub type ArrayViewMut4<'a, A> = ArrayViewMut<'a, A, Ix4>; +/// five-dimensional read-write array view +pub type ArrayViewMut5<'a, A> = ArrayViewMut<'a, A, Ix5>; +/// six-dimensional read-write array view +pub type ArrayViewMut6<'a, A> = ArrayViewMut<'a, A, Ix6>; /// dynamic-dimensional read-write array view pub type ArrayViewMutD<'a, A> = ArrayViewMut<'a, A, IxDyn>; + +/// one-dimensional shared ownership array +pub type RcArray1 = RcArray; +/// two-dimensional shared ownership array +pub type RcArray2 = RcArray; diff --git a/src/array_serde.rs b/src/array_serde.rs index 8aef08d6a..89adcd9d2 100644 --- a/src/array_serde.rs +++ b/src/array_serde.rs @@ -7,12 +7,36 @@ // except according to those terms. use serde::{self, Serialize, Deserialize}; +use std::marker::PhantomData; + use imp_prelude::*; use super::arraytraits::ARRAY_FORMAT_VERSION; use super::Elements; +use Dim; +use dimension::DimPrivate; -use std::marker::PhantomData; +/// **Requires crate feature `"serde"`** +impl Serialize for Dim + where I: Serialize, +{ + fn serialize(&self, serializer: &mut Se) -> Result<(), Se::Error> + where Se: serde::Serializer + { + self.ix().serialize(serializer) + } +} + +/// **Requires crate feature `"serde"`** +impl Deserialize for Dim + where I: Deserialize, +{ + fn deserialize(deserializer: &mut D) -> Result + where D: serde::de::Deserializer + { + I::deserialize(deserializer).map(Dim::new) + } +} /// **Requires crate feature `"serde"`** impl Serialize for ArrayBase diff --git a/src/array_serialize.rs b/src/array_serialize.rs index 600039347..5f0676ea4 100644 --- a/src/array_serialize.rs +++ b/src/array_serialize.rs @@ -11,6 +11,27 @@ use super::arraytraits::ARRAY_FORMAT_VERSION; use imp_prelude::*; +use Dim; +use dimension::DimPrivate; + +/// **Requires crate feature `"rustc-serialize"`** +impl Encodable for Dim + where I: Encodable, +{ + fn encode(&self, s: &mut E) -> Result<(), E::Error> { + self.ix().encode(s) + } +} + +/// **Requires crate feature `"rustc-serialize"`** +impl Decodable for Dim + where I: Decodable, +{ + fn decode(d: &mut E) -> Result { + I::decode(d).map(Dim::new) + } +} + /// **Requires crate feature `"rustc-serialize"`** impl Encodable for ArrayBase where A: Encodable, @@ -43,7 +64,7 @@ impl Decodable for ArrayBase D: Dimension + Decodable, S: DataOwned { - fn decode(d: &mut E) -> Result, E::Error> { + fn decode(d: &mut E) -> Result { d.read_struct("Array", 3, |d| { let version: u8 = try!(d.read_struct_field("v", 0, Decodable::decode)); if version > ARRAY_FORMAT_VERSION { diff --git a/src/arrayformat.rs b/src/arrayformat.rs index 53232d94c..3ce83ad05 100644 --- a/src/arrayformat.rs +++ b/src/arrayformat.rs @@ -11,6 +11,7 @@ use super::{ Data, Dimension, }; +use dimension::IntoDimension; fn format_array(view: &ArrayBase, f: &mut fmt::Formatter, mut format: F) @@ -37,6 +38,7 @@ fn format_array(view: &ArrayBase, f: &mut fmt::Formatter, // Simply use the indexed iterator, and take the index wraparounds // as cues for when to add []'s and how many to add. for (index, elt) in view.indexed_iter() { + let index = index.into_dimension(); let take_n = if ndim == 0 { 1 } else { ndim - 1 }; let mut update_index = false; for (i, (a, b)) in index.slice() diff --git a/src/arraytraits.rs b/src/arraytraits.rs index a8dcc2b45..7e4739c76 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -48,7 +48,7 @@ macro_rules! debug_bounds_check { #[inline(always)] pub fn debug_bounds_check(_a: &ArrayBase, _index: &I) where D: Dimension, - I: NdIndex, + I: NdIndex, S: Data, { debug_bounds_check!(_a, *_index); @@ -59,7 +59,7 @@ pub fn debug_bounds_check(_a: &ArrayBase, _index: &I) /// **Panics** if index is out of bounds. impl Index for ArrayBase where D: Dimension, - I: NdIndex, + I: NdIndex, S: Data, { type Output = S::Elem; @@ -75,7 +75,7 @@ impl Index for ArrayBase /// **Panics** if index is out of bounds. impl IndexMut for ArrayBase where D: Dimension, - I: NdIndex, + I: NdIndex, S: DataMut, { #[inline] @@ -221,7 +221,7 @@ impl<'a, A, Slice: ?Sized> From<&'a Slice> for ArrayBase, Ix1> fn from(slice: &'a Slice) -> Self { let xs = slice.as_ref(); unsafe { - Self::new_(xs.as_ptr(), xs.len(), 1) + Self::new_(xs.as_ptr(), Ix1(xs.len()), Ix1(1)) } } } @@ -247,7 +247,7 @@ impl<'a, A, Slice: ?Sized> From<&'a mut Slice> for ArrayBase fn from(slice: &'a mut Slice) -> Self { let xs = slice.as_mut(); unsafe { - Self::new_(xs.as_mut_ptr(), xs.len(), 1) + Self::new_(xs.as_mut_ptr(), Ix1(xs.len()), Ix1(1)) } } } diff --git a/src/dimension.rs b/src/dimension.rs deleted file mode 100644 index 179e9f874..000000000 --- a/src/dimension.rs +++ /dev/null @@ -1,873 +0,0 @@ -// Copyright 2014-2016 bluss and ndarray developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -use std::cmp::Ordering; -use std::fmt::Debug; -use std::slice; -use itertools::{enumerate, zip}; - -use super::{Si, Ix, Ixs}; -use super::{zipsl, zipsl_mut}; -use error::{from_kind, ErrorKind, ShapeError}; -use ZipExt; - -/// Calculate offset from `Ix` stride converting sign properly -#[inline] -pub fn stride_offset(n: Ix, stride: Ix) -> isize { - (n as isize) * ((stride as Ixs) as isize) -} - -/// Check whether the given `dim` and `stride` lead to overlapping indices -/// -/// There is overlap if, when iterating through the dimensions in the order -/// of maximum variation, the current stride is inferior to the sum of all -/// preceding strides multiplied by their corresponding dimensions. -/// -/// The current implementation assumes strides to be positive -pub fn dim_stride_overlap(dim: &D, strides: &D) -> bool { - let order = strides._fastest_varying_stride_order(); - - let dim = dim.slice(); - let strides = strides.slice(); - let mut prev_offset = 1; - for &index in order.slice() { - let d = dim[index]; - let s = strides[index]; - // any stride is ok if dimension is 1 - if d != 1 && (s as isize) < prev_offset { - return true; - } - prev_offset = stride_offset(d, s); - } - false -} - -/// Check whether the given dimension and strides are memory safe -/// to index the provided slice. -/// -/// To be safe, no stride may be negative, and the offset corresponding -/// to the last element of each dimension should be smaller than the length -/// of the slice. Also, the strides should not allow a same element to be -/// referenced by two different index. -pub fn can_index_slice(data: &[A], dim: &D, strides: &D) - -> Result<(), ShapeError> -{ - // check lengths of axes. - let len = match dim.size_checked() { - Some(l) => l, - None => return Err(from_kind(ErrorKind::OutOfBounds)), - }; - // check if strides are strictly positive (zero ok for len 0) - for &s in strides.slice() { - let s = s as Ixs; - if s < 1 && (len != 0 || s < 0) { - return Err(from_kind(ErrorKind::Unsupported)); - } - } - if len == 0 { - return Ok(()); - } - // check that the maximum index is in bounds - let mut last_index = dim.clone(); - for mut index in last_index.slice_mut().iter_mut() { - *index -= 1; - } - if let Some(offset) = stride_offset_checked_arithmetic(dim, - strides, - &last_index) - { - // offset is guaranteed to be positive so no issue converting - // to usize here - if (offset as usize) >= data.len() { - return Err(from_kind(ErrorKind::OutOfBounds)); - } - if dim_stride_overlap(dim, strides) { - return Err(from_kind(ErrorKind::Unsupported)); - } - } else { - return Err(from_kind(ErrorKind::OutOfBounds)); - } - Ok(()) -} - -/// Return stride offset for this dimension and index. -/// -/// Return None if the indices are out of bounds, or the calculation would wrap -/// around. -fn stride_offset_checked_arithmetic(dim: &D, strides: &D, index: &D) - -> Option - where D: Dimension -{ - let mut offset = 0; - for (&d, &i, &s) in zipsl(dim.slice(), index.slice()).zip_cons(strides.slice()) { - if i >= d { - return None; - } - - if let Some(offset_) = (i as isize) - .checked_mul((s as Ixs) as isize) - .and_then(|x| x.checked_add(offset)) { - offset = offset_; - } else { - return None; - } - } - Some(offset) -} - -/// Array shape and index trait. -/// -/// `unsafe` because of the assumptions in the default methods. -/// -/// ***Don't implement or call methods in this trait, its interface is internal -/// to the crate and will evolve at will.*** -pub unsafe trait Dimension : Clone + Eq + Debug + Send + Sync + Default { - /// `SliceArg` is the type which is used to specify slicing for this - /// dimension. - /// - /// For the fixed size dimensions (tuples) it is a fixed size array - /// of the correct size, which you pass by reference. For the `Vec` - /// dimension it is a slice. - /// - /// - For `Ix`: `[Si; 1]` - /// - For `(Ix, Ix)`: `[Si; 2]` - /// - and so on.. - /// - For `Vec`: `[Si]` - /// - /// The easiest way to create a `&SliceArg` is using the macro - /// [`s![]`](macro.s!.html). - type SliceArg: ?Sized + AsRef<[Si]>; - #[doc(hidden)] - fn ndim(&self) -> usize; - #[doc(hidden)] - fn slice(&self) -> &[Ix] { - unsafe { - slice::from_raw_parts(self as *const _ as *const Ix, self.ndim()) - } - } - - #[doc(hidden)] - fn slice_mut(&mut self) -> &mut [Ix] { - unsafe { - slice::from_raw_parts_mut(self as *mut _ as *mut Ix, self.ndim()) - } - } - - #[doc(hidden)] - fn size(&self) -> usize { - self.slice().iter().fold(1, |s, &a| s * a as usize) - } - - #[doc(hidden)] - /// Compute the size while checking for overflow - fn size_checked(&self) -> Option { - self.slice().iter().fold(Some(1), |s, &a| s.and_then(|s_| s_.checked_mul(a))) - } - - #[doc(hidden)] - fn default_strides(&self) -> Self { - // Compute default array strides - // Shape (a, b, c) => Give strides (b * c, c, 1) - let mut strides = self.clone(); - { - let mut it = strides.slice_mut().iter_mut().rev(); - // Set first element to 1 - for rs in it.by_ref() { - *rs = 1; - break; - } - let mut cum_prod = 1; - for (rs, dim) in it.zip(self.slice().iter().rev()) { - cum_prod *= *dim; - *rs = cum_prod; - } - } - strides - } - - #[doc(hidden)] - fn fortran_strides(&self) -> Self { - // Compute fortran array strides - // Shape (a, b, c) => Give strides (1, a, a * b) - let mut strides = self.clone(); - { - let mut it = strides.slice_mut().iter_mut(); - // Set first element to 1 - for rs in it.by_ref() { - *rs = 1; - break; - } - let mut cum_prod = 1; - for (rs, dim) in it.zip(self.slice().iter()) { - cum_prod *= *dim; - *rs = cum_prod; - } - } - strides - } - - #[doc(hidden)] - #[inline] - fn first_index(&self) -> Option { - for ax in self.slice().iter() { - if *ax == 0 { - return None; - } - } - let mut index = self.clone(); - for rr in index.slice_mut().iter_mut() { - *rr = 0; - } - Some(index) - } - - #[doc(hidden)] - /// Iteration -- Use self as size, and return next index after `index` - /// or None if there are no more. - // FIXME: use &Self for index or even &mut? - #[inline] - fn next_for(&self, index: Self) -> Option { - let mut index = index; - let mut done = false; - for (&dim, ix) in zip(self.slice(), index.slice_mut()).rev() { - *ix += 1; - if *ix == dim { - *ix = 0; - } else { - done = true; - break; - } - } - if done { - Some(index) - } else { - None - } - } - - #[doc(hidden)] - /// Return stride offset for index. - fn stride_offset(index: &Self, strides: &Self) -> isize { - let mut offset = 0; - for (&i, &s) in zipsl(index.slice(), strides.slice()) { - offset += stride_offset(i, s); - } - offset - } - - #[doc(hidden)] - /// Return stride offset for this dimension and index. - fn stride_offset_checked(&self, strides: &Self, index: &Self) -> Option { - let mut offset = 0; - for (&d, &i, &s) in zipsl(self.slice(), index.slice()).zip_cons(strides.slice()) - { - if i >= d { - return None; - } - offset += stride_offset(i, s); - } - Some(offset) - } - - #[doc(hidden)] - fn last_elem(&self) -> usize { - if self.ndim() == 0 { 0 } else { self.slice()[self.ndim() - 1] } - } - - #[doc(hidden)] - /// Modify dimension, strides and return data pointer offset - /// - /// **Panics** if `slices` does not correspond to the number of axes, - /// if any stride is 0, or if any index is out of bounds. - fn do_slices(dim: &mut Self, strides: &mut Self, slices: &Self::SliceArg) -> isize { - let slices = slices.as_ref(); - let mut offset = 0; - assert!(slices.len() == dim.slice().len()); - for (dr, sr, &slc) in zipsl_mut(dim.slice_mut(), strides.slice_mut()).zip_cons(slices) - { - let m = *dr; - let mi = m as Ixs; - let Si(b1, opt_e1, s1) = slc; - let e1 = opt_e1.unwrap_or(mi); - - let b1 = abs_index(mi, b1); - let mut e1 = abs_index(mi, e1); - if e1 < b1 { e1 = b1; } - - assert!(b1 <= m); - assert!(e1 <= m); - - let m = e1 - b1; - // stride - let s = (*sr) as Ixs; - - // Data pointer offset - offset += stride_offset(b1, *sr); - // Adjust for strides - assert!(s1 != 0); - // How to implement negative strides: - // - // Increase start pointer by - // old stride * (old dim - 1) - // to put the pointer completely in the other end - if s1 < 0 { - offset += stride_offset(m - 1, *sr); - } - - let s_prim = s * s1; - - let d = m / s1.abs() as Ix; - let r = m % s1.abs() as Ix; - let m_prim = d + if r > 0 { 1 } else { 0 }; - - // Update dimension and stride coordinate - *dr = m_prim; - *sr = s_prim as Ix; - } - offset - } - - /// Return the axis ordering corresponding to the fastest variation - /// (in ascending order). - /// - /// Assumes that no stride value appears twice. This cannot yield the correct - /// result the strides are not positive. - #[doc(hidden)] - fn _fastest_varying_stride_order(&self) -> Self { - let mut indices = self.clone(); - for (i, elt) in enumerate(indices.slice_mut()) { - *elt = i; - } - let strides = self.slice(); - indices.slice_mut().sort_by_key(|&i| strides[i]); - indices - } -} - -/// Implementation-specific extensions to `Dimension` -pub trait DimensionExt { -// note: many extensions go in the main trait if they need to be special- -// cased per dimension - /// Get the dimension at `axis`. - /// - /// *Panics* if `axis` is out of bounds. - #[inline] - fn axis(&self, axis: Axis) -> Ix; - - /// Set the dimension at `axis`. - /// - /// *Panics* if `axis` is out of bounds. - #[inline] - fn set_axis(&mut self, axis: Axis, value: Ix); -} - -impl DimensionExt for D - where D: Dimension -{ - #[inline] - fn axis(&self, axis: Axis) -> Ix { - self.slice()[axis.axis()] - } - - #[inline] - fn set_axis(&mut self, axis: Axis, value: Ix) { - self.slice_mut()[axis.axis()] = value; - } -} - -impl<'a> DimensionExt for [Ix] -{ - #[inline] - fn axis(&self, axis: Axis) -> Ix { - self[axis.axis()] - } - - #[inline] - fn set_axis(&mut self, axis: Axis, value: Ix) { - self[axis.axis()] = value; - } -} - -#[inline] -fn abs_index(len: Ixs, index: Ixs) -> Ix { - if index < 0 { - (len + index) as Ix - } else { - index as Ix - } -} - -/// Collapse axis `axis` and shift so that only subarray `index` is -/// available. -/// -/// **Panics** if `index` is larger than the size of the axis -// FIXME: Move to Dimension trait -pub fn do_sub(dims: &mut D, ptr: &mut *mut A, strides: &D, - axis: usize, index: Ix) { - let dim = dims.slice()[axis]; - let stride = strides.slice()[axis]; - assert!(index < dim); - dims.slice_mut()[axis] = 1; - let off = stride_offset(index, stride); - unsafe { - *ptr = ptr.offset(off); - } -} - - -unsafe impl Dimension for () { - type SliceArg = [Si; 0]; - // empty product is 1 -> size is 1 - #[inline] - fn ndim(&self) -> usize { 0 } - #[inline] - fn slice(&self) -> &[Ix] { &[] } - #[inline] - fn slice_mut(&mut self) -> &mut [Ix] { &mut [] } - #[inline] - fn _fastest_varying_stride_order(&self) -> Self { } -} - -unsafe impl Dimension for Ix { - type SliceArg = [Si; 1]; - #[inline] - fn ndim(&self) -> usize { 1 } - #[inline] - fn size(&self) -> usize { *self as usize } - #[inline] - fn size_checked(&self) -> Option { Some(*self as usize) } - - #[inline] - fn default_strides(&self) -> Self { 1 } - - #[inline] - fn _fastest_varying_stride_order(&self) -> Self { - 0 - } - - #[inline] - fn first_index(&self) -> Option { - if *self != 0 { - Some(0) - } else { - None - } - } - #[inline] - fn next_for(&self, mut index: Ix) -> Option { - index += 1; - if index < *self { - Some(index) - } else { - None - } - } - - /// Self is an index, return the stride offset - #[inline] - fn stride_offset(index: &Ix, stride: &Ix) -> isize { - stride_offset(*index, *stride) - } - - /// Return stride offset for this dimension and index. - #[inline] - fn stride_offset_checked(&self, stride: &Ix, index: &Ix) -> Option { - if *index < *self { - Some(stride_offset(*index, *stride)) - } else { - None - } - } -} - -unsafe impl Dimension for (Ix, Ix) { - type SliceArg = [Si; 2]; - #[inline] - fn ndim(&self) -> usize { 2 } - - #[inline] - fn size(&self) -> usize { let (m, n) = *self; m as usize * n as usize } - - #[inline] - fn size_checked(&self) -> Option { - let (m, n) = *self; - (m as usize).checked_mul(n as usize) - } - - #[inline] - fn default_strides(&self) -> Self { - // Compute default array strides - // Shape (a, b, c) => Give strides (b * c, c, 1) - (self.1, 1) - } - - #[inline] - fn _fastest_varying_stride_order(&self) -> Self { - if self.0 as Ixs <= self.1 as Ixs { (0, 1) } else { (1, 0) } - } - - #[inline] - fn first_index(&self) -> Option<(Ix, Ix)> { - let (m, n) = *self; - if m != 0 && n != 0 { - Some((0, 0)) - } else { - None - } - } - #[inline] - fn next_for(&self, index: (Ix, Ix)) -> Option<(Ix, Ix)> { - let (mut i, mut j) = index; - let (imax, jmax) = *self; - j += 1; - if j == jmax { - j = 0; - i += 1; - if i == imax { - return None; - } - } - Some((i, j)) - } - - /// Self is an index, return the stride offset - #[inline] - fn stride_offset(index: &(Ix, Ix), strides: &(Ix, Ix)) -> isize { - let (i, j) = *index; - let (s, t) = *strides; - stride_offset(i, s) + stride_offset(j, t) - } - - /// Return stride offset for this dimension and index. - #[inline] - fn stride_offset_checked(&self, strides: &(Ix, Ix), index: &(Ix, Ix)) -> Option - { - let (m, n) = *self; - let (i, j) = *index; - let (s, t) = *strides; - if i < m && j < n { - Some(stride_offset(i, s) + stride_offset(j, t)) - } else { - None - } - } -} - -unsafe impl Dimension for (Ix, Ix, Ix) { - type SliceArg = [Si; 3]; - #[inline] - fn ndim(&self) -> usize { 3 } - #[inline] - fn size(&self) -> usize { let (m, n, o) = *self; m as usize * n as usize * o as usize } - #[inline] - fn next_for(&self, index: (Ix, Ix, Ix)) -> Option<(Ix, Ix, Ix)> { - let (mut i, mut j, mut k) = index; - let (imax, jmax, kmax) = *self; - k += 1; - if k == kmax { - k = 0; - j += 1; - if j == jmax { - j = 0; - i += 1; - if i == imax { - return None; - } - } - } - Some((i, j, k)) - } - - /// Self is an index, return the stride offset - #[inline] - fn stride_offset(index: &(Ix, Ix, Ix), strides: &(Ix, Ix, Ix)) -> isize { - let (i, j, k) = *index; - let (s, t, u) = *strides; - stride_offset(i, s) + stride_offset(j, t) + stride_offset(k, u) - } - - #[inline] - fn _fastest_varying_stride_order(&self) -> Self { - let mut stride = *self; - let mut order = (0, 1, 2); - macro_rules! swap { - ($stride:expr, $order:expr, $x:expr, $y:expr) => { - if $stride[$x] > $stride[$y] { - $stride.swap($x, $y); - $order.swap($x, $y); - } - } - } - { - // stable sorting network for 3 elements - let order = order.slice_mut(); - let strides = stride.slice_mut(); - swap![strides, order, 1, 2]; - swap![strides, order, 0, 1]; - swap![strides, order, 1, 2]; - } - order - } -} - -macro_rules! large_dim { - ($n:expr, $($ix:ident),+) => ( - unsafe impl Dimension for ($($ix),+) { - type SliceArg = [Si; $n]; - #[inline] - fn ndim(&self) -> usize { $n } - } - ) -} - -large_dim!(4, Ix, Ix, Ix, Ix); -large_dim!(5, Ix, Ix, Ix, Ix, Ix); -large_dim!(6, Ix, Ix, Ix, Ix, Ix, Ix); -large_dim!(7, Ix, Ix, Ix, Ix, Ix, Ix, Ix); -large_dim!(8, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix); -large_dim!(9, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix); -large_dim!(10, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix); -large_dim!(11, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix); -large_dim!(12, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix); - -/// Vec is a "dynamic" index, pretty hard to use when indexing, -/// and memory wasteful, but it allows an arbitrary and dynamic number of axes. -unsafe impl Dimension for Vec -{ - type SliceArg = [Si]; - fn ndim(&self) -> usize { self.len() } - fn slice(&self) -> &[Ix] { self } - fn slice_mut(&mut self) -> &mut [Ix] { self } -} - -/// Array shape with a next smaller dimension. -/// -/// `RemoveAxis` defines a larger-than relation for array shapes: -/// removing one axis from *Self* gives smaller dimension *Smaller*. -pub trait RemoveAxis : Dimension { - type Smaller: Dimension; - fn remove_axis(&self, axis: Axis) -> Self::Smaller; -} - -macro_rules! impl_shrink( - ($_a:ident, ) => {}; // implement this case manually below - ($_a:ident, $_b:ident, ) => {}; // implement this case manually below - ($from:ident, $($more:ident,)*) => ( -impl RemoveAxis for ($from $(,$more)*) -{ - type Smaller = ($($more),*); - #[allow(unused_parens)] - #[inline] - fn remove_axis(&self, axis: Axis) -> ($($more),*) { - let mut tup = ($(0 as $more),*); - { - let mut it = tup.slice_mut().iter_mut(); - for (i, &d) in self.slice().iter().enumerate() { - if i == axis.axis() { - continue; - } - for rr in it.by_ref() { - *rr = d; - break - } - } - } - tup - } -} - ) -); - -impl RemoveAxis for Ix { - type Smaller = (); - #[inline] - fn remove_axis(&self, _: Axis) { } -} - -impl RemoveAxis for (Ix, Ix) { - type Smaller = Ix; - #[inline] - fn remove_axis(&self, axis: Axis) -> Ix { - let axis = axis.axis(); - debug_assert!(axis < self.ndim()); - if axis == 0 { self.1 } else { self.0 } - } -} - -macro_rules! impl_shrink_recursive( - ($ix:ident, ) => (impl_shrink!($ix,);); - ($ix1:ident, $($ix:ident,)*) => ( - impl_shrink_recursive!($($ix,)*); - impl_shrink!($ix1, $($ix,)*); - ) -); - -// 12 is the maximum number for having the Eq trait from libstd -impl_shrink_recursive!(Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix, Ix,); - -impl RemoveAxis for Vec { - type Smaller = Vec; - fn remove_axis(&self, axis: Axis) -> Vec { - let mut res = self.clone(); - res.remove(axis.axis()); - res - } -} - -/// Tuple or fixed size arrays that can be used to index an array. -/// -/// ``` -/// use ndarray::arr2; -/// -/// let mut a = arr2(&[[0, 1], [0, 0]]); -/// a[[1, 1]] = 1; -/// assert_eq!(a[[0, 1]], 1); -/// assert_eq!(a[[1, 1]], 1); -/// ``` -/// -/// **Note** that `NdIndex` is implemented for all `D where D: Dimension`. -pub unsafe trait NdIndex : Debug { - type Dim: Dimension; - #[doc(hidden)] - fn index_checked(&self, dim: &Self::Dim, strides: &Self::Dim) -> Option; -} - -unsafe impl NdIndex for D - where D: Dimension -{ - type Dim = D; - fn index_checked(&self, dim: &Self::Dim, strides: &Self::Dim) -> Option { - dim.stride_offset_checked(strides, self) - } -} - -unsafe impl NdIndex for [Ix; 0] { - type Dim = (); - #[inline] - fn index_checked(&self, dim: &Self::Dim, strides: &Self::Dim) -> Option { - dim.stride_offset_checked(strides, &()) - } -} - -unsafe impl NdIndex for [Ix; 1] { - type Dim = Ix; - #[inline] - fn index_checked(&self, dim: &Self::Dim, strides: &Self::Dim) -> Option { - dim.stride_offset_checked(strides, &self[0]) - } -} - -unsafe impl NdIndex for [Ix; 2] { - type Dim = (Ix, Ix); - #[inline] - fn index_checked(&self, dim: &Self::Dim, strides: &Self::Dim) -> Option { - let index = (self[0], self[1]); - dim.stride_offset_checked(strides, &index) - } -} - -unsafe impl NdIndex for [Ix; 3] { - type Dim = (Ix, Ix, Ix); - #[inline] - fn index_checked(&self, dim: &Self::Dim, strides: &Self::Dim) -> Option { - let index = (self[0], self[1], self[2]); - dim.stride_offset_checked(strides, &index) - } -} - -unsafe impl NdIndex for [Ix; 4] { - type Dim = (Ix, Ix, Ix, Ix); - #[inline] - fn index_checked(&self, dim: &Self::Dim, strides: &Self::Dim) -> Option { - let index = (self[0], self[1], self[2], self[3]); - dim.stride_offset_checked(strides, &index) - } -} - -unsafe impl<'a> NdIndex for &'a [Ix] { - type Dim = Vec; - fn index_checked(&self, dim: &Self::Dim, strides: &Self::Dim) -> Option { - let mut offset = 0; - for (&d, &i, &s) in zipsl(&dim[..], &self[..]).zip_cons(strides.slice()) { - if i >= d { - return None; - } - offset += stride_offset(i, s); - } - Some(offset) - } -} - -// NOTE: These tests are not compiled & tested -#[cfg(test)] -mod test { - use super::Dimension; - use error::{from_kind, ErrorKind}; - - #[test] - fn slice_indexing_uncommon_strides() { - let v: Vec<_> = (0..12).collect(); - let dim = (2, 3, 2); - let strides = (1, 2, 6); - assert!(super::can_index_slice(&v, &dim, &strides).is_ok()); - - let strides = (2, 4, 12); - assert_eq!(super::can_index_slice(&v, &dim, &strides), - Err(from_kind(ErrorKind::OutOfBounds))); - } - - #[test] - fn overlapping_strides_dim() { - let dim = (2, 3, 2); - let strides = (5, 2, 1); - assert!(super::dim_stride_overlap(&dim, &strides)); - let strides = (6, 2, 1); - assert!(!super::dim_stride_overlap(&dim, &strides)); - let strides = (6, 0, 1); - assert!(super::dim_stride_overlap(&dim, &strides)); - } -} - -/// An axis index. -/// -/// An axis one of an array’s “dimensions”; an *n*-dimensional array has *n* axes. -/// Axis *0* is the array’s outermost axis and *n*-1 is the innermost. -/// -/// All array axis arguments use this type to make the code easier to write -/// correctly and easier to understand. -#[derive(Copy, Eq, Ord, Hash, Debug)] -pub struct Axis(pub usize); - -impl Axis { - #[inline(always)] - pub fn axis(&self) -> usize { self.0 } -} - -macro_rules! clone_from_copy { - ($typename:ident) => { - impl Clone for $typename { - #[inline] - fn clone(&self) -> Self { *self } - } - } -} - -macro_rules! derive_cmp { - ($traitname:ident for $typename:ident, $method:ident -> $ret:ty) => { - impl $traitname for $typename { - #[inline(always)] - fn $method(&self, rhs: &Self) -> $ret { - (self.0).$method(&rhs.0) - } - } - } -} - -derive_cmp!{PartialEq for Axis, eq -> bool} -derive_cmp!{PartialOrd for Axis, partial_cmp -> Option} -clone_from_copy!{Axis} - diff --git a/src/dimension/axis.rs b/src/dimension/axis.rs new file mode 100644 index 000000000..214693078 --- /dev/null +++ b/src/dimension/axis.rs @@ -0,0 +1,49 @@ +// Copyright 2016 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::cmp::Ordering; + +/// An axis index. +/// +/// An axis one of an array’s “dimensions”; an *n*-dimensional array has *n* axes. +/// Axis *0* is the array’s outermost axis and *n*-1 is the innermost. +/// +/// All array axis arguments use this type to make the code easier to write +/// correctly and easier to understand. +#[derive(Copy, Eq, Ord, Hash, Debug)] +pub struct Axis(pub usize); + +impl Axis { + #[inline(always)] + pub fn axis(&self) -> usize { self.0 } +} + +macro_rules! clone_from_copy { + ($typename:ident) => { + impl Clone for $typename { + #[inline] + fn clone(&self) -> Self { *self } + } + } +} + +macro_rules! derive_cmp { + ($traitname:ident for $typename:ident, $method:ident -> $ret:ty) => { + impl $traitname for $typename { + #[inline(always)] + fn $method(&self, rhs: &Self) -> $ret { + (self.0).$method(&rhs.0) + } + } + } +} + +derive_cmp!{PartialEq for Axis, eq -> bool} +derive_cmp!{PartialOrd for Axis, partial_cmp -> Option} +clone_from_copy!{Axis} + diff --git a/src/dimension/conversion.rs b/src/dimension/conversion.rs new file mode 100644 index 000000000..9da9f34a1 --- /dev/null +++ b/src/dimension/conversion.rs @@ -0,0 +1,168 @@ +// Copyright 2014-2016 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Tuple to array conversion, IntoDimension, and related things + +use std::ops::{Index, IndexMut}; +use libnum::Zero; + +use {Ix, Ix1, IxDyn, Dimension, Dim}; +use super::DimPrivate; + +/// $m: macro callback +/// $m is called with $arg and then the indices corresponding to the size argument +macro_rules! index { + ($m:ident $arg:tt 0) => ($m!($arg)); + ($m:ident $arg:tt 1) => ($m!($arg 0)); + ($m:ident $arg:tt 2) => ($m!($arg 0 1)); + ($m:ident $arg:tt 3) => ($m!($arg 0 1 2)); + ($m:ident $arg:tt 4) => ($m!($arg 0 1 2 3)); + ($m:ident $arg:tt 5) => ($m!($arg 0 1 2 3 4)); + ($m:ident $arg:tt 6) => ($m!($arg 0 1 2 3 4 5)); + ($m:ident $arg:tt 7) => ($m!($arg 0 1 2 3 4 5 6)); +} + +macro_rules! index_item_ix { + ($m:ident $arg:tt 0) => ($m!($arg);); + ($m:ident $arg:tt 1) => ($m!($arg Ix);); + ($m:ident $arg:tt 2) => ($m!($arg Ix Ix);); + ($m:ident $arg:tt 3) => ($m!($arg Ix Ix Ix);); + ($m:ident $arg:tt 4) => ($m!($arg Ix Ix Ix Ix);); + ($m:ident $arg:tt 5) => ($m!($arg Ix Ix Ix Ix Ix);); + ($m:ident $arg:tt 6) => ($m!($arg Ix Ix Ix Ix Ix Ix);); + ($m:ident $arg:tt 7) => ($m!($arg Ix Ix Ix Ix Ix Ix Ix);); +} + +macro_rules! index_item { + ($m:ident $arg:tt 0) => ($m!($arg);); + ($m:ident $arg:tt 1) => ($m!($arg 0);); + ($m:ident $arg:tt 2) => ($m!($arg 0 1);); + ($m:ident $arg:tt 3) => ($m!($arg 0 1 2);); + ($m:ident $arg:tt 4) => ($m!($arg 0 1 2 3);); + ($m:ident $arg:tt 5) => ($m!($arg 0 1 2 3 4);); + ($m:ident $arg:tt 6) => ($m!($arg 0 1 2 3 4 5);); + ($m:ident $arg:tt 7) => ($m!($arg 0 1 2 3 4 5 6);); +} + +/// Convert a value into a dimension. +pub trait IntoDimension { + type Dim: Dimension; + fn into_dimension(self) -> Self::Dim; +} + +impl IntoDimension for Ix { + type Dim = Ix1; + #[inline(always)] + fn into_dimension(self) -> Ix1 { Ix1(self) } +} + +impl IntoDimension for D where D: Dimension { + type Dim = D; + #[inline(always)] + fn into_dimension(self) -> Self { self } +} + +impl IntoDimension for Vec { + type Dim = IxDyn; + #[inline(always)] + fn into_dimension(self) -> Self::Dim { Dim::new(self) } +} + +pub trait Convert { + type To; + fn convert(self) -> Self::To; +} + +macro_rules! sub { + ($_x:tt $y:tt) => ($y); +} + +macro_rules! tuple_type { + ([$T:ident] $($index:tt)*) => ( + ( $(sub!($index $T), )* ) + ) +} + +macro_rules! tuple_expr { + ([$self_:expr] $($index:tt)*) => ( + ( $($self_[$index], )* ) + ) +} + +macro_rules! array_expr { + ([$self_:expr] $($index:tt)*) => ( + [$($self_ . $index, )*] + ) +} + +macro_rules! array_zero { + ([] $($index:tt)*) => ( + [$(sub!($index 0), )*] + ) +} + +macro_rules! tuple_to_array { + ([] $($n:tt)*) => { + $( + index_item_ix!(impl_tuple_to_array [$n] $n); + )* + } +} +macro_rules! impl_tuple_to_array { + ([$n:tt] $($ix:tt)*) => { + impl Convert for [Ix; $n] { + type To = ($($ix ,)*); + fn convert(self) -> Self::To { + index!(tuple_expr [self] $n) + } + } + + impl IntoDimension for [Ix; $n] { + type Dim = Dim<[Ix; $n]>; + #[inline(always)] + fn into_dimension(self) -> Self::Dim { + Dim::new(self) + } + } + + impl IntoDimension for ($($ix ,)*) { + type Dim = Dim<[Ix; $n]>; + #[inline(always)] + fn into_dimension(self) -> Self::Dim { + Dim::new(index!(array_expr [self] $n)) + } + } + + impl Index for Dim<[Ix; $n]> { + type Output = usize; + #[inline(always)] + fn index(&self, index: usize) -> &Self::Output { + &self.ix()[index] + } + } + + impl IndexMut for Dim<[Ix; $n]> { + #[inline(always)] + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.ixm()[index] + } + } + + impl Zero for Dim<[Ix; $n]> { + #[inline] + fn zero() -> Self { + Dim::new(index!(array_zero [] $n)) + } + fn is_zero(&self) -> bool { + self.slice().iter().all(|x| *x == 0) + } + } + } +} + +index_item!(tuple_to_array [] 7); diff --git a/src/dimension/dim.rs b/src/dimension/dim.rs new file mode 100644 index 000000000..fb8b9b47c --- /dev/null +++ b/src/dimension/dim.rs @@ -0,0 +1,173 @@ +// Copyright 2016 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::fmt; +use itertools::zip; + +use super::IntoDimension; +use super::Dimension; +use super::DimPrivate; +use Ix; + +/// Dimension description. +/// +/// `Dim` describes the number of axes and the length of each axis +/// in an array. It is also used as an index type. +/// +/// See also the [`Dimension` trait](Dimension.t.html) for its methods and +/// operations. +/// +/// # Examples +/// +/// To create an array with a particular dimension, you'd just pass +/// a tuple (in this example (3, 2) is used), which is converted to +/// `Dim` by the array constructor. +/// +/// ``` +/// use ndarray::Array2; +/// use ndarray::Dim; +/// +/// let mut array = Array2::zeros((3, 2)); +/// array[[0, 0]] = 1.; +/// assert_eq!(array.dim(), Dim([3, 2])); +/// ``` +#[derive(Copy, Clone, PartialEq, Eq, Default)] +pub struct Dim { + index: I, +} + +impl DimPrivate for Dim { + fn new(index: I) -> Dim { + Dim { + index: index, + } + } + fn ix(&self) -> &I { &self.index } + fn ixm(&mut self) -> &mut I { &mut self.index } +} + +/// Create a new dimension value. +#[allow(non_snake_case)] +pub fn Dim(index: T) -> T::Dim + where T: IntoDimension +{ + index.into_dimension() +} + +impl PartialEq for Dim + where I: PartialEq, +{ + fn eq(&self, rhs: &I) -> bool { + self.index == *rhs + } +} + +impl fmt::Debug for Dim + where I: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.index) + } +} + +use std::ops::{Add, Sub, Mul, AddAssign, SubAssign, MulAssign}; + +macro_rules! impl_op { + ($op:ident, $op_m:ident, $opassign:ident, $opassign_m:ident, $expr:ident) => { + impl $op for Dim + where Dim: Dimension, + { + type Output = Self; + fn $op_m(mut self, rhs: Self) -> Self { + $expr!(self, &rhs); + self + } + } + + impl $opassign for Dim + where Dim: Dimension, + { + fn $opassign_m(&mut self, rhs: Self) { + $expr!(*self, &rhs); + } + } + + impl<'a, I> $opassign<&'a Dim> for Dim + where Dim: Dimension, + { + fn $opassign_m(&mut self, rhs: &Self) { + for (x, &y) in zip(self.slice_mut(), rhs.slice()) { + $expr!(*x, y); + } + } + } + + } +} + +macro_rules! impl_single_op { + ($op:ident, $op_m:ident, $opassign:ident, $opassign_m:ident, $expr:ident) => { + impl $op for Dim<[Ix; 1]> + { + type Output = Self; + #[inline] + fn $op_m(mut self, rhs: Ix) -> Self { + $expr!(self, rhs); + self + } + } + + impl $opassign for Dim<[Ix; 1]> { + #[inline] + fn $opassign_m(&mut self, rhs: Ix) { + $expr!((*self)[0], rhs); + } + } + }; +} + +macro_rules! impl_scalar_op { + ($op:ident, $op_m:ident, $opassign:ident, $opassign_m:ident, $expr:ident) => { + impl $op for Dim + where Dim: Dimension, + { + type Output = Self; + fn $op_m(mut self, rhs: Ix) -> Self { + $expr!(self, rhs); + self + } + } + + impl $opassign for Dim + where Dim: Dimension, + { + fn $opassign_m(&mut self, rhs: Ix) { + for x in self.slice_mut() { + $expr!(*x, rhs); + } + } + } + }; +} + +macro_rules! add { + ($x:expr, $y:expr) => { $x += $y; } +} +macro_rules! sub { + ($x:expr, $y:expr) => { $x -= $y; } +} +macro_rules! mul { + ($x:expr, $y:expr) => { $x *= $y; } +} +impl_op!(Add, add, AddAssign, add_assign, add); +impl_single_op!(Add, add, AddAssign, add_assign, add); +impl_op!(Sub, sub, SubAssign, sub_assign, sub); +impl_single_op!(Sub, sub, SubAssign, sub_assign, sub); +impl_op!(Mul, mul, MulAssign, mul_assign, mul); +impl_scalar_op!(Mul, mul, MulAssign, mul_assign, mul); + diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs new file mode 100644 index 000000000..3c75ff0c3 --- /dev/null +++ b/src/dimension/dimension_trait.rs @@ -0,0 +1,675 @@ +// Copyright 2014-2016 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +use std::fmt::Debug; +use std::ops::{Index, IndexMut}; +use std::ops::{Add, Sub, Mul, AddAssign, SubAssign, MulAssign}; + +use itertools::{enumerate, zip}; + +use {Ix, Ixs, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, Dim, Si}; +use IntoDimension; +use {ArrayView1, ArrayViewMut1}; +use {zipsl, zipsl_mut, ZipExt}; +use super::{ + stride_offset, + stride_offset_checked, + DimPrivate, +}; +use super::conversion::Convert; + +/// Array shape and index trait. +/// +/// This trait defines a number of methods and operations that can be used on +/// dimensions and indices. +/// +/// ***Note:*** *Don't implement this trait.* +pub unsafe trait Dimension : Clone + Eq + Debug + Send + Sync + Default + + IndexMut + + Add + + AddAssign + for<'x> AddAssign<&'x Self> + + Sub + + SubAssign + for<'x> SubAssign<&'x Self> + + Mul + Mul + + MulAssign + for<'x> MulAssign<&'x Self> + MulAssign + +{ + /// `SliceArg` is the type which is used to specify slicing for this + /// dimension. + /// + /// For the fixed size dimensions it is a fixed size array of the correct + /// size, which you pass by reference. For the `Vec` dimension it is + /// a slice. + /// + /// - For `Ix1`: `[Si; 1]` + /// - For `Ix2`: `[Si; 2]` + /// - and so on.. + /// - For `Vec`: `[Si]` + /// + /// The easiest way to create a `&SliceArg` is using the macro + /// [`s![]`](macro.s!.html). + type SliceArg: ?Sized + AsRef<[Si]>; + /// Pattern matching friendly form of the dimension value. + /// + /// - For `Ix1`: `usize`, + /// - For `Ix2`: `(usize, usize)` + /// - and so on.. + /// - For `Vec`: `Vec`, + type Pattern: IntoDimension; + #[doc(hidden)] + fn ndim(&self) -> usize; + + /// Convert the dimension into a pattern matching friendly value. + fn into_pattern(self) -> Self::Pattern; + + /// Compute the size of the dimension (number of elements) + fn size(&self) -> usize { + self.slice().iter().fold(1, |s, &a| s * a as usize) + } + + /// Compute the size while checking for overflow. + fn size_checked(&self) -> Option { + self.slice().iter().fold(Some(1), |s, &a| s.and_then(|s_| s_.checked_mul(a))) + } + + #[doc(hidden)] + fn slice(&self) -> &[Ix]; + + #[doc(hidden)] + fn slice_mut(&mut self) -> &mut [Ix]; + + /// Borrow as a read-only array view. + fn as_array_view(&self) -> ArrayView1 { + ArrayView1::from(self.slice()) + } + + /// Borrow as a read-write array view. + fn as_array_view_mut(&mut self) -> ArrayViewMut1 { + ArrayViewMut1::from(self.slice_mut()) + } + + #[doc(hidden)] + fn equal(&self, rhs: &Self) -> bool { + self.slice() == rhs.slice() + } + + #[doc(hidden)] + fn default_strides(&self) -> Self { + // Compute default array strides + // Shape (a, b, c) => Give strides (b * c, c, 1) + let mut strides = self.clone(); + { + let mut it = strides.slice_mut().iter_mut().rev(); + // Set first element to 1 + for rs in it.by_ref() { + *rs = 1; + break; + } + let mut cum_prod = 1; + for (rs, dim) in it.zip(self.slice().iter().rev()) { + cum_prod *= *dim; + *rs = cum_prod; + } + } + strides + } + + #[doc(hidden)] + fn fortran_strides(&self) -> Self { + // Compute fortran array strides + // Shape (a, b, c) => Give strides (1, a, a * b) + let mut strides = self.clone(); + { + let mut it = strides.slice_mut().iter_mut(); + // Set first element to 1 + for rs in it.by_ref() { + *rs = 1; + break; + } + let mut cum_prod = 1; + for (rs, dim) in it.zip(self.slice().iter()) { + cum_prod *= *dim; + *rs = cum_prod; + } + } + strides + } + + #[doc(hidden)] + #[inline] + fn first_index(&self) -> Option { + for ax in self.slice().iter() { + if *ax == 0 { + return None; + } + } + let mut index = self.clone(); + for rr in index.slice_mut().iter_mut() { + *rr = 0; + } + Some(index) + } + + #[doc(hidden)] + /// Iteration -- Use self as size, and return next index after `index` + /// or None if there are no more. + // FIXME: use &Self for index or even &mut? + #[inline] + fn next_for(&self, index: Self) -> Option { + let mut index = index; + let mut done = false; + for (&dim, ix) in zip(self.slice(), index.slice_mut()).rev() { + *ix += 1; + if *ix == dim { + *ix = 0; + } else { + done = true; + break; + } + } + if done { + Some(index) + } else { + None + } + } + + #[doc(hidden)] + /// Return stride offset for index. + fn stride_offset(index: &Self, strides: &Self) -> isize { + let mut offset = 0; + for (&i, &s) in zipsl(index.slice(), strides.slice()) { + offset += stride_offset(i, s); + } + offset + } + + #[doc(hidden)] + /// Return stride offset for this dimension and index. + fn stride_offset_checked(&self, strides: &Self, index: &Self) -> Option { + stride_offset_checked(self.slice(), strides.slice(), index.slice()) + } + + #[doc(hidden)] + fn last_elem(&self) -> usize { + if self.ndim() == 0 { 0 } else { self.slice()[self.ndim() - 1] } + } + + #[doc(hidden)] + fn set_last_elem(&mut self, i: usize) { + let nd = self.ndim(); + self.slice_mut()[nd - 1] = i; + } + + #[doc(hidden)] + /// Modify dimension, strides and return data pointer offset + /// + /// **Panics** if `slices` does not correspond to the number of axes, + /// if any stride is 0, or if any index is out of bounds. + fn do_slices(dim: &mut Self, strides: &mut Self, slices: &Self::SliceArg) -> isize { + let slices = slices.as_ref(); + let mut offset = 0; + assert!(slices.len() == dim.slice().len()); + for (dr, sr, &slc) in zipsl_mut(dim.slice_mut(), strides.slice_mut()).zip_cons(slices) + { + let m = *dr; + let mi = m as Ixs; + let Si(b1, opt_e1, s1) = slc; + let e1 = opt_e1.unwrap_or(mi); + + let b1 = abs_index(mi, b1); + let mut e1 = abs_index(mi, e1); + if e1 < b1 { e1 = b1; } + + assert!(b1 <= m); + assert!(e1 <= m); + + let m = e1 - b1; + // stride + let s = (*sr) as Ixs; + + // Data pointer offset + offset += stride_offset(b1, *sr); + // Adjust for strides + assert!(s1 != 0); + // How to implement negative strides: + // + // Increase start pointer by + // old stride * (old dim - 1) + // to put the pointer completely in the other end + if s1 < 0 { + offset += stride_offset(m - 1, *sr); + } + + let s_prim = s * s1; + + let d = m / s1.abs() as Ix; + let r = m % s1.abs() as Ix; + let m_prim = d + if r > 0 { 1 } else { 0 }; + + // Update dimension and stride coordinate + *dr = m_prim; + *sr = s_prim as Ix; + } + offset + } + + #[doc(hidden)] + fn is_contiguous(dim: &Self, strides: &Self) -> bool { + let defaults = dim.default_strides(); + if strides.equal(&defaults) { + return true; + } + if dim.ndim() == 1 { return false; } + let order = strides._fastest_varying_stride_order(); + let strides = strides.slice(); + + // FIXME: Negative strides + let dim_slice = dim.slice(); + let mut cstride = 1; + for &i in order.slice() { + // a dimension of length 1 can have unequal strides + if dim_slice[i] != 1 && strides[i] != cstride { + return false; + } + cstride *= dim_slice[i]; + } + true + } + + /// Return the axis ordering corresponding to the fastest variation + /// (in ascending order). + /// + /// Assumes that no stride value appears twice. This cannot yield the correct + /// result the strides are not positive. + #[doc(hidden)] + fn _fastest_varying_stride_order(&self) -> Self { + let mut indices = self.clone(); + for (i, elt) in enumerate(indices.slice_mut()) { + *elt = i; + } + let strides = self.slice(); + indices.slice_mut().sort_by_key(|&i| strides[i]); + indices + } +} + +// utility functions + +#[inline] +fn abs_index(len: Ixs, index: Ixs) -> Ix { + if index < 0 { + (len + index) as Ix + } else { + index as Ix + } +} + + +// Dimension impls + + +unsafe impl Dimension for Ix0 { + type SliceArg = [Si; 0]; + type Pattern = (); + // empty product is 1 -> size is 1 + #[inline] + fn ndim(&self) -> usize { 0 } + #[inline] + fn slice(&self) -> &[Ix] { &[] } + #[inline] + fn slice_mut(&mut self) -> &mut [Ix] { &mut [] } + #[inline] + fn _fastest_varying_stride_order(&self) -> Self { Ix0() } + #[inline] + fn into_pattern(self) -> Self::Pattern { } + #[inline] + fn next_for(&self, _index: Self) -> Option { + None + } +} + + +unsafe impl Dimension for Ix1 { + type SliceArg = [Si; 1]; + type Pattern = Ix; + #[inline] + fn ndim(&self) -> usize { 1 } + #[inline] + fn slice(&self) -> &[Ix] { self.ix() } + #[inline] + fn slice_mut(&mut self) -> &mut [Ix] { self.ixm() } + #[inline] + fn into_pattern(self) -> Self::Pattern { + get!(&self, 0) + } + #[inline] + fn next_for(&self, mut index: Self) -> Option { + getm!(index, 0) += 1; + if get!(&index, 0) < get!(self, 0) { + Some(index) + } else { + None + } + } + + #[inline] + fn equal(&self, rhs: &Self) -> bool { + get!(self, 0) == get!(rhs, 0) + } + + #[inline] + fn size(&self) -> usize { get!(self, 0) } + #[inline] + fn size_checked(&self) -> Option { Some(get!(self, 0)) } + + #[inline] + fn default_strides(&self) -> Self { + Ix1(1) + } + + #[inline] + fn _fastest_varying_stride_order(&self) -> Self { + Ix1(0) + } + + #[inline] + fn first_index(&self) -> Option { + if get!(self, 0) != 0 { + Some(Ix1(0)) + } else { + None + } + } + + /// Self is an index, return the stride offset + #[inline(always)] + fn stride_offset(index: &Self, stride: &Self) -> isize { + stride_offset(get!(index, 0), get!(stride, 0)) + } + + /// Return stride offset for this dimension and index. + #[inline] + fn stride_offset_checked(&self, stride: &Self, index: &Self) -> Option { + if get!(index, 0) < get!(self, 0) { + Some(stride_offset(get!(index, 0), get!(stride, 0))) + } else { + None + } + } +} + +unsafe impl Dimension for Ix2 { + type SliceArg = [Si; 2]; + type Pattern = (Ix, Ix); + #[inline] + fn ndim(&self) -> usize { 2 } + #[inline] + fn into_pattern(self) -> Self::Pattern { + self.ix().convert() + } + #[inline] + fn slice(&self) -> &[Ix] { self.ix() } + #[inline] + fn slice_mut(&mut self) -> &mut [Ix] { self.ixm() } + #[inline] + fn next_for(&self, index: Self) -> Option { + let mut i = get!(&index, 0); + let mut j = get!(&index, 1); + let imax = get!(self, 0); + let jmax = get!(self, 1); + j += 1; + if j >= jmax { + j = 0; + i += 1; + if i >= imax { + return None; + } + } + Some(Ix2(i, j)) + } + + #[inline] + fn equal(&self, rhs: &Self) -> bool { + get!(self, 0) == get!(rhs, 0) && get!(self, 1) == get!(rhs, 1) + } + + #[inline] + fn size(&self) -> usize { get!(self, 0) * get!(self, 1) } + + #[inline] + fn size_checked(&self) -> Option { + let m = get!(self, 0); + let n = get!(self, 1); + (m as usize).checked_mul(n as usize) + } + + #[inline] + fn last_elem(&self) -> usize { + get!(self, 1) + } + + #[inline] + fn set_last_elem(&mut self, i: usize) { + getm!(self, 1) = i; + } + + #[inline] + fn default_strides(&self) -> Self { + // Compute default array strides + // Shape (a, b, c) => Give strides (b * c, c, 1) + Ix2(get!(self, 1), 1) + } + #[inline] + fn fortran_strides(&self) -> Self { + Ix2(1, get!(self, 0)) + } + + #[inline] + fn _fastest_varying_stride_order(&self) -> Self { + if get!(self, 0) as Ixs <= get!(self, 1) as Ixs { Ix2(0, 1) } else { Ix2(1, 0) } + } + + #[inline] + fn is_contiguous(dim: &Self, strides: &Self) -> bool { + let defaults = dim.default_strides(); + if strides.equal(&defaults) { + return true; + } + + if dim.ndim() == 1 { return false; } + let order = strides._fastest_varying_stride_order(); + let strides = strides.slice(); + + // FIXME: Negative strides + let dim_slice = dim.slice(); + let mut cstride = 1; + for &i in order.slice() { + // a dimension of length 1 can have unequal strides + if dim_slice[i] != 1 && strides[i] != cstride { + return false; + } + cstride *= dim_slice[i]; + } + true + } + + #[inline] + fn first_index(&self) -> Option { + let m = get!(self, 0); + let n = get!(self, 1); + if m != 0 && n != 0 { + Some(Ix2(0, 0)) + } else { + None + } + } + + /// Self is an index, return the stride offset + #[inline(always)] + fn stride_offset(index: &Self, strides: &Self) -> isize { + let i = get!(index, 0); + let j = get!(index, 1); + let s = get!(strides, 0); + let t = get!(strides, 1); + stride_offset(i, s) + stride_offset(j, t) + } + + /// Return stride offset for this dimension and index. + #[inline] + fn stride_offset_checked(&self, strides: &Self, index: &Self) -> Option + { + let m = get!(self, 0); + let n = get!(self, 1); + let i = get!(index, 0); + let j = get!(index, 1); + let s = get!(strides, 0); + let t = get!(strides, 1); + if i < m && j < n { + Some(stride_offset(i, s) + stride_offset(j, t)) + } else { + None + } + } +} + +unsafe impl Dimension for Ix3 { + type SliceArg = [Si; 3]; + type Pattern = (Ix, Ix, Ix); + #[inline] + fn ndim(&self) -> usize { 3 } + #[inline] + fn into_pattern(self) -> Self::Pattern { + self.ix().convert() + } + #[inline] + fn slice(&self) -> &[Ix] { self.ix() } + #[inline] + fn slice_mut(&mut self) -> &mut [Ix] { self.ixm() } + + #[inline] + fn size(&self) -> usize { + let m = get!(self, 0); + let n = get!(self, 1); + let o = get!(self, 2); + m as usize * n as usize * o as usize + } + + #[inline] + fn next_for(&self, index: Self) -> Option { + let mut i = get!(&index, 0); + let mut j = get!(&index, 1); + let mut k = get!(&index, 2); + let imax = get!(self, 0); + let jmax = get!(self, 1); + let kmax = get!(self, 2); + k += 1; + if k == kmax { + k = 0; + j += 1; + if j == jmax { + j = 0; + i += 1; + if i == imax { + return None; + } + } + } + Some(Ix3(i, j, k)) + } + + /// Self is an index, return the stride offset + #[inline] + fn stride_offset(index: &Self, strides: &Self) -> isize { + let i = get!(index, 0); + let j = get!(index, 1); + let k = get!(index, 2); + let s = get!(strides, 0); + let t = get!(strides, 1); + let u = get!(strides, 2); + stride_offset(i, s) + stride_offset(j, t) + stride_offset(k, u) + } + + #[inline] + fn _fastest_varying_stride_order(&self) -> Self { + let mut stride = *self; + let mut order = Ix3(0, 1, 2); + macro_rules! swap { + ($stride:expr, $order:expr, $x:expr, $y:expr) => { + if $stride[$x] > $stride[$y] { + $stride.swap($x, $y); + $order.ixm().swap($x, $y); + } + } + } + { + // stable sorting network for 3 elements + let strides = stride.slice_mut(); + swap![strides, order, 1, 2]; + swap![strides, order, 0, 1]; + swap![strides, order, 1, 2]; + } + order + } +} + +macro_rules! large_dim { + ($n:expr, $name:ident, $($ix:ident),+) => ( + unsafe impl Dimension for $name { + type SliceArg = [Si; $n]; + type Pattern = ($($ix,)*); + #[inline] + fn ndim(&self) -> usize { $n } + #[inline] + fn into_pattern(self) -> Self::Pattern { + self.ix().convert() + } + #[inline] + fn slice(&self) -> &[Ix] { self.ix() } + #[inline] + fn slice_mut(&mut self) -> &mut [Ix] { self.ixm() } + } + ) +} + +large_dim!(4, Ix4, Ix, Ix, Ix, Ix); +large_dim!(5, Ix5, Ix, Ix, Ix, Ix, Ix); +large_dim!(6, Ix6, Ix, Ix, Ix, Ix, Ix, Ix); + +/// Vec is a "dynamic" index, pretty hard to use when indexing, +/// and memory wasteful, but it allows an arbitrary and dynamic number of axes. +unsafe impl Dimension for IxDyn +{ + type SliceArg = [Si]; + type Pattern = Self; + fn ndim(&self) -> usize { self.ix().len() } + fn slice(&self) -> &[Ix] { self.ix() } + fn slice_mut(&mut self) -> &mut [Ix] { self.ixm() } + #[inline] + fn into_pattern(self) -> Self::Pattern { + self + } +} + +impl Index for Dim> + where Vec: Index, +{ + type Output = as Index>::Output; + fn index(&self, index: J) -> &Self::Output { + &self.ix()[index] + } +} + +impl IndexMut for Dim> + where Vec: IndexMut, +{ + fn index_mut(&mut self, index: J) -> &mut Self::Output { + &mut self.ixm()[index] + } +} diff --git a/src/dimension/macros.rs b/src/dimension/macros.rs new file mode 100644 index 000000000..9f5f681e7 --- /dev/null +++ b/src/dimension/macros.rs @@ -0,0 +1,9 @@ + +/// Indexing macro for Dim<[usize; N]> this +/// gets the index at `$i` in the underlying array +macro_rules! get { + ($dim:expr, $i:expr) => { (*$dim.ix())[$i] } +} +macro_rules! getm { + ($dim:expr, $i:expr) => { (*$dim.ixm())[$i] } +} diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs new file mode 100644 index 000000000..86339e7cb --- /dev/null +++ b/src/dimension/mod.rs @@ -0,0 +1,248 @@ +// Copyright 2014-2016 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use {Ix, Ixs}; +use error::{from_kind, ErrorKind, ShapeError}; +use {zipsl, ZipExt}; + +pub use self::dim::*; +pub use self::axis::Axis; +pub use self::conversion::IntoDimension; +pub use self::dimension_trait::Dimension; +pub use self::ndindex::NdIndex; +pub use self::remove_axis::RemoveAxis; + +#[macro_use] mod macros; +mod axis; +mod conversion; +pub mod dim; +mod dimension_trait; +mod ndindex; +mod remove_axis; + +/// Private constructor and accessors for Dim +pub trait DimPrivate { + fn new(index: I) -> Self; + fn ix(&self) -> &I; + fn ixm(&mut self) -> &mut I; +} + +/// Calculate offset from `Ix` stride converting sign properly +#[inline(always)] +pub fn stride_offset(n: Ix, stride: Ix) -> isize { + (n as isize) * ((stride as Ixs) as isize) +} + +/// Check whether the given `dim` and `stride` lead to overlapping indices +/// +/// There is overlap if, when iterating through the dimensions in the order +/// of maximum variation, the current stride is inferior to the sum of all +/// preceding strides multiplied by their corresponding dimensions. +/// +/// The current implementation assumes strides to be positive +pub fn dim_stride_overlap(dim: &D, strides: &D) -> bool { + let order = strides._fastest_varying_stride_order(); + + let dim = dim.slice(); + let strides = strides.slice(); + let mut prev_offset = 1; + for &index in order.slice() { + let d = dim[index]; + let s = strides[index]; + // any stride is ok if dimension is 1 + if d != 1 && (s as isize) < prev_offset { + return true; + } + prev_offset = stride_offset(d, s); + } + false +} + +/// Check whether the given dimension and strides are memory safe +/// to index the provided slice. +/// +/// To be safe, no stride may be negative, and the offset corresponding +/// to the last element of each dimension should be smaller than the length +/// of the slice. Also, the strides should not allow a same element to be +/// referenced by two different index. +pub fn can_index_slice(data: &[A], dim: &D, strides: &D) + -> Result<(), ShapeError> +{ + // check lengths of axes. + let len = match dim.size_checked() { + Some(l) => l, + None => return Err(from_kind(ErrorKind::OutOfBounds)), + }; + // check if strides are strictly positive (zero ok for len 0) + for &s in strides.slice() { + let s = s as Ixs; + if s < 1 && (len != 0 || s < 0) { + return Err(from_kind(ErrorKind::Unsupported)); + } + } + if len == 0 { + return Ok(()); + } + // check that the maximum index is in bounds + let mut last_index = dim.clone(); + for mut index in last_index.slice_mut().iter_mut() { + *index -= 1; + } + if let Some(offset) = stride_offset_checked_arithmetic(dim, + strides, + &last_index) + { + // offset is guaranteed to be positive so no issue converting + // to usize here + if (offset as usize) >= data.len() { + return Err(from_kind(ErrorKind::OutOfBounds)); + } + if dim_stride_overlap(dim, strides) { + return Err(from_kind(ErrorKind::Unsupported)); + } + } else { + return Err(from_kind(ErrorKind::OutOfBounds)); + } + Ok(()) +} + +/// Return stride offset for this dimension and index. +/// +/// Return None if the indices are out of bounds, or the calculation would wrap +/// around. +fn stride_offset_checked_arithmetic(dim: &D, strides: &D, index: &D) + -> Option + where D: Dimension +{ + let mut offset = 0; + for (&d, &i, &s) in zipsl(dim.slice(), index.slice()).zip_cons(strides.slice()) { + if i >= d { + return None; + } + + if let Some(offset_) = (i as isize) + .checked_mul((s as Ixs) as isize) + .and_then(|x| x.checked_add(offset)) { + offset = offset_; + } else { + return None; + } + } + Some(offset) +} + +/// Stride offset checked general version (slices) +pub fn stride_offset_checked(dim: &[Ix], strides: &[Ix], index: &[Ix]) -> Option { + if index.len() != dim.len() { + return None; + } + let mut offset = 0; + for (&d, &i, &s) in zipsl(dim, index).zip_cons(strides) + { + if i >= d { + return None; + } + offset += stride_offset(i, s); + } + Some(offset) +} + +/// Implementation-specific extensions to `Dimension` +pub trait DimensionExt { +// note: many extensions go in the main trait if they need to be special- +// cased per dimension + /// Get the dimension at `axis`. + /// + /// *Panics* if `axis` is out of bounds. + #[inline] + fn axis(&self, axis: Axis) -> Ix; + + /// Set the dimension at `axis`. + /// + /// *Panics* if `axis` is out of bounds. + #[inline] + fn set_axis(&mut self, axis: Axis, value: Ix); +} + +impl DimensionExt for D + where D: Dimension +{ + #[inline] + fn axis(&self, axis: Axis) -> Ix { + self[axis.axis()] + } + + #[inline] + fn set_axis(&mut self, axis: Axis, value: Ix) { + self[axis.axis()] = value; + } +} + +impl<'a> DimensionExt for [Ix] +{ + #[inline] + fn axis(&self, axis: Axis) -> Ix { + self[axis.axis()] + } + + #[inline] + fn set_axis(&mut self, axis: Axis, value: Ix) { + self[axis.axis()] = value; + } +} + +/// Collapse axis `axis` and shift so that only subarray `index` is +/// available. +/// +/// **Panics** if `index` is larger than the size of the axis +// FIXME: Move to Dimension trait +pub fn do_sub(dims: &mut D, ptr: &mut *mut A, strides: &D, + axis: usize, index: Ix) { + let dim = dims.slice()[axis]; + let stride = strides.slice()[axis]; + assert!(index < dim); + dims.slice_mut()[axis] = 1; + let off = stride_offset(index, stride); + unsafe { + *ptr = ptr.offset(off); + } +} + + + + +// NOTE: These tests are not compiled & tested +#[cfg(test)] +mod test { + use super::Dimension; + use error::{from_kind, ErrorKind}; + + #[test] + fn slice_indexing_uncommon_strides() { + let v: Vec<_> = (0..12).collect(); + let dim = (2, 3, 2); + let strides = (1, 2, 6); + assert!(super::can_index_slice(&v, &dim, &strides).is_ok()); + + let strides = (2, 4, 12); + assert_eq!(super::can_index_slice(&v, &dim, &strides), + Err(from_kind(ErrorKind::OutOfBounds))); + } + + #[test] + fn overlapping_strides_dim() { + let dim = (2, 3, 2); + let strides = (5, 2, 1); + assert!(super::dim_stride_overlap(&dim, &strides)); + let strides = (6, 2, 1); + assert!(!super::dim_stride_overlap(&dim, &strides)); + let strides = (6, 0, 1); + assert!(super::dim_stride_overlap(&dim, &strides)); + } +} + diff --git a/src/dimension/ndindex.rs b/src/dimension/ndindex.rs new file mode 100644 index 000000000..65096d171 --- /dev/null +++ b/src/dimension/ndindex.rs @@ -0,0 +1,232 @@ + +use std::fmt::Debug; + +use itertools::zip; + +use {Ix, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, Dim, Dimension, IntoDimension}; +use super::{stride_offset, stride_offset_checked}; +use super::DimPrivate; + +/// Tuple or fixed size arrays that can be used to index an array. +/// +/// ``` +/// use ndarray::arr2; +/// +/// let mut a = arr2(&[[0, 1], +/// [2, 3]]); +/// assert_eq!(a[[0, 1]], 1); +/// assert_eq!(a[[1, 1]], 3); +/// a[[1, 1]] += 1; +/// assert_eq!(a[(1, 1)], 4); +/// ``` +/// +/// **Note** that `NdIndex` is implemented for all `D where D: Dimension`. +pub unsafe trait NdIndex : Debug { + #[doc(hidden)] + fn index_checked(&self, dim: &E, strides: &E) -> Option; + fn index_unchecked(&self, strides: &E) -> isize; +} + +unsafe impl NdIndex for D + where D: Dimension +{ + fn index_checked(&self, dim: &D, strides: &D) -> Option { + dim.stride_offset_checked(strides, self) + } + fn index_unchecked(&self, strides: &D) -> isize { + D::stride_offset(self, strides) + } +} + +unsafe impl NdIndex for () { + #[inline] + fn index_checked(&self, dim: &Ix0, strides: &Ix0) -> Option { + dim.stride_offset_checked(strides, &Ix0()) + } + #[inline(always)] + fn index_unchecked(&self, _strides: &Ix0) -> isize { + 0 + } +} + +unsafe impl NdIndex for (Ix, Ix) { + #[inline] + fn index_checked(&self, dim: &Ix2, strides: &Ix2) -> Option { + dim.stride_offset_checked(strides, &Ix2(self.0, self.1)) + } + #[inline] + fn index_unchecked(&self, strides: &Ix2) -> isize { + stride_offset(self.0, get!(strides, 0)) + + stride_offset(self.1, get!(strides, 1)) + } +} +unsafe impl NdIndex for (Ix, Ix, Ix) { + #[inline] + fn index_checked(&self, dim: &Ix3, strides: &Ix3) -> Option { + dim.stride_offset_checked(strides, &self.into_dimension()) + } + + #[inline] + fn index_unchecked(&self, strides: &Ix3) -> isize { + stride_offset(self.0, get!(strides, 0)) + + stride_offset(self.1, get!(strides, 1)) + + stride_offset(self.2, get!(strides, 2)) + } +} + +unsafe impl NdIndex for (Ix, Ix, Ix, Ix) { + #[inline] + fn index_checked(&self, dim: &Ix4, strides: &Ix4) -> Option { + dim.stride_offset_checked(strides, &self.into_dimension()) + } + #[inline] + fn index_unchecked(&self, strides: &Ix4) -> isize { + zip(strides.ix(), self.into_dimension().ix()).map(|(&s, &i)| stride_offset(i, s)).sum() + } +} +unsafe impl NdIndex for (Ix, Ix, Ix, Ix, Ix) { + #[inline] + fn index_checked(&self, dim: &Ix5, strides: &Ix5) -> Option { + dim.stride_offset_checked(strides, &self.into_dimension()) + } + #[inline] + fn index_unchecked(&self, strides: &Ix5) -> isize { + zip(strides.ix(), self.into_dimension().ix()).map(|(&s, &i)| stride_offset(i, s)).sum() + } +} + +unsafe impl NdIndex for Ix { + #[inline] + fn index_checked(&self, dim: &Ix1, strides: &Ix1) -> Option { + dim.stride_offset_checked(strides, &Ix1(*self)) + } + #[inline(always)] + fn index_unchecked(&self, strides: &Ix1) -> isize { + stride_offset(*self, get!(strides, 0)) + } +} + +unsafe impl NdIndex for Ix { + #[inline] + fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { + debug_assert_eq!(dim.ndim(), 1); + stride_offset_checked(dim.ix(), strides.ix(), &[*self]) + } + #[inline(always)] + fn index_unchecked(&self, strides: &IxDyn) -> isize { + debug_assert_eq!(strides.ndim(), 1); + stride_offset(*self, get!(strides, 0)) + } +} + +unsafe impl NdIndex for Ix1 { + #[inline] + fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { + debug_assert_eq!(dim.ndim(), 1); + stride_offset_checked(dim.ix(), strides.ix(), self.ix()) + } + #[inline(always)] + fn index_unchecked(&self, strides: &IxDyn) -> isize { + debug_assert_eq!(strides.ndim(), 1); + stride_offset(get!(self, 0), get!(strides, 0)) + } +} + +macro_rules! ndindex_with_array { + ($([$n:expr, $ix_n:ident $($index:tt)*])+) => { + $( + // implement NdIndex for [Ix; 2] and so on + unsafe impl NdIndex<$ix_n> for [Ix; $n] { + #[inline] + fn index_checked(&self, dim: &$ix_n, strides: &$ix_n) -> Option { + dim.stride_offset_checked(strides, &self.into_dimension()) + } + + #[inline] + fn index_unchecked(&self, strides: &$ix_n) -> isize { + $( + stride_offset(self[$index], get!(strides, $index)) + + )+ + 0 + } + } + + // implement NdIndex for Dim<[Ix; 2]> and so on + unsafe impl NdIndex for Dim<[Ix; $n]> { + #[inline] + fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { + debug_assert!(strides.ndim() == $n, + "Attempted to index with {:?} in array with {} axes", + self, strides.ndim()); + stride_offset_checked(dim.ix(), strides.ix(), self.ix()) + } + + #[inline] + fn index_unchecked(&self, strides: &IxDyn) -> isize { + debug_assert!(strides.ndim() == $n, + "Attempted to index with {:?} in array with {} axes", + self, strides.ndim()); + $( + stride_offset(get!(self, $index), get!(strides, $index)) + + )+ + 0 + } + } + + // implement NdIndex for [Ix; 2] and so on + unsafe impl NdIndex for [Ix; $n] { + #[inline] + fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { + debug_assert!(strides.ndim() == $n, + "Attempted to index with {:?} in array with {} axes", + self, strides.ndim()); + stride_offset_checked(dim.ix(), strides.ix(), self) + } + + #[inline] + fn index_unchecked(&self, strides: &IxDyn) -> isize { + debug_assert!(strides.ndim() == $n, + "Attempted to index with {:?} in array with {} axes", + self, strides.ndim()); + $( + stride_offset(self[$index], get!(strides, $index)) + + )+ + 0 + } + } + )+ + }; +} + +ndindex_with_array!{ + [2, Ix2 0 1] + [3, Ix3 0 1 2] + [4, Ix4 0 1 2 3] + [5, Ix5 0 1 2 3 4] + [6, Ix6 0 1 2 3 4 5] +} + +impl<'a> IntoDimension for &'a [Ix] { + type Dim = Dim>; + fn into_dimension(self) -> Self::Dim { + Dim(self.to_vec()) + } +} + +unsafe impl<'a> NdIndex for &'a [Ix] { + fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { + stride_offset_checked(dim.ix(), strides.ix(), *self) + } + fn index_unchecked(&self, strides: &IxDyn) -> isize { + zip(strides.ix(), *self).map(|(&s, &i)| stride_offset(i, s)).sum() + } +} + +unsafe impl NdIndex for Vec { + fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { + 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() + } +} diff --git a/src/dimension/remove_axis.rs b/src/dimension/remove_axis.rs new file mode 100644 index 000000000..9b26056c3 --- /dev/null +++ b/src/dimension/remove_axis.rs @@ -0,0 +1,77 @@ +// Copyright 2014-2016 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +use {Ix, Ix0, Ix1, Dimension, Dim, Axis}; +use super::DimPrivate; + +/// Array shape with a next smaller dimension. +/// +/// `RemoveAxis` defines a larger-than relation for array shapes: +/// removing one axis from *Self* gives smaller dimension *Smaller*. +pub trait RemoveAxis : Dimension { + type Smaller: Dimension; + fn remove_axis(&self, axis: Axis) -> Self::Smaller; +} + +impl RemoveAxis for Dim<[Ix; 1]> { + type Smaller = Ix0; + #[inline] + fn remove_axis(&self, _: Axis) -> Ix0 { Ix0() } +} + +impl RemoveAxis for Dim<[Ix; 2]> { + type Smaller = Ix1; + #[inline] + fn remove_axis(&self, axis: Axis) -> Ix1 { + let axis = axis.axis(); + debug_assert!(axis < self.ndim()); + if axis == 0 { Ix1(get!(self, 1)) } else { Ix1(get!(self, 0)) } + } +} + +macro_rules! impl_remove_axis_array( + ($($n:expr),*) => ( + $( + impl RemoveAxis for Dim<[Ix; $n]> + { + type Smaller = Dim<[Ix; $n - 1]>; + #[inline] + fn remove_axis(&self, axis: Axis) -> Self::Smaller { + let mut tup = Dim([0; $n - 1]); + { + let mut it = tup.slice_mut().iter_mut(); + for (i, &d) in self.slice().iter().enumerate() { + if i == axis.axis() { + continue; + } + for rr in it.by_ref() { + *rr = d; + break + } + } + } + tup + } + } + )* + ); +); + +impl_remove_axis_array!(3, 4, 5, 6); + + +impl RemoveAxis for Dim> { + type Smaller = Self; + fn remove_axis(&self, axis: Axis) -> Self { + let mut res = self.clone(); + res.ixm().remove(axis.axis()); + res + } +} + diff --git a/src/free_functions.rs b/src/free_functions.rs index 32837c28d..deae37752 100644 --- a/src/free_functions.rs +++ b/src/free_functions.rs @@ -28,7 +28,7 @@ pub fn rcarr1(xs: &[A]) -> RcArray { /// Create a zero-dimensional array view borrowing `x`. pub fn aview0(x: &A) -> ArrayView0 { - unsafe { ArrayView::new_(x, (), ()) } + unsafe { ArrayView::new_(x, Ix0(), Ix0()) } } /// Create a one-dimensional array view with elements borrowing `xs`. @@ -56,7 +56,7 @@ pub fn aview2>(xs: &[V]) -> ArrayView2 { let data = unsafe { slice::from_raw_parts(xs.as_ptr() as *const A, cols * rows) }; - let dim = (rows as Ix, cols as Ix); + let dim = Ix2(rows, cols); unsafe { let strides = dim.default_strides(); ArrayView::new_(data.as_ptr(), dim, strides) @@ -122,8 +122,8 @@ impl_arr_init!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,); /// ); /// ``` pub fn arr2>(xs: &[V]) -> Array2 { - let (m, n) = (xs.len() as Ix, V::len() as Ix); - let dim = (m, n); + let (m, n) = (xs.len(), V::len()); + let dim = Ix2(m, n); let mut result = Vec::::with_capacity(dim.size()); for snd in xs { result.extend_from_slice(snd.as_init_slice()); @@ -159,7 +159,7 @@ pub fn rcarr2>(xs: &[V]) -> RcArray, U: FixedInitializer>(xs: &[V]) -> Array3 { - let dim = (xs.len() as Ix, V::len() as Ix, U::len() as Ix); + let dim = Ix3(xs.len(), V::len(), U::len()); let mut result = Vec::::with_capacity(dim.size()); for snd in xs { for thr in snd.as_init_slice() { diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 5eb989e34..c27ed3855 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -13,19 +13,21 @@ use libnum::{Zero, One, Float}; use imp_prelude::*; -use {Shape, StrideShape}; +use StrideShape; use dimension; use linspace; use error::{self, ShapeError, ErrorKind}; use Indexes; use iterators::{to_vec, to_vec_mapped}; +/// # Constructor Methods for Owned Arrays +/// /// Constructor methods for one-dimensional arrays. /// /// Note that the constructor methods apply to `Array` and `RcArray`, /// the two array types that have owned storage. -impl ArrayBase - where S: DataOwned +impl ArrayBase + where S: DataOwned, { /// Create a one-dimensional array from a vector (no copying needed). /// @@ -34,7 +36,7 @@ impl ArrayBase /// /// let array = Array::from_vec(vec![1., 2., 3., 4.]); /// ``` - pub fn from_vec(v: Vec) -> Self { + pub fn from_vec(v: Vec) -> Self { unsafe { Self::from_shape_vec_unchecked(v.len() as Ix, v) } } @@ -47,13 +49,13 @@ impl ArrayBase /// assert!(array == arr1(&[0, 1, 4, 9, 16])) /// ``` pub fn from_iter(iterable: I) -> Self - where I: IntoIterator + where I: IntoIterator { Self::from_vec(iterable.into_iter().collect()) } /// Create a one-dimensional array from the inclusive interval - /// `[start, end]` with `n` elements. `F` must be a floating point type. + /// `[start, end]` with `n` elements. `A` must be a floating point type. /// /// ```rust /// use ndarray::{Array, arr1}; @@ -61,15 +63,15 @@ impl ArrayBase /// let array = Array::linspace(0., 1., 5); /// assert!(array == arr1(&[0.0, 0.25, 0.5, 0.75, 1.0])) /// ``` - pub fn linspace(start: F, end: F, n: usize) -> Self - where S: Data, - F: Float, + pub fn linspace(start: A, end: A, n: usize) -> Self + where A: Float, { Self::from_vec(to_vec(linspace::linspace(start, end, n))) } /// Create a one-dimensional array from the half-open interval - /// `[start, end)` with elements spaced by `step`. `F` must be a floating point type. + /// `[start, end)` with elements spaced by `step`. `A` must be a floating + /// point type. /// /// ```rust /// use ndarray::{Array, arr1}; @@ -77,9 +79,8 @@ impl ArrayBase /// let array = Array::range(0., 5., 1.); /// assert!(array == arr1(&[0., 1., 2., 3., 4.])) /// ``` - pub fn range(start: F, end: F, step: F) -> Self - where S: Data, - F: Float, + pub fn range(start: A, end: A, step: A) -> Self + where A: Float, { Self::from_vec(to_vec(linspace::range(start, end, step))) } @@ -140,12 +141,12 @@ impl ArrayBase /// ``` pub fn from_elem(shape: Sh, elem: A) -> Self where A: Clone, - Sh: Into>, + Sh: ShapeBuilder, { // Note: We don't need to check the case of a size between // isize::MAX -> usize::MAX; in this case, the vec constructor itself // panics. - let shape = shape.into(); + let shape = shape.into_shape(); let size = size_checked_unwrap!(shape.dim); let v = vec![elem; size]; unsafe { Self::from_shape_vec_unchecked(shape, v) } @@ -156,7 +157,7 @@ impl ArrayBase /// **Panics** if the number of elements in `shape` would overflow usize. pub fn zeros(shape: Sh) -> Self where A: Clone + Zero, - Sh: Into>, + Sh: ShapeBuilder, { Self::from_elem(shape, A::zero()) } @@ -166,9 +167,9 @@ impl ArrayBase /// **Panics** if the number of elements in `shape` would overflow usize. pub fn default(shape: Sh) -> Self where A: Default, - Sh: Into>, + Sh: ShapeBuilder, { - let shape = shape.into(); + let shape = shape.into_shape(); let v = to_vec((0..shape.dim.size()).map(|_| A::default())); unsafe { Self::from_shape_vec_unchecked(shape, v) } } @@ -179,10 +180,10 @@ impl ArrayBase /// /// **Panics** if the number of elements in `shape` would overflow usize. pub fn from_shape_fn(shape: Sh, f: F) -> Self - where Sh: Into>, - F: FnMut(D) -> A, + where Sh: ShapeBuilder, + F: FnMut(D::Pattern) -> A, { - let shape = shape.into(); + let shape = shape.into_shape(); let v = to_vec_mapped(Indexes::new(shape.dim.clone()), f); unsafe { Self::from_shape_vec_unchecked(shape, v) } } @@ -204,13 +205,15 @@ impl ArrayBase /// **Errors** if strides allow multiple indices to point to the same element. /// /// ``` - /// use ndarray::prelude::*; + /// use ndarray::Array; + /// use ndarray::ShapeBuilder; // Needed for .strides() method + /// use ndarray::arr2; /// /// let a = Array::from_shape_vec((2, 2), vec![1., 2., 3., 4.]); /// assert!(a.is_ok()); /// /// let b = Array::from_shape_vec((2, 2).strides((1, 2)), - /// vec![1., 2., 3., 4.]).unwrap(); + /// vec![1., 2., 3., 4.]).unwrap(); /// assert!( /// b == arr2(&[[1., 3.], /// [2., 4.]]) @@ -231,7 +234,7 @@ impl ArrayBase let dim = shape.dim; let strides = shape.strides; if dim.size_checked() != Some(v.len()) { - return Err(error::incompatible_shapes(&v.len(), &dim)); + return Err(error::incompatible_shapes(&Ix1(v.len()), &dim)); } unsafe { Ok(Self::from_vec_dim_stride_unchecked(dim, strides, v)) } } diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 29af2a677..1c117870d 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -20,6 +20,7 @@ use iterators; use error::{self, ShapeError}; use super::zipsl; use super::ZipExt; +use dimension::IntoDimension; use { NdIndex, @@ -36,6 +37,7 @@ use { }; use stacking::stack; +/// # Methods For All Array Types impl ArrayBase where S: Data, D: Dimension { /// Return the total number of elements in the array. @@ -46,6 +48,12 @@ impl ArrayBase where S: Data, D: Dimension /// Return the shape of the array. pub fn dim(&self) -> D { self.dim.clone() + //self.dim.as_tuple() + } + + /// Return the shape of the array as the "pattern" type (usually a tuple). + pub fn dim_pattern(&self) -> D::Pattern { + self.dim.clone().into_pattern() } /// Return the shape of the array as a slice. @@ -141,14 +149,14 @@ impl ArrayBase where S: Data, D: Dimension /// Return an iterator of indexes and references to the elements of the array. /// - /// Iterator element type is `(D, &A)`. + /// Iterator element type is `(D::Pattern, &A)`. pub fn indexed_iter(&self) -> Indexed { Indexed(self.view().into_elements_base()) } /// Return an iterator of indexes and mutable references to the elements of the array. /// - /// Iterator element type is `(D, &mut A)`. + /// Iterator element type is `(D::Pattern, &mut A)`. pub fn indexed_iter_mut(&mut self) -> IndexedMut where S: DataMut, { @@ -224,7 +232,7 @@ impl ArrayBase where S: Data, D: Dimension /// ); /// ``` pub fn get(&self, index: I) -> Option<&A> - where I: NdIndex, + where I: NdIndex, { let ptr = self.ptr; index.index_checked(&self.dim, &self.strides) @@ -235,7 +243,7 @@ impl ArrayBase where S: Data, D: Dimension /// if the index is out of bounds. pub fn get_mut(&mut self, index: I) -> Option<&mut A> where S: DataMut, - I: NdIndex, + I: NdIndex, { let ptr = self.as_mut_ptr(); index.index_checked(&self.dim, &self.strides) @@ -248,9 +256,11 @@ impl ArrayBase where S: Data, D: Dimension /// /// **Note:** only unchecked for non-debug builds of ndarray. #[inline] - pub unsafe fn uget(&self, index: D) -> &A { + pub unsafe fn uget(&self, index: I) -> &A + where I: NdIndex, + { arraytraits::debug_bounds_check(self, &index); - let off = D::stride_offset(&index, &self.strides); + let off = index.index_unchecked(&self.strides); &*self.ptr.offset(off) } @@ -261,12 +271,13 @@ impl ArrayBase where S: Data, D: Dimension /// **Note:** Only unchecked for non-debug builds of ndarray.
/// **Note:** The array must be uniquely held when mutating it. #[inline] - pub unsafe fn uget_mut(&mut self, index: D) -> &mut A - where S: DataMut + pub unsafe fn uget_mut(&mut self, index: I) -> &mut A + where S: DataMut, + I: NdIndex, { debug_assert!(self.data.is_unique()); arraytraits::debug_bounds_check(self, &index); - let off = D::stride_offset(&index, &self.strides); + let off = index.index_unchecked(&self.strides); &mut *self.ptr.offset(off) } @@ -277,7 +288,7 @@ impl ArrayBase where S: Data, D: Dimension /// ***Panics*** if an index is out of bounds. pub fn swap(&mut self, index1: I, index2: I) where S: DataMut, - I: NdIndex, + I: NdIndex, { let ptr1: *mut _ = &mut self[index1]; let ptr2: *mut _ = &mut self[index2]; @@ -513,9 +524,9 @@ impl ArrayBase where S: Data, D: Dimension /// Return an iterator that traverses over `axis` /// and yields each subview along it. /// - /// For example, in a 3 × 5 × 5 array, with `axis` equal to `Axis(2)`, + /// For example, in a 3 × 4 × 5 array, with `axis` equal to `Axis(2)`, /// the iterator element - /// is a 3 × 5 subview (and there are 5 in total), as shown + /// is a 3 × 4 subview (and there are 5 in total), as shown /// in the picture below. /// /// Iterator element is `ArrayView` (read-only array view). @@ -524,7 +535,7 @@ impl ArrayBase where S: Data, D: Dimension /// /// **Panics** if `axis` is out of bounds. /// - /// + /// pub fn axis_iter(&self, axis: Axis) -> AxisIter where D: RemoveAxis, { @@ -621,8 +632,8 @@ impl ArrayBase where S: Data, D: Dimension ArrayBase { data: self.data, ptr: self.ptr, - dim: len, - strides: stride as Ix, + dim: Ix1(len), + strides: Ix1(stride as Ix), } } @@ -644,7 +655,7 @@ impl ArrayBase where S: Data, D: Dimension /// contiguous in memory, it has custom strides, etc. pub fn is_standard_layout(&self) -> bool { let defaults = self.dim.default_strides(); - if self.strides == defaults { + if self.strides.equal(&defaults) { return true; } if self.ndim() == 1 { return false; } @@ -660,25 +671,7 @@ impl ArrayBase where S: Data, D: Dimension } fn is_contiguous(&self) -> bool { - let defaults = self.dim.default_strides(); - if self.strides == defaults { - return true; - } - if self.ndim() == 1 { return false; } - let order = self.strides._fastest_varying_stride_order(); - let strides = self.strides.slice(); - - // FIXME: Negative strides - let dim = self.dim.slice(); - let mut cstride = 1; - for &i in order.slice() { - // a dimension of length 1 can have unequal strides - if dim[i] != 1 && strides[i] != cstride { - return false; - } - cstride *= dim[i]; - } - true + D::is_contiguous(&self.dim, &self.strides) } /// Return a pointer to the first element in the array. @@ -783,11 +776,12 @@ impl ArrayBase where S: Data, D: Dimension /// [3., 4.]]) /// ); /// ``` - pub fn reshape(&self, shape: E) -> ArrayBase + pub fn reshape(&self, shape: E) -> ArrayBase where S: DataShared + DataOwned, A: Clone, - E: Dimension, + E: IntoDimension, { + let shape = shape.into_dimension(); if shape.size_checked() != Some(self.dim.size()) { panic!("ndarray: incompatible shapes in reshape, attempted from: {:?}, to: {:?}", self.dim.slice(), @@ -826,9 +820,10 @@ impl ArrayBase where S: Data, D: Dimension /// [3., 4.]]) /// ); /// ``` - pub fn into_shape(self, shape: E) -> Result, ShapeError> - where E: Dimension + pub fn into_shape(self, shape: E) -> Result, ShapeError> + where E: IntoDimension, { + let shape = shape.into_dimension(); if shape.size_checked() != Some(self.dim.size()) { return Err(error::incompatible_shapes(&self.dim, &shape)); } @@ -881,8 +876,8 @@ impl ArrayBase where S: Data, D: Dimension /// == aview2(&[[1., 0.]; 10]) /// ); /// ``` - pub fn broadcast(&self, dim: E) -> Option> - where E: Dimension + pub fn broadcast(&self, dim: E) -> Option> + where E: IntoDimension { /// Return new stride when trying to grow `from` into shape `to` /// @@ -901,9 +896,10 @@ impl ArrayBase where S: Data, D: Dimension } { + let mut new_stride_iter = new_stride.slice_mut().iter_mut().rev(); for ((er, es), dr) in from.slice().iter().rev() .zip(stride.slice().iter().rev()) - .zip(new_stride.slice_mut().iter_mut().rev()) + .zip(new_stride_iter.by_ref()) { /* update strides */ if *dr == *er { @@ -918,13 +914,13 @@ impl ArrayBase where S: Data, D: Dimension } /* set remaining strides to zero */ - let tail_len = to.ndim() - from.ndim(); - for dr in &mut new_stride.slice_mut()[..tail_len] { + for dr in new_stride_iter { *dr = 0; } } Some(new_stride) } + let dim = dim.into_dimension(); // Note: zero strides are safe precisely because we return an read-only view let broadcast_strides = match upcast(&dim, &self.dim, &self.strides) { @@ -1050,8 +1046,8 @@ impl ArrayBase where S: Data, D: Dimension } // otherwise, break the arrays up into their inner rows let mut try_slices = true; - let mut rows = self.inner_iter_mut().zip(rhs.inner_iter()); - for (mut s_row, r_row) in &mut rows { + let rows = self.inner_iter_mut().zip(rhs.inner_iter()); + for (mut s_row, r_row) in rows { if try_slices { if let Some(self_s) = s_row.as_slice_mut() { if let Some(rhs_s) = r_row.as_slice() { @@ -1288,7 +1284,7 @@ impl ArrayBase where S: Data, D: Dimension // the 0th element. self.subview(axis, 0).map(|first_elt| { unsafe { - mapping(ArrayView::new_(first_elt, view_len, view_stride)) + mapping(ArrayView::new_(first_elt, Ix1(view_len), Ix1(view_stride))) } }) } diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 6cc0ddfcf..7bba676a8 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -40,15 +40,13 @@ impl ScalarOperand for i32 { } impl ScalarOperand for u32 { } impl ScalarOperand for i64 { } impl ScalarOperand for u64 { } +impl ScalarOperand for isize { } +impl ScalarOperand for usize { } impl ScalarOperand for f32 { } impl ScalarOperand for f64 { } impl ScalarOperand for Complex { } impl ScalarOperand for Complex { } -macro_rules! as_expr( - ($e:expr) => ($e) -); - macro_rules! impl_binary_op( ($trt:ident, $operator:tt, $mth:ident, $iop:tt, $doc:expr) => ( /// Perform elementwise @@ -94,7 +92,7 @@ impl<'a, A, S, S2, D, E> $trt<&'a ArrayBase> for ArrayBase fn $mth(mut self, rhs: &ArrayBase) -> ArrayBase { self.zip_mut_with(rhs, |x, y| { - *x = as_expr!(x.clone() $operator y.clone()); + *x = x.clone() $operator y.clone(); }); self } @@ -137,7 +135,7 @@ impl $trt for ArrayBase type Output = ArrayBase; fn $mth(mut self, x: B) -> ArrayBase { self.unordered_foreach_mut(move |elt| { - *elt = as_expr!(elt.clone() $operator x.clone()); + *elt = elt.clone() $operator x.clone(); }); self } @@ -186,7 +184,7 @@ impl $trt> for $scalar } or {{ let mut rhs = rhs; rhs.unordered_foreach_mut(move |elt| { - *elt = as_expr!(self $operator *elt); + *elt = self $operator *elt; }); rhs }}) diff --git a/src/impl_views.rs b/src/impl_views.rs index 7a0577cce..a4743848a 100644 --- a/src/impl_views.rs +++ b/src/impl_views.rs @@ -12,7 +12,7 @@ use error::ShapeError; use StrideShape; -/// # Methods for Array Views +/// # Methods Specific to Array Views /// /// Methods for read-only array views `ArrayView<'a, A, D>` /// diff --git a/src/indexes.rs b/src/indexes.rs index 855b082ac..7744b9d9e 100644 --- a/src/indexes.rs +++ b/src/indexes.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. use super::Dimension; +use dimension::IntoDimension; /// An iterator over the indexes of an array shape. /// @@ -18,7 +19,10 @@ pub struct Indexes { impl Indexes { /// Create an iterator over the array shape `dim`. - pub fn new(dim: D) -> Indexes { + pub fn new(shape: E) -> Indexes + where E: IntoDimension, + { + let dim = shape.into_dimension(); Indexes { index: dim.first_index(), dim: dim, @@ -29,15 +33,15 @@ impl Indexes { impl Iterator for Indexes where D: Dimension, { - type Item = D; + type Item = D::Pattern; #[inline] - fn next(&mut self) -> Option { + fn next(&mut self) -> Option { let index = match self.index { None => return None, Some(ref ix) => ix.clone(), }; self.index = self.dim.next_for(index.clone()); - Some(index) + Some(index.into_pattern()) } fn size_hint(&self) -> (usize, Option) { diff --git a/src/iterators.rs b/src/iterators.rs index 65f5f40b7..763d3123e 100644 --- a/src/iterators.rs +++ b/src/iterators.rs @@ -96,7 +96,7 @@ impl<'a, A, D: Dimension> Baseiter<'a, A, D> { where G: FnMut(Acc, *mut A) -> Acc, { let ndim = self.dim.ndim(); - assert!(ndim > 0); + debug_assert!(ndim > 0); let mut accum = init; loop { if let Some(mut index) = self.index.clone() { @@ -104,12 +104,13 @@ impl<'a, A, D: Dimension> Baseiter<'a, A, D> { let elem_index = index.last_elem(); let len = self.dim.last_elem(); let offset = D::stride_offset(&index, &self.strides); - for i in 0..len - elem_index { - unsafe { - accum = g(accum, self.ptr.offset(offset + i as isize * stride)); + unsafe { + let row_ptr = self.ptr.offset(offset); + for i in 0..(len - elem_index) { + accum = g(accum, row_ptr.offset(i as isize * stride)); } } - index.slice_mut()[ndim - 1] = len - 1; + index.set_last_elem(len - 1); self.index = self.dim.next_for(index); } else { break; @@ -126,7 +127,7 @@ impl<'a, A> Baseiter<'a, A, Ix1> { None => return None, Some(ix) => ix, }; - self.dim -= 1; + self.dim[0] -= 1; let offset = <_>::stride_offset(&self.dim, &self.strides); if index == self.dim { self.index = None; @@ -180,7 +181,7 @@ impl<'a, A, D: Dimension> Iterator for ElementsBase<'a, A, D> { where G: FnMut(Acc, Self::Item) -> Acc, { unsafe { - self.inner.fold(init, |acc, ptr| g(acc, &*ptr)) + self.inner.fold(init, move |acc, ptr| g(acc, &*ptr)) } } } @@ -259,16 +260,16 @@ impl<'a, A, D> ExactSizeIterator for Elements<'a, A, D> impl<'a, A, D: Dimension> Iterator for Indexed<'a, A, D> { - type Item = (D, &'a A); + type Item = (D::Pattern, &'a A); #[inline] - fn next(&mut self) -> Option<(D, &'a A)> { + fn next(&mut self) -> Option { let index = match self.0.inner.index { None => return None, Some(ref ix) => ix.clone(), }; match self.0.inner.next_ref() { None => None, - Some(p) => Some((index, p)), + Some(p) => Some((index.into_pattern(), p)), } } @@ -336,16 +337,16 @@ impl<'a, A> DoubleEndedIterator for ElementsBaseMut<'a, A, Ix1> { } impl<'a, A, D: Dimension> Iterator for IndexedMut<'a, A, D> { - type Item = (D, &'a mut A); + type Item = (D::Pattern, &'a mut A); #[inline] - fn next(&mut self) -> Option<(D, &'a mut A)> { + fn next(&mut self) -> Option { let index = match self.0.inner.index { None => return None, Some(ref ix) => ix.clone(), }; match self.0.inner.next_ref_mut() { None => None, - Some(p) => Some((index, p)), + Some(p) => Some((index.into_pattern(), p)), } } @@ -368,7 +369,8 @@ pub struct InnerIter<'a, A: 'a, D> { pub fn new_inner_iter(mut v: ArrayView) -> InnerIter where D: Dimension { - if v.shape().len() == 0 { + let ndim = v.ndim(); + if ndim == 0 { InnerIter { inner_len: 1, inner_stride: 1, @@ -376,10 +378,9 @@ pub fn new_inner_iter(mut v: ArrayView) -> InnerIter } } else { // Set length of innerest dimension to 1, start iteration - let ndim = v.shape().len(); - let len = v.shape()[ndim - 1]; - let stride = v.strides()[ndim - 1]; - v.dim.slice_mut()[ndim - 1] = 1; + let len = v.dim.last_elem(); + let stride = v.strides.last_elem() as isize; + v.dim.set_last_elem(1); InnerIter { inner_len: len, inner_stride: stride, @@ -394,7 +395,7 @@ impl<'a, A, D> Iterator for InnerIter<'a, A, D> type Item = ArrayView<'a, A, Ix1>; fn next(&mut self) -> Option { self.iter.next().map(|ptr| { - unsafe { ArrayView::new_(ptr, self.inner_len, self.inner_stride as Ix) } + unsafe { ArrayView::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) } }) } @@ -425,7 +426,8 @@ pub struct InnerIterMut<'a, A: 'a, D> { pub fn new_inner_iter_mut(mut v: ArrayViewMut) -> InnerIterMut where D: Dimension, { - if v.shape().len() == 0 { + let ndim = v.ndim(); + if ndim == 0 { InnerIterMut { inner_len: 1, inner_stride: 1, @@ -433,10 +435,9 @@ pub fn new_inner_iter_mut(mut v: ArrayViewMut) -> InnerIterMut } } else { // Set length of innerest dimension to 1, start iteration - let ndim = v.shape().len(); - let len = v.shape()[ndim - 1]; - let stride = v.strides()[ndim - 1]; - v.dim.slice_mut()[ndim - 1] = 1; + let len = v.dim.last_elem(); + let stride = v.strides.last_elem() as isize; + v.dim.set_last_elem(1); InnerIterMut { inner_len: len, inner_stride: stride, @@ -452,7 +453,7 @@ impl<'a, A, D> Iterator for InnerIterMut<'a, A, D> fn next(&mut self) -> Option { self.iter.next().map(|ptr| { unsafe { - ArrayViewMut::new_(ptr, self.inner_len, self.inner_stride as Ix) + ArrayViewMut::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) } }) } diff --git a/src/lib.rs b/src/lib.rs index 5539674a1..eb5e25267 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,9 +87,11 @@ use std::marker::PhantomData; pub use dimension::{ Dimension, + IntoDimension, RemoveAxis, Axis, }; +pub use dimension::dim::*; pub use dimension::NdIndex; pub use indexes::Indexes; @@ -110,7 +112,7 @@ pub use arraytraits::AsArray; pub use linalg_traits::{LinalgScalar, NdFloat}; pub use stacking::stack; -pub use shape_builder::{ ShapeBuilder }; +pub use shape_builder::{ ShapeBuilder}; mod aliases; mod arraytraits; @@ -190,53 +192,112 @@ pub type Ixs = isize; /// /// ## Contents /// -/// + [Array and RcArray](#ownedarray-and-rcarray) +/// + [Array](#array) +/// + [RcArray](#rcarray) +/// + [Array Views](#array-views) /// + [Indexing and Dimension](#indexing-and-dimension) /// + [Slicing](#slicing) /// + [Subviews](#subviews) /// + [Arithmetic Operations](#arithmetic-operations) /// + [Broadcasting](#broadcasting) -/// + [Methods](#methods) -/// + [Methods for Array Views](#methods-for-array-views) +/// + [Constructor Methods for Owned Arrays](#constructor-methods-for-owned-arrays) +/// + [Methods For All Array Types](#methods-for-all-array-types) +/// + [Methods Specific to Array Views](#methods-specific-to-array-views) /// -/// ## `Array` and `RcArray` /// -/// `Array` owns the underlying array elements directly (just like -/// a `Vec`), while [`RcArray`](type.RcArray.html) is a an array with reference -/// counted data. `RcArray` can act both as an owner or as a view in that regard. +/// +/// +/// ## `Array` +/// +/// [`Array`](type.Array.html) is an owned array that ows the underlying array +/// elements directly (just like a `Vec`) and it is the default way to create and +/// store n-dimensional data. `Array` has two type parameters: `A` for +/// the element type, and `D` for the dimensionality. A particular +/// dimensionality's type alias like `Array3
` just has the type parameter +/// `A` for element type. +/// +/// An example: +/// +/// ``` +/// // Create a three-dimensional f64 array, initialized with zeros +/// use ndarray::Array3; +/// let mut temperature = Array3::::zeros((3, 4, 5)); +/// // Increase the temperature in this location +/// temperature[[2, 2, 2]] += 0.5; +/// ``` +/// +/// ## `RcArray` +/// +/// [`RcArray`](type.RcArray.html) is an owned array with reference counted +/// data (shared ownership). /// Sharing requires that it uses copy-on-write for mutable operations. /// Calling a method for mutating elements on `RcArray`, for example /// [`view_mut()`](#method.view_mut) or [`get_mut()`](#method.get_mut), /// will break sharing and require a clone of the data (if it is not uniquely held). /// +/// ## Array Views +/// +/// `ArrayView` and `ArrayViewMut` are read-only and read-write array views +/// respectively. They use dimensionality, indexing, and almost all other +/// methods the same was as the other array types. +/// +/// A view is created from an array using `.view()`, `.view_mut()`, using +/// slicing (`.slice()`, `.slice_mut()`) or from one of the many iterators +/// that yield array views. +/// +/// You can also create an array view from a regular slice of data not +/// allocated with `Array` — see [Methods Specific to Array +/// Views](#methods-specific-to-array-views). +/// /// Note that all `ArrayBase` variants can change their view (slicing) of the /// data freely, even when their data can’t be mutated. /// /// ## Indexing and Dimension /// -/// Array indexes are represented by the types `Ix` and `Ixs` (signed). -/// /// The dimensionality of the array determines the number of *axes*, for example /// a 2D array has two axes. These are listed in “big endian” order, so that /// the greatest dimension is listed first, the lowest dimension with the most /// rapidly varying index is the last. /// -/// In a 2D array the index of each element is `(row, column)` -/// as seen in this 3 × 3 example: +/// In a 2D array the index of each element is `[row, column]` as seen in this +/// 4 × 3 example: /// /// ```ignore -/// [[ (0, 0), (0, 1), (0, 2)], // row 0 -/// [ (1, 0), (1, 1), (1, 2)], // row 1 -/// [ (2, 0), (2, 1), (2, 2)]] // row 2 +/// [[ [0, 0], [0, 1], [0, 2] ], // row 0 +/// [ [1, 0], [1, 1], [1, 2] ], // row 1 +/// [ [2, 0], [2, 1], [2, 2] ], // row 2 +/// [ [3, 0], [3, 1], [3, 2] ]] // row 3 /// // \ \ \ /// // column 0 \ column 2 /// // column 1 /// ``` /// -/// The number of axes for an array is fixed by the `D` parameter: `Ix` for -/// a 1D array, `(Ix, Ix)` for a 2D array etc. The `D` type is also used -/// for element indices in `.get()` and `array[index]`. The dimension type `Vec` -/// allows a dynamic number of axes. +/// The number of axes for an array is fixed by its `D` type parameter: `Ix1` +/// for a 1D array, `Ix2` for a 2D array etc. The dimension type `IxDyn` allows +/// a dynamic number of axes. +/// +/// A fixed size array (`[usize; N]`) of the corresponding dimensionality is +/// used to index the `Array`, making the syntax `array[[` i, j, ...`]]` +/// +/// ``` +/// use ndarray::Array2; +/// let mut array = Array2::zeros((4, 3)); +/// array[[1, 1]] = 7; +/// ``` +/// +/// Important traits and types for dimension and indexing: +/// +/// - A [`Dim`](Dim.t.html) value represents a dimensionality or index. +/// - Trait [`Dimension`](Dimension.t.html) is implemented by all +/// dimensionalities. It defines many operations for dimensions and indices. +/// - Trait [`IntoDimension`](IntoDimension.t.html) is used to convert into a +/// `Dim` value. +/// - Trait [`ShapeBuilder`](ShapeBuilder.t.html) is an extension of +/// `IntoDimension` and is used when constructing an array. A shape describes +/// not just the extent of each axis but also their strides. +/// - Trait [`NdIndex`](NdIndex.t.html) is an extension of `Dimension` and is +/// for values that can be used with indexing syntax. +/// /// /// The default memory order of an array is *row major* order (a.k.a “c” order), /// where each row is contiguous in memory. @@ -434,7 +495,7 @@ pub type OwnedArray = ArrayBase, D>; /// /// Array views have all the methods of an array (see [`ArrayBase`][ab]). /// -/// See also specific [**Methods for Array Views**](struct.ArrayBase.html#methods-for-array-views). +/// See also [**Methods Specific To Array Views**](struct.ArrayBase.html#methods-specific-to-array-views) /// /// [ab]: struct.ArrayBase.html pub type ArrayView<'a, A, D> = ArrayBase, D>; @@ -445,7 +506,7 @@ pub type ArrayView<'a, A, D> = ArrayBase, D>; /// /// Array views have all the methods of an array (see [`ArrayBase`][ab]). /// -/// See also specific [**Methods for Array Views**](struct.ArrayBase.html#methods-for-array-views). +/// See also [**Methods Specific To Array Views**](struct.ArrayBase.html#methods-specific-to-array-views) /// /// [ab]: struct.ArrayBase.html pub type ArrayViewMut<'a, A, D> = ArrayBase, D>; diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs index 7e7b71080..42973128c 100644 --- a/src/linalg/impl_linalg.rs +++ b/src/linalg/impl_linalg.rs @@ -40,7 +40,7 @@ const GEMM_BLAS_CUTOFF: usize = 7; type blas_index = c_int; // blas index type -impl ArrayBase +impl ArrayBase where S: Data, { /// Compute the dot product of one-dimensional arrays. @@ -49,14 +49,14 @@ impl ArrayBase /// of complex operands, and thus not their inner product). /// /// **Panics** if the arrays are not of the same length. - pub fn dot(&self, rhs: &ArrayBase) -> A + pub fn dot(&self, rhs: &ArrayBase) -> A where S2: Data, A: LinalgScalar, { self.dot_impl(rhs) } - fn dot_generic(&self, rhs: &ArrayBase) -> A + fn dot_generic(&self, rhs: &ArrayBase) -> A where S2: Data, A: LinalgScalar, { @@ -77,7 +77,7 @@ impl ArrayBase } #[cfg(not(feature="blas"))] - fn dot_impl(&self, rhs: &ArrayBase) -> A + fn dot_impl(&self, rhs: &ArrayBase) -> A where S2: Data, A: LinalgScalar, { @@ -85,7 +85,7 @@ impl ArrayBase } #[cfg(feature="blas")] - fn dot_impl(&self, rhs: &ArrayBase) -> A + fn dot_impl(&self, rhs: &ArrayBase) -> A where S2: Data, A: LinalgScalar, { @@ -199,7 +199,7 @@ impl Dot> for ArrayBase { let a = self.view(); let b = b.view(); - let ((m, k), (k2, n)) = (a.dim(), b.dim()); + let ((m, k), (k2, n)) = (a.dim_pattern(), b.dim_pattern()); if k != k2 || m.checked_mul(n).is_none() { return dot_shape_error(m, k, k2, n); } @@ -245,15 +245,15 @@ fn general_dot_shape_error(m: usize, k: usize, k2: usize, n: usize, c1: usize, c /// Return a result array with shape *M*. /// /// **Panics** if shapes are incompatible. -impl Dot> for ArrayBase +impl Dot> for ArrayBase where S: Data, S2: Data, A: LinalgScalar, { - type Output = Array; - fn dot(&self, rhs: &ArrayBase) -> Array + type Output = Array; + fn dot(&self, rhs: &ArrayBase) -> Array { - let ((m, a), n) = (self.dim(), rhs.dim()); + let ((m, a), n) = (self.dim_pattern(), rhs.dim_pattern()); if a != n { return dot_shape_error(m, a, n, 1); } @@ -266,7 +266,7 @@ impl Dot> for ArrayBase for (i, rr) in enumerate(&mut res_elems) { unsafe { *rr = (0..a).fold(A::zero(), - move |s, k| s + *self.uget((i, k)) * *rhs.uget(k) + move |s, k| s + *self.uget(Ix2(i, k)) * *rhs.uget(k) ); } } @@ -312,7 +312,7 @@ fn mat_mul_impl(alpha: A, { // size cutoff for using BLAS let cut = GEMM_BLAS_CUTOFF; - let ((mut m, a), (_, mut n)) = (lhs.dim, rhs.dim); + let ((mut m, a), (_, mut n)) = (lhs.dim_pattern(), rhs.dim_pattern()); if !(m > cut || n > cut || a > cut) || !(same_type::() || same_type::()) { return mat_mul_general(alpha, lhs, rhs, beta, c); @@ -351,15 +351,15 @@ fn mat_mul_impl(alpha: A, && blas_row_major_2d::<$ty, _>(&c_) { let (m, k) = match lhs_trans { - CblasNoTrans => lhs_.dim(), + CblasNoTrans => lhs_.dim_pattern(), _ => { - let (rows, cols) = lhs_.dim(); + let (rows, cols) = lhs_.dim_pattern(); (cols, rows) } }; let n = match rhs_trans { - CblasNoTrans => rhs_.dim().1, - _ => rhs_.dim().0, + CblasNoTrans => rhs_.dim()[1], + _ => rhs_.dim()[0], }; // adjust strides, these may [1, 1] for column matrices let lhs_stride = cmp::max(lhs_.strides()[0] as blas_index, k as blas_index); @@ -403,7 +403,7 @@ fn mat_mul_general(alpha: A, c: &mut ArrayViewMut2) where A: LinalgScalar, { - let ((m, k), (_, n)) = (lhs.dim, rhs.dim); + let ((m, k), (_, n)) = (lhs.dim_pattern(), rhs.dim_pattern()); // common parameters for gemm let ap = lhs.as_ptr(); @@ -486,8 +486,8 @@ pub fn general_mat_mul(alpha: A, S3: DataMut, A: LinalgScalar, { - let ((m, k), (k2, n)) = (a.dim(), b.dim()); - let (m2, n2) = c.dim(); + let ((m, k), (k2, n)) = (a.dim_pattern(), b.dim_pattern()); + let (m2, n2) = c.dim_pattern(); if k != k2 || m != m2 || n != n2 { return general_dot_shape_error(m, k, k2, n, m2, n2); } @@ -511,7 +511,7 @@ fn cast_as(a: &A) -> B { } #[cfg(feature="blas")] -fn blas_compat_1d(a: &ArrayBase) -> bool +fn blas_compat_1d(a: &ArrayBase) -> bool where S: Data, A: Any, S::Elem: Any, @@ -552,7 +552,7 @@ fn blas_row_major_2d(a: &ArrayBase) -> bool { return false; } - let (m, n) = a.dim(); + let (m, n) = a.dim_pattern(); if m > blas_index::max_value() as usize || n > blas_index::max_value() as usize { diff --git a/src/shape_builder.rs b/src/shape_builder.rs index 5001f1d6d..d6fd71482 100644 --- a/src/shape_builder.rs +++ b/src/shape_builder.rs @@ -1,6 +1,7 @@ use Dimension; use {Shape, StrideShape}; +use dimension::IntoDimension; /// A trait for `Shape` and `D where D: Dimension` that allows /// customizing the memory layout (strides) of an array shape. @@ -9,35 +10,31 @@ use {Shape, StrideShape}; /// `Array::from_shape_vec`. pub trait ShapeBuilder { type Dim: Dimension; + type Strides; + fn into_shape(self) -> Shape; fn f(self) -> Shape; fn set_f(self, is_f: bool) -> Shape; - fn strides(self, strides: Self::Dim) -> StrideShape; + fn strides(self, strides: Self::Strides) -> StrideShape; } -impl From for Shape - where D: Dimension +impl From for StrideShape + where D: Dimension, + T: ShapeBuilder, { - fn from(d: D) -> Self { - Shape { - dim: d, - is_c: true, - } - } -} - -impl From for StrideShape - where D: Dimension -{ - fn from(d: D) -> Self { + fn from(value: T) -> Self { + let shape = value.into_shape(); + let d = shape.dim; + let st = if shape.is_c { d.default_strides() } else { d.fortran_strides() }; StrideShape { - strides: d.default_strides(), + strides: st, dim: d, custom: false, } } } +/* impl From> for StrideShape where D: Dimension { @@ -51,17 +48,25 @@ impl From> for StrideShape } } } +*/ -impl ShapeBuilder for D - where D: Dimension +impl ShapeBuilder for T + where T: IntoDimension { - type Dim = D; - fn f(self) -> Shape { self.set_f(true) } - fn set_f(self, is_f: bool) -> Shape { - Shape::from(self).set_f(is_f) + type Dim = T::Dim; + type Strides = T; + fn into_shape(self) -> Shape { + Shape { + dim: self.into_dimension(), + is_c: true, + } } - fn strides(self, st: D) -> StrideShape { - Shape::from(self).strides(st) + fn f(self) -> Shape { self.set_f(true) } + fn set_f(self, is_f: bool) -> Shape { + self.into_shape().set_f(is_f) + } + fn strides(self, st: T) -> StrideShape { + self.into_shape().strides(st.into_dimension()) } } @@ -69,6 +74,8 @@ impl ShapeBuilder for Shape where D: Dimension { type Dim = D; + type Strides = D; + fn into_shape(self) -> Shape { self } fn f(self) -> Self { self.set_f(true) } fn set_f(mut self, is_f: bool) -> Self { self.is_c = !is_f; @@ -84,3 +91,11 @@ impl ShapeBuilder for Shape } +impl Shape + where D: Dimension, +{ + // Return a reference to the dimension + //pub fn dimension(&self) -> &D { &self.dim } + /// Return the size of the shape in number of elements + pub fn size(&self) -> usize { self.dim.size() } +} diff --git a/src/si.rs b/src/si.rs index 8ae8ede3a..560b075db 100644 --- a/src/si.rs +++ b/src/si.rs @@ -108,13 +108,9 @@ pub const S: Si = Si(0, None, 1); /// #[macro_use] /// extern crate ndarray; /// -/// use ndarray::{ -/// ArrayView, -/// Ix, -/// Array, -/// }; +/// use ndarray::{Array2, ArrayView2}; /// -/// fn laplacian(v: &ArrayView) -> Array { +/// fn laplacian(v: &ArrayView2) -> Array2 { /// -4. * &v.slice(s![1..-1, 1..-1]) /// + v.slice(s![ ..-2, 1..-1]) /// + v.slice(s![1..-1, ..-2]) @@ -125,14 +121,13 @@ pub const S: Si = Si(0, None, 1); /// ``` #[macro_export] macro_rules! s( - (@as_expr $e:expr) => ($e); // convert a..b;c into @step(a..b, c), final item (@parse [$($stack:tt)*] $r:expr;$s:expr) => { - s![@as_expr &[$($stack)* s!(@step $r, $s)]] + &[$($stack)* s!(@step $r, $s)] }; // convert a..b into @step(a..b, 1), final item (@parse [$($stack:tt)*] $r:expr) => { - s![@as_expr &[$($stack)* s!(@step $r, 1)]] + &[$($stack)* s!(@step $r, 1)] }; // convert a..b;c into @step(a..b, c) (@parse [$($stack:tt)*] $r:expr;$s:expr, $($t:tt)*) => { diff --git a/tests/array.rs b/tests/array.rs index 73fc56aaa..6728c3aee 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -10,6 +10,7 @@ use ndarray::{ rcarr2, arr0, arr3, aview_mut1, + Ix0, Ix2, }; use ndarray::Indexes; use itertools::free::enumerate; @@ -60,7 +61,7 @@ fn test_slice() } let vi = A.slice(s![1.., ..;2]); - assert_eq!(vi.dim(), (2, 2)); + assert_eq!(vi.shape(), &[2, 2]); let vi = A.slice(&[S, S]); assert_eq!(vi.shape(), A.shape()); assert!(vi.iter().zip(A.iter()).all(|(a, b)| a == b)); @@ -127,7 +128,7 @@ fn test_multidim() *elt = i as u8; } } - assert_eq!(mat.dim(), (2,3,4,5,6)); + assert_eq!(mat.shape(), &[2,3,4,5,6]); } @@ -153,7 +154,7 @@ fn test_negative_stride_rcarray() { let vi = mat.slice(&[S, Si(0, None, -1), Si(0, None, -1)]); - assert_eq!(vi.dim(), (2,4,2)); + assert_eq!(vi.shape(), &[2, 4, 2]); // Test against sequential iterator let seq = [7f32,6., 5.,4.,3.,2.,1.,0.,15.,14.,13., 12.,11., 10., 9., 8.]; for (a, b) in vi.clone().iter().zip(seq.iter()) { @@ -206,10 +207,10 @@ fn test_cow() fn test_sub() { let mat = RcArray::linspace(0., 15., 16).reshape((2, 4, 2)); - let s1 = mat.subview(Axis(0),0); - let s2 = mat.subview(Axis(0),1); - assert_eq!(s1.dim(), (4, 2)); - assert_eq!(s2.dim(), (4, 2)); + let s1 = mat.subview(Axis(0), 0); + let s2 = mat.subview(Axis(0), 1); + assert_eq!(s1.shape(), &[4, 2]); + assert_eq!(s2.shape(), &[4, 2]); let n = RcArray::linspace(8., 15., 8).reshape((4,2)); assert_eq!(n, s2); let m = RcArray::from_vec(vec![2., 3., 10., 11.]).reshape((2, 2)); @@ -247,14 +248,14 @@ fn test_select(){ fn diag() { let d = arr2(&[[1., 2., 3.0f32]]).into_diag(); - assert_eq!(d.dim(), 1); + assert_eq!(d.dim_pattern(), 1); let a = arr2(&[[1., 2., 3.0f32], [0., 0., 0.]]); let d = a.view().into_diag(); - assert_eq!(d.dim(), 2); + assert_eq!(d.dim_pattern(), 2); let d = arr2::(&[[]]).into_diag(); - assert_eq!(d.dim(), 0); + assert_eq!(d.dim_pattern(), 0); let d = RcArray::::zeros(()).into_diag(); - assert_eq!(d.dim(), 1); + assert_eq!(d.dim_pattern(), 1); } #[test] @@ -377,7 +378,7 @@ fn zero_axes() // we can even get a subarray of b let bsub = b.subview(Axis(0), 2); - assert_eq!(bsub.dim(), 0); + assert_eq!(bsub.dim_pattern(), 0); } #[test] @@ -485,7 +486,7 @@ fn from_vec_dim_stride_0d() { #[test] fn from_vec_dim_stride_2d_1() { let two = [1., 2.]; - let d = (2, 1); + let d = Ix2(2, 1); let s = d.default_strides(); assert_matches!(Array::from_shape_vec(d.strides(s), two.to_vec()), Ok(_)); } @@ -493,7 +494,7 @@ fn from_vec_dim_stride_2d_1() { #[test] fn from_vec_dim_stride_2d_2() { let two = [1., 2.]; - let d = (1, 2); + let d = Ix2(1, 2); let s = d.default_strides(); assert_matches!(Array::from_shape_vec(d.strides(s), two.to_vec()), Ok(_)); } @@ -1053,13 +1054,13 @@ fn test_view_from_shape_ptr() { #[test] fn test_default() { - let a = as Default>::default(); + let a = as Default>::default(); assert_eq!(a, aview2(&[[0.0; 0]; 0])); #[derive(Default, Debug, PartialEq)] struct Foo(i32); - let b = as Default>::default(); + let b = as Default>::default(); assert_eq!(b, arr0(Foo::default())); } diff --git a/tests/broadcast.rs b/tests/broadcast.rs index daaa3cd9d..7675c032a 100644 --- a/tests/broadcast.rs +++ b/tests/broadcast.rs @@ -2,17 +2,18 @@ extern crate ndarray; use ndarray::prelude::*; +use ndarray::Dim; #[test] fn broadcast_1() { - let a_dim = (2, 4, 2, 2); - let b_dim = (2, 1, 2, 1); + let a_dim = Dim([2, 4, 2, 2]); + let b_dim = Dim([2, 1, 2, 1]); let a = RcArray::linspace(0., 1., a_dim.size()).reshape(a_dim); let b = RcArray::linspace(0., 1., b_dim.size()).reshape(b_dim); assert!(b.broadcast(a.dim()).is_some()); - let c_dim = (2, 1); + let c_dim = Dim([2, 1]); let c = RcArray::linspace(0., 1., c_dim.size()).reshape(c_dim); assert!(c.broadcast(1).is_none()); assert!(c.broadcast(()).is_none()); @@ -32,8 +33,8 @@ fn broadcast_1() #[test] fn test_add() { - let a_dim = (2, 4, 2, 2); - let b_dim = (2, 1, 2, 1); + let a_dim = Dim([2, 4, 2, 2]); + let b_dim = Dim([2, 1, 2, 1]); let mut a = RcArray::linspace(0.0, 1., a_dim.size()).reshape(a_dim); let b = RcArray::linspace(0.0, 1., b_dim.size()).reshape(b_dim); a += &b; @@ -44,7 +45,7 @@ fn test_add() #[test] #[should_panic] fn test_add_incompat() { - let a_dim = (2, 4, 2, 2); + let a_dim = Dim([2, 4, 2, 2]); let mut a = RcArray::linspace(0.0, 1., a_dim.size()).reshape(a_dim); let incompat = RcArray::from_elem(3, 1.0f32); a += &incompat; diff --git a/tests/dimension.rs b/tests/dimension.rs index d8c88c696..f57fbda2c 100644 --- a/tests/dimension.rs +++ b/tests/dimension.rs @@ -7,24 +7,24 @@ use ndarray::{ arr2, Axis, Dimension, + Dim, }; #[test] fn remove_axis() { - assert_eq!(3.remove_axis(Axis(0)), ()); - assert_eq!((1, 2).remove_axis(Axis(0)), 2); - assert_eq!((4, 5, 6).remove_axis(Axis(1)), (4, 6)); + assert_eq!(Dim([3]).remove_axis(Axis(0)), Dim([])); + assert_eq!(Dim([1, 2]).remove_axis(Axis(0)), Dim([2])); + assert_eq!(Dim([4, 5, 6]).remove_axis(Axis(1)), Dim([4, 6])); - assert_eq!(vec![1,2].remove_axis(Axis(0)), vec![2]); - assert_eq!(vec![4, 5, 6].remove_axis(Axis(1)), vec![4, 6]); + assert_eq!(Dim(vec![1,2]).remove_axis(Axis(0)), Dim(vec![2])); + assert_eq!(Dim(vec![4, 5, 6]).remove_axis(Axis(1)), Dim(vec![4, 6])); let a = RcArray::::zeros((4,5)); a.subview(Axis(1), 0); let a = RcArray::::zeros(vec![4,5,6]); let _b = a.into_subview(Axis(1), 0).reshape((4, 6)).reshape(vec![2, 3, 4]); - } #[test] @@ -44,18 +44,71 @@ fn dyn_dimension() #[test] fn fastest_varying_order() { - let strides = (2, 8, 4, 1); + let strides = Dim([2, 8, 4, 1]); let order = strides._fastest_varying_stride_order(); assert_eq!(order.slice(), &[3, 0, 2, 1]); - assert_eq!((1, 3)._fastest_varying_stride_order(), (0, 1)); - assert_eq!((7, 2)._fastest_varying_stride_order(), (1, 0)); - assert_eq!((6, 1, 3)._fastest_varying_stride_order(), (1, 2, 0)); + assert_eq!(Dim([1, 3])._fastest_varying_stride_order(), Dim([0, 1])); + assert_eq!(Dim([7, 2])._fastest_varying_stride_order(), Dim([1, 0])); + assert_eq!(Dim([6, 1, 3])._fastest_varying_stride_order(), Dim([1, 2, 0])); // it's important that it produces distinct indices. Prefer the stable order // where 0 is before 1 when they are equal. - assert_eq!((2, 2)._fastest_varying_stride_order(), (0, 1)); - assert_eq!((2, 2, 1)._fastest_varying_stride_order(), (2, 0, 1)); - assert_eq!((2, 2, 3, 1, 2)._fastest_varying_stride_order(), (3, 0, 1, 4, 2)); + assert_eq!(Dim([2, 2])._fastest_varying_stride_order(), [0, 1]); + assert_eq!(Dim([2, 2, 1])._fastest_varying_stride_order(), [2, 0, 1]); + assert_eq!(Dim([2, 2, 3, 1, 2])._fastest_varying_stride_order(), [3, 0, 1, 4, 2]); } +#[test] +fn test_indexing() { + let mut x = Dim([1, 2]); + + assert_eq!(x[0], 1); + assert_eq!(x[1], 2); + + x[0] = 7; + assert_eq!(x, [7, 2]); +} + +#[test] +fn test_operations() { + let mut x = Dim([1, 2]); + let mut y = Dim([1, 1]); + + assert_eq!(x + y, [2, 3]); + + x += y; + assert_eq!(x, [2, 3]); + x *= 2; + assert_eq!(x, [4, 6]); + + y[0] -= 1; + assert_eq!(y, [0, 1]); +} + +#[test] +fn test_generic_operations() { + fn test_dim(d: &D) { + let mut x = d.clone(); + x[0] += 1; + assert_eq!(x[0], 3); + x += d; + assert_eq!(x[0], 5); + } + + test_dim(&Dim([2, 3, 4])); + test_dim(&Dim(vec![2, 3, 4, 1])); + test_dim(&Dim(2)); +} + +#[test] +fn test_array_view() { + fn test_dim(d: &D) { + assert_eq!(d.as_array_view().scalar_sum(), 7); + assert_eq!(d.as_array_view().strides(), &[1]); + } + + test_dim(&Dim([1, 2, 4])); + test_dim(&Dim(vec![1, 1, 2, 3])); + test_dim(&Dim(7)); +} diff --git a/tests/ixdyn.rs b/tests/ixdyn.rs new file mode 100644 index 000000000..b6d388867 --- /dev/null +++ b/tests/ixdyn.rs @@ -0,0 +1,60 @@ + +extern crate ndarray; + +use ndarray::Array; +use ndarray::Ix3; + +#[test] +fn test_ixdyn() { + // check that we can use fixed size arrays for indexing + let mut a = Array::zeros(vec![2, 3, 4]); + a[[1, 1, 1]] = 1.; + assert_eq!(a[[1, 1, 1]], 1.); +} + +#[should_panic] +#[test] +fn test_ixdyn_wrong_dim() { + // check that we can use but it panics at runtime, if number of axes is wrong + let mut a = Array::zeros(vec![2, 3, 4]); + a[[1, 1, 1]] = 1.; + + let _ = a[[0, 0]]; +} + +#[test] +fn test_ixdyn_out_of_bounds() { + // check that we are out of bounds + let a = Array::::zeros(vec![2, 3, 4]); + let res = a.get([0, 3, 0]); + assert_eq!(res, None); +} + +#[test] +fn test_ixdyn_uget() { + // check that we are out of bounds + let mut a = Array::::zeros(vec![2, 3, 4]); + + a[[1, 2, 0]] = 1.; + a[[1, 2, 1]] = 2.; + a[[1, 2, 3]] = 7.; + + let mut x = Ix3(1, 2, 0); + let step = Ix3(0, 0, 1); + let mut sum = 0.; + while let Some(&v) = a.get(x) { + sum += v; + x += step; + } + assert_eq!(sum, 10.); + + let mut x = Ix3(1, 2, 0); + let mut sum = 0.; + unsafe { + for _ in 0..4 { + sum += *a.uget(x); + x += step; + } + } + assert_eq!(sum, 10.); +} diff --git a/tests/oper.rs b/tests/oper.rs index 3eb937f58..fbf36cce4 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -5,6 +5,8 @@ use ndarray::prelude::*; use ndarray::{arr0, rcarr1, rcarr2}; use ndarray::{LinalgScalar, Data}; use ndarray::linalg::general_mat_mul; +use ndarray::Array2; +use ndarray::Ix2; use std::fmt; use num_traits::Float; @@ -263,26 +265,26 @@ fn fold_and_sum() { } } -fn range_mat(m: Ix, n: Ix) -> Array { +fn range_mat(m: Ix, n: Ix) -> Array2 { Array::linspace(0., (m * n - 1) as f32, m * n).into_shape((m, n)).unwrap() } -fn range_mat64(m: Ix, n: Ix) -> Array { +fn range_mat64(m: Ix, n: Ix) -> Array2 { Array::linspace(0., (m * n - 1) as f64, m * n).into_shape((m, n)).unwrap() } -fn range_i32(m: Ix, n: Ix) -> Array { +fn range_i32(m: Ix, n: Ix) -> Array2 { Array::from_iter(0..(m * n) as i32).into_shape((m, n)).unwrap() } // simple, slow, correct (hopefully) mat mul -fn reference_mat_mul(lhs: &ArrayBase, rhs: &ArrayBase) - -> Array +fn reference_mat_mul(lhs: &ArrayBase, rhs: &ArrayBase) + -> Array2 where A: LinalgScalar, S: Data, S2: Data, { - let ((m, k), (k2, n)) = (lhs.dim(), rhs.dim()); + let ((m, k), (k2, n)) = (lhs.dim_pattern(), rhs.dim_pattern()); assert!(m.checked_mul(n).is_some()); assert_eq!(k, k2); let mut res_elems = Vec::::with_capacity(m * n); diff --git a/tests/serialize.rs b/tests/serialize.rs index 0b5153654..155b25951 100644 --- a/tests/serialize.rs +++ b/tests/serialize.rs @@ -1,7 +1,8 @@ #![cfg(any(feature = "rustc-serialize", feature = "serde"))] #[cfg(feature = "rustc-serialize")] extern crate rustc_serialize as serialize; -extern crate ndarray; + +#[macro_use] extern crate ndarray; #[cfg(feature = "serde")] extern crate serde; @@ -12,12 +13,13 @@ extern crate serde_json; #[cfg(feature = "rustc-serialize")] use serialize::json; -use ndarray::{arr0, arr1, arr2, RcArray, Ix, S, Si}; +use ndarray::{arr0, arr1, arr2, RcArray, RcArray1, RcArray2}; #[cfg(feature = "rustc-serialize")] #[test] fn serial_many_dim() { + /* rustc-serialize does not support serializing [T; 0] { let a = arr0::(2.72); let serial = json::encode(&a).unwrap(); @@ -26,12 +28,13 @@ fn serial_many_dim() println!("{:?}", res); assert_eq!(a, res.unwrap()); } + */ { let a = arr1::(&[2.72, 1., 2.]); let serial = json::encode(&a).unwrap(); println!("Encode {:?} => {:?}", a, serial); - let res = json::decode::>(&serial); + let res = json::decode::>(&serial); println!("{:?}", res); assert_eq!(a, res.unwrap()); } @@ -40,11 +43,11 @@ fn serial_many_dim() let a = arr2(&[[3., 1., 2.2], [3.1, 4., 7.]]); let serial = json::encode(&a).unwrap(); println!("Encode {:?} => {:?}", a, serial); - let res = json::decode::>(&serial); + let res = json::decode::>(&serial); println!("{:?}", res); assert_eq!(a, res.unwrap()); let text = r##"{"v":1,"dim":[2,3],"data":[3,1,2.2,3.1,4,7]}"##; - let b = json::decode::>(text); + let b = json::decode::>(text); assert_eq!(a, b.unwrap()); } @@ -52,7 +55,7 @@ fn serial_many_dim() { // Test a sliced array. let mut a = RcArray::linspace(0., 31., 32).reshape((2, 2, 2, 4)); - a.islice(&[Si(0, None, -1), S, S, Si(0, Some(2), 1)]); + a.islice(s![..;-1, .., .., ..2]); let serial = json::encode(&a).unwrap(); println!("Encode {:?} => {:?}", a, serial); let res = json::decode::>(&serial); @@ -67,13 +70,13 @@ fn serial_wrong_count() { // one element too few let text = r##"{"v":1,"dim":[2,3],"data":[3,1,2.2,3.1,4]}"##; - let arr = json::decode::>(text); + let arr = json::decode::>(text); println!("{:?}", arr); assert!(arr.is_err()); // future version let text = r##"{"v":200,"dim":[2,3],"data":[3,1,2.2,3.1,4,7]}"##; - let arr = json::decode::>(text); + let arr = json::decode::>(text); println!("{:?}", arr); assert!(arr.is_err()); } @@ -95,7 +98,7 @@ fn serial_many_dim_serde() let a = arr1::(&[2.72, 1., 2.]); let serial = serde_json::to_string(&a).unwrap(); println!("Serde encode {:?} => {:?}", a, serial); - let res = serde_json::from_str::>(&serial); + let res = serde_json::from_str::>(&serial); println!("{:?}", res); assert_eq!(a, res.unwrap()); } @@ -104,18 +107,18 @@ fn serial_many_dim_serde() let a = arr2(&[[3., 1., 2.2], [3.1, 4., 7.]]); let serial = serde_json::to_string(&a).unwrap(); println!("Serde encode {:?} => {:?}", a, serial); - let res = serde_json::from_str::>(&serial); + let res = serde_json::from_str::>(&serial); println!("{:?}", res); assert_eq!(a, res.unwrap()); let text = r##"{"v":1,"dim":[2,3],"data":[3,1,2.2,3.1,4,7]}"##; - let b = serde_json::from_str::>(text); + let b = serde_json::from_str::>(text); assert_eq!(a, b.unwrap()); } { // Test a sliced array. let mut a = RcArray::linspace(0., 31., 32).reshape((2, 2, 2, 4)); - a.islice(&[Si(0, None, -1), S, S, Si(0, Some(2), 1)]); + a.islice(s![..;-1, .., .., ..2]); let serial = serde_json::to_string(&a).unwrap(); println!("Encode {:?} => {:?}", a, serial); let res = serde_json::from_str::>(&serial); @@ -130,13 +133,13 @@ fn serial_wrong_count_serde() { // one element too few let text = r##"{"v":1,"dim":[2,3],"data":[3,1,2.2,3.1,4]}"##; - let arr = serde_json::from_str::>(text); + let arr = serde_json::from_str::>(text); println!("{:?}", arr); assert!(arr.is_err()); // future version let text = r##"{"v":200,"dim":[2,3],"data":[3,1,2.2,3.1,4,7]}"##; - let arr = serde_json::from_str::>(text); + let arr = serde_json::from_str::>(text); println!("{:?}", arr); assert!(arr.is_err()); } diff --git a/tests/stacking.rs b/tests/stacking.rs index edf0f2485..4204ee48c 100644 --- a/tests/stacking.rs +++ b/tests/stacking.rs @@ -7,8 +7,7 @@ use ndarray::{ aview1, arr2, Axis, - Ix, - Array, + Array2, ErrorKind, }; @@ -39,6 +38,6 @@ fn stacking() { let res = ndarray::stack(Axis(2), &[a.view(), c.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::OutOfBounds); - let res: Result, _> = ndarray::stack(Axis(0), &[]); + let res: Result, _> = ndarray::stack(Axis(0), &[]); assert_eq!(res.unwrap_err().kind(), ErrorKind::Unsupported); }