eigen.go 9.7 KB

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