Skip to content

Commit 77d3168

Browse files
authored
More robust cuda and cupy identification (#657)
* More robust cuda and cupy identification * Support for new cupy zonal tests * Refactor
1 parent 61cb4e0 commit 77d3168

17 files changed

+82
-85
lines changed

Diff for: benchmarks/benchmarks/common.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import xarray as xr
33

44
from xrspatial.gpu_rtx import has_rtx
5-
from xrspatial.utils import has_cuda, has_cupy
5+
from xrspatial.utils import has_cuda_and_cupy
66

77

88
def get_xr_dataarray(
@@ -46,7 +46,7 @@ def get_xr_dataarray(
4646
if type == "numpy":
4747
pass
4848
elif type == "cupy":
49-
if not (has_cuda() and has_cupy()):
49+
if not has_cuda_and_cupy:
5050
raise NotImplementedError()
5151
import cupy
5252
z = cupy.asarray(z)

Diff for: benchmarks/benchmarks/zonal.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import xarray as xr
33

44
from xrspatial import zonal
5-
from xrspatial.utils import has_cuda
5+
from xrspatial.utils import has_cuda_and_cupy
66

77
from .common import get_xr_dataarray
88

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

16-
if has_cuda() and 'cupy' in backend:
16+
if has_cuda_and_cupy() and 'cupy' in backend:
1717
import cupy
1818
raster.data = cupy.asarray(raster.data)
1919

Diff for: xrspatial/gpu_rtx/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from ..utils import has_cuda, has_cupy
1+
from ..utils import has_cuda_and_cupy
22

33
try:
44
from rtxpy import RTX
@@ -7,4 +7,4 @@
77

88

99
def has_rtx():
10-
return has_cupy() and has_cuda() and RTX is not None
10+
return has_cuda_and_cupy and RTX is not None

Diff for: xrspatial/hillshade.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from numba import cuda
99

1010
from .gpu_rtx import has_rtx
11-
from .utils import calc_cuda_dims, has_cuda, has_cupy, is_cupy_array, is_cupy_backed
11+
from .utils import calc_cuda_dims, has_cuda_and_cupy, is_cupy_array, is_cupy_backed
1212

1313

1414
def _run_numpy(data, azimuth=225, angle_altitude=25):
@@ -170,15 +170,15 @@ def hillshade(agg: xr.DataArray,
170170
out = _run_numpy(agg.data, azimuth, angle_altitude)
171171

172172
# cupy/numba case
173-
elif has_cuda() and has_cupy() and is_cupy_array(agg.data):
173+
elif has_cuda_and_cupy() and is_cupy_array(agg.data):
174174
if shadows and has_rtx():
175175
from .gpu_rtx.hillshade import hillshade_rtx
176176
out = hillshade_rtx(agg, azimuth, angle_altitude, shadows=shadows)
177177
else:
178178
out = _run_cupy(agg.data, azimuth, angle_altitude)
179179

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

Diff for: xrspatial/tests/general_checks.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import dask.array as da
22
import numpy as np
3+
import pytest
34
import xarray as xr
45

5-
from xrspatial.utils import ArrayTypeFunctionMapping, has_cuda
6+
from xrspatial.utils import ArrayTypeFunctionMapping, has_cuda_and_cupy
7+
8+
# Use this as a decorator to skip tests if do not have both CUDA and CuPy available.
9+
cuda_and_cupy_available = pytest.mark.skipif(
10+
not has_cuda_and_cupy(), reason="Requires CUDA and CuPy")
611

712

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

16-
if has_cuda() and 'cupy' in backend:
21+
if has_cuda_and_cupy() and 'cupy' in backend:
1722
import cupy
1823
raster.data = cupy.asarray(raster.data)
1924

Diff for: xrspatial/tests/test_aspect.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
from xrspatial import aspect
55
from xrspatial.tests.general_checks import (assert_nan_edges_effect, assert_numpy_equals_cupy,
66
assert_numpy_equals_dask_numpy, create_test_raster,
7-
general_output_checks)
8-
from xrspatial.utils import doesnt_have_cuda
7+
cuda_and_cupy_available, general_output_checks)
98

109

1110
def input_data(backend='numpy'):
@@ -70,15 +69,15 @@ def test_numpy_equals_dask_random_data(random_data):
7069
assert_numpy_equals_dask_numpy(numpy_agg, dask_agg, aspect)
7170

7271

73-
@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
72+
@cuda_and_cupy_available
7473
def test_numpy_equals_cupy_qgis_data():
7574
# compare using the data run through QGIS
7675
numpy_agg = input_data()
7776
cupy_agg = input_data('cupy')
7877
assert_numpy_equals_cupy(numpy_agg, cupy_agg, aspect)
7978

8079

81-
@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
80+
@cuda_and_cupy_available
8281
@pytest.mark.parametrize("size", [(2, 4), (10, 15)])
8382
@pytest.mark.parametrize(
8483
"dtype", [np.int32, np.int64, np.uint32, np.uint64, np.float32, np.float64])

Diff for: xrspatial/tests/test_classify.py

+11-11
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import xarray as xr
44

55
from xrspatial import binary, equal_interval, natural_breaks, quantile, reclassify
6-
from xrspatial.tests.general_checks import create_test_raster, general_output_checks
7-
from xrspatial.utils import doesnt_have_cuda
6+
from xrspatial.tests.general_checks import (create_test_raster, cuda_and_cupy_available,
7+
general_output_checks)
88

99

1010
def input_data(backend='numpy'):
@@ -44,15 +44,15 @@ def test_binary_dask_numpy(result_binary):
4444
general_output_checks(dask_agg, dask_result, expected_result)
4545

4646

47-
@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
48-
def test_binary_cupy(result_reclassify):
47+
@cuda_and_cupy_available
48+
def test_binary_cupy(result_binary):
4949
values, expected_result = result_binary
5050
cupy_agg = input_data(backend='cupy')
5151
cupy_result = binary(cupy_agg, values)
5252
general_output_checks(cupy_agg, cupy_result, expected_result)
5353

5454

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

9898

99-
@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
99+
@cuda_and_cupy_available
100100
def test_reclassify_cupy(result_reclassify):
101101
bins, new_values, expected_result = result_reclassify
102102
cupy_agg = input_data(backend='cupy')
103103
cupy_result = reclassify(cupy_agg, bins=bins, new_values=new_values)
104104
general_output_checks(cupy_agg, cupy_result, expected_result, verify_dtype=True)
105105

106106

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

161161

162-
@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
162+
@cuda_and_cupy_available
163163
def test_quantile_cupy(result_quantile):
164164
k, expected_result = result_quantile
165165
cupy_agg = input_data('cupy')
@@ -239,15 +239,15 @@ def test_natural_breaks_cpu_deterministic():
239239
)
240240

241241

242-
@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
242+
@cuda_and_cupy_available
243243
def test_natural_breaks_cupy(result_natural_breaks):
244244
cupy_agg = input_data('cupy')
245245
k, expected_result = result_natural_breaks
246246
cupy_natural_breaks = natural_breaks(cupy_agg, k=k)
247247
general_output_checks(cupy_agg, cupy_natural_breaks, expected_result, verify_dtype=True)
248248

249249

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

283283

284-
@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
284+
@cuda_and_cupy_available
285285
def test_equal_interval_cupy(result_equal_interval):
286286
k, expected_result = result_equal_interval
287287
cupy_agg = input_data(backend='cupy')

Diff for: xrspatial/tests/test_curvature.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
from xrspatial import curvature
55
from xrspatial.tests.general_checks import (assert_numpy_equals_cupy,
66
assert_numpy_equals_dask_numpy, create_test_raster,
7-
general_output_checks)
8-
from xrspatial.utils import doesnt_have_cuda
7+
cuda_and_cupy_available, general_output_checks)
98

109

1110
@pytest.fixture
@@ -80,7 +79,7 @@ def test_curvature_on_concave_surface(concave_surface):
8079
general_output_checks(numpy_agg, numpy_result, expected_result, verify_dtype=True)
8180

8281

83-
@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
82+
@cuda_and_cupy_available
8483
@pytest.mark.parametrize("size", [(2, 4), (10, 15)])
8584
@pytest.mark.parametrize(
8685
"dtype", [np.int32, np.int64, np.uint32, np.uint64, np.float32, np.float64])

Diff for: xrspatial/tests/test_focal.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
from xrspatial.convolution import (annulus_kernel, calc_cellsize, circle_kernel, convolution_2d,
88
convolve_2d, custom_kernel)
99
from xrspatial.focal import apply, focal_stats, hotspots
10-
from xrspatial.tests.general_checks import create_test_raster, general_output_checks
11-
from xrspatial.utils import doesnt_have_cuda, ngjit
10+
from xrspatial.tests.general_checks import (create_test_raster, cuda_and_cupy_available,
11+
general_output_checks)
12+
from xrspatial.utils import ngjit
1213

1314

1415
def _do_sparse_array(data_array):
@@ -57,7 +58,7 @@ def test_mean_transfer_function_cpu():
5758
)
5859

5960

60-
@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
61+
@cuda_and_cupy_available
6162
def test_mean_transfer_function_gpu_equals_cpu():
6263

6364
import cupy
@@ -209,7 +210,7 @@ def test_convolution_dask_numpy(
209210
)
210211

211212

212-
@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
213+
@cuda_and_cupy_available
213214
def test_2d_convolution_gpu(
214215
convolve_2d_data,
215216
kernel_circle_1_1_1,
@@ -297,7 +298,7 @@ def test_apply_dask_numpy(data_apply):
297298
general_output_checks(dask_numpy_agg, dask_numpy_apply, expected_result)
298299

299300

300-
@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
301+
@cuda_and_cupy_available
301302
def test_apply_gpu(data_apply):
302303
data, kernel, expected_result = data_apply
303304
# cupy case
@@ -430,7 +431,7 @@ def test_hotspots_dask_numpy(data_hotspots):
430431
general_output_checks(dask_numpy_agg, dask_numpy_hotspots, expected_result)
431432

432433

433-
@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
434+
@cuda_and_cupy_available
434435
def test_hotspot_gpu(data_hotspots):
435436
data, kernel, expected_result = data_hotspots
436437
cupy_agg = create_test_raster(data, backend='cupy')

Diff for: xrspatial/tests/test_hillshade.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
from xrspatial import hillshade
77
from xrspatial.tests.general_checks import (assert_numpy_equals_cupy,
88
assert_numpy_equals_dask_numpy, create_test_raster,
9-
general_output_checks)
10-
from xrspatial.utils import doesnt_have_cuda
9+
cuda_and_cupy_available, general_output_checks)
1110

1211
from ..gpu_rtx import has_rtx
1312

@@ -46,7 +45,7 @@ def test_hillshade_numpy_equals_dask_numpy(random_data):
4645
assert_numpy_equals_dask_numpy(numpy_agg, dask_agg, hillshade)
4746

4847

49-
@pytest.mark.skipif(doesnt_have_cuda(), reason="CUDA Device not Available")
48+
@cuda_and_cupy_available
5049
@pytest.mark.parametrize("size", [(2, 4), (10, 15)])
5150
@pytest.mark.parametrize(
5251
"dtype", [np.int32, np.int64, np.float32, np.float64])

Diff for: xrspatial/tests/test_multispectral.py

+12-12
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
from xrspatial.multispectral import (arvi, ebbi, evi, gci, nbr, nbr2, ndmi, ndvi, savi, sipi,
66
true_color)
7-
from xrspatial.tests.general_checks import create_test_raster, general_output_checks
8-
from xrspatial.utils import doesnt_have_cuda
7+
from xrspatial.tests.general_checks import (create_test_raster, cuda_and_cupy_available,
8+
general_output_checks)
99

1010

1111
@pytest.fixture
@@ -275,7 +275,7 @@ def test_ndvi_cpu(nir_data, red_data, result_ndvi):
275275
general_output_checks(nir_data, result, result_ndvi, verify_dtype=True)
276276

277277

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

306306

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

321321

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

335335

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

349349

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

363363

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

377377

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

391391

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

405405

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

419419

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

0 commit comments

Comments
 (0)