+++ /dev/null
-// Copyright ©2016 The Gonum Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gonum
-
-import (
- "math"
-
- "gonum.org/v1/gonum/blas"
- "gonum.org/v1/gonum/blas/blas64"
- "gonum.org/v1/gonum/lapack"
-)
-
-// Dsteqr computes the eigenvalues and optionally the eigenvectors of a symmetric
-// tridiagonal matrix using the implicit QL or QR method. The eigenvectors of a
-// full or band symmetric matrix can also be found if Dsytrd, Dsptrd, or Dsbtrd
-// have been used to reduce this matrix to tridiagonal form.
-//
-// d, on entry, contains the diagonal elements of the tridiagonal matrix. On exit,
-// d contains the eigenvalues in ascending order. d must have length n and
-// Dsteqr will panic otherwise.
-//
-// e, on entry, contains the off-diagonal elements of the tridiagonal matrix on
-// entry, and is overwritten during the call to Dsteqr. e must have length n-1 and
-// Dsteqr will panic otherwise.
-//
-// z, on entry, contains the n×n orthogonal matrix used in the reduction to
-// tridiagonal form if compz == lapack.OriginalEV. On exit, if
-// compz == lapack.OriginalEV, z contains the orthonormal eigenvectors of the
-// original symmetric matrix, and if compz == lapack.TridiagEV, z contains the
-// orthonormal eigenvectors of the symmetric tridiagonal matrix. z is not used
-// if compz == lapack.None.
-//
-// work must have length at least max(1, 2*n-2) if the eigenvectors are computed,
-// and Dsteqr will panic otherwise.
-//
-// Dsteqr is an internal routine. It is exported for testing purposes.
-func (impl Implementation) Dsteqr(compz lapack.EVComp, n int, d, e, z []float64, ldz int, work []float64) (ok bool) {
- if n < 0 {
- panic(nLT0)
- }
- if len(d) < n {
- panic(badD)
- }
- if len(e) < n-1 {
- panic(badE)
- }
- if compz != lapack.None && compz != lapack.TridiagEV && compz != lapack.OriginalEV {
- panic(badEVComp)
- }
- if compz != lapack.None {
- if len(work) < max(1, 2*n-2) {
- panic(badWork)
- }
- checkMatrix(n, n, z, ldz)
- }
-
- var icompz int
- if compz == lapack.OriginalEV {
- icompz = 1
- } else if compz == lapack.TridiagEV {
- icompz = 2
- }
-
- if n == 0 {
- return true
- }
- if n == 1 {
- if icompz == 2 {
- z[0] = 1
- }
- return true
- }
-
- bi := blas64.Implementation()
-
- eps := dlamchE
- eps2 := eps * eps
- safmin := dlamchS
- safmax := 1 / safmin
- ssfmax := math.Sqrt(safmax) / 3
- ssfmin := math.Sqrt(safmin) / eps2
-
- // Compute the eigenvalues and eigenvectors of the tridiagonal matrix.
- if icompz == 2 {
- impl.Dlaset(blas.All, n, n, 0, 1, z, ldz)
- }
- const maxit = 30
- nmaxit := n * maxit
-
- jtot := 0
-
- // Determine where the matrix splits and choose QL or QR iteration for each
- // block, according to whether top or bottom diagonal element is smaller.
- l1 := 0
- nm1 := n - 1
-
- type scaletype int
- const (
- down scaletype = iota + 1
- up
- )
- var iscale scaletype
-
- for {
- if l1 > n-1 {
- // Order eigenvalues and eigenvectors.
- if icompz == 0 {
- impl.Dlasrt(lapack.SortIncreasing, n, d)
- } else {
- // TODO(btracey): Consider replacing this sort with a call to sort.Sort.
- for ii := 1; ii < n; ii++ {
- i := ii - 1
- k := i
- p := d[i]
- for j := ii; j < n; j++ {
- if d[j] < p {
- k = j
- p = d[j]
- }
- }
- if k != i {
- d[k] = d[i]
- d[i] = p
- bi.Dswap(n, z[i:], ldz, z[k:], ldz)
- }
- }
- }
- return true
- }
- if l1 > 0 {
- e[l1-1] = 0
- }
- var m int
- if l1 <= nm1 {
- for m = l1; m < nm1; m++ {
- test := math.Abs(e[m])
- if test == 0 {
- break
- }
- if test <= (math.Sqrt(math.Abs(d[m]))*math.Sqrt(math.Abs(d[m+1])))*eps {
- e[m] = 0
- break
- }
- }
- }
- l := l1
- lsv := l
- lend := m
- lendsv := lend
- l1 = m + 1
- if lend == l {
- continue
- }
-
- // Scale submatrix in rows and columns L to Lend
- anorm := impl.Dlanst(lapack.MaxAbs, lend-l+1, d[l:], e[l:])
- switch {
- case anorm == 0:
- continue
- case anorm > ssfmax:
- iscale = down
- // Pretend that d and e are matrices with 1 column.
- impl.Dlascl(lapack.General, 0, 0, anorm, ssfmax, lend-l+1, 1, d[l:], 1)
- impl.Dlascl(lapack.General, 0, 0, anorm, ssfmax, lend-l, 1, e[l:], 1)
- case anorm < ssfmin:
- iscale = up
- impl.Dlascl(lapack.General, 0, 0, anorm, ssfmin, lend-l+1, 1, d[l:], 1)
- impl.Dlascl(lapack.General, 0, 0, anorm, ssfmin, lend-l, 1, e[l:], 1)
- }
-
- // Choose between QL and QR.
- if math.Abs(d[lend]) < math.Abs(d[l]) {
- lend = lsv
- l = lendsv
- }
- if lend > l {
- // QL Iteration. Look for small subdiagonal element.
- for {
- if l != lend {
- for m = l; m < lend; m++ {
- v := math.Abs(e[m])
- if v*v <= (eps2*math.Abs(d[m]))*math.Abs(d[m+1])+safmin {
- break
- }
- }
- } else {
- m = lend
- }
- if m < lend {
- e[m] = 0
- }
- p := d[l]
- if m == l {
- // Eigenvalue found.
- l++
- if l > lend {
- break
- }
- continue
- }
-
- // If remaining matrix is 2×2, use Dlae2 to compute its eigensystem.
- if m == l+1 {
- if icompz > 0 {
- d[l], d[l+1], work[l], work[n-1+l] = impl.Dlaev2(d[l], e[l], d[l+1])
- impl.Dlasr(blas.Right, lapack.Variable, lapack.Backward,
- n, 2, work[l:], work[n-1+l:], z[l:], ldz)
- } else {
- d[l], d[l+1] = impl.Dlae2(d[l], e[l], d[l+1])
- }
- e[l] = 0
- l += 2
- if l > lend {
- break
- }
- continue
- }
-
- if jtot == nmaxit {
- break
- }
- jtot++
-
- // Form shift
- g := (d[l+1] - p) / (2 * e[l])
- r := impl.Dlapy2(g, 1)
- g = d[m] - p + e[l]/(g+math.Copysign(r, g))
- s := 1.0
- c := 1.0
- p = 0.0
-
- // Inner loop
- for i := m - 1; i >= l; i-- {
- f := s * e[i]
- b := c * e[i]
- c, s, r = impl.Dlartg(g, f)
- if i != m-1 {
- e[i+1] = r
- }
- g = d[i+1] - p
- r = (d[i]-g)*s + 2*c*b
- p = s * r
- d[i+1] = g + p
- g = c*r - b
-
- // If eigenvectors are desired, then save rotations.
- if icompz > 0 {
- work[i] = c
- work[n-1+i] = -s
- }
- }
- // If eigenvectors are desired, then apply saved rotations.
- if icompz > 0 {
- mm := m - l + 1
- impl.Dlasr(blas.Right, lapack.Variable, lapack.Backward,
- n, mm, work[l:], work[n-1+l:], z[l:], ldz)
- }
- d[l] -= p
- e[l] = g
- }
- } else {
- // QR Iteration.
- // Look for small superdiagonal element.
- for {
- if l != lend {
- for m = l; m > lend; m-- {
- v := math.Abs(e[m-1])
- if v*v <= (eps2*math.Abs(d[m])*math.Abs(d[m-1]) + safmin) {
- break
- }
- }
- } else {
- m = lend
- }
- if m > lend {
- e[m-1] = 0
- }
- p := d[l]
- if m == l {
- // Eigenvalue found
- l--
- if l < lend {
- break
- }
- continue
- }
-
- // If remaining matrix is 2×2, use Dlae2 to compute its eigenvalues.
- if m == l-1 {
- if icompz > 0 {
- d[l-1], d[l], work[m], work[n-1+m] = impl.Dlaev2(d[l-1], e[l-1], d[l])
- impl.Dlasr(blas.Right, lapack.Variable, lapack.Forward,
- n, 2, work[m:], work[n-1+m:], z[l-1:], ldz)
- } else {
- d[l-1], d[l] = impl.Dlae2(d[l-1], e[l-1], d[l])
- }
- e[l-1] = 0
- l -= 2
- if l < lend {
- break
- }
- continue
- }
- if jtot == nmaxit {
- break
- }
- jtot++
-
- // Form shift.
- g := (d[l-1] - p) / (2 * e[l-1])
- r := impl.Dlapy2(g, 1)
- g = d[m] - p + (e[l-1])/(g+math.Copysign(r, g))
- s := 1.0
- c := 1.0
- p = 0.0
-
- // Inner loop.
- for i := m; i < l; i++ {
- f := s * e[i]
- b := c * e[i]
- c, s, r = impl.Dlartg(g, f)
- if i != m {
- e[i-1] = r
- }
- g = d[i] - p
- r = (d[i+1]-g)*s + 2*c*b
- p = s * r
- d[i] = g + p
- g = c*r - b
-
- // If eigenvectors are desired, then save rotations.
- if icompz > 0 {
- work[i] = c
- work[n-1+i] = s
- }
- }
-
- // If eigenvectors are desired, then apply saved rotations.
- if icompz > 0 {
- mm := l - m + 1
- impl.Dlasr(blas.Right, lapack.Variable, lapack.Forward,
- n, mm, work[m:], work[n-1+m:], z[m:], ldz)
- }
- d[l] -= p
- e[l-1] = g
- }
- }
-
- // Undo scaling if necessary.
- switch iscale {
- case down:
- // Pretend that d and e are matrices with 1 column.
- impl.Dlascl(lapack.General, 0, 0, ssfmax, anorm, lendsv-lsv+1, 1, d[lsv:], 1)
- impl.Dlascl(lapack.General, 0, 0, ssfmax, anorm, lendsv-lsv, 1, e[lsv:], 1)
- case up:
- impl.Dlascl(lapack.General, 0, 0, ssfmin, anorm, lendsv-lsv+1, 1, d[lsv:], 1)
- impl.Dlascl(lapack.General, 0, 0, ssfmin, anorm, lendsv-lsv, 1, e[lsv:], 1)
- }
-
- // Check for no convergence to an eigenvalue after a total of n*maxit iterations.
- if jtot >= nmaxit {
- break
- }
- }
- for i := 0; i < n-1; i++ {
- if e[i] != 0 {
- return false
- }
- }
- return true
-}