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.
10 "gonum.org/v1/gonum/lapack"
13 // Dsterf computes all eigenvalues of a symmetric tridiagonal matrix using the
14 // Pal-Walker-Kahan variant of the QL or QR algorithm.
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.
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
24 // Dsterf is an internal routine. It is exported for testing purposes.
25 func (impl Implementation) Dsterf(n int, d, e []float64) (ok bool) {
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.
45 // Determine the unit roundoff for this environment.
50 ssfmax := math.Sqrt(safmax) / 3
51 ssfmin := math.Sqrt(safmin) / eps2
53 // Compute the eigenvalues of the tridiagonal matrix.
62 impl.Dlasrt(lapack.SortIncreasing, n, d)
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 {
85 // Scale submatrix in rows and columns l to lend.
86 anorm := impl.Dlanst(lapack.MaxAbs, lend-l+1, d[l:], e[l:])
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 {
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)
102 for i, v := range el {
106 // Choose between QL and QR iteration.
107 if math.Abs(d[lend]) < math.Abs(d[l]) {
113 // Look for small sub-diagonal element.
116 for m = l; m < lend; m++ {
117 if math.Abs(e[m]) <= eps2*(math.Abs(d[m]*d[m+1])) {
136 // If remaining matrix is 2 by 2, use Dlae2 to compute its eigenvalues.
138 d[l], d[l+1] = impl.Dlae2(d[l], math.Sqrt(e[l]), d[l+1])
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)))
159 gamma := d[m] - sigma
163 for i := m - 1; i >= l; i-- {
174 gamma = c*(alpha-sigma) - s*oldgam
175 d[i+1] = oldgam + (alpha - gamma)
177 p = (gamma * gamma) / c
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]) {
207 // If remaining matrix is 2 by 2, use Dlae2 to compute its eigenvalues.
209 d[l], d[l-1] = impl.Dlae2(d[l], math.Sqrt(e[l-1]), d[l-1])
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)))
230 gamma := d[m] - sigma
234 for i := m; i < l; i++ {
245 gamma = c*(alpha-sigma) - s*oldgam
246 d[i] = oldgam + alpha - gamma
248 p = (gamma * gamma) / c
258 // Undo scaling if necessary
261 impl.Dlascl(lapack.General, 0, 0, ssfmax, anorm, lendsv-lsv+1, 1, d[lsv:], n)
263 impl.Dlascl(lapack.General, 0, 0, ssfmin, anorm, lendsv-lsv+1, 1, d[lsv:], n)
266 // Check for no convergence to an eigenvalue after a total of n*maxit iterations.
271 for _, v := range e[:n-1] {
276 impl.Dlasrt(lapack.SortIncreasing, n, d)