Skip to content

Worktree issue 1030#1059

Merged
brendancol merged 8 commits intomasterfrom
worktree-issue-1030
Mar 23, 2026
Merged

Worktree issue 1030#1059
brendancol merged 8 commits intomasterfrom
worktree-issue-1030

Conversation

@brendancol
Copy link
Contributor

Fixes #

Proposed Changes

New xrspatial.mcda subpackage with five modules:
- standardize: value functions (linear, sigmoidal, gaussian, triangular, piecewise, categorical)
- weights: AHP eigenvector method with consistency ratio, rank-order weighting (ROC, RS, RR)
- combine: WLC, WPM, OWA, fuzzy overlay, boolean overlay
- constrain: exclusion mask application
- sensitivity: one-at-a-time perturbation and Monte Carlo CV analysis
69 tests covering standardize (all 6 methods + dask), AHP and rank
weights, WLC/WPM/OWA/fuzzy/boolean combination, constraints,
sensitivity analysis, and an end-to-end suitability workflow.
Also fix nanmin/nanmax in linear standardize when bounds are not given.
Covers the full workflow: synthetic terrain criteria, all six
standardization methods, AHP and rank-order weights, WLC/WPM/OWA/fuzzy
combination, constraint masking, and sensitivity analysis with
side-by-side method comparison plots.
Correctness:
- Fix triangular degenerate cases (peak==low, peak==high, all equal)
  returning 0 instead of the correct one-sided triangle shape
- Fix _get_xp returning cupy for dask+cupy arrays, which would pass
  dask arrays to cupy ops and fail or materialize full arrays
- Fix dask+cupy map_blocks closures in categorical/piecewise using
  np.asarray to handle cupy chunks
- Clamp sigmoidal exponent to [-500, 500] to prevent overflow warnings
- Suppress RuntimeWarning from nanmin/nanmax on all-NaN input
- Remove unused baseline variable in _oat sensitivity
- Remove unused stacked.copy() in OWA

Performance:
- Monte Carlo sensitivity: compute dask criteria eagerly before the
  loop to prevent graph explosion (1000 iterations was creating
  35,000+ chained graph tasks with no parallelism)
- Fuse two separate dask.compute calls for nanmin/nanmax into one
- Replace xr.where(a < b, a, b) with np.minimum in fuzzy and/or

Tests: add 10 edge case tests (degenerate triangles, sigmoid overflow,
all-NaN input, NaN propagation in WLC, OWA exact equality, dask MC).
Warn when AHP receives fewer than n(n-1)/2 pairwise comparisons,
since missing pairs silently default to equal importance.

28 new tests covering:
- Single-pixel rasters through all standardize methods and combine
- NaN propagation through WLC, WPM, OWA, and all 5 fuzzy operators
- Piecewise extrapolation outside breakpoint range
- Categorical float precision (exact match only) and integer data
- AHP incomplete comparison warning and no-warn for complete input
- Mismatched coords producing NaN outside overlap region
- Dask chunk alignment for standardize, WLC, fuzzy, and OAT sensitivity
- WPM with all-ones and all-zeros
- Constrain with empty exclude list and full exclusion
Input validation:
- Raise on inverted bounds in linear standardize (lo > hi)
- Raise on duplicate piecewise breakpoints
- Raise on AHP self-comparisons, zero, and negative values
- Raise when weights dict has keys not in criteria Dataset
- Boolean overlay casts float input to bool instead of TypeError

25 new test classes / cases covering:
- Inverted bounds, duplicate breakpoints, AHP validation
- Extra weight keys, boolean overlay with float data
- Inf values treated as NaN across all standardize methods
- Sensitivity weight clamping to [0, 1] boundary
- Monte Carlo reproducibility (same seed = same output)
- Gaussian with very small std (delta function behavior)
- OWA risk attitude ordering (conservative <= optimistic)
- Rank weights with 20 criteria (sum, monotonicity)
- Constant surface with no bounds returns 0.5
- Overlapping constraint masks
Fixes:
- Single-criterion sensitivity: _perturb_weights returned weights
  summing to <1 when no other criteria exist to absorb the remainder.
  Now returns unmodified weights (perturbation is a no-op).
- Duplicate criteria names in AHP and rank_weights: silently corrupted
  results by overwriting index mapping. Now raises ValueError.

16 new tests covering:
- Single-criterion Dataset through WLC, WPM, OWA, all fuzzy operators
- Boolean overlay NaN gotcha (bool(NaN)=True documented)
- Sensitivity with single criterion (OAT and MC both zero)
- Sensitivity with WPM combine method (OAT and MC)
- AHP with 16 criteria (>15, _RI table fallback)
- Duplicate criteria names in AHP and rank_weights
- Constrain with integer mask
- Monte Carlo with n_samples=1 (zero CV)
- 3D DataArray input (band, y, x)
@github-actions github-actions bot added the performance PR touches performance-sensitive code label Mar 23, 2026
@brendancol brendancol merged commit 8486517 into master Mar 23, 2026
10 of 11 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.

1 participant