github.com/gonum/lapack@v0.0.0-20181123203213-e4cdc5a0bff9/native/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 native 6 7 import ( 8 "math" 9 10 "github.com/gonum/blas" 11 "github.com/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^T, 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^T = (QZ) T (QZ)^T. 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.None, no Schur vectors will be computed and Z will not be 31 // referenced. 32 // If compz == lapack.HessEV, on return Z will contain the matrix of Schur 33 // vectors of H. 34 // If compz == lapack.OriginalEV, 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.OriginalEV, 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.HessEV, 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.EVJob, compz lapack.EVComp, n, ilo, ihi int, h []float64, ldh int, wr, wi []float64, z []float64, ldz int, work []float64, lwork int) (unconverged int) { 122 var wantt bool 123 switch job { 124 default: 125 panic(badEVJob) 126 case lapack.EigenvaluesOnly: 127 case lapack.EigenvaluesAndSchur: 128 wantt = true 129 } 130 var wantz bool 131 switch compz { 132 default: 133 panic(badEVComp) 134 case lapack.None: 135 case lapack.HessEV, lapack.OriginalEV: 136 wantz = true 137 } 138 switch { 139 case n < 0: 140 panic(nLT0) 141 case ilo < 0 || max(0, n-1) < ilo: 142 panic(badIlo) 143 case ihi < min(ilo, n-1) || n <= ihi: 144 panic(badIhi) 145 case len(work) < lwork: 146 panic(shortWork) 147 case lwork < max(1, n) && lwork != -1: 148 panic(badWork) 149 } 150 if lwork != -1 { 151 checkMatrix(n, n, h, ldh) 152 switch { 153 case wantz: 154 checkMatrix(n, n, z, ldz) 155 case len(wr) < n: 156 panic("lapack: wr has insufficient length") 157 case len(wi) < n: 158 panic("lapack: wi has insufficient length") 159 } 160 } 161 162 const ( 163 // Matrices of order ntiny or smaller must be processed by 164 // Dlahqr because of insufficient subdiagonal scratch space. 165 // This is a hard limit. 166 ntiny = 11 167 168 // nl is the size of a local workspace to help small matrices 169 // through a rare Dlahqr failure. nl > ntiny is required and 170 // nl <= nmin = Ilaenv(ispec=12,...) is recommended (the default 171 // value of nmin is 75). Using nl = 49 allows up to six 172 // simultaneous shifts and a 16×16 deflation window. 173 nl = 49 174 ) 175 176 // Quick return if possible. 177 if n == 0 { 178 work[0] = 1 179 return 0 180 } 181 182 // Quick return in case of a workspace query. 183 if lwork == -1 { 184 impl.Dlaqr04(wantt, wantz, n, ilo, ihi, nil, 0, nil, nil, ilo, ihi, nil, 0, work, -1, 1) 185 work[0] = math.Max(float64(n), work[0]) 186 return 0 187 } 188 189 // Copy eigenvalues isolated by Dgebal. 190 for i := 0; i < ilo; i++ { 191 wr[i] = h[i*ldh+i] 192 wi[i] = 0 193 } 194 for i := ihi + 1; i < n; i++ { 195 wr[i] = h[i*ldh+i] 196 wi[i] = 0 197 } 198 199 // Initialize Z to identity matrix if requested. 200 if compz == lapack.HessEV { 201 impl.Dlaset(blas.All, n, n, 0, 1, z, ldz) 202 } 203 204 // Quick return if possible. 205 if ilo == ihi { 206 wr[ilo] = h[ilo*ldh+ilo] 207 wi[ilo] = 0 208 return 0 209 } 210 211 // Dlahqr/Dlaqr04 crossover point. 212 nmin := impl.Ilaenv(12, "DHSEQR", string(job)+string(compz), n, ilo, ihi, lwork) 213 nmin = max(ntiny, nmin) 214 215 if n > nmin { 216 // Dlaqr0 for big matrices. 217 unconverged = impl.Dlaqr04(wantt, wantz, n, ilo, ihi, h, ldh, wr[:ihi+1], wi[:ihi+1], 218 ilo, ihi, z, ldz, work, lwork, 1) 219 } else { 220 // Dlahqr for small matrices. 221 unconverged = impl.Dlahqr(wantt, wantz, n, ilo, ihi, h, ldh, wr[:ihi+1], wi[:ihi+1], 222 ilo, ihi, z, ldz) 223 if unconverged > 0 { 224 // A rare Dlahqr failure! Dlaqr04 sometimes succeeds 225 // when Dlahqr fails. 226 kbot := unconverged 227 if n >= nl { 228 // Larger matrices have enough subdiagonal 229 // scratch space to call Dlaqr04 directly. 230 unconverged = impl.Dlaqr04(wantt, wantz, n, ilo, kbot, h, ldh, 231 wr[:ihi+1], wi[:ihi+1], ilo, ihi, z, ldz, work, lwork, 1) 232 } else { 233 // Tiny matrices don't have enough subdiagonal 234 // scratch space to benefit from Dlaqr04. Hence, 235 // tiny matrices must be copied into a larger 236 // array before calling Dlaqr04. 237 var hl [nl * nl]float64 238 impl.Dlacpy(blas.All, n, n, h, ldh, hl[:], nl) 239 impl.Dlaset(blas.All, nl, nl-n, 0, 0, hl[n:], nl) 240 var workl [nl]float64 241 unconverged = impl.Dlaqr04(wantt, wantz, nl, ilo, kbot, hl[:], nl, 242 wr[:ihi+1], wi[:ihi+1], ilo, ihi, z, ldz, workl[:], nl, 1) 243 work[0] = workl[0] 244 if wantt || unconverged > 0 { 245 impl.Dlacpy(blas.All, n, n, hl[:], nl, h, ldh) 246 } 247 } 248 } 249 } 250 // Zero out under the first subdiagonal, if necessary. 251 if (wantt || unconverged > 0) && n > 2 { 252 impl.Dlaset(blas.Lower, n-2, n-2, 0, 0, h[2*ldh:], ldh) 253 } 254 255 work[0] = math.Max(float64(n), work[0]) 256 return unconverged 257 }