Skip to content

Commit fcc5958

Browse files
rmlarsenclaude
andcommitted
Bound the bracket widening in xLARRB and xLARRJ so that a NaN matrix 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>
1 parent a6c6e74 commit fcc5958

16 files changed

Lines changed: 366 additions & 46 deletions

File tree

SRC/clarrv.f

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,10 @@ SUBROUTINE CLARRV( N, VL, VU, D, L, PIVMIN,
702702
$ WERR(WBEGIN),WORK( INDWRK ),
703703
$ IWORK( IINDWK ), PIVMIN, SPDIAM,
704704
$ IN, IINFO )
705+
IF( IINFO.NE.0 ) THEN
706+
INFO = -1
707+
RETURN
708+
END IF
705709
55 CONTINUE
706710
*
707711
IF((WBEGIN+NEWLST-1.LT.DOL).OR.

SRC/cstemr.f

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -304,10 +304,11 @@
304304
*> = 0: successful exit
305305
*> < 0: if INFO = -i, the i-th argument had an illegal value
306306
*> > 0: if INFO = 1X, internal error in SLARRE,
307-
*> if INFO = 2X, internal error in CLARRV.
307+
*> if INFO = 2X, internal error in CLARRV,
308+
*> if INFO = 3X, internal error in SLARRJ.
308309
*> Here, the digit X = ABS( IINFO ) < 10, where IINFO is
309-
*> the nonzero error code returned by SLARRE or
310-
*> CLARRV, respectively.
310+
*> the nonzero error code returned by SLARRE, CLARRV
311+
*> or SLARRJ, respectively.
311312
*> \endverbatim
312313
*
313314
* Authors:
@@ -752,6 +753,10 @@ SUBROUTINE CSTEMR( JOBZ, RANGE, N, D, E, VL, VU, IL, IU,
752753
$ WORK( INDERR+WBEGIN-1 ),
753754
$ WORK( INDWRK ), IWORK( IINDWK ), PIVMIN,
754755
$ TNRM, IINFO )
756+
IF( IINFO.NE.0 ) THEN
757+
INFO = 30 + ABS( IINFO )
758+
RETURN
759+
END IF
755760
IBEGIN = IEND + 1
756761
WBEGIN = WEND + 1
757762
39 CONTINUE

SRC/dlarrb.f

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,11 @@
165165
*> \param[out] INFO
166166
*> \verbatim
167167
*> INFO is INTEGER
168-
*> Error flag.
168+
*> = 0: successful exit
169+
*> = 1: the interval around one of the eigenvalues could not
170+
*> be widened to contain it: the Sturm counts of the
171+
*> representation are inconsistent, as they are when it
172+
*> contains a NaN or an Inf.
169173
*> \endverbatim
170174
*
171175
* Authors:
@@ -274,10 +278,23 @@ SUBROUTINE DLARRB( N, D, LLD, IFIRST, ILAST, RTOL1,
274278
*
275279
* Do while( NEGCNT(LEFT).GT.I-1 )
276280
*
277-
BACK = WERR( II )
281+
* The interval is widened by BACK, doubled at every step, until
282+
* the Sturm count agrees with the index. In exact arithmetic
283+
* this terminates because the count is 0 below the spectrum and
284+
* N above it. A NaN or an Inf in the representation is never
285+
* counted, so the count can stay short of the index; stop once
286+
* BACK has overflowed, when no further step can move the
287+
* endpoint. BACK starts at no less than the minimum interval
288+
* width so that a zero WERR still makes progress.
289+
*
290+
BACK = MAX( WERR( II ), MNWDTH )
278291
20 CONTINUE
279292
NEGCNT = DLANEG( N, D, LLD, LEFT, PIVMIN, R )
280293
IF( NEGCNT.GT.I-1 ) THEN
294+
IF( .NOT.( BACK.LT.TWO*BACK ) ) THEN
295+
INFO = 1
296+
RETURN
297+
END IF
281298
LEFT = LEFT - BACK
282299
BACK = TWO*BACK
283300
GO TO 20
@@ -286,15 +303,18 @@ SUBROUTINE DLARRB( N, D, LLD, IFIRST, ILAST, RTOL1,
286303
* Do while( NEGCNT(RIGHT).LT.I )
287304
* Compute negcount from dstqds facto L+D+L+^T = L D L^T - RIGHT
288305
*
289-
BACK = WERR( II )
306+
BACK = MAX( WERR( II ), MNWDTH )
290307
50 CONTINUE
291-
292308
NEGCNT = DLANEG( N, D, LLD, RIGHT, PIVMIN, R )
293-
IF( NEGCNT.LT.I ) THEN
294-
RIGHT = RIGHT + BACK
295-
BACK = TWO*BACK
296-
GO TO 50
297-
END IF
309+
IF( NEGCNT.LT.I ) THEN
310+
IF( .NOT.( BACK.LT.TWO*BACK ) ) THEN
311+
INFO = 1
312+
RETURN
313+
END IF
314+
RIGHT = RIGHT + BACK
315+
BACK = TWO*BACK
316+
GO TO 50
317+
END IF
298318
WIDTH = HALF*ABS( LEFT - RIGHT )
299319
TMP = MAX( ABS( LEFT ), ABS( RIGHT ) )
300320
CVRGD = MAX(RTOL1*GAP,RTOL2*TMP)

SRC/dlarrj.f

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,11 @@
137137
*> \param[out] INFO
138138
*> \verbatim
139139
*> INFO is INTEGER
140-
*> Error flag.
140+
*> = 0: successful exit
141+
*> = 1: the interval around one of the eigenvalues could not
142+
*> be widened to contain it: the Sturm counts of the
143+
*> matrix are inconsistent, as they are when it contains
144+
*> a NaN or an Inf.
141145
*> \endverbatim
142146
*
143147
* Authors:
@@ -190,7 +194,7 @@ SUBROUTINE DLARRJ( N, D, E2, IFIRST, ILAST,
190194
* .. Local Scalars ..
191195
INTEGER CNT, I, I1, I2, II, ITER, J, K, NEXT, NINT,
192196
$ OLNINT, P, PREV, SAVI1
193-
DOUBLE PRECISION DPLUS, FAC, LEFT, MID, RIGHT, S, TMP, WIDTH
197+
DOUBLE PRECISION BACK, DPLUS, LEFT, MID, RIGHT, S, TMP, WIDTH
194198
*
195199
* ..
196200
* .. Intrinsic Functions ..
@@ -249,7 +253,16 @@ SUBROUTINE DLARRJ( N, D, E2, IFIRST, ILAST,
249253
*
250254
* Do while( CNT(LEFT).GT.I-1 )
251255
*
252-
FAC = ONE
256+
* The interval is widened by BACK, doubled at every step,
257+
* until the Sturm count agrees with the index. In exact
258+
* arithmetic this terminates because the count is 0 below the
259+
* spectrum and N above it. A NaN or an Inf in the matrix is
260+
* never counted, so the count can stay short of the index;
261+
* stop once BACK has overflowed, when no further step can move
262+
* the endpoint. BACK starts at no less than the minimum
263+
* interval width so that a zero WERR still makes progress.
264+
*
265+
BACK = MAX( WERR( II ), TWO*PIVMIN )
253266
20 CONTINUE
254267
CNT = 0
255268
S = LEFT
@@ -260,14 +273,18 @@ SUBROUTINE DLARRJ( N, D, E2, IFIRST, ILAST,
260273
IF( DPLUS.LT.ZERO ) CNT = CNT + 1
261274
30 CONTINUE
262275
IF( CNT.GT.I-1 ) THEN
263-
LEFT = LEFT - WERR( II )*FAC
264-
FAC = TWO*FAC
276+
IF( .NOT.( BACK.LT.TWO*BACK ) ) THEN
277+
INFO = 1
278+
RETURN
279+
END IF
280+
LEFT = LEFT - BACK
281+
BACK = TWO*BACK
265282
GO TO 20
266283
END IF
267284
*
268285
* Do while( CNT(RIGHT).LT.I )
269286
*
270-
FAC = ONE
287+
BACK = MAX( WERR( II ), TWO*PIVMIN )
271288
50 CONTINUE
272289
CNT = 0
273290
S = RIGHT
@@ -278,8 +295,12 @@ SUBROUTINE DLARRJ( N, D, E2, IFIRST, ILAST,
278295
IF( DPLUS.LT.ZERO ) CNT = CNT + 1
279296
60 CONTINUE
280297
IF( CNT.LT.I ) THEN
281-
RIGHT = RIGHT + WERR( II )*FAC
282-
FAC = TWO*FAC
298+
IF( .NOT.( BACK.LT.TWO*BACK ) ) THEN
299+
INFO = 1
300+
RETURN
301+
END IF
302+
RIGHT = RIGHT + BACK
303+
BACK = TWO*BACK
283304
GO TO 50
284305
END IF
285306
NINT = NINT + 1

SRC/dlarrv.f

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,10 @@ SUBROUTINE DLARRV( N, VL, VU, D, L, PIVMIN,
698698
$ WERR(WBEGIN),WORK( INDWRK ),
699699
$ IWORK( IINDWK ), PIVMIN, SPDIAM,
700700
$ IN, IINFO )
701+
IF( IINFO.NE.0 ) THEN
702+
INFO = -1
703+
RETURN
704+
END IF
701705
55 CONTINUE
702706
*
703707
IF((WBEGIN+NEWLST-1.LT.DOL).OR.

SRC/dstemr.f

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,10 +287,11 @@
287287
*> = 0: successful exit
288288
*> < 0: if INFO = -i, the i-th argument had an illegal value
289289
*> > 0: if INFO = 1X, internal error in DLARRE,
290-
*> if INFO = 2X, internal error in DLARRV.
290+
*> if INFO = 2X, internal error in DLARRV,
291+
*> if INFO = 3X, internal error in DLARRJ.
291292
*> Here, the digit X = ABS( IINFO ) < 10, where IINFO is
292-
*> the nonzero error code returned by DLARRE or
293-
*> DLARRV, respectively.
293+
*> the nonzero error code returned by DLARRE, DLARRV
294+
*> or DLARRJ, respectively.
294295
*> \endverbatim
295296
*
296297
* Authors:
@@ -735,6 +736,10 @@ SUBROUTINE DSTEMR( JOBZ, RANGE, N, D, E, VL, VU, IL, IU,
735736
$ WORK( INDERR+WBEGIN-1 ),
736737
$ WORK( INDWRK ), IWORK( IINDWK ), PIVMIN,
737738
$ TNRM, IINFO )
739+
IF( IINFO.NE.0 ) THEN
740+
INFO = 30 + ABS( IINFO )
741+
RETURN
742+
END IF
738743
IBEGIN = IEND + 1
739744
WBEGIN = WEND + 1
740745
39 CONTINUE

SRC/slarrb.f

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,11 @@
165165
*> \param[out] INFO
166166
*> \verbatim
167167
*> INFO is INTEGER
168-
*> Error flag.
168+
*> = 0: successful exit
169+
*> = 1: the interval around one of the eigenvalues could not
170+
*> be widened to contain it: the Sturm counts of the
171+
*> representation are inconsistent, as they are when it
172+
*> contains a NaN or an Inf.
169173
*> \endverbatim
170174
*
171175
* Authors:
@@ -274,10 +278,23 @@ SUBROUTINE SLARRB( N, D, LLD, IFIRST, ILAST, RTOL1,
274278
*
275279
* Do while( NEGCNT(LEFT).GT.I-1 )
276280
*
277-
BACK = WERR( II )
281+
* The interval is widened by BACK, doubled at every step, until
282+
* the Sturm count agrees with the index. In exact arithmetic
283+
* this terminates because the count is 0 below the spectrum and
284+
* N above it. A NaN or an Inf in the representation is never
285+
* counted, so the count can stay short of the index; stop once
286+
* BACK has overflowed, when no further step can move the
287+
* endpoint. BACK starts at no less than the minimum interval
288+
* width so that a zero WERR still makes progress.
289+
*
290+
BACK = MAX( WERR( II ), MNWDTH )
278291
20 CONTINUE
279292
NEGCNT = SLANEG( N, D, LLD, LEFT, PIVMIN, R )
280293
IF( NEGCNT.GT.I-1 ) THEN
294+
IF( .NOT.( BACK.LT.TWO*BACK ) ) THEN
295+
INFO = 1
296+
RETURN
297+
END IF
281298
LEFT = LEFT - BACK
282299
BACK = TWO*BACK
283300
GO TO 20
@@ -286,15 +303,18 @@ SUBROUTINE SLARRB( N, D, LLD, IFIRST, ILAST, RTOL1,
286303
* Do while( NEGCNT(RIGHT).LT.I )
287304
* Compute negcount from dstqds facto L+D+L+^T = L D L^T - RIGHT
288305
*
289-
BACK = WERR( II )
306+
BACK = MAX( WERR( II ), MNWDTH )
290307
50 CONTINUE
291-
292308
NEGCNT = SLANEG( N, D, LLD, RIGHT, PIVMIN, R )
293-
IF( NEGCNT.LT.I ) THEN
294-
RIGHT = RIGHT + BACK
295-
BACK = TWO*BACK
296-
GO TO 50
297-
END IF
309+
IF( NEGCNT.LT.I ) THEN
310+
IF( .NOT.( BACK.LT.TWO*BACK ) ) THEN
311+
INFO = 1
312+
RETURN
313+
END IF
314+
RIGHT = RIGHT + BACK
315+
BACK = TWO*BACK
316+
GO TO 50
317+
END IF
298318
WIDTH = HALF*ABS( LEFT - RIGHT )
299319
TMP = MAX( ABS( LEFT ), ABS( RIGHT ) )
300320
CVRGD = MAX(RTOL1*GAP,RTOL2*TMP)

SRC/slarrj.f

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,11 @@
137137
*> \param[out] INFO
138138
*> \verbatim
139139
*> INFO is INTEGER
140-
*> Error flag.
140+
*> = 0: successful exit
141+
*> = 1: the interval around one of the eigenvalues could not
142+
*> be widened to contain it: the Sturm counts of the
143+
*> matrix are inconsistent, as they are when it contains
144+
*> a NaN or an Inf.
141145
*> \endverbatim
142146
*
143147
* Authors:
@@ -190,7 +194,7 @@ SUBROUTINE SLARRJ( N, D, E2, IFIRST, ILAST,
190194
* .. Local Scalars ..
191195
INTEGER CNT, I, I1, I2, II, ITER, J, K, NEXT, NINT,
192196
$ OLNINT, P, PREV, SAVI1
193-
REAL DPLUS, FAC, LEFT, MID, RIGHT, S, TMP, WIDTH
197+
REAL BACK, DPLUS, LEFT, MID, RIGHT, S, TMP, WIDTH
194198
*
195199
* ..
196200
* .. Intrinsic Functions ..
@@ -249,7 +253,16 @@ SUBROUTINE SLARRJ( N, D, E2, IFIRST, ILAST,
249253
*
250254
* Do while( CNT(LEFT).GT.I-1 )
251255
*
252-
FAC = ONE
256+
* The interval is widened by BACK, doubled at every step,
257+
* until the Sturm count agrees with the index. In exact
258+
* arithmetic this terminates because the count is 0 below the
259+
* spectrum and N above it. A NaN or an Inf in the matrix is
260+
* never counted, so the count can stay short of the index;
261+
* stop once BACK has overflowed, when no further step can move
262+
* the endpoint. BACK starts at no less than the minimum
263+
* interval width so that a zero WERR still makes progress.
264+
*
265+
BACK = MAX( WERR( II ), TWO*PIVMIN )
253266
20 CONTINUE
254267
CNT = 0
255268
S = LEFT
@@ -260,14 +273,18 @@ SUBROUTINE SLARRJ( N, D, E2, IFIRST, ILAST,
260273
IF( DPLUS.LT.ZERO ) CNT = CNT + 1
261274
30 CONTINUE
262275
IF( CNT.GT.I-1 ) THEN
263-
LEFT = LEFT - WERR( II )*FAC
264-
FAC = TWO*FAC
276+
IF( .NOT.( BACK.LT.TWO*BACK ) ) THEN
277+
INFO = 1
278+
RETURN
279+
END IF
280+
LEFT = LEFT - BACK
281+
BACK = TWO*BACK
265282
GO TO 20
266283
END IF
267284
*
268285
* Do while( CNT(RIGHT).LT.I )
269286
*
270-
FAC = ONE
287+
BACK = MAX( WERR( II ), TWO*PIVMIN )
271288
50 CONTINUE
272289
CNT = 0
273290
S = RIGHT
@@ -278,8 +295,12 @@ SUBROUTINE SLARRJ( N, D, E2, IFIRST, ILAST,
278295
IF( DPLUS.LT.ZERO ) CNT = CNT + 1
279296
60 CONTINUE
280297
IF( CNT.LT.I ) THEN
281-
RIGHT = RIGHT + WERR( II )*FAC
282-
FAC = TWO*FAC
298+
IF( .NOT.( BACK.LT.TWO*BACK ) ) THEN
299+
INFO = 1
300+
RETURN
301+
END IF
302+
RIGHT = RIGHT + BACK
303+
BACK = TWO*BACK
283304
GO TO 50
284305
END IF
285306
NINT = NINT + 1

SRC/slarrv.f

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,10 @@ SUBROUTINE SLARRV( N, VL, VU, D, L, PIVMIN,
698698
$ WERR(WBEGIN),WORK( INDWRK ),
699699
$ IWORK( IINDWK ), PIVMIN, SPDIAM,
700700
$ IN, IINFO )
701+
IF( IINFO.NE.0 ) THEN
702+
INFO = -1
703+
RETURN
704+
END IF
701705
55 CONTINUE
702706
*
703707
IF((WBEGIN+NEWLST-1.LT.DOL).OR.

SRC/sstemr.f

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,10 +287,11 @@
287287
*> = 0: successful exit
288288
*> < 0: if INFO = -i, the i-th argument had an illegal value
289289
*> > 0: if INFO = 1X, internal error in SLARRE,
290-
*> if INFO = 2X, internal error in SLARRV.
290+
*> if INFO = 2X, internal error in SLARRV,
291+
*> if INFO = 3X, internal error in SLARRJ.
291292
*> Here, the digit X = ABS( IINFO ) < 10, where IINFO is
292-
*> the nonzero error code returned by SLARRE or
293-
*> SLARRV, respectively.
293+
*> the nonzero error code returned by SLARRE, SLARRV
294+
*> or SLARRJ, respectively.
294295
*> \endverbatim
295296
*
296297
* Authors:
@@ -732,6 +733,10 @@ SUBROUTINE SSTEMR( JOBZ, RANGE, N, D, E, VL, VU, IL, IU,
732733
$ WORK( INDERR+WBEGIN-1 ),
733734
$ WORK( INDWRK ), IWORK( IINDWK ), PIVMIN,
734735
$ TNRM, IINFO )
736+
IF( IINFO.NE.0 ) THEN
737+
INFO = 30 + ABS( IINFO )
738+
RETURN
739+
END IF
735740
IBEGIN = IEND + 1
736741
WBEGIN = WEND + 1
737742
39 CONTINUE

0 commit comments

Comments
 (0)