gonum.org/v1/gonum@v0.14.0/lapack/gonum/dsyev.go (about) 1 // Copyright ©2016 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package gonum 6 7 import ( 8 "math" 9 10 "gonum.org/v1/gonum/blas" 11 "gonum.org/v1/gonum/blas/blas64" 12 "gonum.org/v1/gonum/lapack" 13 ) 14 15 // Dsyev computes all eigenvalues and, optionally, the eigenvectors of a real 16 // symmetric matrix A. 17 // 18 // w contains the eigenvalues in ascending order upon return. w must have length 19 // at least n, and Dsyev will panic otherwise. 20 // 21 // On entry, a contains the elements of the symmetric matrix A in the triangular 22 // portion specified by uplo. If jobz == lapack.EVCompute, a contains the 23 // orthonormal eigenvectors of A on exit, otherwise jobz must be lapack.EVNone 24 // and on exit the specified triangular region is overwritten. 25 // 26 // work is temporary storage, and lwork specifies the usable memory length. At minimum, 27 // lwork >= 3*n-1, and Dsyev will panic otherwise. The amount of blocking is 28 // limited by the usable length. If lwork == -1, instead of computing Dsyev the 29 // optimal work length is stored into work[0]. 30 func (impl Implementation) Dsyev(jobz lapack.EVJob, uplo blas.Uplo, n int, a []float64, lda int, w, work []float64, lwork int) (ok bool) { 31 switch { 32 case jobz != lapack.EVNone && jobz != lapack.EVCompute: 33 panic(badEVJob) 34 case uplo != blas.Upper && uplo != blas.Lower: 35 panic(badUplo) 36 case n < 0: 37 panic(nLT0) 38 case lda < max(1, n): 39 panic(badLdA) 40 case lwork < max(1, 3*n-1) && lwork != -1: 41 panic(badLWork) 42 case len(work) < max(1, lwork): 43 panic(shortWork) 44 } 45 46 // Quick return if possible. 47 if n == 0 { 48 return true 49 } 50 51 var opts string 52 if uplo == blas.Upper { 53 opts = "U" 54 } else { 55 opts = "L" 56 } 57 nb := impl.Ilaenv(1, "DSYTRD", opts, n, -1, -1, -1) 58 lworkopt := max(1, (nb+2)*n) 59 if lwork == -1 { 60 work[0] = float64(lworkopt) 61 return 62 } 63 64 switch { 65 case len(a) < (n-1)*lda+n: 66 panic(shortA) 67 case len(w) < n: 68 panic(shortW) 69 } 70 71 if n == 1 { 72 w[0] = a[0] 73 work[0] = 2 74 if jobz == lapack.EVCompute { 75 a[0] = 1 76 } 77 return true 78 } 79 80 safmin := dlamchS 81 eps := dlamchP 82 smlnum := safmin / eps 83 bignum := 1 / smlnum 84 rmin := math.Sqrt(smlnum) 85 rmax := math.Sqrt(bignum) 86 87 // Scale matrix to allowable range, if necessary. 88 anrm := impl.Dlansy(lapack.MaxAbs, uplo, n, a, lda, work) 89 scaled := false 90 var sigma float64 91 if anrm > 0 && anrm < rmin { 92 scaled = true 93 sigma = rmin / anrm 94 } else if anrm > rmax { 95 scaled = true 96 sigma = rmax / anrm 97 } 98 if scaled { 99 kind := lapack.LowerTri 100 if uplo == blas.Upper { 101 kind = lapack.UpperTri 102 } 103 impl.Dlascl(kind, 0, 0, 1, sigma, n, n, a, lda) 104 } 105 var inde int 106 indtau := inde + n 107 indwork := indtau + n 108 llwork := lwork - indwork 109 impl.Dsytrd(uplo, n, a, lda, w, work[inde:], work[indtau:], work[indwork:], llwork) 110 111 // For eigenvalues only, call Dsterf. For eigenvectors, first call Dorgtr 112 // to generate the orthogonal matrix, then call Dsteqr. 113 if jobz == lapack.EVNone { 114 ok = impl.Dsterf(n, w, work[inde:]) 115 } else { 116 impl.Dorgtr(uplo, n, a, lda, work[indtau:], work[indwork:], llwork) 117 ok = impl.Dsteqr(lapack.EVComp(jobz), n, w, work[inde:], a, lda, work[indtau:]) 118 } 119 if !ok { 120 return false 121 } 122 123 // If the matrix was scaled, then rescale eigenvalues appropriately. 124 if scaled { 125 bi := blas64.Implementation() 126 bi.Dscal(n, 1/sigma, w, 1) 127 } 128 work[0] = float64(lworkopt) 129 return true 130 }