1
- //! Defines conversion traits between rust types and numpy data types.
1
+ //! Defines conversion traits between Rust types and NumPy data types.
2
+ #![ deny( missing_docs) ]
3
+
4
+ use std:: { mem, os:: raw:: c_int} ;
2
5
3
6
use ndarray:: { ArrayBase , Data , Dimension , IntoDimension , Ix1 , OwnedRepr } ;
4
7
use pyo3:: Python ;
5
8
6
- use std:: { mem, os:: raw:: c_int} ;
9
+ use crate :: array:: PyArray ;
10
+ use crate :: dtype:: Element ;
11
+ use crate :: npyffi:: { self , npy_intp} ;
12
+ use crate :: sealed:: Sealed ;
7
13
8
- use crate :: {
9
- npyffi:: { self , npy_intp} ,
10
- Element , PyArray ,
11
- } ;
12
-
13
- /// Conversion trait from some rust types to `PyArray`.
14
+ /// Conversion trait from owning Rust types into [`PyArray`].
14
15
///
15
- /// This trait takes `self`, which means **it holds a pointer to Rust heap, until `resize` or other
16
- /// destructive method is called**.
16
+ /// This trait takes ownership of `self`, which means it holds a pointer into the Rust heap.
17
17
///
18
- /// In addition, if you construct `PyArray` via this method,
19
- /// **you cannot use some destructive methods like `resize`.**
18
+ /// In addition, some destructive methods like `resize` cannot be used with NumPy arrays constructed using this trait.
20
19
///
21
20
/// # Example
21
+ ///
22
22
/// ```
23
23
/// use numpy::{PyArray, IntoPyArray};
24
- /// pyo3::Python::with_gil(|py| {
24
+ /// use pyo3::Python;
25
+ ///
26
+ /// Python::with_gil(|py| {
25
27
/// let py_array = vec![1, 2, 3].into_pyarray(py);
28
+ ///
26
29
/// assert_eq!(py_array.readonly().as_slice().unwrap(), &[1, 2, 3]);
27
- /// assert!(py_array.resize(100).is_err()); // You can't resize owned-by-rust array.
30
+ ///
31
+ /// // Array cannot be resized when its data is owned by Rust.
32
+ /// assert!(py_array.resize(100).is_err());
28
33
/// });
29
34
/// ```
30
35
pub trait IntoPyArray {
36
+ /// The element type of resulting array.
31
37
type Item : Element ;
38
+ /// The dimension type of the resulting array.
32
39
type Dim : Dimension ;
33
- fn into_pyarray < ' py > ( self , _: Python < ' py > ) -> & ' py PyArray < Self :: Item , Self :: Dim > ;
40
+
41
+ /// Consumes `self` and moves its data into a NumPy array.
42
+ fn into_pyarray < ' py > ( self , py : Python < ' py > ) -> & ' py PyArray < Self :: Item , Self :: Dim > ;
34
43
}
35
44
36
45
impl < T : Element > IntoPyArray for Box < [ T ] > {
37
46
type Item = T ;
38
47
type Dim = Ix1 ;
48
+
39
49
fn into_pyarray < ' py > ( self , py : Python < ' py > ) -> & ' py PyArray < Self :: Item , Self :: Dim > {
40
50
let dims = [ self . len ( ) ] ;
41
51
let strides = [ mem:: size_of :: < T > ( ) as npy_intp ] ;
@@ -47,6 +57,7 @@ impl<T: Element> IntoPyArray for Box<[T]> {
47
57
impl < T : Element > IntoPyArray for Vec < T > {
48
58
type Item = T ;
49
59
type Dim = Ix1 ;
60
+
50
61
fn into_pyarray < ' py > ( self , py : Python < ' py > ) -> & ' py PyArray < Self :: Item , Self :: Dim > {
51
62
let dims = [ self . len ( ) ] ;
52
63
let strides = [ mem:: size_of :: < T > ( ) as npy_intp ] ;
@@ -62,49 +73,58 @@ where
62
73
{
63
74
type Item = A ;
64
75
type Dim = D ;
76
+
65
77
fn into_pyarray < ' py > ( self , py : Python < ' py > ) -> & ' py PyArray < Self :: Item , Self :: Dim > {
66
78
PyArray :: from_owned_array ( py, self )
67
79
}
68
80
}
69
81
70
- /// Conversion trait from rust types to `PyArray`.
82
+ /// Conversion trait from borrowing Rust types to [`PyArray`].
83
+ ///
84
+ /// This trait takes `&self` by reference, which means it allocates in Python heap and then copies the elements there.
85
+ ///
86
+ /// # Examples
71
87
///
72
- /// This trait takes `&self`, which means **it allocates in Python heap and then copies
73
- /// elements there**.
74
- /// # Example
75
88
/// ```
76
89
/// use numpy::{PyArray, ToPyArray};
77
- /// pyo3::Python::with_gil(|py| {
90
+ /// use pyo3::Python;
91
+ ///
92
+ /// Python::with_gil(|py| {
78
93
/// let py_array = vec![1, 2, 3].to_pyarray(py);
94
+ ///
79
95
/// assert_eq!(py_array.readonly().as_slice().unwrap(), &[1, 2, 3]);
80
96
/// });
81
97
/// ```
82
98
///
83
- /// This method converts a not -contiguous array to C-order contiguous array .
84
- /// # Example
99
+ /// Due to copying the elments, this method converts non -contiguous arrays to C-order contiguous arrays .
100
+ ///
85
101
/// ```
86
102
/// use numpy::{PyArray, ToPyArray};
87
103
/// use ndarray::{arr3, s};
88
- /// pyo3::Python::with_gil(|py| {
89
- /// let a = arr3(&[[[ 1, 2, 3], [ 4, 5, 6]],
90
- /// [[ 7, 8, 9], [10, 11, 12]]]);
91
- /// let slice = a.slice(s![.., 0..1, ..]);
92
- /// let sliced = arr3(&[[[ 1, 2, 3]],
93
- /// [[ 7, 8, 9]]]);
94
- /// let py_slice = slice.to_pyarray(py);
95
- /// assert_eq!(py_slice.readonly().as_array(), sliced);
96
- /// pyo3::py_run!(py, py_slice, "assert py_slice.flags['C_CONTIGUOUS']");
104
+ /// use pyo3::Python;
105
+ ///
106
+ /// Python::with_gil(|py| {
107
+ /// let array = arr3(&[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]);
108
+ /// let py_array = array.slice(s![.., 0..1, ..]).to_pyarray(py);
109
+ ///
110
+ /// assert_eq!(py_array.readonly().as_array(), arr3(&[[[1, 2, 3]], [[7, 8, 9]]]));
111
+ /// assert!(py_array.is_c_contiguous());
97
112
/// });
98
113
/// ```
99
114
pub trait ToPyArray {
115
+ /// The element type of resulting array.
100
116
type Item : Element ;
117
+ /// The dimension type of the resulting array.
101
118
type Dim : Dimension ;
102
- fn to_pyarray < ' py > ( & self , _: Python < ' py > ) -> & ' py PyArray < Self :: Item , Self :: Dim > ;
119
+
120
+ /// Copies the content pointed to by `&self` into a newly allocated NumPy array.
121
+ fn to_pyarray < ' py > ( & self , py : Python < ' py > ) -> & ' py PyArray < Self :: Item , Self :: Dim > ;
103
122
}
104
123
105
124
impl < T : Element > ToPyArray for [ T ] {
106
125
type Item = T ;
107
126
type Dim = Ix1 ;
127
+
108
128
fn to_pyarray < ' py > ( & self , py : Python < ' py > ) -> & ' py PyArray < Self :: Item , Self :: Dim > {
109
129
PyArray :: from_slice ( py, self )
110
130
}
@@ -118,6 +138,7 @@ where
118
138
{
119
139
type Item = A ;
120
140
type Dim = D ;
141
+
121
142
fn to_pyarray < ' py > ( & self , py : Python < ' py > ) -> & ' py PyArray < Self :: Item , Self :: Dim > {
122
143
let len = self . len ( ) ;
123
144
match self . order ( ) {
@@ -216,79 +237,74 @@ impl NpyStrides {
216
237
}
217
238
}
218
239
219
- /// Utility trait to specify the dimention of array
220
- pub trait ToNpyDims : Dimension {
240
+ /// Utility trait to specify the dimensions of an array.
241
+ pub trait ToNpyDims : Dimension + Sealed {
242
+ #[ doc( hidden) ]
221
243
fn ndim_cint ( & self ) -> c_int {
222
244
self . ndim ( ) as c_int
223
245
}
246
+ #[ doc( hidden) ]
224
247
fn as_dims_ptr ( & self ) -> * mut npyffi:: npy_intp {
225
248
self . slice ( ) . as_ptr ( ) as * mut npyffi:: npy_intp
226
249
}
250
+ #[ doc( hidden) ]
227
251
fn to_npy_dims ( & self ) -> npyffi:: PyArray_Dims {
228
252
npyffi:: PyArray_Dims {
229
253
ptr : self . as_dims_ptr ( ) ,
230
254
len : self . ndim_cint ( ) ,
231
255
}
232
256
}
233
- fn __private__ ( & self ) -> PrivateMarker ;
234
257
}
235
258
236
- impl < D : Dimension > ToNpyDims for D {
237
- fn __private__ ( & self ) -> PrivateMarker {
238
- PrivateMarker
239
- }
240
- }
259
+ impl < D > ToNpyDims for D where D : Dimension { }
241
260
242
- /// Types that can be used to index an array.
261
+ /// Trait implemented by types that can be used to index an array.
243
262
///
244
- /// See
245
- /// [IntoDimension](https://docs.rs/ndarray/latest/ndarray/dimension/conversion/trait.IntoDimension.html)
246
- /// for what types you can use as `NpyIndex`.
263
+ /// This is equivalent to [`ndarray::NdIndex`] but accounts for
264
+ /// NumPy strides being in units of bytes instead of elements.
247
265
///
248
- /// But basically, you can use
249
- /// - [tuple](https://doc.rust-lang.org/nightly/std/primitive.tuple.html)
250
- /// - [array](https://doc.rust-lang.org/nightly/std/primitive.array.html)
251
- /// - [slice](https://doc.rust-lang.org/nightly/std/primitive.slice.html)
252
- // Since Numpy's strides is byte offset, we can't use ndarray::NdIndex directly here.
253
- pub trait NpyIndex : IntoDimension {
266
+ /// All types which implement [`IntoDimension`] implement this trait as well.
267
+ /// This includes at least
268
+ /// - [tuple](https://doc.rust-lang.org/stable/std/primitive.tuple.html)
269
+ /// - [array](https://doc.rust-lang.org/stable/std/primitive.array.html)
270
+ /// - [slice](https://doc.rust-lang.org/stable/std/primitive.slice.html)
271
+ pub trait NpyIndex : IntoDimension + Sealed {
272
+ #[ doc( hidden) ]
254
273
fn get_checked < T > ( self , dims : & [ usize ] , strides : & [ isize ] ) -> Option < isize > ;
274
+ #[ doc( hidden) ]
255
275
fn get_unchecked < T > ( self , strides : & [ isize ] ) -> isize ;
256
- fn __private__ ( self ) -> PrivateMarker ;
257
276
}
258
277
278
+ impl < D : IntoDimension > Sealed for D { }
279
+
259
280
impl < D : IntoDimension > NpyIndex for D {
260
281
fn get_checked < T > ( self , dims : & [ usize ] , strides : & [ isize ] ) -> Option < isize > {
261
- let indices_ = self . into_dimension ( ) ;
262
- let indices = indices_. slice ( ) ;
282
+ let indices = self . into_dimension ( ) ;
283
+ let indices = indices. slice ( ) ;
284
+
263
285
if indices. len ( ) != dims. len ( ) {
264
286
return None ;
265
287
}
266
288
if indices. iter ( ) . zip ( dims) . any ( |( i, d) | i >= d) {
267
289
return None ;
268
290
}
269
- Some ( get_unchecked_impl (
270
- indices,
271
- strides,
272
- mem:: size_of :: < T > ( ) as isize ,
273
- ) )
291
+
292
+ Some ( get_unchecked_impl :: < T > ( indices, strides) )
274
293
}
294
+
275
295
fn get_unchecked < T > ( self , strides : & [ isize ] ) -> isize {
276
- let indices_ = self . into_dimension ( ) ;
277
- let indices = indices_. slice ( ) ;
278
- get_unchecked_impl ( indices, strides, mem:: size_of :: < T > ( ) as isize )
279
- }
280
- fn __private__ ( self ) -> PrivateMarker {
281
- PrivateMarker
296
+ let indices = self . into_dimension ( ) ;
297
+ let indices = indices. slice ( ) ;
298
+ get_unchecked_impl :: < T > ( indices, strides)
282
299
}
283
300
}
284
301
285
- fn get_unchecked_impl ( indices : & [ usize ] , strides : & [ isize ] , size : isize ) -> isize {
302
+ fn get_unchecked_impl < T > ( indices : & [ usize ] , strides : & [ isize ] ) -> isize {
303
+ let size = mem:: size_of :: < T > ( ) as isize ;
304
+
286
305
indices
287
306
. iter ( )
288
307
. zip ( strides)
289
308
. map ( |( & i, stride) | stride * i as isize / size)
290
309
. sum ( )
291
310
}
292
-
293
- #[ doc( hidden) ]
294
- pub struct PrivateMarker ;
0 commit comments