Skip to content

Test coverage: atol/rtol forwarding on cupy and dask+cupy backends#2540

Merged
brendancol merged 2 commits into
mainfrom
deep-sweep-test-coverage-polygonize-2026-05-27
May 28, 2026
Merged

Test coverage: atol/rtol forwarding on cupy and dask+cupy backends#2540
brendancol merged 2 commits into
mainfrom
deep-sweep-test-coverage-polygonize-2026-05-27

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

Closes #2537.

Summary

  • Adds 15 tests in test_polygonize_atol_rtol_backend_coverage_2026_05_27.py covering the atol / rtol kwargs on the cupy and dask+cupy backends of polygonize().
  • Pins strict-equality behaviour (atol=0, rtol=0), intermediate atol splits, and the integer-no-op contract.
  • Test-only, no source changes. Pre-existing 237 polygonize tests still pass; new suite of 15 brings the total to 252.

Backend coverage

  • cupy: covered (eager, float and integer)
  • dask+cupy: covered (single-chunk and multi-chunk, float and integer)
  • numpy and dask+numpy were already covered by prior #2173 and #2174 tests; this PR does not duplicate those.

Mutation evidence

  • Dropping atol=atol, rtol=rtol from the _polygonize_numpy forward call at polygonize.py:823-825 (cupy float branch) flips 3 of 5 cupy tests red.
  • Dropping the atol, rtol args from dask.delayed(_polygonize_chunk)(...) at polygonize.py:1748-1754 flips 2 of 6 dask+cupy tests red.
  • Source was restored bit-exact via md5sum.

Test plan

  • Run on CUDA host (pytest test_polygonize_atol_rtol_backend_coverage_2026_05_27.py) -> 15 passed
  • Full polygonize test suite (test_polygonize.py + the two prior coverage files + this one) -> 252 passed, 13 skipped
  • CI green on the PR

The atol/rtol kwargs route through _polygonize_cupy and _polygonize_dask
into the chunked _polygonize_chunk helper (polygonize.py:823-825,
829-830, 1748-1758).  Existing tests pin the parameter behaviour on
the numpy and dask+numpy backends only -- the cupy and dask+cupy
dispatchers were untested.  A regression that dropped the kwargs in
either dispatcher would silently change the float polygon count and
would not be caught.

15 new tests cover:

  - cupy: atol=0, rtol=0 strict equality on _REPRO_2173 produces three
    distinct polygons; default tolerance still merges to one;
    intermediate atol picks the documented splits; rtol-only branch
    coverage at large/small values.
  - dask+cupy: single-chunk and multi-chunk strict equality (the
    multi-chunk case engages the _bucket_key_for_value cross-chunk
    merge); default tolerance still merges adjacent pixels;
    intermediate atol single + multi chunk.
  - integer cupy and dask+cupy: huge atol does not collapse distinct
    integer regions, matching the existing numpy / dask+numpy pins.

Mutation against the _polygonize_cupy float-branch forward call (drop
atol/rtol on the _polygonize_numpy call at polygonize.py:823-825)
flips 3 of 5 cupy tests red.  Mutation against the dask path
(drop atol, rtol args from dask.delayed(_polygonize_chunk)(...) at
polygonize.py:1748-1754) flips 2 of 6 dask+cupy tests red.  Source
was restored bit-exact via md5sum.

Test-only.  No source changes.  Updates the deep-sweep state CSV with
the pass-2 audit notes.
@github-actions github-actions Bot added the performance PR touches performance-sensitive code label May 27, 2026
Copy link
Copy Markdown
Contributor Author

@brendancol brendancol left a comment

Choose a reason for hiding this comment

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

Review

Scope: test additions only, no source changes. Reviewed the new file and the state CSV diff.

Blockers

None.

Suggestions

None. The tests follow the existing conventions in the polygonize coverage file:

  • cuda_and_cupy_available and dask_array_available markers are applied correctly so a CUDA-free host skips cleanly.
  • Strict-equality, intermediate-atol, default-tolerance, and integer-no-op cases mirror the numpy / dask+numpy pins for the same kwargs (1:1 parity with test_polygonize_strict_float_equality, test_polygonize_custom_atol_intermediate, test_polygonize_dask_integer_atol_ignored).
  • The _areas_by_value helper accepts an explicit atol/rtol for cross-backend bucket matching, matching the helper used in test_polygonize_dask_float_tolerance_matches_numpy_2171.
  • Mutation evidence is documented in the commit message: removing atol=atol, rtol=rtol from _polygonize_cupy at lines 823-825 flips 3 cupy tests red; removing the same kwargs from dask.delayed(_polygonize_chunk)(...) flips 2 dask+cupy tests red. The mutated source was restored bit-exact.

Nits

None.

Disposition

LGTM. Test-only PR closes a Cat 4 MEDIUM parameter-coverage gap surfaced by the 2026-05-27 test-coverage sweep on the polygonize module. The state CSV has the audit notes for the future re-inspection.

Expand atol/rtol coverage and tighten one docstring on the new
dask+cupy / cupy parameter-coverage tests.  All additions are test-only
and confirmed via mutation evidence (cupy kwargs drop -> 3 reds;
dask.delayed kwargs drop -> 2 reds; source restored bit-exact).

  - TestRtolDaskCuPy: new class mirroring TestRtolCuPy on the dask+cupy
    backend.  Adds three tests (large_rtol single + multi chunk,
    small_rtol multi chunk) pinning the rtol-only branch through
    _polygonize_chunk and _bucket_key_for_value with atol=0.
  - TestIntermediateAtolDaskCuPy.test_large_atol_one_polygon_multi_chunk:
    pins the cross-chunk merge bucket with a non-default atol (1e-4),
    closing the missing single-chunk / multi-chunk pair for the large-
    atol case.
  - test_default_tolerance_still_merges_multi_chunk docstring: now
    documents the intentional 2-vs-1 polygon count divergence between
    single-chunk numpy (transitive merge: 1.0 <-> 1.000009 <-> 1.000018)
    and the dask cross-chunk merge bucket (pairwise comparison only),
    so the hard-coded assert len(v_dc) == 2 is no longer surprising.

19 passed (was 15), full polygonize suite 256 passed / 13 skipped.
@brendancol
Copy link
Copy Markdown
Contributor Author

Self-review (post-merge follow-up)

Reviewed the diff against the correctness, backend-parity, and test-coverage rubric. No blockers. Pushed one follow-up commit (6f46951) to address the two coverage gaps below and clarify one docstring. All additions are test-only.

What was added in 6f46951

  1. TestRtolDaskCuPy (3 tests). The original PR pinned the rtol-only branch on the eager cupy backend (TestRtolCuPy) but had no dask+cupy counterpart. The new class covers large-rtol single + multi chunk and small-rtol multi chunk, exercising _bucket_key_for_value with atol=0, rtol>0.

  2. test_large_atol_one_polygon_multi_chunk in TestIntermediateAtolDaskCuPy. Closes the missing single-chunk / multi-chunk pair for the large-atol case so the cross-chunk merge bucket is pinned with a non-default atol.

  3. test_default_tolerance_still_merges_multi_chunk docstring. The hard-coded assert len(v_dc) == 2 looked suspicious until I traced through the merge logic: single-chunk numpy transitively chains 1.0 -> 1.000009 -> 1.000018 (one polygon), but the dask cross-chunk merge bucket compares pairwise against bucket keys, so 1.0 and 1.000018 do not merge (their 18e-6 gap exceeds the default rtol*|1.0| = 1e-5 threshold), leaving two polygons. Docstring now documents this intentional divergence.

Mutation evidence re-run on the expanded suite

  • Dropping atol=atol, rtol=rtol from the _polygonize_numpy forward call in the cupy float branch (polygonize.py:823-825): 3 cupy tests fail (test_three_distinct_values, test_small_atol_three_polygons, test_small_rtol_keeps_distinct). Source restored bit-exact via md5sum (64d8ed672018f764daad3ad3ad724be0).
  • Dropping atol, rtol args from dask.delayed(_polygonize_chunk)(...) (polygonize.py:1748-1754): 2 dask+cupy tests fail (test_single_chunk, test_small_atol_three_polygons_single_chunk). Source restored bit-exact.

Test counts

  • New file: 19 passed (was 15).
  • Full polygonize suite: 256 passed, 13 skipped (was 252, 13 skipped).

Suggestions deferred

  • Parametrizing the chunk-size variants would shrink the file by ~20 lines but breaks the existing per-test-class pattern used elsewhere in the polygonize test suite. Not worth the inconsistency.
  • The line-number references in the module docstring (polygonize.py:823-825 etc.) are accurate as of HEAD but will drift. Same comment as above on consistency with the existing suite.

@brendancol brendancol merged commit 011b712 into main May 28, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance PR touches performance-sensitive code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

polygonize: add atol/rtol parameter coverage on cupy and dask+cupy backends

1 participant