// 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.ComputeEV a contains the // orthonormal eigenvectors of A on exit, otherwise 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) { checkMatrix(n, n, a, lda) upper := uplo == blas.Upper wantz := jobz == lapack.ComputeEV var opts string if upper { opts = "U" } else { opts = "L" } nb := impl.Ilaenv(1, "DSYTRD", opts, n, -1, -1, -1) lworkopt := max(1, (nb+2)*n) work[0] = float64(lworkopt) if lwork == -1 { return } if len(work) < lwork { panic(badWork) } if lwork < 3*n-1 { panic(badWork) } if n == 0 { return true } if n == 1 { w[0] = a[0] work[0] = 2 if wantz { 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 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 !wantz { 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 }