use std::{
marker::PhantomData,
mem,
os::raw::{c_int, c_void},
ptr, slice,
};
use ndarray::{
Array, ArrayBase, ArrayView, ArrayViewMut, Axis, Data, Dim, Dimension, IntoDimension, Ix0, Ix1,
Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, RawArrayView, RawArrayViewMut, RawData, ShapeBuilder,
StrideShape,
};
use num_traits::AsPrimitive;
use pyo3::{
ffi,
types::{DerefToPyAny, PyModule},
Bound, CastError, Py, PyAny, PyErr, PyResult, PyTypeCheck, PyTypeInfo, Python,
};
use crate::borrow::{PyReadonlyArray, PyReadwriteArray};
use crate::cold;
use crate::convert::{ArrayExt, IntoPyArray, NpyIndex, ToNpyDims, ToPyArray};
use crate::dtype::{Element, PyArrayDescrMethods};
use crate::error::{
BorrowError, DimensionalityError, FromVecError, IgnoreError, NotContiguousError, TypeError,
DIMENSIONALITY_MISMATCH_ERR, MAX_DIMENSIONALITY_ERR,
};
use crate::npyffi::{self, npy_intp, NPY_ORDER, PY_ARRAY_API};
use crate::slice_container::PySliceContainer;
use crate::untyped_array::{PyUntypedArray, PyUntypedArrayMethods};
#[repr(transparent)]
pub struct PyArray<T, D>(PyAny, PhantomData<T>, PhantomData<D>);
pub type PyArray0<T> = PyArray<T, Ix0>;
pub type PyArray1<T> = PyArray<T, Ix1>;
pub type PyArray2<T> = PyArray<T, Ix2>;
pub type PyArray3<T> = PyArray<T, Ix3>;
pub type PyArray4<T> = PyArray<T, Ix4>;
pub type PyArray5<T> = PyArray<T, Ix5>;
pub type PyArray6<T> = PyArray<T, Ix6>;
pub type PyArrayDyn<T> = PyArray<T, IxDyn>;
pub fn get_array_module<'py>(py: Python<'py>) -> PyResult<Bound<'py, PyModule>> {
PyModule::import(py, npyffi::array::mod_name(py)?)
}
impl<T, D> DerefToPyAny for PyArray<T, D> {}
unsafe impl<T: Element, D: Dimension> PyTypeInfo for PyArray<T, D> {
const NAME: &'static str = "PyArray<T, D>";
const MODULE: Option<&'static str> = Some("numpy");
fn type_object_raw<'py>(py: Python<'py>) -> *mut ffi::PyTypeObject {
unsafe { npyffi::PY_ARRAY_API.get_type_object(py, npyffi::NpyTypes::PyArray_Type) }
}
fn is_type_of(ob: &Bound<'_, PyAny>) -> bool {
Self::extract::<IgnoreError>(ob).is_ok()
}
}
impl<T: Element, D: Dimension> PyArray<T, D> {
fn extract<'a, 'py, E>(ob: &'a Bound<'py, PyAny>) -> Result<&'a Bound<'py, Self>, E>
where
E: From<CastError<'a, 'py>> + From<DimensionalityError> + From<TypeError<'py>>,
{
let array = unsafe {
if npyffi::PyArray_Check(ob.py(), ob.as_ptr()) == 0 {
return Err(CastError::new(
ob.as_borrowed(),
<Self as PyTypeCheck>::classinfo_object(ob.py()),
)
.into());
}
ob.cast_unchecked::<Self>()
};
let src_ndim = array.ndim();
if let Some(dst_ndim) = D::NDIM {
if src_ndim != dst_ndim {
return Err(DimensionalityError::new(src_ndim, dst_ndim).into());
}
}
let src_dtype = array.dtype();
let dst_dtype = T::get_dtype(ob.py());
if !src_dtype.is_equiv_to(&dst_dtype) {
return Err(TypeError::new(src_dtype, dst_dtype).into());
}
Ok(array)
}
pub unsafe fn new<'py, ID>(py: Python<'py>, dims: ID, is_fortran: bool) -> Bound<'py, Self>
where
ID: IntoDimension<Dim = D>,
{
let flags = c_int::from(is_fortran);
Self::new_uninit(py, dims, ptr::null_mut(), flags)
}
pub(crate) unsafe fn new_uninit<'py, ID>(
py: Python<'py>,
dims: ID,
strides: *const npy_intp,
flag: c_int,
) -> Bound<'py, Self>
where
ID: IntoDimension<Dim = D>,
{
let mut dims = dims.into_dimension();
let ptr = PY_ARRAY_API.PyArray_NewFromDescr(
py,
PY_ARRAY_API.get_type_object(py, npyffi::NpyTypes::PyArray_Type),
T::get_dtype(py).into_dtype_ptr(),
dims.ndim_cint(),
dims.as_dims_ptr(),
strides as *mut npy_intp, ptr::null_mut(), flag, ptr::null_mut(), );
Bound::from_owned_ptr(py, ptr).cast_into_unchecked()
}
unsafe fn new_with_data<'py, ID>(
py: Python<'py>,
dims: ID,
strides: *const npy_intp,
data_ptr: *const T,
container: *mut PyAny,
) -> Bound<'py, Self>
where
ID: IntoDimension<Dim = D>,
{
let mut dims = dims.into_dimension();
let ptr = PY_ARRAY_API.PyArray_NewFromDescr(
py,
PY_ARRAY_API.get_type_object(py, npyffi::NpyTypes::PyArray_Type),
T::get_dtype(py).into_dtype_ptr(),
dims.ndim_cint(),
dims.as_dims_ptr(),
strides as *mut npy_intp, data_ptr as *mut c_void, npyffi::NPY_ARRAY_WRITEABLE, ptr::null_mut(), );
PY_ARRAY_API.PyArray_SetBaseObject(
py,
ptr as *mut npyffi::PyArrayObject,
container as *mut ffi::PyObject,
);
Bound::from_owned_ptr(py, ptr).cast_into_unchecked()
}
pub(crate) unsafe fn from_raw_parts<'py>(
py: Python<'py>,
dims: D,
strides: *const npy_intp,
data_ptr: *const T,
container: PySliceContainer,
) -> Bound<'py, Self> {
let container = Bound::new(py, container)
.expect("Failed to create slice container")
.into_ptr();
Self::new_with_data(py, dims, strides, data_ptr, container.cast())
}
pub unsafe fn borrow_from_array<'py, S>(
array: &ArrayBase<S, D>,
container: Bound<'py, PyAny>,
) -> Bound<'py, Self>
where
S: Data<Elem = T>,
{
let (strides, dims) = (array.npy_strides(), array.raw_dim());
let data_ptr = array.as_ptr();
let py = container.py();
Self::new_with_data(
py,
dims,
strides.as_ptr(),
data_ptr,
container.into_ptr().cast(),
)
}
pub fn zeros<ID>(py: Python<'_>, dims: ID, is_fortran: bool) -> Bound<'_, Self>
where
ID: IntoDimension<Dim = D>,
{
let mut dims = dims.into_dimension();
unsafe {
let ptr = PY_ARRAY_API.PyArray_Zeros(
py,
dims.ndim_cint(),
dims.as_dims_ptr(),
T::get_dtype(py).into_dtype_ptr(),
if is_fortran { -1 } else { 0 },
);
Bound::from_owned_ptr(py, ptr).cast_into_unchecked()
}
}
pub fn from_owned_array(py: Python<'_>, mut arr: Array<T, D>) -> Bound<'_, Self> {
let (strides, dims) = (arr.npy_strides(), arr.raw_dim());
let data_ptr = arr.as_mut_ptr();
unsafe {
Self::from_raw_parts(
py,
dims,
strides.as_ptr(),
data_ptr,
PySliceContainer::from(arr),
)
}
}
pub fn from_array<'py, S>(py: Python<'py>, arr: &ArrayBase<S, D>) -> Bound<'py, Self>
where
S: Data<Elem = T>,
{
ToPyArray::to_pyarray(arr, py)
}
}
impl<D: Dimension> PyArray<Py<PyAny>, D> {
pub fn from_owned_object_array<T>(py: Python<'_>, mut arr: Array<Py<T>, D>) -> Bound<'_, Self> {
let (strides, dims) = (arr.npy_strides(), arr.raw_dim());
let data_ptr = arr.as_mut_ptr().cast::<Py<PyAny>>().cast_const();
unsafe {
Self::from_raw_parts(
py,
dims,
strides.as_ptr(),
data_ptr,
PySliceContainer::from(arr),
)
}
}
}
impl<T: Element> PyArray<T, Ix1> {
pub fn from_slice<'py>(py: Python<'py>, slice: &[T]) -> Bound<'py, Self> {
unsafe {
let array = PyArray::new(py, [slice.len()], false);
let mut data_ptr = array.data();
clone_elements(py, slice, &mut data_ptr);
array
}
}
#[inline(always)]
pub fn from_vec<'py>(py: Python<'py>, vec: Vec<T>) -> Bound<'py, Self> {
vec.into_pyarray(py)
}
pub fn from_iter<I>(py: Python<'_>, iter: I) -> Bound<'_, Self>
where
I: IntoIterator<Item = T>,
{
let data = iter.into_iter().collect::<Vec<_>>();
data.into_pyarray(py)
}
}
impl<T: Element> PyArray<T, Ix2> {
pub fn from_vec2<'py>(py: Python<'py>, v: &[Vec<T>]) -> Result<Bound<'py, Self>, FromVecError> {
let len2 = v.first().map_or(0, |v| v.len());
let dims = [v.len(), len2];
unsafe {
let array = Self::new(py, dims, false);
let mut data_ptr = array.data();
for v in v {
if v.len() != len2 {
cold();
return Err(FromVecError::new(v.len(), len2));
}
clone_elements(py, v, &mut data_ptr);
}
Ok(array)
}
}
}
impl<T: Element> PyArray<T, Ix3> {
pub fn from_vec3<'py>(
py: Python<'py>,
v: &[Vec<Vec<T>>],
) -> Result<Bound<'py, Self>, FromVecError> {
let len2 = v.first().map_or(0, |v| v.len());
let len3 = v.first().map_or(0, |v| v.first().map_or(0, |v| v.len()));
let dims = [v.len(), len2, len3];
unsafe {
let array = Self::new(py, dims, false);
let mut data_ptr = array.data();
for v in v {
if v.len() != len2 {
cold();
return Err(FromVecError::new(v.len(), len2));
}
for v in v {
if v.len() != len3 {
cold();
return Err(FromVecError::new(v.len(), len3));
}
clone_elements(py, v, &mut data_ptr);
}
}
Ok(array)
}
}
}
impl<T: Element + AsPrimitive<f64>> PyArray<T, Ix1> {
pub fn arange<'py>(py: Python<'py>, start: T, stop: T, step: T) -> Bound<'py, Self> {
unsafe {
let ptr = PY_ARRAY_API.PyArray_Arange(
py,
start.as_(),
stop.as_(),
step.as_(),
T::get_dtype(py).num(),
);
Bound::from_owned_ptr(py, ptr).cast_into_unchecked()
}
}
}
unsafe fn clone_elements<T: Element>(py: Python<'_>, elems: &[T], data_ptr: &mut *mut T) {
if T::IS_COPY {
ptr::copy_nonoverlapping(elems.as_ptr(), *data_ptr, elems.len());
*data_ptr = data_ptr.add(elems.len());
} else {
for elem in elems {
data_ptr.write(elem.clone_ref(py));
*data_ptr = data_ptr.add(1);
}
}
}
#[doc(alias = "PyArray")]
pub trait PyArrayMethods<'py, T, D>: PyUntypedArrayMethods<'py> {
fn as_untyped(&self) -> &Bound<'py, PyUntypedArray>;
fn data(&self) -> *mut T;
#[inline(always)]
fn dims(&self) -> D
where
D: Dimension,
{
D::from_dimension(&Dim(self.shape())).expect(DIMENSIONALITY_MISMATCH_ERR)
}
unsafe fn as_slice(&self) -> Result<&[T], NotContiguousError>
where
T: Element,
D: Dimension,
{
if self.is_contiguous() {
Ok(slice::from_raw_parts(self.data(), self.len()))
} else {
Err(NotContiguousError)
}
}
#[allow(clippy::mut_from_ref)]
unsafe fn as_slice_mut(&self) -> Result<&mut [T], NotContiguousError>
where
T: Element,
D: Dimension,
{
if self.is_contiguous() {
Ok(slice::from_raw_parts_mut(self.data(), self.len()))
} else {
Err(NotContiguousError)
}
}
unsafe fn get(&self, index: impl NpyIndex<Dim = D>) -> Option<&T>
where
T: Element,
D: Dimension;
#[allow(clippy::mut_from_ref)]
unsafe fn get_mut(&self, index: impl NpyIndex<Dim = D>) -> Option<&mut T>
where
T: Element,
D: Dimension;
#[inline(always)]
unsafe fn uget<Idx>(&self, index: Idx) -> &T
where
T: Element,
D: Dimension,
Idx: NpyIndex<Dim = D>,
{
&*self.uget_raw(index)
}
#[inline(always)]
#[allow(clippy::mut_from_ref)]
unsafe fn uget_mut<Idx>(&self, index: Idx) -> &mut T
where
T: Element,
D: Dimension,
Idx: NpyIndex<Dim = D>,
{
&mut *self.uget_raw(index)
}
#[inline(always)]
unsafe fn uget_raw<Idx>(&self, index: Idx) -> *mut T
where
T: Element,
D: Dimension,
Idx: NpyIndex<Dim = D>,
{
let offset = index.get_unchecked::<T>(self.strides());
self.data().offset(offset) as *mut _
}
fn get_owned<Idx>(&self, index: Idx) -> Option<T>
where
T: Element,
D: Dimension,
Idx: NpyIndex<Dim = D>;
fn to_dyn(&self) -> &Bound<'py, PyArray<T, IxDyn>>
where
T: Element,
D: Dimension;
fn to_vec(&self) -> Result<Vec<T>, NotContiguousError>
where
T: Element,
D: Dimension;
fn try_readonly(&self) -> Result<PyReadonlyArray<'py, T, D>, BorrowError>
where
T: Element,
D: Dimension;
fn readonly(&self) -> PyReadonlyArray<'py, T, D>
where
T: Element,
D: Dimension,
{
self.try_readonly().expect("Operation failed")
}
fn try_readwrite(&self) -> Result<PyReadwriteArray<'py, T, D>, BorrowError>
where
T: Element,
D: Dimension;
fn readwrite(&self) -> PyReadwriteArray<'py, T, D>
where
T: Element,
D: Dimension,
{
self.try_readwrite().expect("Operation failed")
}
unsafe fn as_array(&self) -> ArrayView<'_, T, D>
where
T: Element,
D: Dimension;
unsafe fn as_array_mut(&self) -> ArrayViewMut<'_, T, D>
where
T: Element,
D: Dimension;
fn as_raw_array(&self) -> RawArrayView<T, D>
where
T: Element,
D: Dimension;
fn as_raw_array_mut(&self) -> RawArrayViewMut<T, D>
where
T: Element,
D: Dimension;
fn to_owned_array(&self) -> Array<T, D>
where
T: Element,
D: Dimension;
fn copy_to<U: Element>(&self, other: &Bound<'py, PyArray<U, D>>) -> PyResult<()>
where
T: Element;
#[deprecated(since = "0.26.0", note = "use `cast_array` instead")]
#[inline]
fn cast<U: Element>(&self, is_fortran: bool) -> PyResult<Bound<'py, PyArray<U, D>>>
where
T: Element,
{
self.cast_array(is_fortran)
}
fn cast_array<U: Element>(&self, is_fortran: bool) -> PyResult<Bound<'py, PyArray<U, D>>>
where
T: Element;
fn permute<ID: IntoDimension>(&self, axes: Option<ID>) -> PyResult<Bound<'py, PyArray<T, D>>>
where
T: Element;
fn transpose(&self) -> PyResult<Bound<'py, PyArray<T, D>>>
where
T: Element,
{
self.permute::<()>(None)
}
fn reshape_with_order<ID: IntoDimension>(
&self,
shape: ID,
order: NPY_ORDER,
) -> PyResult<Bound<'py, PyArray<T, ID::Dim>>>
where
T: Element;
#[inline(always)]
fn reshape<ID: IntoDimension>(&self, shape: ID) -> PyResult<Bound<'py, PyArray<T, ID::Dim>>>
where
T: Element,
{
self.reshape_with_order(shape, NPY_ORDER::NPY_ANYORDER)
}
unsafe fn resize<ID: IntoDimension>(&self, newshape: ID) -> PyResult<()>
where
T: Element;
#[doc(alias = "nalgebra")]
#[cfg(feature = "nalgebra")]
unsafe fn try_as_matrix<R, C, RStride, CStride>(
&self,
) -> Option<nalgebra::MatrixView<'_, T, R, C, RStride, CStride>>
where
T: nalgebra::Scalar + Element,
D: Dimension,
R: nalgebra::Dim,
C: nalgebra::Dim,
RStride: nalgebra::Dim,
CStride: nalgebra::Dim;
#[doc(alias = "nalgebra")]
#[cfg(feature = "nalgebra")]
unsafe fn try_as_matrix_mut<R, C, RStride, CStride>(
&self,
) -> Option<nalgebra::MatrixViewMut<'_, T, R, C, RStride, CStride>>
where
T: nalgebra::Scalar + Element,
D: Dimension,
R: nalgebra::Dim,
C: nalgebra::Dim,
RStride: nalgebra::Dim,
CStride: nalgebra::Dim;
}
#[doc(alias = "PyArray", alias = "PyArray0")]
pub trait PyArray0Methods<'py, T>: PyArrayMethods<'py, T, Ix0> {
fn item(&self) -> T
where
T: Element + Copy,
{
unsafe { *self.data() }
}
}
#[inline(always)]
fn get_raw<T, D, Idx>(slf: &Bound<'_, PyArray<T, D>>, index: Idx) -> Option<*mut T>
where
T: Element,
D: Dimension,
Idx: NpyIndex<Dim = D>,
{
let offset = index.get_checked::<T>(slf.shape(), slf.strides())?;
Some(unsafe { slf.data().offset(offset) })
}
fn as_view<T, D, S, F>(slf: &Bound<'_, PyArray<T, D>>, from_shape_ptr: F) -> ArrayBase<S, D>
where
T: Element,
D: Dimension,
S: RawData,
F: FnOnce(StrideShape<D>, *mut T) -> ArrayBase<S, D>,
{
fn inner<D: Dimension>(
shape: &[usize],
strides: &[isize],
itemsize: usize,
mut data_ptr: *mut u8,
) -> (StrideShape<D>, u32, *mut u8) {
let shape = D::from_dimension(&Dim(shape)).expect(DIMENSIONALITY_MISMATCH_ERR);
assert!(strides.len() <= 32, "{}", MAX_DIMENSIONALITY_ERR);
let mut new_strides = D::zeros(strides.len());
let mut inverted_axes = 0_u32;
for i in 0..strides.len() {
if strides[i] >= 0 {
new_strides[i] = strides[i] as usize / itemsize;
} else {
data_ptr = unsafe { data_ptr.offset(strides[i] * (shape[i] as isize - 1)) };
new_strides[i] = (-strides[i]) as usize / itemsize;
inverted_axes |= 1 << i;
}
}
(shape.strides(new_strides), inverted_axes, data_ptr)
}
let (shape, mut inverted_axes, data_ptr) = inner(
slf.shape(),
slf.strides(),
mem::size_of::<T>(),
slf.data() as _,
);
let mut array = from_shape_ptr(shape, data_ptr as _);
while inverted_axes != 0 {
let axis = inverted_axes.trailing_zeros() as usize;
inverted_axes &= !(1 << axis);
array.invert_axis(Axis(axis));
}
array
}
#[cfg(feature = "nalgebra")]
fn try_as_matrix_shape_strides<N, D, R, C, RStride, CStride>(
slf: &Bound<'_, PyArray<N, D>>,
) -> Option<((R, C), (RStride, CStride))>
where
N: nalgebra::Scalar + Element,
D: Dimension,
R: nalgebra::Dim,
C: nalgebra::Dim,
RStride: nalgebra::Dim,
CStride: nalgebra::Dim,
{
let ndim = slf.ndim();
let shape = slf.shape();
let strides = slf.strides();
if ndim != 1 && ndim != 2 {
return None;
}
if strides.iter().any(|strides| *strides < 0) {
return None;
}
let rows = shape[0];
let cols = *shape.get(1).unwrap_or(&1);
if R::try_to_usize().map(|expected| rows == expected) == Some(false) {
return None;
}
if C::try_to_usize().map(|expected| cols == expected) == Some(false) {
return None;
}
let row_stride = strides[0] as usize / mem::size_of::<N>();
let col_stride = strides
.get(1)
.map_or(rows, |stride| *stride as usize / mem::size_of::<N>());
if RStride::try_to_usize().map(|expected| row_stride == expected) == Some(false) {
return None;
}
if CStride::try_to_usize().map(|expected| col_stride == expected) == Some(false) {
return None;
}
let shape = (R::from_usize(rows), C::from_usize(cols));
let strides = (
RStride::from_usize(row_stride),
CStride::from_usize(col_stride),
);
Some((shape, strides))
}
impl<'py, T, D> PyArrayMethods<'py, T, D> for Bound<'py, PyArray<T, D>> {
#[inline(always)]
fn as_untyped(&self) -> &Bound<'py, PyUntypedArray> {
unsafe { self.cast_unchecked() }
}
#[inline(always)]
fn data(&self) -> *mut T {
unsafe { (*self.as_array_ptr()).data.cast() }
}
#[inline(always)]
unsafe fn get(&self, index: impl NpyIndex<Dim = D>) -> Option<&T>
where
T: Element,
D: Dimension,
{
let ptr = get_raw(self, index)?;
Some(&*ptr)
}
#[inline(always)]
unsafe fn get_mut(&self, index: impl NpyIndex<Dim = D>) -> Option<&mut T>
where
T: Element,
D: Dimension,
{
let ptr = get_raw(self, index)?;
Some(&mut *ptr)
}
fn get_owned<Idx>(&self, index: Idx) -> Option<T>
where
T: Element,
D: Dimension,
Idx: NpyIndex<Dim = D>,
{
let element = unsafe { self.get(index) };
element.map(|elem| elem.clone_ref(self.py()))
}
fn to_dyn(&self) -> &Bound<'py, PyArray<T, IxDyn>> {
unsafe { self.cast_unchecked() }
}
fn to_vec(&self) -> Result<Vec<T>, NotContiguousError>
where
T: Element,
D: Dimension,
{
let slice = unsafe { self.as_slice() };
slice.map(|slc| T::vec_from_slice(self.py(), slc))
}
fn try_readonly(&self) -> Result<PyReadonlyArray<'py, T, D>, BorrowError>
where
T: Element,
D: Dimension,
{
PyReadonlyArray::try_new(self.clone())
}
fn try_readwrite(&self) -> Result<PyReadwriteArray<'py, T, D>, BorrowError>
where
T: Element,
D: Dimension,
{
PyReadwriteArray::try_new(self.clone())
}
unsafe fn as_array(&self) -> ArrayView<'_, T, D>
where
T: Element,
D: Dimension,
{
as_view(self, |shape, ptr| ArrayView::from_shape_ptr(shape, ptr))
}
unsafe fn as_array_mut(&self) -> ArrayViewMut<'_, T, D>
where
T: Element,
D: Dimension,
{
as_view(self, |shape, ptr| ArrayViewMut::from_shape_ptr(shape, ptr))
}
fn as_raw_array(&self) -> RawArrayView<T, D>
where
T: Element,
D: Dimension,
{
as_view(self, |shape, ptr| unsafe {
RawArrayView::from_shape_ptr(shape, ptr)
})
}
fn as_raw_array_mut(&self) -> RawArrayViewMut<T, D>
where
T: Element,
D: Dimension,
{
as_view(self, |shape, ptr| unsafe {
RawArrayViewMut::from_shape_ptr(shape, ptr)
})
}
fn to_owned_array(&self) -> Array<T, D>
where
T: Element,
D: Dimension,
{
let view = unsafe { self.as_array() };
T::array_from_view(self.py(), view)
}
fn copy_to<U: Element>(&self, other: &Bound<'py, PyArray<U, D>>) -> PyResult<()>
where
T: Element,
{
let self_ptr = self.as_array_ptr();
let other_ptr = other.as_array_ptr();
let result = unsafe { PY_ARRAY_API.PyArray_CopyInto(self.py(), other_ptr, self_ptr) };
if result != -1 {
Ok(())
} else {
Err(PyErr::fetch(self.py()))
}
}
fn cast_array<U: Element>(&self, is_fortran: bool) -> PyResult<Bound<'py, PyArray<U, D>>>
where
T: Element,
{
let ptr = unsafe {
PY_ARRAY_API.PyArray_CastToType(
self.py(),
self.as_array_ptr(),
U::get_dtype(self.py()).into_dtype_ptr(),
if is_fortran { -1 } else { 0 },
)
};
unsafe { Bound::from_owned_ptr_or_err(self.py(), ptr).map(|ob| ob.cast_into_unchecked()) }
}
fn permute<ID: IntoDimension>(&self, axes: Option<ID>) -> PyResult<Bound<'py, PyArray<T, D>>> {
let mut axes = axes.map(|axes| axes.into_dimension());
let mut axes = axes.as_mut().map(|axes| axes.to_npy_dims());
let axes = axes
.as_mut()
.map_or_else(ptr::null_mut, |axes| axes as *mut npyffi::PyArray_Dims);
let py = self.py();
let ptr = unsafe { PY_ARRAY_API.PyArray_Transpose(py, self.as_array_ptr(), axes) };
unsafe { Bound::from_owned_ptr_or_err(py, ptr).map(|ob| ob.cast_into_unchecked()) }
}
fn reshape_with_order<ID: IntoDimension>(
&self,
shape: ID,
order: NPY_ORDER,
) -> PyResult<Bound<'py, PyArray<T, ID::Dim>>>
where
T: Element,
{
let mut shape = shape.into_dimension();
let mut shape = shape.to_npy_dims();
let py = self.py();
let ptr = unsafe {
PY_ARRAY_API.PyArray_Newshape(
py,
self.as_array_ptr(),
&mut shape as *mut npyffi::PyArray_Dims,
order,
)
};
unsafe { Bound::from_owned_ptr_or_err(py, ptr).map(|ob| ob.cast_into_unchecked()) }
}
unsafe fn resize<ID: IntoDimension>(&self, newshape: ID) -> PyResult<()>
where
T: Element,
{
let mut newshape = newshape.into_dimension();
let mut newshape = newshape.to_npy_dims();
let py = self.py();
let res = PY_ARRAY_API.PyArray_Resize(
py,
self.as_array_ptr(),
&mut newshape as *mut npyffi::PyArray_Dims,
1,
NPY_ORDER::NPY_ANYORDER,
);
if !res.is_null() {
Ok(())
} else {
Err(PyErr::fetch(py))
}
}
#[cfg(feature = "nalgebra")]
unsafe fn try_as_matrix<R, C, RStride, CStride>(
&self,
) -> Option<nalgebra::MatrixView<'_, T, R, C, RStride, CStride>>
where
T: nalgebra::Scalar + Element,
D: Dimension,
R: nalgebra::Dim,
C: nalgebra::Dim,
RStride: nalgebra::Dim,
CStride: nalgebra::Dim,
{
let (shape, strides) = try_as_matrix_shape_strides(self)?;
let storage = nalgebra::ViewStorage::from_raw_parts(self.data(), shape, strides);
Some(nalgebra::Matrix::from_data(storage))
}
#[cfg(feature = "nalgebra")]
unsafe fn try_as_matrix_mut<R, C, RStride, CStride>(
&self,
) -> Option<nalgebra::MatrixViewMut<'_, T, R, C, RStride, CStride>>
where
T: nalgebra::Scalar + Element,
D: Dimension,
R: nalgebra::Dim,
C: nalgebra::Dim,
RStride: nalgebra::Dim,
CStride: nalgebra::Dim,
{
let (shape, strides) = try_as_matrix_shape_strides(self)?;
let storage = nalgebra::ViewStorageMut::from_raw_parts(self.data(), shape, strides);
Some(nalgebra::Matrix::from_data(storage))
}
}
impl<'py, T> PyArray0Methods<'py, T> for Bound<'py, PyArray0<T>> {}
#[cfg(test)]
mod tests {
use super::*;
use ndarray::array;
use pyo3::{py_run, types::PyList};
#[test]
fn test_dyn_to_owned_array() {
Python::attach(|py| {
let array = PyArray::from_vec2(py, &[vec![1, 2], vec![3, 4]])
.expect("Operation failed")
.to_dyn()
.to_owned_array();
assert_eq!(array, array![[1, 2], [3, 4]].into_dyn());
});
}
#[test]
fn test_hasobject_flag() {
Python::attach(|py| {
let array: Bound<'_, PyArray<Py<PyAny>, _>> =
PyArray1::from_slice(py, &[PyList::empty(py).into()]);
py_run!(py, array, "assert array.dtype.hasobject");
});
}
}