github.com/gopherd/gonum@v0.0.4/lapack/gonum/dhseqr.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 "github.com/gopherd/gonum/blas" 11 "github.com/gopherd/gonum/lapack" 12 ) 13 14 // Dhseqr computes the eigenvalues of an n×n Hessenberg matrix H and, 15 // optionally, the matrices T and Z from the Schur decomposition 16 // H = Z T Zᵀ, 17 // where T is an n×n upper quasi-triangular matrix (the Schur form), and Z is 18 // the n×n orthogonal matrix of Schur vectors. 19 // 20 // Optionally Z may be postmultiplied into an input orthogonal matrix Q so that 21 // this routine can give the Schur factorization of a matrix A which has been 22 // reduced to the Hessenberg form H by the orthogonal matrix Q: 23 // A = Q H Qᵀ = (QZ) T (QZ)ᵀ. 24 // 25 // If job == lapack.EigenvaluesOnly, only the eigenvalues will be computed. 26 // If job == lapack.EigenvaluesAndSchur, the eigenvalues and the Schur form T will 27 // be computed. 28 // For other values of job Dhseqr will panic. 29 // 30 // If compz == lapack.SchurNone, no Schur vectors will be computed and Z will not be 31 // referenced. 32 // If compz == lapack.SchurHess, on return Z will contain the matrix of Schur 33 // vectors of H. 34 // If compz == lapack.SchurOrig, on entry z is assumed to contain the orthogonal 35 // matrix Q that is the identity except for the submatrix 36 // Q[ilo:ihi+1,ilo:ihi+1]. On return z will be updated to the product Q*Z. 37 // 38 // ilo and ihi determine the block of H on which Dhseqr operates. It is assumed 39 // that H is already upper triangular in rows and columns [0:ilo] and [ihi+1:n], 40 // although it will be only checked that the block is isolated, that is, 41 // ilo == 0 or H[ilo,ilo-1] == 0, 42 // ihi == n-1 or H[ihi+1,ihi] == 0, 43 // and Dhseqr will panic otherwise. ilo and ihi are typically set by a previous 44 // call to Dgebal, otherwise they should be set to 0 and n-1, respectively. It 45 // must hold that 46 // 0 <= ilo <= ihi < n if n > 0, 47 // ilo == 0 and ihi == -1 if n == 0. 48 // 49 // wr and wi must have length n. 50 // 51 // work must have length at least lwork and lwork must be at least max(1,n) 52 // otherwise Dhseqr will panic. The minimum lwork delivers very good and 53 // sometimes optimal performance, although lwork as large as 11*n may be 54 // required. On return, work[0] will contain the optimal value of lwork. 55 // 56 // If lwork is -1, instead of performing Dhseqr, the function only estimates the 57 // optimal workspace size and stores it into work[0]. Neither h nor z are 58 // accessed. 59 // 60 // unconverged indicates whether Dhseqr computed all the eigenvalues. 61 // 62 // If unconverged == 0, all the eigenvalues have been computed and their real 63 // and imaginary parts will be stored on return in wr and wi, respectively. If 64 // two eigenvalues are computed as a complex conjugate pair, they are stored in 65 // consecutive elements of wr and wi, say the i-th and (i+1)th, with wi[i] > 0 66 // and wi[i+1] < 0. 67 // 68 // If unconverged == 0 and job == lapack.EigenvaluesAndSchur, on return H will 69 // contain the upper quasi-triangular matrix T from the Schur decomposition (the 70 // Schur form). 2×2 diagonal blocks (corresponding to complex conjugate pairs of 71 // eigenvalues) will be returned in standard form, with 72 // H[i,i] == H[i+1,i+1], 73 // and 74 // H[i+1,i]*H[i,i+1] < 0. 75 // The eigenvalues will be stored in wr and wi in the same order as on the 76 // diagonal of the Schur form returned in H, with 77 // wr[i] = H[i,i], 78 // and, if H[i:i+2,i:i+2] is a 2×2 diagonal block, 79 // wi[i] = sqrt(-H[i+1,i]*H[i,i+1]), 80 // wi[i+1] = -wi[i]. 81 // 82 // If unconverged == 0 and job == lapack.EigenvaluesOnly, the contents of h 83 // on return is unspecified. 84 // 85 // If unconverged > 0, some eigenvalues have not converged, and the blocks 86 // [0:ilo] and [unconverged:n] of wr and wi will contain those eigenvalues which 87 // have been successfully computed. Failures are rare. 88 // 89 // If unconverged > 0 and job == lapack.EigenvaluesOnly, on return the 90 // remaining unconverged eigenvalues are the eigenvalues of the upper Hessenberg 91 // matrix H[ilo:unconverged,ilo:unconverged]. 92 // 93 // If unconverged > 0 and job == lapack.EigenvaluesAndSchur, then on 94 // return 95 // (initial H) U = U (final H), (*) 96 // where U is an orthogonal matrix. The final H is upper Hessenberg and 97 // H[unconverged:ihi+1,unconverged:ihi+1] is upper quasi-triangular. 98 // 99 // If unconverged > 0 and compz == lapack.SchurOrig, then on return 100 // (final Z) = (initial Z) U, 101 // where U is the orthogonal matrix in (*) regardless of the value of job. 102 // 103 // If unconverged > 0 and compz == lapack.SchurHess, then on return 104 // (final Z) = U, 105 // where U is the orthogonal matrix in (*) regardless of the value of job. 106 // 107 // References: 108 // [1] R. Byers. LAPACK 3.1 xHSEQR: Tuning and Implementation Notes on the 109 // Small Bulge Multi-Shift QR Algorithm with Aggressive Early Deflation. 110 // LAPACK Working Note 187 (2007) 111 // URL: http://www.netlib.org/lapack/lawnspdf/lawn187.pdf 112 // [2] K. Braman, R. Byers, R. Mathias. The Multishift QR Algorithm. Part I: 113 // Maintaining Well-Focused Shifts and Level 3 Performance. SIAM J. Matrix 114 // Anal. Appl. 23(4) (2002), pp. 929—947 115 // URL: http://dx.doi.org/10.1137/S0895479801384573 116 // [3] K. Braman, R. Byers, R. Mathias. The Multishift QR Algorithm. Part II: 117 // Aggressive Early Deflation. SIAM J. Matrix Anal. Appl. 23(4) (2002), pp. 948—973 118 // URL: http://dx.doi.org/10.1137/S0895479801384585 119 // 120 // Dhseqr is an internal routine. It is exported for testing purposes. 121 func (impl Implementation) Dhseqr(job lapack.SchurJob, compz lapack.SchurComp, n, ilo, ihi int, h []float64, ldh int, wr, wi []float64, z []float64, ldz int, work []float64, lwork int) (unconverged int) { 122 wantt := job == lapack.EigenvaluesAndSchur 123 wantz := compz == lapack.SchurHess || compz == lapack.SchurOrig 124 125 switch { 126 case job != lapack.EigenvaluesOnly && job != lapack.EigenvaluesAndSchur: 127 panic(badSchurJob) 128 case compz != lapack.SchurNone && compz != lapack.SchurHess && compz != lapack.SchurOrig: 129 panic(badSchurComp) 130 case n < 0: 131 panic(nLT0) 132 case ilo < 0 || max(0, n-1) < ilo: 133 panic(badIlo) 134 case ihi < min(ilo, n-1) || n <= ihi: 135 panic(badIhi) 136 case ldh < max(1, n): 137 panic(badLdH) 138 case ldz < 1, wantz && ldz < n: 139 panic(badLdZ) 140 case lwork < max(1, n) && lwork != -1: 141 panic(badLWork) 142 case len(work) < max(1, lwork): 143 panic(shortWork) 144 } 145 146 // Quick return if possible. 147 if n == 0 { 148 work[0] = 1 149 return 0 150 } 151 152 // Quick return in case of a workspace query. 153 if lwork == -1 { 154 impl.Dlaqr04(wantt, wantz, n, ilo, ihi, h, ldh, wr, wi, ilo, ihi, z, ldz, work, -1, 1) 155 work[0] = math.Max(float64(n), work[0]) 156 return 0 157 } 158 159 switch { 160 case len(h) < (n-1)*ldh+n: 161 panic(shortH) 162 case wantz && len(z) < (n-1)*ldz+n: 163 panic(shortZ) 164 case len(wr) < n: 165 panic(shortWr) 166 case len(wi) < n: 167 panic(shortWi) 168 } 169 170 const ( 171 // Matrices of order ntiny or smaller must be processed by 172 // Dlahqr because of insufficient subdiagonal scratch space. 173 // This is a hard limit. 174 ntiny = 11 175 176 // nl is the size of a local workspace to help small matrices 177 // through a rare Dlahqr failure. nl > ntiny is required and 178 // nl <= nmin = Ilaenv(ispec=12,...) is recommended (the default 179 // value of nmin is 75). Using nl = 49 allows up to six 180 // simultaneous shifts and a 16×16 deflation window. 181 nl = 49 182 ) 183 184 // Copy eigenvalues isolated by Dgebal. 185 for i := 0; i < ilo; i++ { 186 wr[i] = h[i*ldh+i] 187 wi[i] = 0 188 } 189 for i := ihi + 1; i < n; i++ { 190 wr[i] = h[i*ldh+i] 191 wi[i] = 0 192 } 193 194 // Initialize Z to identity matrix if requested. 195 if compz == lapack.SchurHess { 196 impl.Dlaset(blas.All, n, n, 0, 1, z, ldz) 197 } 198 199 // Quick return if possible. 200 if ilo == ihi { 201 wr[ilo] = h[ilo*ldh+ilo] 202 wi[ilo] = 0 203 return 0 204 } 205 206 // Dlahqr/Dlaqr04 crossover point. 207 nmin := impl.Ilaenv(12, "DHSEQR", string(job)+string(compz), n, ilo, ihi, lwork) 208 nmin = max(ntiny, nmin) 209 210 if n > nmin { 211 // Dlaqr0 for big matrices. 212 unconverged = impl.Dlaqr04(wantt, wantz, n, ilo, ihi, h, ldh, wr[:ihi+1], wi[:ihi+1], 213 ilo, ihi, z, ldz, work, lwork, 1) 214 } else { 215 // Dlahqr for small matrices. 216 unconverged = impl.Dlahqr(wantt, wantz, n, ilo, ihi, h, ldh, wr[:ihi+1], wi[:ihi+1], 217 ilo, ihi, z, ldz) 218 if unconverged > 0 { 219 // A rare Dlahqr failure! Dlaqr04 sometimes succeeds 220 // when Dlahqr fails. 221 kbot := unconverged 222 if n >= nl { 223 // Larger matrices have enough subdiagonal 224 // scratch space to call Dlaqr04 directly. 225 unconverged = impl.Dlaqr04(wantt, wantz, n, ilo, kbot, h, ldh, 226 wr[:ihi+1], wi[:ihi+1], ilo, ihi, z, ldz, work, lwork, 1) 227 } else { 228 // Tiny matrices don't have enough subdiagonal 229 // scratch space to benefit from Dlaqr04. Hence, 230 // tiny matrices must be copied into a larger 231 // array before calling Dlaqr04. 232 var hl [nl * nl]float64 233 impl.Dlacpy(blas.All, n, n, h, ldh, hl[:], nl) 234 impl.Dlaset(blas.All, nl, nl-n, 0, 0, hl[n:], nl) 235 var workl [nl]float64 236 unconverged = impl.Dlaqr04(wantt, wantz, nl, ilo, kbot, hl[:], nl, 237 wr[:ihi+1], wi[:ihi+1], ilo, ihi, z, ldz, workl[:], nl, 1) 238 work[0] = workl[0] 239 if wantt || unconverged > 0 { 240 impl.Dlacpy(blas.All, n, n, hl[:], nl, h, ldh) 241 } 242 } 243 } 244 } 245 // Zero out under the first subdiagonal, if necessary. 246 if (wantt || unconverged > 0) && n > 2 { 247 impl.Dlaset(blas.Lower, n-2, n-2, 0, 0, h[2*ldh:], ldh) 248 } 249 250 work[0] = math.Max(float64(n), work[0]) 251 return unconverged 252 }