Skip to content

Commit

Permalink
More robust cuda and cupy identification (#657)
Browse files Browse the repository at this point in the history
* More robust cuda and cupy identification

* Support for new cupy zonal tests

* Refactor
  • Loading branch information
ianthomas23 committed Mar 25, 2022
1 parent 61cb4e0 commit 77d3168
Show file tree
Hide file tree
Showing 17 changed files with 82 additions and 85 deletions.
4 changes: 2 additions & 2 deletions benchmarks/benchmarks/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import xarray as xr

from xrspatial.gpu_rtx import has_rtx
from xrspatial.utils import has_cuda, has_cupy
from xrspatial.utils import has_cuda_and_cupy


def get_xr_dataarray(
Expand Down Expand Up @@ -46,7 +46,7 @@ def get_xr_dataarray(
if type == "numpy":
pass
elif type == "cupy":
if not (has_cuda() and has_cupy()):
if not has_cuda_and_cupy:
raise NotImplementedError()
import cupy
z = cupy.asarray(z)
Expand Down
4 changes: 2 additions & 2 deletions benchmarks/benchmarks/zonal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import xarray as xr

from xrspatial import zonal
from xrspatial.utils import has_cuda
from xrspatial.utils import has_cuda_and_cupy

from .common import get_xr_dataarray

Expand All @@ -13,7 +13,7 @@ def create_arr(data=None, H=10, W=10, backend='numpy'):
data = np.zeros((H, W), dtype=np.float32)
raster = xr.DataArray(data, dims=['y', 'x'])

if has_cuda() and 'cupy' in backend:
if has_cuda_and_cupy() and 'cupy' in backend:
import cupy
raster.data = cupy.asarray(raster.data)

Expand Down
4 changes: 2 additions & 2 deletions xrspatial/gpu_rtx/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ..utils import has_cuda, has_cupy
from ..utils import has_cuda_and_cupy

try:
from rtxpy import RTX
Expand All @@ -7,4 +7,4 @@


def has_rtx():
return has_cupy() and has_cuda() and RTX is not None
return has_cuda_and_cupy and RTX is not None
6 changes: 3 additions & 3 deletions xrspatial/hillshade.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from numba import cuda

from .gpu_rtx import has_rtx
from .utils import calc_cuda_dims, has_cuda, has_cupy, is_cupy_array, is_cupy_backed
from .utils import calc_cuda_dims, has_cuda_and_cupy, is_cupy_array, is_cupy_backed


def _run_numpy(data, azimuth=225, angle_altitude=25):
Expand Down Expand Up @@ -170,15 +170,15 @@ def hillshade(agg: xr.DataArray,
out = _run_numpy(agg.data, azimuth, angle_altitude)

# cupy/numba case
elif has_cuda() and has_cupy() and is_cupy_array(agg.data):
elif has_cuda_and_cupy() and is_cupy_array(agg.data):
if shadows and has_rtx():
from .gpu_rtx.hillshade import hillshade_rtx
out = hillshade_rtx(agg, azimuth, angle_altitude, shadows=shadows)
else:
out = _run_cupy(agg.data, azimuth, angle_altitude)

# dask + cupy case
elif (has_cuda() and has_cupy() and isinstance(agg.data, da.Array) and
elif (has_cuda_and_cupy() and isinstance(agg.data, da.Array) and
is_cupy_backed(agg)):
raise NotImplementedError("Dask/CuPy hillshade not implemented")

Expand Down
9 changes: 7 additions & 2 deletions xrspatial/tests/general_checks.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import dask.array as da
import numpy as np
import pytest
import xarray as xr

from xrspatial.utils import ArrayTypeFunctionMapping, has_cuda
from xrspatial.utils import ArrayTypeFunctionMapping, has_cuda_and_cupy

# Use this as a decorator to skip tests if do not have both CUDA and CuPy available.
cuda_and_cupy_available = pytest.mark.skipif(
not has_cuda_and_cupy(), reason="Requires CUDA and CuPy")


def create_test_raster(
Expand All @@ -13,7 +18,7 @@ def create_test_raster(
for i, dim in enumerate(dims):
raster[dim] = np.linspace(0, data.shape[i] - 1, data.shape[i])

if has_cuda() and 'cupy' in backend:
if has_cuda_and_cupy() and 'cupy' in backend:
import cupy
raster.data = cupy.asarray(raster.data)

Expand Down
7 changes: 3 additions & 4 deletions xrspatial/tests/test_aspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
from xrspatial import aspect
from xrspatial.tests.general_checks import (assert_nan_edges_effect, assert_numpy_equals_cupy,
assert_numpy_equals_dask_numpy, create_test_raster,
general_output_checks)
from xrspatial.utils import doesnt_have_cuda
cuda_and_cupy_available, general_output_checks)


def input_data(backend='numpy'):
Expand Down Expand Up @@ -70,15 +69,15 @@ def test_numpy_equals_dask_random_data(random_data):
assert_numpy_equals_dask_numpy(numpy_agg, dask_agg, aspect)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
def test_numpy_equals_cupy_qgis_data():
# compare using the data run through QGIS
numpy_agg = input_data()
cupy_agg = input_data('cupy')
assert_numpy_equals_cupy(numpy_agg, cupy_agg, aspect)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
@pytest.mark.parametrize("size", [(2, 4), (10, 15)])
@pytest.mark.parametrize(
"dtype", [np.int32, np.int64, np.uint32, np.uint64, np.float32, np.float64])
Expand Down
22 changes: 11 additions & 11 deletions xrspatial/tests/test_classify.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import xarray as xr

from xrspatial import binary, equal_interval, natural_breaks, quantile, reclassify
from xrspatial.tests.general_checks import create_test_raster, general_output_checks
from xrspatial.utils import doesnt_have_cuda
from xrspatial.tests.general_checks import (create_test_raster, cuda_and_cupy_available,
general_output_checks)


def input_data(backend='numpy'):
Expand Down Expand Up @@ -44,15 +44,15 @@ def test_binary_dask_numpy(result_binary):
general_output_checks(dask_agg, dask_result, expected_result)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
def test_binary_cupy(result_reclassify):
@cuda_and_cupy_available
def test_binary_cupy(result_binary):
values, expected_result = result_binary
cupy_agg = input_data(backend='cupy')
cupy_result = binary(cupy_agg, values)
general_output_checks(cupy_agg, cupy_result, expected_result)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
def test_binary_dask_cupy(result_binary):
values, expected_result = result_binary
dask_cupy_agg = input_data(backend='dask+cupy')
Expand Down Expand Up @@ -96,15 +96,15 @@ def test_reclassify_dask_numpy(result_reclassify):
general_output_checks(dask_agg, dask_result, expected_result, verify_dtype=True)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
def test_reclassify_cupy(result_reclassify):
bins, new_values, expected_result = result_reclassify
cupy_agg = input_data(backend='cupy')
cupy_result = reclassify(cupy_agg, bins=bins, new_values=new_values)
general_output_checks(cupy_agg, cupy_result, expected_result, verify_dtype=True)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
def test_reclassify_dask_cupy(result_reclassify):
bins, new_values, expected_result = result_reclassify
dask_cupy_agg = input_data(backend='dask+cupy')
Expand Down Expand Up @@ -159,7 +159,7 @@ def test_quantile_dask_numpy(result_quantile):
assert len(unique_elements) == k


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
def test_quantile_cupy(result_quantile):
k, expected_result = result_quantile
cupy_agg = input_data('cupy')
Expand Down Expand Up @@ -239,15 +239,15 @@ def test_natural_breaks_cpu_deterministic():
)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
def test_natural_breaks_cupy(result_natural_breaks):
cupy_agg = input_data('cupy')
k, expected_result = result_natural_breaks
cupy_natural_breaks = natural_breaks(cupy_agg, k=k)
general_output_checks(cupy_agg, cupy_natural_breaks, expected_result, verify_dtype=True)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
def test_natural_breaks_cupy_num_sample(result_natural_breaks_num_sample):
cupy_agg = input_data('cupy')
k, num_sample, expected_result = result_natural_breaks_num_sample
Expand Down Expand Up @@ -281,7 +281,7 @@ def test_equal_interval_dask_numpy(result_equal_interval):
general_output_checks(dask_agg, dask_numpy_result, expected_result, verify_dtype=True)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
def test_equal_interval_cupy(result_equal_interval):
k, expected_result = result_equal_interval
cupy_agg = input_data(backend='cupy')
Expand Down
5 changes: 2 additions & 3 deletions xrspatial/tests/test_curvature.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
from xrspatial import curvature
from xrspatial.tests.general_checks import (assert_numpy_equals_cupy,
assert_numpy_equals_dask_numpy, create_test_raster,
general_output_checks)
from xrspatial.utils import doesnt_have_cuda
cuda_and_cupy_available, general_output_checks)


@pytest.fixture
Expand Down Expand Up @@ -80,7 +79,7 @@ def test_curvature_on_concave_surface(concave_surface):
general_output_checks(numpy_agg, numpy_result, expected_result, verify_dtype=True)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
@pytest.mark.parametrize("size", [(2, 4), (10, 15)])
@pytest.mark.parametrize(
"dtype", [np.int32, np.int64, np.uint32, np.uint64, np.float32, np.float64])
Expand Down
13 changes: 7 additions & 6 deletions xrspatial/tests/test_focal.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
from xrspatial.convolution import (annulus_kernel, calc_cellsize, circle_kernel, convolution_2d,
convolve_2d, custom_kernel)
from xrspatial.focal import apply, focal_stats, hotspots
from xrspatial.tests.general_checks import create_test_raster, general_output_checks
from xrspatial.utils import doesnt_have_cuda, ngjit
from xrspatial.tests.general_checks import (create_test_raster, cuda_and_cupy_available,
general_output_checks)
from xrspatial.utils import ngjit


def _do_sparse_array(data_array):
Expand Down Expand Up @@ -57,7 +58,7 @@ def test_mean_transfer_function_cpu():
)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
def test_mean_transfer_function_gpu_equals_cpu():

import cupy
Expand Down Expand Up @@ -209,7 +210,7 @@ def test_convolution_dask_numpy(
)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
def test_2d_convolution_gpu(
convolve_2d_data,
kernel_circle_1_1_1,
Expand Down Expand Up @@ -297,7 +298,7 @@ def test_apply_dask_numpy(data_apply):
general_output_checks(dask_numpy_agg, dask_numpy_apply, expected_result)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
def test_apply_gpu(data_apply):
data, kernel, expected_result = data_apply
# cupy case
Expand Down Expand Up @@ -430,7 +431,7 @@ def test_hotspots_dask_numpy(data_hotspots):
general_output_checks(dask_numpy_agg, dask_numpy_hotspots, expected_result)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
def test_hotspot_gpu(data_hotspots):
data, kernel, expected_result = data_hotspots
cupy_agg = create_test_raster(data, backend='cupy')
Expand Down
5 changes: 2 additions & 3 deletions xrspatial/tests/test_hillshade.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
from xrspatial import hillshade
from xrspatial.tests.general_checks import (assert_numpy_equals_cupy,
assert_numpy_equals_dask_numpy, create_test_raster,
general_output_checks)
from xrspatial.utils import doesnt_have_cuda
cuda_and_cupy_available, general_output_checks)

from ..gpu_rtx import has_rtx

Expand Down Expand Up @@ -46,7 +45,7 @@ def test_hillshade_numpy_equals_dask_numpy(random_data):
assert_numpy_equals_dask_numpy(numpy_agg, dask_agg, hillshade)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
@pytest.mark.parametrize("size", [(2, 4), (10, 15)])
@pytest.mark.parametrize(
"dtype", [np.int32, np.int64, np.float32, np.float64])
Expand Down
24 changes: 12 additions & 12 deletions xrspatial/tests/test_multispectral.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

from xrspatial.multispectral import (arvi, ebbi, evi, gci, nbr, nbr2, ndmi, ndvi, savi, sipi,
true_color)
from xrspatial.tests.general_checks import create_test_raster, general_output_checks
from xrspatial.utils import doesnt_have_cuda
from xrspatial.tests.general_checks import (create_test_raster, cuda_and_cupy_available,
general_output_checks)


@pytest.fixture
Expand Down Expand Up @@ -275,7 +275,7 @@ def test_ndvi_cpu(nir_data, red_data, result_ndvi):
general_output_checks(nir_data, result, result_ndvi, verify_dtype=True)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
@pytest.mark.parametrize("backend", ["cupy", "dask+cupy"])
def test_ndvi_gpu(nir_data, red_data, result_ndvi):
result = ndvi(nir_data, red_data)
Expand Down Expand Up @@ -304,7 +304,7 @@ def test_savi_cpu(nir_data, red_data, result_savi):
general_output_checks(nir_data, result, result_savi)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
@pytest.mark.parametrize("backend", ["cupy", "dask+cupy"])
def test_savi_gpu(nir_data, red_data, result_savi):
# test default savi where soil_factor = 1.0
Expand All @@ -319,7 +319,7 @@ def test_arvi_cpu(nir_data, red_data, blue_data, result_arvi):
general_output_checks(nir_data, result, result_arvi)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
@pytest.mark.parametrize("backend", ["cupy", "dask+cupy"])
def test_arvi_gpu(nir_data, red_data, blue_data, result_arvi):
result = arvi(nir_data, red_data, blue_data)
Expand All @@ -333,7 +333,7 @@ def test_evi_cpu(nir_data, red_data, blue_data, result_evi):
general_output_checks(nir_data, result, result_evi)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
@pytest.mark.parametrize("backend", ["cupy", "dask+cupy"])
def test_evi_gpu(nir_data, red_data, blue_data, result_evi):
result = evi(nir_data, red_data, blue_data)
Expand All @@ -347,7 +347,7 @@ def test_gci_cpu(nir_data, green_data, result_gci):
general_output_checks(nir_data, result, result_gci)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
@pytest.mark.parametrize("backend", ["cupy", "dask+cupy"])
def test_gci_gpu(nir_data, green_data, result_gci):
result = gci(nir_data, green_data)
Expand All @@ -361,7 +361,7 @@ def test_sipi_cpu(nir_data, red_data, blue_data, result_sipi):
general_output_checks(nir_data, result, result_sipi)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
@pytest.mark.parametrize("backend", ["cupy", "dask+cupy"])
def test_sipi_gpu(nir_data, red_data, blue_data, result_sipi):
result = sipi(nir_data, red_data, blue_data)
Expand All @@ -375,7 +375,7 @@ def test_nbr_cpu(nir_data, swir2_data, result_nbr):
general_output_checks(nir_data, result, result_nbr)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
@pytest.mark.parametrize("backend", ["cupy", "dask+cupy"])
def test_nbr_gpu(nir_data, swir2_data, result_nbr):
result = nbr(nir_data, swir2_data)
Expand All @@ -389,7 +389,7 @@ def test_nbr2_cpu(swir1_data, swir2_data, result_nbr2):
general_output_checks(swir1_data, result, result_nbr2)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
@pytest.mark.parametrize("backend", ["cupy", "dask+cupy"])
def test_nbr2_gpu(swir1_data, swir2_data, result_nbr2):
result = nbr2(swir1_data, swir2_data)
Expand All @@ -403,7 +403,7 @@ def test_ndmi_cpu(nir_data, swir1_data, result_ndmi):
general_output_checks(nir_data, result, result_ndmi)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
@pytest.mark.parametrize("backend", ["cupy", "dask+cupy"])
def test_ndmi_gpu(nir_data, swir1_data, result_ndmi):
result = ndmi(nir_data, swir1_data)
Expand All @@ -417,7 +417,7 @@ def test_ebbi_cpu(red_data, swir1_data, tir_data, result_ebbi):
general_output_checks(red_data, result, result_ebbi)


@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
@cuda_and_cupy_available
@pytest.mark.parametrize("backend", ["cupy", "dask+cupy"])
def test_ebbi_gpu(red_data, swir1_data, tir_data, result_ebbi):
result = ebbi(red_data, swir1_data, tir_data)
Expand Down

0 comments on commit 77d3168

Please sign in to comment.