Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

* Added support for boolean input in `dpnp.positive` and the `+` operator of `dpnp.ndarray` [#3128](https://github.com/IntelPython/dpnp/pull/3128)

### Changed

### Deprecated
Expand Down
12 changes: 0 additions & 12 deletions dpnp/dpnp_algo/dpnp_elementwise_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@
"DPNPUnaryTwoOutputsFunc",
"acceptance_fn_gcd_lcm",
"acceptance_fn_negative",
"acceptance_fn_positive",
"acceptance_fn_sign",
"acceptance_fn_subtract",
"resolve_weak_types_2nd_arg_int",
Expand Down Expand Up @@ -1386,17 +1385,6 @@ def acceptance_fn_negative(arg_dtype, buf_dt, res_dt, sycl_dev):
return True


def acceptance_fn_positive(arg_dtype, buf_dt, res_dt, sycl_dev):
# positive is not defined for boolean data type
if arg_dtype.char == "?":
raise TypeError(
"The `positive` function is not supported for inputs of data type "
"bool"
)
else:
return True


def acceptance_fn_sign(arg_dtype, buf_dt, res_dt, sycl_dev):
# sign is not defined for boolean data type
if arg_dtype.char == "?":
Expand Down
25 changes: 20 additions & 5 deletions dpnp/dpnp_iface_mathematical.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@
DPNPUnaryTwoOutputsFunc,
acceptance_fn_gcd_lcm,
acceptance_fn_negative,
acceptance_fn_positive,
acceptance_fn_sign,
acceptance_fn_subtract,
resolve_weak_types_2nd_arg_int,
Expand Down Expand Up @@ -267,6 +266,23 @@ def _gradient_num_diff_edges(
)


def _positive(src, dst, sycl_queue, depends=None):
"""Call positive kernel, using abs one for boolean input."""

# dpnp.tensor.positive has no boolean loop, abs is identity on bool
if src.dtype.char == "?":
return ti._abs(src, dst, sycl_queue, depends)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We recently had the issues with casting to and from bool with non-zero bytes and alignment with NumPy (#3055), will it be an issue here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it shouldn't be an issue.

The boolean input is dispatched to the abs kernel, and since #3055 every path AbsFunctor<bool> can take (contiguous sub-group, scalar fallback and strided) reads the input through normalize_bool, so any non-zero byte is treated as True and the result always holds a canonical 0x00/0x01 byte.

I also checked it with bytes [0, 1, 2, 255, 0x80, 0x10, 0, 3] viewed as bool, for contiguous, strided, reversed and in-place (out=) inputs, and the result bytes matched NumPy's in every case.

return ti._positive(src, dst, sycl_queue, depends)


def _positive_result_type(dtype):
"""Get the result data type of positive, including boolean input."""

if dtype.char == "?":
return ti._abs_result_type(dtype)
return ti._positive_result_type(dtype)


def _process_ediff1d_args(arg, arg_name, ary_dtype, ary_sycl_queue, usm_type):
"""Process the argument for ediff1d."""
if not dpnp.is_supported_array_type(arg):
Expand Down Expand Up @@ -3901,7 +3917,7 @@ def _check_nan_inf(val, val_dt):
Parameters
----------
x : {dpnp.ndarray, usm_ndarray}
Input array, expected to have a numeric data type.
Input array, expected to have a boolean or numeric data type.
out : {None, dpnp.ndarray, usm_ndarray, tuple of ndarray}, optional
Output array to populate.
Array must have the correct shape and the expected data type.
Expand Down Expand Up @@ -3951,10 +3967,9 @@ def _check_nan_inf(val, val_dt):

positive = DPNPUnaryFunc(
"positive",
ti._positive_result_type,
ti._positive,
_positive_result_type,
_positive,
_POSITIVE_DOCSTRING,
acceptance_fn=acceptance_fn_positive,
)


Expand Down
23 changes: 10 additions & 13 deletions dpnp/tests/test_mathematical.py
Original file line number Diff line number Diff line change
Expand Up @@ -2376,32 +2376,29 @@ def test_negative_boolean():
@pytest.mark.parametrize(
"data", [[[1.0, -1.0], [0.1, -0.1]], [-2, -1, 0, 1, 2]], ids=["2D", "1D"]
)
@pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True, no_bool=True))
@pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True))
def test_positive(data, dtype):
a = get_abs_array(data, dtype=dtype)
ia = dpnp.array(a)

# numpy < 2.6 raises TypeError for boolean input
if dtype == dpnp.bool and numpy_version() < "2.6.0.dev0":
expected = a.copy()
else:
expected = numpy.positive(a)

result = dpnp.positive(ia)
expected = numpy.positive(a)
assert_allclose(result, expected)
assert_dtype_allclose(result, expected)

result = +ia
expected = +a
assert_allclose(result, expected)
assert_dtype_allclose(result, expected)

# out keyword
if dtype is not None:
iout = dpnp.empty(expected.shape, dtype=dtype)
result = dpnp.positive(ia, out=iout)
assert result is iout
assert_allclose(result, expected)


def test_positive_boolean():
ia = dpnp.array([True, False])

with pytest.raises(TypeError):
dpnp.positive(ia)
assert_dtype_allclose(result, expected)


@pytest.mark.parametrize("dtype", get_float_dtypes(no_float16=False))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def test_pos_array(self, xp, dtype):
return +a

@pytest.mark.skip(
"dpnp boolean positive, the `+` operator, is not supported"
"dpnp boolean positive, the `+` operator, emits no DeprecationWarning"
)
@testing.with_requires("numpy<1.25")
def test_pos_boolarray(self):
Expand Down
Loading