gonum.org/v1/gonum@v0.14.0/lapack/gonum/dhseqr.go (about)

     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.
     4  
     5  package gonum
     6  
     7  import (
     8  	"math"
     9  
    10  	"gonum.org/v1/gonum/blas"
    11  	"gonum.org/v1/gonum/lapack"
    12  )
    13  
    14  // Dhseqr computes the eigenvalues of an n×n Hessenberg matrix H and,
    15  // optionally, the matrices T and Z from the Schur decomposition
    16  //
    17  //	H = Z T Zᵀ,
    18  //
    19  // where T is an n×n upper quasi-triangular matrix (the Schur form), and Z is
    20  // the n×n orthogonal matrix of Schur vectors.
    21  //
    22  // Optionally Z may be postmultiplied into an input orthogonal matrix Q so that
    23  // this routine can give the Schur factorization of a matrix A which has been
    24  // reduced to the Hessenberg form H by the orthogonal matrix Q:
    25  //
    26  //	A = Q H Qᵀ = (QZ) T (QZ)ᵀ.
    27  //
    28  // If job == lapack.EigenvaluesOnly, only the eigenvalues will be computed.
    29  // If job == lapack.EigenvaluesAndSchur, the eigenvalues and the Schur form T will
    30  // be computed.
    31  // For other values of job Dhseqr will panic.
    32  //
    33  // If compz == lapack.SchurNone, no Schur vectors will be computed and Z will not be
    34  // referenced.
    35  // If compz == lapack.SchurHess, on return Z will contain the matrix of Schur
    36  // vectors of H.
    37  // If compz == lapack.SchurOrig, on entry z is assumed to contain the orthogonal
    38  // matrix Q that is the identity except for the submatrix
    39  // Q[ilo:ihi+1,ilo:ihi+1]. On return z will be updated to the product Q*Z.
    40  //
    41  // ilo and ihi determine the block of H on which Dhseqr operates. It is assumed
    42  // that H is already upper triangular in rows and columns [0:ilo] and [ihi+1:n],
    43  // although it will be only checked that the block is isolated, that is,
    44  //
    45  //	ilo == 0   or H[ilo,ilo-1] == 0,
    46  //	ihi == n-1 or H[ihi+1,ihi] == 0,
    47  //
    48  // and Dhseqr will panic otherwise. ilo and ihi are typically set by a previous
    49  // call to Dgebal, otherwise they should be set to 0 and n-1, respectively. It
    50  // must hold that
    51  //
    52  //	0 <= ilo <= ihi < n     if n > 0,
    53  //	ilo == 0 and ihi == -1  if n == 0.
    54  //
    55  // wr and wi must have length n.
    56  //
    57  // work must have length at least lwork and lwork must be at least max(1,n)
    58  // otherwise Dhseqr will panic. The minimum lwork delivers very good and
    59  // sometimes optimal performance, although lwork as large as 11*n may be
    60  // required. On return, work[0] will contain the optimal value of lwork.
    61  //
    62  // If lwork is -1, instead of performing Dhseqr, the function only estimates the
    63  // optimal workspace size and stores it into work[0]. Neither h nor z are
    64  // accessed.
    65  //
    66  // unconverged indicates whether Dhseqr computed all the eigenvalues.
    67  //
    68  // If unconverged == 0, all the eigenvalues have been computed and their real
    69  // and imaginary parts will be stored on return in wr and wi, respectively. If
    70  // two eigenvalues are computed as a complex conjugate pair, they are stored in
    71  // consecutive elements of wr and wi, say the i-th and (i+1)th, with wi[i] > 0
    72  // and wi[i+1] < 0.
    73  //
    74  // If unconverged == 0 and job == lapack.EigenvaluesAndSchur, on return H will
    75  // contain the upper quasi-triangular matrix T from the Schur decomposition (the
    76  // Schur form). 2×2 diagonal blocks (corresponding to complex conjugate pairs of
    77  // eigenvalues) will be returned in standard form, with
    78  //
    79  //	H[i,i] == H[i+1,i+1],
    80  //
    81  // and
    82  //
    83  //	H[i+1,i]*H[i,i+1] < 0.
    84  //
    85  // The eigenvalues will be stored in wr and wi in the same order as on the
    86  // diagonal of the Schur form returned in H, with
    87  //
    88  //	wr[i] = H[i,i],
    89  //
    90  // and, if H[i:i+2,i:i+2] is a 2×2 diagonal block,
    91  //
    92  //	wi[i]   = sqrt(-H[i+1,i]*H[i,i+1]),
    93  //	wi[i+1] = -wi[i].
    94  //
    95  // If unconverged == 0 and job == lapack.EigenvaluesOnly, the contents of h
    96  // on return is unspecified.
    97  //
    98  // If unconverged > 0, some eigenvalues have not converged, and the blocks
    99  // [0:ilo] and [unconverged:n] of wr and wi will contain those eigenvalues which
   100  // have been successfully computed. Failures are rare.
   101  //
   102  // If unconverged > 0 and job == lapack.EigenvaluesOnly, on return the
   103  // remaining unconverged eigenvalues are the eigenvalues of the upper Hessenberg
   104  // matrix H[ilo:unconverged,ilo:unconverged].
   105  //
   106  // If unconverged > 0 and job == lapack.EigenvaluesAndSchur, then on
   107  // return
   108  //
   109  //	(initial H) U = U (final H),   (*)
   110  //
   111  // where U is an orthogonal matrix. The final H is upper Hessenberg and
   112  // H[unconverged:ihi+1,unconverged:ihi+1] is upper quasi-triangular.
   113  //
   114  // If unconverged > 0 and compz == lapack.SchurOrig, then on return
   115  //
   116  //	(final Z) = (initial Z) U,
   117  //
   118  // where U is the orthogonal matrix in (*) regardless of the value of job.
   119  //
   120  // If unconverged > 0 and compz == lapack.SchurHess, then on return
   121  //
   122  //	(final Z) = U,
   123  //
   124  // where U is the orthogonal matrix in (*) regardless of the value of job.
   125  //
   126  // References:
   127  //
   128  //	[1] R. Byers. LAPACK 3.1 xHSEQR: Tuning and Implementation Notes on the
   129  //	    Small Bulge Multi-Shift QR Algorithm with Aggressive Early Deflation.
   130  //	    LAPACK Working Note 187 (2007)
   131  //	    URL: http://www.netlib.org/lapack/lawnspdf/lawn187.pdf
   132  //	[2] K. Braman, R. Byers, R. Mathias. The Multishift QR Algorithm. Part I:
   133  //	    Maintaining Well-Focused Shifts and Level 3 Performance. SIAM J. Matrix
   134  //	    Anal. Appl. 23(4) (2002), pp. 929—947
   135  //	    URL: http://dx.doi.org/10.1137/S0895479801384573
   136  //	[3] K. Braman, R. Byers, R. Mathias. The Multishift QR Algorithm. Part II:
   137  //	    Aggressive Early Deflation. SIAM J. Matrix Anal. Appl. 23(4) (2002), pp. 948—973
   138  //	    URL: http://dx.doi.org/10.1137/S0895479801384585
   139  //
   140  // Dhseqr is an internal routine. It is exported for testing purposes.
   141  func (impl Implementation) Dhseqr(job lapack.SchurJob, compz lapack.SchurComp, n, ilo, ihi int, h []float64, ldh int, wr, wi []float64, z []float64, ldz int, work []float64, lwork int) (unconverged int) {
   142  	wantt := job == lapack.EigenvaluesAndSchur
   143  	wantz := compz == lapack.SchurHess || compz == lapack.SchurOrig
   144  
   145  	switch {
   146  	case job != lapack.EigenvaluesOnly && job != lapack.EigenvaluesAndSchur:
   147  		panic(badSchurJob)
   148  	case compz != lapack.SchurNone && compz != lapack.SchurHess && compz != lapack.SchurOrig:
   149  		panic(badSchurComp)
   150  	case n < 0:
   151  		panic(nLT0)
   152  	case ilo < 0 || max(0, n-1) < ilo:
   153  		panic(badIlo)
   154  	case ihi < min(ilo, n-1) || n <= ihi:
   155  		panic(badIhi)
   156  	case ldh < max(1, n):
   157  		panic(badLdH)
   158  	case ldz < 1, wantz && ldz < n:
   159  		panic(badLdZ)
   160  	case lwork < max(1, n) && lwork != -1:
   161  		panic(badLWork)
   162  	case len(work) < max(1, lwork):
   163  		panic(shortWork)
   164  	}
   165  
   166  	// Quick return if possible.
   167  	if n == 0 {
   168  		work[0] = 1
   169  		return 0
   170  	}
   171  
   172  	// Quick return in case of a workspace query.
   173  	if lwork == -1 {
   174  		impl.Dlaqr04(wantt, wantz, n, ilo, ihi, h, ldh, wr, wi, ilo, ihi, z, ldz, work, -1, 1)
   175  		work[0] = math.Max(float64(n), work[0])
   176  		return 0
   177  	}
   178  
   179  	switch {
   180  	case len(h) < (n-1)*ldh+n:
   181  		panic(shortH)
   182  	case wantz && len(z) < (n-1)*ldz+n:
   183  		panic(shortZ)
   184  	case len(wr) < n:
   185  		panic(shortWr)
   186  	case len(wi) < n:
   187  		panic(shortWi)
   188  	}
   189  
   190  	const (
   191  		// Matrices of order ntiny or smaller must be processed by
   192  		// Dlahqr because of insufficient subdiagonal scratch space.
   193  		// This is a hard limit.
   194  		ntiny = 15
   195  
   196  		// nl is the size of a local workspace to help small matrices
   197  		// through a rare Dlahqr failure. nl > ntiny is required and
   198  		// nl <= nmin = Ilaenv(ispec=12,...) is recommended (the default
   199  		// value of nmin is 75). Using nl = 49 allows up to six
   200  		// simultaneous shifts and a 16×16 deflation window.
   201  		nl = 49
   202  	)
   203  
   204  	// Copy eigenvalues isolated by Dgebal.
   205  	for i := 0; i < ilo; i++ {
   206  		wr[i] = h[i*ldh+i]
   207  		wi[i] = 0
   208  	}
   209  	for i := ihi + 1; i < n; i++ {
   210  		wr[i] = h[i*ldh+i]
   211  		wi[i] = 0
   212  	}
   213  
   214  	// Initialize Z to identity matrix if requested.
   215  	if compz == lapack.SchurHess {
   216  		impl.Dlaset(blas.All, n, n, 0, 1, z, ldz)
   217  	}
   218  
   219  	// Quick return if possible.
   220  	if ilo == ihi {
   221  		wr[ilo] = h[ilo*ldh+ilo]
   222  		wi[ilo] = 0
   223  		return 0
   224  	}
   225  
   226  	// Dlahqr/Dlaqr04 crossover point.
   227  	nmin := impl.Ilaenv(12, "DHSEQR", string(job)+string(compz), n, ilo, ihi, lwork)
   228  	nmin = max(ntiny, nmin)
   229  
   230  	if n > nmin {
   231  		// Dlaqr0 for big matrices.
   232  		unconverged = impl.Dlaqr04(wantt, wantz, n, ilo, ihi, h, ldh, wr[:ihi+1], wi[:ihi+1],
   233  			ilo, ihi, z, ldz, work, lwork, 1)
   234  	} else {
   235  		// Dlahqr for small matrices.
   236  		unconverged = impl.Dlahqr(wantt, wantz, n, ilo, ihi, h, ldh, wr[:ihi+1], wi[:ihi+1],
   237  			ilo, ihi, z, ldz)
   238  		if unconverged > 0 {
   239  			// A rare Dlahqr failure! Dlaqr04 sometimes succeeds
   240  			// when Dlahqr fails.
   241  			kbot := unconverged
   242  			if n >= nl {
   243  				// Larger matrices have enough subdiagonal
   244  				// scratch space to call Dlaqr04 directly.
   245  				unconverged = impl.Dlaqr04(wantt, wantz, n, ilo, kbot, h, ldh,
   246  					wr[:ihi+1], wi[:ihi+1], ilo, ihi, z, ldz, work, lwork, 1)
   247  			} else {
   248  				// Tiny matrices don't have enough subdiagonal
   249  				// scratch space to benefit from Dlaqr04. Hence,
   250  				// tiny matrices must be copied into a larger
   251  				// array before calling Dlaqr04.
   252  				var hl [nl * nl]float64
   253  				impl.Dlacpy(blas.All, n, n, h, ldh, hl[:], nl)
   254  				impl.Dlaset(blas.All, nl, nl-n, 0, 0, hl[n:], nl)
   255  				var workl [nl]float64
   256  				unconverged = impl.Dlaqr04(wantt, wantz, nl, ilo, kbot, hl[:], nl,
   257  					wr[:ihi+1], wi[:ihi+1], ilo, ihi, z, ldz, workl[:], nl, 1)
   258  				work[0] = workl[0]
   259  				if wantt || unconverged > 0 {
   260  					impl.Dlacpy(blas.All, n, n, hl[:], nl, h, ldh)
   261  				}
   262  			}
   263  		}
   264  	}
   265  	// Zero out under the first subdiagonal, if necessary.
   266  	if (wantt || unconverged > 0) && n > 2 {
   267  		impl.Dlaset(blas.Lower, n-2, n-2, 0, 0, h[2*ldh:], ldh)
   268  	}
   269  
   270  	work[0] = math.Max(float64(n), work[0])
   271  	return unconverged
   272  }