1use std::{mem, os::raw::c_int, ptr};
4
5use ndarray::{ArrayBase, Data, Dim, Dimension, IntoDimension, Ix1, OwnedRepr};
6use pyo3::{Bound, Python};
7
8use crate::array::{PyArray, PyArrayMethods};
9use crate::dtype::Element;
10use crate::error::MAX_DIMENSIONALITY_ERR;
11use crate::npyffi::{self, npy_intp};
12use crate::slice_container::PySliceContainer;
13
14pub trait IntoPyArray: Sized {
38 type Item: Element;
40 type Dim: Dimension;
42
43 fn into_pyarray<'py>(self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>>;
45
46 #[deprecated(since = "0.23.0", note = "renamed to `IntoPyArray::into_pyarray`")]
48 #[inline]
49 fn into_pyarray_bound<'py>(
50 self,
51 py: Python<'py>,
52 ) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
53 self.into_pyarray(py)
54 }
55}
56
57impl<T: Element> IntoPyArray for Box<[T]> {
58 type Item = T;
59 type Dim = Ix1;
60
61 fn into_pyarray<'py>(self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
62 let container = PySliceContainer::from(self);
63 let dims = Dim([container.len]);
64 let strides = [mem::size_of::<T>() as npy_intp];
65 let data_ptr = container.ptr as *mut T;
69 unsafe { PyArray::from_raw_parts(py, dims, strides.as_ptr(), data_ptr, container) }
70 }
71}
72
73impl<T: Element> IntoPyArray for Vec<T> {
74 type Item = T;
75 type Dim = Ix1;
76
77 fn into_pyarray<'py>(mut self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
78 let dims = Dim([self.len()]);
79 let strides = [mem::size_of::<T>() as npy_intp];
80 let data_ptr = self.as_mut_ptr();
81 unsafe {
82 PyArray::from_raw_parts(
83 py,
84 dims,
85 strides.as_ptr(),
86 data_ptr,
87 PySliceContainer::from(self),
88 )
89 }
90 }
91}
92
93impl<A, D> IntoPyArray for ArrayBase<OwnedRepr<A>, D>
94where
95 A: Element,
96 D: Dimension,
97{
98 type Item = A;
99 type Dim = D;
100
101 fn into_pyarray<'py>(self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
102 PyArray::from_owned_array(py, self)
103 }
104}
105
106pub trait ToPyArray {
140 type Item: Element;
142 type Dim: Dimension;
144
145 fn to_pyarray<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>>;
147
148 #[deprecated(since = "0.23.0", note = "renamed to ToPyArray::to_pyarray`")]
150 #[inline]
151 fn to_pyarray_bound<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
152 self.to_pyarray(py)
153 }
154}
155
156impl<T: Element> ToPyArray for [T] {
157 type Item = T;
158 type Dim = Ix1;
159
160 fn to_pyarray<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
161 PyArray::from_slice(py, self)
162 }
163}
164
165impl<S, D, A> ToPyArray for ArrayBase<S, D>
166where
167 S: Data<Elem = A>,
168 D: Dimension,
169 A: Element,
170{
171 type Item = A;
172 type Dim = D;
173
174 fn to_pyarray<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
175 let len = self.len();
176 match self.order() {
177 Some(flag) if A::IS_COPY => {
178 let strides = self.npy_strides();
180 unsafe {
181 let array = PyArray::new_uninit(py, self.raw_dim(), strides.as_ptr(), flag);
182 ptr::copy_nonoverlapping(self.as_ptr(), array.data(), len);
183 array
184 }
185 }
186 _ => {
187 let dim = self.raw_dim();
189 unsafe {
190 let array = PyArray::<A, _>::new(py, dim, false);
191 let mut data_ptr = array.data();
192 for item in self.iter() {
193 data_ptr.write(item.clone_ref(py));
194 data_ptr = data_ptr.add(1);
195 }
196 array
197 }
198 }
199 }
200 }
201}
202
203#[cfg(feature = "nalgebra")]
204impl<N, R, C, S> ToPyArray for nalgebra::Matrix<N, R, C, S>
205where
206 N: nalgebra::Scalar + Element,
207 R: nalgebra::Dim,
208 C: nalgebra::Dim,
209 S: nalgebra::Storage<N, R, C>,
210{
211 type Item = N;
212 type Dim = crate::Ix2;
213
214 fn to_pyarray<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
219 unsafe {
220 let array = PyArray::<N, _>::new(py, (self.nrows(), self.ncols()), true);
221 let mut data_ptr = array.data();
222 if self.data.is_contiguous() {
223 ptr::copy_nonoverlapping(self.data.ptr(), data_ptr, self.len());
224 } else {
225 for item in self.iter() {
226 data_ptr.write(item.clone_ref(py));
227 data_ptr = data_ptr.add(1);
228 }
229 }
230 array
231 }
232 }
233}
234
235pub(crate) trait ArrayExt {
236 fn npy_strides(&self) -> [npyffi::npy_intp; 32];
237 fn order(&self) -> Option<c_int>;
238}
239
240impl<A, S, D> ArrayExt for ArrayBase<S, D>
241where
242 S: Data<Elem = A>,
243 D: Dimension,
244{
245 fn npy_strides(&self) -> [npyffi::npy_intp; 32] {
246 let strides = self.strides();
247 let itemsize = mem::size_of::<A>() as isize;
248
249 assert!(strides.len() <= 32, "{}", MAX_DIMENSIONALITY_ERR);
250
251 let mut new_strides = [0; 32];
252
253 for i in 0..strides.len() {
254 new_strides[i] = (strides[i] * itemsize) as npyffi::npy_intp;
255 }
256
257 new_strides
258 }
259
260 fn order(&self) -> Option<c_int> {
261 if self.is_standard_layout() {
262 Some(npyffi::NPY_ORDER::NPY_CORDER as _)
263 } else if self.ndim() > 1 && self.raw_view().reversed_axes().is_standard_layout() {
264 Some(npyffi::NPY_ORDER::NPY_FORTRANORDER as _)
265 } else {
266 None
267 }
268 }
269}
270
271pub trait ToNpyDims: Dimension + Sealed {
273 #[doc(hidden)]
274 fn ndim_cint(&self) -> c_int {
275 self.ndim() as c_int
276 }
277 #[doc(hidden)]
278 fn as_dims_ptr(&mut self) -> *mut npyffi::npy_intp {
279 self.slice_mut().as_ptr() as *mut npyffi::npy_intp
280 }
281 #[doc(hidden)]
282 fn to_npy_dims(&mut self) -> npyffi::PyArray_Dims {
283 npyffi::PyArray_Dims {
284 ptr: self.as_dims_ptr(),
285 len: self.ndim_cint(),
286 }
287 }
288}
289
290mod sealed {
291 pub trait Sealed {}
292}
293
294use sealed::Sealed;
295
296impl<D> ToNpyDims for D where D: Dimension {}
297
298pub trait NpyIndex: IntoDimension + Sealed {
309 #[doc(hidden)]
310 fn get_checked<T>(self, dims: &[usize], strides: &[isize]) -> Option<isize>;
311 #[doc(hidden)]
312 fn get_unchecked<T>(self, strides: &[isize]) -> isize;
313}
314
315impl<D: IntoDimension> Sealed for D {}
316
317impl<D: IntoDimension> NpyIndex for D {
318 fn get_checked<T>(self, dims: &[usize], strides: &[isize]) -> Option<isize> {
319 let indices = self.into_dimension();
320 let indices = indices.slice();
321
322 if indices.len() != dims.len() {
323 return None;
324 }
325 if indices.iter().zip(dims).any(|(i, d)| i >= d) {
326 return None;
327 }
328
329 Some(get_unchecked_impl::<T>(indices, strides))
330 }
331
332 fn get_unchecked<T>(self, strides: &[isize]) -> isize {
333 let indices = self.into_dimension();
334 let indices = indices.slice();
335 get_unchecked_impl::<T>(indices, strides)
336 }
337}
338
339fn get_unchecked_impl<T>(indices: &[usize], strides: &[isize]) -> isize {
340 let size = mem::size_of::<T>() as isize;
341
342 indices
343 .iter()
344 .zip(strides)
345 .map(|(&i, stride)| stride * i as isize / size)
346 .sum()
347}