github.com/gonum/lapack@v0.0.0-20181123203213-e4cdc5a0bff9/native/dsterf.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/lapack" 11 ) 12 13 // Dsterf computes all eigenvalues of a symmetric tridiagonal matrix using the 14 // Pal-Walker-Kahan variant of the QL or QR algorithm. 15 // 16 // d contains the diagonal elements of the tridiagonal matrix on entry, and 17 // contains the eigenvalues in ascending order on exit. d must have length at 18 // least n, or Dsterf will panic. 19 // 20 // e contains the off-diagonal elements of the tridiagonal matrix on entry, and is 21 // overwritten during the call to Dsterf. e must have length of at least n-1 or 22 // Dsterf will panic. 23 // 24 // Dsterf is an internal routine. It is exported for testing purposes. 25 func (impl Implementation) Dsterf(n int, d, e []float64) (ok bool) { 26 if n < 0 { 27 panic(nLT0) 28 } 29 if n == 0 { 30 return true 31 } 32 if len(d) < n { 33 panic(badD) 34 } 35 if len(e) < n-1 { 36 panic(badE) 37 } 38 39 const ( 40 none = 0 // The values are not scaled. 41 down = 1 // The values are scaled below ssfmax threshold. 42 up = 2 // The values are scaled below ssfmin threshold. 43 ) 44 45 // Determine the unit roundoff for this environment. 46 eps := dlamchE 47 eps2 := eps * eps 48 safmin := dlamchS 49 safmax := 1 / safmin 50 ssfmax := math.Sqrt(safmax) / 3 51 ssfmin := math.Sqrt(safmin) / eps2 52 53 // Compute the eigenvalues of the tridiagonal matrix. 54 maxit := 30 55 nmaxit := n * maxit 56 jtot := 0 57 58 l1 := 0 59 60 for { 61 if l1 > n-1 { 62 impl.Dlasrt(lapack.SortIncreasing, n, d) 63 return true 64 } 65 if l1 > 0 { 66 e[l1-1] = 0 67 } 68 var m int 69 for m = l1; m < n-1; m++ { 70 if math.Abs(e[m]) <= math.Sqrt(math.Abs(d[m]))*math.Sqrt(math.Abs(d[m+1]))*eps { 71 e[m] = 0 72 break 73 } 74 } 75 76 l := l1 77 lsv := l 78 lend := m 79 lendsv := lend 80 l1 = m + 1 81 if lend == 0 { 82 continue 83 } 84 85 // Scale submatrix in rows and columns l to lend. 86 anorm := impl.Dlanst(lapack.MaxAbs, lend-l+1, d[l:], e[l:]) 87 iscale := none 88 if anorm == 0 { 89 continue 90 } 91 if anorm > ssfmax { 92 iscale = down 93 impl.Dlascl(lapack.General, 0, 0, anorm, ssfmax, lend-l+1, 1, d[l:], n) 94 impl.Dlascl(lapack.General, 0, 0, anorm, ssfmax, lend-l, 1, e[l:], n) 95 } else if anorm < ssfmin { 96 iscale = up 97 impl.Dlascl(lapack.General, 0, 0, anorm, ssfmin, lend-l+1, 1, d[l:], n) 98 impl.Dlascl(lapack.General, 0, 0, anorm, ssfmin, lend-l, 1, e[l:], n) 99 } 100 101 el := e[l:lend] 102 for i, v := range el { 103 el[i] *= v 104 } 105 106 // Choose between QL and QR iteration. 107 if math.Abs(d[lend]) < math.Abs(d[l]) { 108 lend = lsv 109 l = lendsv 110 } 111 if lend >= l { 112 // QL Iteration. 113 // Look for small sub-diagonal element. 114 for { 115 if l != lend { 116 for m = l; m < lend; m++ { 117 if math.Abs(e[m]) <= eps2*(math.Abs(d[m]*d[m+1])) { 118 break 119 } 120 } 121 } else { 122 m = lend 123 } 124 if m < lend { 125 e[m] = 0 126 } 127 p := d[l] 128 if m == l { 129 // Eigenvalue found. 130 l++ 131 if l > lend { 132 break 133 } 134 continue 135 } 136 // If remaining matrix is 2 by 2, use Dlae2 to compute its eigenvalues. 137 if m == l+1 { 138 d[l], d[l+1] = impl.Dlae2(d[l], math.Sqrt(e[l]), d[l+1]) 139 e[l] = 0 140 l += 2 141 if l > lend { 142 break 143 } 144 continue 145 } 146 if jtot == nmaxit { 147 break 148 } 149 jtot++ 150 151 // Form shift. 152 rte := math.Sqrt(e[l]) 153 sigma := (d[l+1] - p) / (2 * rte) 154 r := impl.Dlapy2(sigma, 1) 155 sigma = p - (rte / (sigma + math.Copysign(r, sigma))) 156 157 c := 1.0 158 s := 0.0 159 gamma := d[m] - sigma 160 p = gamma * gamma 161 162 // Inner loop. 163 for i := m - 1; i >= l; i-- { 164 bb := e[i] 165 r := p + bb 166 if i != m-1 { 167 e[i+1] = s * r 168 } 169 oldc := c 170 c = p / r 171 s = bb / r 172 oldgam := gamma 173 alpha := d[i] 174 gamma = c*(alpha-sigma) - s*oldgam 175 d[i+1] = oldgam + (alpha - gamma) 176 if c != 0 { 177 p = (gamma * gamma) / c 178 } else { 179 p = oldc * bb 180 } 181 } 182 e[l] = s * p 183 d[l] = sigma + gamma 184 } 185 } else { 186 for { 187 // QR Iteration. 188 // Look for small super-diagonal element. 189 for m = l; m > lend; m-- { 190 if math.Abs(e[m-1]) <= eps2*math.Abs(d[m]*d[m-1]) { 191 break 192 } 193 } 194 if m > lend { 195 e[m-1] = 0 196 } 197 p := d[l] 198 if m == l { 199 // Eigenvalue found. 200 l-- 201 if l < lend { 202 break 203 } 204 continue 205 } 206 207 // If remaining matrix is 2 by 2, use Dlae2 to compute its eigenvalues. 208 if m == l-1 { 209 d[l], d[l-1] = impl.Dlae2(d[l], math.Sqrt(e[l-1]), d[l-1]) 210 e[l-1] = 0 211 l -= 2 212 if l < lend { 213 break 214 } 215 continue 216 } 217 if jtot == nmaxit { 218 break 219 } 220 jtot++ 221 222 // Form shift. 223 rte := math.Sqrt(e[l-1]) 224 sigma := (d[l-1] - p) / (2 * rte) 225 r := impl.Dlapy2(sigma, 1) 226 sigma = p - (rte / (sigma + math.Copysign(r, sigma))) 227 228 c := 1.0 229 s := 0.0 230 gamma := d[m] - sigma 231 p = gamma * gamma 232 233 // Inner loop. 234 for i := m; i < l; i++ { 235 bb := e[i] 236 r := p + bb 237 if i != m { 238 e[i-1] = s * r 239 } 240 oldc := c 241 c = p / r 242 s = bb / r 243 oldgam := gamma 244 alpha := d[i+1] 245 gamma = c*(alpha-sigma) - s*oldgam 246 d[i] = oldgam + alpha - gamma 247 if c != 0 { 248 p = (gamma * gamma) / c 249 } else { 250 p = oldc * bb 251 } 252 } 253 e[l-1] = s * p 254 d[l] = sigma + gamma 255 } 256 } 257 258 // Undo scaling if necessary 259 switch iscale { 260 case down: 261 impl.Dlascl(lapack.General, 0, 0, ssfmax, anorm, lendsv-lsv+1, 1, d[lsv:], n) 262 case up: 263 impl.Dlascl(lapack.General, 0, 0, ssfmin, anorm, lendsv-lsv+1, 1, d[lsv:], n) 264 } 265 266 // Check for no convergence to an eigenvalue after a total of n*maxit iterations. 267 if jtot >= nmaxit { 268 break 269 } 270 } 271 for _, v := range e[:n-1] { 272 if v != 0 { 273 return false 274 } 275 } 276 impl.Dlasrt(lapack.SortIncreasing, n, d) 277 return true 278 }