Skip to content

Commit 2c99dfa

Browse files
authored
Merge pull request #312 from PyO3/n-dimensional-resize
Enable resizing n-dimensional arrays in addition one-dimensional ones.
2 parents 1caf511 + a31367d commit 2c99dfa

File tree

3 files changed

+88
-86
lines changed

3 files changed

+88
-86
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- The destructive `PyArray::resize` method is now unsafe if used without an instance of `PyReadwriteArray`. ([#302](https://github.com/PyO3/rust-numpy/pull/302))
77
- Add support for IEEE 754-2008 16-bit floating point numbers via an optional dependency on the `half` crate. ([#314](https://github.com/PyO3/rust-numpy/pull/314))
88
- The `inner`, `dot` and `einsum` functions can also return a scalar instead of a zero-dimensional array to match NumPy's types ([#285](https://github.com/PyO3/rust-numpy/pull/285))
9+
- The `PyArray::resize` function supports n-dimensional contiguous arrays. ([#312](https://github.com/PyO3/rust-numpy/pull/312))
910
- Deprecate `PyArray::from_exact_iter` after optimizing `PyArray::from_iter`. ([#292](https://github.com/PyO3/rust-numpy/pull/292))
1011
- Fix returning invalid slices from `PyArray::{strides,shape}` for rank zero arrays. ([#303](https://github.com/PyO3/rust-numpy/pull/303))
1112

src/array.rs

Lines changed: 79 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,58 +1074,6 @@ impl<T: Element> PyArray<T, Ix1> {
10741074
let data = iter.into_iter().collect::<Vec<_>>();
10751075
data.into_pyarray(py)
10761076
}
1077-
1078-
/// Extends or truncates the length of a one-dimensional array.
1079-
///
1080-
/// # Safety
1081-
///
1082-
/// There should be no outstanding references (shared or exclusive) into the array
1083-
/// as this method might re-allocate it and thereby invalidate all pointers into it.
1084-
///
1085-
/// # Example
1086-
///
1087-
/// ```
1088-
/// use numpy::PyArray;
1089-
/// use pyo3::Python;
1090-
///
1091-
/// Python::with_gil(|py| {
1092-
/// let pyarray = PyArray::arange(py, 0, 10, 1);
1093-
/// assert_eq!(pyarray.len(), 10);
1094-
///
1095-
/// unsafe {
1096-
/// pyarray.resize(100).unwrap();
1097-
/// }
1098-
/// assert_eq!(pyarray.len(), 100);
1099-
/// });
1100-
/// ```
1101-
pub unsafe fn resize(&self, new_elems: usize) -> PyResult<()> {
1102-
self.resize_(self.py(), [new_elems], 1, NPY_ORDER::NPY_ANYORDER)
1103-
}
1104-
1105-
fn resize_<D: IntoDimension>(
1106-
&self,
1107-
py: Python,
1108-
dims: D,
1109-
check_ref: c_int,
1110-
order: NPY_ORDER,
1111-
) -> PyResult<()> {
1112-
let dims = dims.into_dimension();
1113-
let mut np_dims = dims.to_npy_dims();
1114-
let res = unsafe {
1115-
PY_ARRAY_API.PyArray_Resize(
1116-
py,
1117-
self.as_array_ptr(),
1118-
&mut np_dims as *mut npyffi::PyArray_Dims,
1119-
check_ref,
1120-
order,
1121-
)
1122-
};
1123-
if res.is_null() {
1124-
Err(PyErr::fetch(self.py()))
1125-
} else {
1126-
Ok(())
1127-
}
1128-
}
11291077
}
11301078

11311079
impl<T: Element> PyArray<T, Ix2> {
@@ -1260,57 +1208,106 @@ impl<T: Element, D> PyArray<T, D> {
12601208
}
12611209
}
12621210

1263-
/// Construct a new array which has same values as self, same matrix order, but has different
1264-
/// dimensions specified by `dims`.
1211+
/// Construct a new array which has same values as self,
1212+
/// but has different dimensions specified by `dims`
1213+
/// and a possibly different memory order specified by `order`.
12651214
///
1266-
/// Since a returned array can contain a same pointer as self, we highly recommend to drop an
1267-
/// old array, if this method returns `Ok`.
1215+
/// See also [`numpy.reshape`][numpy-reshape] and [`PyArray_Newshape`][PyArray_Newshape].
12681216
///
12691217
/// # Example
12701218
///
12711219
/// ```
1272-
/// # #[macro_use] extern crate ndarray;
1273-
/// use numpy::PyArray;
1274-
/// pyo3::Python::with_gil(|py| {
1275-
/// let array = PyArray::from_exact_iter(py, 0..9);
1276-
/// let array = array.reshape([3, 3]).unwrap();
1277-
/// assert_eq!(array.readonly().as_array(), array![[0, 1, 2], [3, 4, 5], [6, 7, 8]]);
1220+
/// use numpy::{npyffi::NPY_ORDER, PyArray};
1221+
/// use pyo3::Python;
1222+
/// use ndarray::array;
1223+
///
1224+
/// Python::with_gil(|py| {
1225+
/// let array =
1226+
/// PyArray::from_iter(py, 0..9).reshape_with_order([3, 3], NPY_ORDER::NPY_FORTRANORDER).unwrap();
1227+
///
1228+
/// assert_eq!(array.readonly().as_array(), array![[0, 3, 6], [1, 4, 7], [2, 5, 8]]);
1229+
/// assert!(array.is_fortran_contiguous());
1230+
///
12781231
/// assert!(array.reshape([5]).is_err());
12791232
/// });
12801233
/// ```
1281-
#[inline(always)]
1282-
pub fn reshape<'py, ID, D2>(&'py self, dims: ID) -> PyResult<&'py PyArray<T, D2>>
1283-
where
1284-
ID: IntoDimension<Dim = D2>,
1285-
D2: Dimension,
1286-
{
1287-
self.reshape_with_order(dims, NPY_ORDER::NPY_ANYORDER)
1288-
}
1289-
1290-
/// Same as [reshape](method.reshape.html), but you can change the order of returned matrix.
1291-
pub fn reshape_with_order<'py, ID, D2>(
1234+
///
1235+
/// [numpy-reshape]: https://numpy.org/doc/stable/reference/generated/numpy.reshape.html
1236+
/// [PyArray_Newshape]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_Newshape
1237+
pub fn reshape_with_order<'py, ID: IntoDimension>(
12921238
&'py self,
12931239
dims: ID,
12941240
order: NPY_ORDER,
1295-
) -> PyResult<&'py PyArray<T, D2>>
1296-
where
1297-
ID: IntoDimension<Dim = D2>,
1298-
D2: Dimension,
1299-
{
1300-
let dims = dims.into_dimension();
1301-
let mut np_dims = dims.to_npy_dims();
1241+
) -> PyResult<&'py PyArray<T, ID::Dim>> {
1242+
let mut dims = dims.into_dimension().to_npy_dims();
13021243
let ptr = unsafe {
13031244
PY_ARRAY_API.PyArray_Newshape(
13041245
self.py(),
13051246
self.as_array_ptr(),
1306-
&mut np_dims as *mut npyffi::PyArray_Dims,
1247+
&mut dims as *mut npyffi::PyArray_Dims,
13071248
order,
13081249
)
13091250
};
13101251
if ptr.is_null() {
13111252
Err(PyErr::fetch(self.py()))
13121253
} else {
1313-
Ok(unsafe { PyArray::<T, D2>::from_owned_ptr(self.py(), ptr) })
1254+
Ok(unsafe { PyArray::<T, ID::Dim>::from_owned_ptr(self.py(), ptr) })
1255+
}
1256+
}
1257+
1258+
/// Special case of [`reshape_with_order`][Self::reshape_with_order] which keeps the memory order the same.
1259+
#[inline(always)]
1260+
pub fn reshape<'py, ID: IntoDimension>(
1261+
&'py self,
1262+
dims: ID,
1263+
) -> PyResult<&'py PyArray<T, ID::Dim>> {
1264+
self.reshape_with_order(dims, NPY_ORDER::NPY_ANYORDER)
1265+
}
1266+
1267+
/// Extends or truncates the dimensions of an array.
1268+
///
1269+
/// This method works only on [contiguous][`Self::is_contiguous`] arrays.
1270+
/// Missing elements will be initialized as if calling [`zeros`][Self::zeros].
1271+
///
1272+
/// See also [`ndarray.resize`][ndarray-resize] and [`PyArray_Resize`][PyArray_Resize].
1273+
///
1274+
/// # Safety
1275+
///
1276+
/// There should be no outstanding references (shared or exclusive) into the array
1277+
/// as this method might re-allocate it and thereby invalidate all pointers into it.
1278+
///
1279+
/// # Example
1280+
///
1281+
/// ```
1282+
/// use numpy::PyArray;
1283+
/// use pyo3::Python;
1284+
///
1285+
/// Python::with_gil(|py| {
1286+
/// let pyarray = PyArray::<f64, _>::zeros(py, (10, 10), false);
1287+
/// assert_eq!(pyarray.shape(), [10, 10]);
1288+
///
1289+
/// unsafe {
1290+
/// pyarray.resize((100, 100)).unwrap();
1291+
/// }
1292+
/// assert_eq!(pyarray.shape(), [100, 100]);
1293+
/// });
1294+
/// ```
1295+
///
1296+
/// [ndarray-resize]: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.resize.html
1297+
/// [PyArray_Resize]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_Resize
1298+
pub unsafe fn resize<ID: IntoDimension>(&self, dims: ID) -> PyResult<()> {
1299+
let mut dims = dims.into_dimension().to_npy_dims();
1300+
let res = PY_ARRAY_API.PyArray_Resize(
1301+
self.py(),
1302+
self.as_array_ptr(),
1303+
&mut dims as *mut npyffi::PyArray_Dims,
1304+
1,
1305+
NPY_ORDER::NPY_ANYORDER,
1306+
);
1307+
if res.is_null() {
1308+
Err(PyErr::fetch(self.py()))
1309+
} else {
1310+
Ok(())
13141311
}
13151312
}
13161313
}

src/borrow.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,9 @@ use std::mem::size_of;
168168
use std::ops::Deref;
169169

170170
use ahash::AHashMap;
171-
use ndarray::{ArrayView, ArrayViewMut, Dimension, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn};
171+
use ndarray::{
172+
ArrayView, ArrayViewMut, Dimension, IntoDimension, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn,
173+
};
172174
use num_integer::gcd;
173175
use pyo3::{FromPyObject, PyAny, PyResult, Python};
174176

@@ -619,7 +621,9 @@ impl<'py, T> PyReadwriteArray<'py, T, Ix1>
619621
where
620622
T: Element,
621623
{
622-
/// Extends or truncates the length of a one-dimensional array.
624+
/// Extends or truncates the dimensions of an array.
625+
///
626+
/// Safe wrapper for [`PyArray::resize`].
623627
///
624628
/// # Example
625629
///
@@ -636,12 +640,12 @@ where
636640
/// assert_eq!(pyarray.len(), 100);
637641
/// });
638642
/// ```
639-
pub fn resize(self, new_elems: usize) -> PyResult<Self> {
643+
pub fn resize<ID: IntoDimension>(self, dims: ID) -> PyResult<Self> {
640644
let array = self.array;
641645

642646
// SAFETY: Ownership of `self` proves exclusive access to the interior of the array.
643647
unsafe {
644-
array.resize(new_elems)?;
648+
array.resize(dims)?;
645649
}
646650

647651
drop(self);

0 commit comments

Comments
 (0)