blob: 35241728f2df871396dbc5d16ab6967c7a876be6 [file] [log] [blame]
// 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
}