github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/mat/eigen.go (about)

     1  // Copyright ©2013 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 mat
     6  
     7  import (
     8  	"github.com/jingcheng-WU/gonum/lapack"
     9  	"github.com/jingcheng-WU/gonum/lapack/lapack64"
    10  )
    11  
    12  const (
    13  	badFact   = "mat: use without successful factorization"
    14  	noVectors = "mat: eigenvectors not computed"
    15  )
    16  
    17  // EigenSym is a type for creating and manipulating the Eigen decomposition of
    18  // symmetric matrices.
    19  type EigenSym struct {
    20  	vectorsComputed bool
    21  
    22  	values  []float64
    23  	vectors *Dense
    24  }
    25  
    26  // Factorize computes the eigenvalue decomposition of the symmetric matrix a.
    27  // The Eigen decomposition is defined as
    28  //  A = P * D * P^-1
    29  // where D is a diagonal matrix containing the eigenvalues of the matrix, and
    30  // P is a matrix of the eigenvectors of A. Factorize computes the eigenvalues
    31  // in ascending order. If the vectors input argument is false, the eigenvectors
    32  // are not computed.
    33  //
    34  // Factorize returns whether the decomposition succeeded. If the decomposition
    35  // failed, methods that require a successful factorization will panic.
    36  func (e *EigenSym) Factorize(a Symmetric, vectors bool) (ok bool) {
    37  	// kill previous decomposition
    38  	e.vectorsComputed = false
    39  	e.values = e.values[:]
    40  
    41  	n := a.Symmetric()
    42  	sd := NewSymDense(n, nil)
    43  	sd.CopySym(a)
    44  
    45  	jobz := lapack.EVNone
    46  	if vectors {
    47  		jobz = lapack.EVCompute
    48  	}
    49  	w := make([]float64, n)
    50  	work := []float64{0}
    51  	lapack64.Syev(jobz, sd.mat, w, work, -1)
    52  
    53  	work = getFloats(int(work[0]), false)
    54  	ok = lapack64.Syev(jobz, sd.mat, w, work, len(work))
    55  	putFloats(work)
    56  	if !ok {
    57  		e.vectorsComputed = false
    58  		e.values = nil
    59  		e.vectors = nil
    60  		return false
    61  	}
    62  	e.vectorsComputed = vectors
    63  	e.values = w
    64  	e.vectors = NewDense(n, n, sd.mat.Data)
    65  	return true
    66  }
    67  
    68  // succFact returns whether the receiver contains a successful factorization.
    69  func (e *EigenSym) succFact() bool {
    70  	return len(e.values) != 0
    71  }
    72  
    73  // Values extracts the eigenvalues of the factorized matrix. If dst is
    74  // non-nil, the values are stored in-place into dst. In this case
    75  // dst must have length n, otherwise Values will panic. If dst is
    76  // nil, then a new slice will be allocated of the proper length and filled
    77  // with the eigenvalues.
    78  //
    79  // Values panics if the Eigen decomposition was not successful.
    80  func (e *EigenSym) Values(dst []float64) []float64 {
    81  	if !e.succFact() {
    82  		panic(badFact)
    83  	}
    84  	if dst == nil {
    85  		dst = make([]float64, len(e.values))
    86  	}
    87  	if len(dst) != len(e.values) {
    88  		panic(ErrSliceLengthMismatch)
    89  	}
    90  	copy(dst, e.values)
    91  	return dst
    92  }
    93  
    94  // VectorsTo stores the eigenvectors of the decomposition into the columns of
    95  // dst.
    96  //
    97  // If dst is empty, VectorsTo will resize dst to be n×n. When dst is
    98  // non-empty, VectorsTo will panic if dst is not n×n. VectorsTo will also
    99  // panic if the eigenvectors were not computed during the factorization,
   100  // or if the receiver does not contain a successful factorization.
   101  func (e *EigenSym) VectorsTo(dst *Dense) {
   102  	if !e.succFact() {
   103  		panic(badFact)
   104  	}
   105  	if !e.vectorsComputed {
   106  		panic(noVectors)
   107  	}
   108  	r, c := e.vectors.Dims()
   109  	if dst.IsEmpty() {
   110  		dst.ReuseAs(r, c)
   111  	} else {
   112  		r2, c2 := dst.Dims()
   113  		if r != r2 || c != c2 {
   114  			panic(ErrShape)
   115  		}
   116  	}
   117  	dst.Copy(e.vectors)
   118  }
   119  
   120  // EigenKind specifies the computation of eigenvectors during factorization.
   121  type EigenKind int
   122  
   123  const (
   124  	// EigenNone specifies to not compute any eigenvectors.
   125  	EigenNone EigenKind = 0
   126  	// EigenLeft specifies to compute the left eigenvectors.
   127  	EigenLeft EigenKind = 1 << iota
   128  	// EigenRight specifies to compute the right eigenvectors.
   129  	EigenRight
   130  	// EigenBoth is a convenience value for computing both eigenvectors.
   131  	EigenBoth EigenKind = EigenLeft | EigenRight
   132  )
   133  
   134  // Eigen is a type for creating and using the eigenvalue decomposition of a dense matrix.
   135  type Eigen struct {
   136  	n int // The size of the factorized matrix.
   137  
   138  	kind EigenKind
   139  
   140  	values   []complex128
   141  	rVectors *CDense
   142  	lVectors *CDense
   143  }
   144  
   145  // succFact returns whether the receiver contains a successful factorization.
   146  func (e *Eigen) succFact() bool {
   147  	return e.n != 0
   148  }
   149  
   150  // Factorize computes the eigenvalues of the square matrix a, and optionally
   151  // the eigenvectors.
   152  //
   153  // A right eigenvalue/eigenvector combination is defined by
   154  //  A * x_r = λ * x_r
   155  // where x_r is the column vector called an eigenvector, and λ is the corresponding
   156  // eigenvalue.
   157  //
   158  // Similarly, a left eigenvalue/eigenvector combination is defined by
   159  //  x_l * A = λ * x_l
   160  // The eigenvalues, but not the eigenvectors, are the same for both decompositions.
   161  //
   162  // Typically eigenvectors refer to right eigenvectors.
   163  //
   164  // In all cases, Factorize computes the eigenvalues of the matrix. kind
   165  // specifies which of the eigenvectors, if any, to compute. See the EigenKind
   166  // documentation for more information.
   167  // Eigen panics if the input matrix is not square.
   168  //
   169  // Factorize returns whether the decomposition succeeded. If the decomposition
   170  // failed, methods that require a successful factorization will panic.
   171  func (e *Eigen) Factorize(a Matrix, kind EigenKind) (ok bool) {
   172  	// kill previous factorization.
   173  	e.n = 0
   174  	e.kind = 0
   175  	// Copy a because it is modified during the Lapack call.
   176  	r, c := a.Dims()
   177  	if r != c {
   178  		panic(ErrShape)
   179  	}
   180  	var sd Dense
   181  	sd.CloneFrom(a)
   182  
   183  	left := kind&EigenLeft != 0
   184  	right := kind&EigenRight != 0
   185  
   186  	var vl, vr Dense
   187  	jobvl := lapack.LeftEVNone
   188  	jobvr := lapack.RightEVNone
   189  	if left {
   190  		vl = *NewDense(r, r, nil)
   191  		jobvl = lapack.LeftEVCompute
   192  	}
   193  	if right {
   194  		vr = *NewDense(c, c, nil)
   195  		jobvr = lapack.RightEVCompute
   196  	}
   197  
   198  	wr := getFloats(c, false)
   199  	defer putFloats(wr)
   200  	wi := getFloats(c, false)
   201  	defer putFloats(wi)
   202  
   203  	work := []float64{0}
   204  	lapack64.Geev(jobvl, jobvr, sd.mat, wr, wi, vl.mat, vr.mat, work, -1)
   205  	work = getFloats(int(work[0]), false)
   206  	first := lapack64.Geev(jobvl, jobvr, sd.mat, wr, wi, vl.mat, vr.mat, work, len(work))
   207  	putFloats(work)
   208  
   209  	if first != 0 {
   210  		e.values = nil
   211  		return false
   212  	}
   213  	e.n = r
   214  	e.kind = kind
   215  
   216  	// Construct complex eigenvalues from float64 data.
   217  	values := make([]complex128, r)
   218  	for i, v := range wr {
   219  		values[i] = complex(v, wi[i])
   220  	}
   221  	e.values = values
   222  
   223  	// Construct complex eigenvectors from float64 data.
   224  	var cvl, cvr CDense
   225  	if left {
   226  		cvl = *NewCDense(r, r, nil)
   227  		e.complexEigenTo(&cvl, &vl)
   228  		e.lVectors = &cvl
   229  	} else {
   230  		e.lVectors = nil
   231  	}
   232  	if right {
   233  		cvr = *NewCDense(c, c, nil)
   234  		e.complexEigenTo(&cvr, &vr)
   235  		e.rVectors = &cvr
   236  	} else {
   237  		e.rVectors = nil
   238  	}
   239  	return true
   240  }
   241  
   242  // Kind returns the EigenKind of the decomposition. If no decomposition has been
   243  // computed, Kind returns -1.
   244  func (e *Eigen) Kind() EigenKind {
   245  	if !e.succFact() {
   246  		return -1
   247  	}
   248  	return e.kind
   249  }
   250  
   251  // Values extracts the eigenvalues of the factorized matrix. If dst is
   252  // non-nil, the values are stored in-place into dst. In this case
   253  // dst must have length n, otherwise Values will panic. If dst is
   254  // nil, then a new slice will be allocated of the proper length and
   255  // filed with the eigenvalues.
   256  //
   257  // Values panics if the Eigen decomposition was not successful.
   258  func (e *Eigen) Values(dst []complex128) []complex128 {
   259  	if !e.succFact() {
   260  		panic(badFact)
   261  	}
   262  	if dst == nil {
   263  		dst = make([]complex128, e.n)
   264  	}
   265  	if len(dst) != e.n {
   266  		panic(ErrSliceLengthMismatch)
   267  	}
   268  	copy(dst, e.values)
   269  	return dst
   270  }
   271  
   272  // complexEigenTo extracts the complex eigenvectors from the real matrix d
   273  // and stores them into the complex matrix dst.
   274  //
   275  // The columns of the returned n×n dense matrix contain the eigenvectors of the
   276  // decomposition in the same order as the eigenvalues.
   277  // If the j-th eigenvalue is real, then
   278  //  dst[:,j] = d[:,j],
   279  // and if it is not real, then the elements of the j-th and (j+1)-th columns of d
   280  // form complex conjugate pairs and the eigenvectors are recovered as
   281  //  dst[:,j]   = d[:,j] + i*d[:,j+1],
   282  //  dst[:,j+1] = d[:,j] - i*d[:,j+1],
   283  // where i is the imaginary unit.
   284  func (e *Eigen) complexEigenTo(dst *CDense, d *Dense) {
   285  	r, c := d.Dims()
   286  	cr, cc := dst.Dims()
   287  	if r != cr {
   288  		panic("size mismatch")
   289  	}
   290  	if c != cc {
   291  		panic("size mismatch")
   292  	}
   293  	for j := 0; j < c; j++ {
   294  		if imag(e.values[j]) == 0 {
   295  			for i := 0; i < r; i++ {
   296  				dst.set(i, j, complex(d.at(i, j), 0))
   297  			}
   298  			continue
   299  		}
   300  		for i := 0; i < r; i++ {
   301  			real := d.at(i, j)
   302  			imag := d.at(i, j+1)
   303  			dst.set(i, j, complex(real, imag))
   304  			dst.set(i, j+1, complex(real, -imag))
   305  		}
   306  		j++
   307  	}
   308  }
   309  
   310  // VectorsTo stores the right eigenvectors of the decomposition into the columns
   311  // of dst. The computed eigenvectors are normalized to have Euclidean norm equal
   312  // to 1 and largest component real.
   313  //
   314  // If dst is empty, VectorsTo will resize dst to be n×n. When dst is
   315  // non-empty, VectorsTo will panic if dst is not n×n. VectorsTo will also
   316  // panic if the eigenvectors were not computed during the factorization,
   317  // or if the receiver does not contain a successful factorization.
   318  func (e *Eigen) VectorsTo(dst *CDense) {
   319  	if !e.succFact() {
   320  		panic(badFact)
   321  	}
   322  	if e.kind&EigenRight == 0 {
   323  		panic(noVectors)
   324  	}
   325  	if dst.IsEmpty() {
   326  		dst.ReuseAs(e.n, e.n)
   327  	} else {
   328  		r, c := dst.Dims()
   329  		if r != e.n || c != e.n {
   330  			panic(ErrShape)
   331  		}
   332  	}
   333  	dst.Copy(e.rVectors)
   334  }
   335  
   336  // LeftVectorsTo stores the left eigenvectors of the decomposition into the
   337  // columns of dst. The computed eigenvectors are normalized to have Euclidean
   338  // norm equal to 1 and largest component real.
   339  //
   340  // If dst is empty, LeftVectorsTo will resize dst to be n×n. When dst is
   341  // non-empty, LeftVectorsTo will panic if dst is not n×n. LeftVectorsTo will also
   342  // panic if the left eigenvectors were not computed during the factorization,
   343  // or if the receiver does not contain a successful factorization
   344  func (e *Eigen) LeftVectorsTo(dst *CDense) {
   345  	if !e.succFact() {
   346  		panic(badFact)
   347  	}
   348  	if e.kind&EigenLeft == 0 {
   349  		panic(noVectors)
   350  	}
   351  	if dst.IsEmpty() {
   352  		dst.ReuseAs(e.n, e.n)
   353  	} else {
   354  		r, c := dst.Dims()
   355  		if r != e.n || c != e.n {
   356  			panic(ErrShape)
   357  		}
   358  	}
   359  	dst.Copy(e.lVectors)
   360  }