fix(mmpde): finite SPD fallback for fully-degenerate metric evaluations (#352)#368
Conversation
…enerate metric input (#352) Two guards, because the issue's wmax sketch alone is insufficient: 1. The relative floor is anchored from the FINITE eigenvalues only, falling back to the unit scale when none exist - previously an all-NaN eigenvalue set made nanmax (and max(nan, 1.0)) NaN, so the floor itself was NaN. 2. The eigenvector basis of a fully-degenerate tensor is also NaN, so rebuilding through it re-introduces NaN even with a finite floor; such tensors fall back to the identity basis, i.e. the isotropic floor metric - the honest 'no information here' answer. The helper is hoisted to module level (it captured nothing) so it is unit-testable; the mover's call site is unchanged. Regression tests: all-NaN and all-inf batches produce finite SPD output; a genuine SPD batch passes through bit-identical; one degenerate tensor cannot perturb its neighbours. Underworld development team with AI support from Claude Code
…ACK-path dependent (adversarial review of #368) Independent review probing found the 2x2 kernel returns quiet NaNs while the general (3x3) kernel raises LinAlgError from the batched eigh BEFORE the degeneracy guards run - and a batched eigh raises for the whole batch if any one tensor fails. Unreachable via the 2D-only mover today, but the 3D MMPDE capstone lands exactly here, and the 2x2 tests were silently LAPACK-build dependent. The eigh call now retries per tensor on LinAlgError: valid neighbours keep their exact decomposition, tensors that still fail are marked non-finite and rebuilt as the isotropic fallback. Docstring also states the floor's absolute 1e-8 minimum honestly (pre-existing semantics: a far-below-unit-scale valid batch would be floored). Tests: 3x3 all-NaN batch; 3x3 mixed batch where one inf tensor makes batched eigh raise - neighbours preserved bit-identical. Underworld development team with AI support from Claude Code
|
An independent adversarial review pass (standing in for the quota-exhausted Copilot) probed this fix and produced two accepted findings, now addressed in the second commit:
One PR-body correction from the review: the invariance claim holds per tensor (any tensor with finite eigenvalues is unchanged); a fully-degenerate tensor in a mixed batch previously produced NaN and now gets the finite fallback — a strict improvement, and exactly what the tests assert. Review verdicts on the remaining attack surfaces: the feared Gates after hardening: Underworld development team with AI support from Claude Code |
Closes #352.
The bug
_spd_sanitise— the guard that keeps the MMPDE energy's fractional powers finite — failed exactly when it was most needed: if EVERY eigenvalue on a rank was NaN/inf (fully-degenerate metric evaluation, e.g. a starved or badly-deformed partition under live convection),np.nanmaxreturned NaN,max(nan, 1.0)propagated it (Pythonmaxcompares with>, false for NaN), and the "sanitised" output was NaN instead of the intended benign SPD fallback. Confirmed by direct probe on the installed build before any change.The fix (two guards — the issue's sketch alone was insufficient)
einsum; such tensors now rebuild in the identity basis — the isotropic floor metric ("no information here, stay isotropic").The helper is hoisted to module level (it captured nothing) for unit-testability; the mover's behaviour for any input with at least one finite eigenvalue is unchanged, and a genuine SPD batch passes through bit-identical (asserted).
Verification
TestSPDSanitiseintest_0850(level_1/tier_a): all-NaN and all-inf batches → finite SPD; valid SPD → bit-identical passthrough; one degenerate tensor cannot perturb neighbours. Shown failing against the unmodified build first.level_1 and tier_a380 passed (376 baseline + the 4 new tests); np2test_0855_mesh_smoothing_parallel4 passed; style gate green (86 allowlisted).This is the live-mmpde robustness path (the surviving mover; see #346 discussion) — relevant to the planned MMPDE+NVB capstone.
Underworld development team with AI support from Claude Code