58
58
type Item = A ;
59
59
type Dim = D ;
60
60
fn into_pyarray < ' py > ( self , py : Python < ' py > ) -> & ' py PyArray < Self :: Item , Self :: Dim > {
61
- let strides = npy_strides ( & self ) ;
61
+ let strides = self . npy_strides ( ) ;
62
62
let dim = self . raw_dim ( ) ;
63
63
let boxed = self . into_raw_vec ( ) . into_boxed_slice ( ) ;
64
64
unsafe { PyArray :: from_boxed_slice ( py, dim, strides. as_ptr ( ) , boxed) }
@@ -71,12 +71,27 @@ where
71
71
/// elements there**.
72
72
/// # Example
73
73
/// ```
74
- /// # fn main() {
75
74
/// use numpy::{PyArray, ToPyArray};
76
75
/// let gil = pyo3::Python::acquire_gil();
77
76
/// let py_array = vec![1, 2, 3].to_pyarray(gil.python());
78
77
/// assert_eq!(py_array.as_slice().unwrap(), &[1, 2, 3]);
79
- /// # }
78
+ /// ```
79
+ ///
80
+ /// This method converts a not-contiguous array to C-order contiguous array.
81
+ /// # Example
82
+ /// ```
83
+ /// use numpy::{PyArray, ToPyArray};
84
+ /// use ndarray::{arr3, s};
85
+ /// let gil = pyo3::Python::acquire_gil();
86
+ /// let py = gil.python();
87
+ /// let a = arr3(&[[[ 1, 2, 3], [ 4, 5, 6]],
88
+ /// [[ 7, 8, 9], [10, 11, 12]]]);
89
+ /// let slice = a.slice(s![.., 0..1, ..]);
90
+ /// let sliced = arr3(&[[[ 1, 2, 3]],
91
+ /// [[ 7, 8, 9]]]);
92
+ /// let py_slice = slice.to_pyarray(py);
93
+ /// assert_eq!(py_slice.as_array(), sliced);
94
+ /// pyo3::py_run!(py, py_slice, "assert py_slice.flags['C_CONTIGUOUS']");
80
95
/// ```
81
96
pub trait ToPyArray {
82
97
type Item : TypeNum ;
@@ -102,26 +117,107 @@ where
102
117
type Dim = D ;
103
118
fn to_pyarray < ' py > ( & self , py : Python < ' py > ) -> & ' py PyArray < Self :: Item , Self :: Dim > {
104
119
let len = self . len ( ) ;
105
- let mut strides = npy_strides ( self ) ;
106
- unsafe {
107
- let array = PyArray :: new_ ( py, self . raw_dim ( ) , strides. as_mut_ptr ( ) as * mut npy_intp , 0 ) ;
108
- array. copy_ptr ( self . as_ptr ( ) , len) ;
109
- array
120
+ if let Some ( order) = self . order ( ) {
121
+ // if the array is contiguous, copy it by `copy_ptr`.
122
+ let strides = self . npy_strides ( ) ;
123
+ unsafe {
124
+ let array = PyArray :: new_ ( py, self . raw_dim ( ) , strides. as_ptr ( ) , order. to_flag ( ) ) ;
125
+ array. copy_ptr ( self . as_ptr ( ) , len) ;
126
+ array
127
+ }
128
+ } else {
129
+ // if the array is not contiguous, copy all elements by `ArrayBase::iter`.
130
+ let dim = self . raw_dim ( ) ;
131
+ let strides = NpyStrides :: from_dim ( & dim, mem:: size_of :: < A > ( ) ) ;
132
+ unsafe {
133
+ let array = PyArray :: < A , _ > :: new_ ( py, dim, strides. as_ptr ( ) , 0 ) ;
134
+ let data_ptr = array. data ( ) ;
135
+ for ( i, item) in self . iter ( ) . enumerate ( ) {
136
+ data_ptr. offset ( i as isize ) . write ( * item) ;
137
+ }
138
+ array
139
+ }
110
140
}
111
141
}
112
142
}
113
143
114
- fn npy_strides < S , D , A > ( array : & ArrayBase < S , D > ) -> Vec < npyffi:: npy_intp >
144
+ enum Order {
145
+ Standard ,
146
+ Fortran ,
147
+ }
148
+
149
+ impl Order {
150
+ fn to_flag ( & self ) -> c_int {
151
+ match self {
152
+ Order :: Standard => 0 ,
153
+ Order :: Fortran => 1 ,
154
+ }
155
+ }
156
+ }
157
+
158
+ trait ArrayExt {
159
+ fn npy_strides ( & self ) -> NpyStrides ;
160
+ fn order ( & self ) -> Option < Order > ;
161
+ }
162
+
163
+ impl < A , S , D > ArrayExt for ArrayBase < S , D >
115
164
where
116
165
S : Data < Elem = A > ,
117
166
D : Dimension ,
118
- A : TypeNum ,
119
167
{
120
- array
121
- . strides ( )
122
- . into_iter ( )
123
- . map ( |n| n * mem:: size_of :: < A > ( ) as npyffi:: npy_intp )
124
- . collect ( )
168
+ fn npy_strides ( & self ) -> NpyStrides {
169
+ NpyStrides :: new (
170
+ self . strides ( ) . into_iter ( ) . map ( |& x| x as npyffi:: npy_intp ) ,
171
+ mem:: size_of :: < A > ( ) ,
172
+ )
173
+ }
174
+
175
+ fn order ( & self ) -> Option < Order > {
176
+ if self . is_standard_layout ( ) {
177
+ Some ( Order :: Standard )
178
+ } else if self . ndim ( ) > 1 && self . raw_view ( ) . reversed_axes ( ) . is_standard_layout ( ) {
179
+ Some ( Order :: Fortran )
180
+ } else {
181
+ None
182
+ }
183
+ }
184
+ }
185
+
186
+ /// Numpy strides with short array optimization
187
+ enum NpyStrides {
188
+ Short ( [ npyffi:: npy_intp ; 8 ] ) ,
189
+ Long ( Vec < npyffi:: npy_intp > ) ,
190
+ }
191
+
192
+ impl NpyStrides {
193
+ fn as_ptr ( & self ) -> * const npy_intp {
194
+ match self {
195
+ NpyStrides :: Short ( inner) => inner. as_ptr ( ) ,
196
+ NpyStrides :: Long ( inner) => inner. as_ptr ( ) ,
197
+ }
198
+ }
199
+ fn from_dim < D : Dimension > ( dim : & D , type_size : usize ) -> Self {
200
+ Self :: new (
201
+ dim. default_strides ( )
202
+ . slice ( )
203
+ . into_iter ( )
204
+ . map ( |& x| x as npyffi:: npy_intp ) ,
205
+ type_size,
206
+ )
207
+ }
208
+ fn new ( strides : impl ExactSizeIterator < Item = npyffi:: npy_intp > , type_size : usize ) -> Self {
209
+ let len = strides. len ( ) ;
210
+ let type_size = type_size as npyffi:: npy_intp ;
211
+ if len <= 8 {
212
+ let mut res = [ 0 ; 8 ] ;
213
+ for ( i, s) in strides. enumerate ( ) {
214
+ res[ i] = s * type_size;
215
+ }
216
+ NpyStrides :: Short ( res)
217
+ } else {
218
+ NpyStrides :: Long ( strides. map ( |n| n as npyffi:: npy_intp * type_size) . collect ( ) )
219
+ }
220
+ }
125
221
}
126
222
127
223
/// Utility trait to specify the dimention of array
0 commit comments