internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: cupy/cupy

Issue:
Reduce memory usage of `cupy.linalg.svd`
As @danielhanchen tried in #2277, `cupy.svd` may be improved to reduce its memory usage. See #2277 for its detail.


Discussion:
Please 🙂

Resolving patch:
diff --git a/cupy/linalg/decomposition.py b/cupy/linalg/decomposition.py
--- a/cupy/linalg/decomposition.py
+++ b/cupy/linalg/decomposition.py
@@ -375,22 +375,23 @@ def svd(a, full_matrices=True, compute_uv=True):
     if compute_uv:
         if full_matrices:
             u = cupy.empty((m, m), dtype=a_dtype)
-            vt = cupy.empty((n, n), dtype=a_dtype)
+            vt = x[:, :n]
+            job_u = ord('A')
+            job_vt = ord('O')
         else:
-            u = cupy.empty((mn, m), dtype=a_dtype)
+            u = x
             vt = cupy.empty((mn, n), dtype=a_dtype)
+            job_u = ord('O')
+            job_vt = ord('S')
         u_ptr, vt_ptr = u.data.ptr, vt.data.ptr
     else:
         u_ptr, vt_ptr = 0, 0  # Use nullptr
+        job_u = ord('N')
+        job_vt = ord('N')
     s = cupy.empty(mn, dtype=s_dtype)
     handle = device.get_cusolver_handle()
     dev_info = cupy.empty(1, dtype=numpy.int32)
 
-    if compute_uv:
-        job = ord('A') if full_matrices else ord('S')
-    else:
-        job = ord('N')
-
     if a_dtype == 'f':
         gesvd = cusolver.sgesvd
         gesvd_bufferSize = cusolver.sgesvd_bufferSize
@@ -407,12 +408,12 @@ def svd(a, full_matrices=True, compute_uv=True):
     buffersize = gesvd_bufferSize(handle, m, n)
     workspace = cupy.empty(buffersize, dtype=a_dtype)
     gesvd(
-        handle, job, job, m, n, x.data.ptr, m, s.data.ptr, u_ptr, m, vt_ptr, n,
-        workspace.data.ptr, buffersize, 0, dev_info.data.ptr)
+        handle, job_u, job_vt, m, n, x.data.ptr, m, s.data.ptr, u_ptr, m,
+        vt_ptr, n, workspace.data.ptr, buffersize, 0, dev_info.data.ptr)
     cupy.linalg.util._check_cusolver_dev_info_if_synchronization_allowed(
         gesvd, dev_info)
 
-    # Note that the returned array may need to be transporsed
+    # Note that the returned array may need to be transposed
     # depending on the structure of an input
     if compute_uv:
         if trans_flag:


Test patch:
Repository: cupy/cupy

Issue:
Incorrect behavior with empty arrays for `linalg.qr` and `linalg.svd`
`qr` and `svd` routines raise `CUSOLVERError: CUSOLVER_STATUS_INVALID_VALUE` for zero-sized input arrays.
NumPy supports this kind of input for `qr`, `svd` since 1.16
https://github.com/numpy/numpy/blob/master/doc/release/1.16.0-notes.rst#linalglstsq-linalgqr-and-linalgsvd-now-work-with-empty-arrays
The problem is that when calling cuSOLVER routines leading dimension is passed as `input.shape[0]` but it should be `max(1, input.shape[0])`.



Discussion:

Resolving patch:
diff --git a/cupy/linalg/decomposition.py b/cupy/linalg/decomposition.py
--- a/cupy/linalg/decomposition.py
+++ b/cupy/linalg/decomposition.py
@@ -349,6 +349,20 @@ def svd(a, full_matrices=True, compute_uv=True):
     # Remark 4: Remark 2 is removed since cuda 8.0 (new!)
     n, m = a.shape
 
+    mn = min(m, n)
+    if mn == 0:
+        s = cupy.empty((0,), s_dtype)
+        if compute_uv:
+            if full_matrices:
+                u = cupy.eye(n, dtype=a_dtype)
+                vt = cupy.eye(m, dtype=a_dtype)
+            else:
+                u = cupy.empty((n, 0), dtype=a_dtype)
+                vt = cupy.empty((0, m), dtype=a_dtype)
+            return u, s, vt
+        else:
+            return s
+
     # `a` must be copied because xgesvd destroys the matrix
     if m >= n:
         x = a.astype(a_dtype, order='C', copy=True)
@@ -358,16 +372,6 @@ def svd(a, full_matrices=True, compute_uv=True):
         x = a.transpose().astype(a_dtype, order='C', copy=True)
         trans_flag = True
 
-    mn = min(m, n)
-    if mn == 0:
-        if compute_uv:
-            if full_matrices:
-                return cupy.eye(m, m), cupy.empty((0,)), cupy.empty((0, 0))
-            else:
-                return cupy.empty((m, 0)), cupy.empty((0,)), cupy.empty((0, 0))
-        else:
-            return cupy.empty((0,))
-
     if compute_uv:
         if full_matrices:
             u = cupy.empty((m, m), dtype=a_dtype)


Test patch:
diff --git a/tests/cupy_tests/linalg_tests/test_decomposition.py b/tests/cupy_tests/linalg_tests/test_decomposition.py
--- a/tests/cupy_tests/linalg_tests/test_decomposition.py
+++ b/tests/cupy_tests/linalg_tests/test_decomposition.py
@@ -138,7 +138,10 @@ def check_usv(self, shape, dtype):
         # Check if the input matrix is not broken
         cupy.testing.assert_allclose(a_gpu, a_cpu)
 
-        self.assertEqual(len(result_gpu), len(result_cpu))
+        assert len(result_gpu) == 3
+        for i in range(3):
+            assert result_gpu[i].shape == result_cpu[i].shape
+            assert result_gpu[i].dtype == result_cpu[i].dtype
         u_cpu, s_cpu, vh_cpu = result_cpu
         u_gpu, s_gpu, vh_gpu = result_gpu
         cupy.testing.assert_allclose(s_gpu, s_cpu, atol=1e-4)
@@ -186,7 +189,7 @@ def check_singular(self, shape, xp, dtype):
         return result
 
     def check_rank2(self, array):
-        with self.assertRaises(numpy.linalg.LinAlgError):
+        with pytest.raises(numpy.linalg.LinAlgError):
             cupy.linalg.svd(array, full_matrices=self.full_matrices)
 
     @condition.repeat(3, 10)
Repository: cupy/cupy

Issue:
Write docs for Optuna optimization



Discussion:

Resolving patch:
diff --git a/cupyx/optimizing/_optimize.py b/cupyx/optimizing/_optimize.py
--- a/cupyx/optimizing/_optimize.py
+++ b/cupyx/optimizing/_optimize.py
@@ -43,6 +43,37 @@ def objective(trial):
 
 @contextlib.contextmanager
 def optimize(*, key=None, **config_dict):
+    """Context manager that optimizes kernel launch parameters.
+
+    In this context, CuPy's routines find the best kernel launch parameter
+    values (e.g., the number of threads and blocks). The found values are
+    cached and reused with keys as the shapes, strides and dtypes of the
+    given inputs arrays.
+
+    Args:
+        key (string or None): The cache key of optimizations.
+        max_trials (int): The number of trials that defaults to 100.
+        timeout (float):
+            Stops study after the given number of seconds. Default is 1.
+        max_total_time_per_trial (float):
+            Repeats measuring the execution time of the routine for the
+            given number of seconds. Default is 0.1.
+
+    Examples
+    --------
+    >>> import cupy
+    >>> from cupyx import optimizing
+    >>>
+    >>> x = cupy.arange(100)
+    >>> with optimizing.optimize():
+    ...     cupy.sum(x)
+    ...
+    array(4950)
+
+    .. note::
+      Optuna (https://optuna.org) installation is required.
+      Currently it works for reduction operations only.
+    """
     if not _optuna_available:
         raise RuntimeError(
             'Optuna is required to run optimization. '


Test patch:
Repository: cupy/cupy

Issue:
Use `CUSPARSE_VER_*` for cuSPARSE generic API
https://github.com/cupy/cupy/pull/3242#issuecomment-635748935


Discussion:

Resolving patch:
diff --git a/cupy/cusparse.py b/cupy/cusparse.py
--- a/cupy/cusparse.py
+++ b/cupy/cusparse.py
@@ -80,18 +80,17 @@ def _dtype_to_DataType(dtype):
         raise TypeError
 
 
-_available_cuda_version = {
+_available_cusparse_version = {
     'csrmv': (8000, 11000),
-    'csrmvEx': (8000, 11000),  # TODO(anaruse): failure in CUDA 11
+    'csrmvEx': (8000, 11000),  # TODO(anaruse): failure in cuSparse 11.0
     'csrmm': (8000, 11000),
     'csrmm2': (8000, 11000),
     'csrgeam': (8000, 11000),
     'csrgeam2': (9020, None),
     'csrgemm': (8000, 11000),
     'csrgemm2': (8000, None),
-    # TODO(anaruse): check availability on Windows when CUDA 11 is relased
-    'spmv': ({'Linux': 10010, 'Windows': 11000}, None),
-    'spmm': ({'Linux': 10010, 'Windows': 11000}, None),
+    'spmv': (10200, None),
+    'spmm': (10301, None),  # accuracy bugs in cuSparse 10.3.0
     'csr2dense': (8000, None),
     'csc2dense': (8000, None),
     'csrsort': (8000, None),
@@ -101,8 +100,8 @@ def _dtype_to_DataType(dtype):
     'csr2coo': (8000, None),
     'csr2csc': (8000, 11000),
     'csc2csr': (8000, 11000),  # the entity is csr2csc
-    'csr2cscEx2': (10010, None),
-    'csc2csrEx2': (10010, None),  # the entity is csr2cscEx2
+    'csr2cscEx2': (10200, None),
+    'csc2csrEx2': (10200, None),  # the entity is csr2cscEx2
     'dense2csc': (8000, None),
     'dense2csr': (8000, None),
     'csr2csr_compress': (8000, None),
@@ -121,16 +120,16 @@ def _get_version(x):
 
 @util.memoize()
 def check_availability(name):
-    if name not in _available_cuda_version:
+    if name not in _available_cusparse_version:
         msg = 'No available version information specified for {}'.name
         raise ValueError(msg)
-    version_added, version_removed = _available_cuda_version[name]
+    version_added, version_removed = _available_cusparse_version[name]
     version_added = _get_version(version_added)
     version_removed = _get_version(version_removed)
-    cuda_version = runtime.runtimeGetVersion()
-    if version_added is not None and cuda_version < version_added:
+    cusparse_version = cusparse.getVersion(device.get_cusparse_handle())
+    if version_added is not None and cusparse_version < version_added:
         return False
-    if version_removed is not None and cuda_version >= version_removed:
+    if version_removed is not None and cusparse_version >= version_removed:
         return False
     return True
 


Test patch:
Repository: cupy/cupy

Issue:
Remove cupy_common.h



Discussion:

Resolving patch:
diff --git a/cupy_setup_build.py b/cupy_setup_build.py
--- a/cupy_setup_build.py
+++ b/cupy_setup_build.py
@@ -74,6 +74,7 @@
     'cupy.core.fusion',
     'cupy.core.new_fusion',
     'cupy.core.raw',
+    'cupy.cuda.common',
     'cupy.cuda.cufft',
     'cupy.cuda.device',
     'cupy.cuda.memory',
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -126,6 +126,7 @@ def reduce_requirements(key):
         'core/include/cupy/carray.cuh',
         'core/include/cupy/complex.cuh',
         'core/include/cupy/atomics.cuh',
+        'core/include/cupy/type_dispatcher.cuh',
         'core/include/cupy/cuComplex_bridge.h',
         'core/include/cupy/_cuda/cuda-*/*.h',
         'core/include/cupy/_cuda/cuda-*/*.hpp',


Test patch:
Repository: cupy/cupy

Issue:
cupy.percentile only calculates integer percentiles when the input data is an integer.
This seems to be caused by a cast of the percentiles array `q` to the same type as the input array `a` in the cupy.percentile source :

https://github.com/cupy/cupy/blob/adfcc44bc9a17886a340cd85b7c9ebadd94b38a1/cupy/statistics/order.py#L189

Example code to reproduce the issue:

`cupy.percentile(cupy.arange(1001).astype(cupy.int16),[98, 99, 99.9, 100]).get()`
`array([ 980.,  990.,  990., 1000.])`

`cupy.percentile(cupy.arange(1001).astype(cupy.float16),[98, 99, 99.9, 100]).get()`
`array([ 980.,  990.,  999., 1000.])`

For comparison the numpy version always calculates correctly:

`numpy.percentile(numpy.arange(1001).astype(numpy.int16),[98, 99, 99.9, 100])`
`array([ 980.,  990.,  999., 1000.])`

Cupy configuration:
CuPy Version          : 7.6.0
CUDA Root             : C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2
CUDA Build Version    : 10020
CUDA Driver Version   : 10020
CUDA Runtime Version  : 10020



Discussion:
Verified with

```
>>> numpy.percentile(numpy.arange(1001).astype(numpy.int16),[98, 99, 99.9, 100])
array([ 980.,  990.,  999., 1000.])
>>> cupy.percentile(cupy.arange(1001).astype(cupy.int16),[98, 99, 99.9, 100])
array([ 980.,  990.,  990., 1000.])
```

Resolving patch:
diff --git a/cupy/statistics/order.py b/cupy/statistics/order.py
--- a/cupy/statistics/order.py
+++ b/cupy/statistics/order.py
@@ -186,7 +186,8 @@ def percentile(a, q, axis=None, out=None, interpolation='linear',
     .. seealso:: :func:`numpy.percentile`
 
     """
-    q = cupy.asarray(q, dtype=a.dtype)
+    if not isinstance(q, cupy.ndarray):
+        q = cupy.asarray(q, dtype='d')
     if q.ndim == 0:
         q = q[None]
         zerod = True


Test patch:
diff --git a/tests/cupy_tests/statics_tests/test_order.py b/tests/cupy_tests/statics_tests/test_order.py
--- a/tests/cupy_tests/statics_tests/test_order.py
+++ b/tests/cupy_tests/statics_tests/test_order.py
@@ -31,6 +31,14 @@ def test_percentile_defaults(self, xp, dtype, interpolation):
         q = testing.shaped_random((3,), xp, dtype=dtype, scale=100)
         return xp.percentile(a, q, interpolation=interpolation)
 
+    @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):
+        a = testing.shaped_arange((1001,), xp, dtype)
+        q = [99, 99.9]
+        return xp.percentile(a, q, interpolation=interpolation)
+
     @for_all_interpolations()
     @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
     @testing.numpy_cupy_allclose(rtol=1e-6)
Repository: cupy/cupy

Issue:
Remove docs mentioning Chainer
* docs/source/tutorial/basic.rst
* Remove intersphinx config for chainer


Discussion:

Resolving patch:
diff --git a/docs/source/conf.py b/docs/source/conf.py
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -346,7 +346,6 @@
     'python': ('https://docs.python.org/3/', None),
     'numpy': ('https://docs.scipy.org/doc/numpy/', None),
     'scipy': ('https://docs.scipy.org/doc/scipy/reference/', None),
-    'chainer': ('https://docs.chainer.org/en/latest/', None),
 }
 
 doctest_global_setup = '''


Test patch:
Repository: cupy/cupy

Issue:
Remove CHAINER_SEED in v8



Discussion:

Resolving patch:
diff --git a/cupy/random/generator.py b/cupy/random/generator.py
--- a/cupy/random/generator.py
+++ b/cupy/random/generator.py
@@ -1273,8 +1273,6 @@ def get_random_state():
     rs = _random_states.get(dev.id, None)
     if rs is None:
         seed = os.getenv('CUPY_SEED')
-        if seed is None:
-            seed = os.getenv('CHAINER_SEED')
         if seed is not None:
             seed = numpy.uint64(int(seed))
         rs = RandomState(seed)


Test patch:
diff --git a/tests/cupy_tests/random_tests/test_generator.py b/tests/cupy_tests/random_tests/test_generator.py
--- a/tests/cupy_tests/random_tests/test_generator.py
+++ b/tests/cupy_tests/random_tests/test_generator.py
@@ -1450,7 +1450,6 @@ def setUp(self):
         self.rs_dict = generator._random_states
         generator._random_states = {}
         self.cupy_seed = os.getenv('CUPY_SEED')
-        self.chainer_seed = os.getenv('CHAINER_SEED')
 
     def tearDown(self, *args):
         generator._random_states = self.rs_dict
@@ -1458,42 +1457,18 @@ def tearDown(self, *args):
             os.environ.pop('CUPY_SEED', None)
         else:
             os.environ['CUPY_SEED'] = self.cupy_seed
-        if self.chainer_seed is None:
-            os.environ.pop('CHAINER_SEED', None)
-        else:
-            os.environ['CHAINER_SEED'] = self.chainer_seed
 
-    def test_get_random_state_no_cupy_no_chainer_seed(self):
+    def test_get_random_state_no_cupy(self):
         os.environ.pop('CUPY_SEED', None)
-        os.environ.pop('CHAINER_SEED', None)
         rvs0 = self._get_rvs_reset()
         rvs1 = self._get_rvs_reset()
 
         self._check_different(rvs0, rvs1)
 
-    def test_get_random_state_no_cupy_with_chainer_seed(self):
-        rvs0 = self._get_rvs(generator.RandomState(5))
-
-        os.environ.pop('CUPY_SEED', None)
-        os.environ['CHAINER_SEED'] = '5'
-        rvs1 = self._get_rvs_reset()
-
-        self._check_same(rvs0, rvs1)
-
-    def test_get_random_state_with_cupy_no_chainer_seed(self):
+    def test_get_random_state_with_cupy(self):
         rvs0 = self._get_rvs(generator.RandomState(6))
 
         os.environ['CUPY_SEED'] = '6'
-        os.environ.pop('CHAINER_SEED', None)
-        rvs1 = self._get_rvs_reset()
-
-        self._check_same(rvs0, rvs1)
-
-    def test_get_random_state_with_cupy_with_chainer_seed(self):
-        rvs0 = self._get_rvs(generator.RandomState(7))
-
-        os.environ['CUPY_SEED'] = '7'
-        os.environ['CHAINER_SEED'] = '8'
         rvs1 = self._get_rvs_reset()
 
         self._check_same(rvs0, rvs1)
Repository: cupy/cupy

Issue:
ROCm: Support RCCL
RCCL is the ROCm's counterpart for NCCL:
https://github.com/ROCmSoftwarePlatform/rccl


Discussion:

Resolving patch:
diff --git a/cupy_setup_build.py b/cupy_setup_build.py
--- a/cupy_setup_build.py
+++ b/cupy_setup_build.py
@@ -256,6 +256,21 @@
         'version_method': build.get_cub_version,
     })
 
+    MODULES.append({
+        'name': 'nccl',
+        'file': [
+            'cupy.cuda.nccl',
+        ],
+        'include': [
+            'rccl.h',
+        ],
+        'libraries': [
+            'rccl',
+        ],
+        'check_method': build.check_nccl_version,
+        'version_method': build.get_nccl_version,
+    })
+
 if bool(int(os.environ.get('CUPY_SETUP_ENABLE_THRUST', 1))):
     if use_hip:
         MODULES.append({
diff --git a/install/build.py b/install/build.py
--- a/install/build.py
+++ b/install/build.py
@@ -155,7 +155,10 @@ def get_compiler_setting(use_hip):
         extra_compile_args.append('-std=c++11')
 
     if PLATFORM_DARWIN:
-        library_dirs.append('/usr/local/cuda/lib')
+        if cuda_path:
+            library_dirs.append('/usr/local/cuda/lib')
+        elif rocm_path:
+            library_dirs.append('/opt/rocm/lib')
 
     if PLATFORM_WIN32:
         nvtoolsext_path = os.environ.get('NVTOOLSEXT_PATH', '')
@@ -404,22 +407,29 @@ def check_nccl_version(compiler, settings):
 
     # NCCL 1.x does not provide version information.
     try:
-        out = build_and_run(compiler, '''
-        #include <nccl.h>
-        #include <stdio.h>
-        #ifdef NCCL_MAJOR
-        #ifndef NCCL_VERSION_CODE
-        #  define NCCL_VERSION_CODE \
-                (NCCL_MAJOR * 1000 + NCCL_MINOR * 100 + NCCL_PATCH)
-        #endif
-        #else
-        #  define NCCL_VERSION_CODE 0
-        #endif
-        int main() {
-          printf("%d", NCCL_VERSION_CODE);
-          return 0;
-        }
-        ''', include_dirs=settings['include_dirs'])
+        out = build_and_run(compiler,
+                            '''
+                            #ifndef CUPY_USE_HIP
+                            #include <nccl.h>
+                            #else
+                            #include <rccl.h>
+                            #endif
+                            #include <stdio.h>
+                            #ifdef NCCL_MAJOR
+                            #ifndef NCCL_VERSION_CODE
+                            #  define NCCL_VERSION_CODE \
+                            (NCCL_MAJOR * 1000 + NCCL_MINOR * 100 + NCCL_PATCH)
+                            #endif
+                            #else
+                            #  define NCCL_VERSION_CODE 0
+                            #endif
+                            int main() {
+                              printf("%d", NCCL_VERSION_CODE);
+                              return 0;
+                            }
+                            ''',
+                            include_dirs=settings['include_dirs'],
+                            define_macros=settings['define_macros'])
 
     except Exception as e:
         utils.print_warning('Cannot include NCCL\n{0}'.format(e))


Test patch:
Repository: cupy/cupy

Issue:
ROCm: `cupy.show_config()` not working
```
$ python -c "import cupy; cupy.show_config()"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/leofang/dev/cupy_rocm3.5.0/cupy/__init__.py", line 874, in show_config
    _sys.stdout.write(str(_cupyx.get_runtime_info()))
  File "/home/leofang/dev/cupy_rocm3.5.0/cupyx/_runtime.py", line 215, in __str__
    props['name'].decode('utf-8')),
AttributeError: 'str' object has no attribute 'decode'
```
Following #3858, I will add another branching to `getDeviceProperties` to support HIP.


Discussion:

Resolving patch:
diff --git a/cupyx/_runtime.py b/cupyx/_runtime.py
--- a/cupyx/_runtime.py
+++ b/cupyx/_runtime.py
@@ -6,6 +6,7 @@
 import numpy
 
 import cupy
+import cupy_backends
 
 try:
     import cupy.cuda.thrust as thrust
@@ -37,6 +38,8 @@
 except ImportError:
     scipy = None
 
+is_hip = cupy_backends.cuda.api.runtime.is_hip
+
 
 def _eval_or_error(func, errors):
     # Evaluates `func` and return the result.
@@ -117,7 +120,10 @@ class _RuntimeInfo(object):
     def __init__(self):
         self.cupy_version = cupy.__version__
 
-        self.cuda_path = cupy.cuda.get_cuda_path()
+        if not is_hip:
+            self.cuda_path = cupy.cuda.get_cuda_path()
+        else:
+            self.cuda_path = cupy._environment.get_rocm_path()
 
         self.cuda_build_version = cupy.cuda.driver.get_build_version()
         self.cuda_driver_version = _eval_or_error(


Test patch:
Repository: cupy/cupy

Issue:
`cupyx.scipy.ndimage.affine_transform` broken with `mode=opencv`
Rel #3559.

`cupyx.scipy.ndimage.affine_transform` can take `opencv` for its `mode` paramter to make it work like OpenCV's one. However, some change (maybe #2483?), broke it and the current master does not work with the parameter.

```
>>> cupyx.scipy.ndimage.affine_transform(im, M, output_shape=smaller_shape, output=smaller, mode='opencv')
/home/ext-mtakagi/cupy/cupyx/scipy/ndimage/interpolation.py:30: UserWarning: In the current feature the default order of affine_transform is 1. It is different from scipy.ndimage and can change in the future.
  'the future.'.format(func_name))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/ext-mtakagi/cupy/cupyx/scipy/ndimage/interpolation.py", line 212, in affine_transform
    integer_output=integer_output)
  File "cupy/util.pyx", line 103, in cupy.util.memoize.decorator.ret
  File "/home/ext-mtakagi/cupy/cupyx/scipy/ndimage/_interp_kernels.py", line 405, in _get_affine_kernel
    integer_output=integer_output,
  File "/home/ext-mtakagi/cupy/cupyx/scipy/ndimage/_interp_kernels.py", line 269, in _generate_interp_custom
    mode, ixvar, 'xsize_{}'.format(j)))
  File "/home/ext-mtakagi/cupy/cupyx/scipy/ndimage/filters.py", line 720, in _generate_boundary_condition_ops
    return ops
UnboundLocalError: local variable 'ops' referenced before assignment
```


Discussion:
Any updates on this issue please ?
Thanks
> Any updates on this issue please ?

I will take a quick look. 

Resolving patch:
diff --git a/cupyx/scipy/ndimage/interpolation.py b/cupyx/scipy/ndimage/interpolation.py
--- a/cupyx/scipy/ndimage/interpolation.py
+++ b/cupyx/scipy/ndimage/interpolation.py
@@ -175,6 +175,17 @@ def affine_transform(input, matrix, offset=0.0, output_shape=None, output=None,
     if output_shape is None:
         output_shape = input.shape
 
+    if mode == 'opencv' or mode == '_opencv_edge':
+        if matrix.ndim == 1:
+            matrix = cupy.diag(matrix)
+        coordinates = cupy.indices(output_shape, dtype=cupy.float64)
+        coordinates = cupy.dot(matrix, coordinates.reshape((input.ndim, -1)))
+        coordinates += cupy.expand_dims(cupy.asarray(offset), -1)
+        ret = _util._get_output(output, input, shape=output_shape)
+        ret[:] = map_coordinates(input, coordinates, ret.dtype, order, mode,
+                                 cval, prefilter).reshape(output_shape)
+        return ret
+
     matrix = matrix.astype(cupy.float64, copy=False)
     if order is None:
         order = 1


Test patch: