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

Boolean operators behave differently when refactoring intermediate expression into variable #7640

Open
2 tasks done
daanmichiels opened this issue Dec 9, 2021 · 3 comments
Labels
bug - incorrect behavior Bugs: incorrect behavior

Comments

@daanmichiels
Copy link

  • I have tried using the latest released version of Numba (most recent is
    visible in the change log (https://github.com/numba/numba/blob/master/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'.

Tested using numba 0.54.1 (Windows 10, python 3.8, numpy 1.19.2).

import numba
import numpy as np

@numba.jit()
def foobar():
    a = np.array([True])
    b = np.array([True])
    a_times_b = a * b
    print(~(a * b))
    print(~a_times_b)

foobar()

Output:

[ True]
[False]

I was expecting both lines to be [False]. Without the @numba.jit(), this prints [False] on both lines.

My best guess was a mix-up between ~ as bitwise negation vs. boolean negation. Adding an explicit dtype=np.bool_ to the array constructors doesn't make a difference.

@esc esc added bug - incorrect behavior Bugs: incorrect behavior needtriage labels Dec 9, 2021
@esc
Copy link
Member

esc commented Dec 9, 2021

@daanmichiels thank you, I can confirm this most probably is a bug and I am able to reproduce this.

@stuartarchibald
Copy link
Contributor

Think it's a bug in the arrayexpr rewrites:

from numba import njit
import numpy as np

@njit(no_rewrites=True)
def foo():
    a = np.array([True])
    b = np.array([True])
    a_times_b = a * b
    x = ~(a * b)
    y = ~a_times_b
    return x, y

print(foo())

gives:

(array([False]), array([False]))

@stuartarchibald
Copy link
Contributor

I think this is down to a slightly convoluted case of "Numba aliases NumPy and CPython scalar types". Compiling just this:

@njit
def foo(a, b):
    x = ~(a * b)
    return x

a = np.array([True])
b = np.array([True])
print(foo(a, b))

and breaking just after here:

cres = context.compile_subroutine(builder, impl, inner_sig, flags=flags,
caching=False)

The ufunc that is compiled is based on byte code operating on scalars that looks like:

(Pdb) import dis
(Pdb) dis.dis(impl)
  6           0 LOAD_FAST                0 (a_1)
              2 LOAD_FAST                1 (b_1)

  1           4 BINARY_MULTIPLY
              6 UNARY_INVERT
              8 RETURN_VALUE

Numba will "see" a_1 and b_1 as CPython bool and type their multiplication as intp, because:

In [7]: True*True
Out[7]: 1

the operator.invert of intp type with value 1 is -2, this obviously has bits set so when casting back to a bool return type it leads to it being True. The LLVM IR optimises down to "return True":

; Function Attrs: nofree norecurse nounwind writeonly
define i32 @_ZN13_3cdynamic_3e37__numba_array_expr_0x7f3d8c0e3f40_242B70c8tJTIeFCjyCbUFRqqOAK_2f5h0ggn2oJ9DwwEtPiAqkREPZIAanTCJIogpmsCAA_3d_3dEbb(i8* noalias nocapture %retptr, { i8*, i32, i8* }** noalias nocapture readnone %excinfo, i8 %arg.a_1, i8 %arg.b_1) local_unnamed_addr #0 {
entry:
  store i8 1, i8* %retptr, align 1
  ret i32 0
}

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