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

As standard layout method #616

Merged
merged 30 commits into from Jun 19, 2019
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b39a0bb
Merge pull request #1 from rust-ndarray/master
Apr 3, 2019
c0890db
Implemented `as_contiguous` method
Apr 12, 2019
4ea2c7d
Added content check for the new array to the tests
Apr 12, 2019
5cdae91
Return CowArray from as_contiguous instead of Array
Apr 26, 2019
c82359a
Removed unused import
Apr 26, 2019
5ce8d31
Fixed wording + minor improvements
Apr 28, 2019
2a67f25
Added more tests + 2 methods for ArrayCow
Apr 30, 2019
2d453c7
Split single test into multiple ones
May 3, 2019
0d54de5
Fix try_ensure_unique for CowRepr
jturner314 May 5, 2019
1f4499f
Remove into_view/owned_array from ArrayCow
jturner314 May 5, 2019
2ede464
Relax to A: Clone for RawDataClone impl of CowRepr
jturner314 May 5, 2019
17e074b
Implement DataMut for CowRepr
jturner314 May 5, 2019
5506113
Impl clone_from_with_ptr for differing CowRepr variants
jturner314 May 5, 2019
556bd59
Add Cow types to preludes
jturner314 May 5, 2019
3101ca3
Move ArrayCow method impls into separate module
jturner314 May 5, 2019
131aea7
Replace from_view/owned_array with From impls
jturner314 May 5, 2019
0d68ea6
Add docs for ArrayCow
jturner314 May 5, 2019
b84b3ec
Remove A: Clone bound from CowRepr
jturner314 May 5, 2019
ebd6cc4
Use .raw_dim() instead of .dim()
jturner314 May 6, 2019
b14b3de
Replace .map(|x| x.clone()) with .cloned()
jturner314 May 6, 2019
9ec732a
Merge pull request #4 from jturner314/as-contiguous-method
May 6, 2019
efd686d
Merge pull request #5 from rust-ndarray/master
May 15, 2019
1417bcb
Removed ArrayCow changes
May 31, 2019
8cd5d5a
Merge pull request #8 from rust-ndarray/master
May 31, 2019
d85d4a8
Merge branch 'master' of github.com:andrei-papou/ndarray into as-cont…
May 31, 2019
4c2d2dd
Updated the tests
May 31, 2019
ac51cfd
Fixed rustfmt errors
May 31, 2019
c2ae416
Changes:
Jun 3, 2019
02d181d
Refine as_standard_layout docs
jturner314 Jun 19, 2019
f669f0f
Add as_standard_layout example
jturner314 Jun 19, 2019
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
83 changes: 83 additions & 0 deletions src/data_traits.rs
Expand Up @@ -16,6 +16,7 @@ use crate::{
Dimension,
RawViewRepr,
ViewRepr,
CowRepr,
OwnedRepr,
OwnedRcRepr,
OwnedArcRepr,
Expand Down Expand Up @@ -412,3 +413,85 @@ unsafe impl<A> DataOwned for OwnedArcRepr<A> {
self
}
}

unsafe impl<'a, A> RawData for CowRepr<'a, A>
where A: Clone
{
type Elem = A;
fn _data_slice(&self) -> Option<&[A]> {
match self {
CowRepr::View(view) => view._data_slice(),
CowRepr::Owned(data) => data._data_slice(),
}
}
private_impl!{}
}

unsafe impl<'a, A> RawDataMut for CowRepr<'a, A>
where A: Clone
{
#[inline]
fn try_ensure_unique<D>(array: &mut ArrayBase<Self, D>)
where Self: Sized,
D: Dimension
{
array.ensure_is_owned();
}

#[inline]
fn try_is_unique(&mut self) -> Option<bool> {
Some(self.is_owned())
}
}

unsafe impl<'a, A> RawDataClone for CowRepr<'a, A>
where A: Copy
{
unsafe fn clone_with_ptr(&self, ptr: *mut Self::Elem) -> (Self, *mut Self::Elem) {
match self {
CowRepr::View(view) => {
let (new_view, ptr) = view.clone_with_ptr(ptr);
(CowRepr::View(new_view), ptr)
},
CowRepr::Owned(data) => {
let (new_data, ptr) = data.clone_with_ptr(ptr);
(CowRepr::Owned(new_data), ptr)
},
}
}

#[doc(hidden)]
unsafe fn clone_from_with_ptr(&mut self, other: &Self, ptr: *mut Self::Elem) -> *mut Self::Elem {
match self {
CowRepr::View(view) => {
match other {
CowRepr::View(other_view) => view.clone_from_with_ptr(other_view, ptr),
CowRepr::Owned(_) => panic!("Cannot copy `CowRepr::View` from `CowRepr::Temp`"),
}
},
CowRepr::Owned(data) => {
match other {
CowRepr::View(_) => panic!("Cannot copy `CowRepr::Temp` from `CowRepr::View`"),
CowRepr::Owned(other_data) => data.clone_from_with_ptr(other_data, ptr),
}
},
}
}
}

unsafe impl<'a, A> Data for CowRepr<'a, A>
where A: Clone
{
#[inline]
fn into_owned<D>(self_: ArrayBase<CowRepr<'a, A>, D>) -> ArrayBase<OwnedRepr<Self::Elem>, D>
where
A: Clone,
D: Dimension,
{
if self_.data.is_view() {
ViewRepr::into_owned(self_.into_view_array().unwrap())
} else {
OwnedRepr::into_owned(self_.into_owned_array().unwrap())
}
}
}
18 changes: 17 additions & 1 deletion src/impl_methods.rs
Expand Up @@ -14,7 +14,7 @@ use itertools::{izip, zip};

use crate::imp_prelude::*;

use crate::arraytraits;
use crate::{arraytraits, ArrayCow};
use crate::dimension;
use crate::error::{self, ShapeError, ErrorKind};
use crate::dimension::IntoDimension;
Expand Down Expand Up @@ -1225,6 +1225,22 @@ where
D::is_contiguous(&self.dim, &self.strides)
}

pub fn as_standard_layout(&self) -> ArrayCow<'_, A, D>
where S: Data<Elem=A>,
A: Clone
{
if self.is_standard_layout() {
ArrayCow::from_view_array(self.view())
} else {
let v = self.iter().map(|x| x.clone()).collect::<Vec<A>>();
let owned_array: Array<A, D> = unsafe {
// Safe because we use shape and content of existing array here.
ArrayBase::from_shape_vec_unchecked(self.dim(), v)
};
ArrayCow::from_owned_array(owned_array)
}
}

/// Return a pointer to the first element in the array.
///
/// Raw access to array elements needs to follow the strided indexing
Expand Down
87 changes: 87 additions & 0 deletions src/lib.rs
Expand Up @@ -1374,6 +1374,30 @@ impl<A> ViewRepr<A> {
}
}

pub enum CowRepr<'a, A>
where A: Clone
{
View(ViewRepr<&'a A>),
Owned(OwnedRepr<A>),
}

impl<'a, A> CowRepr<'a, A>
where A: Clone
{
pub fn is_view(&self) -> bool {
match self {
CowRepr::View(_) => true,
CowRepr::Owned(_) => false,
}
}

pub fn is_owned(&self) -> bool {
!self.is_view()
}
}

pub type ArrayCow<'a, A, D> = ArrayBase<CowRepr<'a, A>, D>;

mod impl_clone;

mod impl_constructors;
Expand Down Expand Up @@ -1472,6 +1496,69 @@ impl<A, S, D> ArrayBase<S, D>
}
}

impl<'a, A, D> ArrayCow<'a, A, D>
where A: Clone,
D: Dimension
{
fn from_view_array(array: ArrayView<'a, A, D>) -> ArrayCow<'a, A, D> {
ArrayBase {
data: CowRepr::View(array.data),
ptr: array.ptr,
dim: array.dim,
strides: array.strides,
}
}

fn from_owned_array(array: Array<A, D>) -> ArrayCow<'a, A, D> {
ArrayBase {
data: CowRepr::Owned(array.data),
ptr: array.ptr,
dim: array.dim,
strides: array.strides,
}
}

fn into_view_array(self) -> Option<ArrayView<'a, A, D>> {
match self.data {
CowRepr::View(view) => Some(ArrayBase {
data: view,
ptr: self.ptr,
dim: self.dim,
strides: self.strides,
}),
CowRepr::Owned(_) => None,
}
}

fn into_owned_array(self) -> Option<Array<A, D>> {
match self.data {
CowRepr::View(_) => None,
CowRepr::Owned(data) => Some(ArrayBase {
data,
ptr: self.ptr,
dim: self.dim,
strides: self.strides,
})
}
}

fn ensure_is_owned(&mut self) {
if self.data.is_view() {
let mut copied_data: Vec<A> = self.iter().map(|x| x.clone()).collect();
self.ptr = copied_data.as_mut_ptr();
self.data = CowRepr::Owned(OwnedRepr(copied_data));
}
}

pub fn is_view(&self) -> bool {
self.data.is_view()
}

pub fn is_owned(&self) -> bool {
self.data.is_owned()
}
}


// parallel methods
#[cfg(feature="rayon")]
Expand Down
65 changes: 64 additions & 1 deletion tests/array.rs
Expand Up @@ -4,7 +4,7 @@ extern crate ndarray;
extern crate defmac;
extern crate itertools;

use ndarray::{Slice, SliceInfo, SliceOrIndex};
use ndarray::{Slice, SliceInfo, SliceOrIndex, Data};
use ndarray::prelude::*;
use ndarray::{
rcarr2,
Expand Down Expand Up @@ -1891,3 +1891,66 @@ fn array_macros() {
let empty2: Array<f32, Ix2> = array![[]];
assert_eq!(empty2, array![[]]);
}

#[test]
fn test_as_contiguous() {
LukeMathWalker marked this conversation as resolved.
Show resolved Hide resolved
fn is_content_identical<A, S1, S2, D>(arr1: &ArrayBase<S1, D>, arr2: &ArrayBase<S2, D>) -> bool
where A: Clone + PartialEq,
S1: Data<Elem=A>,
S2: Data<Elem=A>,
D: Dimension
{
arr1.iter().zip(arr2.iter()).all(|(x1, x2)| x1 == x2)
}

// F layout
let shape = Ix2(2, 2).strides(Ix2(1, 2));
let arr = Array::<i32, Ix2>::from_shape_vec(shape, vec![1, 2, 3, 4]).unwrap();
assert!(!arr.is_standard_layout());
let cont_arr = arr.as_standard_layout();
assert!(cont_arr.is_standard_layout());
assert!(is_content_identical(&arr, &cont_arr));
assert!(cont_arr.is_owned());

// C layout
let arr = Array::<i32, Ix2>::from_shape_vec((2, 2), vec![1, 2, 3, 4]).unwrap();
assert!(arr.is_standard_layout());
let cont_arr = arr.as_standard_layout();
assert!(cont_arr.is_standard_layout());
assert!(is_content_identical(&arr, &cont_arr));
assert!(cont_arr.is_view());

// F layout view
let shape = Ix2(2, 2).strides(Ix2(1, 2));
let arr = Array::<i32, Ix2>::from_shape_vec(shape, vec![1, 2, 3, 4]).unwrap();
let arr_view = arr.view();
assert!(!arr_view.is_standard_layout());
let cont_arr = arr.as_standard_layout();
assert!(cont_arr.is_standard_layout());
assert!(is_content_identical(&arr, &cont_arr));
assert!(cont_arr.is_owned());

// C layout view
let arr = Array::<i32, Ix2>::from_shape_vec((2, 2), vec![1, 2, 3, 4]).unwrap();
let arr_view = arr.view();
assert!(arr_view.is_standard_layout());
let cont_arr = arr_view.as_standard_layout();
assert!(is_content_identical(&arr, &cont_arr));
assert!(cont_arr.is_view());

// 0 dimensional array
let arr_view = ArrayView1::<i32>::from(&[]);
assert!(arr_view.is_standard_layout());
let cont_arr = arr_view.as_standard_layout();
assert!(is_content_identical(&arr_view, &cont_arr));
assert!(cont_arr.is_view());

// Custom layout
let shape = Ix4(1, 2, 3, 2).strides(Ix4(12, 1, 2, 6));
let arr_data: Vec<i32> = (0..12).collect();
let arr = Array::<i32, Ix4>::from_shape_vec(shape, arr_data).unwrap();
assert!(!arr.is_standard_layout());
let cont_arr = arr.as_standard_layout();
assert!(is_content_identical(&arr, &cont_arr));
assert!(cont_arr.is_owned());
}