Skip to content

Commit

Permalink
Reuse dpctl.tensor.isnan(), dpctl.tensor.isinf(), and dpctl.tensor.is…
Browse files Browse the repository at this point in the history
…finite() functions. (#1504)

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

* Add more test for isnan, isinf, and isfinite functions.

* Fix pre-commit

* Update tests/test_logic.py

* Update test_logic.py

---------

Co-authored-by: Anton <100830759+antonwolfy@users.noreply.github.com>
  • Loading branch information
npolina4 and antonwolfy authored Aug 1, 2023
1 parent 3c0f249 commit 70294a3
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 97 deletions.
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

0 comments on commit 70294a3

Please sign in to comment.