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