Skip to content

Fix rescaling after a partial failure in the xSTEBZ-based drivers; accept Inf input and xSTEBZ's negative block numbers - #1384

Open
rmlarsen wants to merge 1 commit into
Reference-LAPACK:masterfrom
rmlarsen:symeig-driver-scaling
Open

rmlarsen wants to merge 1 commit into
Reference-LAPACK:masterfrom
rmlarsen:symeig-driver-scaling

Conversation

@rmlarsen

@rmlarsen rmlarsen commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Disclaimer: This PR was prepared using Claude Code.

Summary

The 32 symmetric eigensolver drivers that reduce to xSTEBZ + xSTEIN (xSTEVX, xSYEVX, xHEEVX, xSPEVX, xHPEVX, xSBEVX, xHBEVX, xSTEVR, xSYEVR, xHEEVR and the _2STAGE variants) scale a matrix whose largest entry lies outside [RMIN, RMAX] and undo the scaling at the end with

      IF( INFO.EQ.0 ) THEN
         IMAX = M
      ELSE
         IMAX = INFO - 1
      END IF
      CALL DSCAL( IMAX, ONE / SIGMA, W, 1 )

That block was copied from xSTEV/xSYEV, where a nonzero INFO comes from xSTEQR. In these drivers a nonzero INFO at that point means either that INFO eigenvectors failed to converge in xSTEIN (INFO <= N; the documentation says the eigenvalues are valid) or that xSTEBZ returned INFO - N. In the first case W(INFO:M) are returned still multiplied by SIGMA, that is wrong by the factor RMIN/|T| or RMAX/|T|; in the second IMAX = N + i - 1 > N and xSCAL writes past the end of W. Two related robustness defects sit in the same drivers: when the matrix contains an Inf, SIGMA = RMAX/ANRM = 0, so for RANGE = 'V' the scaled interval is VLL = VUU = 0 and xSTEBZ stops the process in XERBLA (parameter number 5 had an illegal value); and xSTEIN rejects with INFO = -6 (again XERBLA) or silently skips the negative block numbers with which xSTEBZ flags eigenvalues that did not converge, although every driver deliberately continues into xSTEIN when xSTEBZ returns INFO = 1. This PR rescales all M eigenvalues, leaves a matrix with an infinite norm unscaled, and makes xSTEIN accept xSTEBZ's negative block numbers. 32 driver files plus {s,d,c,z}stein.f.

Description

Rescaling. At the rescale label INFO can be nonzero only because of xSTEBZ or xSTEIN: a failure of xSTERF, xSTEQR or xSTEMR earlier in the driver resets INFO and falls through to xSTEBZ. Both leave M valid eigenvalues in W (xSTEBZ with INFO = 1 returns its best estimates for the unconverged ones, with INFO = 2, 3 the M it found, with INFO = 4 none), so the right count is M in every case and IMAX disappears. With finite input the wrong branch is reached whenever inverse iteration fails for at least one vector, which the drivers report as INFO = i <= N, "i eigenvectors failed to converge, their indices are stored in IFAIL", with no hint that eigenvalues i .. M are off by up to 1e146:

DSTEVX on T = 1e-150 * tridiag(1, 0.5, 1), ABSTOL = 0.3 |T| (so that inverse iteration fails):
BEFORE (master):
DSTEVX info =  6  ifail =  1 2 3 4 5 6
w * 1e150, computed:   9.904E-02  3.765E-01  7.775E-01  1.223E+00  1.623E+00  1.903E+04
            exact:   9.903E-02  3.765E-01  7.775E-01  1.223E+00  1.623E+00  1.901E+00
AFTER (this branch):
DSTEVX info =  6  ifail =  1 2 3 4 5 6
w * 1e150, computed:   9.904E-02  3.765E-01  7.775E-01  1.223E+00  1.623E+00  1.901E+00
            exact:   9.903E-02  3.765E-01  7.775E-01  1.223E+00  1.623E+00  1.901E+00

Infinite norm. SIGMA is now computed first and the scaling is applied only if it is positive, which fails exactly for ANRM = Inf. An infinite matrix then takes the same path as one containing a NaN (which has ANRM = NaN and is never scaled): no abort, INFO = 0 or N + 4 depending on RANGE, NaN in the output, nothing written beyond W(M).

DSYEVX(RANGE = 'V') on a 6x6 matrix with A(2,2) = Inf:
BEFORE (master):     ** On entry to DSTEBZ parameter number  5 had an illegal value   (process stops)
AFTER (this branch): DSYEVX(RANGE=V) on a matrix with an Inf entry: info =   0  m =  5

Negative block numbers. xSTEBZ documents IBLOCK(i) = -j for an eigenvalue of block j that did not converge, and the drivers rely on xSTEIN to accept that array (IF( IINFO.NE.1 ) GO TO 20 skips xSTEIN for every other xSTEBZ failure). xSTEIN now uses ABS( IBLOCK( . ) ) at its four uses and documents it; the drivers then return INFO = N + 1 together with eigenvectors, as intended, instead of stopping in XERBLA when a negative entry follows a positive one or returning an untouched Z when the last entry is negative.

Regression test. xERRST gets the second case: xSTEVX and xSYEVX with RANGE = 'V' on a tridiagonal matrix whose first diagonal entry is an infinity, which must return without reaching XERBLA. On the parent commit both report the illegal fifth argument of xSTEBZ, since the scale factor RMAX / ANRM is zero and both interval ends are scaled to zero; with the fix the matrix goes through unscaled and the drivers return.

The other two changes cannot be reached from the test suite. A partial xSTEIN failure needs a matrix whose norm is below the range the drivers document, and the negative block numbers need xSTEBZ to return INFO = 1, which finite in-range input cannot produce. The sweep below covers both.

Validation

  • The full LAPACK test suite passes on this branch: 5441901 LAPACK tests, 0 numerical errors, 0 other errors, the same totals as the parent commit (ctest: 100% of 215 tests passed).
  • A sweep of 1584 cases (8 routines: DSTEBZ+DSTEIN direct, DSTEVX, DSTEVR, DSYEVX, DSYEVR, ZHEEVX, DSPEVX, DSBEVX; NaN, +Inf, -Inf in each of five positions of D or E; n = 1, 2, 3, 5, 8; RANGE = 'A', 'V', 'I'), one process per case with XERBLA overridden to exit, canaries around every output and work array and a 10 s timeout. Master: 280 aborts (every driver family, Inf, RANGE = 'V'), 200 cases writing three entries past the end of W (RANGE = 'I', Inf, INFO = N + 4), 18 hangs. This branch: 0 aborts, 0 out-of-bounds writes, the same 18 hangs. The hangs are DSTEVR/DSYEVR with RANGE = 'A' and a NaN in the first two rows, inside DLARRB (its bracket-widening loops have no iteration bound); they are an MRRR issue independent of this PR; Bound the bracket widening in xLARRB and xLARRJ so that a NaN matrix cannot hang xSTEMR #1389 fixes them.
  • The direct pair with a matrix outside xSTEBZ's documented range (T * 2**512, where e(j)**2 overflows) aborted in DSTEIN on master and returns INFO = 1 from DSTEBZ on this branch.
  • For every finite matrix inside [RMIN, RMAX] the drivers do not scale and this PR changes nothing; for a scaled matrix with INFO = 0 the rescaling is the same xSCAL( M, ... ) as before.

@codecov

codecov Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.22642% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.36%. Comparing base (a6c6e74) to head (4f91c6d).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
TESTING/EIG/derrst.f 88.23% 4 Missing ⚠️
TESTING/EIG/serrst.f 88.23% 4 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #1384   +/-   ##
=======================================
  Coverage   69.36%   69.36%           
=======================================
  Files        6122     6122           
  Lines      486711   486715    +4     
  Branches    23268    23268           
=======================================
+ Hits       337584   337614   +30     
+ Misses     148689   148663   -26     
  Partials      438      438           
Components Coverage Δ
BLAS 97.94% <ø> (ø)
CBLAS 96.98% <ø> (ø)
LAPACK 82.40% <100.00%> (+0.01%) ⬆️
LAPACKE 2.17% <ø> (ø)
TMGLIB 55.69% <ø> (ø)
BLAS testing 88.33% <ø> (ø)
CBLAS testing 89.63% <ø> (ø)
LAPACK testing 82.21% <88.23%> (+<0.01%) ⬆️
LAPACKE testing ∅ <ø> (∅)
Files with missing lines Coverage Δ
SRC/chbevx.f 82.89% <100.00%> (+0.42%) ⬆️
SRC/chbevx_2stage.f 68.82% <100.00%> (+0.21%) ⬆️
SRC/cheevr.f 97.12% <100.00%> (+0.53%) ⬆️
SRC/cheevr_2stage.f 78.40% <100.00%> (+0.31%) ⬆️
SRC/cheevx.f 83.33% <100.00%> (+0.40%) ⬆️
SRC/cheevx_2stage.f 70.12% <100.00%> (+0.24%) ⬆️
SRC/chpevx.f 81.75% <100.00%> (+0.45%) ⬆️
SRC/cstein.f 93.49% <100.00%> (ø)
SRC/dsbevx.f 82.66% <100.00%> (+0.42%) ⬆️
SRC/dsbevx_2stage.f 67.85% <100.00%> (+0.21%) ⬆️
... and 28 more

... and 1 file with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a6c6e74...4f91c6d. Read the comment docs.

@rmlarsen
rmlarsen force-pushed the symeig-driver-scaling branch from c9bdaf6 to 63ed409 Compare September 8, 2026 04:27
@rmlarsen rmlarsen changed the title Rescale every eigenvalue after a partial failure in the xSTEBZ-based drivers, and survive an Inf or unconverged block numbers Fix rescaling after a partial failure in the xSTEBZ-based drivers; accept Inf input and xSTEBZ's negative block numbers Sep 8, 2026
@rmlarsen

rmlarsen commented Sep 10, 2026

Copy link
Copy Markdown
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.

…drivers

The 32 symmetric eigensolver drivers that reduce to xSTEBZ + xSTEIN
(xSTEVX, xSYEVX, xHEEVX, xSPEVX, xHPEVX, xSBEVX, xHBEVX, xSTEVR,
xSYEVR, xHEEVR and the _2STAGE variants) scale a matrix whose largest
entry lies outside [RMIN, RMAX] and undo the scaling of W at the end
with IMAX = M if INFO = 0 and IMAX = INFO - 1 otherwise.  That block
was copied from xSTEV and xSYEV, where a nonzero INFO comes from
xSTEQR.  Here a nonzero INFO means that INFO eigenvectors failed to
converge in xSTEIN, in which case the eigenvalues are all valid and
W(INFO:M) are returned still multiplied by SIGMA, or that xSTEBZ
returned INFO - N, in which case IMAX = N + i - 1 and xSCAL writes past
the end of W.  A failure of xSTERF, xSTEQR or xSTEMR earlier in the
driver resets INFO and falls through to xSTEBZ, so at the rescale label
M is the right count in every case.

Two related defects sit in the same drivers.  A matrix containing an
Inf has ANRM = Inf and SIGMA = RMAX / ANRM = 0, so for RANGE = 'V' the
scaled interval collapses to VLL = VUU = 0 and xSTEBZ stops the process
in XERBLA; compute SIGMA first and scale only if it is positive, which
sends an infinite matrix down the same path as one containing a NaN.
And xSTEIN rejects with INFO = -6, again XERBLA, or silently skips the
negative block numbers with which xSTEBZ flags eigenvalues that did not
converge, although every driver deliberately continues into xSTEIN
when xSTEBZ returns INFO = 1; take ABS( IBLOCK( . ) ) at its four uses
and document it.

DSTEVX on 1e-150 tridiag(1, 0.5, 1) with ABSTOL = 0.3 |T| reports
INFO = 6 and returns the sixth eigenvalue 1e4 times too large on the
parent, all six correctly on this branch.  xERRST gets the infinite matrix as a regression test: xSTEVX and
xSYEVX with RANGE = 'V' on a tridiagonal whose first diagonal entry is
an infinity, which must return without reaching XERBLA.  On the parent
both report the illegal fifth argument of xSTEBZ.  The other two
changes are not reachable from the test suite: a partial xSTEIN failure
needs a matrix outside the range the drivers document, and the negative
block numbers likewise.

Over 1584 NaN/Inf cases
(8 routines, 3 special values, 5 positions, 5 orders, 3 ranges, one
process each with XERBLA overridden and canaries around every array)
the parent aborts 280 times and writes past the end of W 200 times;
this branch never does either.  The full LAPACK test suite passes:
5441901 LAPACK tests, 0 numerical errors, 0 other errors, the same
totals as the parent.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@rmlarsen
rmlarsen force-pushed the symeig-driver-scaling branch from 63ed409 to 4f91c6d Compare September 15, 2026 20:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant