gonum.org/v1/gonum@v0.14.0/lapack/gonum/dgeev.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/blas/blas64"
    12  	"gonum.org/v1/gonum/lapack"
    13  )
    14  
    15  // Dgeev computes the eigenvalues and, optionally, the left and/or right
    16  // eigenvectors for an n×n real nonsymmetric matrix A.
    17  //
    18  // The right eigenvector v_j of A corresponding to an eigenvalue λ_j
    19  // is defined by
    20  //
    21  //	A v_j = λ_j v_j,
    22  //
    23  // and the left eigenvector u_j corresponding to an eigenvalue λ_j is defined by
    24  //
    25  //	u_jᴴ A = λ_j u_jᴴ,
    26  //
    27  // where u_jᴴ is the conjugate transpose of u_j.
    28  //
    29  // On return, A will be overwritten and the left and right eigenvectors will be
    30  // stored, respectively, in the columns of the n×n matrices VL and VR in the
    31  // same order as their eigenvalues. If the j-th eigenvalue is real, then
    32  //
    33  //	u_j = VL[:,j],
    34  //	v_j = VR[:,j],
    35  //
    36  // and if it is not real, then j and j+1 form a complex conjugate pair and the
    37  // eigenvectors can be recovered as
    38  //
    39  //	u_j     = VL[:,j] + i*VL[:,j+1],
    40  //	u_{j+1} = VL[:,j] - i*VL[:,j+1],
    41  //	v_j     = VR[:,j] + i*VR[:,j+1],
    42  //	v_{j+1} = VR[:,j] - i*VR[:,j+1],
    43  //
    44  // where i is the imaginary unit. The computed eigenvectors are normalized to
    45  // have Euclidean norm equal to 1 and largest component real.
    46  //
    47  // Left eigenvectors will be computed only if jobvl == lapack.LeftEVCompute,
    48  // otherwise jobvl must be lapack.LeftEVNone.
    49  // Right eigenvectors will be computed only if jobvr == lapack.RightEVCompute,
    50  // otherwise jobvr must be lapack.RightEVNone.
    51  // For other values of jobvl and jobvr Dgeev will panic.
    52  //
    53  // wr and wi contain the real and imaginary parts, respectively, of the computed
    54  // eigenvalues. Complex conjugate pairs of eigenvalues appear consecutively with
    55  // the eigenvalue having the positive imaginary part first.
    56  // wr and wi must have length n, and Dgeev will panic otherwise.
    57  //
    58  // work must have length at least lwork and lwork must be at least max(1,4*n) if
    59  // the left or right eigenvectors are computed, and at least max(1,3*n) if no
    60  // eigenvectors are computed. For good performance, lwork must generally be
    61  // larger.  On return, optimal value of lwork will be stored in work[0].
    62  //
    63  // If lwork == -1, instead of performing Dgeev, the function only calculates the
    64  // optimal value of lwork and stores it into work[0].
    65  //
    66  // On return, first is the index of the first valid eigenvalue. If first == 0,
    67  // all eigenvalues and eigenvectors have been computed. If first is positive,
    68  // Dgeev failed to compute all the eigenvalues, no eigenvectors have been
    69  // computed and wr[first:] and wi[first:] contain those eigenvalues which have
    70  // converged.
    71  func (impl Implementation) Dgeev(jobvl lapack.LeftEVJob, jobvr lapack.RightEVJob, n int, a []float64, lda int, wr, wi []float64, vl []float64, ldvl int, vr []float64, ldvr int, work []float64, lwork int) (first int) {
    72  	wantvl := jobvl == lapack.LeftEVCompute
    73  	wantvr := jobvr == lapack.RightEVCompute
    74  	var minwrk int
    75  	if wantvl || wantvr {
    76  		minwrk = max(1, 4*n)
    77  	} else {
    78  		minwrk = max(1, 3*n)
    79  	}
    80  	switch {
    81  	case jobvl != lapack.LeftEVCompute && jobvl != lapack.LeftEVNone:
    82  		panic(badLeftEVJob)
    83  	case jobvr != lapack.RightEVCompute && jobvr != lapack.RightEVNone:
    84  		panic(badRightEVJob)
    85  	case n < 0:
    86  		panic(nLT0)
    87  	case lda < max(1, n):
    88  		panic(badLdA)
    89  	case ldvl < 1 || (ldvl < n && wantvl):
    90  		panic(badLdVL)
    91  	case ldvr < 1 || (ldvr < n && wantvr):
    92  		panic(badLdVR)
    93  	case lwork < minwrk && lwork != -1:
    94  		panic(badLWork)
    95  	case len(work) < lwork:
    96  		panic(shortWork)
    97  	}
    98  
    99  	// Quick return if possible.
   100  	if n == 0 {
   101  		work[0] = 1
   102  		return 0
   103  	}
   104  
   105  	maxwrk := 2*n + n*impl.Ilaenv(1, "DGEHRD", " ", n, 1, n, 0)
   106  	if wantvl || wantvr {
   107  		maxwrk = max(maxwrk, 2*n+(n-1)*impl.Ilaenv(1, "DORGHR", " ", n, 1, n, -1))
   108  		impl.Dhseqr(lapack.EigenvaluesAndSchur, lapack.SchurOrig, n, 0, n-1,
   109  			a, lda, wr, wi, nil, n, work, -1)
   110  		maxwrk = max(maxwrk, max(n+1, n+int(work[0])))
   111  		side := lapack.EVLeft
   112  		if wantvr {
   113  			side = lapack.EVRight
   114  		}
   115  		impl.Dtrevc3(side, lapack.EVAllMulQ, nil, n, a, lda, vl, ldvl, vr, ldvr,
   116  			n, work, -1)
   117  		maxwrk = max(maxwrk, n+int(work[0]))
   118  		maxwrk = max(maxwrk, 4*n)
   119  	} else {
   120  		impl.Dhseqr(lapack.EigenvaluesOnly, lapack.SchurNone, n, 0, n-1,
   121  			a, lda, wr, wi, vr, ldvr, work, -1)
   122  		maxwrk = max(maxwrk, max(n+1, n+int(work[0])))
   123  	}
   124  	maxwrk = max(maxwrk, minwrk)
   125  
   126  	if lwork == -1 {
   127  		work[0] = float64(maxwrk)
   128  		return 0
   129  	}
   130  
   131  	switch {
   132  	case len(a) < (n-1)*lda+n:
   133  		panic(shortA)
   134  	case len(wr) != n:
   135  		panic(badLenWr)
   136  	case len(wi) != n:
   137  		panic(badLenWi)
   138  	case len(vl) < (n-1)*ldvl+n && wantvl:
   139  		panic(shortVL)
   140  	case len(vr) < (n-1)*ldvr+n && wantvr:
   141  		panic(shortVR)
   142  	}
   143  
   144  	// Get machine constants.
   145  	smlnum := math.Sqrt(dlamchS) / dlamchP
   146  	bignum := 1 / smlnum
   147  
   148  	// Scale A if max element outside range [smlnum,bignum].
   149  	anrm := impl.Dlange(lapack.MaxAbs, n, n, a, lda, nil)
   150  	var scalea bool
   151  	var cscale float64
   152  	if 0 < anrm && anrm < smlnum {
   153  		scalea = true
   154  		cscale = smlnum
   155  	} else if anrm > bignum {
   156  		scalea = true
   157  		cscale = bignum
   158  	}
   159  	if scalea {
   160  		impl.Dlascl(lapack.General, 0, 0, anrm, cscale, n, n, a, lda)
   161  	}
   162  
   163  	// Balance the matrix.
   164  	workbal := work[:n]
   165  	ilo, ihi := impl.Dgebal(lapack.PermuteScale, n, a, lda, workbal)
   166  
   167  	// Reduce to upper Hessenberg form.
   168  	iwrk := 2 * n
   169  	tau := work[n : iwrk-1]
   170  	impl.Dgehrd(n, ilo, ihi, a, lda, tau, work[iwrk:], lwork-iwrk)
   171  
   172  	var side lapack.EVSide
   173  	if wantvl {
   174  		side = lapack.EVLeft
   175  		// Copy Householder vectors to VL.
   176  		impl.Dlacpy(blas.Lower, n, n, a, lda, vl, ldvl)
   177  		// Generate orthogonal matrix in VL.
   178  		impl.Dorghr(n, ilo, ihi, vl, ldvl, tau, work[iwrk:], lwork-iwrk)
   179  		// Perform QR iteration, accumulating Schur vectors in VL.
   180  		iwrk = n
   181  		first = impl.Dhseqr(lapack.EigenvaluesAndSchur, lapack.SchurOrig, n, ilo, ihi,
   182  			a, lda, wr, wi, vl, ldvl, work[iwrk:], lwork-iwrk)
   183  		if wantvr {
   184  			// Want left and right eigenvectors.
   185  			// Copy Schur vectors to VR.
   186  			side = lapack.EVBoth
   187  			impl.Dlacpy(blas.All, n, n, vl, ldvl, vr, ldvr)
   188  		}
   189  	} else if wantvr {
   190  		side = lapack.EVRight
   191  		// Copy Householder vectors to VR.
   192  		impl.Dlacpy(blas.Lower, n, n, a, lda, vr, ldvr)
   193  		// Generate orthogonal matrix in VR.
   194  		impl.Dorghr(n, ilo, ihi, vr, ldvr, tau, work[iwrk:], lwork-iwrk)
   195  		// Perform QR iteration, accumulating Schur vectors in VR.
   196  		iwrk = n
   197  		first = impl.Dhseqr(lapack.EigenvaluesAndSchur, lapack.SchurOrig, n, ilo, ihi,
   198  			a, lda, wr, wi, vr, ldvr, work[iwrk:], lwork-iwrk)
   199  	} else {
   200  		// Compute eigenvalues only.
   201  		iwrk = n
   202  		first = impl.Dhseqr(lapack.EigenvaluesOnly, lapack.SchurNone, n, ilo, ihi,
   203  			a, lda, wr, wi, nil, 1, work[iwrk:], lwork-iwrk)
   204  	}
   205  
   206  	if first > 0 {
   207  		if scalea {
   208  			// Undo scaling.
   209  			impl.Dlascl(lapack.General, 0, 0, cscale, anrm, n-first, 1, wr[first:], 1)
   210  			impl.Dlascl(lapack.General, 0, 0, cscale, anrm, n-first, 1, wi[first:], 1)
   211  			impl.Dlascl(lapack.General, 0, 0, cscale, anrm, ilo, 1, wr, 1)
   212  			impl.Dlascl(lapack.General, 0, 0, cscale, anrm, ilo, 1, wi, 1)
   213  		}
   214  		work[0] = float64(maxwrk)
   215  		return first
   216  	}
   217  
   218  	if wantvl || wantvr {
   219  		// Compute left and/or right eigenvectors.
   220  		impl.Dtrevc3(side, lapack.EVAllMulQ, nil, n,
   221  			a, lda, vl, ldvl, vr, ldvr, n, work[iwrk:], lwork-iwrk)
   222  	}
   223  	bi := blas64.Implementation()
   224  	if wantvl {
   225  		// Undo balancing of left eigenvectors.
   226  		impl.Dgebak(lapack.PermuteScale, lapack.EVLeft, n, ilo, ihi, workbal, n, vl, ldvl)
   227  		// Normalize left eigenvectors and make largest component real.
   228  		for i, wii := range wi {
   229  			if wii < 0 {
   230  				continue
   231  			}
   232  			if wii == 0 {
   233  				scl := 1 / bi.Dnrm2(n, vl[i:], ldvl)
   234  				bi.Dscal(n, scl, vl[i:], ldvl)
   235  				continue
   236  			}
   237  			scl := 1 / impl.Dlapy2(bi.Dnrm2(n, vl[i:], ldvl), bi.Dnrm2(n, vl[i+1:], ldvl))
   238  			bi.Dscal(n, scl, vl[i:], ldvl)
   239  			bi.Dscal(n, scl, vl[i+1:], ldvl)
   240  			for k := 0; k < n; k++ {
   241  				vi := vl[k*ldvl+i]
   242  				vi1 := vl[k*ldvl+i+1]
   243  				work[iwrk+k] = vi*vi + vi1*vi1
   244  			}
   245  			k := bi.Idamax(n, work[iwrk:iwrk+n], 1)
   246  			cs, sn, _ := impl.Dlartg(vl[k*ldvl+i], vl[k*ldvl+i+1])
   247  			bi.Drot(n, vl[i:], ldvl, vl[i+1:], ldvl, cs, sn)
   248  			vl[k*ldvl+i+1] = 0
   249  		}
   250  	}
   251  	if wantvr {
   252  		// Undo balancing of right eigenvectors.
   253  		impl.Dgebak(lapack.PermuteScale, lapack.EVRight, n, ilo, ihi, workbal, n, vr, ldvr)
   254  		// Normalize right eigenvectors and make largest component real.
   255  		for i, wii := range wi {
   256  			if wii < 0 {
   257  				continue
   258  			}
   259  			if wii == 0 {
   260  				scl := 1 / bi.Dnrm2(n, vr[i:], ldvr)
   261  				bi.Dscal(n, scl, vr[i:], ldvr)
   262  				continue
   263  			}
   264  			scl := 1 / impl.Dlapy2(bi.Dnrm2(n, vr[i:], ldvr), bi.Dnrm2(n, vr[i+1:], ldvr))
   265  			bi.Dscal(n, scl, vr[i:], ldvr)
   266  			bi.Dscal(n, scl, vr[i+1:], ldvr)
   267  			for k := 0; k < n; k++ {
   268  				vi := vr[k*ldvr+i]
   269  				vi1 := vr[k*ldvr+i+1]
   270  				work[iwrk+k] = vi*vi + vi1*vi1
   271  			}
   272  			k := bi.Idamax(n, work[iwrk:iwrk+n], 1)
   273  			cs, sn, _ := impl.Dlartg(vr[k*ldvr+i], vr[k*ldvr+i+1])
   274  			bi.Drot(n, vr[i:], ldvr, vr[i+1:], ldvr, cs, sn)
   275  			vr[k*ldvr+i+1] = 0
   276  		}
   277  	}
   278  
   279  	if scalea {
   280  		// Undo scaling.
   281  		impl.Dlascl(lapack.General, 0, 0, cscale, anrm, n-first, 1, wr[first:], 1)
   282  		impl.Dlascl(lapack.General, 0, 0, cscale, anrm, n-first, 1, wi[first:], 1)
   283  	}
   284  
   285  	work[0] = float64(maxwrk)
   286  	return first
   287  }