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