Skip to content

Slim down Element, various dtype-related improvements and changes #256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Jan 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
82e641b
Use `PyArray_NewFromDescr`, remove `npy_type()`
aldanor Jan 9, 2022
d1ca21a
Add `PyArrayDescr::into_dtype_ptr()`
aldanor Jan 9, 2022
2067f83
Add `PyArrayDescr::of<T>()`
aldanor Jan 9, 2022
a2ba3fb
Add tests for dtype names (currently failing)
aldanor Jan 9, 2022
e9cc2a6
Rework dtype integer type mapping to match numpy
aldanor Jan 9, 2022
5f1b3b6
Clean up typenums, add `DataType::into_typenum()`
aldanor Jan 9, 2022
612e867
Add `PyArrayDescr::is_equiv_to()`
aldanor Jan 9, 2022
5d55c70
Use `is_equiv_to()` in `FromPyObject::extract()`
aldanor Jan 9, 2022
fdc389e
Remove `Element::is_same_type()`
aldanor Jan 9, 2022
cf25192
`ShapeError` -> `DimensionalityError`/`TypeError`
aldanor Jan 9, 2022
c454a5e
Replace `Element::DATA_TYPE` with `IS_COPY`
aldanor Jan 9, 2022
6688d79
Implement `Element` for `isize`
aldanor Jan 9, 2022
8e7c583
Add a test verifying that `hasobject` flag is set
aldanor Jan 10, 2022
147775d
Rename `c*` aliases to `Complex*`, add docstrings
aldanor Jan 10, 2022
fee3283
Map scalar types directly, bypassing `DataType`
aldanor Jan 10, 2022
2e2b576
Goodbye `DataType` (and update the tests)
aldanor Jan 10, 2022
61ce229
Update the docstring for `Element`
aldanor Jan 10, 2022
f08d6e0
Remove `cfg_if` dependency, clean up dtype macro
aldanor Jan 10, 2022
c3d0a53
Add `dtype()` top-level function for convenience
aldanor Jan 10, 2022
fee612d
(Use `cfg` instead of `size_of::<usize>` in tests)
aldanor Jan 10, 2022
4021797
Update the changelog (descriptors rework)
aldanor Jan 10, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@
- Support borrowing arrays that are part of other Python objects via `PyArray::borrow_from_array` ([#230](https://github.com/PyO3/rust-numpy/pull/216))
- `PyArray::new` is now `unsafe`, as it produces uninitialized arrays ([#220](https://github.com/PyO3/rust-numpy/pull/220))
- `rayon` feature is now removed, and directly specifying the feature via `ndarray` dependency is recommended ([#250](https://github.com/PyO3/rust-numpy/pull/250))
- Descriptors rework and related changes ([#256](https://github.com/PyO3/rust-numpy/pull/256)):
- Remove `DataType`
- Add the top-level `dtype` function for easy access to registered dtypes
- Add `PyArrayDescr::of`, `PyArrayDescr::into_dtype_ptr` and `PyArrayDescr::is_equiv_to`
- `Element` trait has been simplified to just `IS_COPY` const and `get_dtype` method
- `Element` is now implemented for `isize`
- `c32` and `c64` aliases have been replaced with `Complex32` and `Complex64`
- `ShapeError` has been split into `TypeError` and `DimensionalityError`
- `i32`, `i64`, `u32` and `u64` are now guaranteed to map to
`np.int32`, `np.int64`, `np.uint32` and `np.uint64` respectively
- Remove `cfg_if` dependency

- v0.15.1
- Make arrays produced via `IntoPyArray`, i.e. those owning Rust data, writeable ([#235](https://github.com/PyO3/rust-numpy/pull/235))
Expand Down
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ keywords = ["python", "numpy", "ffi", "pyo3"]
license = "BSD-2-Clause"

[dependencies]
cfg-if = "1.0"
libc = "0.2"
num-complex = ">= 0.2, <= 0.4"
num-traits = "0.2"
Expand Down
9 changes: 6 additions & 3 deletions examples/simple-extension/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use numpy::ndarray::{ArrayD, ArrayViewD, ArrayViewMutD};
use numpy::{c64, IntoPyArray, PyArrayDyn, PyReadonlyArrayDyn};
use numpy::{Complex64, IntoPyArray, PyArrayDyn, PyReadonlyArrayDyn};
use pyo3::prelude::{pymodule, PyModule, PyResult, Python};

#[pymodule]
Expand All @@ -15,7 +15,7 @@ fn rust_ext(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
}

// complex example
fn conj(x: ArrayViewD<'_, c64>) -> ArrayD<c64> {
fn conj(x: ArrayViewD<'_, Complex64>) -> ArrayD<Complex64> {
x.map(|c| c.conj())
}

Expand Down Expand Up @@ -44,7 +44,10 @@ fn rust_ext(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
// wrapper of `conj`
#[pyfn(m)]
#[pyo3(name = "conj")]
fn conj_py<'py>(py: Python<'py>, x: PyReadonlyArrayDyn<'_, c64>) -> &'py PyArrayDyn<c64> {
fn conj_py<'py>(
py: Python<'py>,
x: PyReadonlyArrayDyn<'_, Complex64>,
) -> &'py PyArrayDyn<Complex64> {
conj(x.as_array()).into_pyarray(py)
}

Expand Down
66 changes: 41 additions & 25 deletions src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use std::{
use std::{iter::ExactSizeIterator, marker::PhantomData};

use crate::convert::{ArrayExt, IntoPyArray, NpyIndex, ToNpyDims, ToPyArray};
use crate::dtype::{DataType, Element};
use crate::error::{FromVecError, NotContiguousError, ShapeError};
use crate::dtype::Element;
use crate::error::{DimensionalityError, FromVecError, NotContiguousError, TypeError};
use crate::slice_container::PySliceContainer;

/// A safe, static-typed interface for
Expand Down Expand Up @@ -136,13 +136,21 @@ impl<'a, T: Element, D: Dimension> FromPyObject<'a> for &'a PyArray<T, D> {
}
&*(ob as *const PyAny as *const PyArray<T, D>)
};
let dtype = array.dtype();
let dim = array.shape().len();
if T::is_same_type(dtype) && D::NDIM.map(|n| n == dim).unwrap_or(true) {
Ok(array)
} else {
Err(ShapeError::new(dtype, dim, T::DATA_TYPE, D::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());
}

let src_ndim = array.shape().len();
if let Some(dst_ndim) = D::NDIM {
if src_ndim != dst_ndim {
return Err(DimensionalityError::new(src_ndim, dst_ndim).into());
}
}

Ok(array)
}
}

Expand All @@ -160,7 +168,7 @@ impl<T, D> PyArray<T, D> {
/// pyo3::Python::with_gil(|py| {
/// let array = numpy::PyArray::from_vec(py, vec![1, 2, 3i32]);
/// let dtype = array.dtype();
/// assert_eq!(dtype.get_datatype().unwrap(), numpy::DataType::Int32);
/// assert!(dtype.is_equiv_to(numpy::dtype::<i32>(py)));
/// });
/// ```
pub fn dtype(&self) -> &crate::PyArrayDescr {
Expand Down Expand Up @@ -428,14 +436,13 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
ID: IntoDimension<Dim = D>,
{
let dims = dims.into_dimension();
let ptr = PY_ARRAY_API.PyArray_New(
let ptr = PY_ARRAY_API.PyArray_NewFromDescr(
PY_ARRAY_API.get_type_object(npyffi::NpyTypes::PyArray_Type),
T::get_dtype(py).into_dtype_ptr(),
dims.ndim_cint(),
dims.as_dims_ptr(),
T::npy_type() as c_int,
strides as *mut npy_intp, // strides
ptr::null_mut(), // data
0, // itemsize
flag, // flag
ptr::null_mut(), // obj
);
Expand All @@ -453,16 +460,15 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
ID: IntoDimension<Dim = D>,
{
let dims = dims.into_dimension();
let ptr = PY_ARRAY_API.PyArray_New(
let ptr = PY_ARRAY_API.PyArray_NewFromDescr(
PY_ARRAY_API.get_type_object(npyffi::NpyTypes::PyArray_Type),
T::get_dtype(py).into_dtype_ptr(),
dims.ndim_cint(),
dims.as_dims_ptr(),
T::npy_type() as c_int,
strides as *mut npy_intp, // strides
data_ptr as *mut c_void, // data
mem::size_of::<T>() as c_int, // itemsize
npyffi::NPY_ARRAY_WRITEABLE, // flag
ptr::null_mut(), // obj
strides as *mut npy_intp, // strides
data_ptr as *mut c_void, // data
npyffi::NPY_ARRAY_WRITEABLE, // flag
ptr::null_mut(), // obj
);

PY_ARRAY_API.PyArray_SetBaseObject(
Expand Down Expand Up @@ -569,11 +575,10 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
{
let dims = dims.into_dimension();
unsafe {
let dtype = T::get_dtype(py);
let ptr = PY_ARRAY_API.PyArray_Zeros(
dims.ndim_cint(),
dims.as_dims_ptr(),
dtype.into_ptr() as _,
T::get_dtype(py).into_dtype_ptr(),
if is_fortran { -1 } else { 0 },
);
Self::from_owned_ptr(py, ptr)
Expand Down Expand Up @@ -847,7 +852,7 @@ impl<T: Element> PyArray<T, Ix1> {
pub fn from_slice<'py>(py: Python<'py>, slice: &[T]) -> &'py Self {
unsafe {
let array = PyArray::new(py, [slice.len()], false);
if T::DATA_TYPE != DataType::Object {
if T::IS_COPY {
array.copy_ptr(slice.as_ptr(), slice.len());
} else {
let data_ptr = array.data();
Expand Down Expand Up @@ -1104,10 +1109,9 @@ impl<T: Element, D> PyArray<T, D> {
/// ```
pub fn cast<'py, U: Element>(&'py self, is_fortran: bool) -> PyResult<&'py PyArray<U, D>> {
let ptr = unsafe {
let dtype = U::get_dtype(self.py());
PY_ARRAY_API.PyArray_CastToType(
self.as_array_ptr(),
dtype.into_ptr() as _,
U::get_dtype(self.py()).into_dtype_ptr(),
if is_fortran { -1 } else { 0 },
)
};
Expand Down Expand Up @@ -1193,7 +1197,7 @@ impl<T: Element + AsPrimitive<f64>> PyArray<T, Ix1> {
start.as_(),
stop.as_(),
step.as_(),
T::npy_type() as i32,
T::get_dtype(py).get_typenum(),
);
Self::from_owned_ptr(py, ptr)
}
Expand Down Expand Up @@ -1221,4 +1225,16 @@ mod tests {
array.to_dyn().to_owned_array();
})
}

#[test]
fn test_hasobject_flag() {
use super::ToPyArray;
use pyo3::{py_run, types::PyList, Py, PyAny};

pyo3::Python::with_gil(|py| {
let a = ndarray::Array2::from_shape_fn((2, 3), |(_i, _j)| PyList::empty(py).into());
let arr: &PyArray<Py<PyAny>, _> = a.to_pyarray(py);
py_run!(py, arr, "assert arr.dtype.hasobject");
});
}
}
4 changes: 2 additions & 2 deletions src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{mem, os::raw::c_int};

use crate::{
npyffi::{self, npy_intp},
DataType, Element, PyArray,
Element, PyArray,
};

/// Conversion trait from some rust types to `PyArray`.
Expand Down Expand Up @@ -123,7 +123,7 @@ where
fn to_pyarray<'py>(&self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
let len = self.len();
match self.order() {
Some(order) if A::DATA_TYPE != DataType::Object => {
Some(order) if A::IS_COPY => {
// if the array is contiguous, copy it by `copy_ptr`.
let strides = self.npy_strides();
unsafe {
Expand Down
Loading