Skip to content

Commit 0b7c230

Browse files
Implement dpnp.isneginf and dpnp.isposinf (#1888)
* Implement dpnp.isneginf() * Add tests for dpnp.isneginf() * Implement dpnp.isposinf() * Add tests for dpnp.isposinf() * Add new functions to gen docs * Add additional checks * Add test_infinity_sign_errors * Add sycl_queue/usm tests for logic functions * Update tests * Remove out dtype check * Add TODO with support different out dtype * Update test_logic_op_2in --------- Co-authored-by: Anton <[email protected]>
1 parent 73ace12 commit 0b7c230

File tree

6 files changed

+314
-11
lines changed

6 files changed

+314
-11
lines changed

doc/reference/logic.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Infinities and NaNs
2626
dpnp.isfinite
2727
dpnp.isinf
2828
dpnp.isnan
29+
dpnp.isneginf
30+
dpnp.isposinf
2931

3032

3133
Array type testing

dpnp/dpnp_iface_logic.py

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@
6666
"isfinite",
6767
"isinf",
6868
"isnan",
69+
"isneginf",
70+
"isposinf",
6971
"less",
7072
"less_equal",
7173
"logical_and",
@@ -777,6 +779,150 @@ def isclose(x1, x2, rtol=1e-05, atol=1e-08, equal_nan=False):
777779
)
778780

779781

782+
def isneginf(x, out=None):
783+
"""
784+
Test element-wise for negative infinity, return result as bool array.
785+
786+
For full documentation refer to :obj:`numpy.isneginf`.
787+
788+
Parameters
789+
----------
790+
x : {dpnp.ndarray, usm_ndarray}
791+
Input array.
792+
out : {None, dpnp.ndarray, usm_ndarray}, optional
793+
A location into which the result is stored. If provided, it must have a
794+
shape that the input broadcasts to and a boolean data type.
795+
If not provided or ``None``, a freshly-allocated boolean array
796+
is returned.
797+
Default: ``None``.
798+
799+
Returns
800+
-------
801+
out : dpnp.ndarray
802+
Boolean array of same shape as ``x``.
803+
804+
See Also
805+
--------
806+
:obj:`dpnp.isinf` : Test element-wise for positive or negative infinity.
807+
:obj:`dpnp.isposinf` : Test element-wise for positive infinity,
808+
return result as bool array.
809+
:obj:`dpnp.isnan` : Test element-wise for NaN and
810+
return result as a boolean array.
811+
:obj:`dpnp.isfinite` : Test element-wise for finiteness.
812+
813+
Examples
814+
--------
815+
>>> import dpnp as np
816+
>>> x = np.array(np.inf)
817+
>>> np.isneginf(-x)
818+
array(True)
819+
>>> np.isneginf(x)
820+
array(False)
821+
822+
>>> x = np.array([-np.inf, 0., np.inf])
823+
>>> np.isneginf(x)
824+
array([ True, False, False])
825+
826+
>>> x = np.array([-np.inf, 0., np.inf])
827+
>>> y = np.zeros(x.shape, dtype='bool')
828+
>>> np.isneginf(x, y)
829+
array([ True, False, False])
830+
>>> y
831+
array([ True, False, False])
832+
833+
"""
834+
835+
dpnp.check_supported_arrays_type(x)
836+
837+
if out is not None:
838+
dpnp.check_supported_arrays_type(out)
839+
840+
x_dtype = x.dtype
841+
if dpnp.issubdtype(x_dtype, dpnp.complexfloating):
842+
raise TypeError(
843+
f"This operation is not supported for {x_dtype} values "
844+
"because it would be ambiguous."
845+
)
846+
847+
is_inf = dpnp.isinf(x)
848+
signbit = dpnp.signbit(x)
849+
850+
# TODO: support different out dtype #1717(dpctl)
851+
return dpnp.logical_and(is_inf, signbit, out=out)
852+
853+
854+
def isposinf(x, out=None):
855+
"""
856+
Test element-wise for positive infinity, return result as bool array.
857+
858+
For full documentation refer to :obj:`numpy.isposinf`.
859+
860+
Parameters
861+
----------
862+
x : {dpnp.ndarray, usm_ndarray}
863+
Input array.
864+
out : {None, dpnp.ndarray, usm_ndarray}, optional
865+
A location into which the result is stored. If provided, it must have a
866+
shape that the input broadcasts to and a boolean data type.
867+
If not provided or ``None``, a freshly-allocated boolean array
868+
is returned.
869+
Default: ``None``.
870+
871+
Returns
872+
-------
873+
out : dpnp.ndarray
874+
Boolean array of same shape as ``x``.
875+
876+
See Also
877+
--------
878+
:obj:`dpnp.isinf` : Test element-wise for positive or negative infinity.
879+
:obj:`dpnp.isneginf` : Test element-wise for negative infinity,
880+
return result as bool array.
881+
:obj:`dpnp.isnan` : Test element-wise for NaN and
882+
return result as a boolean array.
883+
:obj:`dpnp.isfinite` : Test element-wise for finiteness.
884+
885+
Examples
886+
--------
887+
>>> import dpnp as np
888+
>>> x = np.array(np.inf)
889+
>>> np.isposinf(x)
890+
array(True)
891+
>>> np.isposinf(-x)
892+
array(False)
893+
894+
>>> x = np.array([-np.inf, 0., np.inf])
895+
>>> np.isposinf(x)
896+
array([False, False, True])
897+
898+
>>> x = np.array([-np.inf, 0., np.inf])
899+
>>> y = np.zeros(x.shape, dtype='bool')
900+
>>> np.isposinf(x, y)
901+
array([False, False, True])
902+
>>> y
903+
array([False, False, True])
904+
905+
"""
906+
907+
dpnp.check_supported_arrays_type(x)
908+
909+
if out is not None:
910+
dpnp.check_supported_arrays_type(out)
911+
912+
x_dtype = x.dtype
913+
if dpnp.issubdtype(x_dtype, dpnp.complexfloating):
914+
raise TypeError(
915+
f"This operation is not supported for {x_dtype} values "
916+
"because it would be ambiguous."
917+
)
918+
919+
is_inf = dpnp.isinf(x)
920+
signbit = ~dpnp.signbit(x)
921+
922+
# TODO: support different out dtype #1717(dpctl)
923+
return dpnp.logical_and(is_inf, signbit, out=out)
924+
925+
780926
_LESS_DOCSTRING = """
781927
Computes the less-than test results for each element `x1_i` of
782928
the input array `x1` with the respective element `x2_i` of the input array `x2`.

tests/test_logic.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .helper import (
88
get_all_dtypes,
99
get_float_complex_dtypes,
10+
get_float_dtypes,
1011
)
1112

1213

@@ -432,3 +433,49 @@ def test_finite(op, data, dtype):
432433
dpnp_res = getattr(dpnp, op)(x, out=dp_out)
433434
assert dp_out is dpnp_res
434435
assert_equal(dpnp_res, np_res)
436+
437+
438+
@pytest.mark.parametrize("func", ["isneginf", "isposinf"])
439+
@pytest.mark.parametrize(
440+
"data",
441+
[
442+
[dpnp.inf, -1, 0, 1, dpnp.nan, -dpnp.inf],
443+
[[dpnp.inf, dpnp.nan], [dpnp.nan, 0], [1, -dpnp.inf]],
444+
],
445+
ids=[
446+
"1D array",
447+
"2D array",
448+
],
449+
)
450+
@pytest.mark.parametrize("dtype", get_float_dtypes())
451+
def test_infinity_sign(func, data, dtype):
452+
x = dpnp.asarray(data, dtype=dtype)
453+
np_res = getattr(numpy, func)(x.asnumpy())
454+
dpnp_res = getattr(dpnp, func)(x)
455+
assert_equal(dpnp_res, np_res)
456+
457+
dp_out = dpnp.empty(np_res.shape, dtype=dpnp.bool)
458+
dpnp_res = getattr(dpnp, func)(x, out=dp_out)
459+
assert dp_out is dpnp_res
460+
assert_equal(dpnp_res, np_res)
461+
462+
463+
@pytest.mark.parametrize("func", ["isneginf", "isposinf"])
464+
def test_infinity_sign_errors(func):
465+
data = [dpnp.inf, 0, -dpnp.inf]
466+
467+
# unsupported data type
468+
x = dpnp.asarray(data, dtype="c8")
469+
x_np = dpnp.asnumpy(x)
470+
assert_raises(TypeError, getattr(dpnp, func), x)
471+
assert_raises(TypeError, getattr(numpy, func), x_np)
472+
473+
# unsupported type
474+
assert_raises(TypeError, getattr(dpnp, func), data)
475+
assert_raises(TypeError, getattr(dpnp, func), x_np)
476+
477+
# unsupported `out` data type
478+
x = dpnp.asarray(data, dtype=dpnp.default_float_type())
479+
out = dpnp.empty_like(x, dtype="int32")
480+
with pytest.raises(ValueError):
481+
getattr(dpnp, func)(x, out=out)

tests/test_sycl_queue.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,40 @@ def test_1in_1out(func, data, device):
501501
assert_sycl_queue_equal(result_queue, expected_queue)
502502

503503

504+
@pytest.mark.parametrize(
505+
"op",
506+
[
507+
"all",
508+
"any",
509+
"isfinite",
510+
"isinf",
511+
"isnan",
512+
"isneginf",
513+
"isposinf",
514+
"logical_not",
515+
],
516+
)
517+
@pytest.mark.parametrize(
518+
"device",
519+
valid_devices,
520+
ids=[device.filter_string for device in valid_devices],
521+
)
522+
def test_logic_op_1in(op, device):
523+
x = dpnp.array(
524+
[-dpnp.inf, -1.0, 0.0, 1.0, dpnp.inf, dpnp.nan], device=device
525+
)
526+
result = getattr(dpnp, op)(x)
527+
528+
x_orig = dpnp.asnumpy(x)
529+
expected = getattr(numpy, op)(x_orig)
530+
assert_dtype_allclose(result, expected)
531+
532+
expected_queue = x.get_array().sycl_queue
533+
result_queue = result.get_array().sycl_queue
534+
535+
assert_sycl_queue_equal(result_queue, expected_queue)
536+
537+
504538
@pytest.mark.parametrize(
505539
"device",
506540
valid_devices,
@@ -705,6 +739,55 @@ def test_2in_1out(func, data1, data2, device):
705739
assert_sycl_queue_equal(result.sycl_queue, x2.sycl_queue)
706740

707741

742+
@pytest.mark.parametrize(
743+
"op",
744+
[
745+
"equal",
746+
"greater",
747+
"greater_equal",
748+
# TODO: unblock when dpnp.isclose() is updated
749+
# "isclose",
750+
"less",
751+
"less_equal",
752+
"logical_and",
753+
"logical_or",
754+
"logical_xor",
755+
"not_equal",
756+
],
757+
)
758+
@pytest.mark.parametrize(
759+
"device",
760+
valid_devices,
761+
ids=[device.filter_string for device in valid_devices],
762+
)
763+
def test_logic_op_2in(op, device):
764+
x1 = dpnp.array(
765+
[-dpnp.inf, -1.0, 0.0, 1.0, dpnp.inf, dpnp.nan], device=device
766+
)
767+
x2 = dpnp.array(
768+
[dpnp.inf, 1.0, 0.0, -1.0, -dpnp.inf, dpnp.nan], device=device
769+
)
770+
# Remove NaN value from input arrays because numpy raises RuntimeWarning
771+
if op in [
772+
"greater",
773+
"greater_equal",
774+
"less",
775+
"less_equal",
776+
]:
777+
x1 = x1[:-1]
778+
x2 = x2[:-1]
779+
result = getattr(dpnp, op)(x1, x2)
780+
781+
x1_orig = dpnp.asnumpy(x1)
782+
x2_orig = dpnp.asnumpy(x2)
783+
expected = getattr(numpy, op)(x1_orig, x2_orig)
784+
785+
assert_dtype_allclose(result, expected)
786+
787+
assert_sycl_queue_equal(result.sycl_queue, x1.sycl_queue)
788+
assert_sycl_queue_equal(result.sycl_queue, x2.sycl_queue)
789+
790+
708791
@pytest.mark.parametrize(
709792
"func, data, scalar",
710793
[

tests/test_usm_type.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -357,20 +357,32 @@ def test_tril_triu(func, usm_type):
357357
@pytest.mark.parametrize(
358358
"op",
359359
[
360-
"equal",
361-
"greater",
362-
"greater_equal",
363-
"less",
364-
"less_equal",
365-
"logical_and",
366-
"logical_or",
367-
"logical_xor",
368-
"not_equal",
360+
"all",
361+
"any",
362+
"isfinite",
363+
"isinf",
364+
"isnan",
365+
"isneginf",
366+
"isposinf",
367+
"logical_not",
369368
],
370-
ids=[
369+
)
370+
@pytest.mark.parametrize("usm_type_x", list_of_usm_types, ids=list_of_usm_types)
371+
def test_coerced_usm_types_logic_op_1in(op, usm_type_x):
372+
x = dp.arange(-10, 10, usm_type=usm_type_x)
373+
res = getattr(dp, op)(x)
374+
375+
assert x.usm_type == res.usm_type == usm_type_x
376+
377+
378+
@pytest.mark.parametrize(
379+
"op",
380+
[
371381
"equal",
372382
"greater",
373383
"greater_equal",
384+
# TODO: unblock when dpnp.isclose() is updated
385+
# "isclose",
374386
"less",
375387
"less_equal",
376388
"logical_and",
@@ -381,7 +393,7 @@ def test_tril_triu(func, usm_type):
381393
)
382394
@pytest.mark.parametrize("usm_type_x", list_of_usm_types, ids=list_of_usm_types)
383395
@pytest.mark.parametrize("usm_type_y", list_of_usm_types, ids=list_of_usm_types)
384-
def test_coerced_usm_types_logic_op(op, usm_type_x, usm_type_y):
396+
def test_coerced_usm_types_logic_op_2in(op, usm_type_x, usm_type_y):
385397
x = dp.arange(100, usm_type=usm_type_x)
386398
y = dp.arange(100, usm_type=usm_type_y)[::-1]
387399

tests/third_party/cupy/logic_tests/test_content.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,16 @@ def test_isinf(self):
2929

3030
def test_isnan(self):
3131
self.check_unary_nan("isnan")
32+
33+
34+
class TestUfuncLike(unittest.TestCase):
35+
@testing.numpy_cupy_array_equal()
36+
def check_unary(self, name, xp):
37+
a = xp.array([-3, xp.inf, -1, -xp.inf, 0, 1, 2, xp.nan])
38+
return getattr(xp, name)(a)
39+
40+
def test_isneginf(self):
41+
self.check_unary("isneginf")
42+
43+
def test_isposinf(self):
44+
self.check_unary("isposinf")

0 commit comments

Comments
 (0)