!*> \brief DSYEV computes the eigenvalues and, optionally, the left and/or right eigenvectors for SY matrices
!*
!* =========== DOCUMENTATION ===========
!*
!* Online html documentation available at
!* http://www.netlib.org/lapack/explore-html/
!*
!*> \htmlonly
!*> Download DSYEV + dependencies
!*>
!*> [TGZ]
!*>
!*> [ZIP]
!*>
!*> [TXT]
!*> \endhtmlonly
!*
!* Definition:
!* ===========
!*
!* SUBROUTINE DSYEV( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, INFO )
!*
!* .. Scalar Arguments ..
!* CHARACTER JOBZ, UPLO
!* INTEGER INFO, LDA, LWORK, N
!* ..
!* .. Array Arguments ..
!* DOUBLE PRECISION A( LDA, * ), W( * ), WORK( * )
!* ..
!*
!*
!*> \par Purpose:
!* =============
!*>
!*> \verbatim
!*>
!*> DSYEV computes all eigenvalues and, optionally, eigenvectors of a
!*> real symmetric matrix A.
!*> \endverbatim
!*
!* Arguments:
!* ==========
!*
!*> \param[in] JOBZ
!*> \verbatim
!*> JOBZ is CHARACTER*1
!*> = 'N': Compute eigenvalues only;
!*> = 'V': Compute eigenvalues and eigenvectors.
!*> \endverbatim
!*>
!*> \param[in] UPLO
!*> \verbatim
!*> UPLO is CHARACTER*1
!*> = 'U': Upper triangle of A is stored;
!*> = 'L': Lower triangle of A is stored.
!*> \endverbatim
!*>
!*> \param[in] N
!*> \verbatim
!*> N is INTEGER
!*> The order of the matrix A. N >= 0.
!*> \endverbatim
!*>
!*> \param[in,out] A
!*> \verbatim
!*> A is DOUBLE PRECISION array, dimension (LDA, N)
!*> On entry, the symmetric matrix A. If UPLO = 'U', the
!*> leading N-by-N upper triangular part of A contains the
!*> upper triangular part of the matrix A. If UPLO = 'L',
!*> the leading N-by-N lower triangular part of A contains
!*> the lower triangular part of the matrix A.
!*> On exit, if JOBZ = 'V', then if INFO = 0, A contains the
!*> orthonormal eigenvectors of the matrix A.
!*> If JOBZ = 'N', then on exit the lower triangle (if UPLO='L')
!*> or the upper triangle (if UPLO='U') of A, including the
!*> diagonal, is destroyed.
!*> \endverbatim
!*>
!*> \param[in] LDA
!*> \verbatim
!*> LDA is INTEGER
!*> The leading dimension of the array A. LDA >= max(1,N).
!*> \endverbatim
!*>
!*> \param[out] W
!*> \verbatim
!*> W is DOUBLE PRECISION array, dimension (N)
!*> If INFO = 0, the eigenvalues in ascending order.
!*> \endverbatim
!*>
!*> \param[out] WORK
!*> \verbatim
!*> WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK))
!*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK.
!*> \endverbatim
!*>
!*> \param[in] LWORK
!*> \verbatim
!*> LWORK is INTEGER
!*> The length of the array WORK. LWORK >= max(1,3*N-1).
!*> For optimal efficiency, LWORK >= (NB+2)*N,
!*> where NB is the blocksize for DSYTRD returned by ILAENV.
!*>
!*> If LWORK = -1, then a workspace query is assumed; the routine
!*> only calculates the optimal size of the WORK array, returns
!*> this value as the first entry of the WORK array, and no error
!*> message related to LWORK is issued by XERBLA.
!*> \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, the algorithm failed to converge; i
!*> off-diagonal elements of an intermediate tridiagonal
!*> form did not converge to zero.
!*> \endverbatim
!*
!* Authors:
!* ========
!*
!*> \author Univ. of Tennessee
!*> \author Univ. of California Berkeley
!*> \author Univ. of Colorado Denver
!*> \author NAG Ltd.
!*
!*> \date December 2016
!*
!*> \ingroup doubleSYeigen
!*
module DSYEV_PF
contains
!* =====================================================================
SUBROUTINE DSYEV_PF(JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, INFO)
!*
!* -- LAPACK driver routine (version 3.7.0) --
!* -- LAPACK is a software package provided by Univ. of Tennessee, --
!* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--
!* December 2016
!*
!* .. Scalar Arguments ..
CHARACTER JOBZ, UPLO
INTEGER INFO, LDA, LWORK, N
!* ..
!* .. Array Arguments ..
DOUBLE PRECISION A(LDA, *), W(*), WORK(*)
!* ..
!*
!* =====================================================================
!*
!* .. Parameters ..
DOUBLE PRECISION ZERO, ONE
PARAMETER(ZERO=0.0D0, ONE=1.0D0)
!* ..
!* .. Local Scalars ..
LOGICAL LOWER, LQUERY, WANTZ
INTEGER IINFO, IMAX, INDE, INDTAU, INDWRK, ISCALE, &
LLWORK, LWKOPT, NB
DOUBLE PRECISION ANRM, BIGNUM, EPS, RMAX, RMIN, SAFMIN, SIGMA, &
SMLNUM
!* ..
!* .. External Functions ..
LOGICAL LSAME
INTEGER ILAENV
DOUBLE PRECISION DLAMCH, DLANSY
EXTERNAL LSAME, ILAENV, DLAMCH, DLANSY
!* ..
!* .. External Subroutines ..
EXTERNAL DLASCL, DORGTR, DSCAL, DSTEQR, DSTERF, DSYTRD, &
XERBLA
!* ..
!* .. Intrinsic Functions ..
INTRINSIC MAX, SQRT
!* ..
!* .. Executable Statements ..
!*
!* Test the input parameters.
!*
WANTZ = LSAME(JOBZ, 'V')
LOWER = LSAME(UPLO, 'L')
LQUERY = (LWORK .EQ. -1)
!*
INFO = 0
IF (.NOT. (WANTZ .OR. LSAME(JOBZ, 'N'))) THEN
INFO = -1
ELSE IF (.NOT. (LOWER .OR. LSAME(UPLO, 'U'))) THEN
INFO = -2
ELSE IF (N .LT. 0) THEN
INFO = -3
ELSE IF (LDA .LT. MAX(1, N)) THEN
INFO = -5
END IF
!*
IF (INFO .EQ. 0) THEN
NB = ILAENV(1, 'DSYTRD', UPLO, N, -1, -1, -1)
LWKOPT = MAX(1, (NB + 2)*N)
WORK(1) = LWKOPT
!*
IF (LWORK .LT. MAX(1, 3*N - 1) .AND. .NOT. LQUERY) &
INFO = -8
END IF
!*
IF (INFO .NE. 0) THEN
CALL XERBLA('DSYEV ', -INFO)
RETURN
ELSE IF (LQUERY) THEN
RETURN
END IF
!*
!* Quick return if possible
!*
IF (N .EQ. 0) THEN
RETURN
END IF
!*
IF (N .EQ. 1) THEN
W(1) = A(1, 1)
WORK(1) = 2
IF (WANTZ) &
A(1, 1) = ONE
RETURN
END IF
!*
!* Get machine constants.
!*
SAFMIN = DLAMCH('Safe minimum')
EPS = DLAMCH('Precision')
SMLNUM = SAFMIN/EPS
BIGNUM = ONE/SMLNUM
RMIN = SQRT(SMLNUM)
RMAX = SQRT(BIGNUM)
!*
!* Scale matrix to allowable range, if necessary.
!*
ANRM = DLANSY('M', UPLO, N, A, LDA, WORK)
ISCALE = 0
IF (ANRM .GT. ZERO .AND. ANRM .LT. RMIN) THEN
ISCALE = 1
SIGMA = RMIN/ANRM
ELSE IF (ANRM .GT. RMAX) THEN
ISCALE = 1
SIGMA = RMAX/ANRM
END IF
IF (ISCALE .EQ. 1) &
CALL DLASCL(UPLO, 0, 0, ONE, SIGMA, N, N, A, LDA, INFO)
!*
!* Call DSYTRD to reduce symmetric matrix to tridiagonal form.
!*
INDE = 1
INDTAU = INDE + N
INDWRK = INDTAU + N
LLWORK = LWORK - INDWRK + 1
CALL DSYTRD(UPLO, N, A, LDA, W, WORK(INDE), WORK(INDTAU), &
WORK(INDWRK), LLWORK, IINFO)
!*
!* For eigenvalues only, call DSTERF. For eigenvectors, first call
!* DORGTR to generate the orthogonal matrix, then call DSTEQR.
!*
IF (.NOT. WANTZ) THEN
CALL DSTERF(N, W, WORK(INDE), INFO)
ELSE
CALL DORGTR(UPLO, N, A, LDA, WORK(INDTAU), WORK(INDWRK), &
LLWORK, IINFO)
CALL DSTEQR(JOBZ, N, W, WORK(INDE), A, LDA, WORK(INDTAU), &
INFO)
END IF
!*
!* If matrix was scaled, then rescale eigenvalues appropriately.
!*
IF (ISCALE .EQ. 1) THEN
IF (INFO .EQ. 0) THEN
IMAX = N
ELSE
IMAX = INFO - 1
END IF
CALL DSCAL(IMAX, ONE/SIGMA, W, 1)
END IF
!*
!* Set WORK(1) to optimal workspace size.
!*
WORK(1) = LWKOPT
!*
RETURN
!*
!* End of DSYEV
!*
END SUBROUTINE
end module DSYEV_PF