Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GPU support for ndmorph, binary morphological functions #157

Merged
merged 3 commits into from
Mar 1, 2021

Conversation

GenevieveBuckley
Copy link
Collaborator

Pending cupy/cupy#3907

This branch contains support for GPU array computation with cupy, for the dask-image ndmorph subpackage.

Related pull requests: #150, #151
Related issue: #133

@GenevieveBuckley
Copy link
Collaborator Author

We're waiting on cupy 9.0.0

I'll move this PR out of draft status since the alpha version of cupy 9.0.0 will be made available in the next day or two.

The full (stable) release of cupy 9.0.0 is scheduled for April 2021 (see release schedule here: cupy/cupy#3891). I'm not sure if we need to hold off on merging this PR until then, it may be ok to include this in dask-image earlier for those who might want to use the cupy beta releases.

@GenevieveBuckley GenevieveBuckley marked this pull request as ready for review October 27, 2020 23:42
Base automatically changed from master to main February 2, 2021 01:18
@jakirkham jakirkham mentioned this pull request Feb 11, 2021
@GenevieveBuckley
Copy link
Collaborator Author

GenevieveBuckley commented Mar 1, 2021

Do not merge: local cupy tests are failing with cupy=9.0.0b3

It's only the tests in tests/test_dask_image/test_ndmorph/test_cupy_ndmorph.py FFFF [ 96%]

Details:
=================================== FAILURES ===================================
______________________ test_cupy_ndmorph[binary_closing] _______________________

array = dask.array<array, shape=(10, 10), dtype=float32, chunksize=(5, 5), chunktype=cupy.ndarray>
func = <function binary_closing at 0x7febc8f21430>

    @pytest.mark.cupy
    @pytest.mark.parametrize("func", [
        ndmorph.binary_closing,
        ndmorph.binary_dilation,
        ndmorph.binary_erosion,
        ndmorph.binary_opening,
    ])
    def test_cupy_ndmorph(array, func):
        """Test convolve & correlate filters with cupy input arrays."""
        result = func(array)
>       result.compute()

tests/test_dask_image/test_ndmorph/test_cupy_ndmorph.py:31: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/base.py:281: in compute
    (result,) = compute(self, traverse=False, **kwargs)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/base.py:563: in compute
    results = schedule(dsk, keys, **kwargs)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/threaded.py:76: in get
    results = get_async(
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/local.py:487: in get_async
    raise_exception(exc, tb)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/local.py:317: in reraise
    raise exc
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/local.py:222: in execute_task
    result = _execute_task(task, data)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in _execute_task
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/optimization.py:963: in __call__
    return core.get(self.dsk, self.outkey, dict(zip(self.inkeys, args)))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:151: in get
    result = _execute_task(task, cache)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in _execute_task
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in <genexpr>
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in _execute_task
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in <genexpr>
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in _execute_task
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/utils.py:35: in apply
    return func(*args, **kwargs)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/cupyx/scipy/ndimage/morphology.py:358: in binary_dilation
    return _binary_erosion(input, structure, iterations, mask, output,
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/cupyx/scipy/ndimage/morphology.py:174: in _binary_erosion
    structure = cupy.ascontiguousarray(structure)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

a = array([[False,  True, False],
       [ True,  True,  True],
       [False,  True, False]])
dtype = None

    def ascontiguousarray(a, dtype=None):
        """Returns a C-contiguous array.
    
        Args:
            a (cupy.ndarray): Source array.
            dtype: Data type specifier.
    
        Returns:
            cupy.ndarray: If no copy is required, it returns ``a``. Otherwise, it
            returns a copy of ``a``.
    
        .. seealso:: :func:`numpy.ascontiguousarray`
    
        """
>       return core.ascontiguousarray(a, dtype)
E       TypeError: Argument 'a' has incorrect type (expected cupy.core.core.ndarray, got numpy.ndarray)

../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/cupy/_creation/from_data.py:97: TypeError
______________________ test_cupy_ndmorph[binary_dilation] ______________________

array = dask.array<array, shape=(10, 10), dtype=float32, chunksize=(5, 5), chunktype=cupy.ndarray>
func = <function binary_dilation at 0x7febc8f210d0>

    @pytest.mark.cupy
    @pytest.mark.parametrize("func", [
        ndmorph.binary_closing,
        ndmorph.binary_dilation,
        ndmorph.binary_erosion,
        ndmorph.binary_opening,
    ])
    def test_cupy_ndmorph(array, func):
        """Test convolve & correlate filters with cupy input arrays."""
        result = func(array)
>       result.compute()

tests/test_dask_image/test_ndmorph/test_cupy_ndmorph.py:31: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/base.py:281: in compute
    (result,) = compute(self, traverse=False, **kwargs)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/base.py:563: in compute
    results = schedule(dsk, keys, **kwargs)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/threaded.py:76: in get
    results = get_async(
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/local.py:487: in get_async
    raise_exception(exc, tb)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/local.py:317: in reraise
    raise exc
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/local.py:222: in execute_task
    result = _execute_task(task, data)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in _execute_task
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/optimization.py:963: in __call__
    return core.get(self.dsk, self.outkey, dict(zip(self.inkeys, args)))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:151: in get
    result = _execute_task(task, cache)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in _execute_task
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in <genexpr>
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in _execute_task
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/utils.py:35: in apply
    return func(*args, **kwargs)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/cupyx/scipy/ndimage/morphology.py:358: in binary_dilation
    return _binary_erosion(input, structure, iterations, mask, output,
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/cupyx/scipy/ndimage/morphology.py:174: in _binary_erosion
    structure = cupy.ascontiguousarray(structure)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

a = array([[False,  True, False],
       [ True,  True,  True],
       [False,  True, False]])
dtype = None

    def ascontiguousarray(a, dtype=None):
        """Returns a C-contiguous array.
    
        Args:
            a (cupy.ndarray): Source array.
            dtype: Data type specifier.
    
        Returns:
            cupy.ndarray: If no copy is required, it returns ``a``. Otherwise, it
            returns a copy of ``a``.
    
        .. seealso:: :func:`numpy.ascontiguousarray`
    
        """
>       return core.ascontiguousarray(a, dtype)
E       TypeError: Argument 'a' has incorrect type (expected cupy.core.core.ndarray, got numpy.ndarray)

../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/cupy/_creation/from_data.py:97: TypeError
______________________ test_cupy_ndmorph[binary_erosion] _______________________

array = dask.array<array, shape=(10, 10), dtype=float32, chunksize=(5, 5), chunktype=cupy.ndarray>
func = <function binary_erosion at 0x7febc8f21040>

    @pytest.mark.cupy
    @pytest.mark.parametrize("func", [
        ndmorph.binary_closing,
        ndmorph.binary_dilation,
        ndmorph.binary_erosion,
        ndmorph.binary_opening,
    ])
    def test_cupy_ndmorph(array, func):
        """Test convolve & correlate filters with cupy input arrays."""
        result = func(array)
>       result.compute()

tests/test_dask_image/test_ndmorph/test_cupy_ndmorph.py:31: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/base.py:281: in compute
    (result,) = compute(self, traverse=False, **kwargs)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/base.py:563: in compute
    results = schedule(dsk, keys, **kwargs)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/threaded.py:76: in get
    results = get_async(
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/local.py:487: in get_async
    raise_exception(exc, tb)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/local.py:317: in reraise
    raise exc
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/local.py:222: in execute_task
    result = _execute_task(task, data)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in _execute_task
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/optimization.py:963: in __call__
    return core.get(self.dsk, self.outkey, dict(zip(self.inkeys, args)))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:151: in get
    result = _execute_task(task, cache)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in _execute_task
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in <genexpr>
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in _execute_task
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/utils.py:35: in apply
    return func(*args, **kwargs)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/cupyx/scipy/ndimage/morphology.py:307: in binary_erosion
    return _binary_erosion(input, structure, iterations, mask, output,
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/cupyx/scipy/ndimage/morphology.py:211: in _binary_erosion
    nnz = int(cupy.count_nonzero(structure))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/cupy/_sorting/count.py:24: in count_nonzero
    return _count_nonzero(a, axis=axis)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

>   ???
E   TypeError: Argument 'a' has incorrect type (expected <class 'cupy.core.core.ndarray'>, got <class 'numpy.ndarray'>)

cupy/core/_reduction.pyx:544: TypeError
______________________ test_cupy_ndmorph[binary_opening] _______________________

array = dask.array<array, shape=(10, 10), dtype=float32, chunksize=(5, 5), chunktype=cupy.ndarray>
func = <function binary_opening at 0x7febc8f21160>

    @pytest.mark.cupy
    @pytest.mark.parametrize("func", [
        ndmorph.binary_closing,
        ndmorph.binary_dilation,
        ndmorph.binary_erosion,
        ndmorph.binary_opening,
    ])
    def test_cupy_ndmorph(array, func):
        """Test convolve & correlate filters with cupy input arrays."""
        result = func(array)
>       result.compute()

tests/test_dask_image/test_ndmorph/test_cupy_ndmorph.py:31: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/base.py:281: in compute
    (result,) = compute(self, traverse=False, **kwargs)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/base.py:563: in compute
    results = schedule(dsk, keys, **kwargs)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/threaded.py:76: in get
    results = get_async(
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/local.py:487: in get_async
    raise_exception(exc, tb)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/local.py:317: in reraise
    raise exc
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/local.py:222: in execute_task
    result = _execute_task(task, data)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in _execute_task
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/optimization.py:963: in __call__
    return core.get(self.dsk, self.outkey, dict(zip(self.inkeys, args)))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:151: in get
    result = _execute_task(task, cache)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in _execute_task
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in <genexpr>
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in _execute_task
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in <genexpr>
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/core.py:121: in _execute_task
    return func(*(_execute_task(a, cache) for a in args))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/dask/utils.py:35: in apply
    return func(*args, **kwargs)
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/cupyx/scipy/ndimage/morphology.py:307: in binary_erosion
    return _binary_erosion(input, structure, iterations, mask, output,
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/cupyx/scipy/ndimage/morphology.py:211: in _binary_erosion
    nnz = int(cupy.count_nonzero(structure))
../../anaconda3/envs/dask-image-cupy-dev/lib/python3.8/site-packages/cupy/_sorting/count.py:24: in count_nonzero
    return _count_nonzero(a, axis=axis)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

>   ???
E   TypeError: Argument 'a' has incorrect type (expected <class 'cupy.core.core.ndarray'>, got <class 'numpy.ndarray'>)

cupy/core/_reduction.pyx:544: TypeError

@GenevieveBuckley
Copy link
Collaborator Author

Looks like these are failing because the default structure element is a numpy array.

@GenevieveBuckley
Copy link
Collaborator Author

The cupy tests pass locally on my machine.

Given that cupy 9 can currently be installed using a beta version, I'll merge this into the main branch soon.

conda install -c conda-forge/label/cupy_rc cudatoolkit=11.2 cupy=9.0.0b3

@GenevieveBuckley GenevieveBuckley merged commit 91c4955 into dask:main Mar 1, 2021
@GenevieveBuckley GenevieveBuckley mentioned this pull request Mar 8, 2021
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.

1 participant