Skip to content

Commit 8a7bba7

Browse files
Merge 4c52f58 into d2c623b
2 parents d2c623b + 4c52f58 commit 8a7bba7

File tree

5 files changed

+163
-37
lines changed

5 files changed

+163
-37
lines changed

dpnp/dpnp_iface_mathematical.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
"mod",
111111
"modf",
112112
"multiply",
113+
"nan_to_num",
113114
"negative",
114115
"nextafter",
115116
"positive",
@@ -155,6 +156,13 @@ def _append_to_diff_array(a, axis, combined, values):
155156
combined.append(values)
156157

157158

159+
def _get_max_min(dtype):
160+
"""Get the maximum and minimum representable values for an inexact dtype."""
161+
162+
f = dpnp.finfo(dtype)
163+
return f.max, f.min
164+
165+
158166
def _get_reduction_res_dt(a, dtype, _out):
159167
"""Get a data type used by dpctl for result array in reduction function."""
160168

@@ -2304,6 +2312,119 @@ def modf(x1, **kwargs):
23042312
)
23052313

23062314

2315+
def nan_to_num(x, copy=True, nan=0.0, posinf=None, neginf=None):
2316+
"""
2317+
Replace NaN with zero and infinity with large finite numbers (default
2318+
behaviour) or with the numbers defined by the user using the `nan`,
2319+
`posinf` and/or `neginf` keywords.
2320+
2321+
If `x` is inexact, NaN is replaced by zero or by the user defined value in
2322+
`nan` keyword, infinity is replaced by the largest finite floating point
2323+
values representable by ``x.dtype`` or by the user defined value in
2324+
`posinf` keyword and -infinity is replaced by the most negative finite
2325+
floating point values representable by ``x.dtype`` or by the user defined
2326+
value in `neginf` keyword.
2327+
2328+
For complex dtypes, the above is applied to each of the real and
2329+
imaginary components of `x` separately.
2330+
2331+
If `x` is not inexact, then no replacements are made.
2332+
2333+
For full documentation refer to :obj:`numpy.nan_to_num`.
2334+
2335+
Parameters
2336+
----------
2337+
x : {dpnp.ndarray, usm_ndarray}
2338+
Input data.
2339+
copy : bool, optional
2340+
Whether to create a copy of `x` (True) or to replace values
2341+
in-place (False). The in-place operation only occurs if
2342+
casting to an array does not require a copy.
2343+
Default: ``True``.
2344+
nan : {int, float}, optional
2345+
Value to be used to fill NaN values.
2346+
Default: ``0.0``.
2347+
posinf : {int, float, None}, optional
2348+
Value to be used to fill positive infinity values. If no value is
2349+
passed then positive infinity values will be replaced with a very
2350+
large number.
2351+
Default: ``None``.
2352+
neginf : {int, float, None} optional
2353+
Value to be used to fill negative infinity values. If no value is
2354+
passed then negative infinity values will be replaced with a very
2355+
small (or negative) number.
2356+
Default: ``None``.
2357+
2358+
Returns
2359+
-------
2360+
out : dpnp.ndarray
2361+
`x`, with the non-finite values replaced. If `copy` is False, this may
2362+
be `x` itself.
2363+
2364+
See Also
2365+
--------
2366+
:obj:`dpnp.isinf` : Shows which elements are positive or negative infinity.
2367+
:obj:`dpnp.isneginf` : Shows which elements are negative infinity.
2368+
:obj:`dpnp.isposinf` : Shows which elements are positive infinity.
2369+
:obj:`dpnp.isnan` : Shows which elements are Not a Number (NaN).
2370+
:obj:`dpnp.isfinite` : Shows which elements are finite
2371+
(not NaN, not infinity)
2372+
2373+
Examples
2374+
--------
2375+
>>> import dpnp as np
2376+
>>> np.nan_to_num(np.array(np.inf))
2377+
array(1.79769313e+308)
2378+
>>> np.nan_to_num(np.array(-np.inf))
2379+
array(-1.79769313e+308)
2380+
>>> np.nan_to_num(np.array(np.nan))
2381+
array(0.)
2382+
>>> x = np.array([np.inf, -np.inf, np.nan, -128, 128])
2383+
>>> np.nan_to_num(x)
2384+
array([ 1.79769313e+308, -1.79769313e+308, 0.00000000e+000,
2385+
-1.28000000e+002, 1.28000000e+002])
2386+
>>> np.nan_to_num(x, nan=-9999, posinf=33333333, neginf=33333333)
2387+
array([ 3.3333333e+07, 3.3333333e+07, -9.9990000e+03, -1.2800000e+02,
2388+
1.2800000e+02])
2389+
>>> y = np.array([complex(np.inf, np.nan), np.nan, complex(np.nan, np.inf)])
2390+
>>> np.nan_to_num(y)
2391+
array([1.79769313e+308 +0.00000000e+000j, # may vary
2392+
0.00000000e+000 +0.00000000e+000j,
2393+
0.00000000e+000 +1.79769313e+308j])
2394+
>>> np.nan_to_num(y, nan=111111, posinf=222222)
2395+
array([222222.+111111.j, 111111. +0.j, 111111.+222222.j])
2396+
2397+
"""
2398+
2399+
dpnp.check_supported_arrays_type(x)
2400+
2401+
x = dpnp.array(x, copy=copy)
2402+
x_type = x.dtype.type
2403+
2404+
if not issubclass(x_type, dpnp.inexact):
2405+
return x
2406+
2407+
parts = (
2408+
(x.real, x.imag) if issubclass(x_type, dpnp.complexfloating) else (x,)
2409+
)
2410+
max_f, min_f = _get_max_min(x.real.dtype)
2411+
if posinf is not None:
2412+
max_f = posinf
2413+
if neginf is not None:
2414+
min_f = neginf
2415+
2416+
for part in parts:
2417+
nan_mask = dpnp.isnan(part)
2418+
posinf_mask = dpnp.isposinf(part)
2419+
neginf_mask = dpnp.isneginf(part)
2420+
2421+
part = dpnp.where(nan_mask, nan, part, out=part)
2422+
part = dpnp.where(posinf_mask, max_f, part, out=part)
2423+
part = dpnp.where(neginf_mask, min_f, part, out=part)
2424+
2425+
return x
2426+
2427+
23072428
_NEGATIVE_DOCSTRING = """
23082429
Computes the numerical negative for each element `x_i` of input array `x`.
23092430

tests/skipped_tests.tbl

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -207,22 +207,6 @@ tests/third_party/cupy/manipulation_tests/test_dims.py::TestInvalidBroadcast_par
207207
tests/third_party/cupy/manipulation_tests/test_dims.py::TestInvalidBroadcast_param_2_{shapes=[(3, 2), (3, 4)]}::test_invalid_broadcast
208208
tests/third_party/cupy/manipulation_tests/test_dims.py::TestInvalidBroadcast_param_3_{shapes=[(0,), (2,)]}::test_invalid_broadcast
209209

210-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num
211-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_negative
212-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_for_old_numpy
213-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_negative_for_old_numpy
214-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_inf
215-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_nan
216-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_inf_nan
217-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_nan_arg
218-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_inf_arg
219-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_broadcast[nan]
220-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_broadcast[posinf]
221-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_broadcast[neginf]
222-
223-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_scalar_nan
224-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_copy
225-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_inplace
226210
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_real_if_close_real_dtypes
227211
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_real_if_close_with_tol_real_dtypes
228212
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_real_if_close_true

tests/skipped_tests_gpu.tbl

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -261,22 +261,6 @@ tests/third_party/cupy/manipulation_tests/test_dims.py::TestInvalidBroadcast_par
261261
tests/third_party/cupy/manipulation_tests/test_dims.py::TestInvalidBroadcast_param_2_{shapes=[(3, 2), (3, 4)]}::test_invalid_broadcast
262262
tests/third_party/cupy/manipulation_tests/test_dims.py::TestInvalidBroadcast_param_3_{shapes=[(0,), (2,)]}::test_invalid_broadcast
263263

264-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num
265-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_negative
266-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_for_old_numpy
267-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_negative_for_old_numpy
268-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_inf
269-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_nan
270-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_inf_nan
271-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_nan_arg
272-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_inf_arg
273-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_broadcast[nan]
274-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_broadcast[posinf]
275-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_broadcast[neginf]
276-
277-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_scalar_nan
278-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_copy
279-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_inplace
280264
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_real_if_close_real_dtypes
281265
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_real_if_close_with_tol_real_dtypes
282266
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_real_if_close_true

tests/test_mathematical.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,6 +1445,40 @@ def test_power_scalar(shape, dtype):
14451445
assert_allclose(result, expected, rtol=1e-6)
14461446

14471447

1448+
class TestNanToNum:
1449+
@pytest.mark.parametrize("dtype", get_all_dtypes())
1450+
@pytest.mark.parametrize("shape", [(3,), (2, 3), (3, 2, 2)])
1451+
def test_nan_to_num(self, dtype, shape):
1452+
a = numpy.random.randn(*shape).astype(dtype)
1453+
if not dpnp.issubdtype(dtype, dpnp.integer):
1454+
a.flat[1] = numpy.nan
1455+
a_dp = dpnp.array(a)
1456+
1457+
result = dpnp.nan_to_num(a_dp)
1458+
expected = numpy.nan_to_num(a)
1459+
assert_allclose(result, expected)
1460+
1461+
@pytest.mark.parametrize(
1462+
"data", [[], [numpy.nan], [numpy.inf], [-numpy.inf]]
1463+
)
1464+
@pytest.mark.parametrize("dtype", get_float_complex_dtypes())
1465+
def test_empty_and_single_value_arrays(self, data, dtype):
1466+
a = numpy.array(data, dtype)
1467+
ia = dpnp.array(a)
1468+
1469+
result = dpnp.nan_to_num(ia)
1470+
expected = numpy.nan_to_num(a)
1471+
assert_allclose(result, expected)
1472+
1473+
def test_boolean_array(self):
1474+
a = numpy.array([True, False, numpy.nan], dtype=bool)
1475+
ia = dpnp.array(a)
1476+
1477+
result = dpnp.nan_to_num(ia)
1478+
expected = numpy.nan_to_num(a)
1479+
assert_allclose(result, expected)
1480+
1481+
14481482
@pytest.mark.parametrize(
14491483
"data",
14501484
[[[1.0, -1.0], [0.1, -0.1]], [-2, -1, 0, 1, 2]],

tests/third_party/cupy/math_tests/test_misc.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -260,27 +260,30 @@ def test_nan_to_num_inf_arg(self):
260260

261261
@testing.numpy_cupy_array_equal()
262262
def test_nan_to_num_copy(self, xp):
263-
x = xp.asarray([0, 1, xp.nan, 4], dtype=xp.float64)
263+
x = xp.asarray([0, 1, xp.nan, 4], dtype=cupy.default_float_type())
264264
y = xp.nan_to_num(x, copy=True)
265265
assert x is not y
266266
return y
267267

268268
@testing.numpy_cupy_array_equal()
269269
def test_nan_to_num_inplace(self, xp):
270-
x = xp.asarray([0, 1, xp.nan, 4], dtype=xp.float64)
270+
x = xp.asarray([0, 1, xp.nan, 4], dtype=cupy.default_float_type())
271271
y = xp.nan_to_num(x, copy=False)
272272
assert x is y
273273
return y
274274

275275
@pytest.mark.parametrize("kwarg", ["nan", "posinf", "neginf"])
276276
def test_nan_to_num_broadcast(self, kwarg):
277277
for xp in (numpy, cupy):
278-
x = xp.asarray([0, 1, xp.nan, 4], dtype=xp.float64)
279-
y = xp.zeros((2, 4), dtype=xp.float64)
278+
x = xp.asarray([0, 1, xp.nan, 4], dtype=cupy.default_float_type())
279+
y = xp.zeros((2, 4), dtype=cupy.default_float_type())
280280
with pytest.raises(ValueError):
281281
xp.nan_to_num(x, **{kwarg: y})
282+
# dpnp.nan_to_num() doesn`t support a scalar as an input
283+
# convert 0.0 to 0-ndim array
282284
with pytest.raises(ValueError):
283-
xp.nan_to_num(0.0, **{kwarg: y})
285+
x_ndim_0 = xp.array(0.0)
286+
xp.nan_to_num(x_ndim_0, **{kwarg: y})
284287

285288
@testing.for_all_dtypes(no_bool=True, no_complex=True)
286289
@testing.numpy_cupy_array_equal()

0 commit comments

Comments
 (0)