Skip to content

Instantly share code, notes, and snippets.

@mattip
Created May 31, 2018 05:04
Show Gist options
  • Save mattip/eec3dd590f7012764ff9fb534a986251 to your computer and use it in GitHub Desktop.
Save mattip/eec3dd590f7012764ff9fb534a986251 to your computer and use it in GitHub Desktop.
diff for gufunc-signature-modification2
diff --git a/doc/source/reference/c-api.types-and-structures.rst b/doc/source/reference/c-api.types-and-structures.rst
index dcebd1e..e92cb1e 100644
--- a/doc/source/reference/c-api.types-and-structures.rst
+++ b/doc/source/reference/c-api.types-and-structures.rst
@@ -133,9 +133,9 @@ PyArray_Type
is related to this array. There are two use cases: 1) If this array
does not own its own memory, then base points to the Python object
that owns it (perhaps another array object), 2) If this array has
- the (deprecated) :c:data:`NPY_ARRAY_UPDATEIFCOPY` or
+ the (deprecated) :c:data:`NPY_ARRAY_UPDATEIFCOPY` or
:c:data:NPY_ARRAY_WRITEBACKIFCOPY`: flag set, then this array is
- a working copy of a "misbehaved" array. When
+ a working copy of a "misbehaved" array. When
``PyArray_ResolveWritebackIfCopy`` is called, the array pointed to by base
will be updated with the contents of this array.
@@ -683,7 +683,9 @@ PyUFunc_Type
The core of the ufunc is the :c:type:`PyUFuncObject` which contains all
the information needed to call the underlying C-code loops that
- perform the actual work. It has the following structure:
+ perform the actual work. While it is described here for completeness, it
+ should be considered internal to NumPy and manipulated via ``PyUFunc_*``
+ functions. It has the following structure:
.. code-block:: c
@@ -696,15 +698,29 @@ PyUFunc_Type
PyUFuncGenericFunction *functions;
void **data;
int ntypes;
- int reserved1;
+ int version;
const char *name;
char *types;
const char *doc;
void *ptr;
PyObject *obj;
PyObject *userloops;
+ int core_enabled;
+ int core_num_dim_ix;
+ int *core_num_dims;
+ int *core_dim_ixs;
+ int *core_offsets;
+ char *core_signature;
+ PyUFunc_TypeResolutionFunc *type_resolver;
+ PyUFunc_LegacyInnerLoopSelectionFunc *legacy_inner_loop_selector;
+ void *reserved2;
+ PyUFunc_MaskedInnerLoopSelectionFunc *masked_inner_loop_selector;
npy_uint32 *op_flags;
npy_uint32 *iter_flags;
+ /* new in version 1 */
+ npy_intp *core_dim_sizes;
+ npy_uint32 *core_dim_flags;
+
} PyUFuncObject;
.. c:macro: PyUFuncObject.PyObject_HEAD
@@ -764,6 +780,10 @@ PyUFunc_Type
specifies how many different 1-d loops (of the builtin data
types) are available.
+ .. c:member:: int PyUFuncObject.version
+
+ The version of this struct, currently set to 1
+
.. c:member:: char *PyUFuncObject.name
A string name for the ufunc. This is used dynamically to build
@@ -804,6 +824,51 @@ PyUFunc_Type
User defined type numbers are always larger than
:c:data:`NPY_USERDEF`.
+ .. c:member:: int PyUFuncObject.core_enabled
+
+ 0 for scalar ufuncs; 1 for generalized ufuncs
+
+ .. c:member:: int PyUFuncObject.core_num_dim_ix
+
+ Number of distinct core dimension names in the signature
+
+ .. c:member:: int *PyUFuncObject.core_num_dims
+
+ Number of core dimensions of each argument
+
+ .. c:member:: int *PyUFuncObject.core_dim_ixs
+
+ Dimension idices in a flattened form; indices of argument ``k`` are
+ stored in ``core_dim_ixs[core_offsets[k] : core_offsets[k] +
+ core_numdims[k]]``
+
+ .. c:member:: int *PyUFuncObject.core_offsets
+
+ Position of 1st core dimension of each argument in ``core_dim_ixs``,
+ equivalent to cumsum(``core_num_dims``)
+
+ .. c:member:: char *PyUFuncObject.core_signature
+
+ Core signature string
+
+ .. c:member:: PyUFunc_TypeResolutionFunc *PyUFuncObject.type_resolver
+
+ A function which resolves the types and fills an array with the dtypes
+ for the inputs and outputs
+
+ .. c:member:: PyUFunc_LegacyInnerLoopSelectionFunc *PyUFuncObject.legacy_inner_loop_selector
+
+ A function which returns an inner loop written for NumPy 1.6 and earlier
+ ufuncs. This is for backwards compatibility, and may be NULL if
+ ``inner_loop_selector`` is specified
+
+ .. c:member:: void *PyUFuncObject.reserved2
+
+ Not in use
+
+ .. c:member:: PyUFunc_MaskedInnerLoopSelectionFunc *PyUFuncObject.masked_inner_loop_selector
+
+ Function which returns a masked inner loop for the ufunc
.. c:member:: npy_uint32 PyUFuncObject.op_flags
@@ -813,6 +878,20 @@ PyUFunc_Type
Override the default nditer flags for the ufunc.
+ Added in version 1
+
+ .. c:member:: npy_intp *PyUFuncObject.core_dim_sizes
+
+ For each distinct core dimension, the possible
+ "frozen" size (``-1`` if not frozen)
+
+ .. c:member:: npy_uint32 *PyUFuncObject.core_dim_flags
+
+ For each distinct core dimension, a set of flags ``OR`` ed together:
+
+ - :c:data:`UFUNC_CORE_CAN_IGNORE` if the dim name ends in ``?``
+ - :c:data:`UFUNC_CORE_FROZEN` if the dim is numerical (frozen)
+
PyArrayIter_Type
----------------
diff --git a/numpy/core/src/umath/_umath_tests.c.src b/numpy/core/src/umath/_umath_tests.c.src
index 160d8a4..d0afd53 100644
--- a/numpy/core/src/umath/_umath_tests.c.src
+++ b/numpy/core/src/umath/_umath_tests.c.src
@@ -530,8 +530,8 @@ static PyMethodDef UMath_TestsMethods[] = {
{"test_signature", UMath_Tests_test_signature, METH_VARARGS,
"Test signature parsing of ufunc. \n"
"Arguments: nin nout signature \n"
- "If fails, it returns NULL. Otherwise it will returns 0 for scalar ufunc "
- "and 1 for generalized ufunc. \n",
+ "If fails, it returns NULL. Otherwise it returns a tuple of ufunc "
+ "internals. \n",
},
{NULL, NULL, 0, NULL} /* Sentinel */
};
diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c
index 6dae936..1c866a5 100644
--- a/numpy/core/src/umath/ufunc_object.c
+++ b/numpy/core/src/umath/ufunc_object.c
@@ -2200,19 +2200,20 @@ _parse_axes_arg(PyUFuncObject *ufunc, int core_num_dims[], PyObject *axes,
*
* Returns 0 on success, and -1 on failure
*
- * The behavior has been changed in NumPy 1.10.0, and the following
+ * The behavior has been changed in NumPy 1.16.0, and the following
* requirements must be fulfilled or an error will be raised:
* * Arguments, both input and output, must have at least as many
* dimensions as the corresponding number of core dimensions. In
- * previous versions, 1's were prepended to the shape as needed.
+ * versions before 1.10, 1's were prepended to the shape as needed.
* * Core dimensions with same labels must have exactly matching sizes.
- * In previous versions, core dimensions of size 1 would broadcast
+ * In versions before 1.10, core dimensions of size 1 would broadcast
* against other core dimensions with the same label.
* * All core dimensions must have their size specified by a passed in
- * input or output argument. In previous versions, core dimensions in
+ * input or output argument. In versions before 1.10, core dimensions in
* an output argument that were not specified in an input argument,
* and whose size could not be inferred from a passed in output
* argument, would have their size set to 1.
+ * * Core dimensions may be fixed, new in ufunc->version 1 (NumPy 1.16)
*/
static int
_get_coredim_sizes(PyUFuncObject *ufunc, PyArrayObject **op,
@@ -2223,9 +2224,16 @@ _get_coredim_sizes(PyUFuncObject *ufunc, PyArrayObject **op,
int nout = ufunc->nout;
int nop = nin + nout;
- for (j = 0; j < ufunc->core_num_dim_ix; ++j) {
- /* support fixed-size dim names */
- core_dim_sizes[j] = ufunc->core_dim_sizes[j];
+ if (ufunc->version > 0) {
+ for (j = 0; j < ufunc->core_num_dim_ix; ++j) {
+ /* support fixed-size dim names */
+ core_dim_sizes[j] = ufunc->core_dim_sizes[j];
+ }
+ }
+ else {
+ for (j = 0; j < ufunc->core_num_dim_ix; ++j) {
+ core_dim_sizes[j] = UFUNC_CORE_DIM_SIZE_UNSET;
+ }
}
for (i = 0; i < nop; ++i) {
if (op[i] != NULL) {
@@ -2421,9 +2429,17 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
dtypes[i] = NULL;
arr_prep[i] = NULL;
}
+
/* copy per-argument flags, so they can be changed */
- for (idim = 0; idim < ufunc->core_num_dim_ix; ++idim) {
- core_dim_flags[idim] = ufunc->core_dim_flags[idim];
+ if (ufunc->version > 0) {
+ for (idim = 0; idim < ufunc->core_num_dim_ix; ++idim) {
+ core_dim_flags[idim] = ufunc->core_dim_flags[idim];
+ }
+ }
+ else {
+ for (idim = 0; idim < ufunc->core_num_dim_ix; ++idim) {
+ core_dim_flags[idim] = 0;
+ }
}
NPY_UF_DBG_PRINT("Getting arguments\n");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment