summaryrefslogtreecommitdiff
path: root/numpy/core
diff options
context:
space:
mode:
authorPeter Andreas Entschev <peter@entschev.com>2019-04-25 17:38:47 +0200
committerEric Wieser <wieser.eric@gmail.com>2019-04-25 08:38:47 -0700
commit0c3d18f57c842047ae23383c2eae46a042faef5d (patch)
tree06bc834a1f55061b7e5b17bbd767b6d13cd930d3 /numpy/core
parentd96b446dfe9a2a3e17b01ed13ebf6a459b5605e5 (diff)
downloadnumpy-0c3d18f57c842047ae23383c2eae46a042faef5d.tar.gz
ENH: Add shape to *_like() array creation (#13046)
* ENH: Added shape argument to *_like() array creation functions * ENH: C backend adjustments for shape argument on *_like() * TST: Added test for shape argument in *_like() functions * ENH: Added PyArray_NewLikeArrayWithShape() This change maintains backwards compatibility, rather than passing new arguments to PyArray_NewLikeArray(). * BUG: Fix for PyArray_NewLikeArrayWithShape strides and ndim == 0 Arrays created with new shapes should not take into consideration the original array's stride, and ndim == 0 should not be a case to ignore a new shape, as the caller may request a 0d array. * REL: Updates for C-API, version 1.17.x * Add comments to cversions.txt (new PyArray_NewLikeArrayWithShape function) * Increment C_API_VERSION to 1.17 in setup_common.py * Revert "REL: Updates for C-API, version 1.17.x" This reverts commit 807f512ebeb7797ad374d845e41015948afcc708. * Revert exposing PyArray_NewLikeArrayWithShape on C-API * DOC: fix versionadded for *_like() shape argument * STY: add missing spaces in array initializers * ENH: empty_like raises ValueError This occurs when shape is defined and number of dimensions match but order is 'K'. * TST: test for exception of *_like() functions * DOC: release note for shape argument in *_like() functions * DOC: fix *_like() documentation on raises * BUG: *_like() raises for non-C/F-layout arrays * TST: change *_like() shapes to prevent NPY_RELAXED_STRIDE_DEBUG=1 failure * Move empty_like() exception to C implementation * Update *_like() ValueError documentation * Rearrange stride computation for *_like() if new shape and order='K' * Change handling of order= for *_like() - If order='K' try to keep, otherwise, order='C' is implied - Do not raise ValueError anymore * Fix *_like() tests
Diffstat (limited to 'numpy/core')
-rw-r--r--numpy/core/multiarray.py10
-rw-r--r--numpy/core/numeric.py36
-rw-r--r--numpy/core/src/multiarray/ctors.c51
-rw-r--r--numpy/core/src/multiarray/ctors.h4
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c23
-rw-r--r--numpy/core/tests/test_numeric.py29
6 files changed, 123 insertions, 30 deletions
diff --git a/numpy/core/multiarray.py b/numpy/core/multiarray.py
index 54b3a3e5e..a839ae402 100644
--- a/numpy/core/multiarray.py
+++ b/numpy/core/multiarray.py
@@ -71,9 +71,9 @@ array_function_from_c_func_and_dispatcher = functools.partial(
@array_function_from_c_func_and_dispatcher(_multiarray_umath.empty_like)
-def empty_like(prototype, dtype=None, order=None, subok=None):
+def empty_like(prototype, dtype=None, order=None, subok=None, shape=None):
"""
- empty_like(prototype, dtype=None, order='K', subok=True)
+ empty_like(prototype, dtype=None, order='K', subok=True, shape=None)
Return a new array with the same shape and type as a given array.
@@ -97,6 +97,12 @@ def empty_like(prototype, dtype=None, order=None, subok=None):
If True, then the newly created array will use the sub-class
type of 'a', otherwise it will be a base-class array. Defaults
to True.
+ shape : int or sequence of ints, optional.
+ Overrides the shape of the result. If order='K' and the number of
+ dimensions is unchanged, will try to keep order, otherwise,
+ order='C' is implied.
+
+ .. versionadded:: 1.17.0
Returns
-------
diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py
index 34705efc7..4b59d730d 100644
--- a/numpy/core/numeric.py
+++ b/numpy/core/numeric.py
@@ -90,12 +90,12 @@ class ComplexWarning(RuntimeWarning):
pass
-def _zeros_like_dispatcher(a, dtype=None, order=None, subok=None):
+def _zeros_like_dispatcher(a, dtype=None, order=None, subok=None, shape=None):
return (a,)
@array_function_dispatch(_zeros_like_dispatcher)
-def zeros_like(a, dtype=None, order='K', subok=True):
+def zeros_like(a, dtype=None, order='K', subok=True, shape=None):
"""
Return an array of zeros with the same shape and type as a given array.
@@ -119,6 +119,12 @@ def zeros_like(a, dtype=None, order='K', subok=True):
If True, then the newly created array will use the sub-class
type of 'a', otherwise it will be a base-class array. Defaults
to True.
+ shape : int or sequence of ints, optional.
+ Overrides the shape of the result. If order='K' and the number of
+ dimensions is unchanged, will try to keep order, otherwise,
+ order='C' is implied.
+
+ .. versionadded:: 1.17.0
Returns
-------
@@ -150,7 +156,7 @@ def zeros_like(a, dtype=None, order='K', subok=True):
array([0., 0., 0.])
"""
- res = empty_like(a, dtype=dtype, order=order, subok=subok)
+ res = empty_like(a, dtype=dtype, order=order, subok=subok, shape=shape)
# needed instead of a 0 to get same result as zeros for for string dtypes
z = zeros(1, dtype=res.dtype)
multiarray.copyto(res, z, casting='unsafe')
@@ -210,12 +216,12 @@ def ones(shape, dtype=None, order='C'):
return a
-def _ones_like_dispatcher(a, dtype=None, order=None, subok=None):
+def _ones_like_dispatcher(a, dtype=None, order=None, subok=None, shape=None):
return (a,)
@array_function_dispatch(_ones_like_dispatcher)
-def ones_like(a, dtype=None, order='K', subok=True):
+def ones_like(a, dtype=None, order='K', subok=True, shape=None):
"""
Return an array of ones with the same shape and type as a given array.
@@ -239,6 +245,12 @@ def ones_like(a, dtype=None, order='K', subok=True):
If True, then the newly created array will use the sub-class
type of 'a', otherwise it will be a base-class array. Defaults
to True.
+ shape : int or sequence of ints, optional.
+ Overrides the shape of the result. If order='K' and the number of
+ dimensions is unchanged, will try to keep order, otherwise,
+ order='C' is implied.
+
+ .. versionadded:: 1.17.0
Returns
-------
@@ -270,7 +282,7 @@ def ones_like(a, dtype=None, order='K', subok=True):
array([1., 1., 1.])
"""
- res = empty_like(a, dtype=dtype, order=order, subok=subok)
+ res = empty_like(a, dtype=dtype, order=order, subok=subok, shape=shape)
multiarray.copyto(res, 1, casting='unsafe')
return res
@@ -322,12 +334,12 @@ def full(shape, fill_value, dtype=None, order='C'):
return a
-def _full_like_dispatcher(a, fill_value, dtype=None, order=None, subok=None):
+def _full_like_dispatcher(a, fill_value, dtype=None, order=None, subok=None, shape=None):
return (a,)
@array_function_dispatch(_full_like_dispatcher)
-def full_like(a, fill_value, dtype=None, order='K', subok=True):
+def full_like(a, fill_value, dtype=None, order='K', subok=True, shape=None):
"""
Return a full array with the same shape and type as a given array.
@@ -349,6 +361,12 @@ def full_like(a, fill_value, dtype=None, order='K', subok=True):
If True, then the newly created array will use the sub-class
type of 'a', otherwise it will be a base-class array. Defaults
to True.
+ shape : int or sequence of ints, optional.
+ Overrides the shape of the result. If order='K' and the number of
+ dimensions is unchanged, will try to keep order, otherwise,
+ order='C' is implied.
+
+ .. versionadded:: 1.17.0
Returns
-------
@@ -379,7 +397,7 @@ def full_like(a, fill_value, dtype=None, order='K', subok=True):
array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1])
"""
- res = empty_like(a, dtype=dtype, order=order, subok=subok)
+ res = empty_like(a, dtype=dtype, order=order, subok=subok, shape=shape)
multiarray.copyto(res, fill_value, casting='unsafe')
return res
diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c
index 42cd31069..546eb2a48 100644
--- a/numpy/core/src/multiarray/ctors.c
+++ b/numpy/core/src/multiarray/ctors.c
@@ -1183,9 +1183,9 @@ PyArray_NewFromDescrAndBase(
flags, obj, base, 0, 0);
}
-/*NUMPY_API
+/*
* Creates a new array with the same shape as the provided one,
- * with possible memory layout order and data type changes.
+ * with possible memory layout order, data type and shape changes.
*
* prototype - The array the new one should be like.
* order - NPY_CORDER - C-contiguous result.
@@ -1193,6 +1193,8 @@ PyArray_NewFromDescrAndBase(
* NPY_ANYORDER - Fortran if prototype is Fortran, C otherwise.
* NPY_KEEPORDER - Keeps the axis ordering of prototype.
* dtype - If not NULL, overrides the data type of the result.
+ * ndim - If not 0 and dims not NULL, overrides the shape of the result.
+ * dims - If not NULL and ndim not 0, overrides the shape of the result.
* subok - If 1, use the prototype's array subtype, otherwise
* always create a base-class array.
*
@@ -1200,11 +1202,18 @@ PyArray_NewFromDescrAndBase(
* dtype->subarray is true, dtype will be decrefed.
*/
NPY_NO_EXPORT PyObject *
-PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order,
- PyArray_Descr *dtype, int subok)
+PyArray_NewLikeArrayWithShape(PyArrayObject *prototype, NPY_ORDER order,
+ PyArray_Descr *dtype, int ndim, npy_intp *dims, int subok)
{
PyObject *ret = NULL;
- int ndim = PyArray_NDIM(prototype);
+
+ if (dims == NULL) {
+ ndim = PyArray_NDIM(prototype);
+ dims = PyArray_DIMS(prototype);
+ }
+ else if (order == NPY_KEEPORDER && (ndim != PyArray_NDIM(prototype))) {
+ order = NPY_CORDER;
+ }
/* If no override data type, use the one from the prototype */
if (dtype == NULL) {
@@ -1237,7 +1246,7 @@ PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order,
ret = PyArray_NewFromDescr(subok ? Py_TYPE(prototype) : &PyArray_Type,
dtype,
ndim,
- PyArray_DIMS(prototype),
+ dims,
NULL,
NULL,
order,
@@ -1246,11 +1255,10 @@ PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order,
/* KEEPORDER needs some analysis of the strides */
else {
npy_intp strides[NPY_MAXDIMS], stride;
- npy_intp *shape = PyArray_DIMS(prototype);
npy_stride_sort_item strideperm[NPY_MAXDIMS];
int idim;
- PyArray_CreateSortedStridePerm(PyArray_NDIM(prototype),
+ PyArray_CreateSortedStridePerm(ndim,
PyArray_STRIDES(prototype),
strideperm);
@@ -1259,14 +1267,14 @@ PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order,
for (idim = ndim-1; idim >= 0; --idim) {
npy_intp i_perm = strideperm[idim].perm;
strides[i_perm] = stride;
- stride *= shape[i_perm];
+ stride *= dims[i_perm];
}
/* Finally, allocate the array */
ret = PyArray_NewFromDescr(subok ? Py_TYPE(prototype) : &PyArray_Type,
dtype,
ndim,
- shape,
+ dims,
strides,
NULL,
0,
@@ -1277,6 +1285,29 @@ PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order,
}
/*NUMPY_API
+ * Creates a new array with the same shape as the provided one,
+ * with possible memory layout order and data type changes.
+ *
+ * prototype - The array the new one should be like.
+ * order - NPY_CORDER - C-contiguous result.
+ * NPY_FORTRANORDER - Fortran-contiguous result.
+ * NPY_ANYORDER - Fortran if prototype is Fortran, C otherwise.
+ * NPY_KEEPORDER - Keeps the axis ordering of prototype.
+ * dtype - If not NULL, overrides the data type of the result.
+ * subok - If 1, use the prototype's array subtype, otherwise
+ * always create a base-class array.
+ *
+ * NOTE: If dtype is not NULL, steals the dtype reference. On failure or when
+ * dtype->subarray is true, dtype will be decrefed.
+ */
+NPY_NO_EXPORT PyObject *
+PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order,
+ PyArray_Descr *dtype, int subok)
+{
+ return PyArray_NewLikeArrayWithShape(prototype, order, dtype, 0, NULL, subok);
+}
+
+/*NUMPY_API
* Generic new array creation routine.
*/
NPY_NO_EXPORT PyObject *
diff --git a/numpy/core/src/multiarray/ctors.h b/numpy/core/src/multiarray/ctors.h
index e9a2532da..4932130a7 100644
--- a/numpy/core/src/multiarray/ctors.h
+++ b/numpy/core/src/multiarray/ctors.h
@@ -18,6 +18,10 @@ PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
int flags, PyObject *obj, PyObject *base, int zeroed,
int allow_emptystring);
+NPY_NO_EXPORT PyObject *
+PyArray_NewLikeArrayWithShape(PyArrayObject *prototype, NPY_ORDER order,
+ PyArray_Descr *dtype, int ndim, npy_intp *dims, int subok);
+
NPY_NO_EXPORT PyObject *PyArray_New(PyTypeObject *, int nd, npy_intp *,
int, npy_intp *, void *, int, int, PyObject *);
diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c
index b84de3a8d..52412b827 100644
--- a/numpy/core/src/multiarray/multiarraymodule.c
+++ b/numpy/core/src/multiarray/multiarraymodule.c
@@ -1758,7 +1758,7 @@ static PyObject *
array_copyto(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
{
- static char *kwlist[] = {"dst","src","casting","where",NULL};
+ static char *kwlist[] = {"dst", "src", "casting", "where", NULL};
PyObject *wheremask_in = NULL;
PyArrayObject *dst = NULL, *src = NULL, *wheremask = NULL;
NPY_CASTING casting = NPY_SAME_KIND_CASTING;
@@ -1803,7 +1803,7 @@ static PyObject *
array_empty(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
{
- static char *kwlist[] = {"shape","dtype","order",NULL};
+ static char *kwlist[] = {"shape", "dtype", "order", NULL};
PyArray_Descr *typecode = NULL;
PyArray_Dims shape = {NULL, 0};
NPY_ORDER order = NPY_CORDER;
@@ -1846,23 +1846,28 @@ static PyObject *
array_empty_like(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
{
- static char *kwlist[] = {"prototype","dtype","order","subok",NULL};
+ static char *kwlist[] = {"prototype", "dtype", "order", "subok", "shape", NULL};
PyArrayObject *prototype = NULL;
PyArray_Descr *dtype = NULL;
NPY_ORDER order = NPY_KEEPORDER;
PyArrayObject *ret = NULL;
int subok = 1;
+ PyArray_Dims shape = {NULL, 0};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&O&i:empty_like", kwlist,
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&O&iO&:empty_like", kwlist,
&PyArray_Converter, &prototype,
&PyArray_DescrConverter2, &dtype,
&PyArray_OrderConverter, &order,
- &subok)) {
+ &subok,
+ &PyArray_IntpConverter, &shape)) {
goto fail;
}
/* steals the reference to dtype if it's not NULL */
- ret = (PyArrayObject *)PyArray_NewLikeArray(prototype,
- order, dtype, subok);
+ ret = (PyArrayObject *)PyArray_NewLikeArrayWithShape(prototype, order, dtype,
+ shape.len, shape.ptr, subok);
+ if (!ret) {
+ goto fail;
+ }
Py_DECREF(prototype);
return (PyObject *)ret;
@@ -1881,7 +1886,7 @@ static PyObject *
array_scalar(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
{
- static char *kwlist[] = {"dtype","obj", NULL};
+ static char *kwlist[] = {"dtype", "obj", NULL};
PyArray_Descr *typecode;
PyObject *obj = NULL, *tmpobj = NULL;
int alloc = 0;
@@ -1957,7 +1962,7 @@ array_scalar(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
static PyObject *
array_zeros(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
{
- static char *kwlist[] = {"shape","dtype","order",NULL};
+ static char *kwlist[] = {"shape", "dtype", "order", NULL};
PyArray_Descr *typecode = NULL;
PyArray_Dims shape = {NULL, 0};
NPY_ORDER order = NPY_CORDER;
diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py
index 1822a7adf..1c53f9372 100644
--- a/numpy/core/tests/test_numeric.py
+++ b/numpy/core/tests/test_numeric.py
@@ -2157,6 +2157,7 @@ class TestLikeFuncs(object):
(np.arange(24).reshape(2, 3, 4).swapaxes(0, 1), None),
(np.arange(24).reshape(4, 3, 2).swapaxes(0, 1), '?'),
]
+ self.shapes = [(5,), (5,6,), (5,6,7,)]
def compare_array_value(self, dz, value, fill_value):
if value is not None:
@@ -2222,6 +2223,34 @@ class TestLikeFuncs(object):
assert_equal(dz.dtype, np.dtype(dtype))
self.compare_array_value(dz, value, fill_value)
+ # Test the 'shape' parameter
+ for s in self.shapes:
+ for o in 'CFA':
+ sz = like_function(d, dtype=dtype, shape=s, order=o,
+ **fill_kwarg)
+ assert_equal(sz.shape, s)
+ if dtype is None:
+ assert_equal(sz.dtype, d.dtype)
+ else:
+ assert_equal(sz.dtype, np.dtype(dtype))
+ if o == 'C' or (o == 'A' and d.flags.c_contiguous):
+ assert_(sz.flags.c_contiguous)
+ elif o == 'F' or (o == 'A' and d.flags.f_contiguous):
+ assert_(sz.flags.f_contiguous)
+ self.compare_array_value(sz, value, fill_value)
+
+ if (d.ndim != len(s)):
+ assert_equal(np.argsort(like_function(d, dtype=dtype,
+ shape=s, order='K',
+ **fill_kwarg).strides),
+ np.argsort(np.empty(s, dtype=dtype,
+ order='C').strides))
+ else:
+ assert_equal(np.argsort(like_function(d, dtype=dtype,
+ shape=s, order='K',
+ **fill_kwarg).strides),
+ np.argsort(d.strides))
+
# Test the 'subok' parameter
class MyNDArray(np.ndarray):
pass