Skip to content
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

gcd, lcm, subtract, sign, and negative don't match numpy behavior with bool inputs #7922

Open
2 tasks done
eriknw opened this issue Mar 17, 2022 · 4 comments
Open
2 tasks done
Labels
bug - incorrect behavior Bugs: incorrect behavior

Comments

@eriknw
Copy link

eriknw commented Mar 17, 2022

Reporting a bug

  • I have tried using the latest released version of Numba (most recent is
    visible in the change log (https://github.com/numba/numba/blob/main/CHANGE_LOG).
  • I have included a self contained code sample to reproduce the problem.
    i.e. it's possible to run as 'python bug.py'.

I noticed that numba doesn't match NumPy's behavior for np.gcd, np.lcm, and np.sign with bool inputs. Notably, NumPy raises, but numba treats bools as ints:

>>> import numpy as np
>>> import numba
>>> @numba.njit
... def gcd(x, y):
...     return np.gcd(x, y)
...
>>> gcd(True, True)
1
>>> np.gcd(True, True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
numpy.core._exceptions._UFuncNoLoopError: ufunc 'gcd' did not contain a loop with signature matching types (dtype('bool'), dtype('bool')) -> dtype('bool')
>>> @numba.njit
... def sign(x):
...     return np.sign(x)
...
>>> sign(True)
1
>>> np.sign(True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
numpy.core._exceptions._UFuncNoLoopError: ufunc 'sign' did not contain a loop with signature matching types dtype('bool') -> dtype('bool')
>>> @numba.njit
... def lcm(x, y):
...     return np.lcm(x, y)
...
>>> lcm(True, True)
1
>>> np.lcm(True, True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
numpy.core._exceptions._UFuncNoLoopError: ufunc 'lcm' did not contain a loop with signature matching types (dtype('bool'), dtype('bool')) -> dtype('bool')

I can get by with the current behavior, but thought you would want to know.

@eriknw
Copy link
Author

eriknw commented Mar 17, 2022

Two more: subtract and negative

>>> @numba.njit
... def subtract(x, y):
...     return np.subtract(x, y)
...
>>> subtract(True, True)
0
>>> np.subtract(True, True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: numpy boolean subtract, the `-` operator, is not supported, use the bitwise_xor, the `^` operator, or the logical_xor function instead.

@eriknw eriknw changed the title gcd, lcm, and sign don't match numpy behavior with bool inputs gcd, lcm, subtract, sign, and negative don't match numpy behavior with bool inputs Mar 17, 2022
@stuartarchibald
Copy link
Contributor

Thanks for the report, I can reproduce.

@stuartarchibald stuartarchibald added the bug - incorrect behavior Bugs: incorrect behavior label Mar 18, 2022
@stuartarchibald
Copy link
Contributor

Think this might need some logic like:

            if not set([x.char for x in np_input_types]).intersection(set([x for x in ufunc_inputs])):
                continue

around here:

if found:
# Found: determine the Numba types for the loop's inputs and
# outputs.

i.e. once a loop has been found, require that at least one of the input types is present in the selected loop version. This may however be too restrictive.

@czgdp1807
Copy link
Contributor

I am able to reproduce the incorrect behaviour of Numba. In addition, I added your check as follows and I get the following output with sign example in the code provided by @eriknw.

diff

diff --git a/numba/np/numpy_support.py b/numba/np/numpy_support.py
index eea1b612d..27b1558cd 100644
--- a/numba/np/numpy_support.py
+++ b/numba/np/numpy_support.py
@@ -521,6 +521,13 @@ def ufunc_find_matching_loop(ufunc, arg_types):
         if found:
             # Found: determine the Numba types for the loop's inputs and
             # outputs.
+            np_input_types_set = set([x.char for x in np_input_types])
+            output_types_set = set([x for x in ufunc_inputs])
+            if not np_input_types_set.intersection(output_types_set):
+                print("found but not correct: ", [x.char for x in np_input_types], [x for x in ufu
nc_inputs])
+                continue
+            else:
+                print("found and correct: ", [x.char for x in np_input_types], [x for x in ufunc_i
nputs])
             try:
                 inputs = choose_types(input_types, ufunc_inputs)
                 outputs = choose_types(output_types, ufunc_outputs)
@@ -534,8 +541,10 @@ def ufunc_find_matching_loop(ufunc, arg_types):
             except errors.NumbaNotImplementedError:
                 # One of the selected dtypes isn't supported by Numba
                 # (e.g. float16), try other candidates
+                print("errored")
                 continue
             else:
+                print("returning, :", inputs, outputs, candidate)
                 return UFuncLoopSpec(inputs, outputs, candidate)
 
     return None

Output

found and correct:  ['d', 'd'] ['d', 'd']
returning, : [float64, float64] [float64] dd->d
found and correct:  ['d', 'd'] ['d', 'd']
returning, : [float64, float64] [float64] dd->d
found but not correct:  ['?'] ['b']
found but not correct:  ['?'] ['B']
found but not correct:  ['?'] ['h']
found but not correct:  ['?'] ['H']
found but not correct:  ['?'] ['i']
found but not correct:  ['?'] ['I']
found but not correct:  ['?'] ['l']
found but not correct:  ['?'] ['L']
found but not correct:  ['?'] ['q']
found but not correct:  ['?'] ['Q']
found but not correct:  ['?'] ['f']
found but not correct:  ['?'] ['d']
found but not correct:  ['?'] ['g']
found but not correct:  ['?'] ['F']
found but not correct:  ['?'] ['D']
found but not correct:  ['?'] ['G']
found but not correct:  ['?'] ['b']
found but not correct:  ['?'] ['B']
found but not correct:  ['?'] ['h']
found but not correct:  ['?'] ['H']
found but not correct:  ['?'] ['i']
found but not correct:  ['?'] ['I']
found but not correct:  ['?'] ['l']
found but not correct:  ['?'] ['L']
found but not correct:  ['?'] ['q']
found but not correct:  ['?'] ['Q']
found but not correct:  ['?'] ['f']
found but not correct:  ['?'] ['d']
found but not correct:  ['?'] ['g']
found but not correct:  ['?'] ['F']
found but not correct:  ['?'] ['D']
found but not correct:  ['?'] ['G']
found but not correct:  ['?'] ['b']
found but not correct:  ['?'] ['B']
found but not correct:  ['?'] ['h']
found but not correct:  ['?'] ['H']
found but not correct:  ['?'] ['i']
found but not correct:  ['?'] ['I']
found but not correct:  ['?'] ['l']
found but not correct:  ['?'] ['L']
found but not correct:  ['?'] ['q']
found but not correct:  ['?'] ['Q']
found but not correct:  ['?'] ['f']
found but not correct:  ['?'] ['d']
found but not correct:  ['?'] ['g']
found but not correct:  ['?'] ['F']
found but not correct:  ['?'] ['D']
found but not correct:  ['?'] ['G']
found but not correct:  ['?'] ['b']
found but not correct:  ['?'] ['B']
found but not correct:  ['?'] ['h']
found but not correct:  ['?'] ['H']
found but not correct:  ['?'] ['i']
found but not correct:  ['?'] ['I']
found but not correct:  ['?'] ['l']
found but not correct:  ['?'] ['L']
found but not correct:  ['?'] ['q']
found but not correct:  ['?'] ['Q']
found but not correct:  ['?'] ['f']
found but not correct:  ['?'] ['d']
found but not correct:  ['?'] ['g']
found but not correct:  ['?'] ['F']
found but not correct:  ['?'] ['D']
found but not correct:  ['?'] ['G']
Traceback (most recent call last):
  File "../debug.py", line 320, in <module>
    test_np_gcd_lcm_sub()
  File "../debug.py", line 310, in test_np_gcd_lcm_sub
    print(sign(True))
  File "/Users/czgdp1807/numba_project/numba/numba/core/dispatcher.py", line 468, in _compile_for_args
    error_rewrite(e, 'typing')
  File "/Users/czgdp1807/numba_project/numba/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(<ufunc 'sign'>) found for signature:
 
 >>> sign(bool)
 
There are 2 candidate implementations:
  - Of which 2 did not match due to:
  Overload in function 'Numpy_rules_ufunc.generic': File: numba/core/typing/npydecl.py: Line 98.
    With argument(s): '(bool)':
   Rejected as the implementation raised a specific error:
     TypingError: can't resolve ufunc sign for types [bool]
  raised from /Users/czgdp1807/numba_project/numba/numba/core/typing/npydecl.py:107

During: resolving callee type: Function(<ufunc 'sign'>)
During: typing of call at ../debug.py (308)


File "../debug.py", line 308:
    def sign(x):
        return np.sign(x)
        ^

I have a couple of questions from the output,

  1. What is meant by np_input_types and ufunc_types? As far as I can figure out, np_input_types is the actual type of the input provided by the user, here True and ufunc_types is made available from keys of ufunc_db. Am I right?
  2. Why returning:, happens multiple times. I noticed that the return UFuncLoopSpec(inputs, outputs, candidate) gets executed multiple times. I am unable to figure out, why?

Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug - incorrect behavior Bugs: incorrect behavior
Projects
None yet
Development

No branches or pull requests

3 participants