| // Copyright ©2016 The Gonum Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package gonum |
| |
| import ( |
| "math" |
| |
| "gonum.org/v1/gonum/blas" |
| "gonum.org/v1/gonum/blas/blas64" |
| "gonum.org/v1/gonum/lapack" |
| ) |
| |
| // Dsyev computes all eigenvalues and, optionally, the eigenvectors of a real |
| // symmetric matrix A. |
| // |
| // w contains the eigenvalues in ascending order upon return. w must have length |
| // at least n, and Dsyev will panic otherwise. |
| // |
| // On entry, a contains the elements of the symmetric matrix A in the triangular |
| // portion specified by uplo. If jobz == lapack.EVCompute, a contains the |
| // orthonormal eigenvectors of A on exit, otherwise jobz must be lapack.EVNone |
| // and on exit the specified triangular region is overwritten. |
| // |
| // work is temporary storage, and lwork specifies the usable memory length. At minimum, |
| // lwork >= 3*n-1, and Dsyev will panic otherwise. The amount of blocking is |
| // limited by the usable length. If lwork == -1, instead of computing Dsyev the |
| // optimal work length is stored into work[0]. |
| func (impl Implementation) Dsyev(jobz lapack.EVJob, uplo blas.Uplo, n int, a []float64, lda int, w, work []float64, lwork int) (ok bool) { |
| switch { |
| case jobz != lapack.EVNone && jobz != lapack.EVCompute: |
| panic(badEVJob) |
| case uplo != blas.Upper && uplo != blas.Lower: |
| panic(badUplo) |
| case n < 0: |
| panic(nLT0) |
| case lda < max(1, n): |
| panic(badLdA) |
| case lwork < max(1, 3*n-1) && lwork != -1: |
| panic(badLWork) |
| case len(work) < max(1, lwork): |
| panic(shortWork) |
| } |
| |
| // Quick return if possible. |
| if n == 0 { |
| return true |
| } |
| |
| var opts string |
| if uplo == blas.Upper { |
| opts = "U" |
| } else { |
| opts = "L" |
| } |
| nb := impl.Ilaenv(1, "DSYTRD", opts, n, -1, -1, -1) |
| lworkopt := max(1, (nb+2)*n) |
| if lwork == -1 { |
| work[0] = float64(lworkopt) |
| return |
| } |
| |
| switch { |
| case len(a) < (n-1)*lda+n: |
| panic(shortA) |
| case len(w) < n: |
| panic(shortW) |
| } |
| |
| if n == 1 { |
| w[0] = a[0] |
| work[0] = 2 |
| if jobz == lapack.EVCompute { |
| a[0] = 1 |
| } |
| return true |
| } |
| |
| safmin := dlamchS |
| eps := dlamchP |
| smlnum := safmin / eps |
| bignum := 1 / smlnum |
| rmin := math.Sqrt(smlnum) |
| rmax := math.Sqrt(bignum) |
| |
| // Scale matrix to allowable range, if necessary. |
| anrm := impl.Dlansy(lapack.MaxAbs, uplo, n, a, lda, work) |
| scaled := false |
| var sigma float64 |
| if anrm > 0 && anrm < rmin { |
| scaled = true |
| sigma = rmin / anrm |
| } else if anrm > rmax { |
| scaled = true |
| sigma = rmax / anrm |
| } |
| if scaled { |
| kind := lapack.LowerTri |
| if uplo == blas.Upper { |
| kind = lapack.UpperTri |
| } |
| impl.Dlascl(kind, 0, 0, 1, sigma, n, n, a, lda) |
| } |
| var inde int |
| indtau := inde + n |
| indwork := indtau + n |
| llwork := lwork - indwork |
| impl.Dsytrd(uplo, n, a, lda, w, work[inde:], work[indtau:], work[indwork:], llwork) |
| |
| // For eigenvalues only, call Dsterf. For eigenvectors, first call Dorgtr |
| // to generate the orthogonal matrix, then call Dsteqr. |
| if jobz == lapack.EVNone { |
| ok = impl.Dsterf(n, w, work[inde:]) |
| } else { |
| impl.Dorgtr(uplo, n, a, lda, work[indtau:], work[indwork:], llwork) |
| ok = impl.Dsteqr(lapack.EVComp(jobz), n, w, work[inde:], a, lda, work[indtau:]) |
| } |
| if !ok { |
| return false |
| } |
| |
| // If the matrix was scaled, then rescale eigenvalues appropriately. |
| if scaled { |
| bi := blas64.Implementation() |
| bi.Dscal(n, 1/sigma, w, 1) |
| } |
| work[0] = float64(lworkopt) |
| return true |
| } |