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