files: import unittest
  
  import numpy
  
  import cupy
  from cupy import testing
  import cupyx.scipy.ndimage  # NOQA
  
  try:
      import scipy.ndimage  # NOQA
  except ImportError:
      pass
  
  
  class FilterTestCaseBase(unittest.TestCase):
      """
      Add some utility methods for the parameterized tests for filters. these
      assume there are the "parameters" self.filter, self.wdtype or self.dtype,
      and self.ndim, self.kshape, or self.shape. Other optional "parameters" are
      also used if available like self.footprint when the filter is a filter
      that uses the footprint. These methods allow testing across multiple
      filter types much more easily.
      """
  
      # default param values if not provided
      filter = 'convolve'
      shape = (4, 5)
      ksize = 3
      dtype = numpy.float64
      footprint = True
  
  
      # Params that need no processing and just go into kwargs
      KWARGS_PARAMS = ('output', 'axis', 'mode', 'cval')
  
  
      def _filter(self, xp, scp):
          """
          The function that all tests end up calling, possibly after a few
          adjustments to the class "parameters".
          """
          # The filter function
          filter = getattr(scp.ndimage, self.filter)
  
          # The kwargs to pass to the filter function
          kwargs = {param:getattr(self, param)
                    for param in FilterTestCaseBase.KWARGS_PARAMS
                    if hasattr(self, param)}
          if hasattr(self, 'origin'):
              kwargs['origin'] = self._origin
  
          # The array we are filtering
          arr = testing.shaped_random(self.shape, xp, self.dtype)
  
          # The weights we are using to filter
          wghts = self._get_weights(xp)
          if isinstance(wghts, tuple) and len(wghts) == 2 and wghts[0] is None:
              # w is actually a tuple of (None, footprint)
              wghts, kwargs['footprint'] = wghts
  
          # Actually perform filtering
          return filter(arr, wghts, **kwargs)
  
  
      def _get_weights(self, xp):
          # Gets the second argument to the filter functions.
          # For convolve/correlate/convolve1d/correlate1d this is the weights.
          # For minimum_filter1d/maximum_filter1d this is the kernel size.
          #
          # For minimum_filter/maximum_filter this is a bit more complicated and
          # is either the kernel size or a tuple of None and the footprint. The
          # _filter() method knows about this and handles it automatically.
  
          if self.filter in ('convolve', 'correlate'):
              return testing.shaped_random(self._kshape, xp, self._dtype)
  
          if self.filter in ('convolve1d', 'correlate1d'):
              return testing.shaped_random((self.ksize,), xp, self._dtype)
  
          if self.filter in ('minimum_filter', 'maximum_filter'):
              if not self.footprint:
                  return self.ksize
              kshape = self._kshape
              footprint = testing.shaped_random(kshape, xp, scale=1) > 0.5
              if not footprint.any():
                  footprint = xp.ones(kshape)
              return None, footprint
  
          if self.filter in ('minimum_filter1d', 'maximum_filter1d'):
              return self.ksize
  
          raise RuntimeError('unsupported filter name')
  
  
      @property
      def _dtype(self):
          return getattr(self, 'wdtype', None) or self.dtype
  
  
      @property
      def _ndim(self):
          return getattr(self, 'ndim', len(getattr(self, 'shape', [])))
  
  
      @property
      def _kshape(self):
          return getattr(self, 'kshape', (self.ksize,) * self._ndim)
  
  
      @property
      def _origin(self):
          origin = getattr(self, 'origin', 0)
          if origin is not None:
              return origin
          is_1d = self.filter.endswith('1d')
          return -1 if is_1d else (-1, 1, -1, 1)[:self._ndim]
  
  
  # Parameters common across all modes (with some overrides)
  COMMON_PARAMS = {
      'shape': [(4, 5), (3, 4, 5), (1, 3, 4, 5)],
      'ksize': [3, 4],
      'dtype': [numpy.int32, numpy.float64],
  }
  
  
  # The bulk of the tests are done with this class
  @testing.parameterize(*(
      testing.product([
          # Filter-function specific params
          testing.product({
              'filter': ['convolve', 'correlate'],
          }) + testing.product({
              'filter': ['convolve1d', 'correlate1d',
                         'minimum_filter1d', 'maximum_filter1d'],
              'axis': [0, 1, -1],
          }) + testing.product({
              'filter': ['minimum_filter', 'maximum_filter'],
              'footprint': [False, True],
          }),
  
          # Mode-specific params
          testing.product({
              **COMMON_PARAMS,
              'mode': ['reflect'],
              # With reflect test some of the other parameters as well
              'origin': [0, 1, None],
              'output': [None, numpy.int32, numpy.float64],
              'dtype': [numpy.uint8, numpy.int16, numpy.int32,
                        numpy.float32, numpy.float64],
          }) + testing.product({
              **COMMON_PARAMS,
              'mode': ['constant'], 'cval': [-1.0, 0.0, 1.0],
          }) + testing.product({
              **COMMON_PARAMS,
              'mode': ['nearest', 'wrap'],
          }) + testing.product({
              **COMMON_PARAMS,
              'shape': [(4, 5), (3, 4, 5)], # no (1,3,4,5) here due to scipy bug
              'mode': ['mirror'],
          })
      ])
  ))
  @testing.gpu
  @testing.with_requires('scipy')
  class TestFilter(FilterTestCaseBase):
      @testing.numpy_cupy_allclose(atol=1e-5, rtol=1e-5, scipy_name='scp')
      def test_filter(self, xp, scp):
          if self.dtype == getattr(self, 'output', None):
              raise unittest.SkipTest("redundant")
          return self._filter(xp, scp)
  
  
  # Tests things requiring scipy >= 1.5.0
  @testing.parameterize(*(
      testing.product([
          # Filter-function specific params
          testing.product({
              'filter': ['convolve', 'correlate'],
          }) + testing.product({
              'filter': ['convolve1d', 'correlate1d',
                         'minimum_filter1d', 'maximum_filter1d'],
              'axis': [0, 1, -1],
          }) + testing.product({
              'filter': ['minimum_filter', 'maximum_filter'],
              'footprint': [False, True],
          }),
  
          # Mode-specific params
          testing.product({
              **COMMON_PARAMS,
              'shape': [(1, 3, 4, 5)],
              'mode': ['mirror'],
          })
      ])
  ))
  @testing.gpu
  # SciPy behavior fixed in 1.5.0: https://github.com/scipy/scipy/issues/11661
  @testing.with_requires('scipy>=1.5.0')
  class TestMirrorWithDim1(FilterTestCaseBase):
      @testing.numpy_cupy_allclose(atol=1e-5, rtol=1e-5, scipy_name='scp')
      def test_filter(self, xp, scp):
          return self._filter(xp, scp)
  
  
  # Tests with weight dtypes that are distinct from the input and output dtypes
  @testing.parameterize(*(
      testing.product([
          testing.product({
              'filter': ['convolve', 'correlate'],
          }) + testing.product({
              'filter': ['convolve1d', 'correlate1d'],
              'axis': [0, 1, -1],
          }),
          testing.product({
              **COMMON_PARAMS,
              'mode': ['reflect'],
              'output': [None, numpy.int32, numpy.float64],
              'dtype': [numpy.uint8, numpy.int16, numpy.int32,
                        numpy.float32, numpy.float64],
              'wdtype': [numpy.int32, numpy.float64],
          })
      ])
  ))
  @testing.gpu
  @testing.with_requires('scipy')
  class TestWeightDtype(FilterTestCaseBase):
      @testing.numpy_cupy_allclose(atol=1e-5, rtol=1e-5, scipy_name='scp')
      def test_filter(self, xp, scp):
          if self.dtype == self.wdtype:
              raise unittest.SkipTest("redundant")
          return self._filter(xp, scp)
  
  
  # Tests special weights (ND)
  @testing.parameterize(*testing.product({
      'filter': ['convolve', 'correlate', 'minimum_filter', 'maximum_filter'],
      'shape': [(3, 3), (3, 3, 3)],
      'dtype': [numpy.int32, numpy.float64],
  }))
  @testing.gpu
  @testing.with_requires('scipy')
  class TestSpecialWeightCases(FilterTestCaseBase):
      @testing.numpy_cupy_allclose(atol=1e-5, rtol=1e-5, scipy_name='scp')
      #@testing.numpy_cupy_raises(scipy_name='scp', accept_error=ValueError)
      def test_extra_0_dim(self, xp, scp):
          # NOTE: minimum/maximum_filter raise ValueError but convolve/correlate
          # return an array of zeroes the same shape as the input. This will
          # handle both and only pass is both numpy and cupy do the same thing.
          self.kshape = (0,) + self.shape
          try:
              return self._filter(xp, scp)
          except ValueError:
              return xp.zeros((0,)) #xp.zeros(self.shape)
  
  
      @testing.numpy_cupy_raises(scipy_name='scp', accept_error=RuntimeError)
      def test_missing_dim(self, xp, scp):
          self.kshape = self.shape[1:]
          return self._filter(xp, scp)
  
  
      @testing.numpy_cupy_raises(scipy_name='scp', accept_error=RuntimeError)
      def test_extra_dim(self, xp, scp):
          self.kshape = self.shape[:1] + self.shape
          return self._filter(xp, scp)
  
  
      @testing.numpy_cupy_raises(scipy_name='scp', accept_error=(RuntimeError,
                                                                 ValueError))
      def test_replace_dim_with_0(self, xp, scp):
          self.kshape = (0,) + self.shape[1:]
          return self._filter(xp, scp)
  
  
  # Tests special weights (1D)
  @testing.parameterize(*testing.product({
      'filter': ['convolve1d', 'correlate1d',
                 'minimum_filter1d', 'maximum_filter1d'],
      'shape': [(3, 3), (3, 3, 3)],
      'dtype': [numpy.int32, numpy.float64],
  }))
  @testing.gpu
  @testing.with_requires('scipy')
  class TestSpecialCases1D(FilterTestCaseBase):
      @testing.numpy_cupy_raises(scipy_name='scp', accept_error=RuntimeError)
      def test_0_dim(self, xp, scp):
          self.ksize = 0
          return self._filter(xp, scp)
  
  
  # Tests invalid axis value
  @testing.parameterize(*testing.product({
      'filter': ['convolve1d', 'correlate1d',
                 'minimum_filter1d', 'maximum_filter1d'],
      'shape': [(4, 5), (3, 4, 5), (1, 3, 4, 5)],
  }))
  @testing.gpu
  @testing.with_requires('scipy')
  class TestInvalidAxis(FilterTestCaseBase):
      @testing.numpy_cupy_raises(scipy_name='scp', accept_error=ValueError)
      def test_invalid_axis_pos(self, xp, scp):
          self.axis = len(self.shape)
          try:
              return self._filter(xp, scp)
          except numpy.AxisError:
              # numpy.AxisError is a subclass of ValueError
              # currently cupyx is raising numpy.AxisError but scipy is still
              # raising ValueError
              raise ValueError('invalid axis')
  
      @testing.numpy_cupy_raises(scipy_name='scp', accept_error=ValueError)
      def test_invalid_axis_neg(self, xp, scp):
          self.axis = -len(self.shape) - 1
          try:
              return self._filter(xp, scp)
          except numpy.AxisError:
              raise ValueError('invalid axis')
  
  
  # Tests invalid mode value
  @testing.parameterize(*testing.product({
      'filter': ['convolve', 'correlate',
                 'convolve1d', 'correlate1d',
                 'minimum_filter', 'maximum_filter',
                 'minimum_filter1d', 'maximum_filter1d'],
      'mode': ['unknown'],
      'shape': [(4, 5)],
  }))
  @testing.gpu
  @testing.with_requires('scipy')
  class TestInvalidMode(FilterTestCaseBase):
      @testing.numpy_cupy_raises(scipy_name='scp', accept_error=RuntimeError)
      def test_invalid_mode(self, xp, scp):
          return self._filter(xp, scp)
  
  
  # Tests invalid origin values
  @testing.parameterize(*testing.product({
      'filter': ['convolve', 'correlate',
                 'convolve1d', 'correlate1d',
                 'minimum_filter', 'maximum_filter',
                 'minimum_filter1d', 'maximum_filter1d'],
      'ksize': [3, 4],
      'shape': [(4, 5)], 'dtype': [numpy.float64],
  }))
  @testing.gpu
  # SciPy behavior fixed in 1.2.0: https://github.com/scipy/scipy/issues/822
  @testing.with_requires('scipy>=1.2.0')
  class TestInvalidOrigin(FilterTestCaseBase):
      @testing.numpy_cupy_raises(scipy_name='scp', accept_error=ValueError)
      def test_invalid_origin_neg(self, xp, scp):
          self.origin = -self.ksize // 2 - 1
          return self._filter(xp, scp)
  
      @testing.numpy_cupy_raises(scipy_name='scp', accept_error=ValueError)
      def test_invalid_origin_pos(self, xp, scp):
          self.origin = self.ksize - self.ksize // 2
          return self._filter(xp, scp)
  
files: tests/cupyx_tests/scipy_tests/ndimage_tests/test_filters.py
files: import unittest
  
  import numpy
  
  import cupy
  from cupy import testing
  import cupyx.scipy.ndimage  # NOQA
  
  try:
      import scipy.ndimage  # NOQA
  except ImportError:
      pass
  
  # ######### Testing convolve and correlate ##########
  
  
  @testing.parameterize(*(
      testing.product({
          'shape': [(3, 4), (2, 3, 4), (1, 2, 3, 4)],
          'ksize': [3, 4],
          'mode': ['reflect'],
          'cval': [0.0],
          'origin': [0, 1, None],
          'adtype': [numpy.int8, numpy.int16, numpy.int32,
                     numpy.float32, numpy.float64],
          'wdtype': [None, numpy.int32, numpy.float64],
          'output': [None, numpy.int32, numpy.float64],
          'filter': ['convolve', 'correlate']
      }) + testing.product({
          'shape': [(3, 4), (2, 3, 4), (1, 2, 3, 4)],
          'ksize': [3, 4],
          'mode': ['constant'],
          'cval': [-1.0, 0.0, 1.0],
          'origin': [0],
          'adtype': [numpy.int32, numpy.float64],
          'wdtype': [None],
          'output': [None],
          'filter': ['convolve', 'correlate']
      }) + testing.product({
          'shape': [(3, 4), (2, 3, 4), (1, 2, 3, 4)],
          'ksize': [3, 4],
          'mode': ['nearest', 'mirror', 'wrap'],
          'cval': [0.0],
          'origin': [0],
          'adtype': [numpy.int32, numpy.float64],
          'wdtype': [None],
          'output': [None],
          'filter': ['convolve', 'correlate']
      })
  ))
  @testing.gpu
  @testing.with_requires('scipy')
  class TestConvolveAndCorrelate(unittest.TestCase):
  
      def _filter(self, xp, scp, a, w):
          filter = getattr(scp.ndimage, self.filter)
          if self.origin is None:
              origin = (-1, 1, -1, 1)[:a.ndim]
          else:
              origin = self.origin
          return filter(a, w, output=self.output, mode=self.mode,
                        cval=self.cval, origin=origin)
  
      @testing.numpy_cupy_allclose(atol=1e-5, rtol=1e-5, scipy_name='scp')
      def test_convolve_and_correlate(self, xp, scp):
          if 1 in self.shape and self.mode == 'mirror':
              raise unittest.SkipTest("requires scipy>1.5.0, tested later")
          if self.adtype == self.wdtype or self.adtype == self.output:
              raise unittest.SkipTest("redundant")
          a = testing.shaped_random(self.shape, xp, self.adtype)
          if self.wdtype is None:
              wdtype = self.adtype
          else:
              wdtype = self.wdtype
          w = testing.shaped_random((self.ksize,) * a.ndim, xp, wdtype)
          return self._filter(xp, scp, a, w)
  
  
  @testing.parameterize(*testing.product({
      'shape': [(1, 2, 3, 4)],
      'ksize': [3, 4],
      'dtype': [numpy.int32, numpy.float64],
      'filter': ['convolve', 'correlate']
  }))
  @testing.gpu
  # SciPy behavior fixed in 1.5.0: https://github.com/scipy/scipy/issues/11661
  @testing.with_requires('scipy>=1.5.0')
  class TestConvolveAndCorrelateMirrorDim1(unittest.TestCase):
      @testing.numpy_cupy_allclose(atol=1e-5, rtol=1e-5, scipy_name='scp')
      def test_convolve_and_correlate(self, xp, scp):
          a = testing.shaped_random(self.shape, xp, self.dtype)
          w = testing.shaped_random((self.ksize,) * a.ndim, xp, self.dtype)
          filter = getattr(scp.ndimage, self.filter)
          return filter(a, w, output=None, mode='mirror', cval=0.0, origin=0)
  
  
  @testing.parameterize(*testing.product({
      'ndim': [2, 3],
      'dtype': [numpy.int32, numpy.float64],
      'filter': ['convolve', 'correlate']
  }))
  @testing.gpu
  @testing.with_requires('scipy')
  class TestConvolveAndCorrelateSpecialCases(unittest.TestCase):
  
      def _filter(self, scp, a, w, mode='reflect', origin=0):
          filter = getattr(scp.ndimage, self.filter)
          return filter(a, w, mode=mode, origin=origin)
  
      @testing.numpy_cupy_allclose(atol=1e-5, rtol=1e-5, scipy_name='scp')
      def test_weights_with_size_zero_dim(self, xp, scp):
          a = testing.shaped_random((3, ) * self.ndim, xp, self.dtype)
          w = testing.shaped_random((0, ) + (3, ) * self.ndim, xp, self.dtype)
          return self._filter(scp, a, w)
  
      def test_invalid_shape_weights(self):
          a = testing.shaped_random((3, ) * self.ndim, cupy, self.dtype)
          w = testing.shaped_random((3, ) * (self.ndim - 1), cupy, self.dtype)
          with self.assertRaises(RuntimeError):
              self._filter(cupyx.scipy, a, w)
          w = testing.shaped_random((0, ) + (3, ) * (self.ndim - 1), cupy,
                                    self.dtype)
          with self.assertRaises(RuntimeError):
              self._filter(cupyx.scipy, a, w)
  
      def test_invalid_mode(self):
          a = testing.shaped_random((3, ) * self.ndim, cupy, self.dtype)
          w = testing.shaped_random((3, ) * self.ndim, cupy, self.dtype)
          with self.assertRaises(RuntimeError):
              self._filter(cupyx.scipy, a, w, mode='unknown')
  
      # SciPy behavior fixed in 1.2.0: https://github.com/scipy/scipy/issues/822
      @testing.with_requires('scipy>=1.2.0')
      def test_invalid_origin(self):
          a = testing.shaped_random((3, ) * self.ndim, cupy, self.dtype)
          for lenw in [3, 4]:
              w = testing.shaped_random((lenw, ) * self.ndim, cupy, self.dtype)
              for origin in range(-3, 4):
                  if (lenw // 2 + origin < 0) or (lenw // 2 + origin >= lenw):
                      with self.assertRaises(ValueError):
                          self._filter(cupyx.scipy, a, w, origin=origin)
                  else:
                      self._filter(cupyx.scipy, a, w, origin=origin)
  
  
  # ######### Testing convolve1d and correlate1d ##########
  
  
  @testing.parameterize(*(
      testing.product({
          'shape': [(3, 4), (2, 3, 4), (1, 2, 3, 4)],
          'ksize': [3, 4],
          'axis': [0, 1, -1],
          'mode': ['reflect'],
          'cval': [0.0],
          'origin': [0, 1, -1],
          'adtype': [numpy.int8, numpy.int16, numpy.int32,
                     numpy.float32, numpy.float64],
          'wdtype': [None, numpy.int32, numpy.float64],
          'output': [None, numpy.int32, numpy.float64],
          'filter': ['convolve1d', 'correlate1d']
      }) + testing.product({
          'shape': [(3, 4), (2, 3, 4), (1, 2, 3, 4)],
          'ksize': [3, 4],
          'axis': [0, 1, -1],
          'mode': ['constant'],
          'cval': [-1.0, 0.0, 1.0],
          'origin': [0],
          'adtype': [numpy.int32, numpy.float64],
          'wdtype': [None],
          'output': [None],
          'filter': ['convolve1d', 'correlate1d']
      }) + testing.product({
          'shape': [(3, 4), (2, 3, 4), (1, 2, 3, 4)],
          'ksize': [3, 4],
          'axis': [0, 1, -1],
          'mode': ['nearest', 'mirror', 'wrap'],
          'cval': [0.0],
          'origin': [0],
          'adtype': [numpy.int32, numpy.float64],
          'wdtype': [None],
          'output': [None],
          'filter': ['convolve1d', 'correlate1d']
      })
  ))
  @testing.gpu
  @testing.with_requires('scipy')
  class TestConvolve1DAndCorrelate1D(unittest.TestCase):
  
      def _filter(self, xp, scp, a, w):
          filter = getattr(scp.ndimage, self.filter)
          return filter(a, w, axis=self.axis, output=self.output, mode=self.mode,
                        cval=self.cval, origin=self.origin)
  
      @testing.numpy_cupy_allclose(atol=1e-5, rtol=1e-5, scipy_name='scp')
      def test_convolve1d_and_correlate1d(self, xp, scp):
          if 1 in self.shape and self.mode == 'mirror':
              raise unittest.SkipTest("requires scipy>1.5.0, tested later")
          if self.adtype == self.wdtype or self.adtype == self.output:
              raise unittest.SkipTest("redundant")
          a = testing.shaped_random(self.shape, xp, self.adtype)
          if self.wdtype is None:
              wdtype = self.adtype
          else:
              wdtype = self.wdtype
          w = testing.shaped_random((self.ksize,), xp, wdtype)
          return self._filter(xp, scp, a, w)
  
  
  @testing.parameterize(*testing.product({
      'shape': [(1, 2, 3, 4)],
      'ksize': [3, 4],
      'axis': [0, 1, -1],
      'dtype': [numpy.int32, numpy.float64],
      'filter': ['convolve1d', 'correlate1d']
  }))
  @testing.gpu
  # SciPy behavior fixed in 1.5.0: https://github.com/scipy/scipy/issues/11661
  @testing.with_requires('scipy>=1.5.0')
  class TestConvolveAndCorrelateMirrorDim1(unittest.TestCase):
      @testing.numpy_cupy_allclose(atol=1e-5, rtol=1e-5, scipy_name='scp')
      def test_convolve_and_correlate(self, xp, scp):
          a = testing.shaped_random(self.shape, xp, self.dtype)
          w = testing.shaped_random((self.ksize,) * a.ndim, xp, self.dtype)
          filter = getattr(scp.ndimage, self.filter)
          return filter(a, w, axis=self.axis, output=None, mode='mirror',
                        cval=0.0, origin=0)
  
  
  @testing.parameterize(*testing.product({
      'ndim': [2, 3],
      'dtype': [numpy.int32, numpy.float64],
      'filter': ['convolve1d', 'correlate1d']
  }))
  @testing.gpu
  @testing.with_requires('scipy')
  class TestConvolve1DAndCorrelate1DSpecialCases(unittest.TestCase):
  
      def _filter(self, scp, a, w, mode='reflect', origin=0):
          filter = getattr(scp.ndimage, self.filter)
          return filter(a, w, mode=mode, origin=origin)
  
      def test_weights_with_size_zero_dim(self):
          a = testing.shaped_random((3, ) * self.ndim, cupy, self.dtype)
          w = testing.shaped_random((0, 3), cupy, self.dtype)
          with self.assertRaises(RuntimeError):
              self._filter(cupyx.scipy, a, w)
  
      def test_invalid_shape_weights(self):
          a = testing.shaped_random((3, ) * self.ndim, cupy, self.dtype)
          w = testing.shaped_random((3, 3), cupy, self.dtype)
          with self.assertRaises(RuntimeError):
              self._filter(cupyx.scipy, a, w)
          w = testing.shaped_random((0, ), cupy,
                                    self.dtype)
          with self.assertRaises(RuntimeError):
              self._filter(cupyx.scipy, a, w)
  
      def test_invalid_mode(self):
          a = testing.shaped_random((3, ) * self.ndim, cupy, self.dtype)
          w = testing.shaped_random((3,), cupy, self.dtype)
          with self.assertRaises(RuntimeError):
              self._filter(cupyx.scipy, a, w, mode='unknown')
  
      # SciPy behavior fixed in 1.2.0: https://github.com/scipy/scipy/issues/822
      @testing.with_requires('scipy>=1.2.0')
      def test_invalid_origin(self):
          a = testing.shaped_random((3, ) * self.ndim, cupy, self.dtype)
          for lenw in [3, 4]:
              w = testing.shaped_random((lenw, ), cupy, self.dtype)
              for origin in range(-3, 4):
                  if (lenw // 2 + origin < 0) or (lenw // 2 + origin >= lenw):
                      with self.assertRaises(ValueError):
                          self._filter(cupyx.scipy, a, w, origin=origin)
                  else:
                      self._filter(cupyx.scipy, a, w, origin=origin)
  
  
  # ######### Testing minimum_filter and maximum_filter ##########
  
  @testing.parameterize(*testing.product({
      'size': [3, 4],
      'footprint': [None, 'random'],
      'mode': ['reflect', 'constant', 'nearest', 'mirror', 'wrap'],
      'origin': [0, None],
      'x_dtype': [numpy.int32, numpy.float32],
      'output': [None, numpy.float64],
      'filter': ['minimum_filter', 'maximum_filter']
  }))
  @testing.gpu
  @testing.with_requires('scipy')
  class TestMinimumMaximumFilter(unittest.TestCase):
  
      shape = (4, 5)
      cval = 0.0
  
      def _filter(self, xp, scp, x):
          filter = getattr(scp.ndimage, self.filter)
          if self.origin is None:
              origin = (-1, 1, -1, 1)[:x.ndim]
          else:
              origin = self.origin
          if self.footprint is None:
              size, footprint = self.size, None
          else:
              size = None
              shape = (self.size, ) * x.ndim
              footprint = testing.shaped_random(shape, xp, scale=1) > .5
              if not footprint.any():
                  footprint = xp.ones(shape)
          return filter(x, size=size, footprint=footprint,
                        output=self.output, mode=self.mode, cval=self.cval,
                        origin=origin)
  
      @testing.numpy_cupy_allclose(atol=1e-5, rtol=1e-5, scipy_name='scp')
      def test_minimum_and_maximum_filter(self, xp, scp):
          x = testing.shaped_random(self.shape, xp, self.x_dtype)
          return self._filter(xp, scp, x)
  
  
  # ######### Testing minimum_filter1d and maximum_filter1d ##########
  
  
  @testing.parameterize(*(
      testing.product({
          'shape': [(3, 4), (2, 3, 4), (1, 2, 3, 4)],
          'ksize': [3, 4],
          'axis': [0, 1, -1],
          'mode': ['reflect'],
          'cval': [0.0],
          'origin': [0, 1, -1],
          'wdtype': [numpy.int32, numpy.float64],
          'output': [None, numpy.int32, numpy.float64],
          'filter': ['minimum_filter1d', 'maximum_filter1d']
      }) + testing.product({
          'shape': [(3, 4), (2, 3, 4), (1, 2, 3, 4)],
          'ksize': [3, 4],
          'axis': [0, 1, -1],
          'mode': ['constant'],
          'cval': [-1.0, 0.0, 1.0],
          'origin': [0],
          'wdtype': [numpy.int32, numpy.float64],
          'output': [None],
          'filter': ['minimum_filter1d', 'maximum_filter1d']
      }) + testing.product({
          'shape': [(3, 4), (2, 3, 4), (1, 2, 3, 4)],
          'ksize': [3, 4],
          'axis': [0, 1, -1],
          'mode': ['nearest', 'mirror', 'wrap'],
          'cval': [0.0],
          'origin': [0],
          'wdtype': [numpy.int32, numpy.float64],
          'output': [None],
          'filter': ['minimum_filter1d', 'maximum_filter1d']
      })
  ))
  @testing.gpu
  @testing.with_requires('scipy')
  class TestMinimumMaximum1DFilter(unittest.TestCase):
      def _filter(self, xp, scp, a, w):
          filter = getattr(scp.ndimage, self.filter)
          return filter(a, w, axis=self.axis, output=self.output, mode=self.mode,
                        cval=self.cval, origin=self.origin)
  
      @testing.numpy_cupy_allclose(atol=1e-5, rtol=1e-5, scipy_name='scp')
      def test_convolve1d_and_correlate1d(self, xp, scp):
          a = testing.shaped_random(self.shape, xp, self.x_dtype)
          w = testing.shaped_random((self.ksize,), xp, self.x_dtype)
          return self._filter(xp, scp, a, w)
  
files: tests/cupyx_tests/scipy_tests/ndimage_tests/test_filters.py