Skip to main content

numpy/
array.rs

1//! Safe interface for NumPy's [N-dimensional arrays][ndarray]
2//!
3//! [ndarray]: https://numpy.org/doc/stable/reference/arrays.ndarray.html
4
5use std::{
6    ffi::{c_int, c_void},
7    marker::PhantomData,
8    mem, ptr, slice,
9};
10
11use ndarray::{
12    Array, ArrayBase, ArrayView, ArrayViewMut, Axis, Data, Dim, Dimension, IntoDimension, Ix0, Ix1,
13    Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, RawArrayView, RawArrayViewMut, RawData, ShapeBuilder,
14    StrideShape,
15};
16use num_traits::AsPrimitive;
17use pyo3::{
18    ffi,
19    types::{DerefToPyAny, PyModule},
20    Bound, CastError, Py, PyAny, PyErr, PyResult, PyTypeCheck, PyTypeInfo, Python,
21};
22
23use crate::borrow::{PyReadonlyArray, PyReadwriteArray};
24use crate::cold;
25use crate::convert::{ArrayExt, IntoPyArray, NpyIndex, ToNpyDims, ToPyArray};
26use crate::dtype::{Element, PyArrayDescrMethods};
27use crate::error::{
28    AsSliceError, BorrowError, DimensionalityError, FromVecError, IgnoreError, TypeError,
29    DIMENSIONALITY_MISMATCH_ERR, MAX_DIMENSIONALITY_ERR,
30};
31use crate::npyffi::{self, npy_intp, NPY_ORDER, PY_ARRAY_API};
32use crate::slice_container::PySliceContainer;
33use crate::untyped_array::{PyUntypedArray, PyUntypedArrayMethods};
34
35/// A safe, statically-typed wrapper for NumPy's [`ndarray`][ndarray] class.
36///
37/// # Memory location
38///
39/// - Allocated by Rust: Constructed via [`IntoPyArray`] or
40///   [`from_vec`][Self::from_vec] or [`from_owned_array`][Self::from_owned_array].
41///
42/// These methods transfers ownership of the Rust allocation into a suitable Python object
43/// and uses the memory as the internal buffer backing the NumPy array.
44///
45/// Please note that some destructive methods like [`resize`][PyArrayMethods::resize] will fail
46/// when used with this kind of array as NumPy cannot reallocate the internal buffer.
47///
48/// - Allocated by NumPy: Constructed via other methods, like [`ToPyArray`] or
49///   [`from_slice`][Self::from_slice] or [`from_array`][Self::from_array].
50///
51/// These methods allocate memory in Python's private heap via NumPy's API.
52///
53/// In both cases, `PyArray` is managed by Python so it can neither be moved from
54/// nor deallocated manually.
55///
56/// # References
57///
58/// Like [`new`][Self::new], all constructor methods of `PyArray` return a shared reference `&PyArray`
59/// instead of an owned value. This design follows [PyO3's ownership concept][pyo3-memory],
60/// i.e. the return value is GIL-bound owning reference into Python's heap.
61///
62/// # Element type and dimensionality
63///
64/// `PyArray` has two type parameters `T` and `D`.
65/// `T` represents the type of its elements, e.g. [`f32`] or [`PyObject`].
66/// `D` represents its dimensionality, e.g [`Ix2`][type@Ix2] or [`IxDyn`][type@IxDyn].
67///
68/// Element types are Rust types which implement the [`Element`] trait.
69/// Dimensions are represented by the [`ndarray::Dimension`] trait.
70///
71/// Typically, `Ix1, Ix2, ...` are used for fixed dimensionality arrays,
72/// and `IxDyn` is used for dynamic dimensionality arrays. Type aliases
73/// for combining `PyArray` with these types are provided, e.g. [`PyArray1`] or [`PyArrayDyn`].
74///
75/// To specify concrete dimension like `3×4×5`, types which implement the [`ndarray::IntoDimension`]
76/// trait are used. Typically, this means arrays like `[3, 4, 5]` or tuples like `(3, 4, 5)`.
77///
78/// # Example
79///
80/// ```
81/// use numpy::{PyArray, PyArrayMethods};
82/// use ndarray::{array, Array};
83/// use pyo3::Python;
84///
85/// Python::attach(|py| {
86///     let pyarray = PyArray::arange(py, 0., 4., 1.).reshape([2, 2]).unwrap();
87///     let array = array![[3., 4.], [5., 6.]];
88///
89///     assert_eq!(
90///         array.dot(&pyarray.readonly().as_array()),
91///         array![[8., 15.], [12., 23.]]
92///     );
93/// });
94/// ```
95///
96/// [`PyObject`]: pyo3::ffi::PyObject
97/// [ndarray]: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html
98/// [pyo3-memory]: https://pyo3.rs/main/memory.html
99#[repr(transparent)]
100pub struct PyArray<T, D>(PyAny, PhantomData<T>, PhantomData<D>);
101
102/// Zero-dimensional array.
103pub type PyArray0<T> = PyArray<T, Ix0>;
104/// One-dimensional array.
105pub type PyArray1<T> = PyArray<T, Ix1>;
106/// Two-dimensional array.
107pub type PyArray2<T> = PyArray<T, Ix2>;
108/// Three-dimensional array.
109pub type PyArray3<T> = PyArray<T, Ix3>;
110/// Four-dimensional array.
111pub type PyArray4<T> = PyArray<T, Ix4>;
112/// Five-dimensional array.
113pub type PyArray5<T> = PyArray<T, Ix5>;
114/// Six-dimensional array.
115pub type PyArray6<T> = PyArray<T, Ix6>;
116/// Dynamic-dimensional array.
117pub type PyArrayDyn<T> = PyArray<T, IxDyn>;
118
119/// Returns a handle to NumPy's multiarray module.
120pub fn get_array_module<'py>(py: Python<'py>) -> PyResult<Bound<'py, PyModule>> {
121    PyModule::import(py, npyffi::array::mod_name(py)?)
122}
123
124impl<T, D> DerefToPyAny for PyArray<T, D> {}
125
126unsafe impl<T: Element, D: Dimension> PyTypeInfo for PyArray<T, D> {
127    const NAME: &'static str = "PyArray<T, D>";
128    const MODULE: Option<&'static str> = Some("numpy");
129
130    fn type_object_raw<'py>(py: Python<'py>) -> *mut ffi::PyTypeObject {
131        unsafe { npyffi::get_type_object(py, npyffi::NpyTypes::PyArray_Type) }
132    }
133
134    fn is_type_of(ob: &Bound<'_, PyAny>) -> bool {
135        Self::extract::<IgnoreError>(ob, npyffi::PyArray_Check).is_ok()
136    }
137
138    fn is_exact_type_of(ob: &Bound<'_, PyAny>) -> bool {
139        Self::extract::<IgnoreError>(ob, npyffi::PyArray_CheckExact).is_ok()
140    }
141}
142
143impl<T: Element, D: Dimension> PyArray<T, D> {
144    fn extract<'a, 'py, E>(
145        ob: &'a Bound<'py, PyAny>,
146        check: unsafe fn(Python<'py>, *mut ffi::PyObject) -> c_int,
147    ) -> Result<&'a Bound<'py, Self>, E>
148    where
149        E: From<CastError<'a, 'py>> + From<DimensionalityError> + From<TypeError<'py>>,
150    {
151        // Check if the object is an array.
152        let array = unsafe {
153            if check(ob.py(), ob.as_ptr()) == 0 {
154                return Err(CastError::new(
155                    ob.as_borrowed(),
156                    <Self as PyTypeCheck>::classinfo_object(ob.py()),
157                )
158                .into());
159            }
160            ob.cast_unchecked::<Self>()
161        };
162
163        // Check if the dimensionality matches `D`.
164        let src_ndim = array.ndim();
165        if let Some(dst_ndim) = D::NDIM {
166            if src_ndim != dst_ndim {
167                return Err(DimensionalityError::new(src_ndim, dst_ndim).into());
168            }
169        }
170
171        // Check if the element type matches `T`.
172        let src_dtype = array.dtype();
173        let dst_dtype = T::get_dtype(ob.py());
174        if !src_dtype.is_equiv_to(&dst_dtype) {
175            return Err(TypeError::new(src_dtype, dst_dtype).into());
176        }
177
178        Ok(array)
179    }
180
181    /// Creates a new uninitialized NumPy array.
182    ///
183    /// If `is_fortran` is true, then it has Fortran/column-major order,
184    /// otherwise it has C/row-major order.
185    ///
186    /// # Safety
187    ///
188    /// The returned array will always be safe to be dropped as the elements must either
189    /// be trivially copyable (as indicated by `<T as Element>::IS_COPY`) or be pointers
190    /// into Python's heap, which NumPy will automatically zero-initialize.
191    ///
192    /// However, the elements themselves will not be valid and should be initialized manually
193    /// using raw pointers obtained via [`uget_raw`][PyArrayMethods::uget_raw]. Before that, all methods
194    /// which produce references to the elements invoke undefined behaviour. In particular,
195    /// zero-initialized pointers are _not_ valid instances of `PyObject`.
196    ///
197    /// # Example
198    ///
199    /// ```
200    /// use numpy::prelude::*;
201    /// use numpy::PyArray3;
202    /// use pyo3::Python;
203    ///
204    /// Python::attach(|py| {
205    ///     let arr = unsafe {
206    ///         let arr = PyArray3::<i32>::new(py, [4, 5, 6], false);
207    ///
208    ///         for i in 0..4 {
209    ///             for j in 0..5 {
210    ///                 for k in 0..6 {
211    ///                     arr.uget_raw([i, j, k]).write((i * j * k) as i32);
212    ///                 }
213    ///             }
214    ///         }
215    ///
216    ///         arr
217    ///     };
218    ///
219    ///     assert_eq!(arr.shape(), &[4, 5, 6]);
220    /// });
221    /// ```
222    pub unsafe fn new<'py, ID>(py: Python<'py>, dims: ID, is_fortran: bool) -> Bound<'py, Self>
223    where
224        ID: IntoDimension<Dim = D>,
225    {
226        let flags = c_int::from(is_fortran);
227        Self::new_uninit(py, dims, ptr::null_mut(), flags)
228    }
229
230    pub(crate) unsafe fn new_uninit<'py, ID>(
231        py: Python<'py>,
232        dims: ID,
233        strides: *const npy_intp,
234        flag: c_int,
235    ) -> Bound<'py, Self>
236    where
237        ID: IntoDimension<Dim = D>,
238    {
239        let mut dims = dims.into_dimension();
240        let ptr = PY_ARRAY_API.PyArray_NewFromDescr(
241            py,
242            npyffi::get_type_object(py, npyffi::NpyTypes::PyArray_Type),
243            T::get_dtype(py).into_dtype_ptr(),
244            dims.ndim_cint(),
245            dims.as_dims_ptr(),
246            strides as *mut npy_intp, // strides
247            ptr::null_mut(),          // data
248            flag,                     // flag
249            ptr::null_mut(),          // obj
250        );
251
252        Bound::from_owned_ptr(py, ptr).cast_into_unchecked()
253    }
254
255    unsafe fn new_with_data<'py, ID>(
256        py: Python<'py>,
257        dims: ID,
258        strides: *const npy_intp,
259        data_ptr: *const T,
260        container: *mut PyAny,
261    ) -> Bound<'py, Self>
262    where
263        ID: IntoDimension<Dim = D>,
264    {
265        let mut dims = dims.into_dimension();
266        let ptr = PY_ARRAY_API.PyArray_NewFromDescr(
267            py,
268            npyffi::get_type_object(py, npyffi::NpyTypes::PyArray_Type),
269            T::get_dtype(py).into_dtype_ptr(),
270            dims.ndim_cint(),
271            dims.as_dims_ptr(),
272            strides as *mut npy_intp,    // strides
273            data_ptr as *mut c_void,     // data
274            npyffi::NPY_ARRAY_WRITEABLE, // flag
275            ptr::null_mut(),             // obj
276        );
277
278        PY_ARRAY_API.PyArray_SetBaseObject(
279            py,
280            ptr as *mut npyffi::PyArrayObject,
281            container as *mut ffi::PyObject,
282        );
283
284        Bound::from_owned_ptr(py, ptr).cast_into_unchecked()
285    }
286
287    pub(crate) unsafe fn from_raw_parts<'py>(
288        py: Python<'py>,
289        dims: D,
290        strides: *const npy_intp,
291        data_ptr: *const T,
292        container: PySliceContainer,
293    ) -> Bound<'py, Self> {
294        let container = Bound::new(py, container)
295            .expect("Failed to create slice container")
296            .into_ptr();
297
298        Self::new_with_data(py, dims, strides, data_ptr, container.cast())
299    }
300
301    /// Creates a NumPy array backed by `array` and ties its ownership to the Python object `container`.
302    ///
303    /// The resulting NumPy array will be writeable from Python space.  If this is undesirable, use
304    /// [PyReadwriteArray::make_nonwriteable].
305    ///
306    /// # Safety
307    ///
308    /// `container` is set as a base object of the returned array which must not be dropped until `container` is dropped.
309    /// Furthermore, `array` must not be reallocated from the time this method is called and until `container` is dropped.
310    ///
311    /// # Example
312    ///
313    /// ```rust
314    /// # use pyo3::prelude::*;
315    /// # use numpy::{ndarray::Array1, PyArray1};
316    /// #
317    /// #[pyclass]
318    /// struct Owner {
319    ///     array: Array1<f64>,
320    /// }
321    ///
322    /// #[pymethods]
323    /// impl Owner {
324    ///     #[getter]
325    ///     fn array<'py>(this: Bound<'py, Self>) -> Bound<'py, PyArray1<f64>> {
326    ///         let array = &this.borrow().array;
327    ///
328    ///         // SAFETY: The memory backing `array` will stay valid as long as this object is alive
329    ///         // as we do not modify `array` in any way which would cause it to be reallocated.
330    ///         unsafe { PyArray1::borrow_from_array(array, this.into_any()) }
331    ///     }
332    /// }
333    /// ```
334    pub unsafe fn borrow_from_array<'py, S>(
335        array: &ArrayBase<S, D>,
336        container: Bound<'py, PyAny>,
337    ) -> Bound<'py, Self>
338    where
339        S: Data<Elem = T>,
340    {
341        let (strides, dims) = (array.npy_strides(), array.raw_dim());
342        let data_ptr = array.as_ptr();
343
344        let py = container.py();
345
346        Self::new_with_data(
347            py,
348            dims,
349            strides.as_ptr(),
350            data_ptr,
351            container.into_ptr().cast(),
352        )
353    }
354
355    /// Construct a new NumPy array filled with zeros.
356    ///
357    /// If `is_fortran` is true, then it has Fortran/column-major order,
358    /// otherwise it has C/row-major order.
359    ///
360    /// For arrays of Python objects, this will fill the array
361    /// with valid pointers to zero-valued Python integer objects.
362    ///
363    /// See also [`numpy.zeros`][numpy-zeros] and [`PyArray_Zeros`][PyArray_Zeros].
364    ///
365    /// # Example
366    ///
367    /// ```
368    /// use numpy::{PyArray2, PyArrayMethods};
369    /// use pyo3::Python;
370    ///
371    /// Python::attach(|py| {
372    ///     let pyarray = PyArray2::<usize>::zeros(py, [2, 2], true);
373    ///
374    ///     assert_eq!(pyarray.readonly().as_slice().unwrap(), [0; 4]);
375    /// });
376    /// ```
377    ///
378    /// [numpy-zeros]: https://numpy.org/doc/stable/reference/generated/numpy.zeros.html
379    /// [PyArray_Zeros]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_Zeros
380    pub fn zeros<ID>(py: Python<'_>, dims: ID, is_fortran: bool) -> Bound<'_, Self>
381    where
382        ID: IntoDimension<Dim = D>,
383    {
384        let mut dims = dims.into_dimension();
385        unsafe {
386            let ptr = PY_ARRAY_API.PyArray_Zeros(
387                py,
388                dims.ndim_cint(),
389                dims.as_dims_ptr(),
390                T::get_dtype(py).into_dtype_ptr(),
391                if is_fortran { -1 } else { 0 },
392            );
393            Bound::from_owned_ptr(py, ptr).cast_into_unchecked()
394        }
395    }
396
397    /// Constructs a NumPy from an [`ndarray::Array`]
398    ///
399    /// This method uses the internal [`Vec`] of the [`ndarray::Array`] as the base object of the NumPy array.
400    ///
401    /// # Example
402    ///
403    /// ```
404    /// use numpy::{PyArray, PyArrayMethods};
405    /// use ndarray::array;
406    /// use pyo3::Python;
407    ///
408    /// Python::attach(|py| {
409    ///     let pyarray = PyArray::from_owned_array(py, array![[1, 2], [3, 4]]);
410    ///
411    ///     assert_eq!(pyarray.readonly().as_array(), array![[1, 2], [3, 4]]);
412    /// });
413    /// ```
414    pub fn from_owned_array(py: Python<'_>, mut arr: Array<T, D>) -> Bound<'_, Self> {
415        let (strides, dims) = (arr.npy_strides(), arr.raw_dim());
416        let data_ptr = arr.as_mut_ptr();
417        unsafe {
418            Self::from_raw_parts(
419                py,
420                dims,
421                strides.as_ptr(),
422                data_ptr,
423                PySliceContainer::from(arr),
424            )
425        }
426    }
427
428    /// Construct a NumPy array from a [`ndarray::ArrayBase`].
429    ///
430    /// This method allocates memory in Python's heap via the NumPy API,
431    /// and then copies all elements of the array there.
432    ///
433    /// # Example
434    ///
435    /// ```
436    /// use numpy::{PyArray, PyArrayMethods};
437    /// use ndarray::array;
438    /// use pyo3::Python;
439    ///
440    /// Python::attach(|py| {
441    ///     let pyarray = PyArray::from_array(py, &array![[1, 2], [3, 4]]);
442    ///
443    ///     assert_eq!(pyarray.readonly().as_array(), array![[1, 2], [3, 4]]);
444    /// });
445    /// ```
446    pub fn from_array<'py, S>(py: Python<'py>, arr: &ArrayBase<S, D>) -> Bound<'py, Self>
447    where
448        S: Data<Elem = T>,
449    {
450        ToPyArray::to_pyarray(arr, py)
451    }
452}
453
454impl<D: Dimension> PyArray<Py<PyAny>, D> {
455    /// Construct a NumPy array containing objects stored in a [`ndarray::Array`]
456    ///
457    /// This method uses the internal [`Vec`] of the [`ndarray::Array`] as the base object of the NumPy array.
458    ///
459    /// # Example
460    ///
461    /// ```
462    /// use ndarray::array;
463    /// use pyo3::{pyclass, Py, Python, types::PyAnyMethods};
464    /// use numpy::{PyArray, PyArrayMethods};
465    ///
466    /// #[pyclass]
467    /// # #[allow(dead_code)]
468    /// struct CustomElement {
469    ///     foo: i32,
470    ///     bar: f64,
471    /// }
472    ///
473    /// Python::attach(|py| {
474    ///     let array = array![
475    ///         Py::new(py, CustomElement {
476    ///             foo: 1,
477    ///             bar: 2.0,
478    ///         }).unwrap(),
479    ///         Py::new(py, CustomElement {
480    ///             foo: 3,
481    ///             bar: 4.0,
482    ///         }).unwrap(),
483    ///     ];
484    ///
485    ///     let pyarray = PyArray::from_owned_object_array(py, array);
486    ///
487    ///     assert!(pyarray.readonly().as_array().get(0).unwrap().bind(py).is_instance_of::<CustomElement>());
488    /// });
489    /// ```
490    pub fn from_owned_object_array<T>(py: Python<'_>, mut arr: Array<Py<T>, D>) -> Bound<'_, Self> {
491        let (strides, dims) = (arr.npy_strides(), arr.raw_dim());
492        let data_ptr = arr.as_mut_ptr().cast::<Py<PyAny>>().cast_const();
493        unsafe {
494            Self::from_raw_parts(
495                py,
496                dims,
497                strides.as_ptr(),
498                data_ptr,
499                PySliceContainer::from(arr),
500            )
501        }
502    }
503}
504
505impl<T: Element> PyArray<T, Ix1> {
506    /// Construct a one-dimensional array from a [mod@slice].
507    ///
508    /// # Example
509    ///
510    /// ```
511    /// use numpy::{PyArray, PyArrayMethods};
512    /// use pyo3::Python;
513    ///
514    /// Python::attach(|py| {
515    ///     let slice = &[1, 2, 3, 4, 5];
516    ///     let pyarray = PyArray::from_slice(py, slice);
517    ///     assert_eq!(pyarray.readonly().as_slice().unwrap(), &[1, 2, 3, 4, 5]);
518    /// });
519    /// ```
520    pub fn from_slice<'py>(py: Python<'py>, slice: &[T]) -> Bound<'py, Self> {
521        unsafe {
522            let array = PyArray::new(py, [slice.len()], false);
523            let mut data_ptr = array.data();
524            clone_elements(py, slice, &mut data_ptr);
525            array
526        }
527    }
528
529    /// Construct a one-dimensional array from a [`Vec<T>`][Vec].
530    ///
531    /// # Example
532    ///
533    /// ```
534    /// use numpy::{PyArray, PyArrayMethods};
535    /// use pyo3::Python;
536    ///
537    /// Python::attach(|py| {
538    ///     let vec = vec![1, 2, 3, 4, 5];
539    ///     let pyarray = PyArray::from_vec(py, vec);
540    ///     assert_eq!(pyarray.readonly().as_slice().unwrap(), &[1, 2, 3, 4, 5]);
541    /// });
542    /// ```
543    #[inline(always)]
544    pub fn from_vec<'py>(py: Python<'py>, vec: Vec<T>) -> Bound<'py, Self> {
545        vec.into_pyarray(py)
546    }
547
548    /// Construct a one-dimensional array from an [`Iterator`].
549    ///
550    /// If no reliable [`size_hint`][Iterator::size_hint] is available,
551    /// this method can allocate memory multiple times, which can hurt performance.
552    ///
553    /// # Example
554    ///
555    /// ```
556    /// use numpy::{PyArray, PyArrayMethods};
557    /// use pyo3::Python;
558    ///
559    /// Python::attach(|py| {
560    ///     let pyarray = PyArray::from_iter(py, "abcde".chars().map(u32::from));
561    ///     assert_eq!(pyarray.readonly().as_slice().unwrap(), &[97, 98, 99, 100, 101]);
562    /// });
563    /// ```
564    pub fn from_iter<I>(py: Python<'_>, iter: I) -> Bound<'_, Self>
565    where
566        I: IntoIterator<Item = T>,
567    {
568        let data = iter.into_iter().collect::<Vec<_>>();
569        data.into_pyarray(py)
570    }
571}
572
573impl<T: Element> PyArray<T, Ix2> {
574    /// Construct a two-dimension array from a [`Vec<Vec<T>>`][Vec].
575    ///
576    /// This function checks all dimensions of the inner vectors and returns
577    /// an error if they are not all equal.
578    ///
579    /// # Example
580    ///
581    /// ```
582    /// use numpy::{PyArray, PyArrayMethods};
583    /// use pyo3::Python;
584    /// use ndarray::array;
585    ///
586    /// Python::attach(|py| {
587    ///     let vec2 = vec![vec![11, 12], vec![21, 22]];
588    ///     let pyarray = PyArray::from_vec2(py, &vec2).unwrap();
589    ///     assert_eq!(pyarray.readonly().as_array(), array![[11, 12], [21, 22]]);
590    ///
591    ///     let ragged_vec2 = vec![vec![11, 12], vec![21]];
592    ///     assert!(PyArray::from_vec2(py, &ragged_vec2).is_err());
593    /// });
594    /// ```
595    pub fn from_vec2<'py>(py: Python<'py>, v: &[Vec<T>]) -> Result<Bound<'py, Self>, FromVecError> {
596        let len2 = v.first().map_or(0, |v| v.len());
597        let dims = [v.len(), len2];
598        // SAFETY: The result of `Self::new` is always safe to drop.
599        unsafe {
600            let array = Self::new(py, dims, false);
601            let mut data_ptr = array.data();
602            for v in v {
603                if v.len() != len2 {
604                    cold();
605                    return Err(FromVecError::new(v.len(), len2));
606                }
607                clone_elements(py, v, &mut data_ptr);
608            }
609            Ok(array)
610        }
611    }
612}
613
614impl<T: Element> PyArray<T, Ix3> {
615    /// Construct a three-dimensional array from a [`Vec<Vec<Vec<T>>>`][Vec].
616    ///
617    /// This function checks all dimensions of the inner vectors and returns
618    /// an error if they are not all equal.
619    ///
620    /// # Example
621    ///
622    /// ```
623    /// use numpy::{PyArray, PyArrayMethods};
624    /// use pyo3::Python;
625    /// use ndarray::array;
626    ///
627    /// Python::attach(|py| {
628    ///     let vec3 = vec![
629    ///         vec![vec![111, 112], vec![121, 122]],
630    ///         vec![vec![211, 212], vec![221, 222]],
631    ///     ];
632    ///     let pyarray = PyArray::from_vec3(py, &vec3).unwrap();
633    ///     assert_eq!(
634    ///         pyarray.readonly().as_array(),
635    ///         array![[[111, 112], [121, 122]], [[211, 212], [221, 222]]]
636    ///     );
637    ///
638    ///     let ragged_vec3 = vec![
639    ///         vec![vec![111, 112], vec![121, 122]],
640    ///         vec![vec![211], vec![221, 222]],
641    ///     ];
642    ///     assert!(PyArray::from_vec3(py, &ragged_vec3).is_err());
643    /// });
644    /// ```
645    pub fn from_vec3<'py>(
646        py: Python<'py>,
647        v: &[Vec<Vec<T>>],
648    ) -> Result<Bound<'py, Self>, FromVecError> {
649        let len2 = v.first().map_or(0, |v| v.len());
650        let len3 = v.first().map_or(0, |v| v.first().map_or(0, |v| v.len()));
651        let dims = [v.len(), len2, len3];
652        // SAFETY: The result of `Self::new` is always safe to drop.
653        unsafe {
654            let array = Self::new(py, dims, false);
655            let mut data_ptr = array.data();
656            for v in v {
657                if v.len() != len2 {
658                    cold();
659                    return Err(FromVecError::new(v.len(), len2));
660                }
661                for v in v {
662                    if v.len() != len3 {
663                        cold();
664                        return Err(FromVecError::new(v.len(), len3));
665                    }
666                    clone_elements(py, v, &mut data_ptr);
667                }
668            }
669            Ok(array)
670        }
671    }
672}
673
674impl<T: Element + AsPrimitive<f64>> PyArray<T, Ix1> {
675    /// Return evenly spaced values within a given interval.
676    ///
677    /// See [numpy.arange][numpy.arange] for the Python API and [PyArray_Arange][PyArray_Arange] for the C API.
678    ///
679    /// # Example
680    ///
681    /// ```
682    /// use numpy::{PyArray, PyArrayMethods};
683    /// use pyo3::Python;
684    ///
685    /// Python::attach(|py| {
686    ///     let pyarray = PyArray::arange(py, 2.0, 4.0, 0.5);
687    ///     assert_eq!(pyarray.readonly().as_slice().unwrap(), &[2.0, 2.5, 3.0, 3.5]);
688    ///
689    ///     let pyarray = PyArray::arange(py, -2, 4, 3);
690    ///     assert_eq!(pyarray.readonly().as_slice().unwrap(), &[-2, 1]);
691    /// });
692    /// ```
693    ///
694    /// [numpy.arange]: https://numpy.org/doc/stable/reference/generated/numpy.arange.html
695    /// [PyArray_Arange]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_Arange
696    pub fn arange<'py>(py: Python<'py>, start: T, stop: T, step: T) -> Bound<'py, Self> {
697        unsafe {
698            let ptr = PY_ARRAY_API.PyArray_Arange(
699                py,
700                start.as_(),
701                stop.as_(),
702                step.as_(),
703                T::get_dtype(py).num(),
704            );
705            Bound::from_owned_ptr(py, ptr).cast_into_unchecked()
706        }
707    }
708}
709
710unsafe fn clone_elements<T: Element>(py: Python<'_>, elems: &[T], data_ptr: &mut *mut T) {
711    if T::IS_COPY {
712        ptr::copy_nonoverlapping(elems.as_ptr(), *data_ptr, elems.len());
713        *data_ptr = data_ptr.add(elems.len());
714    } else {
715        for elem in elems {
716            data_ptr.write(elem.clone_ref(py));
717            *data_ptr = data_ptr.add(1);
718        }
719    }
720}
721
722/// Implementation of functionality for [`PyArray<T, D>`].
723#[doc(alias = "PyArray")]
724pub trait PyArrayMethods<'py, T, D>: PyUntypedArrayMethods<'py> + Sized {
725    /// Access an untyped representation of this array.
726    fn as_untyped(&self) -> &Bound<'py, PyUntypedArray>;
727
728    /// Returns a pointer to the first element of the array.
729    fn data(&self) -> *mut T;
730
731    /// Same as [`shape`][PyUntypedArrayMethods::shape], but returns `D` instead of `&[usize]`.
732    #[inline(always)]
733    fn dims(&self) -> D
734    where
735        D: Dimension,
736    {
737        D::from_dimension(&Dim(self.shape())).expect(DIMENSIONALITY_MISMATCH_ERR)
738    }
739
740    /// Returns an immutable view of the internal data as a slice.
741    ///
742    /// # Safety
743    ///
744    /// Calling this method is undefined behaviour if the underlying array
745    /// is aliased mutably by other instances of `PyArray`
746    /// or concurrently modified by Python or other native code.
747    ///
748    /// Please consider the safe alternative [`PyReadonlyArray::as_slice`].
749    unsafe fn as_slice(&self) -> Result<&[T], AsSliceError>
750    where
751        T: Element,
752        D: Dimension,
753    {
754        let len = self.len();
755        if len == 0 {
756            // We can still produce a slice over zero objects regardless of whether
757            // the underlying pointer is aligned or not.
758            Ok(&[])
759        } else if self.is_aligned() && self.is_contiguous() {
760            Ok(slice::from_raw_parts(self.data(), len))
761        } else {
762            Err(AsSliceError)
763        }
764    }
765
766    /// Returns a mutable view of the internal data as a slice.
767    ///
768    /// # Safety
769    ///
770    /// Calling this method is undefined behaviour if the underlying array
771    /// is aliased immutably or mutably by other instances of [`PyArray`]
772    /// or concurrently modified by Python or other native code.
773    ///
774    /// Please consider the safe alternative [`PyReadwriteArray::as_slice_mut`].
775    #[allow(clippy::mut_from_ref)]
776    unsafe fn as_slice_mut(&self) -> Result<&mut [T], AsSliceError>
777    where
778        T: Element,
779        D: Dimension,
780    {
781        let len = self.len();
782        if len == 0 {
783            // We can still produce a slice over zero objects regardless of whether
784            // the underlying pointer is aligned or not.
785            Ok(&mut [])
786        } else if self.is_aligned() && self.is_contiguous() {
787            Ok(slice::from_raw_parts_mut(self.data(), len))
788        } else {
789            Err(AsSliceError)
790        }
791    }
792
793    /// Get a reference of the specified element if the given index is valid.
794    ///
795    /// # Safety
796    ///
797    /// Calling this method is undefined behaviour if the underlying array
798    /// is aliased mutably by other instances of `PyArray`
799    /// or concurrently modified by Python or other native code.
800    ///
801    /// Consider using safe alternatives like [`PyReadonlyArray::get`].
802    ///
803    /// # Example
804    ///
805    /// ```
806    /// use numpy::{PyArray, PyArrayMethods};
807    /// use pyo3::Python;
808    ///
809    /// Python::attach(|py| {
810    ///     let pyarray = PyArray::arange(py, 0, 16, 1).reshape([2, 2, 4]).unwrap();
811    ///
812    ///     assert_eq!(unsafe { *pyarray.get([1, 0, 3]).unwrap() }, 11);
813    /// });
814    /// ```
815    unsafe fn get(&self, index: impl NpyIndex<Dim = D>) -> Option<&T>
816    where
817        T: Element,
818        D: Dimension;
819
820    /// Same as [`get`][Self::get], but returns `Option<&mut T>`.
821    ///
822    /// # Safety
823    ///
824    /// Calling this method is undefined behaviour if the underlying array
825    /// is aliased immutably or mutably by other instances of [`PyArray`]
826    /// or concurrently modified by Python or other native code.
827    ///
828    /// Consider using safe alternatives like [`PyReadwriteArray::get_mut`].
829    ///
830    /// # Example
831    ///
832    /// ```
833    /// use numpy::{PyArray, PyArrayMethods};
834    /// use pyo3::Python;
835    ///
836    /// Python::attach(|py| {
837    ///     let pyarray = PyArray::arange(py, 0, 16, 1).reshape([2, 2, 4]).unwrap();
838    ///
839    ///     unsafe {
840    ///         *pyarray.get_mut([1, 0, 3]).unwrap() = 42;
841    ///     }
842    ///
843    ///     assert_eq!(unsafe { *pyarray.get([1, 0, 3]).unwrap() }, 42);
844    /// });
845    /// ```
846    #[allow(clippy::mut_from_ref)]
847    unsafe fn get_mut(&self, index: impl NpyIndex<Dim = D>) -> Option<&mut T>
848    where
849        T: Element,
850        D: Dimension;
851
852    /// Get an immutable reference of the specified element,
853    /// without checking the given index.
854    ///
855    /// See [`NpyIndex`] for what types can be used as the index.
856    ///
857    /// # Safety
858    ///
859    /// Passing an invalid index is undefined behavior.
860    /// The element must also have been initialized and
861    /// all other references to it is must also be shared.
862    ///
863    /// See [`PyReadonlyArray::get`] for a safe alternative.
864    ///
865    /// # Example
866    ///
867    /// ```
868    /// use numpy::{PyArray, PyArrayMethods};
869    /// use pyo3::Python;
870    ///
871    /// Python::attach(|py| {
872    ///     let pyarray = PyArray::arange(py, 0, 16, 1).reshape([2, 2, 4]).unwrap();
873    ///
874    ///     assert_eq!(unsafe { *pyarray.uget([1, 0, 3]) }, 11);
875    /// });
876    /// ```
877    #[inline(always)]
878    unsafe fn uget<Idx>(&self, index: Idx) -> &T
879    where
880        T: Element,
881        D: Dimension,
882        Idx: NpyIndex<Dim = D>,
883    {
884        &*self.uget_raw(index)
885    }
886
887    /// Same as [`uget`](Self::uget), but returns `&mut T`.
888    ///
889    /// # Safety
890    ///
891    /// Passing an invalid index is undefined behavior.
892    /// The element must also have been initialized and
893    /// other references to it must not exist.
894    ///
895    /// See [`PyReadwriteArray::get_mut`] for a safe alternative.
896    #[inline(always)]
897    #[allow(clippy::mut_from_ref)]
898    unsafe fn uget_mut<Idx>(&self, index: Idx) -> &mut T
899    where
900        T: Element,
901        D: Dimension,
902        Idx: NpyIndex<Dim = D>,
903    {
904        &mut *self.uget_raw(index)
905    }
906
907    /// Same as [`uget`][Self::uget], but returns `*mut T`.
908    ///
909    /// # Safety
910    ///
911    /// Passing an invalid index is undefined behavior.
912    #[inline(always)]
913    unsafe fn uget_raw<Idx>(&self, index: Idx) -> *mut T
914    where
915        T: Element,
916        D: Dimension,
917        Idx: NpyIndex<Dim = D>,
918    {
919        let offset = index.get_unchecked::<T>(self.strides());
920        self.data().offset(offset) as *mut _
921    }
922
923    /// Get a copy of the specified element in the array.
924    ///
925    /// See [`NpyIndex`] for what types can be used as the index.
926    ///
927    /// # Example
928    /// ```
929    /// use numpy::{PyArray, PyArrayMethods};
930    /// use pyo3::Python;
931    ///
932    /// Python::attach(|py| {
933    ///     let pyarray = PyArray::arange(py, 0, 16, 1).reshape([2, 2, 4]).unwrap();
934    ///
935    ///     assert_eq!(pyarray.get_owned([1, 0, 3]), Some(11));
936    /// });
937    /// ```
938    fn get_owned<Idx>(&self, index: Idx) -> Option<T>
939    where
940        T: Element,
941        D: Dimension,
942        Idx: NpyIndex<Dim = D>;
943
944    /// Turn an array with fixed dimensionality into one with dynamic dimensionality.
945    fn to_dyn(&self) -> &Bound<'py, PyArray<T, IxDyn>>
946    where
947        T: Element,
948        D: Dimension;
949
950    /// Returns a copy of the internal data of the array as a [`Vec`].
951    ///
952    /// Fails if the internal array is not contiguous. See also [`as_slice`][Self::as_slice].
953    ///
954    /// # Example
955    ///
956    /// ```
957    /// use numpy::{PyArray2, PyArrayMethods};
958    /// use pyo3::{Python, types::PyAnyMethods, ffi::c_str};
959    ///
960    /// # fn main() -> pyo3::PyResult<()> {
961    /// Python::attach(|py| {
962    ///     let pyarray= py
963    ///         .eval(c_str!("__import__('numpy').array([[0, 1], [2, 3]], dtype='int64')"), None, None)?
964    ///         .cast_into::<PyArray2<i64>>()?;
965    ///
966    ///     assert_eq!(pyarray.to_vec()?, vec![0, 1, 2, 3]);
967    /// #   Ok(())
968    /// })
969    /// # }
970    /// ```
971    fn to_vec(&self) -> Result<Vec<T>, AsSliceError>
972    where
973        T: Element,
974        D: Dimension;
975
976    /// Consume `self` into an immutable borrow of the NumPy array
977    fn try_into_readonly(self) -> Result<PyReadonlyArray<'py, T, D>, BorrowError>
978    where
979        T: Element,
980        D: Dimension;
981
982    /// Get an immutable borrow of the NumPy array
983    fn try_readonly(&self) -> Result<PyReadonlyArray<'py, T, D>, BorrowError>
984    where
985        T: Element,
986        D: Dimension;
987
988    /// Consume `self` into an immutable borrow of the NumPy array
989    ///
990    /// # Panics
991    ///
992    /// Panics if the allocation backing the array is currently mutably borrowed.
993    ///
994    /// For a non-panicking variant, use [`try_into_readonly`][Self::try_into_readonly].
995    fn into_readonly(self) -> PyReadonlyArray<'py, T, D>
996    where
997        T: Element,
998        D: Dimension,
999    {
1000        self.try_into_readonly().unwrap()
1001    }
1002
1003    /// Get an immutable borrow of the NumPy array
1004    ///
1005    /// # Panics
1006    ///
1007    /// Panics if the allocation backing the array is currently mutably borrowed.
1008    ///
1009    /// For a non-panicking variant, use [`try_readonly`][Self::try_readonly].
1010    fn readonly(&self) -> PyReadonlyArray<'py, T, D>
1011    where
1012        T: Element,
1013        D: Dimension,
1014    {
1015        self.try_readonly().unwrap()
1016    }
1017
1018    /// Consume `self` into an mutable borrow of the NumPy array
1019    fn try_into_readwrite(self) -> Result<PyReadwriteArray<'py, T, D>, BorrowError>
1020    where
1021        T: Element,
1022        D: Dimension;
1023
1024    /// Get a mutable borrow of the NumPy array
1025    fn try_readwrite(&self) -> Result<PyReadwriteArray<'py, T, D>, BorrowError>
1026    where
1027        T: Element,
1028        D: Dimension;
1029
1030    /// Consume `self` into an mutable borrow of the NumPy array
1031    ///
1032    /// # Panics
1033    ///
1034    /// Panics if the allocation backing the array is currently borrowed or
1035    /// if the array is [flagged as][flags] not writeable.
1036    ///
1037    /// For a non-panicking variant, use [`try_into_readwrite`][Self::try_into_readwrite].
1038    ///
1039    /// [flags]: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.flags.html
1040    fn into_readwrite(self) -> PyReadwriteArray<'py, T, D>
1041    where
1042        T: Element,
1043        D: Dimension,
1044    {
1045        self.try_into_readwrite().unwrap()
1046    }
1047
1048    /// Get a mutable borrow of the NumPy array
1049    ///
1050    /// # Panics
1051    ///
1052    /// Panics if the allocation backing the array is currently borrowed or
1053    /// if the array is [flagged as][flags] not writeable.
1054    ///
1055    /// For a non-panicking variant, use [`try_readwrite`][Self::try_readwrite].
1056    ///
1057    /// [flags]: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.flags.html
1058    fn readwrite(&self) -> PyReadwriteArray<'py, T, D>
1059    where
1060        T: Element,
1061        D: Dimension,
1062    {
1063        self.try_readwrite().unwrap()
1064    }
1065
1066    /// Returns an [`ArrayView`] of the internal array.
1067    ///
1068    /// See also [`PyReadonlyArray::as_array`].
1069    ///
1070    /// # Safety
1071    ///
1072    /// Calling this method invalidates all exclusive references to the internal data, e.g. `&mut [T]` or `ArrayViewMut`.
1073    unsafe fn as_array(&self) -> ArrayView<'_, T, D>
1074    where
1075        T: Element,
1076        D: Dimension;
1077
1078    /// Returns an [`ArrayViewMut`] of the internal array.
1079    ///
1080    /// See also [`PyReadwriteArray::as_array_mut`].
1081    ///
1082    /// # Safety
1083    ///
1084    /// Calling this method invalidates all other references to the internal data, e.g. `ArrayView` or `ArrayViewMut`.
1085    unsafe fn as_array_mut(&self) -> ArrayViewMut<'_, T, D>
1086    where
1087        T: Element,
1088        D: Dimension;
1089
1090    /// Returns the internal array as [`RawArrayView`] enabling element access via raw pointers
1091    fn as_raw_array(&self) -> RawArrayView<T, D>
1092    where
1093        T: Element,
1094        D: Dimension;
1095
1096    /// Returns the internal array as [`RawArrayViewMut`] enabling element access via raw pointers
1097    fn as_raw_array_mut(&self) -> RawArrayViewMut<T, D>
1098    where
1099        T: Element,
1100        D: Dimension;
1101
1102    /// Get a copy of the array as an [`ndarray::Array`].
1103    ///
1104    /// # Example
1105    ///
1106    /// ```
1107    /// use numpy::{PyArray, PyArrayMethods};
1108    /// use ndarray::array;
1109    /// use pyo3::Python;
1110    ///
1111    /// Python::attach(|py| {
1112    ///     let pyarray = PyArray::arange(py, 0, 4, 1).reshape([2, 2]).unwrap();
1113    ///
1114    ///     assert_eq!(
1115    ///         pyarray.to_owned_array(),
1116    ///         array![[0, 1], [2, 3]]
1117    ///     )
1118    /// });
1119    /// ```
1120    fn to_owned_array(&self) -> Array<T, D>
1121    where
1122        T: Element,
1123        D: Dimension;
1124
1125    /// Copies `self` into `other`, performing a data type conversion if necessary.
1126    ///
1127    /// See also [`PyArray_CopyInto`][PyArray_CopyInto].
1128    ///
1129    /// # Example
1130    ///
1131    /// ```
1132    /// use numpy::{PyArray, PyArrayMethods};
1133    /// use pyo3::Python;
1134    ///
1135    /// Python::attach(|py| {
1136    ///     let pyarray_f = PyArray::arange(py, 2.0, 5.0, 1.0);
1137    ///     let pyarray_i = unsafe { PyArray::<i64, _>::new(py, [3], false) };
1138    ///
1139    ///     assert!(pyarray_f.copy_to(&pyarray_i).is_ok());
1140    ///
1141    ///     assert_eq!(pyarray_i.readonly().as_slice().unwrap(), &[2, 3, 4]);
1142    /// });
1143    /// ```
1144    ///
1145    /// [PyArray_CopyInto]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_CopyInto
1146    fn copy_to<U: Element>(&self, other: &Bound<'py, PyArray<U, D>>) -> PyResult<()>
1147    where
1148        T: Element;
1149
1150    /// Deprecated version of [`cast_array`](PyArrayMethods::cast_array)
1151    #[deprecated(since = "0.26.0", note = "use `cast_array` instead")]
1152    #[inline]
1153    fn cast<U: Element>(&self, is_fortran: bool) -> PyResult<Bound<'py, PyArray<U, D>>>
1154    where
1155        T: Element,
1156    {
1157        self.cast_array(is_fortran)
1158    }
1159
1160    /// Cast the `PyArray<T>` to `PyArray<U>`, by allocating a new array.
1161    ///
1162    /// See also [`PyArray_CastToType`][PyArray_CastToType].
1163    ///
1164    /// # Example
1165    ///
1166    /// ```
1167    /// use numpy::{PyArray, PyArrayMethods};
1168    /// use pyo3::Python;
1169    ///
1170    /// Python::attach(|py| {
1171    ///     let pyarray_f = PyArray::arange(py, 2.0, 5.0, 1.0);
1172    ///
1173    ///     let pyarray_i = pyarray_f.cast_array::<i32>(false).unwrap();
1174    ///
1175    ///     assert_eq!(pyarray_i.readonly().as_slice().unwrap(), &[2, 3, 4]);
1176    /// });
1177    /// ```
1178    ///
1179    /// [PyArray_CastToType]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_CastToType
1180    fn cast_array<U: Element>(&self, is_fortran: bool) -> PyResult<Bound<'py, PyArray<U, D>>>
1181    where
1182        T: Element;
1183
1184    /// A view of `self` with a different order of axes determined by `axes`.
1185    ///
1186    /// If `axes` is `None`, the order of axes is reversed which corresponds to the standard matrix transpose.
1187    ///
1188    /// See also [`numpy.transpose`][numpy-transpose] and [`PyArray_Transpose`][PyArray_Transpose].
1189    ///
1190    /// # Example
1191    ///
1192    /// ```
1193    /// use numpy::prelude::*;
1194    /// use numpy::PyArray;
1195    /// use pyo3::Python;
1196    /// use ndarray::array;
1197    ///
1198    /// Python::attach(|py| {
1199    ///     let array = array![[0, 1, 2], [3, 4, 5]].into_pyarray(py);
1200    ///
1201    ///     let array = array.permute(Some([1, 0])).unwrap();
1202    ///
1203    ///     assert_eq!(array.readonly().as_array(), array![[0, 3], [1, 4], [2, 5]]);
1204    /// });
1205    /// ```
1206    ///
1207    /// [numpy-transpose]: https://numpy.org/doc/stable/reference/generated/numpy.transpose.html
1208    /// [PyArray_Transpose]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_Transpose
1209    fn permute<ID: IntoDimension>(&self, axes: Option<ID>) -> PyResult<Bound<'py, PyArray<T, D>>>
1210    where
1211        T: Element;
1212
1213    /// Special case of [`permute`][Self::permute] which reverses the order the axes.
1214    fn transpose(&self) -> PyResult<Bound<'py, PyArray<T, D>>>
1215    where
1216        T: Element,
1217    {
1218        self.permute::<()>(None)
1219    }
1220
1221    /// Construct a new array which has same values as `self`,
1222    /// but has different dimensions specified by `shape`
1223    /// and a possibly different memory order specified by `order`.
1224    ///
1225    /// See also [`numpy.reshape`][numpy-reshape] and [`PyArray_Newshape`][PyArray_Newshape].
1226    ///
1227    /// # Example
1228    ///
1229    /// ```
1230    /// use numpy::prelude::*;
1231    /// use numpy::{npyffi::NPY_ORDER, PyArray};
1232    /// use pyo3::Python;
1233    /// use ndarray::array;
1234    ///
1235    /// Python::attach(|py| {
1236    ///     let array =
1237    ///         PyArray::from_iter(py, 0..9).reshape_with_order([3, 3], NPY_ORDER::NPY_FORTRANORDER).unwrap();
1238    ///
1239    ///     assert_eq!(array.readonly().as_array(), array![[0, 3, 6], [1, 4, 7], [2, 5, 8]]);
1240    ///     assert!(array.is_fortran_contiguous());
1241    ///
1242    ///     assert!(array.reshape([5]).is_err());
1243    /// });
1244    /// ```
1245    ///
1246    /// [numpy-reshape]: https://numpy.org/doc/stable/reference/generated/numpy.reshape.html
1247    /// [PyArray_Newshape]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_Newshape
1248    fn reshape_with_order<ID: IntoDimension>(
1249        &self,
1250        shape: ID,
1251        order: NPY_ORDER,
1252    ) -> PyResult<Bound<'py, PyArray<T, ID::Dim>>>
1253    where
1254        T: Element;
1255
1256    /// Special case of [`reshape_with_order`][Self::reshape_with_order] which keeps the memory order the same.
1257    #[inline(always)]
1258    fn reshape<ID: IntoDimension>(&self, shape: ID) -> PyResult<Bound<'py, PyArray<T, ID::Dim>>>
1259    where
1260        T: Element,
1261    {
1262        self.reshape_with_order(shape, NPY_ORDER::NPY_ANYORDER)
1263    }
1264
1265    /// Extends or truncates the dimensions of an array.
1266    ///
1267    /// This method works only on [contiguous][PyUntypedArrayMethods::is_contiguous] arrays.
1268    /// Missing elements will be initialized as if calling [`zeros`][PyArray::zeros].
1269    ///
1270    /// See also [`ndarray.resize`][ndarray-resize] and [`PyArray_Resize`][PyArray_Resize].
1271    ///
1272    /// # Safety
1273    ///
1274    /// There should be no outstanding references (shared or exclusive) into the array
1275    /// as this method might re-allocate it and thereby invalidate all pointers into it.
1276    ///
1277    /// # Example
1278    ///
1279    /// ```
1280    /// use numpy::prelude::*;
1281    /// use numpy::PyArray;
1282    /// use pyo3::Python;
1283    ///
1284    /// Python::attach(|py| {
1285    ///     let pyarray = PyArray::<f64, _>::zeros(py, (10, 10), false);
1286    ///     assert_eq!(pyarray.shape(), [10, 10]);
1287    ///
1288    ///     unsafe {
1289    ///         pyarray.resize((100, 100)).unwrap();
1290    ///     }
1291    ///     assert_eq!(pyarray.shape(), [100, 100]);
1292    /// });
1293    /// ```
1294    ///
1295    /// [ndarray-resize]: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.resize.html
1296    /// [PyArray_Resize]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_Resize
1297    unsafe fn resize<ID: IntoDimension>(&self, newshape: ID) -> PyResult<()>
1298    where
1299        T: Element;
1300
1301    /// Try to convert this array into a [`nalgebra::MatrixView`] using the given shape and strides.
1302    ///
1303    /// # Safety
1304    ///
1305    /// Calling this method invalidates all exclusive references to the internal data, e.g. `ArrayViewMut` or `MatrixSliceMut`.
1306    #[doc(alias = "nalgebra")]
1307    #[cfg(feature = "nalgebra")]
1308    unsafe fn try_as_matrix<R, C, RStride, CStride>(
1309        &self,
1310    ) -> Option<nalgebra::MatrixView<'_, T, R, C, RStride, CStride>>
1311    where
1312        T: nalgebra::Scalar + Element,
1313        D: Dimension,
1314        R: nalgebra::Dim,
1315        C: nalgebra::Dim,
1316        RStride: nalgebra::Dim,
1317        CStride: nalgebra::Dim;
1318
1319    /// Try to convert this array into a [`nalgebra::MatrixViewMut`] using the given shape and strides.
1320    ///
1321    /// # Safety
1322    ///
1323    /// Calling this method invalidates all other references to the internal data, e.g. `ArrayView`, `MatrixSlice`, `ArrayViewMut` or `MatrixSliceMut`.
1324    #[doc(alias = "nalgebra")]
1325    #[cfg(feature = "nalgebra")]
1326    unsafe fn try_as_matrix_mut<R, C, RStride, CStride>(
1327        &self,
1328    ) -> Option<nalgebra::MatrixViewMut<'_, T, R, C, RStride, CStride>>
1329    where
1330        T: nalgebra::Scalar + Element,
1331        D: Dimension,
1332        R: nalgebra::Dim,
1333        C: nalgebra::Dim,
1334        RStride: nalgebra::Dim,
1335        CStride: nalgebra::Dim;
1336}
1337
1338/// Implementation of functionality for [`PyArray0<T>`].
1339#[doc(alias = "PyArray", alias = "PyArray0")]
1340pub trait PyArray0Methods<'py, T>: PyArrayMethods<'py, T, Ix0> {
1341    /// Get the single element of a zero-dimensional array.
1342    ///
1343    /// See [`inner`][crate::inner] for an example.
1344    fn item(&self) -> T
1345    where
1346        T: Element + Copy,
1347    {
1348        unsafe { *self.data() }
1349    }
1350}
1351
1352#[inline(always)]
1353fn get_raw<T, D, Idx>(slf: &Bound<'_, PyArray<T, D>>, index: Idx) -> Option<*mut T>
1354where
1355    T: Element,
1356    D: Dimension,
1357    Idx: NpyIndex<Dim = D>,
1358{
1359    let offset = index.get_checked::<T>(slf.shape(), slf.strides())?;
1360    Some(unsafe { slf.data().offset(offset) })
1361}
1362
1363fn as_view<T, D, S, F>(slf: &Bound<'_, PyArray<T, D>>, from_shape_ptr: F) -> ArrayBase<S, D>
1364where
1365    T: Element,
1366    D: Dimension,
1367    S: RawData,
1368    F: FnOnce(StrideShape<D>, *mut T) -> ArrayBase<S, D>,
1369{
1370    fn inner<D: Dimension>(
1371        shape: &[usize],
1372        strides: &[isize],
1373        itemsize: usize,
1374        mut data_ptr: *mut u8,
1375    ) -> (StrideShape<D>, u32, *mut u8) {
1376        let shape = D::from_dimension(&Dim(shape)).expect(DIMENSIONALITY_MISMATCH_ERR);
1377
1378        assert!(strides.len() <= 32, "{}", MAX_DIMENSIONALITY_ERR);
1379
1380        let mut new_strides = D::zeros(strides.len());
1381        let mut inverted_axes = 0_u32;
1382
1383        for i in 0..strides.len() {
1384            // FIXME(kngwyu): Replace this hacky negative strides support with
1385            // a proper constructor, when it's implemented.
1386            // See https://github.com/rust-ndarray/ndarray/issues/842 for more.
1387            if strides[i] >= 0 {
1388                new_strides[i] = strides[i] as usize / itemsize;
1389            } else {
1390                // Move the pointer to the start position.
1391                data_ptr = unsafe { data_ptr.offset(strides[i] * (shape[i] as isize - 1)) };
1392
1393                new_strides[i] = (-strides[i]) as usize / itemsize;
1394                inverted_axes |= 1 << i;
1395            }
1396        }
1397
1398        (shape.strides(new_strides), inverted_axes, data_ptr)
1399    }
1400
1401    let (shape, mut inverted_axes, data_ptr) = inner(
1402        slf.shape(),
1403        slf.strides(),
1404        mem::size_of::<T>(),
1405        slf.data() as _,
1406    );
1407
1408    let mut array = from_shape_ptr(shape, data_ptr as _);
1409
1410    while inverted_axes != 0 {
1411        let axis = inverted_axes.trailing_zeros() as usize;
1412        inverted_axes &= !(1 << axis);
1413
1414        array.invert_axis(Axis(axis));
1415    }
1416
1417    array
1418}
1419
1420#[cfg(feature = "nalgebra")]
1421fn try_as_matrix_shape_strides<N, D, R, C, RStride, CStride>(
1422    slf: &Bound<'_, PyArray<N, D>>,
1423) -> Option<((R, C), (RStride, CStride))>
1424where
1425    N: nalgebra::Scalar + Element,
1426    D: Dimension,
1427    R: nalgebra::Dim,
1428    C: nalgebra::Dim,
1429    RStride: nalgebra::Dim,
1430    CStride: nalgebra::Dim,
1431{
1432    let ndim = slf.ndim();
1433    let shape = slf.shape();
1434    let strides = slf.strides();
1435
1436    if ndim != 1 && ndim != 2 {
1437        return None;
1438    }
1439
1440    if strides.iter().any(|strides| *strides < 0) {
1441        return None;
1442    }
1443
1444    let rows = shape[0];
1445    let cols = *shape.get(1).unwrap_or(&1);
1446
1447    if R::try_to_usize().map(|expected| rows == expected) == Some(false) {
1448        return None;
1449    }
1450
1451    if C::try_to_usize().map(|expected| cols == expected) == Some(false) {
1452        return None;
1453    }
1454
1455    let row_stride = strides[0] as usize / mem::size_of::<N>();
1456    let col_stride = strides
1457        .get(1)
1458        .map_or(rows, |stride| *stride as usize / mem::size_of::<N>());
1459
1460    if RStride::try_to_usize().map(|expected| row_stride == expected) == Some(false) {
1461        return None;
1462    }
1463
1464    if CStride::try_to_usize().map(|expected| col_stride == expected) == Some(false) {
1465        return None;
1466    }
1467
1468    let shape = (R::from_usize(rows), C::from_usize(cols));
1469
1470    let strides = (
1471        RStride::from_usize(row_stride),
1472        CStride::from_usize(col_stride),
1473    );
1474
1475    Some((shape, strides))
1476}
1477
1478impl<'py, T, D> PyArrayMethods<'py, T, D> for Bound<'py, PyArray<T, D>> {
1479    #[inline(always)]
1480    fn as_untyped(&self) -> &Bound<'py, PyUntypedArray> {
1481        unsafe { self.cast_unchecked() }
1482    }
1483
1484    #[inline(always)]
1485    fn data(&self) -> *mut T {
1486        unsafe { (*self.as_array_ptr()).data.cast() }
1487    }
1488
1489    #[inline(always)]
1490    unsafe fn get(&self, index: impl NpyIndex<Dim = D>) -> Option<&T>
1491    where
1492        T: Element,
1493        D: Dimension,
1494    {
1495        let ptr = get_raw(self, index)?;
1496        Some(&*ptr)
1497    }
1498
1499    #[inline(always)]
1500    unsafe fn get_mut(&self, index: impl NpyIndex<Dim = D>) -> Option<&mut T>
1501    where
1502        T: Element,
1503        D: Dimension,
1504    {
1505        let ptr = get_raw(self, index)?;
1506        Some(&mut *ptr)
1507    }
1508
1509    fn get_owned<Idx>(&self, index: Idx) -> Option<T>
1510    where
1511        T: Element,
1512        D: Dimension,
1513        Idx: NpyIndex<Dim = D>,
1514    {
1515        let element = unsafe { self.get(index) };
1516        element.map(|elem| elem.clone_ref(self.py()))
1517    }
1518
1519    fn to_dyn(&self) -> &Bound<'py, PyArray<T, IxDyn>> {
1520        unsafe { self.cast_unchecked() }
1521    }
1522
1523    fn to_vec(&self) -> Result<Vec<T>, AsSliceError>
1524    where
1525        T: Element,
1526        D: Dimension,
1527    {
1528        let slice = unsafe { self.as_slice() };
1529        slice.map(|slc| T::vec_from_slice(self.py(), slc))
1530    }
1531
1532    fn try_into_readonly(self) -> Result<PyReadonlyArray<'py, T, D>, BorrowError>
1533    where
1534        T: Element,
1535        D: Dimension,
1536    {
1537        PyReadonlyArray::try_new(self)
1538    }
1539
1540    fn try_readonly(&self) -> Result<PyReadonlyArray<'py, T, D>, BorrowError>
1541    where
1542        T: Element,
1543        D: Dimension,
1544    {
1545        self.clone().try_into_readonly()
1546    }
1547
1548    fn try_into_readwrite(self) -> Result<PyReadwriteArray<'py, T, D>, BorrowError>
1549    where
1550        T: Element,
1551        D: Dimension,
1552    {
1553        PyReadwriteArray::try_new(self)
1554    }
1555
1556    fn try_readwrite(&self) -> Result<PyReadwriteArray<'py, T, D>, BorrowError>
1557    where
1558        T: Element,
1559        D: Dimension,
1560    {
1561        self.clone().try_into_readwrite()
1562    }
1563
1564    unsafe fn as_array(&self) -> ArrayView<'_, T, D>
1565    where
1566        T: Element,
1567        D: Dimension,
1568    {
1569        as_view(self, |shape, ptr| ArrayView::from_shape_ptr(shape, ptr))
1570    }
1571
1572    unsafe fn as_array_mut(&self) -> ArrayViewMut<'_, T, D>
1573    where
1574        T: Element,
1575        D: Dimension,
1576    {
1577        as_view(self, |shape, ptr| ArrayViewMut::from_shape_ptr(shape, ptr))
1578    }
1579
1580    fn as_raw_array(&self) -> RawArrayView<T, D>
1581    where
1582        T: Element,
1583        D: Dimension,
1584    {
1585        as_view(self, |shape, ptr| unsafe {
1586            RawArrayView::from_shape_ptr(shape, ptr)
1587        })
1588    }
1589
1590    fn as_raw_array_mut(&self) -> RawArrayViewMut<T, D>
1591    where
1592        T: Element,
1593        D: Dimension,
1594    {
1595        as_view(self, |shape, ptr| unsafe {
1596            RawArrayViewMut::from_shape_ptr(shape, ptr)
1597        })
1598    }
1599
1600    fn to_owned_array(&self) -> Array<T, D>
1601    where
1602        T: Element,
1603        D: Dimension,
1604    {
1605        let view = unsafe { self.as_array() };
1606        T::array_from_view(self.py(), view)
1607    }
1608
1609    fn copy_to<U: Element>(&self, other: &Bound<'py, PyArray<U, D>>) -> PyResult<()>
1610    where
1611        T: Element,
1612    {
1613        let self_ptr = self.as_array_ptr();
1614        let other_ptr = other.as_array_ptr();
1615        let result = unsafe { PY_ARRAY_API.PyArray_CopyInto(self.py(), other_ptr, self_ptr) };
1616        if result != -1 {
1617            Ok(())
1618        } else {
1619            Err(PyErr::fetch(self.py()))
1620        }
1621    }
1622
1623    fn cast_array<U: Element>(&self, is_fortran: bool) -> PyResult<Bound<'py, PyArray<U, D>>>
1624    where
1625        T: Element,
1626    {
1627        let ptr = unsafe {
1628            PY_ARRAY_API.PyArray_CastToType(
1629                self.py(),
1630                self.as_array_ptr(),
1631                U::get_dtype(self.py()).into_dtype_ptr(),
1632                if is_fortran { -1 } else { 0 },
1633            )
1634        };
1635        unsafe { Bound::from_owned_ptr_or_err(self.py(), ptr).map(|ob| ob.cast_into_unchecked()) }
1636    }
1637
1638    fn permute<ID: IntoDimension>(&self, axes: Option<ID>) -> PyResult<Bound<'py, PyArray<T, D>>> {
1639        let mut axes = axes.map(|axes| axes.into_dimension());
1640        let mut axes = axes.as_mut().map(|axes| axes.to_npy_dims());
1641        let axes = axes
1642            .as_mut()
1643            .map_or_else(ptr::null_mut, |axes| axes as *mut npyffi::PyArray_Dims);
1644
1645        let py = self.py();
1646        let ptr = unsafe { PY_ARRAY_API.PyArray_Transpose(py, self.as_array_ptr(), axes) };
1647        unsafe { Bound::from_owned_ptr_or_err(py, ptr).map(|ob| ob.cast_into_unchecked()) }
1648    }
1649
1650    fn reshape_with_order<ID: IntoDimension>(
1651        &self,
1652        shape: ID,
1653        order: NPY_ORDER,
1654    ) -> PyResult<Bound<'py, PyArray<T, ID::Dim>>>
1655    where
1656        T: Element,
1657    {
1658        let mut shape = shape.into_dimension();
1659        let mut shape = shape.to_npy_dims();
1660
1661        let py = self.py();
1662        let ptr = unsafe {
1663            PY_ARRAY_API.PyArray_Newshape(
1664                py,
1665                self.as_array_ptr(),
1666                &mut shape as *mut npyffi::PyArray_Dims,
1667                order,
1668            )
1669        };
1670        unsafe { Bound::from_owned_ptr_or_err(py, ptr).map(|ob| ob.cast_into_unchecked()) }
1671    }
1672
1673    unsafe fn resize<ID: IntoDimension>(&self, newshape: ID) -> PyResult<()>
1674    where
1675        T: Element,
1676    {
1677        let mut newshape = newshape.into_dimension();
1678        let mut newshape = newshape.to_npy_dims();
1679
1680        let py = self.py();
1681        let res = PY_ARRAY_API.PyArray_Resize(
1682            py,
1683            self.as_array_ptr(),
1684            &mut newshape as *mut npyffi::PyArray_Dims,
1685            1,
1686            NPY_ORDER::NPY_ANYORDER,
1687        );
1688
1689        if !res.is_null() {
1690            Ok(())
1691        } else {
1692            Err(PyErr::fetch(py))
1693        }
1694    }
1695
1696    #[cfg(feature = "nalgebra")]
1697    unsafe fn try_as_matrix<R, C, RStride, CStride>(
1698        &self,
1699    ) -> Option<nalgebra::MatrixView<'_, T, R, C, RStride, CStride>>
1700    where
1701        T: nalgebra::Scalar + Element,
1702        D: Dimension,
1703        R: nalgebra::Dim,
1704        C: nalgebra::Dim,
1705        RStride: nalgebra::Dim,
1706        CStride: nalgebra::Dim,
1707    {
1708        let (shape, strides) = try_as_matrix_shape_strides(self)?;
1709
1710        let storage = nalgebra::ViewStorage::from_raw_parts(self.data(), shape, strides);
1711
1712        Some(nalgebra::Matrix::from_data(storage))
1713    }
1714
1715    #[cfg(feature = "nalgebra")]
1716    unsafe fn try_as_matrix_mut<R, C, RStride, CStride>(
1717        &self,
1718    ) -> Option<nalgebra::MatrixViewMut<'_, T, R, C, RStride, CStride>>
1719    where
1720        T: nalgebra::Scalar + Element,
1721        D: Dimension,
1722        R: nalgebra::Dim,
1723        C: nalgebra::Dim,
1724        RStride: nalgebra::Dim,
1725        CStride: nalgebra::Dim,
1726    {
1727        let (shape, strides) = try_as_matrix_shape_strides(self)?;
1728
1729        let storage = nalgebra::ViewStorageMut::from_raw_parts(self.data(), shape, strides);
1730
1731        Some(nalgebra::Matrix::from_data(storage))
1732    }
1733}
1734
1735impl<'py, T> PyArray0Methods<'py, T> for Bound<'py, PyArray0<T>> {}
1736
1737#[cfg(test)]
1738mod tests {
1739    use super::*;
1740
1741    use ndarray::array;
1742    use pyo3::{py_run, types::PyList};
1743
1744    #[test]
1745    fn test_dyn_to_owned_array() {
1746        Python::attach(|py| {
1747            let array = PyArray::from_vec2(py, &[vec![1, 2], vec![3, 4]])
1748                .unwrap()
1749                .to_dyn()
1750                .to_owned_array();
1751
1752            assert_eq!(array, array![[1, 2], [3, 4]].into_dyn());
1753        });
1754    }
1755
1756    #[test]
1757    fn test_hasobject_flag() {
1758        Python::attach(|py| {
1759            let array: Bound<'_, PyArray<Py<PyAny>, _>> =
1760                PyArray1::from_slice(py, &[PyList::empty(py).into()]);
1761
1762            py_run!(py, array, "assert array.dtype.hasobject");
1763        });
1764    }
1765}