diff options
| author | Sebastian Berg <sebastianb@nvidia.com> | 2022-11-03 10:44:17 +0100 |
|---|---|---|
| committer | Sebastian Berg <sebastianb@nvidia.com> | 2023-01-20 15:28:48 +0100 |
| commit | 07a0d9c8e80dde5676183bf817ebf55889d5cfec (patch) | |
| tree | c2f2e6cdd3bd67bda461df27011fe6d7d01006ab /numpy | |
| parent | 336bb64f2aedc8eaf3a52975b99e14958068618d (diff) | |
| download | numpy-07a0d9c8e80dde5676183bf817ebf55889d5cfec.tar.gz | |
MAINT: Rename arraymethod reduction identity getter
Need to look into whether to cut out the dynamic discovery of
reorderability though.
Diffstat (limited to 'numpy')
| -rw-r--r-- | numpy/core/include/numpy/experimental_dtype_api.h | 36 | ||||
| -rw-r--r-- | numpy/core/src/multiarray/array_method.c | 16 | ||||
| -rw-r--r-- | numpy/core/src/multiarray/array_method.h | 21 | ||||
| -rw-r--r-- | numpy/core/src/umath/reduction.c | 44 | ||||
| -rw-r--r-- | numpy/core/src/umath/wrapping_array_method.c | 9 |
5 files changed, 69 insertions, 57 deletions
diff --git a/numpy/core/include/numpy/experimental_dtype_api.h b/numpy/core/include/numpy/experimental_dtype_api.h index 05fb1d936..1664c1a9a 100644 --- a/numpy/core/include/numpy/experimental_dtype_api.h +++ b/numpy/core/include/numpy/experimental_dtype_api.h @@ -322,24 +322,32 @@ typedef int (PyArrayMethod_StridedLoop)(PyArrayMethod_Context *context, /* - * For reductions, NumPy sometimes requires an identity or default value. - * The typical way of relying on a single "default" for ufuncs does not always - * work however. This function allows customizing the identity value as well - * as whether the operation is "reorderable". + * For reductions, NumPy sometimes may use an identity or default value + * (which we group as the "initial" value; a user provided `initial=` is + * used as both). + * The function is after the reduction identity, but generalizes it. + * It further is used to indicate whether the reduction can be reordered + * for optimization. + * The value may be used for reductions which are empty, non-empty, or both + * to allow maximum flexibility. A typical identity allows both. + * Object dtype sum has only a default 0, but no identity which allows things + * like `np.array(["a", "b"], dtype=object).sum()` to work. + * The opposite should not really happen, but allows `np.min([])` to error, + * when `-inf` is a valid identity (for optimization/easier processing). */ -#define NPY_METH_get_identity 7 +#define NPY_METH_get_reduction_initial 4 typedef enum { - /* The value can be used as a default for empty reductions */ - NPY_METH_ITEM_IS_DEFAULT = 1 << 0, - /* The value represents the identity value */ - NPY_METH_ITEM_IS_IDENTITY = 1 << 1, + /* The "identity" is used as result for empty reductions */ + NPY_METH_INITIAL_IS_DEFAULT = 1 << 0, + /* The "identity" is used for non-empty reductions as initial value */ + NPY_METH_INITIAL_IS_IDENTITY = 1 << 1, /* The operation is fully reorderable (iteration order may be optimized) */ NPY_METH_IS_REORDERABLE = 1 << 2, -} NPY_ARRAYMETHOD_IDENTITY_FLAGS; +} NPY_ARRAYMETHOD_REDUCTION_FLAGS; /* - * If an identity exists, should set the `NPY_METH_ITEM_IS_IDENTITY`, normally + * If an identity exists, should set the `NPY_METH_INITIAL_IS_IDENTITY`, normally * the `NPY_METH_ITEM_IS_DEFAULT` should also be set, but it is distinct. * By default NumPy provides a "default" for `object` dtype, but does not use * it as an identity (this is e.g. to allows reducing even Python strings @@ -350,9 +358,9 @@ typedef enum { * NOTE: `item` can be `NULL` when a user passed a custom initial value, in * this case only the `reorderable` flag is valid. */ -typedef int (get_identity_function)( - PyArrayMethod_Context *context, char *item, - NPY_ARRAYMETHOD_IDENTITY_FLAGS *flags); +typedef int (get_reduction_intial_function)( + PyArrayMethod_Context *context, char *initial, + NPY_ARRAYMETHOD_REDUCTION_FLAGS *flags); /* diff --git a/numpy/core/src/multiarray/array_method.c b/numpy/core/src/multiarray/array_method.c index e9b1c3e83..02199280a 100644 --- a/numpy/core/src/multiarray/array_method.c +++ b/numpy/core/src/multiarray/array_method.c @@ -176,7 +176,7 @@ PyUFunc_GetIdentity(PyUFuncObject *ufunc, npy_bool *reorderable); */ static int default_get_identity_function(PyArrayMethod_Context *context, - char *item, NPY_ARRAYMETHOD_IDENTITY_FLAGS *flags) + char *initial, NPY_ARRAYMETHOD_REDUCTION_FLAGS *flags) { *flags = 0; @@ -202,7 +202,7 @@ default_get_identity_function(PyArrayMethod_Context *context, *flags |= NPY_METH_IS_REORDERABLE; } - if (item == NULL || identity_obj == Py_None) { + if (initial == NULL || identity_obj == Py_None) { /* * Only reorderable flag was requested (user provided initial value) * or there is no identity/default value to report. @@ -212,11 +212,11 @@ default_get_identity_function(PyArrayMethod_Context *context, } /* Report the default value and identity unless object dtype */ - *flags |= NPY_METH_ITEM_IS_DEFAULT; + *flags |= NPY_METH_INITIAL_IS_DEFAULT; if (context->descriptors[0]->type_num != NPY_OBJECT) { - *flags |= NPY_METH_ITEM_IS_IDENTITY; + *flags |= NPY_METH_INITIAL_IS_IDENTITY; } - int res = PyArray_Pack(context->descriptors[0], item, identity_obj); + int res = PyArray_Pack(context->descriptors[0], initial, identity_obj); Py_DECREF(identity_obj); return res; } @@ -307,7 +307,7 @@ fill_arraymethod_from_slots( /* Set the defaults */ meth->get_strided_loop = &npy_default_get_strided_loop; meth->resolve_descriptors = &default_resolve_descriptors; - meth->get_identity = &default_get_identity_function; + meth->get_reduction_initial = &default_get_identity_function; /* Fill in the slots passed by the user */ /* @@ -344,8 +344,8 @@ fill_arraymethod_from_slots( case NPY_METH_unaligned_contiguous_loop: meth->unaligned_contiguous_loop = slot->pfunc; continue; - case NPY_METH_get_identity: - meth->get_identity = slot->pfunc; + case NPY_METH_get_reduction_initial: + meth->get_reduction_initial = slot->pfunc; continue; default: break; diff --git a/numpy/core/src/multiarray/array_method.h b/numpy/core/src/multiarray/array_method.h index 0715bcd39..493bae5e1 100644 --- a/numpy/core/src/multiarray/array_method.h +++ b/numpy/core/src/multiarray/array_method.h @@ -105,13 +105,13 @@ typedef int (get_loop_function)( typedef enum { - /* The value can be used as a default for empty reductions */ - NPY_METH_ITEM_IS_DEFAULT = 1 << 0, - /* The value represents the identity value */ - NPY_METH_ITEM_IS_IDENTITY = 1 << 1, + /* The "identity" is used as result for empty reductions */ + NPY_METH_INITIAL_IS_DEFAULT = 1 << 0, + /* The "identity" is used for non-empty reductions as initial value */ + NPY_METH_INITIAL_IS_IDENTITY = 1 << 1, /* The operation is fully reorderable (iteration order may be optimized) */ NPY_METH_IS_REORDERABLE = 1 << 2, -} NPY_ARRAYMETHOD_IDENTITY_FLAGS; +} NPY_ARRAYMETHOD_REDUCTION_FLAGS; /* * Query an ArrayMethod for its identity (for use with reductions) and whether @@ -127,10 +127,9 @@ typedef enum { * * The function must return 0 on success and -1 on error (and clean up `item`). */ -typedef int (get_identity_function)( - PyArrayMethod_Context *context, char *item, - NPY_ARRAYMETHOD_IDENTITY_FLAGS *flags); - +typedef int (get_reduction_intial_function)( + PyArrayMethod_Context *context, char *initial, + NPY_ARRAYMETHOD_REDUCTION_FLAGS *flags); /* * The following functions are only used be the wrapping array method defined @@ -221,7 +220,7 @@ typedef struct PyArrayMethodObject_tag { NPY_ARRAYMETHOD_FLAGS flags; resolve_descriptors_function *resolve_descriptors; get_loop_function *get_strided_loop; - get_identity_function *get_identity; + get_reduction_intial_function *get_reduction_initial; /* Typical loop functions (contiguous ones are used in current casts) */ PyArrayMethod_StridedLoop *strided_loop; PyArrayMethod_StridedLoop *contiguous_loop; @@ -263,7 +262,7 @@ extern NPY_NO_EXPORT PyTypeObject PyBoundArrayMethod_Type; #define NPY_METH_resolve_descriptors 1 #define NPY_METH_get_loop 2 #define NPY_METH_strided_loop 3 -#define NPY_METH_get_identity 4 +#define NPY_METH_get_reduction_initial 4 #define NPY_METH_contiguous_loop 5 #define NPY_METH_unaligned_strided_loop 6 #define NPY_METH_unaligned_contiguous_loop 7 diff --git a/numpy/core/src/umath/reduction.c b/numpy/core/src/umath/reduction.c index fa37bdef0..5e7b8224e 100644 --- a/numpy/core/src/umath/reduction.c +++ b/numpy/core/src/umath/reduction.c @@ -6,6 +6,7 @@ * * See LICENSE.txt for the license. */ +#include "array_method.h" #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE #define _UMATHMODULE @@ -199,27 +200,27 @@ PyUFunc_ReduceWrapper(PyArrayMethod_Context *context, /* * Fill in default or identity value and ask if this is reorderable. * Note that if the initial value was provided, we pass `initial_buf=NULL` - * to the `get_identity` function to indicate that we only require the - * reorderable flag. + * to the `get_reduction_initial` function to indicate that we only + * require the reorderable flag. * If a `result` was passed in, it is possible that the result has a dtype * differing to the operation one. */ - NPY_ARRAYMETHOD_IDENTITY_FLAGS identity_flags = 0; - char *identity_buf = NULL; + NPY_ARRAYMETHOD_REDUCTION_FLAGS reduction_flags = 0; + char *initial_buf = NULL; if (initial == NULL) { /* Always init buffer (only necessary if it holds references) */ - identity_buf = PyMem_Calloc(1, op_dtypes[0]->elsize); - if (identity_buf == NULL) { + initial_buf = PyMem_Calloc(1, op_dtypes[0]->elsize); + if (initial_buf == NULL) { PyErr_NoMemory(); goto fail; } } - if (context->method->get_identity( - context, identity_buf, &identity_flags) < 0) { + if (context->method->get_reduction_initial( + context, initial_buf, &reduction_flags) < 0) { goto fail; } /* More than one axis means multiple orders are possible */ - if (!(identity_flags & NPY_METH_IS_REORDERABLE) + if (!(reduction_flags & NPY_METH_IS_REORDERABLE) && count_axes(PyArray_NDIM(operand), axis_flags) > 1) { PyErr_Format(PyExc_ValueError, "reduction operation '%s' is not reorderable, " @@ -235,7 +236,7 @@ PyUFunc_ReduceWrapper(PyArrayMethod_Context *context, NPY_ITER_REFS_OK | NPY_ITER_DELAY_BUFALLOC | NPY_ITER_COPY_IF_OVERLAP; - if (!(identity_flags & NPY_METH_IS_REORDERABLE)) { + if (!(reduction_flags & NPY_METH_IS_REORDERABLE)) { it_flags |= NPY_ITER_DONT_NEGATE_STRIDES; } op_flags[0] = NPY_ITER_READWRITE | @@ -307,6 +308,9 @@ PyUFunc_ReduceWrapper(PyArrayMethod_Context *context, result = NpyIter_GetOperandArray(iter)[0]; npy_bool empty_reduce = NpyIter_GetIterSize(iter) == 0; + if (empty_reduce) { + //empty_reduce = PyArray_SIZE(result) != 0; + } PyArrayMethod_StridedLoop *strided_loop; NPY_ARRAYMETHOD_FLAGS flags = 0; @@ -333,15 +337,15 @@ PyUFunc_ReduceWrapper(PyArrayMethod_Context *context, goto fail; } } - else if (identity_buf != NULL && ( /* cannot fill for `initial=None` */ - identity_flags & NPY_METH_ITEM_IS_IDENTITY || - (empty_reduce && identity_flags & NPY_METH_ITEM_IS_DEFAULT))) { + else if (initial_buf != NULL && ( /* cannot fill for `initial=None` */ + (reduction_flags & NPY_METH_INITIAL_IS_IDENTITY) + || (empty_reduce && reduction_flags & NPY_METH_INITIAL_IS_DEFAULT))) { /* Loop provided an identity or default value, assign to result. */ int ret = raw_array_assign_scalar( PyArray_NDIM(result), PyArray_DIMS(result), PyArray_DESCR(result), PyArray_BYTES(result), PyArray_STRIDES(result), - op_dtypes[0], identity_buf); + op_dtypes[0], initial_buf); if (ret < 0) { goto fail; } @@ -424,10 +428,10 @@ PyUFunc_ReduceWrapper(PyArrayMethod_Context *context, } Py_INCREF(result); - if (identity_buf != NULL && PyDataType_REFCHK(PyArray_DESCR(result))) { - PyArray_Item_XDECREF(identity_buf, PyArray_DESCR(result)); + if (initial_buf != NULL && PyDataType_REFCHK(PyArray_DESCR(result))) { + PyArray_Item_XDECREF(initial_buf, PyArray_DESCR(result)); } - PyMem_FREE(identity_buf); + PyMem_FREE(initial_buf); NPY_AUXDATA_FREE(auxdata); if (!NpyIter_Deallocate(iter)) { Py_DECREF(result); @@ -436,10 +440,10 @@ PyUFunc_ReduceWrapper(PyArrayMethod_Context *context, return result; fail: - if (identity_buf != NULL && PyDataType_REFCHK(op_dtypes[0])) { - PyArray_Item_XDECREF(identity_buf, op_dtypes[0]); + if (initial_buf != NULL && PyDataType_REFCHK(op_dtypes[0])) { + PyArray_Item_XDECREF(initial_buf, op_dtypes[0]); } - PyMem_FREE(identity_buf); + PyMem_FREE(initial_buf); NPY_AUXDATA_FREE(auxdata); if (iter != NULL) { NpyIter_Deallocate(iter); diff --git a/numpy/core/src/umath/wrapping_array_method.c b/numpy/core/src/umath/wrapping_array_method.c index 4d0c4caa4..688f71fde 100644 --- a/numpy/core/src/umath/wrapping_array_method.c +++ b/numpy/core/src/umath/wrapping_array_method.c @@ -185,7 +185,7 @@ wrapping_method_get_loop( */ static int wrapping_method_get_identity_function(PyArrayMethod_Context *context, - char *item, NPY_ARRAYMETHOD_IDENTITY_FLAGS *flags) + char *item, NPY_ARRAYMETHOD_REDUCTION_FLAGS *flags) { /* Copy the context, and replace descriptors: */ PyArrayMethod_Context orig_context = *context; @@ -200,8 +200,8 @@ wrapping_method_get_identity_function(PyArrayMethod_Context *context, nin, nout, dtypes, context->descriptors, orig_descrs) < 0) { return -1; } - int res = context->method->wrapped_meth->get_identity(&orig_context, - item, flags); + int res = context->method->wrapped_meth->get_reduction_initial( + &orig_context, item, flags); for (int i = 0; i < nin + nout; i++) { Py_DECREF(orig_descrs); } @@ -275,7 +275,8 @@ PyUFunc_AddWrappingLoop(PyObject *ufunc_obj, PyType_Slot slots[] = { {NPY_METH_resolve_descriptors, &wrapping_method_resolve_descriptors}, {NPY_METH_get_loop, &wrapping_method_get_loop}, - {NPY_METH_get_identity, &wrapping_method_get_identity_function}, + {NPY_METH_get_reduction_initial, + &wrapping_method_get_identity_function}, {0, NULL} }; |
