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.
10 "gonum.org/v1/gonum/lapack"
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
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.
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
30 // 3: Termination criterion of outer while loop not met (program created more
31 // than N unreduced blocks).
33 // z must have length at least 4*n, and must not contain any negative elements.
34 // Dlasq2 will panic otherwise.
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.
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]
66 z[4] = z[0] + z[1] + z[2]
68 t := 0.5 * (z[0] - z[2] + z[1])
69 s := z[2] * (z[1] / t)
71 s = z[2] * (z[1] / (t * (1 + math.Sqrt(1+s/t))))
73 s = z[2] * (z[1] / (t + math.Sqrt(t)*math.Sqrt(t+s)))
83 // Check for negative data and compute sums of q's and e's.
86 var d, e, qmax float64
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")
94 qmax = math.Max(qmax, z[k])
95 emin = math.Min(emin, z[k+1])
98 panic("lapack: bad z value")
101 qmax = math.Max(qmax, z[2*(n-1)])
102 // Check for diagonality.
104 for k := 1; k < n; k++ {
107 impl.Dlasrt(lapack.SortDecreasing, n, z)
112 // Check for zero data.
117 // Rearrange data for locality: Z=(q1,qq1,e1,ee1,q2,qq2,e2,ee2,...).
118 for k := 2 * n; k >= 2; k -= 2 {
127 // Reverse the qd-array, if warranted.
128 // z[4*i0-3] --> z[4*(i0+1)-3-1] --> z[4*i0]
129 if cbias*z[4*i0] < z[4*n0] {
130 ipn4Out := 4 * (i0 + n0 + 2)
131 for i4loop := 4 * (i0 + 1); i4loop <= 2*(i0+n0+1); i4loop += 4 {
134 z[i4-3], z[ipn4-i4-4] = z[ipn4-i4-4], z[i4-3]
135 z[i4-1], z[ipn4-i4-6] = z[ipn4-i4-6], z[i4-1]
139 // Initial split checking via dqd and Li's test.
141 for k := 0; k < 2; k++ {
143 for i4loop := 4*n0 + pp; i4loop >= 4*(i0+1)+pp; i4loop -= 4 {
145 if z[i4-1] <= tol2*d {
146 z[i4-1] = math.Copysign(0, -1)
149 d = z[i4-3] * (d / (d + z[i4-1]))
152 // dqd maps Z to ZZ plus Li's test.
153 emin = z[4*(i0+1)+pp]
155 for i4loop := 4*(i0+1) + pp; i4loop <= 4*n0+pp; i4loop += 4 {
157 z[i4-2*pp-2] = d + z[i4-1]
158 if z[i4-1] <= tol2*d {
159 z[i4-1] = math.Copysign(0, -1)
163 } else if safmin*z[i4+1] < z[i4-2*pp-2] && safmin*z[i4-2*pp-2] < z[i4+1] {
164 tmp := z[i4+1] / z[i4-2*pp-2]
165 z[i4-2*pp] = z[i4-1] * tmp
168 z[i4-2*pp] = z[i4+1] * (z[i4-1] / z[i4-2*pp-2])
169 d = z[i4+1] * (d / z[i4-2*pp-2])
171 emin = math.Min(emin, z[i4-2*pp])
176 qmax = z[4*(i0+1)-pp-3]
177 for i4loop := 4*(i0+1) - pp + 2; i4loop <= 4*(n0+1)+pp-2; i4loop += 4 {
179 qmax = math.Max(qmax, z[i4])
181 // Prepare for the next iteration on K.
185 // Initialise variables to pass to DLASQ3.
187 var dmin1, dmin2, dn, dn1, dn2, g, tau float64
191 nDiv := 2 * (n0 - i0)
194 for iwhila := 1; iwhila <= n+1; iwhila++ {
195 // Test for completion.
197 // Move q's to the front.
198 for k := 1; k < n; k++ {
201 // Sort and compute sum of eigenvalues.
202 impl.Dlasrt(lapack.SortDecreasing, n, z)
204 for k := n - 1; k >= 0; k-- {
207 // Store trace, sum(eigenvalues) and information on performance.
210 z[2*n+2] = float64(iter)
211 z[2*n+3] = float64(nDiv) / float64(n*n)
212 z[2*n+4] = 100 * float64(nFail) / float64(iter)
216 // While array unfinished do
217 // e[n0] holds the value of sigma when submatrix in i0:n0
218 // splits from the rest of the array, but is negated.
222 sigma = -z[4*(n0+1)-2]
228 // Find last unreduced submatrix's top index i0, find qmax and
229 // emin. Find Gershgorin-type bound if Q's much greater than E's.
232 emin = math.Abs(z[4*(n0+1)-6])
236 qmin := z[4*(n0+1)-4]
239 for i4loop := 4 * (n0 + 1); i4loop >= 8; i4loop -= 4 {
246 qmin = math.Min(qmin, z[i4-3])
247 emax = math.Max(emax, z[i4-5])
249 qmax = math.Max(qmax, z[i4-7]+z[i4-5])
250 emin = math.Min(emin, z[i4-5])
261 for i4loop := 4*(i0+1) + 1; i4loop <= 4*(n0+1)-3; i4loop += 4 {
263 dee = z[i4] * (dee / (dee + z[i4-2]))
269 if (kmin-i0)*2 < n0-kmin && deemin <= 0.5*z[4*n0] {
270 ipn4Out := 4 * (i0 + n0 + 2)
272 for i4loop := 4 * (i0 + 1); i4loop <= 2*(i0+n0+1); i4loop += 4 {
275 z[i4-3], z[ipn4-i4-4] = z[ipn4-i4-4], z[i4-3]
276 z[i4-2], z[ipn4-i4-3] = z[ipn4-i4-3], z[i4-2]
277 z[i4-1], z[ipn4-i4-6] = z[ipn4-i4-6], z[i4-1]
278 z[i4], z[ipn4-i4-5] = z[ipn4-i4-5], z[i4]
282 // Put -(initial shift) into DMIN.
283 dmin := -math.Max(0, qmin-2*math.Sqrt(qmin)*math.Sqrt(emax))
285 // Now i0:n0 is unreduced.
286 // PP = 0 for ping, PP = 1 for pong.
287 // PP = 2 indicates that flipping was applied to the Z array and
288 // and that the tests for deflation upon entry in Dlasq3
289 // should not be performed.
290 nbig := 100 * (n0 - i0 + 1)
291 for iwhilb := 0; iwhilb < nbig; iwhilb++ {
296 // While submatrix unfinished take a good dqds step.
297 i0, n0, pp, dmin, sigma, desig, qmax, nFail, iter, nDiv, ttype, dmin1, dmin2, dn, dn1, dn2, g, tau =
298 impl.Dlasq3(i0, n0, z, pp, dmin, sigma, desig, qmax, nFail, iter, nDiv, ttype, dmin1, dmin2, dn, dn1, dn2, g, tau)
301 // When emin is very small check for splits.
302 if pp == 0 && n0-i0 >= 3 {
303 if z[4*(n0+1)-1] <= tol2*qmax || z[4*(n0+1)-2] <= tol2*sigma {
307 oldemn := z[4*(i0+1)-1]
308 for i4loop := 4 * (i0 + 1); i4loop <= 4*(n0-2); i4loop += 4 {
310 if z[i4] <= tol2*z[i4-3] || z[i4-1] <= tol2*sigma {
317 qmax = math.Max(qmax, z[i4+1])
318 emin = math.Min(emin, z[i4-1])
319 oldemn = math.Min(oldemn, z[i4])
323 z[4*(n0+1)-1] = oldemn
328 // Maximum number of iterations exceeded, restore the shift
329 // sigma and place the new d's and e's in a qd array.
330 // This might need to be done for several blocks.
337 for k := i0 + 1; k <= n0; k++ {
338 tempe := z[4*(k+1)-6]
339 z[4*(k+1)-6] *= tempq / z[4*(k+1)-8]
341 z[4*k] += sigma + tempe - z[4*(k+1)-6]
343 // Prepare to do this on the previous block if there is one.
348 for i1 >= 1 && z[4*(i1+1)-6] >= 0 {
351 sigma = -z[4*(n1+1)-2]
353 for k := 0; k < n; k++ {
355 // Only the block 1..N0 is unfinished. The rest of the e's
356 // must be essentially zero, although sometimes other data
357 // has been stored in them.
359 z[2*(k+1)-1] = z[4*(k+1)-1]