Skip to content

Commit 5b9ffe3

Browse files
authored
Merge d318b15 into 996cf54
2 parents 996cf54 + d318b15 commit 5b9ffe3

File tree

9 files changed

+205
-22
lines changed

9 files changed

+205
-22
lines changed

dpnp/dpnp_array.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,16 @@ def choose(input, choices, out=None, mode="raise"):
628628

629629
return dpnp.choose(input, choices, out, mode)
630630

631-
# 'clip',
631+
def clip(self, min=None, max=None, out=None, **kwargs):
632+
"""
633+
Clip (limit) the values in an array.
634+
635+
Refer to :obj:`dpnp.clip` for full documentation.
636+
637+
"""
638+
639+
return dpnp.clip(self, min, max, out=out, **kwargs)
640+
632641
# 'compress',
633642

634643
def conj(self):

dpnp/dpnp_iface_mathematical.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
"add",
8585
"around",
8686
"ceil",
87+
"clip",
8788
"conj",
8889
"conjugate",
8990
"convolve",
@@ -381,6 +382,78 @@ def ceil(
381382
)
382383

383384

385+
def clip(a, a_min, a_max, *, out=None, order="K", **kwargs):
386+
"""
387+
Clip (limit) the values in an array.
388+
389+
For full documentation refer to :obj:`numpy.clip`.
390+
391+
Parameters
392+
----------
393+
a : {dpnp_array, usm_ndarray}
394+
Array containing elements to clip.
395+
a_min, a_max : {dpnp_array, usm_ndarray, None}
396+
Minimum and maximum value. If ``None``, clipping is not performed on the corresponding edge.
397+
Only one of `a_min` and `a_max` may be ``None``. Both are broadcast against `a`.
398+
out : {dpnp_array, usm_ndarray}, optional
399+
The results will be placed in this array. It may be the input array for in-place clipping.
400+
`out` must be of the right shape to hold the output. Its type is preserved.
401+
order : {"C", "F", "A", "K", None}, optional
402+
Memory layout of the newly output array, if parameter `out` is `None`.
403+
If `order` is ``None``, the default value "K" will be used.
404+
405+
Returns
406+
-------
407+
out : dpnp_array
408+
An array with the elements of `a`, but where values < `a_min` are replaced with `a_min`,
409+
and those > `a_max` with `a_max`.
410+
411+
Limitations
412+
-----------
413+
Keyword argument `kwargs` is currently unsupported.
414+
Otherwise ``NotImplementedError`` exception will be raised.
415+
416+
Examples
417+
--------
418+
>>> import dpnp as np
419+
>>> a = np.arange(10)
420+
>>> a
421+
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
422+
>>> np.clip(a, 1, 8)
423+
array([1, 1, 2, 3, 4, 5, 6, 7, 8, 8])
424+
>>> np.clip(a, 8, 1)
425+
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
426+
>>> np.clip(a, 3, 6, out=a)
427+
array([3, 3, 3, 3, 4, 5, 6, 6, 6, 6])
428+
>>> a
429+
array([3, 3, 3, 3, 4, 5, 6, 6, 6, 6])
430+
431+
>>> a = np.arange(10)
432+
>>> a
433+
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
434+
>>> min = np.array([3, 4, 1, 1, 1, 4, 4, 4, 4, 4])
435+
>>> np.clip(a, min, 8)
436+
array([3, 4, 2, 3, 4, 5, 6, 7, 8, 8])
437+
438+
"""
439+
440+
if kwargs:
441+
raise NotImplementedError(f"kwargs={kwargs} is currently not supported")
442+
443+
if order is None:
444+
order = "K"
445+
446+
usm_arr = dpnp.get_usm_ndarray(a)
447+
usm_min = None if a_min is None else dpnp.get_usm_ndarray_or_scalar(a_min)
448+
usm_max = None if a_max is None else dpnp.get_usm_ndarray_or_scalar(a_max)
449+
450+
usm_out = None if out is None else dpnp.get_usm_ndarray(out)
451+
usm_res = dpt.clip(usm_arr, usm_min, usm_max, out=usm_out, order=order)
452+
if out is not None and isinstance(out, dpnp_array):
453+
return out
454+
return dpnp_array._create_from_usm_ndarray(usm_res)
455+
456+
384457
def conjugate(
385458
x,
386459
/,

tests/skipped_tests.tbl

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -412,8 +412,6 @@ tests/third_party/cupy/math_tests/test_floating.py::TestFloating::test_ldexp
412412
tests/third_party/cupy/math_tests/test_floating.py::TestFloating::test_nextafter_combination
413413
tests/third_party/cupy/math_tests/test_floating.py::TestFloating::test_nextafter_float
414414

415-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_clip_min_max_none
416-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_external_clip4
417415
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_fmax_nan
418416
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_fmin_nan
419417
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num
@@ -435,14 +433,6 @@ tests/third_party/cupy/math_tests/test_misc.py::TestConvolveInvalid_param_1_{mod
435433
tests/third_party/cupy/math_tests/test_misc.py::TestConvolveInvalid_param_2_{mode='full'}::test_convolve_empty
436434
tests/third_party/cupy/math_tests/test_misc.py::TestConvolveInvalid_param_2_{mode='full'}::test_convolve_ndim
437435

438-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_clip1
439-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_clip3
440-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_clip_min_none
441-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_clip_max_none
442-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_external_clip1
443-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_external_clip2
444-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_external_clip3
445-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_clip2
446436
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_fabs
447437
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_fabs_negative
448438
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_scalar_nan

tests/skipped_tests_gpu.tbl

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -509,8 +509,6 @@ tests/third_party/cupy/math_tests/test_floating.py::TestFloating::test_ldexp
509509
tests/third_party/cupy/math_tests/test_floating.py::TestFloating::test_nextafter_combination
510510
tests/third_party/cupy/math_tests/test_floating.py::TestFloating::test_nextafter_float
511511

512-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_clip_min_max_none
513-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_external_clip4
514512
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_fmax_nan
515513
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_fmin_nan
516514
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num
@@ -532,14 +530,7 @@ tests/third_party/cupy/math_tests/test_misc.py::TestConvolveInvalid_param_1_{mod
532530
tests/third_party/cupy/math_tests/test_misc.py::TestConvolveInvalid_param_2_{mode='full'}::test_convolve_empty
533531
tests/third_party/cupy/math_tests/test_misc.py::TestConvolveInvalid_param_2_{mode='full'}::test_convolve_ndim
534532

535-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_clip1
536-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_clip3
537-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_clip_min_none
538-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_clip_max_none
539-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_external_clip1
540-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_external_clip2
541-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_external_clip3
542-
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_clip2
533+
543534
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_fabs
544535
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_fabs_negative
545536
tests/third_party/cupy/math_tests/test_misc.py::TestMisc::test_nan_to_num_scalar_nan

tests/test_dparray.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,3 +246,12 @@ def test_repeat():
246246
numpy_array = numpy.arange(4).repeat(3)
247247
dpnp_array = dpnp.arange(4).repeat(3)
248248
assert_array_equal(numpy_array, dpnp_array)
249+
250+
251+
def test_clip():
252+
numpy_array = numpy.arange(10)
253+
dpnp_array = dpnp.arange(10)
254+
result = dpnp.clip(dpnp_array, 3, 7)
255+
expected = numpy.clip(numpy_array, 3, 7)
256+
257+
assert_array_equal(expected, result)

tests/test_mathematical.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
)
1414

1515
import dpnp
16+
from dpnp.dpnp_array import dpnp_array
1617

1718
from .helper import (
1819
assert_dtype_allclose,
@@ -27,6 +28,98 @@
2728
)
2829

2930

31+
class TestClip:
32+
@pytest.mark.parametrize(
33+
"dtype", get_all_dtypes(no_bool=True, no_none=True, no_complex=True)
34+
)
35+
@pytest.mark.parametrize("order", ["C", "F", "A", "K", None])
36+
def test_clip(self, dtype, order):
37+
dp_a = dpnp.asarray([[1, 2, 8], [1, 6, 4], [9, 5, 1]], dtype=dtype)
38+
np_a = dpnp.asnumpy(dp_a)
39+
40+
result = dpnp.clip(dp_a, 2, 6, order=order)
41+
expected = numpy.clip(np_a, 2, 6, order=order)
42+
assert_allclose(expected, result)
43+
assert expected.flags.c_contiguous == result.flags.c_contiguous
44+
assert expected.flags.f_contiguous == result.flags.f_contiguous
45+
46+
@pytest.mark.parametrize(
47+
"dtype", get_all_dtypes(no_bool=True, no_none=True, no_complex=True)
48+
)
49+
def test_clip_arrays(self, dtype):
50+
dp_a = dpnp.asarray([1, 2, 8, 1, 6, 4, 1], dtype=dtype)
51+
np_a = dpnp.asnumpy(dp_a)
52+
53+
min_v = dpnp.asarray(2, dtype=dtype)
54+
max_v = dpnp.asarray(6, dtype=dtype)
55+
56+
result = dpnp.clip(dp_a, min_v, max_v)
57+
expected = numpy.clip(np_a, min_v.asnumpy(), max_v.asnumpy())
58+
assert_allclose(expected, result)
59+
60+
@pytest.mark.parametrize(
61+
"dtype", get_all_dtypes(no_bool=True, no_none=True, no_complex=True)
62+
)
63+
@pytest.mark.parametrize("in_dp", [dpnp, dpt])
64+
@pytest.mark.parametrize("out_dp", [dpnp, dpt])
65+
def test_clip_out(self, dtype, in_dp, out_dp):
66+
np_a = numpy.array([[1, 2, 8], [1, 6, 4], [9, 5, 1]], dtype=dtype)
67+
dp_a = in_dp.asarray(np_a)
68+
69+
dp_out = out_dp.ones(dp_a.shape, dtype=dtype)
70+
np_out = numpy.ones(np_a.shape, dtype=dtype)
71+
72+
result = dpnp.clip(dp_a, 2, 6, out=dp_out)
73+
expected = numpy.clip(np_a, 2, 6, out=np_out)
74+
assert_allclose(expected, result)
75+
assert_allclose(np_out, dp_out)
76+
assert isinstance(result, dpnp_array)
77+
78+
def test_input_nan(self):
79+
np_a = numpy.array([-2.0, numpy.nan, 0.5, 3.0, 0.25, numpy.nan])
80+
dp_a = dpnp.array(np_a)
81+
82+
result = dpnp.clip(dp_a, -1, 1)
83+
expected = numpy.clip(np_a, -1, 1)
84+
assert_array_equal(result, expected)
85+
86+
# TODO: unmute the test once dpctl resolves the issue
87+
@pytest.mark.skip(reason="dpctl-1489 issue")
88+
@pytest.mark.parametrize(
89+
"kwargs",
90+
[
91+
{"min": numpy.nan},
92+
{"max": numpy.nan},
93+
{"min": numpy.nan, "max": numpy.nan},
94+
{"min": -2, "max": numpy.nan},
95+
{"min": numpy.nan, "max": 10},
96+
],
97+
)
98+
def test_nan_edges(self, kwargs):
99+
np_a = numpy.arange(7)
100+
dp_a = dpnp.asarray(np_a)
101+
102+
result = dp_a.clip(**kwargs)
103+
expected = np_a.clip(**kwargs)
104+
assert_allclose(expected, result)
105+
106+
@pytest.mark.parametrize(
107+
"kwargs",
108+
[
109+
{"casting": "same_kind"},
110+
{"dtype": "i8"},
111+
{"subok": True},
112+
{"where": True},
113+
],
114+
)
115+
def test_not_implemented_kwargs(self, kwargs):
116+
a = dpnp.arange(8, dtype="i4")
117+
118+
numpy.clip(a.asnumpy(), 1, 5, **kwargs)
119+
with pytest.raises(NotImplementedError):
120+
dpnp.clip(a, 1, 5, **kwargs)
121+
122+
30123
class TestDiff:
31124
@pytest.mark.parametrize("n", list(range(0, 3)))
32125
@pytest.mark.parametrize("dt", get_integer_dtypes())

tests/test_sycl_queue.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,3 +1372,14 @@ def test_solve(device):
13721372

13731373
assert_sycl_queue_equal(result_queue, dpnp_x.sycl_queue)
13741374
assert_sycl_queue_equal(result_queue, dpnp_y.sycl_queue)
1375+
1376+
1377+
@pytest.mark.parametrize(
1378+
"device",
1379+
valid_devices,
1380+
ids=[device.filter_string for device in valid_devices],
1381+
)
1382+
def test_clip(device):
1383+
x = dpnp.arange(10, device=device)
1384+
y = dpnp.clip(x, 3, 7)
1385+
assert_sycl_queue_equal(x.sycl_queue, y.sycl_queue)

tests/test_usm_type.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,3 +527,10 @@ def test_solve(matrix, vector, usm_type_matrix, usm_type_vector):
527527
assert z.usm_type == du.get_coerced_usm_type(
528528
[usm_type_matrix, usm_type_vector]
529529
)
530+
531+
532+
@pytest.mark.parametrize("usm_type", list_of_usm_types, ids=list_of_usm_types)
533+
def test_clip(usm_type):
534+
x = dp.arange(10, usm_type=usm_type)
535+
y = dp.clip(x, 2, 7)
536+
assert x.usm_type == y.usm_type

tests/third_party/cupy/math_tests/test_misc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ def test_convolve_non_contiguous(self, xp, dtype, mode):
519519
return xp.convolve(a[::200], b[10::70], mode=mode)
520520

521521
@testing.for_all_dtypes(no_float16=True)
522-
@testing.numpy_cupy_allclose(rtol=1e-4)
522+
@testing.numpy_cupy_allclose(rtol=5e-4)
523523
def test_convolve_large_non_contiguous(self, xp, dtype, mode):
524524
a = testing.shaped_arange((10000,), xp, dtype)
525525
b = testing.shaped_arange((100,), xp, dtype)

0 commit comments

Comments
 (0)