Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1389 +/- ##
==========================================
+ Coverage 69.36% 69.38% +0.02%
==========================================
Files 6122 6122
Lines 486711 486847 +136
Branches 23268 23268
==========================================
+ Hits 337584 337815 +231
+ Misses 148689 148594 -95
Partials 438 438
... and 2 files with indirect coverage changes Continue to review full report in Codecov by Harness.
|
rmlarsen
force-pushed
the
larrb-bounded-bracket
branch
from
September 8, 2026 03:11
a0d34af to
dded2ab
Compare
rmlarsen
force-pushed
the
larrb-bounded-bracket
branch
from
September 8, 2026 03:47
dded2ab to
111c8c5
Compare
Contributor
Author
|
Verified on an Apple M4 (macOS, Homebrew gfortran 16.2, Release build with the CI flags). With this branch merged onto current master, the full test suite passes, the new tests fail without the fix, and the reproducer behaves as described above. |
…cannot hang xSTEMR Before bisecting, xLARRB widens the initial interval [W-WERR, W+WERR] until the Sturm count of the representation agrees with the eigenvalue index, moving the endpoint by BACK and doubling BACK at every step. In exact arithmetic that ends quickly, because the count is 0 below the spectrum and N above it. A NaN pivot is never counted, so with a NaN in the representation the count can stay short of the index and the loop runs forever: xSTEMR, and through it xSTEVR, xSYEVR and xHEEVR with RANGE = 'A', never return for a symmetric tridiagonal matrix with a NaN in one of its first two rows. The loop also stalls when WERR is exactly zero, which xLARRE produces for an eigenvalue that dqds returns as exactly zero. xLARRJ, which refines the eigenvalues by bisection on the matrix itself when only eigenvalues are wanted, has the same two widening loops with the same step and the same hang: xSTEMR with JOBZ = 'N' never returns for the same matrices. Start BACK at no less than the minimum interval width 2*PIVMIN, and stop widening with INFO = 1 once BACK has overflowed, when no further step can move the endpoint; a finite matrix never gets there, since BACK passes the spectral diameter within MAXITR doublings. The xLARRB callers already propagate a nonzero INFO (xLARRV returns -1, xLARRE -4, xSTEMR 21 or 11) and the drivers then fall back to xSTEBZ and xSTEIN; the one xLARRB call in xLARRV that ignored INFO now checks it like the others, in all four precisions. xSTEMR never looked at the INFO of xLARRJ; it now returns INFO = 3X for a failure there, next to the documented 1X and 2X codes of xLARRE and xLARRV. xCHKST gets the case as a regression test: after the size and type loops it calls xSTEMR with RANGE = 'A' on a tridiagonal matrix whose first diagonal entry is a NaN, once with JOBZ = 'V' and once with JOBZ = 'N', so that both refinement paths are covered. Only a negative INFO is reported as a failure; the test is that the call returns at all. On the parent all four xeigtst binaries stop responding there. Over 100 NaN cases (DSTEMR, SSTEMR, DSYEVR, DSTEVR, ZHEEVR; five NaN positions; n = 3, 5, 8, 20) the parent hangs in 60, this branch returns in all of them; with JOBZ = 'N' (xSTEMR in all four precisions, five NaN positions, n = 3, 5, 8, 20) the parent hangs in 48 of 80, this branch returns INFO = 31 in every case that hung. 200 finite matrices give bit-identical eigenvalues from DSTEMR on both, with JOBZ = 'V' and with JOBZ = 'N'. The full LAPACK test suite passes: 5441941 LAPACK tests, 0 numerical errors, 0 other errors, 40 tests more than the parent from the new calls. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
rmlarsen
added a commit
to rmlarsen/lapack
that referenced
this pull request
Sep 15, 2026
xSTEMR scales the matrix into the range xLARRE can represent:
ELSE IF( TNRM.GT.RMAX ) THEN
SCALE = RMAX / TNRM
For a matrix with an infinite entry TNRM is infinite, SCALE underflows
to zero, the scaling turns the infinite entry into a NaN and every
other entry into zero, and the solver ran on that matrix: RANGE = 'A'
and 'I' returned INFO = 0 with every eigenvalue a NaN, RANGE = 'V'
scaled the interval to (0, 0] and returned no eigenvalues, and with
the infinite entry off the diagonal the representation tree search
never returned (the NaN hang of Reference-LAPACK#1389).
Leave a matrix whose scale factor would be zero unscaled, and let
xLARRE, which computes the base representations, reject a matrix with
an infinite entry as one for which no representation exists, INFO = 2,
the code it already uses when the search for a representation gives up.
xSTEMR returns INFO = 12 and the drivers that call it, xSTEVR, xSYEVR
and xHEEVR, fall back to xSTEBZ and xSTEIN as for any other xSTEMR
failure. A NaN matrix is not touched by this change: its norm
compares false against every threshold and it takes the same path as
before.
xCHKST gets the case as a regression test: after the size and type
loops it calls xSTEMR with RANGE = 'A' on an N = NMAX tridiagonal
matrix whose first diagonal entry is +Inf, once with JOBZ = 'V' and
once with JOBZ = 'N', and reports INFO = 0 as a failure. On the
parent commit all four calls return INFO = 0 with NaN eigenvalues;
here they return INFO = 12. Over 420 placements of +Inf and -Inf
(n = 3 to 64, RANGE = 'A', 'V', 'I', real and complex) the parent
returned INFO = 0 with NaN eigenvalues in 52 cases, hung in 24 and
returned INFO = 11 in the rest; this branch returns INFO = 12 in all
of them. The 1 by 1 and 2 by 2 matrices, which xSTEMR solves in closed
form before scaling, are unchanged, and every finite case is
bit-identical.
The full LAPACK test suite passes: 0 numerical errors, 0 other errors,
40 tests more than the parent from the new calls.
The regression test fails on the parent and passes with the fix, and the
reproducer prints the same before and after output, with gfortran 13
(x86-64 Release and Debug with -fcheck=all, and under QEMU on aarch64,
ppc64le, s390x and riscv64), flang-19 and Intel ifx 2025.3.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
rmlarsen
force-pushed
the
larrb-bounded-bracket
branch
from
September 15, 2026 20:28
111c8c5 to
fcc5958
Compare
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.
Disclaimer: This PR was prepared using Claude Code.
Summary
xLARRBrefines eigenvalues of a relatively robust representationxLARRJdoes the same on the tridiagonal matrix itself when only eigenvalues are wanted. Before bisecting each makes sure the initial interval[W - WERR, W + WERR]contains the wanted eigenvalue by widening it: it moves the endpoint byBACK, doublesBACK, and repeats until the Sturm count agrees with the index. In exact arithmetic that ends quickly, because the count is 0 below the spectrum andNabove it. A NaN pivot is never counted, so with a NaN in the representation the count stays short of the index and the loop runs forever:DSTEMR, and through itxSTEVR,xSYEVRandxHEEVRwithRANGE = 'A', never return for a symmetric tridiagonal matrix with a NaN in one of its first two rows, withJOBZ = 'V'throughxLARRBand withJOBZ = 'N'throughxLARRJ. This PR stops the widening in both routines onceBACKhas overflowed, at which point no further step can move the endpoint, and returnsINFO = 1, which thexLARRBcallers already propagate (xLARRVreturns -1,xLARRE-4,xSTEMR21 or 11, and the drivers fall back toxSTEBZ+xSTEIN). It also checksxLARRB's return code at the one call site inxLARRVthat ignored it, andxLARRJ's return code inxSTEMR, which never looked at it and now returnsINFO = 3Xfor a failure there. Twelve files:{s,d}larrb.f,{s,d}larrj.f,{s,d,c,z}larrv.f,{s,d,c,z}stemr.f.Description
The loops are
and its mirror image for
RIGHT.DLANEGdeliberately does not count a NaN pivot (DPLUS.LT.ZEROis false) and replaces a NaN quotient by 1 to continue the recurrence, so for a representation with a NaN inD(1)the count never exceedsN - 1for any shift, and theRIGHTloop for the last eigenvalue widens through+Infwithout end. The widening also stalls whenWERRis exactly zero, whichxLARREproduces for an eigenvalue that the dqds algorithm returns as exactly zero (WERR(I) = RTOL*ABS(W(I))): thenBACK = 0, the endpoint never moves, and the loop terminates only if the Sturm count atWalready agrees with the index.xLARRJhas the same two loops, written with a factorFACthat doubles and a stepWERR( II )*FAC, and the same Sturm count (DPLUS.LT.ZERO), soDSTEMR( 'N', 'A', ... )on the same matrices never returns either: it skipsxLARRVand refines thexLARREeigenvalues withxLARRJinstead.xSTEMRignores theINFOofxLARRJ.Fix. In both routines
BACKstarts atMAX( WERR( II ), MNWDTH ), whereMNWDTH = 2*PIVMINis the minimum interval width the bisection already uses, so a zeroWERRstill makes progress; and each loop gives up withINFO = 1whenBACK.LT.TWO*BACKis false, i.e. whenBACKis infinite (or NaN), because no further step can move the endpoint. The test is false only afterBACKhas already overflowed, which a finite representation never reaches: the count is 0 onceLEFTis below the Gershgorin bound andNonceRIGHTis above it, andBACKdoubles from at least2*PIVMINto beyond the spectral diameter in at mostMAXITRsteps.INFOis documented in both (= 1: the interval could not be widened to contain the eigenvalue; the counts are inconsistent, as they are for a NaN or an Inf).xSTEMRnow checksxLARRJ'sINFOand returnsINFO = 30 + ABS( IINFO ), documented next to the existing1X(xLARRE) and2X(xLARRV) codes. The secondxLARRBcall inxLARRV(refining the extremal eigenvalues of a child cluster) now returnsINFO = -1on failure like the first one instead of continuing with an unrefined bracket, in all four precisions:CLARRVandZLARRVcall the realxLARRBat the same three places asSLARRVandDLARRV, and without the checkCSTEMRandZSTEMRon the NaN matrices below go on into the representation construction and report thexLARRFfailure (INFO = 22) whereSSTEMRandDSTEMRreport the refinement failure (INFO = 21).Minimal reproducer
DSYEVRreturns through itsDSTEBZ+DSTEINfallback, which reports no eigenvalue for the NaN matrix, the same result it gives on master for a NaN that is not in the first two rows.Regression test.
xCHKSTgets the case: after the size and type loops it callsxSTEMRwithRANGE = 'A'on a tridiagonal matrix whose first diagonal entry is a NaN, once withJOBZ = 'V'and once withJOBZ = 'N', which coversxLARRBandxLARRJrespectively. Only a negativeINFOcounts as a failure, since the routine returns no meaningful output for such a matrix and the point of the test is that it returns at all. On the parent commit all fourxeigtstbinaries stop responding at that call, soLAPACK-xeigtst{s,d,c,z}_sep_infail by timeout; with the fixsep.inpasses unchanged (13464 tests for the real precisions, 11016 for the complex).Validation
ctest: 100% of 215 tests passed). The 40 extra tests over the parent commit are the new NaN calls, 5 sizes times 2 job options times 4 precisions.DSTEMR,SSTEMR,DSYEVR,DSTEVR,ZHEEVR; NaN inD(1),D(2),E(1),D(N),E(N-1);N = 3, 5, 8, 20), one process per case with a 10 s timeout: master hangs in 60 of them (every case with the NaN in the first two rows); this branch returns in all 100,xSTEMRwithINFO = 21(48 cases) orINFO = 11(32 cases, whenxLARRE's own refinement hits the loop first), the drivers withINFO = 0and no eigenvalues.xSTEMRcalled directly in all four precisions on the 12 NaN positions in rows 1-2 (D(1),D(2),E(1);N = 3, 5, 8, 20) returnsINFO = 21in every case;CSTEMRandZSTEMRreturnedINFO = 22before the{c,z}larrv.fcheck.xSTEMR( 'N', 'A', ... )in all four precisions on five NaN positions (D(1),D(2),E(1),D(N),E(N-1);N = 3, 5, 8, 20), one process per case with a 10 s timeout: master hangs in 48 of the 80 cases; this branch returns in all 80, withINFO = 31in every case that hung (repro/probe8n.f90,run8n.sh).N = 1 ... 40; random, glued-Wilkinson clusters, scaled to 1e-150 and to 1e100, nearly diagonal with off-diagonals 1e-9) give bit-identical eigenvalues fromDSTEMRon master and on this branch, withJOBZ = 'V'and withJOBZ = 'N', so theMNWDTHfloor never changed a computed bracket in that sample.Found while auditing the tridiagonal bisection routines (the same NaN sweep that produced #1384);
xLARRBandxLARRJare the only routines in the symmetric eigenvalue path that hang rather than returning garbage for a NaN. ThexLARRJhalf was found afterwards, when preparing a NaN test for the suite that callsDSTEMRwithJOBZ = 'N'asdchkst.fdoes.