-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Fix broken np.allclose, add np.isclose #8857
Conversation
93e8f77
to
7ca086f
Compare
Here is a reproduction… lericson@strixy:~$ ~/.pyenv/versions/3.10.6/bin/python3 -m venv tmp_env
lericson@strixy:~$ cd tmp_env
lericson@strixy:~/tmp_env$ ./bin/pip install numba
…
Successfully installed llvmlite-0.39.1 numba-0.56.4 numpy-1.23.5
lericson@strixy:~/tmp_env$ ./bin/python3
Python 3.10.6 (main, Aug 22 2022, 12:13:43) [GCC 7.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information. Scalar-scalar case >>> from numba import njit
>>> @njit
... def f(a, b):
... return np.allclose(a, b)
...
>>> import numpy as np
>>> f(0.0, 1.0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/lericson/tmp_env/lib/python3.10/site-packages/numba/core/dispatcher.py", line 468, in _compile_for_args
error_rewrite(e, 'typing')
File "/home/lericson/tmp_env/lib/python3.10/site-packages/numba/core/dispatcher.py", line 409, in error_rewrite
raise e.with_traceback(None)
numba.core.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
No implementation of function Function(<function allclose at 0x7f5bba179c60>) found for signature:
>>> allclose(float64, float64)
There are 2 candidate implementations:
- Of which 2 did not match due to:
Overload in function 'np_allclose': File: numba/np/arraymath.py: Line 894.
With argument(s): '(float64, float64)':
Rejected as the implementation raised a specific error:
TypeError: The third argument "rtol" must be a floating point
raised from /home/lericson/tmp_env/lib/python3.10/site-packages/numba/np/arraymath.py:905
During: resolving callee type: Function(<function allclose at 0x7f5bba179c60>)
During: typing of call at <stdin> (3)
File "<stdin>", line 3:
<source missing, REPL/exec in use?> Array-array case >>> f(np.array([0.0]), np.array([1.0]))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/lericson/tmp_env/lib/python3.10/site-packages/numba/core/dispatcher.py", line 468, in _compile_for_args
error_rewrite(e, 'typing')
File "/home/lericson/tmp_env/lib/python3.10/site-packages/numba/core/dispatcher.py", line 409, in error_rewrite
raise e.with_traceback(None)
numba.core.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
No implementation of function Function(<function allclose at 0x7f5bba179c60>) found for signature:
>>> allclose(array(float64, 1d, C), array(float64, 1d, C))
There are 2 candidate implementations:
- Of which 2 did not match due to:
Overload in function 'np_allclose': File: numba/np/arraymath.py: Line 894.
With argument(s): '(array(float64, 1d, C), array(float64, 1d, C))':
Rejected as the implementation raised a specific error:
TypeError: The third argument "rtol" must be a floating point
raised from /home/lericson/tmp_env/lib/python3.10/site-packages/numba/np/arraymath.py:905
During: resolving callee type: Function(<function allclose at 0x7f5bba179c60>)
During: typing of call at <stdin> (3)
File "<stdin>", line 3:
<source missing, REPL/exec in use?>
>>> |
Note that the PR you xref'd seems to reimplement allclose also, maybe the PR is out of date. Numba has np.allclose already, it just doesn't work at all. |
Having just updated the release notes for the upcoming 0.57 release, I noticed that |
@overload(np.isclose) | ||
def np_isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False): |
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 file already contains an implementation of np.isclose
in line 1191. Could you update the existing implementation? Or remove it in favor of this one?
@guilhermeleobas I think the existing implementation is just as good, let's ignore this PR since the issue seems resolved. As for #8860, it is also likely fixed with the next release. Thanks for a great project! |
Actually on closer inspection, the code in master should be rewritten to reuse its >>> import numpy as np
>>> a = np.arange(10)[:, None]
>>> b = np.arange(10)[None, :]
>>> ab, ba = np.broadcast_arrays(a, b) The current implementation uses >>> ab.shape
(10, 10)
>>> ab.strides
(8, 0)
>>> ab_flat = ab.reshape(-1)
>>> ab_flat.shape
(100,)
>>> ab_flat.strides
(8,)
>>> ab.base.nbytes
80
>>> ab_flat.base.nbytes
800 |
Any chance you could replace the existing implementation by yours? |
Certainly, just give me a little time- LudvigOn 30 Mar 2023, at 15:48, Guilherme Leobas ***@***.***> wrote:
It may actually be preferable to use my code instead, as I use np.nditer
Any chance you could replace the existing implementation by yours?
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you modified the open/close state.Message ID: ***@***.***>
|
I suggest deprecating |
It was previously impossible to use `np.allclose` as the default arguments `rtol`, `atol`, and ´equal_nan` had types `float` and `bool`, whereas the code was testing for the Numba types `Float` and `Boolean`. Also replaced `np.isclose`, the element-wise equivalent of `np.allclose`. It is also debatable whether `allclose` should be an array method; it is not in numpy.
numba/np/arraymath.py
Outdated
cv[()] = _isclose_scalars(av.item(), bv.item(), rtol=rtol, | ||
atol=atol, equal_nan=equal_nan) | ||
|
||
return c_c |
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.
By the way, wouldn't this variant work for all array-scalar combinations?
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.
In fact I think this works for scalar-scalar also. At least in non-numba Numpy.
I tested it and this version uses way less code, and does exactly the same thing. |
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.
Hi @lericson, there's currently an error on CI regarding using np.broadcast_arrays
with arrays with different dtype. This is currently not supported in Numba as it is not possible to infer the output list dtype.
numba/np/arraymath.py
Outdated
@overload(np.isclose) | ||
def np_isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False): | ||
def impl(a, b, rtol=1e-05, atol=1e-08, equal_nan=False): | ||
ab, ba = np.broadcast_arrays(a, b) |
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.
rather than calling np.broadcast_arrays
, compute the final shape with np.broadcast_shapes
and call np.broadcast_to
in each array individually. This might solve the current CI issue.
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.
Interesting. I have changed the code. Pushing in a moment.
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.
Hold on, why can't broadcast_arrays
just return a Tuple(ArrayType1, ArrayType2)
?
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.
I would also note that np.nditer
doesn't work quite the same as the true numpy version; my use of it here seems to fail. I have to manually broadcast the arrays.
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.
Hold on, why can't broadcast_arrays just return a Tuple(ArrayType1, ArrayType2)?
We follow NumPy spec where np.broadcast_arrays
returns a list.
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.
Hi @lericson, thanks for working on these changes.
Current changes are almost 2x slower (np.allclose
) than previous code:
$ ./runtests.py -f -v numba.tests.test_np_functions -k allclose
test_allclose (numba.tests.test_np_functions.TestNPFunctions) ... ok
test_allclose_exception (numba.tests.test_np_functions.TestNPFunctions) ... ok
test_ip_allclose_numpy (numba.tests.test_np_functions.TestNPFunctions) ... ok
test_ip_not_allclose_numpy (numba.tests.test_np_functions.TestNPFunctions) ... ok
----------------------------------------------------------------------
Ran 4 tests in 3.802s
$ time ./runtests.py -f -v numba.tests.test_np_functions -k isclose
test_isclose (numba.tests.test_np_functions.TestNPFunctions) ... /Users/guilhermeleobas/git/numba/numba/tests/test_np_functions.py:135: NumbaExperimentalFeatureWarning: Use of isinstance() detected. This is an experimental feature.
return np.isclose(a, b, rtol, atol, equal_nan)
/Users/guilhermeleobas/git/numba/numba/tests/test_np_functions.py:135: NumbaExperimentalFeatureWarning: Use of isinstance() detected. This is an experimental feature.
return np.isclose(a, b, rtol, atol, equal_nan)
ok
----------------------------------------------------------------------
Ran 1 test in 4.077s
$ ./runtests.py -f -v numba.tests.test_np_functions -k allclose
test_allclose (numba.tests.test_np_functions.TestNPFunctions) ... /Users/guilhermeleobas/git/numba/numba/tests/test_np_functions.py:343: NumbaExperimentalFeatureWarning: Use of isinstance() detected. This is an experimental feature.
return np.allclose(a, b, rtol, atol, equal_nan)
/Users/guilhermeleobas/git/numba/numba/tests/test_np_functions.py:343: NumbaExperimentalFeatureWarning: Use of isinstance() detected. This is an experimental feature.
return np.allclose(a, b, rtol, atol, equal_nan)
ok
test_allclose_exception (numba.tests.test_np_functions.TestNPFunctions) ... ok
test_ip_allclose_numpy (numba.tests.test_np_functions.TestNPFunctions) ... /Users/guilhermeleobas/git/numba/numba/tests/test_np_functions.py:343: NumbaExperimentalFeatureWarning: Use of isinstance() detected. This is an experimental feature.
return np.allclose(a, b, rtol, atol, equal_nan)
ok
test_ip_not_allclose_numpy (numba.tests.test_np_functions.TestNPFunctions) ... ok
----------------------------------------------------------------------
Ran 4 tests in 7.379s
$ ./runtests.py -f -v numba.tests.test_np_functions -k isclose
test_isclose (numba.tests.test_np_functions.TestNPFunctions) ... /Users/guilhermeleobas/git/numba/numba/tests/test_np_functions.py:135: NumbaExperimentalFeatureWarning: Use of isinstance() detected. This is an experimental feature.
return np.isclose(a, b, rtol, atol, equal_nan)
/Users/guilhermeleobas/git/numba/numba/tests/test_np_functions.py:135: NumbaExperimentalFeatureWarning: Use of isinstance() detected. This is an experimental feature.
return np.isclose(a, b, rtol, atol, equal_nan)
/Users/guilhermeleobas/git/numba/numba/tests/test_np_functions.py:135: NumbaExperimentalFeatureWarning: Use of isinstance() detected. This is an experimental feature.
return np.isclose(a, b, rtol, atol, equal_nan)
ok
----------------------------------------------------------------------
Ran 1 test in 5.415s
It's not necessary to create and broadcast an array for scalar items, as it can be an expensive operation in terms of both compilation and execution. Could you investigate this a bit further?
if not type_can_asarray(a): | ||
raise TypeError('The first argument "a" must be array-like') | ||
|
||
if not type_can_asarray(b): | ||
raise TypeError('The second argument "b" must be array-like') | ||
|
||
if not isinstance(rtol, types.Float): | ||
raise TypeError('The third argument "rtol" must be a ' | ||
'floating point') | ||
|
||
if not isinstance(atol, types.Float): | ||
raise TypingError('The fourth argument "atol" must be a ' | ||
'floating point') | ||
|
||
if not isinstance(equal_nan, types.Boolean): | ||
raise TypeError('The fifth argument "equal_nan" must be a ' | ||
'boolean') |
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.
When np.allclose
is used with unsupported types, these exceptions provide a useful error message. Could we keep them?
if not (type_can_asarray(a) and type_can_asarray(b)): | ||
raise TypingError("Inputs for `np.isclose` must be array-like.") |
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.
Same thing here. Having the exception has its upsides.
@lericson could you add a couple of tests for the case you reported in your first comment?
|
Good luck! |
It was previously impossible to use
np.allclose
as the default argumentsrtol
,atol
, andequal_nan
had typesfloat
andbool
, whereas the code was testing for the Numba typesFloat
andBoolean
.Also there was no
np.isclose
, the element-wise equivalent ofnp.allclose
.It is also debatable whether
allclose
should be an array method; it is not in numpy. I have not madeisclose
a method.