github.com/gopherd/gonum@v0.0.4/lapack/gonum/dlahqr.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/blas64" 11 ) 12 13 // Dlahqr computes the eigenvalues and Schur factorization of a block of an n×n 14 // upper Hessenberg matrix H, using the double-shift/single-shift QR algorithm. 15 // 16 // h and ldh represent the matrix H. Dlahqr works primarily with the Hessenberg 17 // submatrix H[ilo:ihi+1,ilo:ihi+1], but applies transformations to all of H if 18 // wantt is true. It is assumed that H[ihi+1:n,ihi+1:n] is already upper 19 // quasi-triangular, although this is not checked. 20 // 21 // It must hold that 22 // 0 <= ilo <= max(0,ihi), and ihi < n, 23 // and that 24 // H[ilo,ilo-1] == 0, if ilo > 0, 25 // otherwise Dlahqr will panic. 26 // 27 // If unconverged is zero on return, wr[ilo:ihi+1] and wi[ilo:ihi+1] will contain 28 // respectively the real and imaginary parts of the computed eigenvalues ilo 29 // to ihi. If two eigenvalues are computed as a complex conjugate pair, they are 30 // stored in consecutive elements of wr and wi, say the i-th and (i+1)th, with 31 // wi[i] > 0 and wi[i+1] < 0. If wantt is true, the eigenvalues are stored in 32 // the same order as on the diagonal of the Schur form returned in H, with 33 // wr[i] = H[i,i], and, if H[i:i+2,i:i+2] is a 2×2 diagonal block, 34 // wi[i] = sqrt(abs(H[i+1,i]*H[i,i+1])) and wi[i+1] = -wi[i]. 35 // 36 // wr and wi must have length ihi+1. 37 // 38 // z and ldz represent an n×n matrix Z. If wantz is true, the transformations 39 // will be applied to the submatrix Z[iloz:ihiz+1,ilo:ihi+1] and it must hold that 40 // 0 <= iloz <= ilo, and ihi <= ihiz < n. 41 // If wantz is false, z is not referenced. 42 // 43 // unconverged indicates whether Dlahqr computed all the eigenvalues ilo to ihi 44 // in a total of 30 iterations per eigenvalue. 45 // 46 // If unconverged is zero, all the eigenvalues ilo to ihi have been computed and 47 // will be stored on return in wr[ilo:ihi+1] and wi[ilo:ihi+1]. 48 // 49 // If unconverged is zero and wantt is true, H[ilo:ihi+1,ilo:ihi+1] will be 50 // overwritten on return by upper quasi-triangular full Schur form with any 51 // 2×2 diagonal blocks in standard form. 52 // 53 // If unconverged is zero and if wantt is false, the contents of h on return is 54 // unspecified. 55 // 56 // If unconverged is positive, some eigenvalues have not converged, and 57 // wr[unconverged:ihi+1] and wi[unconverged:ihi+1] contain those eigenvalues 58 // which have been successfully computed. 59 // 60 // If unconverged is positive and wantt is true, then on return 61 // (initial H)*U = U*(final H), (*) 62 // where U is an orthogonal matrix. The final H is upper Hessenberg and 63 // H[unconverged:ihi+1,unconverged:ihi+1] is upper quasi-triangular. 64 // 65 // If unconverged is positive and wantt is false, on return the remaining 66 // unconverged eigenvalues are the eigenvalues of the upper Hessenberg matrix 67 // H[ilo:unconverged,ilo:unconverged]. 68 // 69 // If unconverged is positive and wantz is true, then on return 70 // (final Z) = (initial Z)*U, 71 // where U is the orthogonal matrix in (*) regardless of the value of wantt. 72 // 73 // Dlahqr is an internal routine. It is exported for testing purposes. 74 func (impl Implementation) Dlahqr(wantt, wantz bool, n, ilo, ihi int, h []float64, ldh int, wr, wi []float64, iloz, ihiz int, z []float64, ldz int) (unconverged int) { 75 switch { 76 case n < 0: 77 panic(nLT0) 78 case ilo < 0, max(0, ihi) < ilo: 79 panic(badIlo) 80 case ihi >= n: 81 panic(badIhi) 82 case ldh < max(1, n): 83 panic(badLdH) 84 case wantz && (iloz < 0 || ilo < iloz): 85 panic(badIloz) 86 case wantz && (ihiz < ihi || n <= ihiz): 87 panic(badIhiz) 88 case ldz < 1, wantz && ldz < n: 89 panic(badLdZ) 90 } 91 92 // Quick return if possible. 93 if n == 0 { 94 return 0 95 } 96 97 switch { 98 case len(h) < (n-1)*ldh+n: 99 panic(shortH) 100 case len(wr) != ihi+1: 101 panic(shortWr) 102 case len(wi) != ihi+1: 103 panic(shortWi) 104 case wantz && len(z) < (n-1)*ldz+n: 105 panic(shortZ) 106 case ilo > 0 && h[ilo*ldh+ilo-1] != 0: 107 panic(notIsolated) 108 } 109 110 if ilo == ihi { 111 wr[ilo] = h[ilo*ldh+ilo] 112 wi[ilo] = 0 113 return 0 114 } 115 116 // Clear out the trash. 117 for j := ilo; j < ihi-2; j++ { 118 h[(j+2)*ldh+j] = 0 119 h[(j+3)*ldh+j] = 0 120 } 121 if ilo <= ihi-2 { 122 h[ihi*ldh+ihi-2] = 0 123 } 124 125 nh := ihi - ilo + 1 126 nz := ihiz - iloz + 1 127 128 // Set machine-dependent constants for the stopping criterion. 129 ulp := dlamchP 130 smlnum := float64(nh) / ulp * dlamchS 131 132 // i1 and i2 are the indices of the first row and last column of H to 133 // which transformations must be applied. If eigenvalues only are being 134 // computed, i1 and i2 are set inside the main loop. 135 var i1, i2 int 136 if wantt { 137 i1 = 0 138 i2 = n - 1 139 } 140 141 itmax := 30 * max(10, nh) // Total number of QR iterations allowed. 142 143 // The main loop begins here. i is the loop index and decreases from ihi 144 // to ilo in steps of 1 or 2. Each iteration of the loop works with the 145 // active submatrix in rows and columns l to i. Eigenvalues i+1 to ihi 146 // have already converged. Either l = ilo or H[l,l-1] is negligible so 147 // that the matrix splits. 148 bi := blas64.Implementation() 149 i := ihi 150 for i >= ilo { 151 l := ilo 152 153 // Perform QR iterations on rows and columns ilo to i until a 154 // submatrix of order 1 or 2 splits off at the bottom because a 155 // subdiagonal element has become negligible. 156 converged := false 157 for its := 0; its <= itmax; its++ { 158 // Look for a single small subdiagonal element. 159 var k int 160 for k = i; k > l; k-- { 161 if math.Abs(h[k*ldh+k-1]) <= smlnum { 162 break 163 } 164 tst := math.Abs(h[(k-1)*ldh+k-1]) + math.Abs(h[k*ldh+k]) 165 if tst == 0 { 166 if k-2 >= ilo { 167 tst += math.Abs(h[(k-1)*ldh+k-2]) 168 } 169 if k+1 <= ihi { 170 tst += math.Abs(h[(k+1)*ldh+k]) 171 } 172 } 173 // The following is a conservative small 174 // subdiagonal deflation criterion due to Ahues 175 // & Tisseur (LAWN 122, 1997). It has better 176 // mathematical foundation and improves accuracy 177 // in some cases. 178 if math.Abs(h[k*ldh+k-1]) <= ulp*tst { 179 ab := math.Max(math.Abs(h[k*ldh+k-1]), math.Abs(h[(k-1)*ldh+k])) 180 ba := math.Min(math.Abs(h[k*ldh+k-1]), math.Abs(h[(k-1)*ldh+k])) 181 aa := math.Max(math.Abs(h[k*ldh+k]), math.Abs(h[(k-1)*ldh+k-1]-h[k*ldh+k])) 182 bb := math.Min(math.Abs(h[k*ldh+k]), math.Abs(h[(k-1)*ldh+k-1]-h[k*ldh+k])) 183 s := aa + ab 184 if ab/s*ba <= math.Max(smlnum, aa/s*bb*ulp) { 185 break 186 } 187 } 188 } 189 l = k 190 if l > ilo { 191 // H[l,l-1] is negligible. 192 h[l*ldh+l-1] = 0 193 } 194 if l >= i-1 { 195 // Break the loop because a submatrix of order 1 196 // or 2 has split off. 197 converged = true 198 break 199 } 200 201 // Now the active submatrix is in rows and columns l to 202 // i. If eigenvalues only are being computed, only the 203 // active submatrix need be transformed. 204 if !wantt { 205 i1 = l 206 i2 = i 207 } 208 209 const ( 210 dat1 = 3.0 211 dat2 = -0.4375 212 ) 213 var h11, h21, h12, h22 float64 214 switch its { 215 case 10: // Exceptional shift. 216 s := math.Abs(h[(l+1)*ldh+l]) + math.Abs(h[(l+2)*ldh+l+1]) 217 h11 = dat1*s + h[l*ldh+l] 218 h12 = dat2 * s 219 h21 = s 220 h22 = h11 221 case 20: // Exceptional shift. 222 s := math.Abs(h[i*ldh+i-1]) + math.Abs(h[(i-1)*ldh+i-2]) 223 h11 = dat1*s + h[i*ldh+i] 224 h12 = dat2 * s 225 h21 = s 226 h22 = h11 227 default: // Prepare to use Francis' double shift (i.e., 228 // 2nd degree generalized Rayleigh quotient). 229 h11 = h[(i-1)*ldh+i-1] 230 h21 = h[i*ldh+i-1] 231 h12 = h[(i-1)*ldh+i] 232 h22 = h[i*ldh+i] 233 } 234 s := math.Abs(h11) + math.Abs(h12) + math.Abs(h21) + math.Abs(h22) 235 var ( 236 rt1r, rt1i float64 237 rt2r, rt2i float64 238 ) 239 if s != 0 { 240 h11 /= s 241 h21 /= s 242 h12 /= s 243 h22 /= s 244 tr := (h11 + h22) / 2 245 det := (h11-tr)*(h22-tr) - h12*h21 246 rtdisc := math.Sqrt(math.Abs(det)) 247 if det >= 0 { 248 // Complex conjugate shifts. 249 rt1r = tr * s 250 rt2r = rt1r 251 rt1i = rtdisc * s 252 rt2i = -rt1i 253 } else { 254 // Real shifts (use only one of them). 255 rt1r = tr + rtdisc 256 rt2r = tr - rtdisc 257 if math.Abs(rt1r-h22) <= math.Abs(rt2r-h22) { 258 rt1r *= s 259 rt2r = rt1r 260 } else { 261 rt2r *= s 262 rt1r = rt2r 263 } 264 rt1i = 0 265 rt2i = 0 266 } 267 } 268 269 // Look for two consecutive small subdiagonal elements. 270 var m int 271 var v [3]float64 272 for m = i - 2; m >= l; m-- { 273 // Determine the effect of starting the 274 // double-shift QR iteration at row m, and see 275 // if this would make H[m,m-1] negligible. The 276 // following uses scaling to avoid overflows and 277 // most underflows. 278 h21s := h[(m+1)*ldh+m] 279 s := math.Abs(h[m*ldh+m]-rt2r) + math.Abs(rt2i) + math.Abs(h21s) 280 h21s /= s 281 v[0] = h21s*h[m*ldh+m+1] + (h[m*ldh+m]-rt1r)*((h[m*ldh+m]-rt2r)/s) - rt2i/s*rt1i 282 v[1] = h21s * (h[m*ldh+m] + h[(m+1)*ldh+m+1] - rt1r - rt2r) 283 v[2] = h21s * h[(m+2)*ldh+m+1] 284 s = math.Abs(v[0]) + math.Abs(v[1]) + math.Abs(v[2]) 285 v[0] /= s 286 v[1] /= s 287 v[2] /= s 288 if m == l { 289 break 290 } 291 dsum := math.Abs(h[(m-1)*ldh+m-1]) + math.Abs(h[m*ldh+m]) + math.Abs(h[(m+1)*ldh+m+1]) 292 if math.Abs(h[m*ldh+m-1])*(math.Abs(v[1])+math.Abs(v[2])) <= ulp*math.Abs(v[0])*dsum { 293 break 294 } 295 } 296 297 // Double-shift QR step. 298 for k := m; k < i; k++ { 299 // The first iteration of this loop determines a 300 // reflection G from the vector V and applies it 301 // from left and right to H, thus creating a 302 // non-zero bulge below the subdiagonal. 303 // 304 // Each subsequent iteration determines a 305 // reflection G to restore the Hessenberg form 306 // in the (k-1)th column, and thus chases the 307 // bulge one step toward the bottom of the 308 // active submatrix. nr is the order of G. 309 310 nr := min(3, i-k+1) 311 if k > m { 312 bi.Dcopy(nr, h[k*ldh+k-1:], ldh, v[:], 1) 313 } 314 var t0 float64 315 v[0], t0 = impl.Dlarfg(nr, v[0], v[1:], 1) 316 if k > m { 317 h[k*ldh+k-1] = v[0] 318 h[(k+1)*ldh+k-1] = 0 319 if k < i-1 { 320 h[(k+2)*ldh+k-1] = 0 321 } 322 } else if m > l { 323 // Use the following instead of H[k,k-1] = -H[k,k-1] 324 // to avoid a bug when v[1] and v[2] underflow. 325 h[k*ldh+k-1] *= 1 - t0 326 } 327 t1 := t0 * v[1] 328 if nr == 3 { 329 t2 := t0 * v[2] 330 331 // Apply G from the left to transform 332 // the rows of the matrix in columns k 333 // to i2. 334 for j := k; j <= i2; j++ { 335 sum := h[k*ldh+j] + v[1]*h[(k+1)*ldh+j] + v[2]*h[(k+2)*ldh+j] 336 h[k*ldh+j] -= sum * t0 337 h[(k+1)*ldh+j] -= sum * t1 338 h[(k+2)*ldh+j] -= sum * t2 339 } 340 341 // Apply G from the right to transform 342 // the columns of the matrix in rows i1 343 // to min(k+3,i). 344 for j := i1; j <= min(k+3, i); j++ { 345 sum := h[j*ldh+k] + v[1]*h[j*ldh+k+1] + v[2]*h[j*ldh+k+2] 346 h[j*ldh+k] -= sum * t0 347 h[j*ldh+k+1] -= sum * t1 348 h[j*ldh+k+2] -= sum * t2 349 } 350 351 if wantz { 352 // Accumulate transformations in the matrix Z. 353 for j := iloz; j <= ihiz; j++ { 354 sum := z[j*ldz+k] + v[1]*z[j*ldz+k+1] + v[2]*z[j*ldz+k+2] 355 z[j*ldz+k] -= sum * t0 356 z[j*ldz+k+1] -= sum * t1 357 z[j*ldz+k+2] -= sum * t2 358 } 359 } 360 } else if nr == 2 { 361 // Apply G from the left to transform 362 // the rows of the matrix in columns k 363 // to i2. 364 for j := k; j <= i2; j++ { 365 sum := h[k*ldh+j] + v[1]*h[(k+1)*ldh+j] 366 h[k*ldh+j] -= sum * t0 367 h[(k+1)*ldh+j] -= sum * t1 368 } 369 370 // Apply G from the right to transform 371 // the columns of the matrix in rows i1 372 // to min(k+3,i). 373 for j := i1; j <= i; j++ { 374 sum := h[j*ldh+k] + v[1]*h[j*ldh+k+1] 375 h[j*ldh+k] -= sum * t0 376 h[j*ldh+k+1] -= sum * t1 377 } 378 379 if wantz { 380 // Accumulate transformations in the matrix Z. 381 for j := iloz; j <= ihiz; j++ { 382 sum := z[j*ldz+k] + v[1]*z[j*ldz+k+1] 383 z[j*ldz+k] -= sum * t0 384 z[j*ldz+k+1] -= sum * t1 385 } 386 } 387 } 388 } 389 } 390 391 if !converged { 392 // The QR iteration finished without splitting off a 393 // submatrix of order 1 or 2. 394 return i + 1 395 } 396 397 if l == i { 398 // H[i,i-1] is negligible: one eigenvalue has converged. 399 wr[i] = h[i*ldh+i] 400 wi[i] = 0 401 } else if l == i-1 { 402 // H[i-1,i-2] is negligible: a pair of eigenvalues have converged. 403 404 // Transform the 2×2 submatrix to standard Schur form, 405 // and compute and store the eigenvalues. 406 var cs, sn float64 407 a, b := h[(i-1)*ldh+i-1], h[(i-1)*ldh+i] 408 c, d := h[i*ldh+i-1], h[i*ldh+i] 409 a, b, c, d, wr[i-1], wi[i-1], wr[i], wi[i], cs, sn = impl.Dlanv2(a, b, c, d) 410 h[(i-1)*ldh+i-1], h[(i-1)*ldh+i] = a, b 411 h[i*ldh+i-1], h[i*ldh+i] = c, d 412 413 if wantt { 414 // Apply the transformation to the rest of H. 415 if i2 > i { 416 bi.Drot(i2-i, h[(i-1)*ldh+i+1:], 1, h[i*ldh+i+1:], 1, cs, sn) 417 } 418 bi.Drot(i-i1-1, h[i1*ldh+i-1:], ldh, h[i1*ldh+i:], ldh, cs, sn) 419 } 420 421 if wantz { 422 // Apply the transformation to Z. 423 bi.Drot(nz, z[iloz*ldz+i-1:], ldz, z[iloz*ldz+i:], ldz, cs, sn) 424 } 425 } 426 427 // Return to start of the main loop with new value of i. 428 i = l - 1 429 } 430 return 0 431 }