summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Wieser <wieser.eric@gmail.com>2017-04-17 23:41:36 +0100
committerEric Wieser <wieser.eric@gmail.com>2018-11-12 09:14:50 -0800
commitd3b2036949255e48ecbcfcc70ed2ea95c755cf2a (patch)
tree703569f42f06539eb1c7a95c841d5a271079ea5a
parent97df928718a46b869d0d6675ffd6e8c539f32773 (diff)
downloadnumpy-d3b2036949255e48ecbcfcc70ed2ea95c755cf2a.tar.gz
ENH: Allow ufunc.identity to be any python object
-rw-r--r--doc/release/1.16.0-notes.rst9
-rw-r--r--doc/source/reference/c-api.ufunc.rst21
-rw-r--r--numpy/core/code_generators/numpy_api.py3
-rw-r--r--numpy/core/include/numpy/ufuncobject.h9
-rw-r--r--numpy/core/src/umath/ufunc_object.c27
5 files changed, 65 insertions, 4 deletions
diff --git a/doc/release/1.16.0-notes.rst b/doc/release/1.16.0-notes.rst
index bb2b17782..7bfcb37ff 100644
--- a/doc/release/1.16.0-notes.rst
+++ b/doc/release/1.16.0-notes.rst
@@ -102,8 +102,13 @@ for unraveling. ``dims`` remains supported, but is now deprecated.
C API changes
=============
-The :c:data:`NPY_API_VERSION` was incremented to 0x0000D since
-``core_dim_flags`` and ``core_dim_sizes`` were added to :c:type:`PyUFuncObject`.
+The :c:data:`NPY_API_VERSION` was incremented to 0x0000D, due to the addition
+of:
+
+* :c:member:`PyUFuncObject.core_dim_flags`
+* :c:member:`PyUFuncObject.core_dim_sizes`
+* :c:member:`PyUFuncObject.identity_value`
+* :c:function:`PyUFunc_FromFuncAndDataAndSignatureAndIdentity`
New Features
============
diff --git a/doc/source/reference/c-api.ufunc.rst b/doc/source/reference/c-api.ufunc.rst
index 07c7b0c80..0499ccf5b 100644
--- a/doc/source/reference/c-api.ufunc.rst
+++ b/doc/source/reference/c-api.ufunc.rst
@@ -169,8 +169,12 @@ Functions
:param identity:
Either :c:data:`PyUFunc_One`, :c:data:`PyUFunc_Zero`,
- :c:data:`PyUFunc_None`. This specifies what should be returned when
+ :c:data:`PyUFunc_MinusOne`, or :c:data:`PyUFunc_None`.
+ This specifies what should be returned when
an empty array is passed to the reduce method of the ufunc.
+ The special value :c:data:`PyUFunc_IdentityValue` may only be used with
+ the :c:func:`PyUFunc_FromFuncAndDataAndSignatureAndIdentity` method, to
+ allow an arbitrary python object to be used as the identity.
:param name:
The name for the ufunc as a ``NULL`` terminated string. Specifying
@@ -206,6 +210,21 @@ Functions
to calling PyUFunc_FromFuncAndData. A copy of the string is made,
so the passed in buffer can be freed.
+.. c:function:: PyObject* PyUFunc_FromFuncAndDataAndSignatureAndIdentity(
+ PyUFuncGenericFunction *func, void **data, char *types, int ntypes, \
+ int nin, int nout, int identity, char *name, char *doc, int unused, char *signature,
+ PyObject *identity_value)
+
+ This function is very similar to `PyUFunc_FromFuncAndDataAndSignature` above,
+ but has an extra *identity_value* argument, to define an arbitrary identity
+ for the ufunc when ``identity`` is passed as ``PyUFunc_IdentityValue``.
+
+ :param identity_value:
+ The identity for the new gufunc. Must be passed as ``NULL`` unless the
+ ``identity`` argument is ``PyUFunc_IdentityValue``. Setting it to NULL
+ is equivalent to calling PyUFunc_FromFuncAndDataAndSignature.
+
+
.. c:function:: int PyUFunc_RegisterLoopForType( \
PyUFuncObject* ufunc, int usertype, PyUFuncGenericFunction function, \
int* arg_types, void* data)
diff --git a/numpy/core/code_generators/numpy_api.py b/numpy/core/code_generators/numpy_api.py
index d8a9ee6b4..fdf97ac00 100644
--- a/numpy/core/code_generators/numpy_api.py
+++ b/numpy/core/code_generators/numpy_api.py
@@ -402,6 +402,9 @@ ufunc_funcs_api = {
# End 1.7 API
'PyUFunc_RegisterLoopForDescr': (41,),
# End 1.8 API
+ 'PyUFunc_FromFuncAndDataAndSignatureAndIdentity':
+ (42,),
+ # End 1.16 API
}
# List of all the dicts which define the C API
diff --git a/numpy/core/include/numpy/ufuncobject.h b/numpy/core/include/numpy/ufuncobject.h
index 85f8a6c08..90d837a9b 100644
--- a/numpy/core/include/numpy/ufuncobject.h
+++ b/numpy/core/include/numpy/ufuncobject.h
@@ -223,7 +223,8 @@ typedef struct _tagPyUFuncObject {
*/
npy_uint32 *core_dim_flags;
-
+ /* Identity for reduction, when identity == PyUFunc_IdentityValue */
+ PyObject *identity_value;
} PyUFuncObject;
@@ -299,6 +300,12 @@ typedef struct _tagPyUFuncObject {
* This case allows reduction with multiple axes at once.
*/
#define PyUFunc_ReorderableNone -2
+/*
+ * UFunc unit is in identity_value, and the order of operations can be reordered
+ * This case allows reduction with multiple axes at once.
+ */
+#define PyUFunc_IdentityValue -3
+
#define UFUNC_REDUCE 0
#define UFUNC_ACCUMULATE 1
diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c
index e60c734ec..1fe8745a0 100644
--- a/numpy/core/src/umath/ufunc_object.c
+++ b/numpy/core/src/umath/ufunc_object.c
@@ -2453,6 +2453,11 @@ _get_identity(PyUFuncObject *ufunc, npy_bool *reorderable) {
*reorderable = 0;
Py_RETURN_NONE;
+ case PyUFunc_IdentityValue:
+ *reorderable = 1;
+ Py_INCREF(ufunc->identity_value);
+ return ufunc->identity_value;
+
default:
PyErr_Format(PyExc_ValueError,
"ufunc %s has an invalid identity", ufunc_get_name_cstr(ufunc));
@@ -4833,6 +4838,20 @@ PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data,
const char *name, const char *doc,
int unused, const char *signature)
{
+ return PyUFunc_FromFuncAndDataAndSignatureAndIdentity(
+ func, data, types, ntypes, nin, nout, identity, name, doc,
+ unused, signature, NULL);
+}
+
+/*UFUNC_API*/
+NPY_NO_EXPORT PyObject *
+PyUFunc_FromFuncAndDataAndSignatureAndIdentity(PyUFuncGenericFunction *func, void **data,
+ char *types, int ntypes,
+ int nin, int nout, int identity,
+ const char *name, const char *doc,
+ int unused, const char *signature,
+ PyObject *identity_value)
+{
PyUFuncObject *ufunc;
if (nin + nout > NPY_MAXARGS) {
PyErr_Format(PyExc_ValueError,
@@ -4853,6 +4872,10 @@ PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data,
ufunc->nout = nout;
ufunc->nargs = nin+nout;
ufunc->identity = identity;
+ if (ufunc->identity == PyUFunc_IdentityValue) {
+ Py_INCREF(identity_value);
+ }
+ ufunc->identity_value = identity_value;
ufunc->functions = func;
ufunc->data = data;
@@ -4874,6 +4897,7 @@ PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data,
ufunc->op_flags = PyArray_malloc(sizeof(npy_uint32)*ufunc->nargs);
if (ufunc->op_flags == NULL) {
+ Py_DECREF(ufunc);
return PyErr_NoMemory();
}
memset(ufunc->op_flags, 0, sizeof(npy_uint32)*ufunc->nargs);
@@ -5230,6 +5254,9 @@ ufunc_dealloc(PyUFuncObject *ufunc)
PyArray_free(ufunc->op_flags);
Py_XDECREF(ufunc->userloops);
Py_XDECREF(ufunc->obj);
+ if (ufunc->identity == PyUFunc_IdentityValue) {
+ Py_DECREF(ufunc->identity_value);
+ }
PyArray_free(ufunc);
}