-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
BUG: ufuncs that call into Python do not ignore FPEs e.g. for inf - inf
#21416
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
Comments
This is a bit confusing, because the warning seems to be caused by casadi and not NumPy, but casadi would ignore it (not the Python warning, but the CPU one). For example, code like this:
In that case The same seems to happen here, NumPy could work around this by storing and restoring those FPE (floating point exception) flags on the CPU. Considering that Python calls are not super fast, that would probably not be a problem. |
inf - inf
It appears you are right. Here is my reworked minimal code that generates the issue:
which generates
Just to understand the problem further,
So originally I labeled this issue as "Unexpected behavior of ndarray with dtype='O'". I guess after looking into it a bit it is consistent with numpy trying to be more precise about handling floating point numbers consistently with respect to the IEEE standard. The behavior still seems a little odd, though, because in this case the result isn't expected to be an int or a float, but just some object, which could be any valid python object. I have no idea whats going on inside casadi (the casadi module is a wrapper around C++ code), but in the test case above there's nothing that should generate a warning. Indeed, as far as I can tell, no floating point operation at all should be generated by the example, since the true_divide in |
The NumPy ufunc machinery does this, since it has to do it especially for Numeric arrays. In principle NumPy could skip doing it here (because it should never happen), but that wouldn't even the absolute correct fix. Using As for
Since |
I have tried a workaround for it: diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src
index 3a8a54913..7115ccd67 100644
--- a/numpy/core/src/umath/loops.c.src
+++ b/numpy/core/src/umath/loops.c.src
@@ -206,27 +206,37 @@ PyUFunc_FF_F_As_DD_D(char **args, npy_intp const *dimensions, npy_intp const *st
** GENERIC OBJECT lOOPS **
*****************************************************************************/
+#include <fenv.h>
+
/*UFUNC_API*/
NPY_NO_EXPORT void
PyUFunc_O_O(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)
{
+ fenv_t fenv;
+ fegetenv(&fenv);
+
unaryfunc f = (unaryfunc)func;
UNARY_LOOP {
PyObject *in1 = *(PyObject **)ip1;
PyObject **out = (PyObject **)op1;
PyObject *ret = f(in1 ? in1 : Py_None);
if (ret == NULL) {
+ fesetenv(&fenv);
return;
}
Py_XDECREF(*out);
*out = ret;
}
+ fesetenv(&fenv);
} In most cases, the speed difference is neglegible due to buffering. The most extreme case I found was:
Changing from
on the current version to:
with the EDIT: Of course the above is probably the most extreme case possible by a very far amount, so it may well be OK in practice! EDIT2: There may also be speedups available with some dance to avoid setting the flags when they are not used anyway? |
The following diff is only ~10% unless warnings are actually being silenced. So this is still slow: arr = np.full(10000000, fill_value=np.inf, dtype=object)
where=np.ones_like(arr, dtype=bool)
where[::2] = False
%timeit np.subtract(arr, arr, where=where) but checking errors is much faster than setting them, so doing it only if necessary really saves a lot of time. New improved diffdiff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src
index 3a8a54913..19e0f6314 100644
--- a/numpy/core/src/umath/loops.c.src
+++ b/numpy/core/src/umath/loops.c.src
@@ -206,21 +206,31 @@ PyUFunc_FF_F_As_DD_D(char **args, npy_intp const *dimensions, npy_intp const *st
** GENERIC OBJECT lOOPS **
*****************************************************************************/
+#include <fenv.h>
+
/*UFUNC_API*/
NPY_NO_EXPORT void
PyUFunc_O_O(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)
{
+ fexcept_t excepts, after;
+ fegetexceptflag(&excepts, FE_ALL_EXCEPT);
+
unaryfunc f = (unaryfunc)func;
UNARY_LOOP {
PyObject *in1 = *(PyObject **)ip1;
PyObject **out = (PyObject **)op1;
PyObject *ret = f(in1 ? in1 : Py_None);
if (ret == NULL) {
- return;
+ goto finish;
}
Py_XDECREF(*out);
*out = ret;
}
+ finish:
+ fegetexceptflag(&after, FE_ALL_EXCEPT);
+ if (after != excepts) {
+ fesetexceptflag(&excepts, FE_ALL_EXCEPT);
+ }
}
/*UFUNC_API*/
@@ -263,6 +273,9 @@ PyUFunc_O_O_method(char **args, npy_intp const *dimensions, npy_intp const *step
NPY_NO_EXPORT void
PyUFunc_OO_O(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)
{
+ fexcept_t excepts;
+ fegetexceptflag(&excepts, FE_ALL_EXCEPT);
+
binaryfunc f = (binaryfunc)func;
BINARY_LOOP {
PyObject *in1 = *(PyObject **)ip1;
@@ -270,11 +283,14 @@ PyUFunc_OO_O(char **args, npy_intp const *dimensions, npy_intp const *steps, voi
PyObject **out = (PyObject **)op1;
PyObject *ret = f(in1 ? in1 : Py_None, in2 ? in2 : Py_None);
if (ret == NULL) {
- return;
+ goto finish;
}
Py_XDECREF(*out);
*out = ret;
}
+
+ finish:
+ fesetexceptflag(&excepts, FE_ALL_EXCEPT);
}
NPY_NO_EXPORT void |
@seberg, thank you for this. Now that I understand the situation, I'm not sure whether this is a bug or not, and whether all Numpy users should see a slowdown. As you noted, there must be something going on in the casadi code that generates flags which casadi ignores. In my use case, the arrays are of modest size (maybe 10000 elements or fewer), and are operated on only a few times (order 10s). So the slowdown would be relatively unimportant for me. But for others, a 10% hit might be significant. |
I it a bug, but a very low priority one. Arguably, it almost never gets triggered (and if it does, like here, the triggering side is also doing something non-ideal). Doing the fix I said is a simple judgment call to me. Getting it 100% right is probably not going to happen (at least not soon), it is just too complex. Whether getting it more right here is worth it, I don't have a strong opinion on. In practice, the slowdown should not be noticable in the vast majority of cases and serious slowdowns will only happen in some extreme cases (errors require silencing on very small chunked/unbuffered inputs, which probably means the use of |
The reason I said it's maybe not a bug is this: if I'll leave it to the Numpy team to figure this out, but thanks again so much for the quick and informative responses. |
We discussed this today a bit in the triage meeting, and we agreed that it seems just not worth the hassle. The number of people running into this seems low to non-existing. Let me close the issue, if someone runs into the issue we can reconsider. I do think in principle it would be cleaner to try to clean it up, but the win just seems super small. |
I am not asking for any action on this, but I wanted to note a context where this effect came up for me. In the uncertainties package, there is a function that calls |
This PR is a follow up to qiskit-community#1070. It suppresses numpy warnings during uncertainties array creation in more places in the code. Something changed recently in the numpy code so that it generates warnings when it used to suppress them, in particular when using `numpy.vectorize` which wraps user-level Python code inside of numpy C code and seems to lose context about warning state. See numpy/numpy#21416 for more information.
This PR is a follow up to #1070. It suppresses numpy warnings during uncertainties array creation in more places in the code. Something changed recently in the numpy code so that it generates warnings when it used to suppress them, in particular when using `numpy.vectorize` which wraps user-level Python code inside of numpy C code and seems to lose context about warning state. See numpy/numpy#21416 for more information.
This PR is a follow up to qiskit-community#1070. It suppresses numpy warnings during uncertainties array creation in more places in the code. Something changed recently in the numpy code so that it generates warnings when it used to suppress them, in particular when using `numpy.vectorize` which wraps user-level Python code inside of numpy C code and seems to lose context about warning state. See numpy/numpy#21416 for more information.
Describe the issue:
I'm writing a program that uses the casadi module to automatically differentiate Python expressions. The program constructs a numpy ndarray (with dtype='O') of objects with the casadi SX data type. Say the array is called 'x'. When performing arithmetic operations on the array, sometimes a Numpy warning is generated. For example,
generates the warning
and
generates the warning
Despite the warning, the correct value is produced. However, the following code runs without warning:
My expectation is that
should run without a warning whenever y is a float or int, x is a numpy.ndarray with dtype='O', and y/x[i] can be calculated without warning for all values of i in range. But that is not what happens here. Curiously, it fails only when the integer or float is at least 2 **31, even though no such behavior exists in casadi (or at least not obviously).
The sample code below is the simplest code that demonstrates the issue. It seems that the line calculating y2 should never generate a warning if the line calculating y1 does not.
Reproduce the code example:
Error message:
NumPy/Python version information:
1.21.4 3.9.9 (main, Nov 30 2021, 10:15:14)
The text was updated successfully, but these errors were encountered: