eigen.go 9.3 KB

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