Skip to content

Compatibility with Numpy v2.4#425

Merged
GenevieveBuckley merged 1 commit intodask:mainfrom
avalentino:bugfix/numpy-v2.4-compat
Apr 15, 2026
Merged

Compatibility with Numpy v2.4#425
GenevieveBuckley merged 1 commit intodask:mainfrom
avalentino:bugfix/numpy-v2.4-compat

Conversation

@avalentino
Copy link
Copy Markdown
Contributor

  • Do not use the deprecated numpy.in1d

Copy link
Copy Markdown
Collaborator

@GenevieveBuckley GenevieveBuckley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well spotted! I approve this PR.

It looks like this is the only instance of np.in1d in our codebase, but if anyone else does spot any other deprecated numpy functions, we welcome pull requests.

@GenevieveBuckley
Copy link
Copy Markdown
Collaborator

I do see "[WIP]" (work in progress) in the PR title, but I'm assuming that this is ready to go now. The PR is not marked as a draft, and the CI tests have all passed, this seems good to me.

@GenevieveBuckley GenevieveBuckley merged commit a513d9c into dask:main Apr 15, 2026
17 checks passed
@GenevieveBuckley
Copy link
Copy Markdown
Collaborator

Thank you @avalentino!

@avalentino avalentino deleted the bugfix/numpy-v2.4-compat branch April 15, 2026 07:02
@avalentino
Copy link
Copy Markdown
Contributor Author

Actually there are 4 more tests failing when I run the test suite against numpy 2.4.
I was planning to fix also those ones but I dint have time so far.
I will open a new PR if I manage for find a fix.

The full log is at: https://ci.debian.net/packages/d/dask-image/unstable/amd64/69506502/

Pease find below the relevant part:

277s =================================== FAILURES ===================================
277s _ test_generic_filter_identity[<lambda>-1-None-generic_filter-generic_filter] __
277s 
277s sp_func = <function generic_filter at 0x7f893219c680>
277s da_func = <function generic_filter at 0x7f89321cd440>
277s function = <function <lambda> at 0x7f8931f9ae80>, size = 1, footprint = None
277s 
277s     @pytest.mark.parametrize(
277s         "sp_func, da_func",
277s         [(scipy.ndimage.generic_filter, dask_image.ndfilters.generic_filter)],
277s     )
277s     @pytest.mark.parametrize(
277s         "function, size, footprint",
277s         [
277s             (lambda x: x, 1, None),
277s             (lambda x: x, (1, 1), None),
277s             (lambda x: x, None, np.ones((1, 1))),
277s         ],
277s     )
277s     def test_generic_filter_identity(sp_func, da_func, function, size, footprint):
277s         a = np.arange(140.0).reshape(10, 14)
277s         d = da.from_array(a, chunks=(5, 7))
277s     
277s >       da.utils.assert_eq(d, da_func(d, function, size=size, footprint=footprint))
277s 
277s tests/test_dask_image/test_ndfilters/test__generic.py:82: 
277s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
277s /usr/lib/python3/dist-packages/dask/array/utils.py:328: in assert_eq
277s     b, bdt, b_meta, b_computed = _get_dt_meta_computed(
277s /usr/lib/python3/dist-packages/dask/array/utils.py:279: in _get_dt_meta_computed
277s     x = _check_chunks(x, check_ndim=check_ndim, scheduler=scheduler)
277s         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277s /usr/lib/python3/dist-packages/dask/array/utils.py:242: in _check_chunks
277s     x = x.persist(scheduler=scheduler)
277s         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277s /usr/lib/python3/dist-packages/dask/base.py:345: in persist
277s     (result,) = persist(self, traverse=False, **kwargs)
277s                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277s /usr/lib/python3/dist-packages/dask/base.py:999: in persist
277s     results = schedule(dsk, keys, **kwargs)
277s               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
277s 
277s input = array([[ 77.,  78.,  79.,  80.,  81.,  82.,  83.],
277s        [ 91.,  92.,  93.,  94.,  95.,  96.,  97.],
277s        [105., 10...9., 110., 111.],
277s        [119., 120., 121., 122., 123., 124., 125.],
277s        [133., 134., 135., 136., 137., 138., 139.]])
277s function = <function <lambda> at 0x7f8931f9ae80>, size = None
277s footprint = array([[ True]])
277s output = array([[-1., -1., -1., -1., -1., -1., -1.],
277s        [-1., -1., -1., -1., -1., -1., -1.],
277s        [-1., -1., -1., -1., -1., -1., -1.],
277s        [-1., -1., -1., -1., -1., -1., -1.],
277s        [-1., -1., -1., -1., -1., -1., -1.]])
277s mode = 2, cval = 0.0, origin = np.int64(0), extra_arguments = ()
277s extra_keywords = {}
277s 
277s     @_ni_docstrings.docfiller
277s     def generic_filter(input, function, size=None, footprint=None,
277s                        output=None, mode="reflect", cval=0.0, origin=0,
277s                        extra_arguments=(), extra_keywords=None, *, axes=None):
277s         """Calculate a multidimensional filter using the given function.
277s     
277s         At each element the provided function is called. The input values
277s         within the filter footprint at that element are passed to the function
277s         as a 1-D array of double values.
277s     
277s         Parameters
277s         ----------
277s         %(input)s
277s         function : {callable, scipy.LowLevelCallable}
277s             Function to apply at each element.
277s         %(size_foot)s
277s         %(output)s
277s         %(mode_reflect)s
277s         %(cval)s
277s         %(origin_multiple)s
277s         %(extra_arguments)s
277s         %(extra_keywords)s
277s         axes : tuple of int or None, optional
277s             If None, `input` is filtered along all axes. Otherwise,
277s             `input` is filtered along the specified axes. When `axes` is
277s             specified, any tuples used for `size` or `origin` must match the length
277s             of `axes`. The ith entry in any of these tuples corresponds to the ith
277s             entry in `axes`.
277s     
277s         Returns
277s         -------
277s         output : ndarray
277s             Filtered array. Has the same shape as `input`.
277s     
277s         See Also
277s         --------
277s         vectorized_filter : similar functionality, but optimized for vectorized callables
277s     
277s         Notes
277s         -----
277s         This function is ideal for use with instances of `scipy.LowLevelCallable`;
277s         for vectorized, pure-Python callables, consider `vectorized_filter` for improved
277s         performance.
277s     
277s         Low-level callback functions must have one of the following signatures:
277s     
277s         .. code:: c
277s     
277s            int callback(double *buffer, npy_intp filter_size,
277s                         double *return_value, void *user_data)
277s            int callback(double *buffer, intptr_t filter_size,
277s                         double *return_value, void *user_data)
277s     
277s         The calling function iterates over the elements of the input and
277s         output arrays, calling the callback function at each element. The
277s         elements within the footprint of the filter at the current element are
277s         passed through the ``buffer`` parameter, and the number of elements
277s         within the footprint through ``filter_size``. The calculated value is
277s         returned in ``return_value``. ``user_data`` is the data pointer provided
277s         to `scipy.LowLevelCallable` as-is.
277s     
277s         The callback function must return an integer error status that is zero
277s         if something went wrong and one otherwise. If an error occurs, you should
277s         normally set the python error status with an informative message
277s         before returning, otherwise a default error message is set by the
277s         calling function.
277s     
277s         In addition, some other low-level function pointer specifications
277s         are accepted, but these are for backward compatibility only and should
277s         not be used in new code.
277s     
277s         Examples
277s         --------
277s         Import the necessary modules and load the example image used for
277s         filtering.
277s     
277s         >>> import numpy as np
277s         >>> from scipy import datasets
277s         >>> from scipy.ndimage import zoom, generic_filter
277s         >>> import matplotlib.pyplot as plt
277s         >>> ascent = zoom(datasets.ascent(), 0.5)
277s     
277s         Compute a maximum filter with kernel size 5 by passing a simple NumPy
277s         aggregation function as argument to `function`.
277s     
277s         >>> maximum_filter_result = generic_filter(ascent, np.amax, [5, 5])
277s     
277s         While a maximum filter could also directly be obtained using
277s         `maximum_filter`, `generic_filter` allows generic Python function or
277s         `scipy.LowLevelCallable` to be used as a filter. Here, we compute the
277s         range between maximum and minimum value as an example for a kernel size
277s         of 5.
277s     
277s         >>> def custom_filter(image):
277s         ...     return np.amax(image) - np.amin(image)
277s         >>> custom_filter_result = generic_filter(ascent, custom_filter, [5, 5])
277s     
277s         Plot the original and filtered images.
277s     
277s         >>> fig, axes = plt.subplots(3, 1, figsize=(3, 9))
277s         >>> plt.gray()  # show the filtered result in grayscale
277s         >>> top, middle, bottom = axes
277s         >>> for ax in axes:
277s         ...     ax.set_axis_off()  # remove coordinate system
277s         >>> top.imshow(ascent)
277s         >>> top.set_title("Original image")
277s         >>> middle.imshow(maximum_filter_result)
277s         >>> middle.set_title("Maximum filter, Kernel: 5x5")
277s         >>> bottom.imshow(custom_filter_result)
277s         >>> bottom.set_title("Custom filter, Kernel: 5x5")
277s         >>> fig.tight_layout()
277s     
277s         """
277s         if (size is not None) and (footprint is not None):
277s             warnings.warn("ignoring size because footprint is set",
277s                           UserWarning, stacklevel=2)
277s         if extra_keywords is None:
277s             extra_keywords = {}
277s         input = np.asarray(input)
277s         if np.iscomplexobj(input):
277s             raise TypeError('Complex type not supported')
277s         axes = _ni_support._check_axes(axes, input.ndim)
277s         num_axes = len(axes)
277s         if footprint is None:
277s             if size is None:
277s                 raise RuntimeError("no footprint or filter size provided")
277s             sizes = _ni_support._normalize_sequence(size, num_axes)
277s             footprint = np.ones(sizes, dtype=bool)
277s         else:
277s             footprint = np.asarray(footprint, dtype=bool)
277s     
277s         # expand origins, footprint if num_axes < input.ndim
277s         footprint = _expand_footprint(input.ndim, axes, footprint)
277s         origins = _expand_origin(input.ndim, axes, origin)
277s     
277s         fshape = [ii for ii in footprint.shape if ii > 0]
277s         if len(fshape) != input.ndim:
277s             raise RuntimeError(f"footprint.ndim ({footprint.ndim}) "
277s                                f"must match len(axes) ({num_axes})")
277s         for origin, lenf in zip(origins, fshape):
277s             if (lenf // 2 + origin < 0) or (lenf // 2 + origin >= lenf):
277s                 raise ValueError('invalid origin')
277s         if not footprint.flags.contiguous:
277s             footprint = footprint.copy()
277s         output = _ni_support._get_output(output, input)
277s     
277s         mode = _ni_support._extend_mode_to_code(mode)
277s >       _nd_image.generic_filter(input, function, footprint, output, mode,
277s                                  cval, origins, extra_arguments, extra_keywords)
277s E       TypeError: only 0-dimensional arrays can be converted to Python scalars
277s 
277s /usr/lib/python3/dist-packages/scipy/ndimage/_filters.py:2420: TypeError
277s _ test_generic_filter_identity[<lambda>-size1-None-generic_filter-generic_filter] _
277s 
277s sp_func = <function generic_filter at 0x7f893219c680>
277s da_func = <function generic_filter at 0x7f89321cd440>
277s function = <function <lambda> at 0x7f8931f9af20>, size = (1, 1)
277s footprint = None
277s 
277s     @pytest.mark.parametrize(
277s         "sp_func, da_func",
277s         [(scipy.ndimage.generic_filter, dask_image.ndfilters.generic_filter)],
277s     )
277s     @pytest.mark.parametrize(
277s         "function, size, footprint",
277s         [
277s             (lambda x: x, 1, None),
277s             (lambda x: x, (1, 1), None),
277s             (lambda x: x, None, np.ones((1, 1))),
277s         ],
277s     )
277s     def test_generic_filter_identity(sp_func, da_func, function, size, footprint):
277s         a = np.arange(140.0).reshape(10, 14)
277s         d = da.from_array(a, chunks=(5, 7))
277s     
277s >       da.utils.assert_eq(d, da_func(d, function, size=size, footprint=footprint))
277s 
277s tests/test_dask_image/test_ndfilters/test__generic.py:82: 
277s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
277s /usr/lib/python3/dist-packages/dask/array/utils.py:328: in assert_eq
277s     b, bdt, b_meta, b_computed = _get_dt_meta_computed(
277s /usr/lib/python3/dist-packages/dask/array/utils.py:279: in _get_dt_meta_computed
277s     x = _check_chunks(x, check_ndim=check_ndim, scheduler=scheduler)
277s         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277s /usr/lib/python3/dist-packages/dask/array/utils.py:242: in _check_chunks
277s     x = x.persist(scheduler=scheduler)
277s         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277s /usr/lib/python3/dist-packages/dask/base.py:345: in persist
277s     (result,) = persist(self, traverse=False, **kwargs)
277s                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277s /usr/lib/python3/dist-packages/dask/base.py:999: in persist
277s     results = schedule(dsk, keys, **kwargs)
277s               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
277s 
277s input = array([[ 77.,  78.,  79.,  80.,  81.,  82.,  83.],
277s        [ 91.,  92.,  93.,  94.,  95.,  96.,  97.],
277s        [105., 10...9., 110., 111.],
277s        [119., 120., 121., 122., 123., 124., 125.],
277s        [133., 134., 135., 136., 137., 138., 139.]])
277s function = <function <lambda> at 0x7f8931f9af20>, size = None
277s footprint = array([[ True]])
277s output = array([[-1., -1., -1., -1., -1., -1., -1.],
277s        [-1., -1., -1., -1., -1., -1., -1.],
277s        [-1., -1., -1., -1., -1., -1., -1.],
277s        [-1., -1., -1., -1., -1., -1., -1.],
277s        [-1., -1., -1., -1., -1., -1., -1.]])
277s mode = 2, cval = 0.0, origin = np.int64(0), extra_arguments = ()
277s extra_keywords = {}
277s 
277s     @_ni_docstrings.docfiller
277s     def generic_filter(input, function, size=None, footprint=None,
277s                        output=None, mode="reflect", cval=0.0, origin=0,
277s                        extra_arguments=(), extra_keywords=None, *, axes=None):
277s         """Calculate a multidimensional filter using the given function.
277s     
277s         At each element the provided function is called. The input values
277s         within the filter footprint at that element are passed to the function
277s         as a 1-D array of double values.
277s     
277s         Parameters
277s         ----------
277s         %(input)s
277s         function : {callable, scipy.LowLevelCallable}
277s             Function to apply at each element.
277s         %(size_foot)s
277s         %(output)s
277s         %(mode_reflect)s
277s         %(cval)s
277s         %(origin_multiple)s
277s         %(extra_arguments)s
277s         %(extra_keywords)s
277s         axes : tuple of int or None, optional
277s             If None, `input` is filtered along all axes. Otherwise,
277s             `input` is filtered along the specified axes. When `axes` is
277s             specified, any tuples used for `size` or `origin` must match the length
277s             of `axes`. The ith entry in any of these tuples corresponds to the ith
277s             entry in `axes`.
277s     
277s         Returns
277s         -------
277s         output : ndarray
277s             Filtered array. Has the same shape as `input`.
277s     
277s         See Also
277s         --------
277s         vectorized_filter : similar functionality, but optimized for vectorized callables
277s     
277s         Notes
277s         -----
277s         This function is ideal for use with instances of `scipy.LowLevelCallable`;
277s         for vectorized, pure-Python callables, consider `vectorized_filter` for improved
277s         performance.
277s     
277s         Low-level callback functions must have one of the following signatures:
277s     
277s         .. code:: c
277s     
277s            int callback(double *buffer, npy_intp filter_size,
277s                         double *return_value, void *user_data)
277s            int callback(double *buffer, intptr_t filter_size,
277s                         double *return_value, void *user_data)
277s     
277s         The calling function iterates over the elements of the input and
277s         output arrays, calling the callback function at each element. The
277s         elements within the footprint of the filter at the current element are
277s         passed through the ``buffer`` parameter, and the number of elements
277s         within the footprint through ``filter_size``. The calculated value is
277s         returned in ``return_value``. ``user_data`` is the data pointer provided
277s         to `scipy.LowLevelCallable` as-is.
277s     
277s         The callback function must return an integer error status that is zero
277s         if something went wrong and one otherwise. If an error occurs, you should
277s         normally set the python error status with an informative message
277s         before returning, otherwise a default error message is set by the
277s         calling function.
277s     
277s         In addition, some other low-level function pointer specifications
277s         are accepted, but these are for backward compatibility only and should
277s         not be used in new code.
277s     
277s         Examples
277s         --------
277s         Import the necessary modules and load the example image used for
277s         filtering.
277s     
277s         >>> import numpy as np
277s         >>> from scipy import datasets
277s         >>> from scipy.ndimage import zoom, generic_filter
277s         >>> import matplotlib.pyplot as plt
277s         >>> ascent = zoom(datasets.ascent(), 0.5)
277s     
277s         Compute a maximum filter with kernel size 5 by passing a simple NumPy
277s         aggregation function as argument to `function`.
277s     
277s         >>> maximum_filter_result = generic_filter(ascent, np.amax, [5, 5])
277s     
277s         While a maximum filter could also directly be obtained using
277s         `maximum_filter`, `generic_filter` allows generic Python function or
277s         `scipy.LowLevelCallable` to be used as a filter. Here, we compute the
277s         range between maximum and minimum value as an example for a kernel size
277s         of 5.
277s     
277s         >>> def custom_filter(image):
277s         ...     return np.amax(image) - np.amin(image)
277s         >>> custom_filter_result = generic_filter(ascent, custom_filter, [5, 5])
277s     
277s         Plot the original and filtered images.
277s     
277s         >>> fig, axes = plt.subplots(3, 1, figsize=(3, 9))
277s         >>> plt.gray()  # show the filtered result in grayscale
277s         >>> top, middle, bottom = axes
277s         >>> for ax in axes:
277s         ...     ax.set_axis_off()  # remove coordinate system
277s         >>> top.imshow(ascent)
277s         >>> top.set_title("Original image")
277s         >>> middle.imshow(maximum_filter_result)
277s         >>> middle.set_title("Maximum filter, Kernel: 5x5")
277s         >>> bottom.imshow(custom_filter_result)
277s         >>> bottom.set_title("Custom filter, Kernel: 5x5")
277s         >>> fig.tight_layout()
277s     
277s         """
277s         if (size is not None) and (footprint is not None):
277s             warnings.warn("ignoring size because footprint is set",
277s                           UserWarning, stacklevel=2)
277s         if extra_keywords is None:
277s             extra_keywords = {}
277s         input = np.asarray(input)
277s         if np.iscomplexobj(input):
277s             raise TypeError('Complex type not supported')
277s         axes = _ni_support._check_axes(axes, input.ndim)
277s         num_axes = len(axes)
277s         if footprint is None:
277s             if size is None:
277s                 raise RuntimeError("no footprint or filter size provided")
277s             sizes = _ni_support._normalize_sequence(size, num_axes)
277s             footprint = np.ones(sizes, dtype=bool)
277s         else:
277s             footprint = np.asarray(footprint, dtype=bool)
277s     
277s         # expand origins, footprint if num_axes < input.ndim
277s         footprint = _expand_footprint(input.ndim, axes, footprint)
277s         origins = _expand_origin(input.ndim, axes, origin)
277s     
277s         fshape = [ii for ii in footprint.shape if ii > 0]
277s         if len(fshape) != input.ndim:
277s             raise RuntimeError(f"footprint.ndim ({footprint.ndim}) "
277s                                f"must match len(axes) ({num_axes})")
277s         for origin, lenf in zip(origins, fshape):
277s             if (lenf // 2 + origin < 0) or (lenf // 2 + origin >= lenf):
277s                 raise ValueError('invalid origin')
277s         if not footprint.flags.contiguous:
277s             footprint = footprint.copy()
277s         output = _ni_support._get_output(output, input)
277s     
277s         mode = _ni_support._extend_mode_to_code(mode)
277s >       _nd_image.generic_filter(input, function, footprint, output, mode,
277s                                  cval, origins, extra_arguments, extra_keywords)
277s E       TypeError: only 0-dimensional arrays can be converted to Python scalars
277s 
277s /usr/lib/python3/dist-packages/scipy/ndimage/_filters.py:2420: TypeError
277s _ test_generic_filter_identity[<lambda>-None-footprint2-generic_filter-generic_filter] _
277s 
277s sp_func = <function generic_filter at 0x7f893219c680>
277s da_func = <function generic_filter at 0x7f89321cd440>
277s function = <function <lambda> at 0x7f8931f9afc0>, size = None
277s footprint = array([[1.]])
277s 
277s     @pytest.mark.parametrize(
277s         "sp_func, da_func",
277s         [(scipy.ndimage.generic_filter, dask_image.ndfilters.generic_filter)],
277s     )
277s     @pytest.mark.parametrize(
277s         "function, size, footprint",
277s         [
277s             (lambda x: x, 1, None),
277s             (lambda x: x, (1, 1), None),
277s             (lambda x: x, None, np.ones((1, 1))),
277s         ],
277s     )
277s     def test_generic_filter_identity(sp_func, da_func, function, size, footprint):
277s         a = np.arange(140.0).reshape(10, 14)
277s         d = da.from_array(a, chunks=(5, 7))
277s     
277s >       da.utils.assert_eq(d, da_func(d, function, size=size, footprint=footprint))
277s 
277s tests/test_dask_image/test_ndfilters/test__generic.py:82: 
277s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
277s /usr/lib/python3/dist-packages/dask/array/utils.py:328: in assert_eq
277s     b, bdt, b_meta, b_computed = _get_dt_meta_computed(
277s /usr/lib/python3/dist-packages/dask/array/utils.py:279: in _get_dt_meta_computed
277s     x = _check_chunks(x, check_ndim=check_ndim, scheduler=scheduler)
277s         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277s /usr/lib/python3/dist-packages/dask/array/utils.py:242: in _check_chunks
277s     x = x.persist(scheduler=scheduler)
277s         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277s /usr/lib/python3/dist-packages/dask/base.py:345: in persist
277s     (result,) = persist(self, traverse=False, **kwargs)
277s                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277s /usr/lib/python3/dist-packages/dask/base.py:999: in persist
277s     results = schedule(dsk, keys, **kwargs)
277s               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
277s 
277s input = array([[ 77.,  78.,  79.,  80.,  81.,  82.,  83.],
277s        [ 91.,  92.,  93.,  94.,  95.,  96.,  97.],
277s        [105., 10...9., 110., 111.],
277s        [119., 120., 121., 122., 123., 124., 125.],
277s        [133., 134., 135., 136., 137., 138., 139.]])
277s function = <function <lambda> at 0x7f8931f9afc0>, size = None
277s footprint = array([[ True]])
277s output = array([[-1., -1., -1., -1., -1., -1., -1.],
277s        [-1., -1., -1., -1., -1., -1., -1.],
277s        [-1., -1., -1., -1., -1., -1., -1.],
277s        [-1., -1., -1., -1., -1., -1., -1.],
277s        [-1., -1., -1., -1., -1., -1., -1.]])
277s mode = 2, cval = 0.0, origin = np.int64(0), extra_arguments = ()
277s extra_keywords = {}
277s 
277s     @_ni_docstrings.docfiller
277s     def generic_filter(input, function, size=None, footprint=None,
277s                        output=None, mode="reflect", cval=0.0, origin=0,
277s                        extra_arguments=(), extra_keywords=None, *, axes=None):
277s         """Calculate a multidimensional filter using the given function.
277s     
277s         At each element the provided function is called. The input values
277s         within the filter footprint at that element are passed to the function
277s         as a 1-D array of double values.
277s     
277s         Parameters
277s         ----------
277s         %(input)s
277s         function : {callable, scipy.LowLevelCallable}
277s             Function to apply at each element.
277s         %(size_foot)s
277s         %(output)s
277s         %(mode_reflect)s
277s         %(cval)s
277s         %(origin_multiple)s
277s         %(extra_arguments)s
277s         %(extra_keywords)s
277s         axes : tuple of int or None, optional
277s             If None, `input` is filtered along all axes. Otherwise,
277s             `input` is filtered along the specified axes. When `axes` is
277s             specified, any tuples used for `size` or `origin` must match the length
277s             of `axes`. The ith entry in any of these tuples corresponds to the ith
277s             entry in `axes`.
277s     
277s         Returns
277s         -------
277s         output : ndarray
277s             Filtered array. Has the same shape as `input`.
277s     
277s         See Also
277s         --------
277s         vectorized_filter : similar functionality, but optimized for vectorized callables
277s     
277s         Notes
277s         -----
277s         This function is ideal for use with instances of `scipy.LowLevelCallable`;
277s         for vectorized, pure-Python callables, consider `vectorized_filter` for improved
277s         performance.
277s     
277s         Low-level callback functions must have one of the following signatures:
277s     
277s         .. code:: c
277s     
277s            int callback(double *buffer, npy_intp filter_size,
277s                         double *return_value, void *user_data)
277s            int callback(double *buffer, intptr_t filter_size,
277s                         double *return_value, void *user_data)
277s     
277s         The calling function iterates over the elements of the input and
277s         output arrays, calling the callback function at each element. The
277s         elements within the footprint of the filter at the current element are
277s         passed through the ``buffer`` parameter, and the number of elements
277s         within the footprint through ``filter_size``. The calculated value is
277s         returned in ``return_value``. ``user_data`` is the data pointer provided
277s         to `scipy.LowLevelCallable` as-is.
277s     
277s         The callback function must return an integer error status that is zero
277s         if something went wrong and one otherwise. If an error occurs, you should
277s         normally set the python error status with an informative message
277s         before returning, otherwise a default error message is set by the
277s         calling function.
277s     
277s         In addition, some other low-level function pointer specifications
277s         are accepted, but these are for backward compatibility only and should
277s         not be used in new code.
277s     
277s         Examples
277s         --------
277s         Import the necessary modules and load the example image used for
277s         filtering.
277s     
277s         >>> import numpy as np
277s         >>> from scipy import datasets
277s         >>> from scipy.ndimage import zoom, generic_filter
277s         >>> import matplotlib.pyplot as plt
277s         >>> ascent = zoom(datasets.ascent(), 0.5)
277s     
277s         Compute a maximum filter with kernel size 5 by passing a simple NumPy
277s         aggregation function as argument to `function`.
277s     
277s         >>> maximum_filter_result = generic_filter(ascent, np.amax, [5, 5])
277s     
277s         While a maximum filter could also directly be obtained using
277s         `maximum_filter`, `generic_filter` allows generic Python function or
277s         `scipy.LowLevelCallable` to be used as a filter. Here, we compute the
277s         range between maximum and minimum value as an example for a kernel size
277s         of 5.
277s     
277s         >>> def custom_filter(image):
277s         ...     return np.amax(image) - np.amin(image)
277s         >>> custom_filter_result = generic_filter(ascent, custom_filter, [5, 5])
277s     
277s         Plot the original and filtered images.
277s     
277s         >>> fig, axes = plt.subplots(3, 1, figsize=(3, 9))
277s         >>> plt.gray()  # show the filtered result in grayscale
277s         >>> top, middle, bottom = axes
277s         >>> for ax in axes:
277s         ...     ax.set_axis_off()  # remove coordinate system
277s         >>> top.imshow(ascent)
277s         >>> top.set_title("Original image")
277s         >>> middle.imshow(maximum_filter_result)
277s         >>> middle.set_title("Maximum filter, Kernel: 5x5")
277s         >>> bottom.imshow(custom_filter_result)
277s         >>> bottom.set_title("Custom filter, Kernel: 5x5")
277s         >>> fig.tight_layout()
277s     
277s         """
277s         if (size is not None) and (footprint is not None):
277s             warnings.warn("ignoring size because footprint is set",
277s                           UserWarning, stacklevel=2)
277s         if extra_keywords is None:
277s             extra_keywords = {}
277s         input = np.asarray(input)
277s         if np.iscomplexobj(input):
277s             raise TypeError('Complex type not supported')
277s         axes = _ni_support._check_axes(axes, input.ndim)
277s         num_axes = len(axes)
277s         if footprint is None:
277s             if size is None:
277s                 raise RuntimeError("no footprint or filter size provided")
277s             sizes = _ni_support._normalize_sequence(size, num_axes)
277s             footprint = np.ones(sizes, dtype=bool)
277s         else:
277s             footprint = np.asarray(footprint, dtype=bool)
277s     
277s         # expand origins, footprint if num_axes < input.ndim
277s         footprint = _expand_footprint(input.ndim, axes, footprint)
277s         origins = _expand_origin(input.ndim, axes, origin)
277s     
277s         fshape = [ii for ii in footprint.shape if ii > 0]
277s         if len(fshape) != input.ndim:
277s             raise RuntimeError(f"footprint.ndim ({footprint.ndim}) "
277s                                f"must match len(axes) ({num_axes})")
277s         for origin, lenf in zip(origins, fshape):
277s             if (lenf // 2 + origin < 0) or (lenf // 2 + origin >= lenf):
277s                 raise ValueError('invalid origin')
277s         if not footprint.flags.contiguous:
277s             footprint = footprint.copy()
277s         output = _ni_support._get_output(output, input)
277s     
277s         mode = _ni_support._extend_mode_to_code(mode)
277s >       _nd_image.generic_filter(input, function, footprint, output, mode,
277s                                  cval, origins, extra_arguments, extra_keywords)
277s E       TypeError: only 0-dimensional arrays can be converted to Python scalars
277s 
277s /usr/lib/python3/dist-packages/scipy/ndimage/_filters.py:2420: TypeError
277s ______________ test_generic_filter_comprehensions[generic_filter] ______________
277s 
277s da_func = <function generic_filter at 0x7f89321cd440>
277s 
277s     @pytest.mark.parametrize(
277s         "da_func",
277s         [
277s             dask_image.ndfilters.generic_filter,
277s         ],
277s     )
277s     def test_generic_filter_comprehensions(da_func):
277s         da_wfunc = lambda arr: da_func(arr, lambda x: x, 1)  # noqa: E731
277s     
277s         np.random.seed(0)
277s     
277s         a = np.random.random((3, 12, 14))
277s         d = da.from_array(a, chunks=(3, 6, 7))
277s     
277s         l2s = [da_wfunc(d[i]) for i in range(len(d))]
277s         l2c = [da_wfunc(d[i])[None] for i in range(len(d))]
277s     
277s >       da.utils.assert_eq(np.stack(l2s), da.stack(l2s))
277s 
277s tests/test_dask_image/test_ndfilters/test__generic.py:107: 
277s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
277s /usr/lib/python3/dist-packages/dask/array/utils.py:320: in assert_eq
277s     a, adt, a_meta, a_computed = _get_dt_meta_computed(
277s /usr/lib/python3/dist-packages/dask/array/utils.py:279: in _get_dt_meta_computed
277s     x = _check_chunks(x, check_ndim=check_ndim, scheduler=scheduler)
277s         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277s /usr/lib/python3/dist-packages/dask/array/utils.py:242: in _check_chunks
277s     x = x.persist(scheduler=scheduler)
277s         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277s /usr/lib/python3/dist-packages/dask/base.py:345: in persist
277s     (result,) = persist(self, traverse=False, **kwargs)
277s                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277s /usr/lib/python3/dist-packages/dask/base.py:999: in persist
277s     results = schedule(dsk, keys, **kwargs)
277s               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
277s 
277s input = array([[0.06530421, 0.78323444, 0.2883985 , 0.24141862, 0.66250457,
277s         0.24606318, 0.66585912],
277s        [0.8286569...467, 0.5468849 ],
277s        [0.40171354, 0.24841347, 0.50586638, 0.31038083, 0.37303486,
277s         0.52497044, 0.75059502]])
277s function = <function test_generic_filter_comprehensions.<locals>.<lambda>.<locals>.<lambda> at 0x7f892deac900>
277s size = None, footprint = array([[ True]])
277s output = array([[-1., -1., -1., -1., -1., -1., -1.],
277s        [-1., -1., -1., -1., -1., -1., -1.],
277s        [-1., -1., -1., -1., -1...-1., -1., -1., -1., -1., -1.],
277s        [-1., -1., -1., -1., -1., -1., -1.],
277s        [-1., -1., -1., -1., -1., -1., -1.]])
277s mode = 2, cval = 0.0, origin = np.int64(0), extra_arguments = ()
277s extra_keywords = {}
277s 
277s     @_ni_docstrings.docfiller
277s     def generic_filter(input, function, size=None, footprint=None,
277s                        output=None, mode="reflect", cval=0.0, origin=0,
277s                        extra_arguments=(), extra_keywords=None, *, axes=None):
277s         """Calculate a multidimensional filter using the given function.
277s     
277s         At each element the provided function is called. The input values
277s         within the filter footprint at that element are passed to the function
277s         as a 1-D array of double values.
277s     
277s         Parameters
277s         ----------
277s         %(input)s
277s         function : {callable, scipy.LowLevelCallable}
277s             Function to apply at each element.
277s         %(size_foot)s
277s         %(output)s
277s         %(mode_reflect)s
277s         %(cval)s
277s         %(origin_multiple)s
277s         %(extra_arguments)s
277s         %(extra_keywords)s
277s         axes : tuple of int or None, optional
277s             If None, `input` is filtered along all axes. Otherwise,
277s             `input` is filtered along the specified axes. When `axes` is
277s             specified, any tuples used for `size` or `origin` must match the length
277s             of `axes`. The ith entry in any of these tuples corresponds to the ith
277s             entry in `axes`.
277s     
277s         Returns
277s         -------
277s         output : ndarray
277s             Filtered array. Has the same shape as `input`.
277s     
277s         See Also
277s         --------
277s         vectorized_filter : similar functionality, but optimized for vectorized callables
277s     
277s         Notes
277s         -----
277s         This function is ideal for use with instances of `scipy.LowLevelCallable`;
277s         for vectorized, pure-Python callables, consider `vectorized_filter` for improved
277s         performance.
277s     
277s         Low-level callback functions must have one of the following signatures:
277s     
277s         .. code:: c
277s     
277s            int callback(double *buffer, npy_intp filter_size,
277s                         double *return_value, void *user_data)
277s            int callback(double *buffer, intptr_t filter_size,
277s                         double *return_value, void *user_data)
277s     
277s         The calling function iterates over the elements of the input and
277s         output arrays, calling the callback function at each element. The
277s         elements within the footprint of the filter at the current element are
277s         passed through the ``buffer`` parameter, and the number of elements
277s         within the footprint through ``filter_size``. The calculated value is
277s         returned in ``return_value``. ``user_data`` is the data pointer provided
277s         to `scipy.LowLevelCallable` as-is.
277s     
277s         The callback function must return an integer error status that is zero
277s         if something went wrong and one otherwise. If an error occurs, you should
277s         normally set the python error status with an informative message
277s         before returning, otherwise a default error message is set by the
277s         calling function.
277s     
277s         In addition, some other low-level function pointer specifications
277s         are accepted, but these are for backward compatibility only and should
277s         not be used in new code.
277s     
277s         Examples
277s         --------
277s         Import the necessary modules and load the example image used for
277s         filtering.
277s     
277s         >>> import numpy as np
277s         >>> from scipy import datasets
277s         >>> from scipy.ndimage import zoom, generic_filter
277s         >>> import matplotlib.pyplot as plt
277s         >>> ascent = zoom(datasets.ascent(), 0.5)
277s     
277s         Compute a maximum filter with kernel size 5 by passing a simple NumPy
277s         aggregation function as argument to `function`.
277s     
277s         >>> maximum_filter_result = generic_filter(ascent, np.amax, [5, 5])
277s     
277s         While a maximum filter could also directly be obtained using
277s         `maximum_filter`, `generic_filter` allows generic Python function or
277s         `scipy.LowLevelCallable` to be used as a filter. Here, we compute the
277s         range between maximum and minimum value as an example for a kernel size
277s         of 5.
277s     
277s         >>> def custom_filter(image):
277s         ...     return np.amax(image) - np.amin(image)
277s         >>> custom_filter_result = generic_filter(ascent, custom_filter, [5, 5])
277s     
277s         Plot the original and filtered images.
277s     
277s         >>> fig, axes = plt.subplots(3, 1, figsize=(3, 9))
277s         >>> plt.gray()  # show the filtered result in grayscale
277s         >>> top, middle, bottom = axes
277s         >>> for ax in axes:
277s         ...     ax.set_axis_off()  # remove coordinate system
277s         >>> top.imshow(ascent)
277s         >>> top.set_title("Original image")
277s         >>> middle.imshow(maximum_filter_result)
277s         >>> middle.set_title("Maximum filter, Kernel: 5x5")
277s         >>> bottom.imshow(custom_filter_result)
277s         >>> bottom.set_title("Custom filter, Kernel: 5x5")
277s         >>> fig.tight_layout()
277s     
277s         """
277s         if (size is not None) and (footprint is not None):
277s             warnings.warn("ignoring size because footprint is set",
277s                           UserWarning, stacklevel=2)
277s         if extra_keywords is None:
277s             extra_keywords = {}
277s         input = np.asarray(input)
277s         if np.iscomplexobj(input):
277s             raise TypeError('Complex type not supported')
277s         axes = _ni_support._check_axes(axes, input.ndim)
277s         num_axes = len(axes)
277s         if footprint is None:
277s             if size is None:
277s                 raise RuntimeError("no footprint or filter size provided")
277s             sizes = _ni_support._normalize_sequence(size, num_axes)
277s             footprint = np.ones(sizes, dtype=bool)
277s         else:
277s             footprint = np.asarray(footprint, dtype=bool)
277s     
277s         # expand origins, footprint if num_axes < input.ndim
277s         footprint = _expand_footprint(input.ndim, axes, footprint)
277s         origins = _expand_origin(input.ndim, axes, origin)
277s     
277s         fshape = [ii for ii in footprint.shape if ii > 0]
277s         if len(fshape) != input.ndim:
277s             raise RuntimeError(f"footprint.ndim ({footprint.ndim}) "
277s                                f"must match len(axes) ({num_axes})")
277s         for origin, lenf in zip(origins, fshape):
277s             if (lenf // 2 + origin < 0) or (lenf // 2 + origin >= lenf):
277s                 raise ValueError('invalid origin')
277s         if not footprint.flags.contiguous:
277s             footprint = footprint.copy()
277s         output = _ni_support._get_output(output, input)
277s     
277s         mode = _ni_support._extend_mode_to_code(mode)
277s >       _nd_image.generic_filter(input, function, footprint, output, mode,
277s                                  cval, origins, extra_arguments, extra_keywords)
277s E       TypeError: only 0-dimensional arrays can be converted to Python scalars
277s 
277s /usr/lib/python3/dist-packages/scipy/ndimage/_filters.py:2420: TypeError

[CUT]

278s =========================== short test summary info ============================
278s FAILED tests/test_dask_image/test_ndfilters/test__generic.py::test_generic_filter_identity[<lambda>-1-None-generic_filter-generic_filter]
278s FAILED tests/test_dask_image/test_ndfilters/test__generic.py::test_generic_filter_identity[<lambda>-size1-None-generic_filter-generic_filter]
278s FAILED tests/test_dask_image/test_ndfilters/test__generic.py::test_generic_filter_identity[<lambda>-None-footprint2-generic_filter-generic_filter]
278s FAILED tests/test_dask_image/test_ndfilters/test__generic.py::test_generic_filter_comprehensions[generic_filter]
278s FAILED tests/test_dask_image/test_ndmeasure/test_core.py::test_measure_props[shape3-chunks3-True-0-median]
278s FAILED tests/test_dask_image/test_ndmeasure/test_core.py::test_measure_props[shape4-chunks4-True-1-median]
278s FAILED tests/test_dask_image/test_ndmeasure/test_core.py::test_measure_props[shape5-chunks5-True-ind5-median]
278s FAILED tests/test_dask_image/test_ndmeasure/test_core.py::test_measure_props[shape6-chunks6-True-ind6-median]
278s FAILED tests/test_dask_image/test_ndmeasure/test_core.py::test_measure_props[shape7-chunks7-True-ind7-median]
278s FAILED tests/test_dask_image/test_ndmeasure/test_core.py::test_measure_props[shape8-chunks8-True-ind8-median]
278s FAILED tests/test_dask_image/test_ndmeasure/test_core.py::test_measure_props[shape9-chunks9-True-ind9-median]
278s FAILED tests/test_dask_image/test_ndmeasure/test_core.py::test_measure_props[shape10-chunks10-True-ind10-median]
278s FAILED tests/test_dask_image/test_ndmeasure/test_core.py::test_measure_props[shape11-chunks11-True-ind11-median]
278s FAILED tests/test_dask_image/test_ndmeasure/test_core.py::test_measure_props[shape12-chunks12-True-ind12-median]
278s ==== 14 failed, 2159 passed, 175 skipped, 123 warnings in 74.39s (0:01:14) =====

@GenevieveBuckley
Copy link
Copy Markdown
Collaborator

Separate PRs is totally fine, however you want to group them.
I'll try and port your comment above into an open issue, that way we'll have a record of it.

@avalentino
Copy link
Copy Markdown
Contributor Author

Thanks @GenevieveBuckley

@avalentino avalentino changed the title [WIP] Compatibility with Numpy v2.4 Compatibility with Numpy v2.4 Apr 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants