Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reimplement IxDyn, with no allocation for the small dimension case #301

Merged
merged 12 commits into from
Apr 4, 2017
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ matrixmultiply = { version = "0.1.13" }
version = "0.9"
optional = true

[dev-dependencies]
defmac = "0.1"

[features]
blas = ["blas-sys"]

Expand Down
28 changes: 22 additions & 6 deletions src/aliases.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
//! Type aliases for common array sizes
//!

use ::{Ix, Array, ArrayView, ArrayViewMut, RcArray};
use ::{
Ix,
Array,
ArrayView,
ArrayViewMut,
RcArray,
IxDynImpl,
};
use ::dimension::Dim;
use dimension::DimPrivate;

Expand Down Expand Up @@ -38,6 +45,13 @@ pub fn Ix6(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix, i5: Ix) -> Ix6 {
Dim::new([i0, i1, i2, i3, i4, i5])
}

/// Create a dynamic-dimensional index
#[allow(non_snake_case)]
#[inline(always)]
pub fn IxDyn(ix: &[Ix]) -> IxDyn {
Dim(ix)
}

/// zero-dimensionial
pub type Ix0 = Dim<[Ix; 0]>;
/// one-dimensional
Expand All @@ -54,16 +68,18 @@ pub type Ix5 = Dim<[Ix; 5]>;
pub type Ix6 = Dim<[Ix; 6]>;
/// dynamic-dimensional
///
/// `Vec<Ix>` and `&[usize]` implement `IntoDimension` to produce `IxDyn`;
/// use them to create arrays with a dynamic number of axes.
/// You can use the `IxDyn` function to create a dimension for an array with
/// dynamic number of dimensions. (`Vec<Ix>` and `&[usize]` also implement
/// `IntoDimension` to produce `IxDyn`).
///
/// ```
/// use ndarray::ArrayD;
/// use ndarray::IxDyn;
///
/// // Create a 5 × 6 × 3 × 4 array using the dynamic dimension type
/// let mut a = ArrayD::<f64>::zeros(vec![5, 6, 3, 4]);
/// let mut a = ArrayD::<f64>::zeros(IxDyn(&[5, 6, 3, 4]));
/// // Create a 1 × 3 × 4 array using the dynamic dimension type
/// let mut b = ArrayD::<f64>::zeros(vec![1, 3, 4]);
/// let mut b = ArrayD::<f64>::zeros(IxDyn(&[1, 3, 4]));
///
/// // We can use broadcasting to add arrays of compatible shapes together:
/// a += &b;
Expand All @@ -78,7 +94,7 @@ pub type Ix6 = Dim<[Ix; 6]>;
/// // the same type `Array<f64, IxDyn>` a.k.a `ArrayD<f64>`:
/// let arrays = vec![a, b];
/// ```
pub type IxDyn = Dim<Vec<Ix>>;
pub type IxDyn = Dim<IxDynImpl>;

/// zero-dimensional array
pub type Array0<A> = Array<A, Ix0>;
Expand Down
8 changes: 5 additions & 3 deletions src/arraytraits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,11 @@ impl<'a, A: 'a, D, T> AsArray<'a, A, D> for T
///
/// The array is created with dimension `D::default()`, which results
/// in for example dimensions `0` and `(0, 0)` with zero elements for the
/// one-dimensional and two-dimensional cases respectively, while for example
/// the zero dimensional case uses `()` (or `Vec::new()`) which
/// results in an array with one element.
/// one-dimensional and two-dimensional cases respectively.
///
/// The default dimension for `IxDyn` is `IxDyn(&[0])` (array has zero
/// elements). And the default for the dimension `()` is `()` (array has
/// one element).
///
/// Since arrays cannot grow, the intention is to use the default value as
/// placeholder.
Expand Down
10 changes: 8 additions & 2 deletions src/dimension/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use std::ops::{Index, IndexMut};
use libnum::Zero;

use {Ix, Ix1, IxDyn, Dimension, Dim};
use {Ix, Ix1, IxDyn, Dimension, Dim, IxDynImpl};
use super::DimPrivate;

/// $m: macro callback
Expand Down Expand Up @@ -56,12 +56,18 @@ impl<D> IntoDimension for D where D: Dimension {
fn into_dimension(self) -> Self { self }
}

impl IntoDimension for Vec<usize> {
impl IntoDimension for IxDynImpl {
type Dim = IxDyn;
#[inline(always)]
fn into_dimension(self) -> Self::Dim { Dim::new(self) }
}

impl IntoDimension for Vec<Ix> {
type Dim = IxDyn;
#[inline(always)]
fn into_dimension(self) -> Self::Dim { Dim::new(IxDynImpl::from(self)) }
}

pub trait Convert {
type To;
fn convert(self) -> Self::To;
Expand Down
18 changes: 9 additions & 9 deletions src/dimension/dimension_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::ops::{Add, Sub, Mul, AddAssign, SubAssign, MulAssign};

use itertools::{enumerate, zip};

use {Ix, Ixs, Ix0, Ix1, Ix2, Ix3, IxDyn, Dim, Si};
use {Ix, Ixs, Ix0, Ix1, Ix2, Ix3, IxDyn, Dim, Si, IxDynImpl};
use IntoDimension;
use RemoveAxis;
use {ArrayView1, ArrayViewMut1};
Expand Down Expand Up @@ -53,7 +53,7 @@ pub unsafe trait Dimension : Clone + Eq + Debug + Send + Sync + Default +
/// - For `Ix1`: `[Si; 1]`
/// - For `Ix2`: `[Si; 2]`
/// - and so on..
/// - For `Vec<Ix>`: `[Si]`
/// - For `IxDyn: `[Si]`
///
/// The easiest way to create a `&SliceArg` is using the macro
/// [`s![]`](macro.s!.html).
Expand All @@ -63,7 +63,7 @@ pub unsafe trait Dimension : Clone + Eq + Debug + Send + Sync + Default +
/// - For `Ix1`: `usize`,
/// - For `Ix2`: `(usize, usize)`
/// - and so on..
/// - For `Vec<Ix>`: `Vec<usize>`,
/// - For `IxDyn: `IxDyn`
type Pattern: IntoDimension<Dim=Self>;
// Next smaller dimension (if it exists)
#[doc(hidden)]
Expand Down Expand Up @@ -734,7 +734,7 @@ 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<Ix> is a "dynamic" index, pretty hard to use when indexing,
/// IxDyn 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
{
Expand All @@ -761,17 +761,17 @@ unsafe impl Dimension for IxDyn
}
}

impl<J> Index<J> for Dim<Vec<usize>>
where Vec<usize>: Index<J>,
impl<J> Index<J> for Dim<IxDynImpl>
where IxDynImpl: Index<J>,
{
type Output = <Vec<usize> as Index<J>>::Output;
type Output = <IxDynImpl as Index<J>>::Output;
fn index(&self, index: J) -> &Self::Output {
&self.ix()[index]
}
}

impl<J> IndexMut<J> for Dim<Vec<usize>>
where Vec<usize>: IndexMut<J>,
impl<J> IndexMut<J> for Dim<IxDynImpl>
where IxDynImpl: IndexMut<J>,
{
fn index_mut(&mut self, index: J) -> &mut Self::Output {
&mut self.ixm()[index]
Expand Down
211 changes: 211 additions & 0 deletions src/dimension/dynindeximpl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@

use std::ops::{
Index,
IndexMut,
Deref,
DerefMut,
};
use imp_prelude::*;
use dimension::DimPrivate;

const CAP: usize = 4;

/// T is usize or isize
#[derive(Debug)]
enum IxDynRepr<T> {
Inline(u32, [T; CAP]),
Alloc(Box<[T]>),
}

impl<T> Deref for IxDynRepr<T> {
type Target = [T];
fn deref(&self) -> &[T] {
match *self {
IxDynRepr::Inline(len, ref ar) => {
unsafe {
ar.get_unchecked(..len as usize)
}
}
IxDynRepr::Alloc(ref ar) => &*ar,
}
}
}

impl<T> DerefMut for IxDynRepr<T> {
fn deref_mut(&mut self) -> &mut [T] {
match *self {
IxDynRepr::Inline(len, ref mut ar) => {
unsafe {
ar.get_unchecked_mut(..len as usize)
}
}
IxDynRepr::Alloc(ref mut ar) => &mut *ar,
}
}
}

/// The default is equivalent to `Self::from(&[0])`.
impl Default for IxDynRepr<Ix> {
fn default() -> Self {
Self::copy_from(&[0])
}
}


use ::libnum::Zero;

impl<T: Copy + Zero> IxDynRepr<T> {
pub fn copy_from(x: &[T]) -> Self {
if x.len() <= CAP {
let mut arr = [T::zero(); CAP];
for i in 0..x.len() {
arr[i] = x[i];
}
IxDynRepr::Inline(x.len() as _, arr)
} else {
Self::from(x)
}
}
}

impl<T: Copy + Zero> IxDynRepr<T> {
// make an Inline or Alloc version as appropriate
fn from_vec_auto(v: Vec<T>) -> Self {
if v.len() <= CAP {
Self::copy_from(&v)
} else {
Self::from_vec(v)
}
}
}

impl<T: Copy> IxDynRepr<T> {
fn from_vec(v: Vec<T>) -> Self {
IxDynRepr::Alloc(v.into_boxed_slice())
}

fn from(x: &[T]) -> Self {
Self::from_vec(x.to_vec())
}
}

impl<T: Copy> Clone for IxDynRepr<T> {
fn clone(&self) -> Self {
match *self {
IxDynRepr::Inline(len, arr) => {
IxDynRepr::Inline(len, arr)
}
_ => Self::from(&self[..])
}
}
}

impl<T: Eq> Eq for IxDynRepr<T> { }

impl<T: PartialEq> PartialEq for IxDynRepr<T> {
fn eq(&self, rhs: &Self) -> bool {
match (self, rhs) {
(&IxDynRepr::Inline(slen, ref sarr), &IxDynRepr::Inline(rlen, ref rarr)) => {
slen == rlen &&
(0..CAP as usize).filter(|&i| i < slen as usize)
.all(|i| sarr[i] == rarr[i])
}
_ => self[..] == rhs[..]
}
}
}

/// Dynamic dimension or index type.
///
/// Use `IxDyn` directly. This type implements a dynamic number of
/// dimensions or indices. Short dimensions are stored inline and don't need
/// any dynamic memory allocation.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct IxDynImpl(IxDynRepr<Ix>);
unsafe impl Send for IxDynImpl {}
unsafe impl Sync for IxDynImpl {}

impl IxDynImpl {
fn remove(&self, i: usize) -> Self {
IxDynImpl(match self.0 {
IxDynRepr::Inline(0, _) => IxDynRepr::Inline(0, [0; CAP]),
IxDynRepr::Inline(1, _) => IxDynRepr::Inline(0, [0; CAP]),
IxDynRepr::Inline(2, ref arr) => {
let mut out = [0; CAP];
out[0] = arr[1 - i];
IxDynRepr::Inline(1, out)
}
ref ixdyn => {
let len = ixdyn.len();
let mut result = IxDynRepr::copy_from(&ixdyn[..len - 1]);
for j in i..len - 1 {
result[j] = ixdyn[j + 1]
}
result
}
})
}
}

impl<'a> From<&'a [Ix]> for IxDynImpl {
#[inline]
fn from(ix: &'a [Ix]) -> Self {
IxDynImpl(IxDynRepr::copy_from(ix))
}
}

impl From<Vec<Ix>> for IxDynImpl {
#[inline]
fn from(ix: Vec<Ix>) -> Self {
IxDynImpl(IxDynRepr::from_vec_auto(ix))
}
}

impl<J> Index<J> for IxDynImpl
where [Ix]: Index<J>,
{
type Output = <[Ix] as Index<J>>::Output;
fn index(&self, index: J) -> &Self::Output {
&self.0[index]
}
}

impl<J> IndexMut<J> for IxDynImpl
where [Ix]: IndexMut<J>,
{
fn index_mut(&mut self, index: J) -> &mut Self::Output {
&mut self.0[index]
}
}

impl Deref for IxDynImpl {
type Target = [Ix];
#[inline]
fn deref(&self) -> &[Ix] {
&self.0
}
}

impl DerefMut for IxDynImpl {
#[inline]
fn deref_mut(&mut self) -> &mut [Ix] {
&mut self.0
}
}

impl<'a> IntoIterator for &'a IxDynImpl {
type Item = &'a Ix;
type IntoIter = <&'a [Ix] as IntoIterator>::IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self[..].into_iter()
}
}

impl RemoveAxis for Dim<IxDynImpl> {
type Smaller = Self;
fn remove_axis(&self, axis: Axis) -> Self {
debug_assert!(axis.index() < self.ndim());
Dim::new(self.ix().remove(axis.index()))
}
}
Loading