Skip to content

Commit

Permalink
Inline boolean operators in NumPy are bitwise, not logical (#1057)
Browse files Browse the repository at this point in the history
* Inline boolean operators in NumPy are bitwise, not logical

* Add tests for inline operators
  • Loading branch information
manopapad authored Oct 4, 2023
1 parent 2e52081 commit 74490ef
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 53 deletions.
28 changes: 14 additions & 14 deletions cunumeric/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -817,9 +817,9 @@ def __and__(self, rhs: Any) -> ndarray:
Multiple GPUs, Multiple CPUs
"""
from ._ufunc import logical_and
from ._ufunc import bitwise_and

return logical_and(self, rhs)
return bitwise_and(self, rhs)

def __array__(
self, dtype: Union[np.dtype[Any], None] = None
Expand Down Expand Up @@ -1073,9 +1073,9 @@ def __iand__(self, rhs: Any) -> ndarray:
Multiple GPUs, Multiple CPUs
"""
from ._ufunc import logical_and
from ._ufunc import bitwise_and

return logical_and(self, rhs, out=self)
return bitwise_and(self, rhs, out=self)

def __idiv__(self, rhs: Any) -> ndarray:
"""a.__idiv__(value, /)
Expand Down Expand Up @@ -1186,9 +1186,9 @@ def __ior__(self, rhs: Any) -> ndarray:
Multiple GPUs, Multiple CPUs
"""
from ._ufunc import logical_or
from ._ufunc import bitwise_or

return logical_or(self, rhs, out=self)
return bitwise_or(self, rhs, out=self)

def __ipow__(self, rhs: float) -> ndarray:
"""a.__ipow__(/)
Expand Down Expand Up @@ -1260,9 +1260,9 @@ def __ixor__(self, rhs: Any) -> ndarray:
Multiple GPUs, Multiple CPUs
"""
from ._ufunc import logical_xor
from ._ufunc import bitwise_xor

return logical_xor(self, rhs, out=self)
return bitwise_xor(self, rhs, out=self)

def __le__(self, rhs: Any) -> ndarray:
"""a.__le__(value, /)
Expand Down Expand Up @@ -1416,9 +1416,9 @@ def __or__(self, rhs: Any) -> ndarray:
Multiple GPUs, Multiple CPUs
"""
from ._ufunc import logical_or
from ._ufunc import bitwise_or

return logical_or(self, rhs)
return bitwise_or(self, rhs)

def __pos__(self) -> ndarray:
"""a.__pos__(value, /)
Expand Down Expand Up @@ -1473,9 +1473,9 @@ def __rand__(self, lhs: Any) -> ndarray:
Multiple GPUs, Multiple CPUs
"""
from ._ufunc import logical_and
from ._ufunc import bitwise_and

return logical_and(lhs, self)
return bitwise_and(lhs, self)

def __rdiv__(self, lhs: Any) -> ndarray:
"""a.__rdiv__(value, /)
Expand Down Expand Up @@ -1584,9 +1584,9 @@ def __ror__(self, lhs: Any) -> ndarray:
Multiple GPUs, Multiple CPUs
"""
from ._ufunc import logical_or
from ._ufunc import bitwise_or

return logical_or(lhs, self)
return bitwise_or(lhs, self)

def __rpow__(self, lhs: Any) -> ndarray:
"""__rpow__(value, /)
Expand Down
69 changes: 52 additions & 17 deletions tests/integration/test_binary_ufunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,41 +41,68 @@ def check_result(op, in_np, out_np, out_num):


def check_ops(ops, in_np, out_dtype="D"):
in_num = tuple(num.array(arr) for arr in in_np)

for op in ops:
op_np = getattr(np, op)
op_num = getattr(num, op)
if op.isidentifier():
op_np = getattr(np, op)
op_num = getattr(num, op)
assert op_np.nout == 1

out_np = op_np(*in_np)
out_num = op_num(*in_num)

check_result(op, in_np, out_np, out_num)

assert op_np.nout == 1
out_np = np.empty(out_np.shape, dtype=out_dtype)
out_num = num.empty(out_num.shape, dtype=out_dtype)
op_np(*in_np, out=out_np)
op_num(*in_num, out=out_num)

in_num = tuple(num.array(arr) for arr in in_np)
check_result(op, in_np, out_np, out_num)

out_np = op_np(*in_np)
out_num = op_num(*in_num)
# Ask cuNumeric to produce outputs to NumPy ndarrays
out_num = np.empty(out_np.shape, dtype=out_dtype)
op_num(*in_num, out=out_num)

check_result(op, in_np, out_np, out_num)
check_result(op, in_np, out_np, out_num)

else:
# Doing it this way instead of invoking the dunders directly, to
# avoid having to select the right version, __add__ vs __radd__,
# when one isn't supported, e.g. for scalar.__add__(array)

out_np = np.empty(out_np.shape, dtype=out_dtype)
out_num = num.empty(out_num.shape, dtype=out_dtype)
out_np = eval(f"in_np[0] {op} in_np[1]")
out_num = eval(f"in_num[0] {op} in_num[1]")

op_np(*in_np, out=out_np)
op_num(*in_num, out=out_num)
check_result(op, in_np, out_np, out_num)

check_result(op, in_np, out_np, out_num)
out_np = np.ones_like(out_np)
out_num = num.ones_like(out_num)
exec(f"out_np {op}= in_np[0]")
exec(f"out_num {op}= in_num[0]")

# Ask cuNumeric to produce outputs to NumPy ndarrays
out_num = np.ones(out_np.shape, dtype=out_dtype)
op_num(*in_num, out_num)
check_result(op, in_np, out_np, out_num)

check_result(op, in_np, out_np, out_num)
out_num = np.ones_like(out_np)
exec(f"out_num {op}= in_num[0]")

check_result(op, in_np, out_np, out_num)


def test_all():
# TODO: right now we will simply check if the operations work
# for some boring inputs. For some of these, we will want to
# test corner cases in the future.

# TODO: matmul, @

# Math operations
ops = [
"*",
"+",
"-",
"/",
"add",
# "divmod",
"equal",
Expand Down Expand Up @@ -121,6 +148,7 @@ def test_all():
check_ops(ops, (scalar1, scalar2))

ops = [
"//",
"arctan2",
"copysign",
"floor_divide",
Expand All @@ -142,6 +170,7 @@ def test_all():
check_ops(ops, (scalar1, scalar2))

ops = [
"**",
"power",
"float_power",
]
Expand All @@ -159,6 +188,7 @@ def test_all():
check_ops(ops, (scalars[3], scalars[0]))

ops = [
"%",
"remainder",
]

Expand All @@ -173,12 +203,17 @@ def test_all():
check_ops(ops, (scalar1, scalar2))

ops = [
"&",
"<<",
">>",
"^",
"|",
"bitwise_and",
"bitwise_or",
"bitwise_xor",
"gcd",
"left_shift",
"lcm",
"left_shift",
"right_shift",
]

Expand Down
53 changes: 31 additions & 22 deletions tests/integration/test_unary_ufunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,39 +61,46 @@ def check_result(op, in_np, out_np, out_num, **isclose_kwargs):


def check_op(op, in_np, out_dtype="d", **check_kwargs):
op_np = getattr(np, op)
op_num = getattr(num, op)
in_num = num.array(in_np)

assert op_np.nout == 1
if op.isidentifier():
op_np = getattr(np, op)
op_num = getattr(num, op)

in_num = num.array(in_np)
assert op_np.nout == 1

out_np = op_np(in_np)
out_num = op_num(in_num)

out_np = op_np(in_np)
out_num = op_num(in_num)
assert check_result(op, in_np, out_np, out_num, **check_kwargs)

assert check_result(op, in_np, out_np, out_num, **check_kwargs)
out_np = np.empty(out_np.shape, dtype=out_dtype)
out_num = num.empty(out_num.shape, dtype=out_dtype)

out_np = np.empty(out_np.shape, dtype=out_dtype)
out_num = num.empty(out_num.shape, dtype=out_dtype)
op_np(in_np, out=out_np)
op_num(in_num, out=out_num)

op_np(in_np, out=out_np)
op_num(in_num, out=out_num)
assert check_result(op, in_np, out_np, out_num, **check_kwargs)

assert check_result(op, in_np, out_np, out_num, **check_kwargs)
out_np = np.empty(out_np.shape, dtype=out_dtype)
out_num = num.empty(out_num.shape, dtype=out_dtype)

out_np = np.empty(out_np.shape, dtype=out_dtype)
out_num = num.empty(out_num.shape, dtype=out_dtype)
op_np(in_np, out_np)
op_num(in_num, out_num)

op_np(in_np, out_np)
op_num(in_num, out_num)
assert check_result(op, in_np, out_np, out_num, **check_kwargs)

assert check_result(op, in_np, out_np, out_num, **check_kwargs)
# Ask cuNumeric to produce outputs to NumPy ndarrays
out_num = np.ones(out_np.shape, dtype=out_dtype)
op_num(in_num, out_num)

# Ask cuNumeric to produce outputs to NumPy ndarrays
out_num = np.ones(out_np.shape, dtype=out_dtype)
op_num(in_num, out_num)
assert check_result(op, in_np, out_np, out_num, **check_kwargs)

else:
out_np = eval(f"{op} in_np")
out_num = eval(f"{op} in_num")

assert check_result(op, in_np, out_np, out_num, **check_kwargs)
assert check_result(op, in_np, out_np, out_num, **check_kwargs)


def check_ops(ops, in_np, out_dtype="d"):
Expand Down Expand Up @@ -155,6 +162,8 @@ def check_math_ops(op, **kwargs):

# Math operations
math_ops = (
"+",
"-",
"absolute",
"conjugate",
"exp",
Expand Down Expand Up @@ -283,7 +292,7 @@ def test_arc_hyp_trig_ops(op):
check_op(op, np.array(np.random.uniform(low=1, high=5)))


bit_ops = ("invert",)
bit_ops = ("invert", "~")


@pytest.mark.parametrize("op", bit_ops)
Expand Down

0 comments on commit 74490ef

Please sign in to comment.