Files
lapack/SRC/zhbgvx.f
T
NAKATA Maho 94f05be6c3 Fix incorrect INFO return codes from xSTEBZ in driver routines
In 26 driver routines across all four precisions (S, D, C, Z),
xSTEBZ was called writing directly to INFO, which was then
silently overwritten by xSTEIN.  Two bugs are fixed:

1. xSTEBZ errors were not propagated to the caller at all.

2. When xSTEBZ returned INFO > 0, eigenvectors were computed
   with an incomplete or empty eigenvalue set (INFO=2,3,4) or
   with reduced accuracy (INFO=1), producing silently wrong
   results.

Fix: route xSTEBZ return value through IINFO.  For INFO != 1,
skip xSTEIN entirely via GO TO.  For INFO == 1 (bisection
convergence warning), xSTEIN is still called since M eigenvalues
are available; the warning is preserved in INFO.

The xSTEBZ error is encoded as INFO = N + IINFO to distinguish
it from xSTEIN failures (1..N), matching the convention already
used for xPBSTF errors in the xBGVX routines.  For the four
xBGVX routines, which already use INFO = N+i for xPBSTF, the
xSTEBZ error is encoded as INFO = 2*N + IINFO.

Update INFO documentation in all 26 files accordingly.
Also add IINFO to the INTEGER declaration in dstevx.f and
sstevx.f where it was missing.

Files modified (26):
  SRC/chbevx.f, SRC/chbevx_2stage.f, SRC/chbgvx.f,
  SRC/cheevx.f, SRC/cheevx_2stage.f, SRC/chpevx.f,
  SRC/dsbevx.f, SRC/dsbevx_2stage.f, SRC/dsbgvx.f,
  SRC/dspevx.f, SRC/dstevx.f, SRC/dsyevx.f, SRC/dsyevx_2stage.f,
  SRC/ssbevx.f, SRC/ssbevx_2stage.f, SRC/ssbgvx.f,
  SRC/sspevx.f, SRC/sstevx.f, SRC/ssyevx.f, SRC/ssyevx_2stage.f,
  SRC/zhbevx.f, SRC/zhbevx_2stage.f, SRC/zhbgvx.f,
  SRC/zheevx.f, SRC/zheevx_2stage.f, SRC/zhpevx.f
2026-03-23 13:58:37 +09:00

541 lines
17 KiB
FortranFixed

*> \brief \b ZHBGVX
*
* =========== DOCUMENTATION ===========
*
* Online html documentation available at
* http://www.netlib.org/lapack/explore-html/
*
*> Download ZHBGVX + dependencies
*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/zhbgvx.f">
*> [TGZ]</a>
*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/zhbgvx.f">
*> [ZIP]</a>
*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/zhbgvx.f">
*> [TXT]</a>
*
* Definition:
* ===========
*
* SUBROUTINE ZHBGVX( JOBZ, RANGE, UPLO, N, KA, KB, AB, LDAB, BB,
* LDBB, Q, LDQ, VL, VU, IL, IU, ABSTOL, M, W, Z,
* LDZ, WORK, RWORK, IWORK, IFAIL, INFO )
*
* .. Scalar Arguments ..
* CHARACTER JOBZ, RANGE, UPLO
* INTEGER IL, INFO, IU, KA, KB, LDAB, LDBB, LDQ, LDZ, M,
* $ N
* DOUBLE PRECISION ABSTOL, VL, VU
* ..
* .. Array Arguments ..
* INTEGER IFAIL( * ), IWORK( * )
* DOUBLE PRECISION RWORK( * ), W( * )
* COMPLEX*16 AB( LDAB, * ), BB( LDBB, * ), Q( LDQ, * ),
* $ WORK( * ), Z( LDZ, * )
* ..
*
*
*> \par Purpose:
* =============
*>
*> \verbatim
*>
*> ZHBGVX computes all the eigenvalues, and optionally, the eigenvectors
*> of a complex generalized Hermitian-definite banded eigenproblem, of
*> the form A*x=(lambda)*B*x. Here A and B are assumed to be Hermitian
*> and banded, and B is also positive definite. Eigenvalues and
*> eigenvectors can be selected by specifying either all eigenvalues,
*> a range of values or a range of indices for the desired eigenvalues.
*> \endverbatim
*
* Arguments:
* ==========
*
*> \param[in] JOBZ
*> \verbatim
*> JOBZ is CHARACTER*1
*> = 'N': Compute eigenvalues only;
*> = 'V': Compute eigenvalues and eigenvectors.
*> \endverbatim
*>
*> \param[in] RANGE
*> \verbatim
*> RANGE is CHARACTER*1
*> = 'A': all eigenvalues will be found;
*> = 'V': all eigenvalues in the half-open interval (VL,VU]
*> will be found;
*> = 'I': the IL-th through IU-th eigenvalues will be found.
*> \endverbatim
*>
*> \param[in] UPLO
*> \verbatim
*> UPLO is CHARACTER*1
*> = 'U': Upper triangles of A and B are stored;
*> = 'L': Lower triangles of A and B are stored.
*> \endverbatim
*>
*> \param[in] N
*> \verbatim
*> N is INTEGER
*> The order of the matrices A and B. N >= 0.
*> \endverbatim
*>
*> \param[in] KA
*> \verbatim
*> KA is INTEGER
*> The number of superdiagonals of the matrix A if UPLO = 'U',
*> or the number of subdiagonals if UPLO = 'L'. KA >= 0.
*> \endverbatim
*>
*> \param[in] KB
*> \verbatim
*> KB is INTEGER
*> The number of superdiagonals of the matrix B if UPLO = 'U',
*> or the number of subdiagonals if UPLO = 'L'. KB >= 0.
*> \endverbatim
*>
*> \param[in,out] AB
*> \verbatim
*> AB is COMPLEX*16 array, dimension (LDAB, N)
*> On entry, the upper or lower triangle of the Hermitian band
*> matrix A, stored in the first ka+1 rows of the array. The
*> j-th column of A is stored in the j-th column of the array AB
*> as follows:
*> if UPLO = 'U', AB(ka+1+i-j,j) = A(i,j) for max(1,j-ka)<=i<=j;
*> if UPLO = 'L', AB(1+i-j,j) = A(i,j) for j<=i<=min(n,j+ka).
*>
*> On exit, the contents of AB are destroyed.
*> \endverbatim
*>
*> \param[in] LDAB
*> \verbatim
*> LDAB is INTEGER
*> The leading dimension of the array AB. LDAB >= KA+1.
*> \endverbatim
*>
*> \param[in,out] BB
*> \verbatim
*> BB is COMPLEX*16 array, dimension (LDBB, N)
*> On entry, the upper or lower triangle of the Hermitian band
*> matrix B, stored in the first kb+1 rows of the array. The
*> j-th column of B is stored in the j-th column of the array BB
*> as follows:
*> if UPLO = 'U', BB(kb+1+i-j,j) = B(i,j) for max(1,j-kb)<=i<=j;
*> if UPLO = 'L', BB(1+i-j,j) = B(i,j) for j<=i<=min(n,j+kb).
*>
*> On exit, the factor S from the split Cholesky factorization
*> B = S**H*S, as returned by ZPBSTF.
*> \endverbatim
*>
*> \param[in] LDBB
*> \verbatim
*> LDBB is INTEGER
*> The leading dimension of the array BB. LDBB >= KB+1.
*> \endverbatim
*>
*> \param[out] Q
*> \verbatim
*> Q is COMPLEX*16 array, dimension (LDQ, N)
*> If JOBZ = 'V', the n-by-n matrix used in the reduction of
*> A*x = (lambda)*B*x to standard form, i.e. C*x = (lambda)*x,
*> and consequently C to tridiagonal form.
*> If JOBZ = 'N', the array Q is not referenced.
*> \endverbatim
*>
*> \param[in] LDQ
*> \verbatim
*> LDQ is INTEGER
*> The leading dimension of the array Q. If JOBZ = 'N',
*> LDQ >= 1. If JOBZ = 'V', LDQ >= max(1,N).
*> \endverbatim
*>
*> \param[in] VL
*> \verbatim
*> VL is DOUBLE PRECISION
*>
*> If RANGE='V', the lower bound of the interval to
*> be searched for eigenvalues. VL < VU.
*> Not referenced if RANGE = 'A' or 'I'.
*> \endverbatim
*>
*> \param[in] VU
*> \verbatim
*> VU is DOUBLE PRECISION
*>
*> If RANGE='V', the upper bound of the interval to
*> be searched for eigenvalues. VL < VU.
*> Not referenced if RANGE = 'A' or 'I'.
*> \endverbatim
*>
*> \param[in] IL
*> \verbatim
*> IL is INTEGER
*>
*> If RANGE='I', the index of the
*> smallest eigenvalue to be returned.
*> 1 <= IL <= IU <= N, if N > 0; IL = 1 and IU = 0 if N = 0.
*> Not referenced if RANGE = 'A' or 'V'.
*> \endverbatim
*>
*> \param[in] IU
*> \verbatim
*> IU is INTEGER
*>
*> If RANGE='I', the index of the
*> largest eigenvalue to be returned.
*> 1 <= IL <= IU <= N, if N > 0; IL = 1 and IU = 0 if N = 0.
*> Not referenced if RANGE = 'A' or 'V'.
*> \endverbatim
*>
*> \param[in] ABSTOL
*> \verbatim
*> ABSTOL is DOUBLE PRECISION
*> The absolute error tolerance for the eigenvalues.
*> An approximate eigenvalue is accepted as converged
*> when it is determined to lie in an interval [a,b]
*> of width less than or equal to
*>
*> ABSTOL + EPS * max( |a|,|b| ) ,
*>
*> where EPS is the machine precision. If ABSTOL is less than
*> or equal to zero, then EPS*|T| will be used in its place,
*> where |T| is the 1-norm of the tridiagonal matrix obtained
*> by reducing AP to tridiagonal form.
*>
*> Eigenvalues will be computed most accurately when ABSTOL is
*> set to twice the underflow threshold 2*DLAMCH('S'), not zero.
*> If this routine returns with INFO>0, indicating that some
*> eigenvectors did not converge, try setting ABSTOL to
*> 2*DLAMCH('S').
*> \endverbatim
*>
*> \param[out] M
*> \verbatim
*> M is INTEGER
*> The total number of eigenvalues found. 0 <= M <= N.
*> If RANGE = 'A', M = N, and if RANGE = 'I', M = IU-IL+1.
*> \endverbatim
*>
*> \param[out] W
*> \verbatim
*> W is DOUBLE PRECISION array, dimension (N)
*> If INFO = 0, the eigenvalues in ascending order.
*> \endverbatim
*>
*> \param[out] Z
*> \verbatim
*> Z is COMPLEX*16 array, dimension (LDZ, N)
*> If JOBZ = 'V', then if INFO = 0, Z contains the matrix Z of
*> eigenvectors, with the i-th column of Z holding the
*> eigenvector associated with W(i). The eigenvectors are
*> normalized so that Z**H*B*Z = I.
*> If JOBZ = 'N', then Z is not referenced.
*> \endverbatim
*>
*> \param[in] LDZ
*> \verbatim
*> LDZ is INTEGER
*> The leading dimension of the array Z. LDZ >= 1, and if
*> JOBZ = 'V', LDZ >= N.
*> \endverbatim
*>
*> \param[out] WORK
*> \verbatim
*> WORK is COMPLEX*16 array, dimension (N)
*> \endverbatim
*>
*> \param[out] RWORK
*> \verbatim
*> RWORK is DOUBLE PRECISION array, dimension (7*N)
*> \endverbatim
*>
*> \param[out] IWORK
*> \verbatim
*> IWORK is INTEGER array, dimension (5*N)
*> \endverbatim
*>
*> \param[out] IFAIL
*> \verbatim
*> IFAIL is INTEGER array, dimension (N)
*> If JOBZ = 'V', then if INFO = 0, the first M elements of
*> IFAIL are zero. If INFO > 0, then IFAIL contains the
*> indices of the eigenvectors that failed to converge.
*> If JOBZ = 'N', then IFAIL is not referenced.
*> \endverbatim
*>
*> \param[out] INFO
*> \verbatim
*> INFO is INTEGER
*> = 0: successful exit
*> < 0: if INFO = -i, the i-th argument had an illegal value
*> > 0: if INFO = i, and i is:
*> <= N: then i eigenvectors failed to converge in
*> ZSTEIN; their indices are stored in IFAIL.
*> N+1..2N: if INFO = N + i, for 1 <= i <= N, then ZPBSTF
*> returned INFO = i: B is not positive definite.
*> The factorization of B could not be completed
*> and no eigenvalues or eigenvectors were computed.
*> > 2N: if INFO = 2*N + i, then DSTEBZ returned
*> INFO = i; see DSTEBZ for details.
*> \endverbatim
*
* Authors:
* ========
*
*> \author Univ. of Tennessee
*> \author Univ. of California Berkeley
*> \author Univ. of Colorado Denver
*> \author NAG Ltd.
*
*> \ingroup hbgvx
*
*> \par Contributors:
* ==================
*>
*> Mark Fahey, Department of Mathematics, Univ. of Kentucky, USA
*
* =====================================================================
SUBROUTINE ZHBGVX( JOBZ, RANGE, UPLO, N, KA, KB, AB, LDAB, BB,
$ LDBB, Q, LDQ, VL, VU, IL, IU, ABSTOL, M, W, Z,
$ LDZ, WORK, RWORK, IWORK, IFAIL, INFO )
IMPLICIT NONE
*
* -- LAPACK driver routine --
* -- LAPACK is a software package provided by Univ. of Tennessee, --
* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--
*
* .. Scalar Arguments ..
CHARACTER JOBZ, RANGE, UPLO
INTEGER IL, INFO, IU, KA, KB, LDAB, LDBB, LDQ, LDZ, M,
$ N
DOUBLE PRECISION ABSTOL, VL, VU
* ..
* .. Array Arguments ..
INTEGER IFAIL( * ), IWORK( * )
DOUBLE PRECISION RWORK( * ), W( * )
COMPLEX*16 AB( LDAB, * ), BB( LDBB, * ), Q( LDQ, * ),
$ WORK( * ), Z( LDZ, * )
* ..
*
* =====================================================================
*
* .. Parameters ..
DOUBLE PRECISION ZERO
PARAMETER ( ZERO = 0.0D+0 )
COMPLEX*16 CZERO, CONE
PARAMETER ( CZERO = ( 0.0D+0, 0.0D+0 ),
$ CONE = ( 1.0D+0, 0.0D+0 ) )
* ..
* .. Local Scalars ..
LOGICAL ALLEIG, INDEIG, TEST, UPPER, VALEIG, WANTZ
CHARACTER ORDER, VECT
INTEGER I, IINFO, INDD, INDE, INDEE, INDISP,
$ INDIWK, INDRWK, INDWRK, ITMP1, J, JJ, NSPLIT
DOUBLE PRECISION TMP1
* ..
* .. External Functions ..
LOGICAL LSAME
EXTERNAL LSAME
* ..
* .. External Subroutines ..
EXTERNAL DCOPY, DSTEBZ, DSTERF, XERBLA, ZCOPY,
$ ZGEMV,
$ ZHBGST, ZHBTRD, ZLACPY, ZPBSTF, ZSTEIN, ZSTEQR,
$ ZSWAP
* ..
* .. Intrinsic Functions ..
INTRINSIC MIN
* ..
* .. Executable Statements ..
*
* Test the input parameters.
*
WANTZ = LSAME( JOBZ, 'V' )
UPPER = LSAME( UPLO, 'U' )
ALLEIG = LSAME( RANGE, 'A' )
VALEIG = LSAME( RANGE, 'V' )
INDEIG = LSAME( RANGE, 'I' )
*
INFO = 0
IF( .NOT.( WANTZ .OR. LSAME( JOBZ, 'N' ) ) ) THEN
INFO = -1
ELSE IF( .NOT.( ALLEIG .OR. VALEIG .OR. INDEIG ) ) THEN
INFO = -2
ELSE IF( .NOT.( UPPER .OR. LSAME( UPLO, 'L' ) ) ) THEN
INFO = -3
ELSE IF( N.LT.0 ) THEN
INFO = -4
ELSE IF( KA.LT.0 ) THEN
INFO = -5
ELSE IF( KB.LT.0 .OR. KB.GT.KA ) THEN
INFO = -6
ELSE IF( LDAB.LT.KA+1 ) THEN
INFO = -8
ELSE IF( LDBB.LT.KB+1 ) THEN
INFO = -10
ELSE IF( LDQ.LT.1 .OR. ( WANTZ .AND. LDQ.LT.N ) ) THEN
INFO = -12
ELSE
IF( VALEIG ) THEN
IF( N.GT.0 .AND. VU.LE.VL )
$ INFO = -14
ELSE IF( INDEIG ) THEN
IF( IL.LT.1 .OR. IL.GT.MAX( 1, N ) ) THEN
INFO = -15
ELSE IF ( IU.LT.MIN( N, IL ) .OR. IU.GT.N ) THEN
INFO = -16
END IF
END IF
END IF
IF( INFO.EQ.0) THEN
IF( LDZ.LT.1 .OR. ( WANTZ .AND. LDZ.LT.N ) ) THEN
INFO = -21
END IF
END IF
*
IF( INFO.NE.0 ) THEN
CALL XERBLA( 'ZHBGVX', -INFO )
RETURN
END IF
*
* Quick return if possible
*
M = 0
IF( N.EQ.0 )
$ RETURN
*
* Form a split Cholesky factorization of B.
*
CALL ZPBSTF( UPLO, N, KB, BB, LDBB, INFO )
IF( INFO.NE.0 ) THEN
INFO = N + INFO
RETURN
END IF
*
* Transform problem to standard eigenvalue problem.
*
CALL ZHBGST( JOBZ, UPLO, N, KA, KB, AB, LDAB, BB, LDBB, Q, LDQ,
$ WORK, RWORK, IINFO )
*
* Solve the standard eigenvalue problem.
* Reduce Hermitian band matrix to tridiagonal form.
*
INDD = 1
INDE = INDD + N
INDRWK = INDE + N
INDWRK = 1
IF( WANTZ ) THEN
VECT = 'U'
ELSE
VECT = 'N'
END IF
CALL ZHBTRD( VECT, UPLO, N, KA, AB, LDAB, RWORK( INDD ),
$ RWORK( INDE ), Q, LDQ, WORK( INDWRK ), IINFO )
*
* If all eigenvalues are desired and ABSTOL is less than or equal
* to zero, then call DSTERF or ZSTEQR. If this fails for some
* eigenvalue, then try DSTEBZ.
*
TEST = .FALSE.
IF( INDEIG ) THEN
IF( IL.EQ.1 .AND. IU.EQ.N ) THEN
TEST = .TRUE.
END IF
END IF
IF( ( ALLEIG .OR. TEST ) .AND. ( ABSTOL.LE.ZERO ) ) THEN
CALL DCOPY( N, RWORK( INDD ), 1, W, 1 )
INDEE = INDRWK + 2*N
CALL DCOPY( N-1, RWORK( INDE ), 1, RWORK( INDEE ), 1 )
IF( .NOT.WANTZ ) THEN
CALL DSTERF( N, W, RWORK( INDEE ), INFO )
ELSE
CALL ZLACPY( 'A', N, N, Q, LDQ, Z, LDZ )
CALL ZSTEQR( JOBZ, N, W, RWORK( INDEE ), Z, LDZ,
$ RWORK( INDRWK ), INFO )
IF( INFO.EQ.0 ) THEN
DO 10 I = 1, N
IFAIL( I ) = 0
10 CONTINUE
END IF
END IF
IF( INFO.EQ.0 ) THEN
M = N
GO TO 30
END IF
INFO = 0
END IF
*
* Otherwise, call DSTEBZ and, if eigenvectors are desired,
* call ZSTEIN.
*
IF( WANTZ ) THEN
ORDER = 'B'
ELSE
ORDER = 'E'
END IF
INDISP = 1 + N
INDIWK = INDISP + N
CALL DSTEBZ( RANGE, ORDER, N, VL, VU, IL, IU, ABSTOL,
$ RWORK( INDD ), RWORK( INDE ), M, NSPLIT, W,
$ IWORK( 1 ), IWORK( INDISP ), RWORK( INDRWK ),
$ IWORK( INDIWK ), IINFO )
IF( IINFO.NE.0 ) THEN
INFO = 2*N + IINFO
IF( IINFO.NE.1 )
$ GO TO 30
END IF
*
IF( WANTZ ) THEN
CALL ZSTEIN( N, RWORK( INDD ), RWORK( INDE ), M, W,
$ IWORK( 1 ), IWORK( INDISP ), Z, LDZ,
$ RWORK( INDRWK ), IWORK( INDIWK ), IFAIL, IINFO )
IF( IINFO.NE.0 .AND. INFO.EQ.0 )
$ INFO = IINFO
*
* Apply unitary matrix used in reduction to tridiagonal
* form to eigenvectors returned by ZSTEIN.
*
DO 20 J = 1, M
CALL ZCOPY( N, Z( 1, J ), 1, WORK( 1 ), 1 )
CALL ZGEMV( 'N', N, N, CONE, Q, LDQ, WORK, 1, CZERO,
$ Z( 1, J ), 1 )
20 CONTINUE
END IF
*
30 CONTINUE
*
* If eigenvalues are not in order, then sort them, along with
* eigenvectors.
*
IF( WANTZ ) THEN
DO 50 J = 1, M - 1
I = 0
TMP1 = W( J )
DO 40 JJ = J + 1, M
IF( W( JJ ).LT.TMP1 ) THEN
I = JJ
TMP1 = W( JJ )
END IF
40 CONTINUE
*
IF( I.NE.0 ) THEN
ITMP1 = IWORK( 1 + I-1 )
W( I ) = W( J )
IWORK( 1 + I-1 ) = IWORK( 1 + J-1 )
W( J ) = TMP1
IWORK( 1 + J-1 ) = ITMP1
CALL ZSWAP( N, Z( 1, I ), 1, Z( 1, J ), 1 )
IF( INFO.NE.0 ) THEN
ITMP1 = IFAIL( I )
IFAIL( I ) = IFAIL( J )
IFAIL( J ) = ITMP1
END IF
END IF
50 CONTINUE
END IF
*
RETURN
*
* End of ZHBGVX
*
END