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