-
-
Notifications
You must be signed in to change notification settings - Fork 10.9k
MAINT: Removed duplicated code around ufunc->identity
#8952
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -70,16 +70,6 @@ | |
static int | ||
_does_loop_use_arrays(void *data); | ||
|
||
static int | ||
assign_reduce_identity_zero(PyArrayObject *result, void *data); | ||
|
||
static int | ||
assign_reduce_identity_minusone(PyArrayObject *result, void *data); | ||
|
||
static int | ||
assign_reduce_identity_one(PyArrayObject *result, void *data); | ||
|
||
|
||
/*UFUNC_API*/ | ||
NPY_NO_EXPORT int | ||
PyUFunc_getfperr(void) | ||
|
@@ -1847,6 +1837,42 @@ _get_coredim_sizes(PyUFuncObject *ufunc, PyArrayObject **op, | |
return 0; | ||
} | ||
|
||
/* | ||
* Returns a new reference | ||
* TODO: store a reference in the ufunc object itself, rather than | ||
* constructing one each time | ||
*/ | ||
static PyObject * | ||
_get_identity(PyUFuncObject *ufunc, npy_bool *reorderable) { | ||
switch(ufunc->identity) { | ||
case PyUFunc_One: | ||
*reorderable = 1; | ||
return PyInt_FromLong(1); | ||
|
||
case PyUFunc_Zero: | ||
*reorderable = 1; | ||
return PyInt_FromLong(0); | ||
|
||
case PyUFunc_MinusOne: | ||
*reorderable = 1; | ||
return PyInt_FromLong(-1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previously, we cached these values in a static variable. Is this something we should keep doing? |
||
|
||
case PyUFunc_ReorderableNone: | ||
*reorderable = 1; | ||
Py_RETURN_NONE; | ||
|
||
case PyUFunc_None: | ||
*reorderable = 0; | ||
Py_RETURN_NONE; | ||
|
||
default: | ||
PyErr_Format(PyExc_ValueError, | ||
"ufunc %s has an invalid identity", ufunc_get_name_cstr(ufunc)); | ||
return NULL; | ||
} | ||
} | ||
|
||
|
||
static int | ||
PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, | ||
PyObject *args, PyObject *kwds, | ||
|
@@ -2249,34 +2275,27 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, | |
* product of two zero-length arrays will be a scalar, | ||
* which has size one. | ||
*/ | ||
npy_bool reorderable; | ||
PyObject *identity = _get_identity(ufunc, &reorderable); | ||
if (identity == NULL) { | ||
retval = -1; | ||
goto fail; | ||
} | ||
|
||
for (i = nin; i < nop; ++i) { | ||
if (PyArray_SIZE(op[i]) != 0) { | ||
switch (ufunc->identity) { | ||
case PyUFunc_Zero: | ||
assign_reduce_identity_zero(op[i], NULL); | ||
break; | ||
case PyUFunc_One: | ||
assign_reduce_identity_one(op[i], NULL); | ||
break; | ||
case PyUFunc_MinusOne: | ||
assign_reduce_identity_minusone(op[i], NULL); | ||
break; | ||
case PyUFunc_None: | ||
case PyUFunc_ReorderableNone: | ||
PyErr_Format(PyExc_ValueError, | ||
"ufunc %s ", | ||
ufunc_name); | ||
retval = -1; | ||
goto fail; | ||
default: | ||
PyErr_Format(PyExc_ValueError, | ||
"ufunc %s has an invalid identity for reduction", | ||
ufunc_name); | ||
retval = -1; | ||
goto fail; | ||
if (identity == Py_None) { | ||
PyErr_Format(PyExc_ValueError, | ||
"ufunc %s ", | ||
ufunc_name); | ||
Py_DECREF(identity); | ||
retval = -1; | ||
goto fail; | ||
} | ||
PyArray_FillWithScalar(op[i], identity); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
} | ||
Py_DECREF(identity); | ||
} | ||
|
||
/* Check whether any errors occurred during the loop */ | ||
|
@@ -2665,31 +2684,6 @@ reduce_type_resolver(PyUFuncObject *ufunc, PyArrayObject *arr, | |
return 0; | ||
} | ||
|
||
static int | ||
assign_reduce_identity_zero(PyArrayObject *result, void *NPY_UNUSED(data)) | ||
{ | ||
return PyArray_FillWithScalar(result, PyArrayScalar_False); | ||
} | ||
|
||
static int | ||
assign_reduce_identity_one(PyArrayObject *result, void *NPY_UNUSED(data)) | ||
{ | ||
return PyArray_FillWithScalar(result, PyArrayScalar_True); | ||
} | ||
|
||
static int | ||
assign_reduce_identity_minusone(PyArrayObject *result, void *NPY_UNUSED(data)) | ||
{ | ||
static PyObject *MinusOne = NULL; | ||
|
||
if (MinusOne == NULL) { | ||
if ((MinusOne = PyInt_FromLong(-1)) == NULL) { | ||
return -1; | ||
} | ||
} | ||
return PyArray_FillWithScalar(result, MinusOne); | ||
} | ||
|
||
static int | ||
reduce_loop(NpyIter *iter, char **dataptrs, npy_intp *strides, | ||
npy_intp *countptr, NpyIter_IterNextFunc *iternext, | ||
|
@@ -2795,11 +2789,12 @@ static PyArrayObject * | |
PyUFunc_Reduce(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, | ||
int naxes, int *axes, PyArray_Descr *odtype, int keepdims) | ||
{ | ||
int iaxes, reorderable, ndim; | ||
int iaxes, ndim; | ||
npy_bool reorderable; | ||
npy_bool axis_flags[NPY_MAXDIMS]; | ||
PyArray_Descr *dtype; | ||
PyArrayObject *result; | ||
PyArray_AssignReduceIdentityFunc *assign_identity = NULL; | ||
PyObject *identity = NULL; | ||
const char *ufunc_name = ufunc_get_name_cstr(ufunc); | ||
/* These parameters come from a TLS global */ | ||
int buffersize = 0, errormask = 0; | ||
|
@@ -2820,72 +2815,41 @@ PyUFunc_Reduce(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, | |
axis_flags[axis] = 1; | ||
} | ||
|
||
switch (ufunc->identity) { | ||
case PyUFunc_Zero: | ||
assign_identity = &assign_reduce_identity_zero; | ||
reorderable = 1; | ||
/* | ||
* The identity for a dynamic dtype like | ||
* object arrays can't be used in general | ||
*/ | ||
if (PyArray_ISOBJECT(arr) && PyArray_SIZE(arr) != 0) { | ||
assign_identity = NULL; | ||
} | ||
break; | ||
case PyUFunc_One: | ||
assign_identity = &assign_reduce_identity_one; | ||
reorderable = 1; | ||
/* | ||
* The identity for a dynamic dtype like | ||
* object arrays can't be used in general | ||
*/ | ||
if (PyArray_ISOBJECT(arr) && PyArray_SIZE(arr) != 0) { | ||
assign_identity = NULL; | ||
} | ||
break; | ||
case PyUFunc_MinusOne: | ||
assign_identity = &assign_reduce_identity_minusone; | ||
reorderable = 1; | ||
/* | ||
* The identity for a dynamic dtype like | ||
* object arrays can't be used in general | ||
*/ | ||
if (PyArray_ISOBJECT(arr) && PyArray_SIZE(arr) != 0) { | ||
assign_identity = NULL; | ||
} | ||
break; | ||
|
||
case PyUFunc_None: | ||
reorderable = 0; | ||
break; | ||
case PyUFunc_ReorderableNone: | ||
reorderable = 1; | ||
break; | ||
default: | ||
PyErr_Format(PyExc_ValueError, | ||
"ufunc %s has an invalid identity for reduction", | ||
ufunc_name); | ||
return NULL; | ||
if (_get_bufsize_errmask(NULL, "reduce", &buffersize, &errormask) < 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moving this check to occur first makes cleanup a little easier. |
||
return NULL; | ||
} | ||
|
||
if (_get_bufsize_errmask(NULL, "reduce", &buffersize, &errormask) < 0) { | ||
/* Get the identity */ | ||
identity = _get_identity(ufunc, &reorderable); | ||
if (identity == NULL) { | ||
return NULL; | ||
} | ||
/* | ||
* The identity for a dynamic dtype like | ||
* object arrays can't be used in general | ||
*/ | ||
if (identity != Py_None && PyArray_ISOBJECT(arr) && PyArray_SIZE(arr) != 0) { | ||
Py_DECREF(identity); | ||
identity = Py_None; | ||
Py_INCREF(identity); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this behaviour is justified, but this is a maintenance commit, so for now, we should leave it in |
||
|
||
/* Get the reduction dtype */ | ||
if (reduce_type_resolver(ufunc, arr, odtype, &dtype) < 0) { | ||
Py_DECREF(identity); | ||
return NULL; | ||
} | ||
|
||
result = PyUFunc_ReduceWrapper(arr, out, NULL, dtype, dtype, | ||
NPY_UNSAFE_CASTING, | ||
axis_flags, reorderable, | ||
keepdims, 0, | ||
assign_identity, | ||
identity, | ||
reduce_loop, | ||
ufunc, buffersize, ufunc_name, errormask); | ||
|
||
Py_DECREF(dtype); | ||
Py_DECREF(identity); | ||
return result; | ||
} | ||
|
||
|
@@ -5377,15 +5341,8 @@ ufunc_get_name(PyUFuncObject *ufunc) | |
static PyObject * | ||
ufunc_get_identity(PyUFuncObject *ufunc) | ||
{ | ||
switch(ufunc->identity) { | ||
case PyUFunc_One: | ||
return PyInt_FromLong(1); | ||
case PyUFunc_Zero: | ||
return PyInt_FromLong(0); | ||
case PyUFunc_MinusOne: | ||
return PyInt_FromLong(-1); | ||
} | ||
Py_RETURN_NONE; | ||
npy_bool reorderable; | ||
return _get_identity(ufunc, &reorderable); | ||
} | ||
|
||
static PyObject * | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This used to be
True
when used internally, and1
when viewed externally.The upshot is that this fixes #8860.
Ideally, we'd use
True
for functions likelogical_or
, and1
for functions likeadd