123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- // Copyright ©2013 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 mat
- import (
- "gonum.org/v1/gonum/lapack"
- "gonum.org/v1/gonum/lapack/lapack64"
- )
- const (
- badFact = "mat: use without successful factorization"
- noVectors = "mat: eigenvectors not computed"
- )
- // EigenSym is a type for creating and manipulating the Eigen decomposition of
- // symmetric matrices.
- type EigenSym struct {
- vectorsComputed bool
- values []float64
- vectors *Dense
- }
- // Factorize computes the eigenvalue decomposition of the symmetric matrix a.
- // The Eigen decomposition is defined as
- // A = P * D * P^-1
- // where D is a diagonal matrix containing the eigenvalues of the matrix, and
- // P is a matrix of the eigenvectors of A. Factorize computes the eigenvalues
- // in ascending order. If the vectors input argument is false, the eigenvectors
- // are not computed.
- //
- // Factorize returns whether the decomposition succeeded. If the decomposition
- // failed, methods that require a successful factorization will panic.
- func (e *EigenSym) Factorize(a Symmetric, vectors bool) (ok bool) {
- // kill previous decomposition
- e.vectorsComputed = false
- e.values = e.values[:]
- n := a.Symmetric()
- sd := NewSymDense(n, nil)
- sd.CopySym(a)
- jobz := lapack.EVNone
- if vectors {
- jobz = lapack.EVCompute
- }
- w := make([]float64, n)
- work := []float64{0}
- lapack64.Syev(jobz, sd.mat, w, work, -1)
- work = getFloats(int(work[0]), false)
- ok = lapack64.Syev(jobz, sd.mat, w, work, len(work))
- putFloats(work)
- if !ok {
- e.vectorsComputed = false
- e.values = nil
- e.vectors = nil
- return false
- }
- e.vectorsComputed = vectors
- e.values = w
- e.vectors = NewDense(n, n, sd.mat.Data)
- return true
- }
- // succFact returns whether the receiver contains a successful factorization.
- func (e *EigenSym) succFact() bool {
- return len(e.values) != 0
- }
- // Values extracts the eigenvalues of the factorized matrix. If dst is
- // non-nil, the values are stored in-place into dst. In this case
- // dst must have length n, otherwise Values will panic. If dst is
- // nil, then a new slice will be allocated of the proper length and filled
- // with the eigenvalues.
- //
- // Values panics if the Eigen decomposition was not successful.
- func (e *EigenSym) Values(dst []float64) []float64 {
- if !e.succFact() {
- panic(badFact)
- }
- if dst == nil {
- dst = make([]float64, len(e.values))
- }
- if len(dst) != len(e.values) {
- panic(ErrSliceLengthMismatch)
- }
- copy(dst, e.values)
- return dst
- }
- // VectorsTo stores the eigenvectors of the decomposition into the columns of
- // dst.
- //
- // If dst is empty, VectorsTo will resize dst to be n×n. When dst is
- // non-empty, VectorsTo will panic if dst is not n×n. VectorsTo will also
- // panic if the eigenvectors were not computed during the factorization,
- // or if the receiver does not contain a successful factorization.
- func (e *EigenSym) VectorsTo(dst *Dense) {
- if !e.succFact() {
- panic(badFact)
- }
- if !e.vectorsComputed {
- panic(noVectors)
- }
- r, c := e.vectors.Dims()
- if dst.IsEmpty() {
- dst.ReuseAs(r, c)
- } else {
- r2, c2 := dst.Dims()
- if r != r2 || c != c2 {
- panic(ErrShape)
- }
- }
- dst.Copy(e.vectors)
- }
- // EigenKind specifies the computation of eigenvectors during factorization.
- type EigenKind int
- const (
- // EigenNone specifies to not compute any eigenvectors.
- EigenNone EigenKind = 0
- // EigenLeft specifies to compute the left eigenvectors.
- EigenLeft EigenKind = 1 << iota
- // EigenRight specifies to compute the right eigenvectors.
- EigenRight
- // EigenBoth is a convenience value for computing both eigenvectors.
- EigenBoth EigenKind = EigenLeft | EigenRight
- )
- // Eigen is a type for creating and using the eigenvalue decomposition of a dense matrix.
- type Eigen struct {
- n int // The size of the factorized matrix.
- kind EigenKind
- values []complex128
- rVectors *CDense
- lVectors *CDense
- }
- // succFact returns whether the receiver contains a successful factorization.
- func (e *Eigen) succFact() bool {
- return e.n != 0
- }
- // Factorize computes the eigenvalues of the square matrix a, and optionally
- // the eigenvectors.
- //
- // A right eigenvalue/eigenvector combination is defined by
- // A * x_r = λ * x_r
- // where x_r is the column vector called an eigenvector, and λ is the corresponding
- // eigenvalue.
- //
- // Similarly, a left eigenvalue/eigenvector combination is defined by
- // x_l * A = λ * x_l
- // The eigenvalues, but not the eigenvectors, are the same for both decompositions.
- //
- // Typically eigenvectors refer to right eigenvectors.
- //
- // In all cases, Factorize computes the eigenvalues of the matrix. kind
- // specifies which of the eigenvectors, if any, to compute. See the EigenKind
- // documentation for more information.
- // Eigen panics if the input matrix is not square.
- //
- // Factorize returns whether the decomposition succeeded. If the decomposition
- // failed, methods that require a successful factorization will panic.
- func (e *Eigen) Factorize(a Matrix, kind EigenKind) (ok bool) {
- // kill previous factorization.
- e.n = 0
- e.kind = 0
- // Copy a because it is modified during the Lapack call.
- r, c := a.Dims()
- if r != c {
- panic(ErrShape)
- }
- var sd Dense
- sd.CloneFrom(a)
- left := kind&EigenLeft != 0
- right := kind&EigenRight != 0
- var vl, vr Dense
- jobvl := lapack.LeftEVNone
- jobvr := lapack.RightEVNone
- if left {
- vl = *NewDense(r, r, nil)
- jobvl = lapack.LeftEVCompute
- }
- if right {
- vr = *NewDense(c, c, nil)
- jobvr = lapack.RightEVCompute
- }
- wr := getFloats(c, false)
- defer putFloats(wr)
- wi := getFloats(c, false)
- defer putFloats(wi)
- work := []float64{0}
- lapack64.Geev(jobvl, jobvr, sd.mat, wr, wi, vl.mat, vr.mat, work, -1)
- work = getFloats(int(work[0]), false)
- first := lapack64.Geev(jobvl, jobvr, sd.mat, wr, wi, vl.mat, vr.mat, work, len(work))
- putFloats(work)
- if first != 0 {
- e.values = nil
- return false
- }
- e.n = r
- e.kind = kind
- // Construct complex eigenvalues from float64 data.
- values := make([]complex128, r)
- for i, v := range wr {
- values[i] = complex(v, wi[i])
- }
- e.values = values
- // Construct complex eigenvectors from float64 data.
- var cvl, cvr CDense
- if left {
- cvl = *NewCDense(r, r, nil)
- e.complexEigenTo(&cvl, &vl)
- e.lVectors = &cvl
- } else {
- e.lVectors = nil
- }
- if right {
- cvr = *NewCDense(c, c, nil)
- e.complexEigenTo(&cvr, &vr)
- e.rVectors = &cvr
- } else {
- e.rVectors = nil
- }
- return true
- }
- // Kind returns the EigenKind of the decomposition. If no decomposition has been
- // computed, Kind returns -1.
- func (e *Eigen) Kind() EigenKind {
- if !e.succFact() {
- return -1
- }
- return e.kind
- }
- // Values extracts the eigenvalues of the factorized matrix. If dst is
- // non-nil, the values are stored in-place into dst. In this case
- // dst must have length n, otherwise Values will panic. If dst is
- // nil, then a new slice will be allocated of the proper length and
- // filed with the eigenvalues.
- //
- // Values panics if the Eigen decomposition was not successful.
- func (e *Eigen) Values(dst []complex128) []complex128 {
- if !e.succFact() {
- panic(badFact)
- }
- if dst == nil {
- dst = make([]complex128, e.n)
- }
- if len(dst) != e.n {
- panic(ErrSliceLengthMismatch)
- }
- copy(dst, e.values)
- return dst
- }
- // complexEigenTo extracts the complex eigenvectors from the real matrix d
- // and stores them into the complex matrix dst.
- //
- // The columns of the returned n×n dense matrix contain the eigenvectors of the
- // decomposition in the same order as the eigenvalues.
- // If the j-th eigenvalue is real, then
- // dst[:,j] = d[:,j],
- // and if it is not real, then the elements of the j-th and (j+1)-th columns of d
- // form complex conjugate pairs and the eigenvectors are recovered as
- // dst[:,j] = d[:,j] + i*d[:,j+1],
- // dst[:,j+1] = d[:,j] - i*d[:,j+1],
- // where i is the imaginary unit.
- func (e *Eigen) complexEigenTo(dst *CDense, d *Dense) {
- r, c := d.Dims()
- cr, cc := dst.Dims()
- if r != cr {
- panic("size mismatch")
- }
- if c != cc {
- panic("size mismatch")
- }
- for j := 0; j < c; j++ {
- if imag(e.values[j]) == 0 {
- for i := 0; i < r; i++ {
- dst.set(i, j, complex(d.at(i, j), 0))
- }
- continue
- }
- for i := 0; i < r; i++ {
- real := d.at(i, j)
- imag := d.at(i, j+1)
- dst.set(i, j, complex(real, imag))
- dst.set(i, j+1, complex(real, -imag))
- }
- j++
- }
- }
- // VectorsTo stores the right eigenvectors of the decomposition into the columns
- // of dst. The computed eigenvectors are normalized to have Euclidean norm equal
- // to 1 and largest component real.
- //
- // If dst is empty, VectorsTo will resize dst to be n×n. When dst is
- // non-empty, VectorsTo will panic if dst is not n×n. VectorsTo will also
- // panic if the eigenvectors were not computed during the factorization,
- // or if the receiver does not contain a successful factorization.
- func (e *Eigen) VectorsTo(dst *CDense) {
- if !e.succFact() {
- panic(badFact)
- }
- if e.kind&EigenRight == 0 {
- panic(noVectors)
- }
- if dst.IsEmpty() {
- dst.ReuseAs(e.n, e.n)
- } else {
- r, c := dst.Dims()
- if r != e.n || c != e.n {
- panic(ErrShape)
- }
- }
- dst.Copy(e.rVectors)
- }
- // LeftVectorsTo stores the left eigenvectors of the decomposition into the
- // columns of dst. The computed eigenvectors are normalized to have Euclidean
- // norm equal to 1 and largest component real.
- //
- // If dst is empty, LeftVectorsTo will resize dst to be n×n. When dst is
- // non-empty, LeftVectorsTo will panic if dst is not n×n. LeftVectorsTo will also
- // panic if the left eigenvectors were not computed during the factorization,
- // or if the receiver does not contain a successful factorization
- func (e *Eigen) LeftVectorsTo(dst *CDense) {
- if !e.succFact() {
- panic(badFact)
- }
- if e.kind&EigenLeft == 0 {
- panic(noVectors)
- }
- if dst.IsEmpty() {
- dst.ReuseAs(e.n, e.n)
- } else {
- r, c := dst.Dims()
- if r != e.n || c != e.n {
- panic(ErrShape)
- }
- }
- dst.Copy(e.lVectors)
- }
|