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

Add tests of reflected ufuncs and fix behavior of logical reflected ufuncs #10261

Merged
merged 5 commits into from
Feb 11, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion python/cudf/cudf/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -1000,7 +1000,25 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
if fname in binary_operations:
not_reflect = self is inputs[0]
other = inputs[not_reflect]
op = f"__{'' if not_reflect else 'r'}{binary_operations[fname]}__"

# These operators need to be mapped to their inverses when
# performing a reflected operation because no reflected version of
# the operators themselves exist.
ops_without_reflection = {
"gt": "lt",
"ge": "le",
"lt": "gt",
"le": "ge",
# ne and eq are symmetric
"ne": "ne",
"eq": "eq",
}

op = binary_operations[fname]
if op in ops_without_reflection and not not_reflect:
vyasr marked this conversation as resolved.
Show resolved Hide resolved
op = ops_without_reflection[op]
vyasr marked this conversation as resolved.
Show resolved Hide resolved
not_reflect = True
isVoid marked this conversation as resolved.
Show resolved Hide resolved
op = f"__{'' if not_reflect else 'r'}{op}__"

# pandas bitwise operations return bools if indexes are misaligned.
# TODO: Generalize for other types of Frames
Expand Down
35 changes: 28 additions & 7 deletions python/cudf/cudf/tests/test_array_ufunc.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Copyright (c) 2020-2022, NVIDIA CORPORATION.

import operator
from functools import reduce

Expand Down Expand Up @@ -98,17 +100,26 @@ def test_ufunc_series(ufunc, has_nulls, indexed):
raise


@pytest.mark.parametrize("ufunc", [np.add, np.greater, np.logical_and])
@pytest.mark.parametrize(
"ufunc", [np.add, np.greater, np.greater_equal, np.logical_and]
)
@pytest.mark.parametrize("has_nulls", [True, False])
@pytest.mark.parametrize("indexed", [True, False])
@pytest.mark.parametrize("type_", ["cupy", "numpy", "list"])
def test_binary_ufunc_series_array(ufunc, has_nulls, indexed, type_):
@pytest.mark.parametrize("reflect", [True, False])
def test_binary_ufunc_series_array(ufunc, has_nulls, indexed, type_, reflect):
fname = ufunc.__name__
if fname == "greater" and has_nulls:
if fname in ("greater", "greater_equal") and has_nulls:
pytest.xfail(
"The way cudf casts nans in arrays to nulls during binops with "
"cudf objects is currently incompatible with pandas."
)
if reflect and has_nulls and type_ == "cupy":
pytest.skip(
"When cupy is the left operand there is no way for us to avoid "
"calling its binary operators, which cannot handle cudf objects "
"that contain nulls."
)
N = 100
# Avoid zeros in either array to skip division by 0 errors. Also limit the
# scale to avoid issues with overflow, etc. We use ints because some
Expand Down Expand Up @@ -136,18 +147,28 @@ def test_binary_ufunc_series_array(ufunc, has_nulls, indexed, type_):
if type_ == "list":
arg1 = arg1.tolist()

got = ufunc(args[0], arg1)
expect = ufunc(args[0].to_pandas(), args[1].to_numpy())
if reflect:
got = ufunc(arg1, args[0])
expect = ufunc(args[1].to_numpy(), args[0].to_pandas())
else:
got = ufunc(args[0], arg1)
expect = ufunc(args[0].to_pandas(), args[1].to_numpy())

if ufunc.nout > 1:
for g, e in zip(got, expect):
if has_nulls:
e[mask] = np.nan
assert_eq(g, e)
if type_ == "cupy" and reflect:
assert (cp.asnumpy(g) == e).all()
else:
assert_eq(g, e)
else:
if has_nulls:
expect[mask] = np.nan
assert_eq(got, expect)
if type_ == "cupy" and reflect:
assert (cp.asnumpy(got) == expect).all()
else:
assert_eq(got, expect)


@pytest.mark.parametrize(
Expand Down