diff options
author | Peter Andreas Entschev <peter@entschev.com> | 2019-04-25 17:38:47 +0200 |
---|---|---|
committer | Eric Wieser <wieser.eric@gmail.com> | 2019-04-25 08:38:47 -0700 |
commit | 0c3d18f57c842047ae23383c2eae46a042faef5d (patch) | |
tree | 06bc834a1f55061b7e5b17bbd767b6d13cd930d3 /numpy/core | |
parent | d96b446dfe9a2a3e17b01ed13ebf6a459b5605e5 (diff) | |
download | numpy-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.py | 10 | ||||
-rw-r--r-- | numpy/core/numeric.py | 36 | ||||
-rw-r--r-- | numpy/core/src/multiarray/ctors.c | 51 | ||||
-rw-r--r-- | numpy/core/src/multiarray/ctors.h | 4 | ||||
-rw-r--r-- | numpy/core/src/multiarray/multiarraymodule.c | 23 | ||||
-rw-r--r-- | numpy/core/tests/test_numeric.py | 29 |
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 |