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