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