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