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