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

Reuse dpctl.tensor.isnan(), dpctl.tensor.isinf(), and dpctl.tensor.isfinite() functions. #1504

Merged
merged 8 commits into from
Aug 1, 2023
48 changes: 0 additions & 48 deletions dpnp/dpnp_algo/dpnp_algo_logic.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ __all__ += [
"dpnp_allclose",
"dpnp_any",
"dpnp_isclose",
"dpnp_isfinite",
"dpnp_isinf",
"dpnp_isnan",
]


Expand Down Expand Up @@ -174,48 +171,3 @@ cpdef utils.dpnp_descriptor dpnp_isclose(utils.dpnp_descriptor input1,
result.get_pyobj()[i] = numpy.isclose(input1.get_pyobj()[i], input2.get_pyobj()[i], rtol, atol, equal_nan)

return result


cpdef utils.dpnp_descriptor dpnp_isfinite(utils.dpnp_descriptor input1):
input1_obj = input1.get_array()
cdef utils.dpnp_descriptor result = utils_py.create_output_descriptor_py(input1.shape,
dpnp.bool,
None,
device=input1_obj.sycl_device,
usm_type=input1_obj.usm_type,
sycl_queue=input1_obj.sycl_queue)

for i in range(result.size):
result.get_pyobj()[i] = numpy.isfinite(input1.get_pyobj()[i])

return result


cpdef utils.dpnp_descriptor dpnp_isinf(utils.dpnp_descriptor input1):
input1_obj = input1.get_array()
cdef utils.dpnp_descriptor result = utils_py.create_output_descriptor_py(input1.shape,
dpnp.bool,
None,
device=input1_obj.sycl_device,
usm_type=input1_obj.usm_type,
sycl_queue=input1_obj.sycl_queue)

for i in range(result.size):
result.get_pyobj()[i] = numpy.isinf(input1.get_pyobj()[i])

return result


cpdef utils.dpnp_descriptor dpnp_isnan(utils.dpnp_descriptor input1):
input1_obj = input1.get_array()
cdef utils.dpnp_descriptor result = utils_py.create_output_descriptor_py(input1.shape,
dpnp.bool,
None,
device=input1_obj.sycl_device,
usm_type=input1_obj.usm_type,
sycl_queue=input1_obj.sycl_queue)

for i in range(result.size):
result.get_pyobj()[i] = numpy.isnan(input1.get_pyobj()[i])

return result
109 changes: 109 additions & 0 deletions dpnp/dpnp_algo/dpnp_elementwise_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
"dpnp_floor_divide",
"dpnp_greater",
"dpnp_greater_equal",
"dpnp_isfinite",
"dpnp_isinf",
"dpnp_isnan",
"dpnp_less",
"dpnp_less_equal",
"dpnp_log",
Expand Down Expand Up @@ -466,6 +469,112 @@ def dpnp_greater_equal(x1, x2, out=None, order="K"):
return dpnp_array._create_from_usm_ndarray(res_usm)


_isfinite_docstring = """
isfinite(x, out=None, order="K")

Checks if each element of input array is a finite number.

Args:
x (dpnp.ndarray):
Input array, expected to have numeric data type.
out ({None, dpnp.ndarray}, optional):
Output array to populate.
Array have the correct shape and the expected data type.
order ("C","F","A","K", optional):
Memory layout of the newly output array, if parameter `out` is `None`.
Default: "K".
Returns:
dpnp.ndarray:
An array which is True where `x` is not positive infinity,
negative infinity, or NaN, False otherwise.
The data type of the returned array is `bool`.
"""


def dpnp_isfinite(x, out=None, order="K"):
"""Invokes isfinite() from dpctl.tensor implementation for isfinite() function."""

# dpctl.tensor only works with usm_ndarray
x1_usm = dpnp.get_usm_ndarray(x)
out_usm = None if out is None else dpnp.get_usm_ndarray(out)

func = UnaryElementwiseFunc(
"isfinite", ti._isfinite_result_type, ti._isfinite, _isfinite_docstring
)
res_usm = func(x1_usm, out=out_usm, order=order)
return dpnp_array._create_from_usm_ndarray(res_usm)


_isinf_docstring = """
isinf(x, out=None, order="K")

Checks if each element of input array is an infinity.

Args:
x (dpnp.ndarray):
Input array, expected to have numeric data type.
out ({None, dpnp.ndarray}, optional):
Output array to populate.
Array have the correct shape and the expected data type.
order ("C","F","A","K", optional):
Memory layout of the newly output array, if parameter `out` is `None`.
Default: "K".
Returns:
dpnp.ndarray:
An array which is True where `x` is positive or negative infinity,
False otherwise. The data type of the returned array is `bool`.
"""


def dpnp_isinf(x, out=None, order="K"):
"""Invokes isinf() from dpctl.tensor implementation for isinf() function."""

# dpctl.tensor only works with usm_ndarray
x1_usm = dpnp.get_usm_ndarray(x)
out_usm = None if out is None else dpnp.get_usm_ndarray(out)

func = UnaryElementwiseFunc(
"isinf", ti._isinf_result_type, ti._isinf, _isinf_docstring
)
res_usm = func(x1_usm, out=out_usm, order=order)
return dpnp_array._create_from_usm_ndarray(res_usm)


_isnan_docstring = """
isnan(x, out=None, order="K")

Checks if each element of an input array is a NaN.

Args:
x (dpnp.ndarray):
Input array, expected to have numeric data type.
out ({None, dpnp.ndarray}, optional):
Output array to populate.
Array have the correct shape and the expected data type.
order ("C","F","A","K", optional):
Memory layout of the newly output array, if parameter `out` is `None`.
Default: "K".
Returns:
dpnp.ndarray:
An array which is True where x is NaN, False otherwise.
The data type of the returned array is `bool`.
"""


def dpnp_isnan(x, out=None, order="K"):
"""Invokes isnan() from dpctl.tensor implementation for isnan() function."""

# dpctl.tensor only works with usm_ndarray
x1_usm = dpnp.get_usm_ndarray(x)
out_usm = None if out is None else dpnp.get_usm_ndarray(out)

func = UnaryElementwiseFunc(
"isnan", ti._isnan_result_type, ti._isnan, _isnan_docstring
)
res_usm = func(x1_usm, out=out_usm, order=order)
return dpnp_array._create_from_usm_ndarray(res_usm)


_less_docstring_ = """
less(x1, x2, out=None, order="K")

Expand Down
102 changes: 57 additions & 45 deletions dpnp/dpnp_iface_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@
dpnp_equal,
dpnp_greater,
dpnp_greater_equal,
dpnp_isfinite,
dpnp_isinf,
dpnp_isnan,
dpnp_less,
dpnp_less_equal,
dpnp_logical_and,
Expand Down Expand Up @@ -492,19 +495,20 @@ def isclose(x1, x2, rtol=1e-05, atol=1e-08, equal_nan=False):
)


def isfinite(x1, out=None, **kwargs):
def isfinite(
x, /, out=None, *, where=True, order="K", dtype=None, subok=True, **kwargs
):
"""
Test element-wise for finiteness (not infinity or not Not a Number).

For full documentation refer to :obj:`numpy.isfinite`.

Limitations
-----------
Input array is supported as :obj:`dpnp.ndarray`.
Parameters `x` is only supported as either :class:`dpnp.ndarray` or :class:`dpctl.tensor.usm_ndarray`.
Parameters `where`, `dtype` and `subok` are supported with their default values.
Otherwise the function will be executed sequentially on CPU.
Input array data types are limited by supported DPNP :ref:`Data types`.
Parameter ``out`` is supported only with default value ``None``.
Parameter ``where`` is supported only with default value ``True``.

See Also
--------
Expand All @@ -525,35 +529,38 @@ def isfinite(x1, out=None, **kwargs):
--------
>>> import dpnp as np
>>> x = np.array([-np.inf, 0., np.inf])
>>> out = np.isfinite(x)
>>> [i for i in out]
[False, True, False]
>>> np.isfinite(x)
array([False, True, False])

"""

# x1_desc = dpnp.get_dpnp_descriptor(x1)
# if x1_desc and kwargs:
# if out is not None:
# pass
# else:
# return dpnp_isfinite(x1_desc).get_pyobj()

return call_origin(numpy.isfinite, x1, out, **kwargs)
return check_nd_call_func(
numpy.isfinite,
dpnp_isfinite,
x,
out=out,
where=where,
order=order,
dtype=dtype,
subok=subok,
**kwargs,
)


def isinf(x1, out=None, **kwargs):
def isinf(
x, /, out=None, *, where=True, order="K", dtype=None, subok=True, **kwargs
):
"""
Test element-wise for positive or negative infinity.

For full documentation refer to :obj:`numpy.isinf`.

Limitations
-----------
Input array is supported as :obj:`dpnp.ndarray`.
Parameters `x` is only supported as either :class:`dpnp.ndarray` or :class:`dpctl.tensor.usm_ndarray`.
Parameters `where`, `dtype` and `subok` are supported with their default values.
Otherwise the function will be executed sequentially on CPU.
Input array data types are limited by supported DPNP :ref:`Data types`.
Parameter ``out`` is supported only with default value ``None``.
Parameter ``where`` is supported only with default value ``True``.

See Also
--------
Expand All @@ -569,35 +576,38 @@ def isinf(x1, out=None, **kwargs):
--------
>>> import dpnp as np
>>> x = np.array([-np.inf, 0., np.inf])
>>> out = np.isinf(x)
>>> [i for i in out]
[True, False, True]
>>> np.isinf(x)
array([ True, False, True])

"""

# x1_desc = dpnp.get_dpnp_descriptor(x1)
# if x1_desc and kwargs:
# if out is not None:
# pass
# else:
# return dpnp_isinf(x1_desc).get_pyobj()

return call_origin(numpy.isinf, x1, out, **kwargs)
return check_nd_call_func(
numpy.isinf,
dpnp_isinf,
x,
out=out,
where=where,
order=order,
dtype=dtype,
subok=subok,
**kwargs,
)


def isnan(x1, out=None, **kwargs):
def isnan(
x, /, out=None, *, where=True, order="K", dtype=None, subok=True, **kwargs
):
"""
Test element-wise for NaN and return result as a boolean array.

For full documentation refer to :obj:`numpy.isnan`.

Limitations
-----------
Input array is supported as :obj:`dpnp.ndarray`.
Parameters `x` is only supported as either :class:`dpnp.ndarray` or :class:`dpctl.tensor.usm_ndarray`.
Parameters `where`, `dtype` and `subok` are supported with their default values.
Otherwise the function will be executed sequentially on CPU.
Input array data types are limited by supported DPNP :ref:`Data types`.
Parameter ``out`` is supported only with default value ``None``.
Parameter ``where`` is supported only with default value ``True``.

See Also
--------
Expand All @@ -614,20 +624,22 @@ def isnan(x1, out=None, **kwargs):
--------
>>> import dpnp as np
>>> x = np.array([np.inf, 0., np.nan])
>>> out = np.isnan(x)
>>> [i for i in out]
[False, False, True]
>>> np.isnan(x)
array([False, False, True])

"""

# x1_desc = dpnp.get_dpnp_descriptor(x1)
# if x1_desc and kwargs:
# if out is not None:
# pass
# else:
# return dpnp_isnan(x1_desc).get_pyobj()

return call_origin(numpy.isnan, x1, out, **kwargs)
return check_nd_call_func(
numpy.isnan,
dpnp_isnan,
x,
out=out,
where=where,
order=order,
dtype=dtype,
subok=subok,
**kwargs,
)


def less(
Expand Down
28 changes: 27 additions & 1 deletion tests/test_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

import dpnp

from .helper import get_all_dtypes, has_support_aspect64
from .helper import (
get_all_dtypes,
get_float_complex_dtypes,
has_support_aspect64,
)


@pytest.mark.parametrize("type", get_all_dtypes())
Expand Down Expand Up @@ -297,3 +301,25 @@ def test_comparison_no_broadcast_with_shapes(op, sh1, sh2):
with pytest.raises(ValueError):
getattr(dpnp, op)(x1, x2)
getattr(numpy, op)(x1.asnumpy(), x2.asnumpy())


@pytest.mark.parametrize(
"op", ["isfinite", "isinf", "isnan"], ids=["isfinite", "isinf", "isnan"]
)
@pytest.mark.parametrize(
"data",
[
[dpnp.inf, -1, 0, 1, dpnp.nan],
[[dpnp.inf, dpnp.nan], [dpnp.nan, 0], [1, dpnp.inf]],
],
ids=[
"[dpnp.inf, -1, 0, 1, dpnp.nan]",
"[[dpnp.inf, dpnp.nan], [dpnp.nan, 0], [1, dpnp.inf]]",
],
)
@pytest.mark.parametrize("dtype", get_float_complex_dtypes())
def test_finite(op, data, dtype):
x = dpnp.asarray(data, dtype=dtype)
np_res = getattr(dpnp, op)(x)
dpnp_res = getattr(numpy, op)(x.asnumpy())
assert_equal(dpnp_res, np_res)
Loading