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

Implement construction from negative strides #901

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
101 changes: 100 additions & 1 deletion src/dimension/conversion.rs
Expand Up @@ -12,7 +12,7 @@ use num_traits::Zero;
use std::ops::{Index, IndexMut};
use alloc::vec::Vec;

use crate::{Dim, Dimension, Ix, Ix1, IxDyn, IxDynImpl};
use crate::{Dim, Dimension, Ix, Ix1, IxDyn, IxDynImpl, Ixs};

/// $m: macro callback
/// $m is called with $arg and then the indices corresponding to the size argument
Expand Down Expand Up @@ -41,11 +41,13 @@ macro_rules! index_item {
/// Argument conversion a dimension.
pub trait IntoDimension {
type Dim: Dimension;
type Strides: IntoStrides<Dim = Self::Dim>;
fn into_dimension(self) -> Self::Dim;
}

impl IntoDimension for Ix {
type Dim = Ix1;
type Strides = Ixs;
#[inline(always)]
fn into_dimension(self) -> Ix1 {
Ix1(self)
Expand All @@ -57,6 +59,7 @@ where
D: Dimension,
{
type Dim = D;
type Strides = D;
#[inline(always)]
fn into_dimension(self) -> Self {
self
Expand All @@ -65,6 +68,7 @@ where

impl IntoDimension for IxDynImpl {
type Dim = IxDyn;
type Strides = IxDyn;
#[inline(always)]
fn into_dimension(self) -> Self::Dim {
Dim::new(self)
Expand All @@ -73,6 +77,7 @@ impl IntoDimension for IxDynImpl {

impl IntoDimension for Vec<Ix> {
type Dim = IxDyn;
type Strides = Vec<Ixs>;
#[inline(always)]
fn into_dimension(self) -> Self::Dim {
Dim::new(IxDynImpl::from(self))
Expand Down Expand Up @@ -127,6 +132,7 @@ macro_rules! tuple_to_array {

impl IntoDimension for [Ix; $n] {
type Dim = Dim<[Ix; $n]>;
type Strides = [Ixs; $n];
#[inline(always)]
fn into_dimension(self) -> Self::Dim {
Dim::new(self)
Expand All @@ -135,6 +141,7 @@ macro_rules! tuple_to_array {

impl IntoDimension for index!(tuple_type [Ix] $n) {
type Dim = Dim<[Ix; $n]>;
type Strides = index!(tuple_type [Ixs] $n);
#[inline(always)]
fn into_dimension(self) -> Self::Dim {
Dim::new(index!(array_expr [self] $n))
Expand Down Expand Up @@ -171,3 +178,95 @@ macro_rules! tuple_to_array {
}

index_item!(tuple_to_array [] 7);

/// Argument conversion strides.
pub trait IntoStrides {
type Dim: Dimension;
fn into_strides(self) -> Self::Dim;
}

impl IntoStrides for Ixs {
type Dim = Ix1;
#[inline(always)]
fn into_strides(self) -> Ix1 {
Ix1(self as Ix)
}
}

impl<D> IntoStrides for D
where
D: Dimension,
{
type Dim = D;
#[inline(always)]
fn into_strides(self) -> D {
self
}
}

impl IntoStrides for Vec<Ixs> {
type Dim = IxDyn;
#[inline(always)]
fn into_strides(self) -> IxDyn {
let v: Vec<Ix> = self.into_iter().map(|x| x as Ix).collect();
Dim::new(IxDynImpl::from(v))
}
}

impl<'a> IntoStrides for &'a [Ixs] {
type Dim = IxDyn;
#[inline(always)]
fn into_strides(self) -> IxDyn {
let v: Vec<Ix> = self.iter().map(|x| *x as Ix).collect();
Dim::new(IxDynImpl::from(v))
}
}

macro_rules! index_item_ixs {
($m:ident $arg:tt 0) => ();
($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! array_expr_ixs {
([$self_:expr] $($index:tt)*) => (
[$($self_[$index] as Ix, )*]
)
}

macro_rules! tuple_expr_ixs {
([$self_:expr] $($index:tt)*) => (
[$($self_.$index as Ix, )*]
)
}

macro_rules! tuple_to_strides {
([] $($n:tt)*) => {
$(
impl IntoStrides for [Ixs; $n] {
type Dim = Dim<[Ix; $n]>;
#[inline(always)]
fn into_strides(self) -> Dim<[Ix; $n]> {
let self_: [Ix; $n] = index!(array_expr_ixs [self] $n);
Dim::new(self_)
}
}

impl IntoStrides for index!(tuple_type [Ixs] $n) {
type Dim = Dim<[Ix; $n]>;
#[inline(always)]
fn into_strides(self) -> Dim<[Ix; $n]> {
Dim::new(index!(tuple_expr_ixs [self] $n))
}
}

)*
}
}

index_item_ixs!(tuple_to_strides [] 7);
4 changes: 2 additions & 2 deletions src/dimension/mod.rs
Expand Up @@ -12,7 +12,7 @@ use num_integer::div_floor;

pub use self::axes::{axes_of, Axes, AxisDescription};
pub use self::axis::Axis;
pub use self::conversion::IntoDimension;
pub use self::conversion::{IntoDimension, IntoStrides};
pub use self::dim::*;
pub use self::dimension_trait::Dimension;
pub use self::dynindeximpl::IxDynImpl;
Expand Down Expand Up @@ -402,7 +402,7 @@ fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize) {
/// memory of the array. The result is always <= 0.
pub fn offset_from_ptr_to_memory<D: Dimension>(dim: &D, strides: &D) -> isize {
let offset = izip!(dim.slice(), strides.slice()).fold(0, |_offset, (d, s)| {
if (*s as isize) < 0 {
if (*s as isize) < 0 && *d != 0 {
_offset + *s as isize * (*d as isize - 1)
} else {
_offset
Expand Down
5 changes: 2 additions & 3 deletions src/dimension/ndindex.rs
Expand Up @@ -2,9 +2,7 @@ use std::fmt::Debug;

use super::{stride_offset, stride_offset_checked};
use crate::itertools::zip;
use crate::{
Dim, Dimension, IntoDimension, Ix, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, IxDynImpl,
};
use crate::{Dim, Dimension, IntoDimension, Ix, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, IxDynImpl, Ixs};

/// Tuple or fixed size arrays that can be used to index an array.
///
Expand Down Expand Up @@ -199,6 +197,7 @@ ndindex_with_array! {

impl<'a> IntoDimension for &'a [Ix] {
type Dim = IxDyn;
type Strides = &'a [Ixs];
fn into_dimension(self) -> Self::Dim {
Dim(IxDynImpl::from(self))
}
Expand Down
5 changes: 3 additions & 2 deletions src/impl_views/constructors.rs
Expand Up @@ -13,6 +13,7 @@ use crate::error::ShapeError;
use crate::extension::nonnull::nonnull_debug_checked_from_ptr;
use crate::imp_prelude::*;
use crate::{is_aligned, StrideShape};
use crate::dimension::offset_from_ptr_to_memory;

/// Methods for read-only array views.
impl<'a, A, D> ArrayView<'a, A, D>
Expand Down Expand Up @@ -55,7 +56,7 @@ where
let dim = shape.dim;
dimension::can_index_slice_with_strides(xs, &dim, &shape.strides)?;
let strides = shape.strides.strides_for_dim(&dim);
unsafe { Ok(Self::new_(xs.as_ptr(), dim, strides)) }
unsafe { Ok(Self::new_(xs.as_ptr().offset(-offset_from_ptr_to_memory(&dim, &strides)), dim, strides)) }
}

/// Create an `ArrayView<A, D>` from shape information and a raw pointer to
Expand Down Expand Up @@ -152,7 +153,7 @@ where
let dim = shape.dim;
dimension::can_index_slice_with_strides(xs, &dim, &shape.strides)?;
let strides = shape.strides.strides_for_dim(&dim);
unsafe { Ok(Self::new_(xs.as_mut_ptr(), dim, strides)) }
unsafe { Ok(Self::new_(xs.as_mut_ptr().offset(-offset_from_ptr_to_memory(&dim, &strides)), dim, strides)) }
}

/// Create an `ArrayViewMut<A, D>` from shape information and a
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Expand Up @@ -134,7 +134,7 @@ use std::marker::PhantomData;
use alloc::sync::Arc;

pub use crate::dimension::dim::*;
pub use crate::dimension::{Axis, AxisDescription, Dimension, IntoDimension, RemoveAxis};
pub use crate::dimension::{Axis, AxisDescription, Dimension, IntoDimension, IntoStrides, RemoveAxis};

pub use crate::dimension::IxDynImpl;
pub use crate::dimension::NdIndex;
Expand Down
8 changes: 4 additions & 4 deletions src/shape_builder.rs
@@ -1,4 +1,4 @@
use crate::dimension::IntoDimension;
use crate::dimension::{IntoDimension, IntoStrides};
use crate::Dimension;

/// A contiguous array shape of n dimensions.
Expand Down Expand Up @@ -111,7 +111,7 @@ where
T: IntoDimension,
{
type Dim = T::Dim;
type Strides = T;
type Strides = T::Strides;
fn into_shape(self) -> Shape<Self::Dim> {
Shape {
dim: self.into_dimension(),
Expand All @@ -124,8 +124,8 @@ where
fn set_f(self, is_f: bool) -> Shape<Self::Dim> {
self.into_shape().set_f(is_f)
}
fn strides(self, st: T) -> StrideShape<Self::Dim> {
self.into_shape().strides(st.into_dimension())
fn strides(self, st: Self::Strides) -> StrideShape<Self::Dim> {
self.into_shape().strides(st.into_strides())
}
}

Expand Down
44 changes: 44 additions & 0 deletions tests/array.rs
Expand Up @@ -1896,6 +1896,50 @@ fn test_shape() {
assert_eq!(a.strides(), &[6, 3, 1]);
assert_eq!(b.strides(), &[1, 1, 2]);
assert_eq!(c.strides(), &[1, 3, 1]);

// negative strides
let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13].to_vec();
let a= Array::from_shape_vec((2, 3, 2).strides((-1, -4, 2)),s.clone()).unwrap();
assert_eq!(a, arr3(&[[[9, 11], [5, 7], [1, 3]], [[8, 10], [4, 6], [0, 2]]]));
assert_eq!(a.shape(), &[2, 3, 2]);
assert_eq!(a.strides(), &[-1, -4, 2]);

// ()
let b=Array::from_shape_vec(().strides(()),s.clone()).unwrap();
assert_eq!(b,arr0(0));

let v = vec![5];
let mut c = ArrayView2::<u8>::from_shape((1, 1).strides((-10, -1)), v.as_slice()).unwrap();
assert_eq!(c, arr2(&[[5]]));
c.slice_collapse(s![1..1, ..]);
assert_eq!(c.shape(), &[0, 1]);

// discontinuous
let d = Array3::from_shape_vec((3, 2, 2).strides((-8, -4, -2)), (0..24).collect()).unwrap();
assert_eq!(d, arr3(&[[[22, 20], [18, 16]], [[14, 12], [10, 8]], [[6, 4], [2, 0]]]));

// empty
let empty: [f32; 0] = [];
let e = Array::from_shape_vec(0.strides(-2), empty.to_vec()).unwrap();
assert_eq!(e, arr1(&[]));

let a = [1., 2., 3., 4., 5., 6.];
let d = (2, 1, 1);
let s = (-2, 2, 1);
let b = ArrayView::from_shape(d.strides(s), &a).unwrap();
assert_eq!(b, arr3(&[[[3.0]], [[1.0]]]));

let d = (1, 2, 1);
let s = (2, -2, -1);
let b = Array::from_shape_vec(d.strides(s), a.to_vec()).unwrap();
assert_eq!(b, arr3(&[[[3.0], [1.0]]]));

let a: [f32; 0] = [];
// [[]] shape=[4, 0], strides=[0, 1]
let d = (4, 0);
let s = (0, -1);
let b = ArrayView::from_shape(d.strides(s), &a).unwrap();
assert_eq!(b, arr2(&[[],[],[],[]]));
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion tests/raw_views.rs
Expand Up @@ -89,7 +89,7 @@ fn raw_view_negative_strides() {
fn misaligned_deref(data: &[u16; 2]) -> ArrayView1<'_, u16> {
let ptr: *const u16 = data.as_ptr();
unsafe {
let raw_view = RawArrayView::from_shape_ptr(1.strides((-1isize) as usize), ptr);
let raw_view = RawArrayView::from_shape_ptr(1.strides(-1), ptr);
raw_view.deref_into_view()
}
}
Expand Down