Summary
sensitivity(..., method="monte_carlo", n_samples=0) returns a raster of zeros (with a numpy RuntimeWarning) instead of raising ValueError. Negative n_samples also passes through silently.
Cat 3 sibling of the NaN-weight bug fixed in #1312. Both came out of the same mcda audit; the audit notes in sweep-security-state.csv flag this one as "sensitivity n_samples=0 divides by zero".
Reproducer
import numpy as np
import xarray as xr
from xrspatial.mcda.sensitivity import sensitivity
data = np.random.rand(5, 5).astype(np.float32)
ds = xr.Dataset({
"a": xr.DataArray(data, dims=["y", "x"]),
"b": xr.DataArray(data * 2, dims=["y", "x"]),
})
result = sensitivity(ds, {"a": 0.5, "b": 0.5},
method="monte_carlo", n_samples=0)
# Expected: ValueError naming n_samples
# Actual: RuntimeWarning, then a DataArray of zeros
Root cause
In _monte_carlo, the sample loop runs range(n_samples). With n_samples=0 the loop body never executes, so running_m2 stays at zero, and variance = running_m2 / n_samples is 0/0 -> NaN. The downstream np.where(running_mean != 0, ...) masks the NaN as 0.0, so the caller gets a clean-looking zero raster with no indication anything went wrong. Negative n_samples falls through the same way (range(-1) is empty).
Fix
Validate n_samples >= 1 in the public sensitivity() entry point, raise ValueError naming the parameter. One is the meaningful minimum; test_single_sample_zero_cv already covers n_samples=1.
One fix per PR, per the security-sweep convention.
Summary
sensitivity(..., method="monte_carlo", n_samples=0)returns a raster of zeros (with a numpy RuntimeWarning) instead of raising ValueError. Negativen_samplesalso passes through silently.Cat 3 sibling of the NaN-weight bug fixed in #1312. Both came out of the same mcda audit; the audit notes in
sweep-security-state.csvflag this one as "sensitivity n_samples=0 divides by zero".Reproducer
Root cause
In
_monte_carlo, the sample loop runsrange(n_samples). Withn_samples=0the loop body never executes, sorunning_m2stays at zero, andvariance = running_m2 / n_samplesis0/0-> NaN. The downstreamnp.where(running_mean != 0, ...)masks the NaN as 0.0, so the caller gets a clean-looking zero raster with no indication anything went wrong. Negativen_samplesfalls through the same way (range(-1)is empty).Fix
Validate
n_samples >= 1in the publicsensitivity()entry point, raise ValueError naming the parameter. One is the meaningful minimum;test_single_sample_zero_cvalready coversn_samples=1.One fix per PR, per the security-sweep convention.