…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>
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.