|
110 | 110 | "mod", |
111 | 111 | "modf", |
112 | 112 | "multiply", |
| 113 | + "nan_to_num", |
113 | 114 | "negative", |
114 | 115 | "nextafter", |
115 | 116 | "positive", |
|
130 | 131 | ] |
131 | 132 |
|
132 | 133 |
|
| 134 | +def _get_max_min(dtype): |
| 135 | + """Get the maximum and minimum representable values for an inexact dtype.""" |
| 136 | + |
| 137 | + f = dpnp.finfo(dtype) |
| 138 | + return f.max, f.min |
| 139 | + |
| 140 | + |
133 | 141 | def _get_reduction_res_dt(a, dtype, _out): |
134 | 142 | """Get a data type used by dpctl for result array in reduction function.""" |
135 | 143 |
|
@@ -2353,6 +2361,141 @@ def modf(x1, **kwargs): |
2353 | 2361 | ) |
2354 | 2362 |
|
2355 | 2363 |
|
| 2364 | +def nan_to_num(x, copy=True, nan=0.0, posinf=None, neginf=None): |
| 2365 | + """ |
| 2366 | + Replace ``NaN`` with zero and infinity with large finite numbers (default |
| 2367 | + behaviour) or with the numbers defined by the user using the `nan`, |
| 2368 | + `posinf` and/or `neginf` keywords. |
| 2369 | +
|
| 2370 | + If `x` is inexact, ``NaN`` is replaced by zero or by the user defined value |
| 2371 | + in `nan` keyword, infinity is replaced by the largest finite floating point |
| 2372 | + values representable by ``x.dtype`` or by the user defined value in |
| 2373 | + `posinf` keyword and -infinity is replaced by the most negative finite |
| 2374 | + floating point values representable by ``x.dtype`` or by the user defined |
| 2375 | + value in `neginf` keyword. |
| 2376 | +
|
| 2377 | + For complex dtypes, the above is applied to each of the real and |
| 2378 | + imaginary components of `x` separately. |
| 2379 | +
|
| 2380 | + If `x` is not inexact, then no replacements are made. |
| 2381 | +
|
| 2382 | + For full documentation refer to :obj:`numpy.nan_to_num`. |
| 2383 | +
|
| 2384 | + Parameters |
| 2385 | + ---------- |
| 2386 | + x : {dpnp.ndarray, usm_ndarray} |
| 2387 | + Input data. |
| 2388 | + copy : bool, optional |
| 2389 | + Whether to create a copy of `x` (``True``) or to replace values |
| 2390 | + in-place (``False``). The in-place operation only occurs if casting to |
| 2391 | + an array does not require a copy. |
| 2392 | + nan : {int, float, bool}, optional |
| 2393 | + Value to be used to fill ``NaN`` values. |
| 2394 | + Default: ``0.0``. |
| 2395 | + posinf : {int, float, bool, None}, optional |
| 2396 | + Value to be used to fill positive infinity values. If no value is |
| 2397 | + passed then positive infinity values will be replaced with a very |
| 2398 | + large number. |
| 2399 | + Default: ``None``. |
| 2400 | + neginf : {int, float, bool, None} optional |
| 2401 | + Value to be used to fill negative infinity values. If no value is |
| 2402 | + passed then negative infinity values will be replaced with a very |
| 2403 | + small (or negative) number. |
| 2404 | + Default: ``None``. |
| 2405 | +
|
| 2406 | + Returns |
| 2407 | + ------- |
| 2408 | + out : dpnp.ndarray |
| 2409 | + `x`, with the non-finite values replaced. If `copy` is ``False``, this |
| 2410 | + may be `x` itself. |
| 2411 | +
|
| 2412 | + See Also |
| 2413 | + -------- |
| 2414 | + :obj:`dpnp.isinf` : Shows which elements are positive or negative infinity. |
| 2415 | + :obj:`dpnp.isneginf` : Shows which elements are negative infinity. |
| 2416 | + :obj:`dpnp.isposinf` : Shows which elements are positive infinity. |
| 2417 | + :obj:`dpnp.isnan` : Shows which elements are Not a Number (NaN). |
| 2418 | + :obj:`dpnp.isfinite` : Shows which elements are finite |
| 2419 | + (not NaN, not infinity) |
| 2420 | +
|
| 2421 | + Examples |
| 2422 | + -------- |
| 2423 | + >>> import dpnp as np |
| 2424 | + >>> np.nan_to_num(np.array(np.inf)) |
| 2425 | + array(1.79769313e+308) |
| 2426 | + >>> np.nan_to_num(np.array(-np.inf)) |
| 2427 | + array(-1.79769313e+308) |
| 2428 | + >>> np.nan_to_num(np.array(np.nan)) |
| 2429 | + array(0.) |
| 2430 | + >>> x = np.array([np.inf, -np.inf, np.nan, -128, 128]) |
| 2431 | + >>> np.nan_to_num(x) |
| 2432 | + array([ 1.79769313e+308, -1.79769313e+308, 0.00000000e+000, |
| 2433 | + -1.28000000e+002, 1.28000000e+002]) |
| 2434 | + >>> np.nan_to_num(x, nan=-9999, posinf=33333333, neginf=33333333) |
| 2435 | + array([ 3.3333333e+07, 3.3333333e+07, -9.9990000e+03, -1.2800000e+02, |
| 2436 | + 1.2800000e+02]) |
| 2437 | + >>> y = np.array([complex(np.inf, np.nan), np.nan, complex(np.nan, np.inf)]) |
| 2438 | + >>> np.nan_to_num(y) |
| 2439 | + array([1.79769313e+308 +0.00000000e+000j, # may vary |
| 2440 | + 0.00000000e+000 +0.00000000e+000j, |
| 2441 | + 0.00000000e+000 +1.79769313e+308j]) |
| 2442 | + >>> np.nan_to_num(y, nan=111111, posinf=222222) |
| 2443 | + array([222222.+111111.j, 111111. +0.j, 111111.+222222.j]) |
| 2444 | +
|
| 2445 | + """ |
| 2446 | + |
| 2447 | + dpnp.check_supported_arrays_type(x) |
| 2448 | + |
| 2449 | + # Python boolean is a subtype of an integer |
| 2450 | + # so additional check for bool is not needed. |
| 2451 | + if not isinstance(nan, (int, float)): |
| 2452 | + raise TypeError( |
| 2453 | + "nan must be a scalar of an integer, float, bool, " |
| 2454 | + f"but got {type(nan)}" |
| 2455 | + ) |
| 2456 | + |
| 2457 | + out = dpnp.empty_like(x) if copy else x |
| 2458 | + x_type = x.dtype.type |
| 2459 | + |
| 2460 | + if not issubclass(x_type, dpnp.inexact): |
| 2461 | + return x |
| 2462 | + |
| 2463 | + parts = ( |
| 2464 | + (x.real, x.imag) if issubclass(x_type, dpnp.complexfloating) else (x,) |
| 2465 | + ) |
| 2466 | + parts_out = ( |
| 2467 | + (out.real, out.imag) |
| 2468 | + if issubclass(x_type, dpnp.complexfloating) |
| 2469 | + else (out,) |
| 2470 | + ) |
| 2471 | + max_f, min_f = _get_max_min(x.real.dtype) |
| 2472 | + if posinf is not None: |
| 2473 | + if not isinstance(posinf, (int, float)): |
| 2474 | + raise TypeError( |
| 2475 | + "posinf must be a scalar of an integer, float, bool, " |
| 2476 | + f"or be None, but got {type(posinf)}" |
| 2477 | + ) |
| 2478 | + max_f = posinf |
| 2479 | + if neginf is not None: |
| 2480 | + if not isinstance(neginf, (int, float)): |
| 2481 | + raise TypeError( |
| 2482 | + "neginf must be a scalar of an integer, float, bool, " |
| 2483 | + f"or be None, but got {type(neginf)}" |
| 2484 | + ) |
| 2485 | + min_f = neginf |
| 2486 | + |
| 2487 | + for part, part_out in zip(parts, parts_out): |
| 2488 | + nan_mask = dpnp.isnan(part) |
| 2489 | + posinf_mask = dpnp.isposinf(part) |
| 2490 | + neginf_mask = dpnp.isneginf(part) |
| 2491 | + |
| 2492 | + part = dpnp.where(nan_mask, nan, part, out=part_out) |
| 2493 | + part = dpnp.where(posinf_mask, max_f, part, out=part_out) |
| 2494 | + part = dpnp.where(neginf_mask, min_f, part, out=part_out) |
| 2495 | + |
| 2496 | + return out |
| 2497 | + |
| 2498 | + |
2356 | 2499 | _NEGATIVE_DOCSTRING = """ |
2357 | 2500 | Computes the numerical negative for each element `x_i` of input array `x`. |
2358 | 2501 |
|
|
0 commit comments