diff --git a/examples/convo.rs b/examples/convo.rs new file mode 100644 index 000000000..77b42db6a --- /dev/null +++ b/examples/convo.rs @@ -0,0 +1,68 @@ +#![allow(unused)] +#[macro_use(s)] +extern crate ndarray; +extern crate num; + +use num::Float; + +use ndarray::{ + ArrayView, + ArrayViewMut, + OwnedArray, + Ix, +}; + +type Ix2 = (Ix, Ix); +const SOBEL_X: [[f32; 3]; 3] = [[-1., 0., 1.], [-2., 0., 2.], [-1., 0., 1.]]; +const SOBEL_Y: [[f32; 3]; 3] = [[ 1., 2., 1.], [ 0., 0., 0.], [-1., -2., -1.]]; +const SHARPEN: [[f32; 3]; 3] = [[0., -1., 0.], [ -1., 5., -1.], [0., -1., 0.]]; + +type Kernel3x3 = [[A; 3]; 3]; + +#[inline(never)] +fn conv_3x3(a: &ArrayView, out: &mut ArrayViewMut, kernel: &Kernel3x3) + where F: Float, +{ + let (n, m) = a.dim(); + let (np, mp) = out.dim(); + if n < 3 || m < 3 { + return; + } + assert!(np >= n && mp >= m); + // i, j offset by -1 so that we can use unsigned indices + unsafe { + for i in 0..n - 2 { + for j in 0..m - 2 { + let mut conv = F::zero(); + for k in 0..3 { + for l in 0..3 { + conv = conv + *a.uget((i + k, j + l)) * kernel[k][l]; + //conv += a[[i + k, j + l]] * x_kernel[k][l]; + } + } + *out.uget_mut((i + 1, j + 1)) = conv; + } + } + } +} + +fn main() { + let n = 16; + let mut a = OwnedArray::zeros((n, n)); + // make a circle + let c = (8., 8.); + for ((i, j), elt) in a.indexed_iter_mut() { + { + let s = ((i as f32) - c.0).powi(2) + (j as f32 - c.1).powi(2); + if s.sqrt() > 3. && s.sqrt() < 6. { + *elt = 1.; + } + } + } + println!("{:?}", a); + let mut res = OwnedArray::zeros(a.dim()); + for _ in 0..1000 { + conv_3x3(&a.view(), &mut res.view_mut(), &SOBEL_X); + } + println!("{:?}", res); +} diff --git a/examples/life.rs b/examples/life.rs index 89bd1146b..378cfcd14 100644 --- a/examples/life.rs +++ b/examples/life.rs @@ -63,10 +63,11 @@ fn iterate(z: &mut Board, scratch: &mut Board) { } fn turn_on_corners(z: &mut Board) { - z.slice_mut(s![1..2, 1..2]).assign_scalar(&1); - z.slice_mut(s![1..2, -2..-1]).assign_scalar(&1); - z.slice_mut(s![-2..-1, 1..2]).assign_scalar(&1); - z.slice_mut(s![-2..-1, -2..-1]).assign_scalar(&1); + let (n, m) = z.dim(); + z[[1 , 1 ]] = 1; + z[[1 , m - 2]] = 1; + z[[n - 2, 1 ]] = 1; + z[[n - 2, m - 2]] = 1; } fn render(a: &Board) { diff --git a/src/arraytraits.rs b/src/arraytraits.rs index b150950d3..49798ec3a 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -19,36 +19,44 @@ use super::{ Data, DataMut, DataOwned, + NdIndex, }; +#[cold] +#[inline(never)] +fn array_out_of_bounds() -> ! { + panic!("ndarray: index out of bounds"); +} + /// Access the element at **index**. /// /// **Panics** if index is out of bounds. -impl Index for ArrayBase +impl Index for ArrayBase where D: Dimension, + I: NdIndex, S: Data, { type Output = S::Elem; #[inline] - fn index(&self, index: D) -> &S::Elem { - self.get(index).expect("Array::index: out of bounds") + fn index(&self, index: I) -> &S::Elem { + self.get(index).unwrap_or_else(|| array_out_of_bounds()) } } /// Access the element at **index** mutably. /// /// **Panics** if index is out of bounds. -impl IndexMut for ArrayBase +impl IndexMut for ArrayBase where D: Dimension, + I: NdIndex, S: DataMut, { #[inline] - fn index_mut(&mut self, index: D) -> &mut S::Elem { - self.get_mut(index).expect("Array::index_mut: out of bounds") + fn index_mut(&mut self, index: I) -> &mut S::Elem { + self.get_mut(index).unwrap_or_else(|| array_out_of_bounds()) } } - impl PartialEq> for ArrayBase where D: Dimension, S: Data, diff --git a/src/dimension.rs b/src/dimension.rs index 848f8df7e..0bd450e8d 100644 --- a/src/dimension.rs +++ b/src/dimension.rs @@ -254,6 +254,7 @@ unsafe impl Dimension for Ix { } /// Return stride offset for this dimension and index. + #[inline] fn stride_offset_checked(&self, stride: &Ix, index: &Ix) -> Option { if *index < *self { @@ -309,6 +310,7 @@ unsafe impl Dimension for (Ix, Ix) { } /// 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; @@ -459,3 +461,87 @@ impl RemoveAxis for Vec } } +/// A tuple or fixed size array 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** the blanket implementation that's not visible in rustdoc: +/// `impl NdIndex for D where D: Dimension { ... }` +pub unsafe trait NdIndex { + 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(zipsl(&dim[..], &self[..]), + strides.slice()) + { + if i >= d { + return None; + } + offset += stride_offset(i, s); + } + Some(offset) + } +} diff --git a/src/lib.rs b/src/lib.rs index a47f89a8c..8c972fb88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,6 +81,7 @@ use std::slice::{self, Iter, IterMut}; use it::ZipSlices; pub use dimension::{Dimension, RemoveAxis}; +pub use dimension::NdIndex; pub use indexes::Indexes; pub use shape_error::ShapeError; pub use si::{Si, S}; @@ -343,10 +344,13 @@ pub unsafe trait Data { /// Array’s writable inner representation. pub unsafe trait DataMut : Data { fn slice_mut(&mut self) -> &mut [Self::Elem]; + #[inline] fn ensure_unique(&mut ArrayBase) where Self: Sized, D: Dimension { } + #[inline] + fn is_unique(&mut self) -> bool { true } } /// Clone an Array’s storage. @@ -384,6 +388,10 @@ unsafe impl DataMut for Rc> where A: Clone { self_.ptr = rvec.as_mut_ptr().offset(our_off); } } + + fn is_unique(&mut self) -> bool { + Rc::get_mut(self).is_some() + } } unsafe impl DataClone for Rc> { @@ -874,12 +882,15 @@ impl ArrayBase where S: Data, D: Dimension /// assert!( /// a.get((0, 1)) == Some(&2.) && /// a.get((0, 2)) == None && - /// a[(0, 1)] == 2. + /// a[(0, 1)] == 2. && + /// a[[0, 1]] == 2. /// ); /// ``` - pub fn get(&self, index: D) -> Option<&A> { + pub fn get(&self, index: I) -> Option<&A> + where I: NdIndex, + { let ptr = self.ptr; - self.dim.stride_offset_checked(&self.strides, &index) + index.index_checked(&self.dim, &self.strides) .map(move |offset| unsafe { &*ptr.offset(offset) }) @@ -893,12 +904,13 @@ impl ArrayBase where S: Data, D: Dimension /// Return a mutable reference to the element at `index`, or return `None` /// if the index is out of bounds. - pub fn get_mut(&mut self, index: D) -> Option<&mut A> + pub fn get_mut(&mut self, index: I) -> Option<&mut A> where S: DataMut, + I: NdIndex, { self.ensure_unique(); let ptr = self.ptr; - self.dim.stride_offset_checked(&self.strides, &index) + index.index_checked(&self.dim, &self.strides) .map(move |offset| unsafe { &mut *ptr.offset(offset) }) @@ -941,7 +953,7 @@ impl ArrayBase where S: Data, D: Dimension pub unsafe fn uget_mut(&mut self, index: D) -> &mut A where S: DataMut { - //debug_assert!(Rc::get_mut(&mut self.data).is_some()); + debug_assert!(self.data.is_unique()); debug_assert!(self.dim.stride_offset_checked(&self.strides, &index).is_some()); let off = Dimension::stride_offset(&index, &self.strides); &mut *self.ptr.offset(off) diff --git a/src/linalg.rs b/src/linalg.rs index bb053b877..36ed64d22 100644 --- a/src/linalg.rs +++ b/src/linalg.rs @@ -124,7 +124,7 @@ pub fn least_squares(a: &Mat, b: &Col) -> Col let (m, _) = L.dim(); for i in 1..m { for j in 0..i { - let elt = &mut L[(i, j)]; + let elt = &mut L[[i, j]]; *elt = elt.conjugate(); } } @@ -177,7 +177,7 @@ pub fn cholesky(a: Mat) -> Mat } // L_ij = [ A_ij - Sum(k = 1 .. j) L_ik L_jk ] / L_jj - L[(i, j)] = (L[(i, j)] - lik_ljk_sum) / L[(j, j)]; + L[[i, j]] = (L[[i, j]] - lik_ljk_sum) / L[[j, j]]; } // Diagonal where i == j @@ -188,12 +188,12 @@ pub fn cholesky(a: Mat) -> Mat for &ljk in L.row_iter(j).take(j as usize) { ljk_sum = ljk_sum + ljk * ljk.conjugate(); } - L[(j, j)] = (L[(j, j)] - ljk_sum).sqrt_real(); + L[[j, j]] = (L[[j, j]] - ljk_sum).sqrt_real(); // After the diagonal // L_ij = 0 for j > i for j in i + 1..n { - L[(i, j)] = z; + L[[i, j]] = z; } } L diff --git a/tests/array.rs b/tests/array.rs index d42df2ab0..42a24b4d0 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -72,13 +72,13 @@ fn test_index() } for ((i, j), a) in Indexes::new((2, 3)).zip(A.iter()) { - assert_eq!(*a, A[(i, j)]); + assert_eq!(*a, A[[i, j]]); } let vi = A.slice(&[Si(1, None, 1), Si(0, None, 2)]); let mut it = vi.iter(); for ((i, j), x) in Indexes::new((1, 2)).zip(it.by_ref()) { - assert_eq!(*x, vi[(i, j)]); + assert_eq!(*x, vi[[i, j]]); } assert!(it.next().is_none()); } @@ -93,10 +93,10 @@ fn test_add() let B = A.clone(); A.iadd(&B); - assert_eq!(A[(0,0)], 0); - assert_eq!(A[(0,1)], 2); - assert_eq!(A[(1,0)], 4); - assert_eq!(A[(1,1)], 6); + assert_eq!(A[[0, 0]], 0); + assert_eq!(A[[0, 1]], 2); + assert_eq!(A[[1, 0]], 4); + assert_eq!(A[[1, 1]], 6); } #[test] @@ -156,15 +156,16 @@ fn test_negative_stride_rcarray() fn test_cow() { let mut mat = Array::::zeros((2,2)); - mat[(0, 0)] = 1; + mat[[0, 0]] = 1; let n = mat.clone(); - mat[(0, 1)] = 2; - mat[(1, 0)] = 3; - mat[(1, 1)] = 4; - assert_eq!(mat[(0,0)], 1); - assert_eq!(mat[(0,1)], 2); - assert_eq!(n[(0,0)], 1); - assert_eq!(n[(0,1)], 0); + mat[[0, 1]] = 2; + mat[[1, 0]] = 3; + mat[[1, 1]] = 4; + assert_eq!(mat[[0, 0]], 1); + assert_eq!(mat[[0, 1]], 2); + assert_eq!(n[[0, 0]], 1); + assert_eq!(n[[0, 1]], 0); + assert_eq!(n.get((0, 1)), Some(&0)); let mut rev = mat.reshape(4); rev.islice(&[Si(0, None, -1)]); assert_eq!(rev[0], 4); @@ -267,19 +268,6 @@ fn assign() assert_eq!(a, arr2(&[[0, 0], [3, 4]])); } -#[test] -fn dyn_dimension() -{ - let a = arr2(&[[1., 2.], [3., 4.0]]).reshape(vec![2, 2]); - assert_eq!(&a - &a, Array::zeros(vec![2, 2])); - - let mut dim = vec![1; 1024]; - dim[16] = 4; - dim[17] = 3; - let z = Array::::zeros(dim.clone()); - assert_eq!(z.shape(), &dim[..]); -} - #[test] fn sum_mean() { diff --git a/tests/dimension.rs b/tests/dimension.rs index ecc3faaeb..6e3985fc1 100644 --- a/tests/dimension.rs +++ b/tests/dimension.rs @@ -2,8 +2,8 @@ extern crate ndarray; use ndarray::{ Array, - //Dimension, RemoveAxis, + arr2, }; #[test] @@ -20,3 +20,19 @@ fn remove_axis() let _b = a.subview(1, 0).reshape((4, 6)).reshape(vec![2, 3, 4]); } + +#[test] +fn dyn_dimension() +{ + let a = arr2(&[[1., 2.], [3., 4.0]]).reshape(vec![2, 2]); + assert_eq!(&a - &a, Array::zeros(vec![2, 2])); + assert_eq!(a[&[0, 0][..]], 1.); + assert_eq!(a[vec![0, 0]], 1.); + + let mut dim = vec![1; 1024]; + dim[16] = 4; + dim[17] = 3; + let z = Array::::zeros(dim.clone()); + assert_eq!(z.shape(), &dim[..]); +} +