Skip to content

Commit 46b166a

Browse files
committed
Update statistics_tests/test_order.py
1 parent a4a89f5 commit 46b166a

File tree

1 file changed

+211
-31
lines changed

1 file changed

+211
-31
lines changed

tests/third_party/cupy/statistics_tests/test_order.py

Lines changed: 211 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,85 +7,130 @@
77
from tests.third_party.cupy import testing
88

99
_all_methods = (
10-
# "inverted_cdf", # TODO(takagi) Not implemented
11-
# "averaged_inverted_cdf", # TODO(takagi) Not implemented
12-
# "closest_observation", # TODO(takagi) Not implemented
13-
# "interpolated_inverted_cdf", # TODO(takagi) Not implemented
14-
# "hazen", # TODO(takagi) Not implemented
15-
# "weibull", # TODO(takagi) Not implemented
10+
# 'inverted_cdf', # TODO(takagi) Not implemented
11+
# 'averaged_inverted_cdf', # TODO(takagi) Not implemented
12+
# 'closest_observation', # TODO(takagi) Not implemented
13+
# 'interpolated_inverted_cdf', # TODO(takagi) Not implemented
14+
# 'hazen', # TODO(takagi) Not implemented
15+
# 'weibull', # TODO(takagi) Not implemented
1616
"linear",
17-
# "median_unbiased", # TODO(takagi) Not implemented
18-
# "normal_unbiased", # TODO(takagi) Not implemented
17+
# 'median_unbiased', # TODO(takagi) Not implemented
18+
# 'normal_unbiased', # TODO(takagi) Not implemented
1919
"lower",
2020
"higher",
2121
"midpoint",
22-
# "nearest", # TODO(hvy): Not implemented
22+
"nearest",
2323
)
2424

2525

2626
def for_all_methods(name="method"):
2727
return pytest.mark.parametrize(name, _all_methods)
2828

2929

30+
@pytest.mark.skip("dpnp.quantile() is not implemented yet")
3031
@testing.with_requires("numpy>=1.22.0rc1")
31-
class TestOrder:
32-
@for_all_methods()
32+
class TestQuantile:
33+
34+
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
35+
def test_percentile_unexpected_method(self, dtype):
36+
for xp in (numpy, cupy):
37+
a = testing.shaped_random((4, 2, 3, 2), xp, dtype)
38+
q = testing.shaped_random((5,), xp, dtype=dtype, scale=100)
39+
with pytest.raises(ValueError):
40+
xp.percentile(a, q, axis=-1, method="deadbeef")
41+
42+
# See gh-4453
43+
@testing.for_float_dtypes()
44+
def test_percentile_memory_access(self, dtype):
45+
# Create an allocator that guarantees array allocated in
46+
# cupy.percentile call will be followed by a NaN
47+
original_allocator = cuda.get_allocator()
48+
49+
def controlled_allocator(size):
50+
memptr = original_allocator(size)
51+
base_size = memptr.mem.size
52+
assert base_size % 512 == 0
53+
item_size = dtype().itemsize
54+
shape = (base_size // item_size,)
55+
x = cupy.ndarray(memptr=memptr, shape=shape, dtype=dtype)
56+
x.fill(cupy.nan)
57+
return memptr
58+
59+
# Check that percentile still returns non-NaN results
60+
a = testing.shaped_random((5,), cupy, dtype)
61+
q = cupy.array((0, 100), dtype=dtype)
62+
63+
cuda.set_allocator(controlled_allocator)
64+
try:
65+
percentiles = cupy.percentile(a, q, axis=None, method="linear")
66+
finally:
67+
cuda.set_allocator(original_allocator)
68+
69+
assert not cupy.any(cupy.isnan(percentiles))
70+
71+
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
72+
def test_quantile_unexpected_method(self, dtype):
73+
for xp in (numpy, cupy):
74+
a = testing.shaped_random((4, 2, 3, 2), xp, dtype)
75+
q = testing.shaped_random((5,), xp, dtype=dtype, scale=1)
76+
with pytest.raises(ValueError):
77+
xp.quantile(a, q, axis=-1, method="deadbeef")
78+
79+
80+
@pytest.mark.skip("dpnp.quantile() is not implemented yet")
81+
@testing.with_requires("numpy>=1.22.0rc1")
82+
@for_all_methods()
83+
class TestQuantileMethods:
84+
3385
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
3486
@testing.numpy_cupy_allclose()
3587
def test_percentile_defaults(self, xp, dtype, method):
3688
a = testing.shaped_random((2, 3, 8), xp, dtype)
3789
q = testing.shaped_random((3,), xp, dtype=dtype, scale=100)
3890
return xp.percentile(a, q, method=method)
3991

40-
@for_all_methods()
4192
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
4293
@testing.numpy_cupy_allclose()
4394
def test_percentile_q_list(self, xp, dtype, method):
4495
a = testing.shaped_arange((1001,), xp, dtype)
4596
q = [99, 99.9]
4697
return xp.percentile(a, q, method=method)
4798

48-
@for_all_methods()
4999
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
50100
@testing.numpy_cupy_allclose(rtol=1e-6)
51101
def test_percentile_no_axis(self, xp, dtype, method):
52102
a = testing.shaped_random((10, 2, 4, 8), xp, dtype)
53103
q = testing.shaped_random((5,), xp, dtype=dtype, scale=100)
54104
return xp.percentile(a, q, axis=None, method=method)
55105

56-
@for_all_methods()
57106
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
58107
@testing.numpy_cupy_allclose(rtol=1e-6)
59108
def test_percentile_neg_axis(self, xp, dtype, method):
60109
a = testing.shaped_random((4, 3, 10, 2, 8), xp, dtype)
61110
q = testing.shaped_random((5,), xp, dtype=dtype, scale=100)
62111
return xp.percentile(a, q, axis=-1, method=method)
63112

64-
@for_all_methods()
65113
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
66114
@testing.numpy_cupy_allclose(rtol=1e-6)
67115
def test_percentile_tuple_axis(self, xp, dtype, method):
68116
a = testing.shaped_random((1, 6, 3, 2), xp, dtype)
69117
q = testing.shaped_random((5,), xp, dtype=dtype, scale=100)
70118
return xp.percentile(a, q, axis=(0, 1, 2), method=method)
71119

72-
@for_all_methods()
73120
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
74121
@testing.numpy_cupy_allclose()
75122
def test_percentile_scalar_q(self, xp, dtype, method):
76123
a = testing.shaped_random((2, 3, 8), xp, dtype)
77124
q = 13.37
78125
return xp.percentile(a, q, method=method)
79126

80-
@for_all_methods()
81127
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
82128
@testing.numpy_cupy_allclose(rtol=1e-5)
83129
def test_percentile_keepdims(self, xp, dtype, method):
84130
a = testing.shaped_random((7, 2, 9, 2), xp, dtype)
85131
q = testing.shaped_random((5,), xp, dtype=dtype, scale=100)
86132
return xp.percentile(a, q, axis=None, keepdims=True, method=method)
87133

88-
@for_all_methods()
89134
@testing.for_float_dtypes(no_float16=True) # NumPy raises error on int8
90135
@testing.numpy_cupy_allclose(rtol=1e-6)
91136
def test_percentile_out(self, xp, dtype, method):
@@ -94,7 +139,17 @@ def test_percentile_out(self, xp, dtype, method):
94139
out = testing.shaped_random((5, 10, 2, 3), xp, dtype)
95140
return xp.percentile(a, q, axis=-1, method=method, out=out)
96141

97-
@for_all_methods()
142+
@testing.for_float_dtypes(no_float16=True)
143+
@testing.numpy_cupy_allclose(rtol=1e-6)
144+
def test_percentile_overwrite(self, xp, dtype, method):
145+
a = testing.shaped_random((10, 2, 3, 2), xp, dtype)
146+
ap = a.copy()
147+
q = testing.shaped_random((5,), xp, dtype=dtype, scale=100)
148+
res = xp.percentile(ap, q, axis=-1, method=method, overwrite_input=True)
149+
150+
assert not xp.all(ap == a)
151+
return res
152+
98153
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
99154
def test_percentile_bad_q(self, dtype, method):
100155
for xp in (numpy, cupy):
@@ -104,38 +159,127 @@ def test_percentile_bad_q(self, dtype, method):
104159
xp.percentile(a, q, axis=-1, method=method)
105160

106161
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
107-
def test_percentile_unxpected_method(self, dtype):
162+
def test_percentile_out_of_range_q(self, dtype, method):
108163
for xp in (numpy, cupy):
109164
a = testing.shaped_random((4, 2, 3, 2), xp, dtype)
110-
q = testing.shaped_random((5,), xp, dtype=dtype, scale=100)
165+
for q in [[-0.1], [100.1]]:
166+
with pytest.raises(ValueError):
167+
xp.percentile(a, q, axis=-1, method=method)
168+
169+
@testing.for_all_dtypes()
170+
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
171+
@testing.numpy_cupy_allclose()
172+
def test_quantile_defaults(self, xp, dtype, method):
173+
a = testing.shaped_random((2, 3, 8), xp, dtype)
174+
q = testing.shaped_random((3,), xp, scale=1)
175+
return xp.quantile(a, q, method=method)
176+
177+
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
178+
@testing.numpy_cupy_allclose()
179+
def test_quantile_q_list(self, xp, dtype, method):
180+
a = testing.shaped_arange((1001,), xp, dtype)
181+
q = [0.99, 0.999]
182+
return xp.quantile(a, q, method=method)
183+
184+
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
185+
@testing.numpy_cupy_allclose(rtol=1e-5)
186+
def test_quantile_no_axis(self, xp, dtype, method):
187+
a = testing.shaped_random((10, 2, 4, 8), xp, dtype)
188+
q = testing.shaped_random((5,), xp, scale=1)
189+
return xp.quantile(a, q, axis=None, method=method)
190+
191+
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
192+
@testing.numpy_cupy_allclose(rtol=1e-6)
193+
def test_quantile_neg_axis(self, xp, dtype, method):
194+
a = testing.shaped_random((4, 3, 10, 2, 8), xp, dtype)
195+
q = testing.shaped_random((5,), xp, scale=1)
196+
return xp.quantile(a, q, axis=-1, method=method)
197+
198+
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
199+
@testing.numpy_cupy_allclose(rtol=1e-6)
200+
def test_quantile_tuple_axis(self, xp, dtype, method):
201+
a = testing.shaped_random((1, 6, 3, 2), xp, dtype)
202+
q = testing.shaped_random((5,), xp, scale=1)
203+
return xp.quantile(a, q, axis=(0, 1, 2), method=method)
204+
205+
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
206+
@testing.numpy_cupy_allclose()
207+
def test_quantile_scalar_q(self, xp, dtype, method):
208+
a = testing.shaped_random((2, 3, 8), xp, dtype)
209+
q = 0.1337
210+
return xp.quantile(a, q, method=method)
211+
212+
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
213+
@testing.numpy_cupy_allclose(rtol=1e-5)
214+
def test_quantile_keepdims(self, xp, dtype, method):
215+
a = testing.shaped_random((7, 2, 9, 2), xp, dtype)
216+
q = testing.shaped_random((5,), xp, scale=1)
217+
return xp.quantile(a, q, axis=None, keepdims=True, method=method)
218+
219+
@testing.for_float_dtypes(no_float16=True) # NumPy raises error on int8
220+
@testing.numpy_cupy_allclose(rtol=1e-6)
221+
def test_quantile_out(self, xp, dtype, method):
222+
a = testing.shaped_random((10, 2, 3, 2), xp, dtype)
223+
q = testing.shaped_random((5,), xp, dtype=dtype, scale=1)
224+
out = testing.shaped_random((5, 10, 2, 3), xp, dtype)
225+
return xp.quantile(a, q, axis=-1, method=method, out=out)
226+
227+
@testing.for_float_dtypes(no_float16=True)
228+
@testing.numpy_cupy_allclose(rtol=1e-6)
229+
def test_quantile_overwrite(self, xp, dtype, method):
230+
a = testing.shaped_random((10, 2, 3, 2), xp, dtype)
231+
ap = a.copy()
232+
q = testing.shaped_random((5,), xp, dtype=dtype, scale=1)
233+
234+
res = xp.quantile(a, q, axis=-1, method=method, overwrite_input=True)
235+
236+
assert not xp.all(ap == a)
237+
return res
238+
239+
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
240+
def test_quantile_bad_q(self, dtype, method):
241+
for xp in (numpy, cupy):
242+
a = testing.shaped_random((4, 2, 3, 2), xp, dtype)
243+
q = testing.shaped_random((1, 2, 3), xp, dtype=dtype, scale=1)
111244
with pytest.raises(ValueError):
112-
xp.percentile(a, q, axis=-1, method="deadbeef")
245+
xp.quantile(a, q, axis=-1, method=method)
246+
247+
@testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
248+
def test_quantile_out_of_range_q(self, dtype, method):
249+
for xp in (numpy, cupy):
250+
a = testing.shaped_random((4, 2, 3, 2), xp, dtype)
251+
for q in [[-0.1], [1.1]]:
252+
with pytest.raises(ValueError):
253+
xp.quantile(a, q, axis=-1, method=method)
254+
255+
256+
class TestOrder:
113257

114258
@testing.for_all_dtypes(no_complex=True)
115259
@testing.numpy_cupy_allclose()
116260
def test_nanmax_all(self, xp, dtype):
117261
a = testing.shaped_random((2, 3), xp, dtype)
118262
return xp.nanmax(a)
119263

120-
@testing.for_all_dtypes(no_complex=True)
264+
@testing.for_all_dtypes()
121265
@testing.numpy_cupy_allclose()
122266
def test_nanmax_axis_large(self, xp, dtype):
123267
a = testing.shaped_random((3, 1000), xp, dtype)
124268
return xp.nanmax(a, axis=0)
125269

126-
@testing.for_all_dtypes(no_complex=True)
270+
@testing.for_all_dtypes()
127271
@testing.numpy_cupy_allclose()
128272
def test_nanmax_axis0(self, xp, dtype):
129273
a = testing.shaped_random((2, 3, 4), xp, dtype)
130274
return xp.nanmax(a, axis=0)
131275

132-
@testing.for_all_dtypes(no_complex=True)
276+
@testing.for_all_dtypes()
133277
@testing.numpy_cupy_allclose()
134278
def test_nanmax_axis1(self, xp, dtype):
135279
a = testing.shaped_random((2, 3, 4), xp, dtype)
136280
return xp.nanmax(a, axis=1)
137281

138-
@testing.for_all_dtypes(no_complex=True)
282+
@testing.for_all_dtypes()
139283
@testing.numpy_cupy_allclose()
140284
def test_nanmax_axis2(self, xp, dtype):
141285
a = testing.shaped_random((2, 3, 4), xp, dtype)
@@ -159,31 +303,31 @@ def test_nanmax_all_nan(self, xp, dtype):
159303
assert w[0].category is RuntimeWarning
160304
return m
161305

162-
@testing.for_all_dtypes(no_complex=True)
306+
@testing.for_all_dtypes()
163307
@testing.numpy_cupy_allclose()
164308
def test_nanmin_all(self, xp, dtype):
165309
a = testing.shaped_random((2, 3), xp, dtype)
166310
return xp.nanmin(a)
167311

168-
@testing.for_all_dtypes(no_complex=True)
312+
@testing.for_all_dtypes()
169313
@testing.numpy_cupy_allclose()
170314
def test_nanmin_axis_large(self, xp, dtype):
171315
a = testing.shaped_random((3, 1000), xp, dtype)
172316
return xp.nanmin(a, axis=0)
173317

174-
@testing.for_all_dtypes(no_complex=True)
318+
@testing.for_all_dtypes()
175319
@testing.numpy_cupy_allclose()
176320
def test_nanmin_axis0(self, xp, dtype):
177321
a = testing.shaped_random((2, 3, 4), xp, dtype)
178322
return xp.nanmin(a, axis=0)
179323

180-
@testing.for_all_dtypes(no_complex=True)
324+
@testing.for_all_dtypes()
181325
@testing.numpy_cupy_allclose()
182326
def test_nanmin_axis1(self, xp, dtype):
183327
a = testing.shaped_random((2, 3, 4), xp, dtype)
184328
return xp.nanmin(a, axis=1)
185329

186-
@testing.for_all_dtypes(no_complex=True)
330+
@testing.for_all_dtypes()
187331
@testing.numpy_cupy_allclose()
188332
def test_nanmin_axis2(self, xp, dtype):
189333
a = testing.shaped_random((2, 3, 4), xp, dtype)
@@ -248,3 +392,39 @@ def test_ptp_nan(self, xp, dtype):
248392
def test_ptp_all_nan(self, xp, dtype):
249393
a = xp.array([float("nan"), float("nan")], dtype)
250394
return xp.ptp(a)
395+
396+
397+
# See gh-4607
398+
# "Magic" values used in this test were empirically found to result in
399+
# non-monotonicity for less accurate linear interpolation formulas
400+
@pytest.mark.skip("dpnp.percentile() is not implemented yet")
401+
@testing.parameterize(
402+
*testing.product(
403+
{
404+
"magic_value": (
405+
-29,
406+
-53,
407+
-207,
408+
-16373,
409+
-99999,
410+
)
411+
}
412+
)
413+
)
414+
class TestPercentileMonotonic:
415+
416+
@testing.with_requires("numpy>=1.22.0rc1")
417+
@testing.for_float_dtypes(no_float16=True)
418+
@testing.numpy_cupy_allclose()
419+
def test_percentile_monotonic(self, dtype, xp):
420+
a = testing.shaped_random((5,), xp, dtype)
421+
422+
a[0] = self.magic_value
423+
a[1] = self.magic_value
424+
q = xp.linspace(0, 100, 21)
425+
percentiles = xp.percentile(a, q, method="linear")
426+
427+
# Assert that percentile output increases monotonically
428+
assert xp.all(xp.diff(percentiles) >= 0)
429+
430+
return percentiles

0 commit comments

Comments
 (0)