fix(test): degenerate-band sanity check asserts on BLAS rounding, not the library - #438
Merged
Merged
Conversation
…t the library `test__fnnls_cholesky__never_returns_a_non_finite_solution[0.0]` fails on the CI runners while passing locally, and has been red on main since at least efaf304 (run 31219374045). The failing line is the test's own vacuity guard: assert raised > 0 It requires `fnnls_cholesky` to raise LinAlgError for at least one of 40 seeds. But raising is an *allowed* outcome here, not a required one — this file already says so, in `test__cholinsertlast__singular_insertion_never_yields_an_unusable_pivot`: The invariant is therefore NOT "always raise" (a small positive pivot is still a usable pivot, and rejecting it would change likelihood evaluations). It is: either raise, or return a strictly positive finite pivot. On a singular insertion the Schur complement is zero up to rounding, so which side of zero it lands on is decided by floating-point summation order — the module comment says exactly this, and cites it as why the original bug reproduced on CI but not locally. `raised > 0` therefore asserts on the runner's BLAS rather than on the library. Measured across the 40 seeds: jitter raised solved 0.0 1 39 <- passes by a single seed locally; 0 on CI 1e-15 16 24 1e-12 20 20 1e-9 14 26 Only [0.0] is marginal, which is exactly the one parametrisation CI fails. Fix, keeping the regression this test exists for (never hand back NaN as though it were a valid reconstruction) fully intact: - The vacuity guard becomes `solved > 0` — at least one seed must actually have reached the finiteness assertion. That is what "not vacuous" means here; it is independent of how the runner rounds. - "The band really is degenerate" is now pinned on the fixture instead, via `cond(ZTZ) > 1e12`. Measured range is 1.07e16 to 3.71e18 across all seeds and jitter values, so the margin is wide and the property is deterministic. Verified in both directions by simulation: with no seed raising (the CI condition) all four parametrisations now pass; with every seed raising (genuine vacuity, finiteness never checked) the test still fails. Full suite: 906 passed, 52 skipped. The 3 test_transformer failures are pre-existing and unrelated (missing optional pynufft). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KazMzMZYPLfaZoYQ79YQ8Q
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
mainis red.test__fnnls_cholesky__never_returns_a_non_finite_solution[0.0]has been failing on the CI runners since at leastefaf3041(run 31219374045, 2026-08-07) while passing locally.Nothing is wrong with the library. The failing line is the test's own vacuity guard:
Why that assertion cannot hold reliably
It requires
fnnls_choleskyto raiseLinAlgErrorfor at least one of 40 seeds. But raising is an allowed outcome here, not a required one — and this file already says so, intest__cholinsertlast__singular_insertion_never_yields_an_unusable_pivot:The module comment goes further and identifies the exact mechanism:
So
raised > 0asserts on how the runner's BLAS rounds, not on library behaviour.Measurements
Across the 40 seeds, on a single-threaded local BLAS:
[0.0]passes locally by a single seed out of 40, and zero seeds raise on the CI runners. That is precisely the one parametrisation CI fails — the other three have comfortable margins.The fix
The regression this test exists for — never hand back NaN as though it were a valid reconstruction — is untouched. Only the vacuity guard changes.
solved > 0replacesraised > 0: at least one seed must actually have reached the finiteness assertion. That is what "not vacuous" means here, and it does not depend on rounding.cond(ZTZ) > 1e12. Measured range is 1.07e16 – 3.71e18 across every seed and jitter value, so the margin is wide and the property is deterministic on any machine.Verification
Both directions checked by simulation, not just by the suite going green:
Full suite: 906 passed, 52 skipped. The 3
test_transformer.pyfailures are pre-existing and unrelated (missing optionalpynufft).Opened so #437 can merge onto a green base.
Generated by Claude Code