gonum.org/v1/gonum@v0.14.0/lapack/gonum/dgeev.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 // Dgeev computes the eigenvalues and, optionally, the left and/or right 16 // eigenvectors for an n×n real nonsymmetric matrix A. 17 // 18 // The right eigenvector v_j of A corresponding to an eigenvalue λ_j 19 // is defined by 20 // 21 // A v_j = λ_j v_j, 22 // 23 // and the left eigenvector u_j corresponding to an eigenvalue λ_j is defined by 24 // 25 // u_jᴴ A = λ_j u_jᴴ, 26 // 27 // where u_jᴴ is the conjugate transpose of u_j. 28 // 29 // On return, A will be overwritten and the left and right eigenvectors will be 30 // stored, respectively, in the columns of the n×n matrices VL and VR in the 31 // same order as their eigenvalues. If the j-th eigenvalue is real, then 32 // 33 // u_j = VL[:,j], 34 // v_j = VR[:,j], 35 // 36 // and if it is not real, then j and j+1 form a complex conjugate pair and the 37 // eigenvectors can be recovered as 38 // 39 // u_j = VL[:,j] + i*VL[:,j+1], 40 // u_{j+1} = VL[:,j] - i*VL[:,j+1], 41 // v_j = VR[:,j] + i*VR[:,j+1], 42 // v_{j+1} = VR[:,j] - i*VR[:,j+1], 43 // 44 // where i is the imaginary unit. The computed eigenvectors are normalized to 45 // have Euclidean norm equal to 1 and largest component real. 46 // 47 // Left eigenvectors will be computed only if jobvl == lapack.LeftEVCompute, 48 // otherwise jobvl must be lapack.LeftEVNone. 49 // Right eigenvectors will be computed only if jobvr == lapack.RightEVCompute, 50 // otherwise jobvr must be lapack.RightEVNone. 51 // For other values of jobvl and jobvr Dgeev will panic. 52 // 53 // wr and wi contain the real and imaginary parts, respectively, of the computed 54 // eigenvalues. Complex conjugate pairs of eigenvalues appear consecutively with 55 // the eigenvalue having the positive imaginary part first. 56 // wr and wi must have length n, and Dgeev will panic otherwise. 57 // 58 // work must have length at least lwork and lwork must be at least max(1,4*n) if 59 // the left or right eigenvectors are computed, and at least max(1,3*n) if no 60 // eigenvectors are computed. For good performance, lwork must generally be 61 // larger. On return, optimal value of lwork will be stored in work[0]. 62 // 63 // If lwork == -1, instead of performing Dgeev, the function only calculates the 64 // optimal value of lwork and stores it into work[0]. 65 // 66 // On return, first is the index of the first valid eigenvalue. If first == 0, 67 // all eigenvalues and eigenvectors have been computed. If first is positive, 68 // Dgeev failed to compute all the eigenvalues, no eigenvectors have been 69 // computed and wr[first:] and wi[first:] contain those eigenvalues which have 70 // converged. 71 func (impl Implementation) Dgeev(jobvl lapack.LeftEVJob, jobvr lapack.RightEVJob, n int, a []float64, lda int, wr, wi []float64, vl []float64, ldvl int, vr []float64, ldvr int, work []float64, lwork int) (first int) { 72 wantvl := jobvl == lapack.LeftEVCompute 73 wantvr := jobvr == lapack.RightEVCompute 74 var minwrk int 75 if wantvl || wantvr { 76 minwrk = max(1, 4*n) 77 } else { 78 minwrk = max(1, 3*n) 79 } 80 switch { 81 case jobvl != lapack.LeftEVCompute && jobvl != lapack.LeftEVNone: 82 panic(badLeftEVJob) 83 case jobvr != lapack.RightEVCompute && jobvr != lapack.RightEVNone: 84 panic(badRightEVJob) 85 case n < 0: 86 panic(nLT0) 87 case lda < max(1, n): 88 panic(badLdA) 89 case ldvl < 1 || (ldvl < n && wantvl): 90 panic(badLdVL) 91 case ldvr < 1 || (ldvr < n && wantvr): 92 panic(badLdVR) 93 case lwork < minwrk && lwork != -1: 94 panic(badLWork) 95 case len(work) < lwork: 96 panic(shortWork) 97 } 98 99 // Quick return if possible. 100 if n == 0 { 101 work[0] = 1 102 return 0 103 } 104 105 maxwrk := 2*n + n*impl.Ilaenv(1, "DGEHRD", " ", n, 1, n, 0) 106 if wantvl || wantvr { 107 maxwrk = max(maxwrk, 2*n+(n-1)*impl.Ilaenv(1, "DORGHR", " ", n, 1, n, -1)) 108 impl.Dhseqr(lapack.EigenvaluesAndSchur, lapack.SchurOrig, n, 0, n-1, 109 a, lda, wr, wi, nil, n, work, -1) 110 maxwrk = max(maxwrk, max(n+1, n+int(work[0]))) 111 side := lapack.EVLeft 112 if wantvr { 113 side = lapack.EVRight 114 } 115 impl.Dtrevc3(side, lapack.EVAllMulQ, nil, n, a, lda, vl, ldvl, vr, ldvr, 116 n, work, -1) 117 maxwrk = max(maxwrk, n+int(work[0])) 118 maxwrk = max(maxwrk, 4*n) 119 } else { 120 impl.Dhseqr(lapack.EigenvaluesOnly, lapack.SchurNone, n, 0, n-1, 121 a, lda, wr, wi, vr, ldvr, work, -1) 122 maxwrk = max(maxwrk, max(n+1, n+int(work[0]))) 123 } 124 maxwrk = max(maxwrk, minwrk) 125 126 if lwork == -1 { 127 work[0] = float64(maxwrk) 128 return 0 129 } 130 131 switch { 132 case len(a) < (n-1)*lda+n: 133 panic(shortA) 134 case len(wr) != n: 135 panic(badLenWr) 136 case len(wi) != n: 137 panic(badLenWi) 138 case len(vl) < (n-1)*ldvl+n && wantvl: 139 panic(shortVL) 140 case len(vr) < (n-1)*ldvr+n && wantvr: 141 panic(shortVR) 142 } 143 144 // Get machine constants. 145 smlnum := math.Sqrt(dlamchS) / dlamchP 146 bignum := 1 / smlnum 147 148 // Scale A if max element outside range [smlnum,bignum]. 149 anrm := impl.Dlange(lapack.MaxAbs, n, n, a, lda, nil) 150 var scalea bool 151 var cscale float64 152 if 0 < anrm && anrm < smlnum { 153 scalea = true 154 cscale = smlnum 155 } else if anrm > bignum { 156 scalea = true 157 cscale = bignum 158 } 159 if scalea { 160 impl.Dlascl(lapack.General, 0, 0, anrm, cscale, n, n, a, lda) 161 } 162 163 // Balance the matrix. 164 workbal := work[:n] 165 ilo, ihi := impl.Dgebal(lapack.PermuteScale, n, a, lda, workbal) 166 167 // Reduce to upper Hessenberg form. 168 iwrk := 2 * n 169 tau := work[n : iwrk-1] 170 impl.Dgehrd(n, ilo, ihi, a, lda, tau, work[iwrk:], lwork-iwrk) 171 172 var side lapack.EVSide 173 if wantvl { 174 side = lapack.EVLeft 175 // Copy Householder vectors to VL. 176 impl.Dlacpy(blas.Lower, n, n, a, lda, vl, ldvl) 177 // Generate orthogonal matrix in VL. 178 impl.Dorghr(n, ilo, ihi, vl, ldvl, tau, work[iwrk:], lwork-iwrk) 179 // Perform QR iteration, accumulating Schur vectors in VL. 180 iwrk = n 181 first = impl.Dhseqr(lapack.EigenvaluesAndSchur, lapack.SchurOrig, n, ilo, ihi, 182 a, lda, wr, wi, vl, ldvl, work[iwrk:], lwork-iwrk) 183 if wantvr { 184 // Want left and right eigenvectors. 185 // Copy Schur vectors to VR. 186 side = lapack.EVBoth 187 impl.Dlacpy(blas.All, n, n, vl, ldvl, vr, ldvr) 188 } 189 } else if wantvr { 190 side = lapack.EVRight 191 // Copy Householder vectors to VR. 192 impl.Dlacpy(blas.Lower, n, n, a, lda, vr, ldvr) 193 // Generate orthogonal matrix in VR. 194 impl.Dorghr(n, ilo, ihi, vr, ldvr, tau, work[iwrk:], lwork-iwrk) 195 // Perform QR iteration, accumulating Schur vectors in VR. 196 iwrk = n 197 first = impl.Dhseqr(lapack.EigenvaluesAndSchur, lapack.SchurOrig, n, ilo, ihi, 198 a, lda, wr, wi, vr, ldvr, work[iwrk:], lwork-iwrk) 199 } else { 200 // Compute eigenvalues only. 201 iwrk = n 202 first = impl.Dhseqr(lapack.EigenvaluesOnly, lapack.SchurNone, n, ilo, ihi, 203 a, lda, wr, wi, nil, 1, work[iwrk:], lwork-iwrk) 204 } 205 206 if first > 0 { 207 if scalea { 208 // Undo scaling. 209 impl.Dlascl(lapack.General, 0, 0, cscale, anrm, n-first, 1, wr[first:], 1) 210 impl.Dlascl(lapack.General, 0, 0, cscale, anrm, n-first, 1, wi[first:], 1) 211 impl.Dlascl(lapack.General, 0, 0, cscale, anrm, ilo, 1, wr, 1) 212 impl.Dlascl(lapack.General, 0, 0, cscale, anrm, ilo, 1, wi, 1) 213 } 214 work[0] = float64(maxwrk) 215 return first 216 } 217 218 if wantvl || wantvr { 219 // Compute left and/or right eigenvectors. 220 impl.Dtrevc3(side, lapack.EVAllMulQ, nil, n, 221 a, lda, vl, ldvl, vr, ldvr, n, work[iwrk:], lwork-iwrk) 222 } 223 bi := blas64.Implementation() 224 if wantvl { 225 // Undo balancing of left eigenvectors. 226 impl.Dgebak(lapack.PermuteScale, lapack.EVLeft, n, ilo, ihi, workbal, n, vl, ldvl) 227 // Normalize left eigenvectors and make largest component real. 228 for i, wii := range wi { 229 if wii < 0 { 230 continue 231 } 232 if wii == 0 { 233 scl := 1 / bi.Dnrm2(n, vl[i:], ldvl) 234 bi.Dscal(n, scl, vl[i:], ldvl) 235 continue 236 } 237 scl := 1 / impl.Dlapy2(bi.Dnrm2(n, vl[i:], ldvl), bi.Dnrm2(n, vl[i+1:], ldvl)) 238 bi.Dscal(n, scl, vl[i:], ldvl) 239 bi.Dscal(n, scl, vl[i+1:], ldvl) 240 for k := 0; k < n; k++ { 241 vi := vl[k*ldvl+i] 242 vi1 := vl[k*ldvl+i+1] 243 work[iwrk+k] = vi*vi + vi1*vi1 244 } 245 k := bi.Idamax(n, work[iwrk:iwrk+n], 1) 246 cs, sn, _ := impl.Dlartg(vl[k*ldvl+i], vl[k*ldvl+i+1]) 247 bi.Drot(n, vl[i:], ldvl, vl[i+1:], ldvl, cs, sn) 248 vl[k*ldvl+i+1] = 0 249 } 250 } 251 if wantvr { 252 // Undo balancing of right eigenvectors. 253 impl.Dgebak(lapack.PermuteScale, lapack.EVRight, n, ilo, ihi, workbal, n, vr, ldvr) 254 // Normalize right eigenvectors and make largest component real. 255 for i, wii := range wi { 256 if wii < 0 { 257 continue 258 } 259 if wii == 0 { 260 scl := 1 / bi.Dnrm2(n, vr[i:], ldvr) 261 bi.Dscal(n, scl, vr[i:], ldvr) 262 continue 263 } 264 scl := 1 / impl.Dlapy2(bi.Dnrm2(n, vr[i:], ldvr), bi.Dnrm2(n, vr[i+1:], ldvr)) 265 bi.Dscal(n, scl, vr[i:], ldvr) 266 bi.Dscal(n, scl, vr[i+1:], ldvr) 267 for k := 0; k < n; k++ { 268 vi := vr[k*ldvr+i] 269 vi1 := vr[k*ldvr+i+1] 270 work[iwrk+k] = vi*vi + vi1*vi1 271 } 272 k := bi.Idamax(n, work[iwrk:iwrk+n], 1) 273 cs, sn, _ := impl.Dlartg(vr[k*ldvr+i], vr[k*ldvr+i+1]) 274 bi.Drot(n, vr[i:], ldvr, vr[i+1:], ldvr, cs, sn) 275 vr[k*ldvr+i+1] = 0 276 } 277 } 278 279 if scalea { 280 // Undo scaling. 281 impl.Dlascl(lapack.General, 0, 0, cscale, anrm, n-first, 1, wr[first:], 1) 282 impl.Dlascl(lapack.General, 0, 0, cscale, anrm, n-first, 1, wi[first:], 1) 283 } 284 285 work[0] = float64(maxwrk) 286 return first 287 }