github.com/gonum/lapack@v0.0.0-20181123203213-e4cdc5a0bff9/native/dsteqr.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/blas/blas64" 12 "github.com/gonum/lapack" 13 ) 14 15 // Dsteqr computes the eigenvalues and optionally the eigenvectors of a symmetric 16 // tridiagonal matrix using the implicit QL or QR method. The eigenvectors of a 17 // full or band symmetric matrix can also be found if Dsytrd, Dsptrd, or Dsbtrd 18 // have been used to reduce this matrix to tridiagonal form. 19 // 20 // d, on entry, contains the diagonal elements of the tridiagonal matrix. On exit, 21 // d contains the eigenvalues in ascending order. d must have length n and 22 // Dsteqr will panic otherwise. 23 // 24 // e, on entry, contains the off-diagonal elements of the tridiagonal matrix on 25 // entry, and is overwritten during the call to Dsteqr. e must have length n-1 and 26 // Dsteqr will panic otherwise. 27 // 28 // z, on entry, contains the n×n orthogonal matrix used in the reduction to 29 // tridiagonal form if compz == lapack.OriginalEV. On exit, if 30 // compz == lapack.OriginalEV, z contains the orthonormal eigenvectors of the 31 // original symmetric matrix, and if compz == lapack.TridiagEV, z contains the 32 // orthonormal eigenvectors of the symmetric tridiagonal matrix. z is not used 33 // if compz == lapack.None. 34 // 35 // work must have length at least max(1, 2*n-2) if the eigenvectors are computed, 36 // and Dsteqr will panic otherwise. 37 // 38 // Dsteqr is an internal routine. It is exported for testing purposes. 39 func (impl Implementation) Dsteqr(compz lapack.EVComp, n int, d, e, z []float64, ldz int, work []float64) (ok bool) { 40 if n < 0 { 41 panic(nLT0) 42 } 43 if len(d) < n { 44 panic(badD) 45 } 46 if len(e) < n-1 { 47 panic(badE) 48 } 49 if compz != lapack.None && compz != lapack.TridiagEV && compz != lapack.OriginalEV { 50 panic(badEVComp) 51 } 52 if compz != lapack.None { 53 if len(work) < max(1, 2*n-2) { 54 panic(badWork) 55 } 56 checkMatrix(n, n, z, ldz) 57 } 58 59 var icompz int 60 if compz == lapack.OriginalEV { 61 icompz = 1 62 } else if compz == lapack.TridiagEV { 63 icompz = 2 64 } 65 66 if n == 0 { 67 return true 68 } 69 if n == 1 { 70 if icompz == 2 { 71 z[0] = 1 72 } 73 return true 74 } 75 76 bi := blas64.Implementation() 77 78 eps := dlamchE 79 eps2 := eps * eps 80 safmin := dlamchS 81 safmax := 1 / safmin 82 ssfmax := math.Sqrt(safmax) / 3 83 ssfmin := math.Sqrt(safmin) / eps2 84 85 // Compute the eigenvalues and eigenvectors of the tridiagonal matrix. 86 if icompz == 2 { 87 impl.Dlaset(blas.All, n, n, 0, 1, z, ldz) 88 } 89 const maxit = 30 90 nmaxit := n * maxit 91 92 jtot := 0 93 94 // Determine where the matrix splits and choose QL or QR iteration for each 95 // block, according to whether top or bottom diagonal element is smaller. 96 l1 := 0 97 nm1 := n - 1 98 99 type scaletype int 100 const ( 101 none scaletype = iota 102 down 103 up 104 ) 105 var iscale scaletype 106 107 for { 108 if l1 > n-1 { 109 // Order eigenvalues and eigenvectors. 110 if icompz == 0 { 111 impl.Dlasrt(lapack.SortIncreasing, n, d) 112 } else { 113 // TODO(btracey): Consider replacing this sort with a call to sort.Sort. 114 for ii := 1; ii < n; ii++ { 115 i := ii - 1 116 k := i 117 p := d[i] 118 for j := ii; j < n; j++ { 119 if d[j] < p { 120 k = j 121 p = d[j] 122 } 123 } 124 if k != i { 125 d[k] = d[i] 126 d[i] = p 127 bi.Dswap(n, z[i:], ldz, z[k:], ldz) 128 } 129 } 130 } 131 return true 132 } 133 if l1 > 0 { 134 e[l1-1] = 0 135 } 136 var m int 137 if l1 <= nm1 { 138 for m = l1; m < nm1; m++ { 139 test := math.Abs(e[m]) 140 if test == 0 { 141 break 142 } 143 if test <= (math.Sqrt(math.Abs(d[m]))*math.Sqrt(math.Abs(d[m+1])))*eps { 144 e[m] = 0 145 break 146 } 147 } 148 } 149 l := l1 150 lsv := l 151 lend := m 152 lendsv := lend 153 l1 = m + 1 154 if lend == l { 155 continue 156 } 157 158 // Scale submatrix in rows and columns L to Lend 159 anorm := impl.Dlanst(lapack.MaxAbs, lend-l+1, d[l:], e[l:]) 160 switch { 161 case anorm == 0: 162 continue 163 case anorm > ssfmax: 164 iscale = down 165 // Pretend that d and e are matrices with 1 column. 166 impl.Dlascl(lapack.General, 0, 0, anorm, ssfmax, lend-l+1, 1, d[l:], 1) 167 impl.Dlascl(lapack.General, 0, 0, anorm, ssfmax, lend-l, 1, e[l:], 1) 168 case anorm < ssfmin: 169 iscale = up 170 impl.Dlascl(lapack.General, 0, 0, anorm, ssfmin, lend-l+1, 1, d[l:], 1) 171 impl.Dlascl(lapack.General, 0, 0, anorm, ssfmin, lend-l, 1, e[l:], 1) 172 } 173 174 // Choose between QL and QR. 175 if math.Abs(d[lend]) < math.Abs(d[l]) { 176 lend = lsv 177 l = lendsv 178 } 179 if lend > l { 180 // QL Iteration. Look for small subdiagonal element. 181 for { 182 if l != lend { 183 for m = l; m < lend; m++ { 184 v := math.Abs(e[m]) 185 if v*v <= (eps2*math.Abs(d[m]))*math.Abs(d[m+1])+safmin { 186 break 187 } 188 } 189 } else { 190 m = lend 191 } 192 if m < lend { 193 e[m] = 0 194 } 195 p := d[l] 196 if m == l { 197 // Eigenvalue found. 198 l++ 199 if l > lend { 200 break 201 } 202 continue 203 } 204 205 // If remaining matrix is 2×2, use Dlae2 to compute its eigensystem. 206 if m == l+1 { 207 if icompz > 0 { 208 d[l], d[l+1], work[l], work[n-1+l] = impl.Dlaev2(d[l], e[l], d[l+1]) 209 impl.Dlasr(blas.Right, lapack.Variable, lapack.Backward, 210 n, 2, work[l:], work[n-1+l:], z[l:], ldz) 211 } else { 212 d[l], d[l+1] = impl.Dlae2(d[l], e[l], d[l+1]) 213 } 214 e[l] = 0 215 l += 2 216 if l > lend { 217 break 218 } 219 continue 220 } 221 222 if jtot == nmaxit { 223 break 224 } 225 jtot++ 226 227 // Form shift 228 g := (d[l+1] - p) / (2 * e[l]) 229 r := impl.Dlapy2(g, 1) 230 g = d[m] - p + e[l]/(g+math.Copysign(r, g)) 231 s := 1.0 232 c := 1.0 233 p = 0.0 234 235 // Inner loop 236 for i := m - 1; i >= l; i-- { 237 f := s * e[i] 238 b := c * e[i] 239 c, s, r = impl.Dlartg(g, f) 240 if i != m-1 { 241 e[i+1] = r 242 } 243 g = d[i+1] - p 244 r = (d[i]-g)*s + 2*c*b 245 p = s * r 246 d[i+1] = g + p 247 g = c*r - b 248 249 // If eigenvectors are desired, then save rotations. 250 if icompz > 0 { 251 work[i] = c 252 work[n-1+i] = -s 253 } 254 } 255 // If eigenvectors are desired, then apply saved rotations. 256 if icompz > 0 { 257 mm := m - l + 1 258 impl.Dlasr(blas.Right, lapack.Variable, lapack.Backward, 259 n, mm, work[l:], work[n-1+l:], z[l:], ldz) 260 } 261 d[l] -= p 262 e[l] = g 263 } 264 } else { 265 // QR Iteration. 266 // Look for small superdiagonal element. 267 for { 268 if l != lend { 269 for m = l; m > lend; m-- { 270 v := math.Abs(e[m-1]) 271 if v*v <= (eps2*math.Abs(d[m])*math.Abs(d[m-1]) + safmin) { 272 break 273 } 274 } 275 } else { 276 m = lend 277 } 278 if m > lend { 279 e[m-1] = 0 280 } 281 p := d[l] 282 if m == l { 283 // Eigenvalue found 284 l-- 285 if l < lend { 286 break 287 } 288 continue 289 } 290 291 // If remaining matrix is 2×2, use Dlae2 to compute its eigenvalues. 292 if m == l-1 { 293 if icompz > 0 { 294 d[l-1], d[l], work[m], work[n-1+m] = impl.Dlaev2(d[l-1], e[l-1], d[l]) 295 impl.Dlasr(blas.Right, lapack.Variable, lapack.Forward, 296 n, 2, work[m:], work[n-1+m:], z[l-1:], ldz) 297 } else { 298 d[l-1], d[l] = impl.Dlae2(d[l-1], e[l-1], d[l]) 299 } 300 e[l-1] = 0 301 l -= 2 302 if l < lend { 303 break 304 } 305 continue 306 } 307 if jtot == nmaxit { 308 break 309 } 310 jtot++ 311 312 // Form shift. 313 g := (d[l-1] - p) / (2 * e[l-1]) 314 r := impl.Dlapy2(g, 1) 315 g = d[m] - p + (e[l-1])/(g+math.Copysign(r, g)) 316 s := 1.0 317 c := 1.0 318 p = 0.0 319 320 // Inner loop. 321 for i := m; i < l; i++ { 322 f := s * e[i] 323 b := c * e[i] 324 c, s, r = impl.Dlartg(g, f) 325 if i != m { 326 e[i-1] = r 327 } 328 g = d[i] - p 329 r = (d[i+1]-g)*s + 2*c*b 330 p = s * r 331 d[i] = g + p 332 g = c*r - b 333 334 // If eigenvectors are desired, then save rotations. 335 if icompz > 0 { 336 work[i] = c 337 work[n-1+i] = s 338 } 339 } 340 341 // If eigenvectors are desired, then apply saved rotations. 342 if icompz > 0 { 343 mm := l - m + 1 344 impl.Dlasr(blas.Right, lapack.Variable, lapack.Forward, 345 n, mm, work[m:], work[n-1+m:], z[m:], ldz) 346 } 347 d[l] -= p 348 e[l-1] = g 349 } 350 } 351 352 // Undo scaling if necessary. 353 switch iscale { 354 case down: 355 // Pretend that d and e are matrices with 1 column. 356 impl.Dlascl(lapack.General, 0, 0, ssfmax, anorm, lendsv-lsv+1, 1, d[lsv:], 1) 357 impl.Dlascl(lapack.General, 0, 0, ssfmax, anorm, lendsv-lsv, 1, e[lsv:], 1) 358 case up: 359 impl.Dlascl(lapack.General, 0, 0, ssfmin, anorm, lendsv-lsv+1, 1, d[lsv:], 1) 360 impl.Dlascl(lapack.General, 0, 0, ssfmin, anorm, lendsv-lsv, 1, e[lsv:], 1) 361 } 362 363 // Check for no convergence to an eigenvalue after a total of n*maxit iterations. 364 if jtot >= nmaxit { 365 break 366 } 367 } 368 for i := 0; i < n-1; i++ { 369 if e[i] != 0 { 370 return false 371 } 372 } 373 return true 374 }