Skip to content

Add cupy, dask, metadata, and edge-case test coverage for mcda (#3149)#3156

Merged
brendancol merged 5 commits into
mainfrom
deep-sweep-test-coverage-mcda-2026-06-10
Jun 10, 2026
Merged

Add cupy, dask, metadata, and edge-case test coverage for mcda (#3149)#3156
brendancol merged 5 commits into
mainfrom
deep-sweep-test-coverage-mcda-2026-06-10

Conversation

@brendancol

Copy link
Copy Markdown
Contributor

Closes #3149.

  • Adds cross-backend parity tests for mcda: standardize (all 7 value functions), wlc, wpm, owa, fuzzy_overlay, boolean_overlay, constrain, and sensitivity, each compared against the numpy result on cupy, dask+numpy, and dask+cupy. test_mcda.py previously had no cupy or dask+cupy tests at all.
  • Adds metadata preservation tests (attrs, coords, dim names, output name) for every pipeline stage, plus edge cases: all-NaN criterion through wpm's positivity check and Inf propagation through wlc and fuzzy "and".
  • Test-only, no source changes. Paths that currently raise are pinned with strict xfail markers that flip when the bugs get fixed: owa on dask and cupy, piecewise/categorical standardize on GPU backends, and monte_carlo sensitivity on cupy (mcda: owa, piecewise/categorical standardize, and monte_carlo sensitivity raise on cupy/dask backends #3146); constrain dropping attrs under masking (mcda.constrain() drops raster attrs when exclusion masks are applied #3147). constrain on cupy/dask+cupy is xfail(strict=False) because that failure is the known cupy 13.6 + xarray xr.where incompatibility, not an mcda bug.

Backend coverage: numpy (existing), cupy, dask+numpy, dask+cupy. All new tests executed locally on a CUDA host.

Test plan:

  • pytest xrspatial/tests/test_mcda.py: 233 passed, 11 xfailed (CUDA host, cupy 13.6)
  • GPU classes ran rather than skipped: 32 passed, 9 xfailed under -k Cupy
  • flake8: no new warnings (3 pre-existing ones in the file left alone)

@github-actions github-actions Bot added the performance PR touches performance-sensitive code label Jun 10, 2026

@brendancol brendancol left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

PR Review: Add cupy, dask, metadata, and edge-case test coverage for mcda (#3149)

Blockers (must fix before merge)

  • None.

Suggestions (should fix, not blocking)

  • xrspatial/tests/test_mcda.py:2186 (TestConstrainBackends) and :2225 (TestBooleanOverlayBackends): the dask variants don't assert the result stays lazy, unlike _assert_standardize_matches_numpy and _assert_combine_matches_numpy which check hasattr(result.data, "compute"). A regression that eagerly materializes constrain/boolean_overlay on dask input would pass these tests. Add the same laziness assertion.
  • xrspatial/tests/test_mcda.py:2037,2070 : the xfail split for the broken standardize methods uses positional slices (STANDARDIZE_CASES[:-1], [:-2]). If someone appends a case to the list the slices silently shift and the wrong method gets the xfail. Selecting by method name (e.g. filtering on case[0]) keeps the markers pinned to the right cases.

Nits (optional improvements)

  • xrspatial/tests/test_mcda.py:2056 (test_result_stays_on_gpu): the inline import cupy is fine under the @cuda_and_cupy_available gate, but the parity helper already proves dispatch returns cupy via _to_numpy's .get() path; the extra test is borderline redundant. Harmless, keep or drop.

What looks good

  • The xfail markers are strict for the deterministic #3146 breakages and non-strict only for the environment-dependent xr.where/cupy incompatibility, so the markers flip loudly when #3146 is fixed.
  • Parity tests reuse create_test_raster and compare every backend against the numpy result on data that includes a NaN cell, so NaN handling parity is covered, not just the happy path.
  • The new metadata tests pin attrs/coords/dims/name per stage, with the constrain attrs drop correctly split out and xfailed against #3147 instead of papering over it.
  • Test-only diff; the source bugs surfaced by the new coverage are tracked in #3146/#3147 rather than bundled here.

Checklist

  • Algorithm matches reference (n/a, test-only)
  • All implemented backends produce consistent results (asserted; broken paths xfailed with issue links)
  • NaN handling is correct (NaN-bearing parity data, all-NaN wpm case)
  • Edge cases are covered by tests
  • Dask chunk boundaries handled correctly (ragged 3x3 chunks on 5x4 data)
  • No premature materialization in tests beyond final comparisons
  • Benchmark exists or is not needed (test-only)
  • README feature matrix update not applicable
  • Docstrings present where useful

@brendancol brendancol left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Follow-up review after 80cb5c9.

Disposition of the first-pass findings:

  • Suggestion (missing laziness assertions in constrain/boolean_overlay dask tests): fixed. Both _assert_matches_numpy helpers now assert hasattr(result.data, "compute") for dask backends.
  • Suggestion (positional STANDARDIZE_CASES[:-1] / [:-2] slices): fixed. Cases are now selected by method name via _standardize_cases(exclude=...) and _standardize_case(name), so appending a case can't shift the xfail markers.
  • Nit (test_result_stays_on_gpu redundancy): dismissed, and the opposite turned out to be true. _to_numpy passes numpy arrays through silently, so without a type check the cupy parity tests would pass even if dispatch fell back to numpy. Kept the explicit test and additionally added isinstance(result.data, type(input.data)) assertions to both parity helpers, which extends the backend-type check to every parametrized case.

Re-ran the suite on the CUDA host: 233 passed, 11 xfailed. No new findings.

@brendancol brendancol merged commit a93f684 into main Jun 10, 2026
3 of 7 checks passed
brendancol added a commit that referenced this pull request Jun 10, 2026
…e-mcda-2026-06-10-01

Conflicts:
- .claude/sweep-performance-state.csv: took main's newer geotiff/rasterize/
  reproject rows (LF endings) and re-inserted this branch's mcda row.

Semantic fixups in xrspatial/tests/test_mcda.py (auto-merged textually):
- Removed strict xfail markers that now XPASS: the three owa backend
  tests fixed by this PR, plus standardize cupy piecewise, standardize
  dask+cupy categorical (#3159) and constrain attrs (#3154), whose
  fixes merged before the #3156 xfails landed.
brendancol added a commit that referenced this pull request Jun 10, 2026
…cda-2026-06-10

Conflicts: .claude/sweep-accuracy-state.csv, xrspatial/mcda/sensitivity.py,
xrspatial/mcda/standardize.py, xrspatial/tests/test_mcda.py.

sensitivity.py taken from main (#3160 chunk-bounded monte_carlo already
handles cupy). standardize.py taken from main (#3159) plus this PR's
ascontiguousarray for cupy.interp, which #3159 lacked. test_mcda.py keeps
main's #3156 coverage with the strict xfail markers removed for the paths
this PR fixes (owa cupy/dask, standardize piecewise/categorical GPU,
monte-carlo sensitivity GPU) and for constrain attrs (fixed by #3154);
the de-xfail'd monte_carlo assertions are now NaN-aware. Sweep CSV is
main's rows plus this PR's mcda row.
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.

mcda: no cupy/dask+cupy test coverage; owa/wpm/constrain dask paths and metadata preservation untested

1 participant