gonum.org/v1/gonum@v0.14.0/lapack/gonum/dlasq2.go (about) 1 // Copyright ©2015 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 "gonum.org/v1/gonum/lapack" 11 ) 12 13 // Dlasq2 computes all the eigenvalues of the symmetric positive 14 // definite tridiagonal matrix associated with the qd array Z. Eigevalues 15 // are computed to high relative accuracy avoiding denormalization, underflow 16 // and overflow. 17 // 18 // To see the relation of Z to the tridiagonal matrix, let L be a 19 // unit lower bidiagonal matrix with sub-diagonals Z(2,4,6,,..) and 20 // let U be an upper bidiagonal matrix with 1's above and diagonal 21 // Z(1,3,5,,..). The tridiagonal is L*U or, if you prefer, the 22 // symmetric tridiagonal to which it is similar. 23 // 24 // info returns a status error. The return codes mean as follows: 25 // 26 // 0: The algorithm completed successfully. 27 // 1: A split was marked by a positive value in e. 28 // 2: Current block of Z not diagonalized after 100*n iterations (in inner 29 // while loop). On exit Z holds a qd array with the same eigenvalues as 30 // the given Z. 31 // 3: Termination criterion of outer while loop not met (program created more 32 // than N unreduced blocks). 33 // 34 // z must have length at least 4*n, and must not contain any negative elements. 35 // Dlasq2 will panic otherwise. 36 // 37 // Dlasq2 is an internal routine. It is exported for testing purposes. 38 func (impl Implementation) Dlasq2(n int, z []float64) (info int) { 39 if n < 0 { 40 panic(nLT0) 41 } 42 43 if n == 0 { 44 return info 45 } 46 47 if len(z) < 4*n { 48 panic(shortZ) 49 } 50 51 if n == 1 { 52 if z[0] < 0 { 53 panic(negZ) 54 } 55 return info 56 } 57 58 const cbias = 1.5 59 60 eps := dlamchP 61 safmin := dlamchS 62 tol := eps * 100 63 tol2 := tol * tol 64 if n == 2 { 65 if z[1] < 0 || z[2] < 0 { 66 panic(negZ) 67 } else if z[2] > z[0] { 68 z[0], z[2] = z[2], z[0] 69 } 70 z[4] = z[0] + z[1] + z[2] 71 if z[1] > z[2]*tol2 { 72 t := 0.5 * (z[0] - z[2] + z[1]) 73 s := z[2] * (z[1] / t) 74 if s <= t { 75 s = z[2] * (z[1] / (t * (1 + math.Sqrt(1+s/t)))) 76 } else { 77 s = z[2] * (z[1] / (t + math.Sqrt(t)*math.Sqrt(t+s))) 78 } 79 t = z[0] + s + z[1] 80 z[2] *= z[0] / t 81 z[0] = t 82 } 83 z[1] = z[2] 84 z[5] = z[1] + z[0] 85 return info 86 } 87 // Check for negative data and compute sums of q's and e's. 88 z[2*n-1] = 0 89 emin := z[1] 90 var d, e, qmax float64 91 var i1, n1 int 92 for k := 0; k < 2*(n-1); k += 2 { 93 if z[k] < 0 || z[k+1] < 0 { 94 panic(negZ) 95 } 96 d += z[k] 97 e += z[k+1] 98 qmax = math.Max(qmax, z[k]) 99 emin = math.Min(emin, z[k+1]) 100 } 101 if z[2*(n-1)] < 0 { 102 panic(negZ) 103 } 104 d += z[2*(n-1)] 105 // Check for diagonality. 106 if e == 0 { 107 for k := 1; k < n; k++ { 108 z[k] = z[2*k] 109 } 110 impl.Dlasrt(lapack.SortDecreasing, n, z) 111 z[2*(n-1)] = d 112 return info 113 } 114 trace := d + e 115 // Check for zero data. 116 if trace == 0 { 117 z[2*(n-1)] = 0 118 return info 119 } 120 // Rearrange data for locality: Z=(q1,qq1,e1,ee1,q2,qq2,e2,ee2,...). 121 for k := 2 * n; k >= 2; k -= 2 { 122 z[2*k-1] = 0 123 z[2*k-2] = z[k-1] 124 z[2*k-3] = 0 125 z[2*k-4] = z[k-2] 126 } 127 i0 := 0 128 n0 := n - 1 129 130 // Reverse the qd-array, if warranted. 131 // z[4*i0-3] --> z[4*(i0+1)-3-1] --> z[4*i0] 132 if cbias*z[4*i0] < z[4*n0] { 133 ipn4Out := 4 * (i0 + n0 + 2) 134 for i4loop := 4 * (i0 + 1); i4loop <= 2*(i0+n0+1); i4loop += 4 { 135 i4 := i4loop - 1 136 ipn4 := ipn4Out - 1 137 z[i4-3], z[ipn4-i4-4] = z[ipn4-i4-4], z[i4-3] 138 z[i4-1], z[ipn4-i4-6] = z[ipn4-i4-6], z[i4-1] 139 } 140 } 141 142 // Initial split checking via dqd and Li's test. 143 pp := 0 144 for k := 0; k < 2; k++ { 145 d = z[4*n0+pp] 146 for i4loop := 4*n0 + pp; i4loop >= 4*(i0+1)+pp; i4loop -= 4 { 147 i4 := i4loop - 1 148 if z[i4-1] <= tol2*d { 149 z[i4-1] = math.Copysign(0, -1) 150 d = z[i4-3] 151 } else { 152 d = z[i4-3] * (d / (d + z[i4-1])) 153 } 154 } 155 // dqd maps Z to ZZ plus Li's test. 156 emin = z[4*(i0+1)+pp] 157 d = z[4*i0+pp] 158 for i4loop := 4*(i0+1) + pp; i4loop <= 4*n0+pp; i4loop += 4 { 159 i4 := i4loop - 1 160 z[i4-2*pp-2] = d + z[i4-1] 161 if z[i4-1] <= tol2*d { 162 z[i4-1] = math.Copysign(0, -1) 163 z[i4-2*pp-2] = d 164 z[i4-2*pp] = 0 165 d = z[i4+1] 166 } else if safmin*z[i4+1] < z[i4-2*pp-2] && safmin*z[i4-2*pp-2] < z[i4+1] { 167 tmp := z[i4+1] / z[i4-2*pp-2] 168 z[i4-2*pp] = z[i4-1] * tmp 169 d *= tmp 170 } else { 171 z[i4-2*pp] = z[i4+1] * (z[i4-1] / z[i4-2*pp-2]) 172 d = z[i4+1] * (d / z[i4-2*pp-2]) 173 } 174 emin = math.Min(emin, z[i4-2*pp]) 175 } 176 z[4*(n0+1)-pp-3] = d 177 178 // Now find qmax. 179 qmax = z[4*(i0+1)-pp-3] 180 for i4loop := 4*(i0+1) - pp + 2; i4loop <= 4*(n0+1)+pp-2; i4loop += 4 { 181 i4 := i4loop - 1 182 qmax = math.Max(qmax, z[i4]) 183 } 184 // Prepare for the next iteration on K. 185 pp = 1 - pp 186 } 187 188 // Initialise variables to pass to DLASQ3. 189 var ttype int 190 var dmin1, dmin2, dn, dn1, dn2, g, tau float64 191 var tempq float64 192 iter := 2 193 var nFail int 194 nDiv := 2 * (n0 - i0) 195 var i4 int 196 outer: 197 for iwhila := 1; iwhila <= n+1; iwhila++ { 198 // Test for completion. 199 if n0 < 0 { 200 // Move q's to the front. 201 for k := 1; k < n; k++ { 202 z[k] = z[4*k] 203 } 204 // Sort and compute sum of eigenvalues. 205 impl.Dlasrt(lapack.SortDecreasing, n, z) 206 e = 0 207 for k := n - 1; k >= 0; k-- { 208 e += z[k] 209 } 210 // Store trace, sum(eigenvalues) and information on performance. 211 z[2*n] = trace 212 z[2*n+1] = e 213 z[2*n+2] = float64(iter) 214 z[2*n+3] = float64(nDiv) / float64(n*n) 215 z[2*n+4] = 100 * float64(nFail) / float64(iter) 216 return info 217 } 218 219 // While array unfinished do 220 // e[n0] holds the value of sigma when submatrix in i0:n0 221 // splits from the rest of the array, but is negated. 222 var desig float64 223 var sigma float64 224 if n0 != n-1 { 225 sigma = -z[4*(n0+1)-2] 226 } 227 if sigma < 0 { 228 info = 1 229 return info 230 } 231 // Find last unreduced submatrix's top index i0, find qmax and 232 // emin. Find Gershgorin-type bound if Q's much greater than E's. 233 var emax float64 234 if n0 > i0 { 235 emin = math.Abs(z[4*(n0+1)-6]) 236 } else { 237 emin = 0 238 } 239 qmin := z[4*(n0+1)-4] 240 qmax = qmin 241 zSmall := false 242 for i4loop := 4 * (n0 + 1); i4loop >= 8; i4loop -= 4 { 243 i4 = i4loop - 1 244 if z[i4-5] <= 0 { 245 zSmall = true 246 break 247 } 248 if qmin >= 4*emax { 249 qmin = math.Min(qmin, z[i4-3]) 250 emax = math.Max(emax, z[i4-5]) 251 } 252 qmax = math.Max(qmax, z[i4-7]+z[i4-5]) 253 emin = math.Min(emin, z[i4-5]) 254 } 255 if !zSmall { 256 i4 = 3 257 } 258 i0 = (i4+1)/4 - 1 259 pp = 0 260 if n0-i0 > 1 { 261 dee := z[4*i0] 262 deemin := dee 263 kmin := i0 264 for i4loop := 4*(i0+1) + 1; i4loop <= 4*(n0+1)-3; i4loop += 4 { 265 i4 := i4loop - 1 266 dee = z[i4] * (dee / (dee + z[i4-2])) 267 if dee <= deemin { 268 deemin = dee 269 kmin = (i4+4)/4 - 1 270 } 271 } 272 if (kmin-i0)*2 < n0-kmin && deemin <= 0.5*z[4*n0] { 273 ipn4Out := 4 * (i0 + n0 + 2) 274 pp = 2 275 for i4loop := 4 * (i0 + 1); i4loop <= 2*(i0+n0+1); i4loop += 4 { 276 i4 := i4loop - 1 277 ipn4 := ipn4Out - 1 278 z[i4-3], z[ipn4-i4-4] = z[ipn4-i4-4], z[i4-3] 279 z[i4-2], z[ipn4-i4-3] = z[ipn4-i4-3], z[i4-2] 280 z[i4-1], z[ipn4-i4-6] = z[ipn4-i4-6], z[i4-1] 281 z[i4], z[ipn4-i4-5] = z[ipn4-i4-5], z[i4] 282 } 283 } 284 } 285 // Put -(initial shift) into DMIN. 286 dmin := -math.Max(0, qmin-2*math.Sqrt(qmin)*math.Sqrt(emax)) 287 288 // Now i0:n0 is unreduced. 289 // PP = 0 for ping, PP = 1 for pong. 290 // PP = 2 indicates that flipping was applied to the Z array and 291 // that the tests for deflation upon entry in Dlasq3 should 292 // not be performed. 293 nbig := 100 * (n0 - i0 + 1) 294 for iwhilb := 0; iwhilb < nbig; iwhilb++ { 295 if i0 > n0 { 296 continue outer 297 } 298 299 // While submatrix unfinished take a good dqds step. 300 i0, n0, pp, dmin, sigma, desig, qmax, nFail, iter, nDiv, ttype, dmin1, dmin2, dn, dn1, dn2, g, tau = 301 impl.Dlasq3(i0, n0, z, pp, dmin, sigma, desig, qmax, nFail, iter, nDiv, ttype, dmin1, dmin2, dn, dn1, dn2, g, tau) 302 303 pp = 1 - pp 304 // When emin is very small check for splits. 305 if pp == 0 && n0-i0 >= 3 { 306 if z[4*(n0+1)-1] <= tol2*qmax || z[4*(n0+1)-2] <= tol2*sigma { 307 splt := i0 - 1 308 qmax = z[4*i0] 309 emin = z[4*(i0+1)-2] 310 oldemn := z[4*(i0+1)-1] 311 for i4loop := 4 * (i0 + 1); i4loop <= 4*(n0-2); i4loop += 4 { 312 i4 := i4loop - 1 313 if z[i4] <= tol2*z[i4-3] || z[i4-1] <= tol2*sigma { 314 z[i4-1] = -sigma 315 splt = i4 / 4 316 qmax = 0 317 emin = z[i4+3] 318 oldemn = z[i4+4] 319 } else { 320 qmax = math.Max(qmax, z[i4+1]) 321 emin = math.Min(emin, z[i4-1]) 322 oldemn = math.Min(oldemn, z[i4]) 323 } 324 } 325 z[4*(n0+1)-2] = emin 326 z[4*(n0+1)-1] = oldemn 327 i0 = splt + 1 328 } 329 } 330 } 331 // Maximum number of iterations exceeded, restore the shift 332 // sigma and place the new d's and e's in a qd array. 333 // This might need to be done for several blocks. 334 info = 2 335 i1 = i0 336 for { 337 tempq = z[4*i0] 338 z[4*i0] += sigma 339 for k := i0 + 1; k <= n0; k++ { 340 tempe := z[4*(k+1)-6] 341 z[4*(k+1)-6] *= tempq / z[4*(k+1)-8] 342 tempq = z[4*k] 343 z[4*k] += sigma + tempe - z[4*(k+1)-6] 344 } 345 // Prepare to do this on the previous block if there is one. 346 if i1 <= 0 { 347 break 348 } 349 n1 = i1 - 1 350 for i1 >= 1 && z[4*(i1+1)-6] >= 0 { 351 i1 -= 1 352 } 353 sigma = -z[4*(n1+1)-2] 354 } 355 for k := 0; k < n; k++ { 356 z[2*k] = z[4*k] 357 // Only the block 1..N0 is unfinished. The rest of the e's 358 // must be essentially zero, although sometimes other data 359 // has been stored in them. 360 if k < n0 { 361 z[2*(k+1)-1] = z[4*(k+1)-1] 362 } else { 363 z[2*(k+1)] = 0 364 } 365 } 366 return info 367 } 368 info = 3 369 return info 370 }