Skip to content

Commit 70294a3

Browse files
npolina4antonwolfy
andauthored
Reuse dpctl.tensor.isnan(), dpctl.tensor.isinf(), and dpctl.tensor.isfinite() 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 <[email protected]>
1 parent 3c0f249 commit 70294a3

File tree

5 files changed

+193
-97
lines changed

5 files changed

+193
-97
lines changed

dpnp/dpnp_algo/dpnp_algo_logic.pxi

-48
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,6 @@ __all__ += [
4040
"dpnp_allclose",
4141
"dpnp_any",
4242
"dpnp_isclose",
43-
"dpnp_isfinite",
44-
"dpnp_isinf",
45-
"dpnp_isnan",
4643
]
4744

4845

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

176173
return result
177-
178-
179-
cpdef utils.dpnp_descriptor dpnp_isfinite(utils.dpnp_descriptor input1):
180-
input1_obj = input1.get_array()
181-
cdef utils.dpnp_descriptor result = utils_py.create_output_descriptor_py(input1.shape,
182-
dpnp.bool,
183-
None,
184-
device=input1_obj.sycl_device,
185-
usm_type=input1_obj.usm_type,
186-
sycl_queue=input1_obj.sycl_queue)
187-
188-
for i in range(result.size):
189-
result.get_pyobj()[i] = numpy.isfinite(input1.get_pyobj()[i])
190-
191-
return result
192-
193-
194-
cpdef utils.dpnp_descriptor dpnp_isinf(utils.dpnp_descriptor input1):
195-
input1_obj = input1.get_array()
196-
cdef utils.dpnp_descriptor result = utils_py.create_output_descriptor_py(input1.shape,
197-
dpnp.bool,
198-
None,
199-
device=input1_obj.sycl_device,
200-
usm_type=input1_obj.usm_type,
201-
sycl_queue=input1_obj.sycl_queue)
202-
203-
for i in range(result.size):
204-
result.get_pyobj()[i] = numpy.isinf(input1.get_pyobj()[i])
205-
206-
return result
207-
208-
209-
cpdef utils.dpnp_descriptor dpnp_isnan(utils.dpnp_descriptor input1):
210-
input1_obj = input1.get_array()
211-
cdef utils.dpnp_descriptor result = utils_py.create_output_descriptor_py(input1.shape,
212-
dpnp.bool,
213-
None,
214-
device=input1_obj.sycl_device,
215-
usm_type=input1_obj.usm_type,
216-
sycl_queue=input1_obj.sycl_queue)
217-
218-
for i in range(result.size):
219-
result.get_pyobj()[i] = numpy.isnan(input1.get_pyobj()[i])
220-
221-
return result

dpnp/dpnp_algo/dpnp_elementwise_common.py

+109
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@
4949
"dpnp_floor_divide",
5050
"dpnp_greater",
5151
"dpnp_greater_equal",
52+
"dpnp_isfinite",
53+
"dpnp_isinf",
54+
"dpnp_isnan",
5255
"dpnp_less",
5356
"dpnp_less_equal",
5457
"dpnp_log",
@@ -466,6 +469,112 @@ def dpnp_greater_equal(x1, x2, out=None, order="K"):
466469
return dpnp_array._create_from_usm_ndarray(res_usm)
467470

468471

472+
_isfinite_docstring = """
473+
isfinite(x, out=None, order="K")
474+
475+
Checks if each element of input array is a finite number.
476+
477+
Args:
478+
x (dpnp.ndarray):
479+
Input array, expected to have numeric data type.
480+
out ({None, dpnp.ndarray}, optional):
481+
Output array to populate.
482+
Array have the correct shape and the expected data type.
483+
order ("C","F","A","K", optional):
484+
Memory layout of the newly output array, if parameter `out` is `None`.
485+
Default: "K".
486+
Returns:
487+
dpnp.ndarray:
488+
An array which is True where `x` is not positive infinity,
489+
negative infinity, or NaN, False otherwise.
490+
The data type of the returned array is `bool`.
491+
"""
492+
493+
494+
def dpnp_isfinite(x, out=None, order="K"):
495+
"""Invokes isfinite() from dpctl.tensor implementation for isfinite() function."""
496+
497+
# dpctl.tensor only works with usm_ndarray
498+
x1_usm = dpnp.get_usm_ndarray(x)
499+
out_usm = None if out is None else dpnp.get_usm_ndarray(out)
500+
501+
func = UnaryElementwiseFunc(
502+
"isfinite", ti._isfinite_result_type, ti._isfinite, _isfinite_docstring
503+
)
504+
res_usm = func(x1_usm, out=out_usm, order=order)
505+
return dpnp_array._create_from_usm_ndarray(res_usm)
506+
507+
508+
_isinf_docstring = """
509+
isinf(x, out=None, order="K")
510+
511+
Checks if each element of input array is an infinity.
512+
513+
Args:
514+
x (dpnp.ndarray):
515+
Input array, expected to have numeric data type.
516+
out ({None, dpnp.ndarray}, optional):
517+
Output array to populate.
518+
Array have the correct shape and the expected data type.
519+
order ("C","F","A","K", optional):
520+
Memory layout of the newly output array, if parameter `out` is `None`.
521+
Default: "K".
522+
Returns:
523+
dpnp.ndarray:
524+
An array which is True where `x` is positive or negative infinity,
525+
False otherwise. The data type of the returned array is `bool`.
526+
"""
527+
528+
529+
def dpnp_isinf(x, out=None, order="K"):
530+
"""Invokes isinf() from dpctl.tensor implementation for isinf() function."""
531+
532+
# dpctl.tensor only works with usm_ndarray
533+
x1_usm = dpnp.get_usm_ndarray(x)
534+
out_usm = None if out is None else dpnp.get_usm_ndarray(out)
535+
536+
func = UnaryElementwiseFunc(
537+
"isinf", ti._isinf_result_type, ti._isinf, _isinf_docstring
538+
)
539+
res_usm = func(x1_usm, out=out_usm, order=order)
540+
return dpnp_array._create_from_usm_ndarray(res_usm)
541+
542+
543+
_isnan_docstring = """
544+
isnan(x, out=None, order="K")
545+
546+
Checks if each element of an input array is a NaN.
547+
548+
Args:
549+
x (dpnp.ndarray):
550+
Input array, expected to have numeric data type.
551+
out ({None, dpnp.ndarray}, optional):
552+
Output array to populate.
553+
Array have the correct shape and the expected data type.
554+
order ("C","F","A","K", optional):
555+
Memory layout of the newly output array, if parameter `out` is `None`.
556+
Default: "K".
557+
Returns:
558+
dpnp.ndarray:
559+
An array which is True where x is NaN, False otherwise.
560+
The data type of the returned array is `bool`.
561+
"""
562+
563+
564+
def dpnp_isnan(x, out=None, order="K"):
565+
"""Invokes isnan() from dpctl.tensor implementation for isnan() function."""
566+
567+
# dpctl.tensor only works with usm_ndarray
568+
x1_usm = dpnp.get_usm_ndarray(x)
569+
out_usm = None if out is None else dpnp.get_usm_ndarray(out)
570+
571+
func = UnaryElementwiseFunc(
572+
"isnan", ti._isnan_result_type, ti._isnan, _isnan_docstring
573+
)
574+
res_usm = func(x1_usm, out=out_usm, order=order)
575+
return dpnp_array._create_from_usm_ndarray(res_usm)
576+
577+
469578
_less_docstring_ = """
470579
less(x1, x2, out=None, order="K")
471580

dpnp/dpnp_iface_logic.py

+57-45
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@
5151
dpnp_equal,
5252
dpnp_greater,
5353
dpnp_greater_equal,
54+
dpnp_isfinite,
55+
dpnp_isinf,
56+
dpnp_isnan,
5457
dpnp_less,
5558
dpnp_less_equal,
5659
dpnp_logical_and,
@@ -492,19 +495,20 @@ def isclose(x1, x2, rtol=1e-05, atol=1e-08, equal_nan=False):
492495
)
493496

494497

495-
def isfinite(x1, out=None, **kwargs):
498+
def isfinite(
499+
x, /, out=None, *, where=True, order="K", dtype=None, subok=True, **kwargs
500+
):
496501
"""
497502
Test element-wise for finiteness (not infinity or not Not a Number).
498503
499504
For full documentation refer to :obj:`numpy.isfinite`.
500505
501506
Limitations
502507
-----------
503-
Input array is supported as :obj:`dpnp.ndarray`.
508+
Parameters `x` is only supported as either :class:`dpnp.ndarray` or :class:`dpctl.tensor.usm_ndarray`.
509+
Parameters `where`, `dtype` and `subok` are supported with their default values.
504510
Otherwise the function will be executed sequentially on CPU.
505511
Input array data types are limited by supported DPNP :ref:`Data types`.
506-
Parameter ``out`` is supported only with default value ``None``.
507-
Parameter ``where`` is supported only with default value ``True``.
508512
509513
See Also
510514
--------
@@ -525,35 +529,38 @@ def isfinite(x1, out=None, **kwargs):
525529
--------
526530
>>> import dpnp as np
527531
>>> x = np.array([-np.inf, 0., np.inf])
528-
>>> out = np.isfinite(x)
529-
>>> [i for i in out]
530-
[False, True, False]
532+
>>> np.isfinite(x)
533+
array([False, True, False])
531534
532535
"""
533536

534-
# x1_desc = dpnp.get_dpnp_descriptor(x1)
535-
# if x1_desc and kwargs:
536-
# if out is not None:
537-
# pass
538-
# else:
539-
# return dpnp_isfinite(x1_desc).get_pyobj()
540-
541-
return call_origin(numpy.isfinite, x1, out, **kwargs)
537+
return check_nd_call_func(
538+
numpy.isfinite,
539+
dpnp_isfinite,
540+
x,
541+
out=out,
542+
where=where,
543+
order=order,
544+
dtype=dtype,
545+
subok=subok,
546+
**kwargs,
547+
)
542548

543549

544-
def isinf(x1, out=None, **kwargs):
550+
def isinf(
551+
x, /, out=None, *, where=True, order="K", dtype=None, subok=True, **kwargs
552+
):
545553
"""
546554
Test element-wise for positive or negative infinity.
547555
548556
For full documentation refer to :obj:`numpy.isinf`.
549557
550558
Limitations
551559
-----------
552-
Input array is supported as :obj:`dpnp.ndarray`.
560+
Parameters `x` is only supported as either :class:`dpnp.ndarray` or :class:`dpctl.tensor.usm_ndarray`.
561+
Parameters `where`, `dtype` and `subok` are supported with their default values.
553562
Otherwise the function will be executed sequentially on CPU.
554563
Input array data types are limited by supported DPNP :ref:`Data types`.
555-
Parameter ``out`` is supported only with default value ``None``.
556-
Parameter ``where`` is supported only with default value ``True``.
557564
558565
See Also
559566
--------
@@ -569,35 +576,38 @@ def isinf(x1, out=None, **kwargs):
569576
--------
570577
>>> import dpnp as np
571578
>>> x = np.array([-np.inf, 0., np.inf])
572-
>>> out = np.isinf(x)
573-
>>> [i for i in out]
574-
[True, False, True]
579+
>>> np.isinf(x)
580+
array([ True, False, True])
575581
576582
"""
577583

578-
# x1_desc = dpnp.get_dpnp_descriptor(x1)
579-
# if x1_desc and kwargs:
580-
# if out is not None:
581-
# pass
582-
# else:
583-
# return dpnp_isinf(x1_desc).get_pyobj()
584-
585-
return call_origin(numpy.isinf, x1, out, **kwargs)
584+
return check_nd_call_func(
585+
numpy.isinf,
586+
dpnp_isinf,
587+
x,
588+
out=out,
589+
where=where,
590+
order=order,
591+
dtype=dtype,
592+
subok=subok,
593+
**kwargs,
594+
)
586595

587596

588-
def isnan(x1, out=None, **kwargs):
597+
def isnan(
598+
x, /, out=None, *, where=True, order="K", dtype=None, subok=True, **kwargs
599+
):
589600
"""
590601
Test element-wise for NaN and return result as a boolean array.
591602
592603
For full documentation refer to :obj:`numpy.isnan`.
593604
594605
Limitations
595606
-----------
596-
Input array is supported as :obj:`dpnp.ndarray`.
607+
Parameters `x` is only supported as either :class:`dpnp.ndarray` or :class:`dpctl.tensor.usm_ndarray`.
608+
Parameters `where`, `dtype` and `subok` are supported with their default values.
597609
Otherwise the function will be executed sequentially on CPU.
598610
Input array data types are limited by supported DPNP :ref:`Data types`.
599-
Parameter ``out`` is supported only with default value ``None``.
600-
Parameter ``where`` is supported only with default value ``True``.
601611
602612
See Also
603613
--------
@@ -614,20 +624,22 @@ def isnan(x1, out=None, **kwargs):
614624
--------
615625
>>> import dpnp as np
616626
>>> x = np.array([np.inf, 0., np.nan])
617-
>>> out = np.isnan(x)
618-
>>> [i for i in out]
619-
[False, False, True]
627+
>>> np.isnan(x)
628+
array([False, False, True])
620629
621630
"""
622631

623-
# x1_desc = dpnp.get_dpnp_descriptor(x1)
624-
# if x1_desc and kwargs:
625-
# if out is not None:
626-
# pass
627-
# else:
628-
# return dpnp_isnan(x1_desc).get_pyobj()
629-
630-
return call_origin(numpy.isnan, x1, out, **kwargs)
632+
return check_nd_call_func(
633+
numpy.isnan,
634+
dpnp_isnan,
635+
x,
636+
out=out,
637+
where=where,
638+
order=order,
639+
dtype=dtype,
640+
subok=subok,
641+
**kwargs,
642+
)
631643

632644

633645
def less(

tests/test_logic.py

+27-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44

55
import dpnp
66

7-
from .helper import get_all_dtypes, has_support_aspect64
7+
from .helper import (
8+
get_all_dtypes,
9+
get_float_complex_dtypes,
10+
has_support_aspect64,
11+
)
812

913

1014
@pytest.mark.parametrize("type", get_all_dtypes())
@@ -297,3 +301,25 @@ def test_comparison_no_broadcast_with_shapes(op, sh1, sh2):
297301
with pytest.raises(ValueError):
298302
getattr(dpnp, op)(x1, x2)
299303
getattr(numpy, op)(x1.asnumpy(), x2.asnumpy())
304+
305+
306+
@pytest.mark.parametrize(
307+
"op", ["isfinite", "isinf", "isnan"], ids=["isfinite", "isinf", "isnan"]
308+
)
309+
@pytest.mark.parametrize(
310+
"data",
311+
[
312+
[dpnp.inf, -1, 0, 1, dpnp.nan],
313+
[[dpnp.inf, dpnp.nan], [dpnp.nan, 0], [1, dpnp.inf]],
314+
],
315+
ids=[
316+
"[dpnp.inf, -1, 0, 1, dpnp.nan]",
317+
"[[dpnp.inf, dpnp.nan], [dpnp.nan, 0], [1, dpnp.inf]]",
318+
],
319+
)
320+
@pytest.mark.parametrize("dtype", get_float_complex_dtypes())
321+
def test_finite(op, data, dtype):
322+
x = dpnp.asarray(data, dtype=dtype)
323+
np_res = getattr(dpnp, op)(x)
324+
dpnp_res = getattr(numpy, op)(x.asnumpy())
325+
assert_equal(dpnp_res, np_res)

0 commit comments

Comments
 (0)