Description
The polygonize() function exposes atol and rtol parameters for grouping near-equal float pixels into the same polygon. These tolerance values flow through every backend:
- numpy:
_polygonize_numpy(values, mask, conn8, transform, atol, rtol)
- cupy:
_polygonize_cupy(...) at polygonize.py:808 forwards atol/rtol to either _polygonize_numpy (float dtypes, lines 823-825 / 829-830) or to the integer GPU CCL path (where the args are documented no-ops)
- dask / dask+cupy:
_polygonize_dask(...) at polygonize.py:1719 plumbs the values through dask.delayed(_polygonize_chunk)(..., atol, rtol) and into _bucket_key_for_value(boundary_by_value, val, atol, rtol) for cross-chunk merging
Existing tests cover atol/rtol end-to-end on the numpy and dask+numpy backends (test_polygonize_strict_float_equality, test_polygonize_dask_multi_chunk_strict_float, test_polygonize_custom_atol_intermediate, etc.), but no test exercises non-default atol/rtol values on the cupy or dask+cupy backends.
This is the same dispatcher-silently-drops-backend-kwarg pattern that has shipped as a defect on adjacent surfaces (see the GeoTIFF max_cloud_bytes, dtype, and mask_nodata dispatcher tests). A regression in the GPU backend dispatchers that dropped atol/rtol on its way to _polygonize_numpy would change the polygon count silently on float rasters and would not be caught by any current test.
Scope
Add pure-test coverage:
atol=0, rtol=0 on a float cupy / dask+cupy raster recovers strict-equality grouping and matches the numpy result on the same input.
- An intermediate
atol value (between two known float steps) picks the same split on cupy / dask+cupy as it does on numpy.
- Integer cupy / dask+cupy rasters still ignore
atol/rtol (matching the numpy / dask+numpy behaviour already pinned by test_polygonize_integer_default_unchanged_by_atol and test_polygonize_dask_integer_atol_ignored).
Source is not touched. Tests only.
Test plan
- New tests in
xrspatial/tests/test_polygonize_atol_rtol_backend_coverage_2026_05_27.py
- All four backends covered by parity comparisons against numpy
- Run on a CUDA host so the cupy and dask+cupy branches execute, not just the skip-marker
Description
The
polygonize()function exposesatolandrtolparameters for grouping near-equal float pixels into the same polygon. These tolerance values flow through every backend:_polygonize_numpy(values, mask, conn8, transform, atol, rtol)_polygonize_cupy(...)atpolygonize.py:808forwardsatol/rtolto either_polygonize_numpy(float dtypes, lines 823-825 / 829-830) or to the integer GPU CCL path (where the args are documented no-ops)_polygonize_dask(...)atpolygonize.py:1719plumbs the values throughdask.delayed(_polygonize_chunk)(..., atol, rtol)and into_bucket_key_for_value(boundary_by_value, val, atol, rtol)for cross-chunk mergingExisting tests cover
atol/rtolend-to-end on the numpy and dask+numpy backends (test_polygonize_strict_float_equality,test_polygonize_dask_multi_chunk_strict_float,test_polygonize_custom_atol_intermediate, etc.), but no test exercises non-defaultatol/rtolvalues on the cupy or dask+cupy backends.This is the same dispatcher-silently-drops-backend-kwarg pattern that has shipped as a defect on adjacent surfaces (see the GeoTIFF
max_cloud_bytes,dtype, andmask_nodatadispatcher tests). A regression in the GPU backend dispatchers that droppedatol/rtolon its way to_polygonize_numpywould change the polygon count silently on float rasters and would not be caught by any current test.Scope
Add pure-test coverage:
atol=0, rtol=0on a float cupy / dask+cupy raster recovers strict-equality grouping and matches the numpy result on the same input.atolvalue (between two known float steps) picks the same split on cupy / dask+cupy as it does on numpy.atol/rtol(matching the numpy / dask+numpy behaviour already pinned bytest_polygonize_integer_default_unchanged_by_atolandtest_polygonize_dask_integer_atol_ignored).Source is not touched. Tests only.
Test plan
xrspatial/tests/test_polygonize_atol_rtol_backend_coverage_2026_05_27.py