Skip to content

wlc/wpm/owa/ahp_weights silently propagate NaN weights to all-NaN raster #1311

@brendancol

Description

@brendancol

Description

_validate_weights in xrspatial.mcda.combine (and the owa() order-weights check) only validates that weights sum to about 1.0:

total = sum(weights.values())
if abs(total - 1.0) > 0.01:
    raise ValueError(...)

If any weight is NaN, the sum is NaN, abs(NaN - 1.0) > 0.01 is False, and the check passes. wlc(), wpm(), and owa() then propagate the NaN to every pixel and return an all-NaN raster, no exception raised.

ahp_weights in xrspatial.mcda.weights has the same problem. Its guard is if val <= 0:, which NaN slips past. The matrix fills with NaN, the eigenvector solve produces NaN, and the function returns NaN weights.

Cat 3 (NaN as logic error) finding from the mcda security sweep. HIGH severity because the output looks structurally valid: same shape, no warning, no exception. Anything downstream (constrain, plotting, export) will treat it as real data.

Reproduction

import xarray as xr
import numpy as np
from xrspatial.mcda.combine import wlc

data = xr.Dataset({
    'a': xr.DataArray(np.array([[0.5, 0.5], [0.5, 0.5]])),
    'b': xr.DataArray(np.array([[0.5, 0.5], [0.5, 0.5]])),
})
result = wlc(data, {'a': 0.5, 'b': float('nan')})
print(result.values)
# Expected: ValueError naming the offending criterion
# Actual:   [[nan nan] [nan nan]]

Expected behaviour

_validate_weights and the owa() order-weights check should raise ValueError and name the offending criteria (or positions) when a weight is NaN or infinite. ahp_weights should reject NaN/Inf comparison values the same way it rejects non-positive ones.

Affected files

  • xrspatial/mcda/combine.py -- _validate_weights (~lines 22-39), owa order-weights check (~lines 154-158)
  • xrspatial/mcda/weights.py -- ahp_weights value guard (~line 94)

Fix sketch

Sweep the weight dict once, raise ValueError on any NaN/Inf, then run the sum-to-1 check. Apply the same sweep to order_weights in owa. In ahp_weights, extend the val <= 0 check to also reject np.isnan(val) and np.isinf(val).

One fix per PR, per the existing security-sweep convention.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions