Skip to content

Commit dcd9012

Browse files
authored
Merge pull request #301 from bluss/ixdyn
Reimplement IxDyn, with no allocation for the small dimension case
2 parents 2e45375 + 204a150 commit dcd9012

14 files changed

+353
-39
lines changed

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ matrixmultiply = { version = "0.1.13" }
4646
version = "0.9"
4747
optional = true
4848

49+
[dev-dependencies]
50+
defmac = "0.1"
51+
4952
[features]
5053
blas = ["blas-sys"]
5154

src/aliases.rs

+22-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
//! Type aliases for common array sizes
22
//!
33
4-
use ::{Ix, Array, ArrayView, ArrayViewMut, RcArray};
4+
use ::{
5+
Ix,
6+
Array,
7+
ArrayView,
8+
ArrayViewMut,
9+
RcArray,
10+
IxDynImpl,
11+
};
512
use ::dimension::Dim;
613
use dimension::DimPrivate;
714

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

48+
/// Create a dynamic-dimensional index
49+
#[allow(non_snake_case)]
50+
#[inline(always)]
51+
pub fn IxDyn(ix: &[Ix]) -> IxDyn {
52+
Dim(ix)
53+
}
54+
4155
/// zero-dimensionial
4256
pub type Ix0 = Dim<[Ix; 0]>;
4357
/// one-dimensional
@@ -54,16 +68,18 @@ pub type Ix5 = Dim<[Ix; 5]>;
5468
pub type Ix6 = Dim<[Ix; 6]>;
5569
/// dynamic-dimensional
5670
///
57-
/// `Vec<Ix>` and `&[usize]` implement `IntoDimension` to produce `IxDyn`;
58-
/// use them to create arrays with a dynamic number of axes.
71+
/// You can use the `IxDyn` function to create a dimension for an array with
72+
/// dynamic number of dimensions. (`Vec<Ix>` and `&[usize]` also implement
73+
/// `IntoDimension` to produce `IxDyn`).
5974
///
6075
/// ```
6176
/// use ndarray::ArrayD;
77+
/// use ndarray::IxDyn;
6278
///
6379
/// // Create a 5 × 6 × 3 × 4 array using the dynamic dimension type
64-
/// let mut a = ArrayD::<f64>::zeros(vec![5, 6, 3, 4]);
80+
/// let mut a = ArrayD::<f64>::zeros(IxDyn(&[5, 6, 3, 4]));
6581
/// // Create a 1 × 3 × 4 array using the dynamic dimension type
66-
/// let mut b = ArrayD::<f64>::zeros(vec![1, 3, 4]);
82+
/// let mut b = ArrayD::<f64>::zeros(IxDyn(&[1, 3, 4]));
6783
///
6884
/// // We can use broadcasting to add arrays of compatible shapes together:
6985
/// a += &b;
@@ -78,7 +94,7 @@ pub type Ix6 = Dim<[Ix; 6]>;
7894
/// // the same type `Array<f64, IxDyn>` a.k.a `ArrayD<f64>`:
7995
/// let arrays = vec![a, b];
8096
/// ```
81-
pub type IxDyn = Dim<Vec<Ix>>;
97+
pub type IxDyn = Dim<IxDynImpl>;
8298

8399
/// zero-dimensional array
84100
pub type Array0<A> = Array<A, Ix0>;

src/arraytraits.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,11 @@ impl<'a, A: 'a, D, T> AsArray<'a, A, D> for T
304304
///
305305
/// The array is created with dimension `D::default()`, which results
306306
/// in for example dimensions `0` and `(0, 0)` with zero elements for the
307-
/// one-dimensional and two-dimensional cases respectively, while for example
308-
/// the zero dimensional case uses `()` (or `Vec::new()`) which
309-
/// results in an array with one element.
307+
/// one-dimensional and two-dimensional cases respectively.
308+
///
309+
/// The default dimension for `IxDyn` is `IxDyn(&[0])` (array has zero
310+
/// elements). And the default for the dimension `()` is `()` (array has
311+
/// one element).
310312
///
311313
/// Since arrays cannot grow, the intention is to use the default value as
312314
/// placeholder.

src/dimension/conversion.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use std::ops::{Index, IndexMut};
1212
use libnum::Zero;
1313

14-
use {Ix, Ix1, IxDyn, Dimension, Dim};
14+
use {Ix, Ix1, IxDyn, Dimension, Dim, IxDynImpl};
1515
use super::DimPrivate;
1616

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

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

65+
impl IntoDimension for Vec<Ix> {
66+
type Dim = IxDyn;
67+
#[inline(always)]
68+
fn into_dimension(self) -> Self::Dim { Dim::new(IxDynImpl::from(self)) }
69+
}
70+
6571
pub trait Convert {
6672
type To;
6773
fn convert(self) -> Self::To;

src/dimension/dimension_trait.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use std::ops::{Add, Sub, Mul, AddAssign, SubAssign, MulAssign};
1313

1414
use itertools::{enumerate, zip};
1515

16-
use {Ix, Ixs, Ix0, Ix1, Ix2, Ix3, IxDyn, Dim, Si};
16+
use {Ix, Ixs, Ix0, Ix1, Ix2, Ix3, IxDyn, Dim, Si, IxDynImpl};
1717
use IntoDimension;
1818
use RemoveAxis;
1919
use {ArrayView1, ArrayViewMut1};
@@ -53,7 +53,7 @@ pub unsafe trait Dimension : Clone + Eq + Debug + Send + Sync + Default +
5353
/// - For `Ix1`: `[Si; 1]`
5454
/// - For `Ix2`: `[Si; 2]`
5555
/// - and so on..
56-
/// - For `Vec<Ix>`: `[Si]`
56+
/// - For `IxDyn: `[Si]`
5757
///
5858
/// The easiest way to create a `&SliceArg` is using the macro
5959
/// [`s![]`](macro.s!.html).
@@ -63,7 +63,7 @@ pub unsafe trait Dimension : Clone + Eq + Debug + Send + Sync + Default +
6363
/// - For `Ix1`: `usize`,
6464
/// - For `Ix2`: `(usize, usize)`
6565
/// - and so on..
66-
/// - For `Vec<Ix>`: `Vec<usize>`,
66+
/// - For `IxDyn: `IxDyn`
6767
type Pattern: IntoDimension<Dim=Self>;
6868
// Next smaller dimension (if it exists)
6969
#[doc(hidden)]
@@ -734,7 +734,7 @@ large_dim!(4, Ix4, Ix, Ix, Ix, Ix);
734734
large_dim!(5, Ix5, Ix, Ix, Ix, Ix, Ix);
735735
large_dim!(6, Ix6, Ix, Ix, Ix, Ix, Ix, Ix);
736736

737-
/// Vec<Ix> is a "dynamic" index, pretty hard to use when indexing,
737+
/// IxDyn is a "dynamic" index, pretty hard to use when indexing,
738738
/// and memory wasteful, but it allows an arbitrary and dynamic number of axes.
739739
unsafe impl Dimension for IxDyn
740740
{
@@ -761,17 +761,17 @@ unsafe impl Dimension for IxDyn
761761
}
762762
}
763763

764-
impl<J> Index<J> for Dim<Vec<usize>>
765-
where Vec<usize>: Index<J>,
764+
impl<J> Index<J> for Dim<IxDynImpl>
765+
where IxDynImpl: Index<J>,
766766
{
767-
type Output = <Vec<usize> as Index<J>>::Output;
767+
type Output = <IxDynImpl as Index<J>>::Output;
768768
fn index(&self, index: J) -> &Self::Output {
769769
&self.ix()[index]
770770
}
771771
}
772772

773-
impl<J> IndexMut<J> for Dim<Vec<usize>>
774-
where Vec<usize>: IndexMut<J>,
773+
impl<J> IndexMut<J> for Dim<IxDynImpl>
774+
where IxDynImpl: IndexMut<J>,
775775
{
776776
fn index_mut(&mut self, index: J) -> &mut Self::Output {
777777
&mut self.ixm()[index]

src/dimension/dynindeximpl.rs

+211
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
2+
use std::ops::{
3+
Index,
4+
IndexMut,
5+
Deref,
6+
DerefMut,
7+
};
8+
use imp_prelude::*;
9+
use dimension::DimPrivate;
10+
11+
const CAP: usize = 4;
12+
13+
/// T is usize or isize
14+
#[derive(Debug)]
15+
enum IxDynRepr<T> {
16+
Inline(u32, [T; CAP]),
17+
Alloc(Box<[T]>),
18+
}
19+
20+
impl<T> Deref for IxDynRepr<T> {
21+
type Target = [T];
22+
fn deref(&self) -> &[T] {
23+
match *self {
24+
IxDynRepr::Inline(len, ref ar) => {
25+
unsafe {
26+
ar.get_unchecked(..len as usize)
27+
}
28+
}
29+
IxDynRepr::Alloc(ref ar) => &*ar,
30+
}
31+
}
32+
}
33+
34+
impl<T> DerefMut for IxDynRepr<T> {
35+
fn deref_mut(&mut self) -> &mut [T] {
36+
match *self {
37+
IxDynRepr::Inline(len, ref mut ar) => {
38+
unsafe {
39+
ar.get_unchecked_mut(..len as usize)
40+
}
41+
}
42+
IxDynRepr::Alloc(ref mut ar) => &mut *ar,
43+
}
44+
}
45+
}
46+
47+
/// The default is equivalent to `Self::from(&[0])`.
48+
impl Default for IxDynRepr<Ix> {
49+
fn default() -> Self {
50+
Self::copy_from(&[0])
51+
}
52+
}
53+
54+
55+
use ::libnum::Zero;
56+
57+
impl<T: Copy + Zero> IxDynRepr<T> {
58+
pub fn copy_from(x: &[T]) -> Self {
59+
if x.len() <= CAP {
60+
let mut arr = [T::zero(); CAP];
61+
for i in 0..x.len() {
62+
arr[i] = x[i];
63+
}
64+
IxDynRepr::Inline(x.len() as _, arr)
65+
} else {
66+
Self::from(x)
67+
}
68+
}
69+
}
70+
71+
impl<T: Copy + Zero> IxDynRepr<T> {
72+
// make an Inline or Alloc version as appropriate
73+
fn from_vec_auto(v: Vec<T>) -> Self {
74+
if v.len() <= CAP {
75+
Self::copy_from(&v)
76+
} else {
77+
Self::from_vec(v)
78+
}
79+
}
80+
}
81+
82+
impl<T: Copy> IxDynRepr<T> {
83+
fn from_vec(v: Vec<T>) -> Self {
84+
IxDynRepr::Alloc(v.into_boxed_slice())
85+
}
86+
87+
fn from(x: &[T]) -> Self {
88+
Self::from_vec(x.to_vec())
89+
}
90+
}
91+
92+
impl<T: Copy> Clone for IxDynRepr<T> {
93+
fn clone(&self) -> Self {
94+
match *self {
95+
IxDynRepr::Inline(len, arr) => {
96+
IxDynRepr::Inline(len, arr)
97+
}
98+
_ => Self::from(&self[..])
99+
}
100+
}
101+
}
102+
103+
impl<T: Eq> Eq for IxDynRepr<T> { }
104+
105+
impl<T: PartialEq> PartialEq for IxDynRepr<T> {
106+
fn eq(&self, rhs: &Self) -> bool {
107+
match (self, rhs) {
108+
(&IxDynRepr::Inline(slen, ref sarr), &IxDynRepr::Inline(rlen, ref rarr)) => {
109+
slen == rlen &&
110+
(0..CAP as usize).filter(|&i| i < slen as usize)
111+
.all(|i| sarr[i] == rarr[i])
112+
}
113+
_ => self[..] == rhs[..]
114+
}
115+
}
116+
}
117+
118+
/// Dynamic dimension or index type.
119+
///
120+
/// Use `IxDyn` directly. This type implements a dynamic number of
121+
/// dimensions or indices. Short dimensions are stored inline and don't need
122+
/// any dynamic memory allocation.
123+
#[derive(Debug, Clone, PartialEq, Eq, Default)]
124+
pub struct IxDynImpl(IxDynRepr<Ix>);
125+
unsafe impl Send for IxDynImpl {}
126+
unsafe impl Sync for IxDynImpl {}
127+
128+
impl IxDynImpl {
129+
fn remove(&self, i: usize) -> Self {
130+
IxDynImpl(match self.0 {
131+
IxDynRepr::Inline(0, _) => IxDynRepr::Inline(0, [0; CAP]),
132+
IxDynRepr::Inline(1, _) => IxDynRepr::Inline(0, [0; CAP]),
133+
IxDynRepr::Inline(2, ref arr) => {
134+
let mut out = [0; CAP];
135+
out[0] = arr[1 - i];
136+
IxDynRepr::Inline(1, out)
137+
}
138+
ref ixdyn => {
139+
let len = ixdyn.len();
140+
let mut result = IxDynRepr::copy_from(&ixdyn[..len - 1]);
141+
for j in i..len - 1 {
142+
result[j] = ixdyn[j + 1]
143+
}
144+
result
145+
}
146+
})
147+
}
148+
}
149+
150+
impl<'a> From<&'a [Ix]> for IxDynImpl {
151+
#[inline]
152+
fn from(ix: &'a [Ix]) -> Self {
153+
IxDynImpl(IxDynRepr::copy_from(ix))
154+
}
155+
}
156+
157+
impl From<Vec<Ix>> for IxDynImpl {
158+
#[inline]
159+
fn from(ix: Vec<Ix>) -> Self {
160+
IxDynImpl(IxDynRepr::from_vec_auto(ix))
161+
}
162+
}
163+
164+
impl<J> Index<J> for IxDynImpl
165+
where [Ix]: Index<J>,
166+
{
167+
type Output = <[Ix] as Index<J>>::Output;
168+
fn index(&self, index: J) -> &Self::Output {
169+
&self.0[index]
170+
}
171+
}
172+
173+
impl<J> IndexMut<J> for IxDynImpl
174+
where [Ix]: IndexMut<J>,
175+
{
176+
fn index_mut(&mut self, index: J) -> &mut Self::Output {
177+
&mut self.0[index]
178+
}
179+
}
180+
181+
impl Deref for IxDynImpl {
182+
type Target = [Ix];
183+
#[inline]
184+
fn deref(&self) -> &[Ix] {
185+
&self.0
186+
}
187+
}
188+
189+
impl DerefMut for IxDynImpl {
190+
#[inline]
191+
fn deref_mut(&mut self) -> &mut [Ix] {
192+
&mut self.0
193+
}
194+
}
195+
196+
impl<'a> IntoIterator for &'a IxDynImpl {
197+
type Item = &'a Ix;
198+
type IntoIter = <&'a [Ix] as IntoIterator>::IntoIter;
199+
#[inline]
200+
fn into_iter(self) -> Self::IntoIter {
201+
self[..].into_iter()
202+
}
203+
}
204+
205+
impl RemoveAxis for Dim<IxDynImpl> {
206+
type Smaller = Self;
207+
fn remove_axis(&self, axis: Axis) -> Self {
208+
debug_assert!(axis.index() < self.ndim());
209+
Dim::new(self.ix().remove(axis.index()))
210+
}
211+
}

0 commit comments

Comments
 (0)