Repository: cupy/cupy

Issue:
Support NumPy 1.22
## [Tasks](https://github.com/cupy/cupy/wiki/Actions-Needed-for-Dependency-Update)

- [x] Read Release Notes.
- [x] Update FlexCI schema/matrix to test against the version. #6323
- [x] For NumPy/SciPy, update requirements in `setup.py`.
  - [x] Update the pinned upper bound version (see #4773) #6323
  - [x] Backport. #6354
- [x] Update documentation
  - [x] Update Requirements section in [Installation Guide](https://docs.cupy.dev/en/latest/install.html). The baseline version must be updated in the development branch. #6323
  - [x] Update `docs/requirements.txt` to use the latest versions. #6323
  - [x] Backport. Do not update the baseline version in backport. #6354
- [x] Update CuPy codebase (if needed) and backport.


----

Test result with NumPy 1.22.0rc1: https://ci.preferred.jp/cupy.linux.cuda-head/88054/

Output dtype must be `float32` instead of `float64` (ref #5583):

```
cupy_tests/linalg_tests/test_decomposition.py::TestQRDecomposition_param_1_{mode='raw'}::test_empty_array	
cupy_tests/linalg_tests/test_decomposition.py::TestQRDecomposition_param_1_{mode='raw'}::test_mode	
cupy_tests/linalg_tests/test_decomposition.py::TestQRDecomposition_param_1_{mode='raw'}::test_mode_rank3	
cupy_tests/linalg_tests/test_decomposition.py::TestQRDecomposition_param_1_{mode='raw'}::test_mode_rank4	
```

Add `method` option (`interpolation` deprecated) in `quantile` and `percentile`:

```
cupy_tests/statistics_tests/test_order.py::TestPercentileMonotonic_param_0_{magic_value=-29}::test_percentile_monotonic	
cupy_tests/statistics_tests/test_order.py::TestPercentileMonotonic_param_1_{magic_value=-53}::test_percentile_monotonic	
cupy_tests/statistics_tests/test_order.py::TestPercentileMonotonic_param_2_{magic_value=-207}::test_percentile_monotonic	
cupy_tests/statistics_tests/test_order.py::TestPercentileMonotonic_param_3_{magic_value=-16373}::test_percentile_monotonic	
cupy_tests/statistics_tests/test_order.py::TestPercentileMonotonic_param_4_{magic_value=-99999}::test_percentile_monotonic	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_percentile_bad_q	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_percentile_defaults	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_percentile_keepdims	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_percentile_neg_axis	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_percentile_no_axis	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_percentile_out	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_percentile_out_of_range_q	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_percentile_q_list	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_percentile_scalar_q	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_percentile_tuple_axis	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_percentile_unexpected_interpolation	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_quantile_bad_q	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_quantile_defaults	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_quantile_keepdims	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_quantile_neg_axis	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_quantile_no_axis	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_quantile_out	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_quantile_out_of_range_q	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_quantile_q_list	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_quantile_scalar_q	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_quantile_tuple_axis	
cupy_tests/statistics_tests/test_order.py::TestOrder::test_quantile_unexpected_interpolation	
```



Discussion:

Resolving patch:
diff --git a/cupy/_statistics/order.py b/cupy/_statistics/order.py
--- a/cupy/_statistics/order.py
+++ b/cupy/_statistics/order.py
@@ -174,7 +174,7 @@ def ptp(a, axis=None, out=None, keepdims=False):
     return a.ptp(axis=axis, out=out, keepdims=keepdims)
 
 
-def _quantile_unchecked(a, q, axis=None, out=None, interpolation='linear',
+def _quantile_unchecked(a, q, axis=None, out=None, method='linear',
                         keepdims=False):
     if q.ndim == 0:
         q = q[None]
@@ -213,22 +213,28 @@ def _quantile_unchecked(a, q, axis=None, out=None, interpolation='linear',
     Nx = ap.shape[axis]
     indices = q * (Nx - 1.)
 
-    if interpolation == 'lower':
+    if method in ['inverted_cdf', 'averaged_inverted_cdf',
+                  'closest_observation', 'interpolated_inverted_cdf',
+                  'hazen', 'weibull', 'median_unbiased', 'normal_unbiased']:
+        # TODO(takagi) Implement new methods introduced in NumPy 1.22
+        raise ValueError(f'\'{method}\' method is not yet supported. '
+                         'Please use any other method.')
+    elif method == 'lower':
         indices = cupy.floor(indices).astype(cupy.int32)
-    elif interpolation == 'higher':
+    elif method == 'higher':
         indices = cupy.ceil(indices).astype(cupy.int32)
-    elif interpolation == 'midpoint':
+    elif method == 'midpoint':
         indices = 0.5 * (cupy.floor(indices) + cupy.ceil(indices))
-    elif interpolation == 'nearest':
+    elif method == 'nearest':
         # TODO(hvy): Implement nearest using around
-        raise ValueError('\'nearest\' interpolation is not yet supported. '
-                         'Please use any other interpolation method.')
-    elif interpolation == 'linear':
+        raise ValueError('\'nearest\' method is not yet supported. '
+                         'Please use any other method.')
+    elif method == 'linear':
         pass
     else:
         raise ValueError('Unexpected interpolation method.\n'
                          'Actual: \'{0}\' not in (\'linear\', \'lower\', '
-                         '\'higher\', \'midpoint\')'.format(interpolation))
+                         '\'higher\', \'midpoint\')'.format(method))
 
     if indices.dtype == cupy.int32:
         ret = cupy.rollaxis(ap, axis)
@@ -277,8 +283,8 @@ def _quantile_is_valid(q):
     return True
 
 
-def percentile(a, q, axis=None, out=None, interpolation='linear',
-               keepdims=False):
+def percentile(a, q, axis=None, out=None, *, method='linear', keepdims=False,
+               interpolation=None):
     """Computes the q-th percentile of the data along the specified axis.
 
     Args:
@@ -288,29 +294,33 @@ def percentile(a, q, axis=None, out=None, interpolation='linear',
         axis (int or tuple of ints): Along which axis or axes to compute the
             percentiles. The flattened array is used by default.
         out (cupy.ndarray): Output array.
-        interpolation (str): Interpolation method when a quantile lies between
+        method (str): Interpolation method when a quantile lies between
             two data points. ``linear`` interpolation is used by default.
             Supported interpolations are``lower``, ``higher``, ``midpoint``,
             ``nearest`` and ``linear``.
         keepdims (bool): If ``True``, the axis is remained as an axis of
             size one.
+        interpolation (str): Deprecated name for the method keyword argument.
 
     Returns:
         cupy.ndarray: The percentiles of ``a``, along the axis if specified.
 
     .. seealso:: :func:`numpy.percentile`
     """
+    if interpolation is not None:
+        method = _check_interpolation_as_method(
+            method, interpolation, 'percentile')
     if not isinstance(q, cupy.ndarray):
         q = cupy.asarray(q, dtype='d')
     q = cupy.true_divide(q, 100)
     if not _quantile_is_valid(q):  # synchronize
         raise ValueError('Percentiles must be in the range [0, 100]')
-    return _quantile_unchecked(a, q, axis=axis, out=out,
-                               interpolation=interpolation, keepdims=keepdims)
+    return _quantile_unchecked(
+        a, q, axis=axis, out=out, method=method, keepdims=keepdims)
 
 
-def quantile(a, q, axis=None, out=None, interpolation='linear',
-             keepdims=False):
+def quantile(a, q, axis=None, out=None, *, method='linear', keepdims=False,
+             interpolation=None):
     """Computes the q-th quantile of the data along the specified axis.
 
     Args:
@@ -320,21 +330,43 @@ def quantile(a, q, axis=None, out=None, interpolation='linear',
         axis (int or tuple of ints): Along which axis or axes to compute the
             quantiles. The flattened array is used by default.
         out (cupy.ndarray): Output array.
-        interpolation (str): Interpolation method when a quantile lies between
+        method (str): Interpolation method when a quantile lies between
             two data points. ``linear`` interpolation is used by default.
             Supported interpolations are``lower``, ``higher``, ``midpoint``,
             ``nearest`` and ``linear``.
         keepdims (bool): If ``True``, the axis is remained as an axis of
             size one.
+        interpolation (str): Deprecated name for the method keyword argument.
 
     Returns:
         cupy.ndarray: The quantiles of ``a``, along the axis if specified.
 
     .. seealso:: :func:`numpy.quantile`
     """
+    if interpolation is not None:
+        method = _check_interpolation_as_method(
+            method, interpolation, 'quantile')
     if not isinstance(q, cupy.ndarray):
         q = cupy.asarray(q, dtype='d')
     if not _quantile_is_valid(q):  # synchronize
         raise ValueError('Quantiles must be in the range [0, 1]')
-    return _quantile_unchecked(a, q, axis=axis, out=out,
-                               interpolation=interpolation, keepdims=keepdims)
+    return _quantile_unchecked(
+        a, q, axis=axis, out=out, method=method, keepdims=keepdims)
+
+
+# Borrowd from NumPy
+def _check_interpolation_as_method(method, interpolation, fname):
+    # Deprecated NumPy 1.22, 2021-11-08
+    warnings.warn(
+        f"the `interpolation=` argument to {fname} was renamed to "
+        "`method=`, which has additional options.\n"
+        "Users of the modes 'nearest', 'lower', 'higher', or "
+        "'midpoint' are encouraged to review the method they. "
+        "(Deprecated NumPy 1.22)",
+        DeprecationWarning, stacklevel=3)
+    if method != "linear":
+        # sanity check, we assume this basically never happens
+        raise TypeError(
+            "You shall not pass both `method` and `interpolation`!\n"
+            "(`interpolation` is Deprecated in favor of `method`)")
+    return interpolation


Test patch:
diff --git a/tests/cupy_tests/statistics_tests/test_order.py b/tests/cupy_tests/statistics_tests/test_order.py
--- a/tests/cupy_tests/statistics_tests/test_order.py
+++ b/tests/cupy_tests/statistics_tests/test_order.py
@@ -1,4 +1,3 @@
-import unittest
 import warnings
 
 import numpy
@@ -10,237 +9,234 @@
 from cupy import testing
 
 
-_all_interpolations = (
+_all_methods = (
+    # 'inverted_cdf',               # TODO(takagi) Not implemented
+    # 'averaged_inverted_cdf',      # TODO(takagi) Not implemented
+    # 'closest_observation',        # TODO(takagi) Not implemented
+    # 'interpolated_inverted_cdf',  # TODO(takagi) Not implemented
+    # 'hazen',                      # TODO(takagi) Not implemented
+    # 'weibull',                    # TODO(takagi) Not implemented
+    'linear',
+    # 'median_unbiased',            # TODO(takagi) Not implemented
+    # 'normal_unbiased',            # TODO(takagi) Not implemented
     'lower',
     'higher',
     'midpoint',
-    # 'nearest', # TODO(hvy): Not implemented
-    'linear')
+    # 'nearest',                    # TODO(hvy): Not implemented
+)
 
 
-def for_all_interpolations(name='interpolation'):
-    return testing.for_orders(_all_interpolations, name=name)
+def for_all_methods(name='method'):
+    return pytest.mark.parametrize(name, _all_methods)
 
 
-@testing.gpu
-class TestOrder(unittest.TestCase):
+@testing.with_requires('numpy>=1.22.0rc1')
+class TestQuantile:
+
+    @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
+    def test_percentile_unexpected_method(self, dtype):
+        for xp in (numpy, cupy):
+            a = testing.shaped_random((4, 2, 3, 2), xp, dtype)
+            q = testing.shaped_random((5,), xp, dtype=dtype, scale=100)
+            with pytest.raises(ValueError):
+                xp.percentile(a, q, axis=-1, method='deadbeef')
+
+    # See gh-4453
+    @testing.for_float_dtypes()
+    def test_percentile_memory_access(self, dtype):
+        # Create an allocator that guarantees array allocated in
+        # cupy.percentile call will be followed by a NaN
+        original_allocator = cuda.get_allocator()
+
+        def controlled_allocator(size):
+            memptr = original_allocator(size)
+            base_size = memptr.mem.size
+            assert base_size % 512 == 0
+            item_size = dtype().itemsize
+            shape = (base_size // item_size,)
+            x = cupy.ndarray(memptr=memptr, shape=shape, dtype=dtype)
+            x.fill(cupy.nan)
+            return memptr
+
+        # Check that percentile still returns non-NaN results
+        a = testing.shaped_random((5,), cupy, dtype)
+        q = cupy.array((0, 100), dtype=dtype)
+
+        cuda.set_allocator(controlled_allocator)
+        try:
+            percentiles = cupy.percentile(a, q, axis=None,
+                                          method='linear')
+        finally:
+            cuda.set_allocator(original_allocator)
+
+        assert not cupy.any(cupy.isnan(percentiles))
+
+    @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
+    def test_quantile_unexpected_method(self, dtype):
+        for xp in (numpy, cupy):
+            a = testing.shaped_random((4, 2, 3, 2), xp, dtype)
+            q = testing.shaped_random((5,), xp, dtype=dtype, scale=1)
+            with pytest.raises(ValueError):
+                xp.quantile(a, q, axis=-1, method='deadbeef')
+
+
+@testing.with_requires('numpy>=1.22.0rc1')
+@for_all_methods()
+class TestQuantileMethods:
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
     @testing.numpy_cupy_allclose()
-    def test_percentile_defaults(self, xp, dtype, interpolation):
+    def test_percentile_defaults(self, xp, dtype, method):
         a = testing.shaped_random((2, 3, 8), xp, dtype)
         q = testing.shaped_random((3,), xp, dtype=dtype, scale=100)
-        return xp.percentile(a, q, interpolation=interpolation)
+        return xp.percentile(a, q, method=method)
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
     @testing.numpy_cupy_allclose()
-    def test_percentile_q_list(self, xp, dtype, interpolation):
+    def test_percentile_q_list(self, xp, dtype, method):
         a = testing.shaped_arange((1001,), xp, dtype)
         q = [99, 99.9]
-        return xp.percentile(a, q, interpolation=interpolation)
+        return xp.percentile(a, q, method=method)
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
     @testing.numpy_cupy_allclose(rtol=1e-6)
-    def test_percentile_no_axis(self, xp, dtype, interpolation):
+    def test_percentile_no_axis(self, xp, dtype, method):
         a = testing.shaped_random((10, 2, 4, 8), xp, dtype)
         q = testing.shaped_random((5,), xp, dtype=dtype, scale=100)
-        return xp.percentile(a, q, axis=None, interpolation=interpolation)
+        return xp.percentile(a, q, axis=None, method=method)
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
     @testing.numpy_cupy_allclose(rtol=1e-6)
-    def test_percentile_neg_axis(self, xp, dtype, interpolation):
+    def test_percentile_neg_axis(self, xp, dtype, method):
         a = testing.shaped_random((4, 3, 10, 2, 8), xp, dtype)
         q = testing.shaped_random((5,), xp, dtype=dtype, scale=100)
-        return xp.percentile(a, q, axis=-1, interpolation=interpolation)
+        return xp.percentile(a, q, axis=-1, method=method)
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
     @testing.numpy_cupy_allclose(rtol=1e-6)
-    def test_percentile_tuple_axis(self, xp, dtype, interpolation):
+    def test_percentile_tuple_axis(self, xp, dtype, method):
         a = testing.shaped_random((1, 6, 3, 2), xp, dtype)
         q = testing.shaped_random((5,), xp, dtype=dtype, scale=100)
-        return xp.percentile(a, q, axis=(0, 1, 2), interpolation=interpolation)
+        return xp.percentile(a, q, axis=(0, 1, 2), method=method)
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
     @testing.numpy_cupy_allclose()
-    def test_percentile_scalar_q(self, xp, dtype, interpolation):
+    def test_percentile_scalar_q(self, xp, dtype, method):
         a = testing.shaped_random((2, 3, 8), xp, dtype)
         q = 13.37
-        return xp.percentile(a, q, interpolation=interpolation)
+        return xp.percentile(a, q, method=method)
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
     @testing.numpy_cupy_allclose(rtol=1e-5)
-    def test_percentile_keepdims(self, xp, dtype, interpolation):
+    def test_percentile_keepdims(self, xp, dtype, method):
         a = testing.shaped_random((7, 2, 9, 2), xp, dtype)
         q = testing.shaped_random((5,), xp, dtype=dtype, scale=100)
         return xp.percentile(
-            a, q, axis=None, keepdims=True, interpolation=interpolation)
+            a, q, axis=None, keepdims=True, method=method)
 
-    @for_all_interpolations()
     @testing.for_float_dtypes(no_float16=True)  # NumPy raises error on int8
     @testing.numpy_cupy_allclose(rtol=1e-6)
-    def test_percentile_out(self, xp, dtype, interpolation):
+    def test_percentile_out(self, xp, dtype, method):
         a = testing.shaped_random((10, 2, 3, 2), xp, dtype)
         q = testing.shaped_random((5,), xp, dtype=dtype, scale=100)
         out = testing.shaped_random((5, 10, 2, 3), xp, dtype)
         return xp.percentile(
-            a, q, axis=-1, interpolation=interpolation, out=out)
+            a, q, axis=-1, method=method, out=out)
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
-    def test_percentile_bad_q(self, dtype, interpolation):
+    def test_percentile_bad_q(self, dtype, method):
         for xp in (numpy, cupy):
             a = testing.shaped_random((4, 2, 3, 2), xp, dtype)
             q = testing.shaped_random((1, 2, 3), xp, dtype=dtype, scale=100)
             with pytest.raises(ValueError):
-                xp.percentile(a, q, axis=-1, interpolation=interpolation)
+                xp.percentile(a, q, axis=-1, method=method)
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
-    def test_percentile_out_of_range_q(self, dtype, interpolation):
+    def test_percentile_out_of_range_q(self, dtype, method):
         for xp in (numpy, cupy):
             a = testing.shaped_random((4, 2, 3, 2), xp, dtype)
             for q in [[-0.1], [100.1]]:
                 with pytest.raises(ValueError):
-                    xp.percentile(a, q, axis=-1, interpolation=interpolation)
-
-    @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
-    def test_percentile_unexpected_interpolation(self, dtype):
-        for xp in (numpy, cupy):
-            a = testing.shaped_random((4, 2, 3, 2), xp, dtype)
-            q = testing.shaped_random((5,), xp, dtype=dtype, scale=100)
-            with pytest.raises(ValueError):
-                xp.percentile(a, q, axis=-1, interpolation='deadbeef')
-
-    # See gh-4453
-    @testing.for_float_dtypes()
-    def test_percentile_memory_access(self, dtype):
-        # Create an allocator that guarantees array allocated in
-        # cupy.percentile call will be followed by a NaN
-        original_allocator = cuda.get_allocator()
-
-        def controlled_allocator(size):
-            memptr = original_allocator(size)
-            base_size = memptr.mem.size
-            assert base_size % 512 == 0
-            item_size = dtype().itemsize
-            shape = (base_size // item_size,)
-            x = cupy.ndarray(memptr=memptr, shape=shape, dtype=dtype)
-            x.fill(cupy.nan)
-            return memptr
-
-        # Check that percentile still returns non-NaN results
-        a = testing.shaped_random((5,), cupy, dtype)
-        q = cupy.array((0, 100), dtype=dtype)
-
-        cuda.set_allocator(controlled_allocator)
-        try:
-            percentiles = cupy.percentile(a, q, axis=None,
-                                          interpolation='linear')
-        finally:
-            cuda.set_allocator(original_allocator)
-
-        assert not cupy.any(cupy.isnan(percentiles))
+                    xp.percentile(a, q, axis=-1, method=method)
 
     @testing.for_all_dtypes()
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
     @testing.numpy_cupy_allclose()
-    def test_quantile_defaults(self, xp, dtype, interpolation):
+    def test_quantile_defaults(self, xp, dtype, method):
         a = testing.shaped_random((2, 3, 8), xp, dtype)
         q = testing.shaped_random((3,), xp, scale=1)
-        return xp.quantile(a, q, interpolation=interpolation)
+        return xp.quantile(a, q, method=method)
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
     @testing.numpy_cupy_allclose()
-    def test_quantile_q_list(self, xp, dtype, interpolation):
+    def test_quantile_q_list(self, xp, dtype, method):
         a = testing.shaped_arange((1001,), xp, dtype)
         q = [.99, .999]
-        return xp.quantile(a, q, interpolation=interpolation)
+        return xp.quantile(a, q, method=method)
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
     @testing.numpy_cupy_allclose(rtol=1e-5)
-    def test_quantile_no_axis(self, xp, dtype, interpolation):
+    def test_quantile_no_axis(self, xp, dtype, method):
         a = testing.shaped_random((10, 2, 4, 8), xp, dtype)
         q = testing.shaped_random((5,), xp, scale=1)
-        return xp.quantile(a, q, axis=None, interpolation=interpolation)
+        return xp.quantile(a, q, axis=None, method=method)
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
     @testing.numpy_cupy_allclose(rtol=1e-6)
-    def test_quantile_neg_axis(self, xp, dtype, interpolation):
+    def test_quantile_neg_axis(self, xp, dtype, method):
         a = testing.shaped_random((4, 3, 10, 2, 8), xp, dtype)
         q = testing.shaped_random((5,), xp, scale=1)
-        return xp.quantile(a, q, axis=-1, interpolation=interpolation)
+        return xp.quantile(a, q, axis=-1, method=method)
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
     @testing.numpy_cupy_allclose(rtol=1e-6)
-    def test_quantile_tuple_axis(self, xp, dtype, interpolation):
+    def test_quantile_tuple_axis(self, xp, dtype, method):
         a = testing.shaped_random((1, 6, 3, 2), xp, dtype)
         q = testing.shaped_random((5,), xp, scale=1)
-        return xp.quantile(a, q, axis=(0, 1, 2), interpolation=interpolation)
+        return xp.quantile(a, q, axis=(0, 1, 2), method=method)
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
     @testing.numpy_cupy_allclose()
-    def test_quantile_scalar_q(self, xp, dtype, interpolation):
+    def test_quantile_scalar_q(self, xp, dtype, method):
         a = testing.shaped_random((2, 3, 8), xp, dtype)
         q = .1337
-        return xp.quantile(a, q, interpolation=interpolation)
+        return xp.quantile(a, q, method=method)
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
     @testing.numpy_cupy_allclose(rtol=1e-5)
-    def test_quantile_keepdims(self, xp, dtype, interpolation):
+    def test_quantile_keepdims(self, xp, dtype, method):
         a = testing.shaped_random((7, 2, 9, 2), xp, dtype)
         q = testing.shaped_random((5,), xp, scale=1)
         return xp.quantile(
-            a, q, axis=None, keepdims=True, interpolation=interpolation)
+            a, q, axis=None, keepdims=True, method=method)
 
-    @for_all_interpolations()
     @testing.for_float_dtypes(no_float16=True)  # NumPy raises error on int8
     @testing.numpy_cupy_allclose(rtol=1e-6)
-    def test_quantile_out(self, xp, dtype, interpolation):
+    def test_quantile_out(self, xp, dtype, method):
         a = testing.shaped_random((10, 2, 3, 2), xp, dtype)
         q = testing.shaped_random((5,), xp, dtype=dtype, scale=1)
         out = testing.shaped_random((5, 10, 2, 3), xp, dtype)
         return xp.quantile(
-            a, q, axis=-1, interpolation=interpolation, out=out)
+            a, q, axis=-1, method=method, out=out)
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
-    def test_quantile_bad_q(self, dtype, interpolation):
+    def test_quantile_bad_q(self, dtype, method):
         for xp in (numpy, cupy):
             a = testing.shaped_random((4, 2, 3, 2), xp, dtype)
             q = testing.shaped_random((1, 2, 3), xp, dtype=dtype, scale=1)
             with pytest.raises(ValueError):
-                xp.quantile(a, q, axis=-1, interpolation=interpolation)
+                xp.quantile(a, q, axis=-1, method=method)
 
-    @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
-    def test_quantile_out_of_range_q(self, dtype, interpolation):
+    def test_quantile_out_of_range_q(self, dtype, method):
         for xp in (numpy, cupy):
             a = testing.shaped_random((4, 2, 3, 2), xp, dtype)
             for q in [[-0.1], [1.1]]:
                 with pytest.raises(ValueError):
-                    xp.quantile(a, q, axis=-1, interpolation=interpolation)
+                    xp.quantile(a, q, axis=-1, method=method)
 
-    @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
-    def test_quantile_unexpected_interpolation(self, dtype):
-        for xp in (numpy, cupy):
-            a = testing.shaped_random((4, 2, 3, 2), xp, dtype)
-            q = testing.shaped_random((5,), xp, dtype=dtype, scale=1)
-            with pytest.raises(ValueError):
-                xp.quantile(a, q, axis=-1, interpolation='deadbeef')
+
+class TestOrder:
 
     @testing.for_all_dtypes(no_complex=True)
     @testing.numpy_cupy_allclose()
@@ -391,10 +387,9 @@ def test_ptp_all_nan(self, xp, dtype):
 @testing.parameterize(*testing.product({
     'magic_value': (-29, -53, -207, -16373, -99999,)
 }))
-@testing.gpu
-class TestPercentileMonotonic(unittest.TestCase):
+class TestPercentileMonotonic:
 
-    @testing.with_requires('numpy>=1.20')
+    @testing.with_requires('numpy>=1.22.0rc1')
     @testing.for_float_dtypes(no_float16=True)
     @testing.numpy_cupy_allclose()
     def test_percentile_monotonic(self, dtype, xp):
@@ -403,7 +398,7 @@ def test_percentile_monotonic(self, dtype, xp):
         a[0] = self.magic_value
         a[1] = self.magic_value
         q = xp.linspace(0, 100, 21)
-        percentiles = xp.percentile(a, q, interpolation='linear')
+        percentiles = xp.percentile(a, q, method='linear')
 
         # Assert that percentile output increases monotonically
         assert xp.all(xp.diff(percentiles) >= 0)