symmetric.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. // Copyright ©2015 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. "math"
  7. "gonum.org/v1/gonum/blas"
  8. "gonum.org/v1/gonum/blas/blas64"
  9. )
  10. var (
  11. symDense *SymDense
  12. _ Matrix = symDense
  13. _ allMatrix = symDense
  14. _ denseMatrix = symDense
  15. _ Symmetric = symDense
  16. _ RawSymmetricer = symDense
  17. _ MutableSymmetric = symDense
  18. )
  19. const (
  20. badSymTriangle = "mat: blas64.Symmetric not upper"
  21. badSymCap = "mat: bad capacity for SymDense"
  22. )
  23. // SymDense is a symmetric matrix that uses dense storage. SymDense
  24. // matrices are stored in the upper triangle.
  25. type SymDense struct {
  26. mat blas64.Symmetric
  27. cap int
  28. }
  29. // Symmetric represents a symmetric matrix (where the element at {i, j} equals
  30. // the element at {j, i}). Symmetric matrices are always square.
  31. type Symmetric interface {
  32. Matrix
  33. // Symmetric returns the number of rows/columns in the matrix.
  34. Symmetric() int
  35. }
  36. // A RawSymmetricer can return a view of itself as a BLAS Symmetric matrix.
  37. type RawSymmetricer interface {
  38. RawSymmetric() blas64.Symmetric
  39. }
  40. // A MutableSymmetric can set elements of a symmetric matrix.
  41. type MutableSymmetric interface {
  42. Symmetric
  43. SetSym(i, j int, v float64)
  44. }
  45. // NewSymDense creates a new Symmetric matrix with n rows and columns. If data == nil,
  46. // a new slice is allocated for the backing slice. If len(data) == n*n, data is
  47. // used as the backing slice, and changes to the elements of the returned SymDense
  48. // will be reflected in data. If neither of these is true, NewSymDense will panic.
  49. // NewSymDense will panic if n is zero.
  50. //
  51. // The data must be arranged in row-major order, i.e. the (i*c + j)-th
  52. // element in the data slice is the {i, j}-th element in the matrix.
  53. // Only the values in the upper triangular portion of the matrix are used.
  54. func NewSymDense(n int, data []float64) *SymDense {
  55. if n <= 0 {
  56. if n == 0 {
  57. panic(ErrZeroLength)
  58. }
  59. panic("mat: negative dimension")
  60. }
  61. if data != nil && n*n != len(data) {
  62. panic(ErrShape)
  63. }
  64. if data == nil {
  65. data = make([]float64, n*n)
  66. }
  67. return &SymDense{
  68. mat: blas64.Symmetric{
  69. N: n,
  70. Stride: n,
  71. Data: data,
  72. Uplo: blas.Upper,
  73. },
  74. cap: n,
  75. }
  76. }
  77. // Dims returns the number of rows and columns in the matrix.
  78. func (s *SymDense) Dims() (r, c int) {
  79. return s.mat.N, s.mat.N
  80. }
  81. // Caps returns the number of rows and columns in the backing matrix.
  82. func (s *SymDense) Caps() (r, c int) {
  83. return s.cap, s.cap
  84. }
  85. // T returns the receiver, the transpose of a symmetric matrix.
  86. func (s *SymDense) T() Matrix {
  87. return s
  88. }
  89. // Symmetric implements the Symmetric interface and returns the number of rows
  90. // and columns in the matrix.
  91. func (s *SymDense) Symmetric() int {
  92. return s.mat.N
  93. }
  94. // RawSymmetric returns the matrix as a blas64.Symmetric. The returned
  95. // value must be stored in upper triangular format.
  96. func (s *SymDense) RawSymmetric() blas64.Symmetric {
  97. return s.mat
  98. }
  99. // SetRawSymmetric sets the underlying blas64.Symmetric used by the receiver.
  100. // Changes to elements in the receiver following the call will be reflected
  101. // in the input.
  102. //
  103. // The supplied Symmetric must use blas.Upper storage format.
  104. func (s *SymDense) SetRawSymmetric(mat blas64.Symmetric) {
  105. if mat.Uplo != blas.Upper {
  106. panic(badSymTriangle)
  107. }
  108. s.cap = mat.N
  109. s.mat = mat
  110. }
  111. // Reset empties the matrix so that it can be reused as the
  112. // receiver of a dimensionally restricted operation.
  113. //
  114. // Reset should not be used when the matrix shares backing data.
  115. // See the Reseter interface for more information.
  116. func (s *SymDense) Reset() {
  117. // N and Stride must be zeroed in unison.
  118. s.mat.N, s.mat.Stride = 0, 0
  119. s.mat.Data = s.mat.Data[:0]
  120. }
  121. // ReuseAsSym changes the receiver if it IsEmpty() to be of size n×n.
  122. //
  123. // ReuseAsSym re-uses the backing data slice if it has sufficient capacity,
  124. // otherwise a new slice is allocated. The backing data is zero on return.
  125. //
  126. // ReuseAsSym panics if the receiver is not empty, and panics if
  127. // the input size is less than one. To empty the receiver for re-use,
  128. // Reset should be used.
  129. func (s *SymDense) ReuseAsSym(n int) {
  130. if n <= 0 {
  131. if n == 0 {
  132. panic(ErrZeroLength)
  133. }
  134. panic(ErrNegativeDimension)
  135. }
  136. if !s.IsEmpty() {
  137. panic(ErrReuseNonEmpty)
  138. }
  139. s.reuseAsZeroed(n)
  140. }
  141. // Zero sets all of the matrix elements to zero.
  142. func (s *SymDense) Zero() {
  143. for i := 0; i < s.mat.N; i++ {
  144. zero(s.mat.Data[i*s.mat.Stride+i : i*s.mat.Stride+s.mat.N])
  145. }
  146. }
  147. // IsEmpty returns whether the receiver is empty. Empty matrices can be the
  148. // receiver for size-restricted operations. The receiver can be emptied using
  149. // Reset.
  150. func (s *SymDense) IsEmpty() bool {
  151. // It must be the case that m.Dims() returns
  152. // zeros in this case. See comment in Reset().
  153. return s.mat.N == 0
  154. }
  155. // reuseAsNonZeroed resizes an empty matrix to a n×n matrix,
  156. // or checks that a non-empty matrix is n×n.
  157. func (s *SymDense) reuseAsNonZeroed(n int) {
  158. // reuseAsNonZeroed must be kept in sync with reuseAsZeroed.
  159. if n == 0 {
  160. panic(ErrZeroLength)
  161. }
  162. if s.mat.N > s.cap {
  163. panic(badSymCap)
  164. }
  165. if s.IsEmpty() {
  166. s.mat = blas64.Symmetric{
  167. N: n,
  168. Stride: n,
  169. Data: use(s.mat.Data, n*n),
  170. Uplo: blas.Upper,
  171. }
  172. s.cap = n
  173. return
  174. }
  175. if s.mat.Uplo != blas.Upper {
  176. panic(badSymTriangle)
  177. }
  178. if s.mat.N != n {
  179. panic(ErrShape)
  180. }
  181. }
  182. // reuseAsNonZeroed resizes an empty matrix to a n×n matrix,
  183. // or checks that a non-empty matrix is n×n. It then zeros the
  184. // elements of the matrix.
  185. func (s *SymDense) reuseAsZeroed(n int) {
  186. // reuseAsZeroed must be kept in sync with reuseAsNonZeroed.
  187. if n == 0 {
  188. panic(ErrZeroLength)
  189. }
  190. if s.mat.N > s.cap {
  191. panic(badSymCap)
  192. }
  193. if s.IsEmpty() {
  194. s.mat = blas64.Symmetric{
  195. N: n,
  196. Stride: n,
  197. Data: useZeroed(s.mat.Data, n*n),
  198. Uplo: blas.Upper,
  199. }
  200. s.cap = n
  201. return
  202. }
  203. if s.mat.Uplo != blas.Upper {
  204. panic(badSymTriangle)
  205. }
  206. if s.mat.N != n {
  207. panic(ErrShape)
  208. }
  209. s.Zero()
  210. }
  211. func (s *SymDense) isolatedWorkspace(a Symmetric) (w *SymDense, restore func()) {
  212. n := a.Symmetric()
  213. if n == 0 {
  214. panic(ErrZeroLength)
  215. }
  216. w = getWorkspaceSym(n, false)
  217. return w, func() {
  218. s.CopySym(w)
  219. putWorkspaceSym(w)
  220. }
  221. }
  222. // DiagView returns the diagonal as a matrix backed by the original data.
  223. func (s *SymDense) DiagView() Diagonal {
  224. n := s.mat.N
  225. return &DiagDense{
  226. mat: blas64.Vector{
  227. N: n,
  228. Inc: s.mat.Stride + 1,
  229. Data: s.mat.Data[:(n-1)*s.mat.Stride+n],
  230. },
  231. }
  232. }
  233. func (s *SymDense) AddSym(a, b Symmetric) {
  234. n := a.Symmetric()
  235. if n != b.Symmetric() {
  236. panic(ErrShape)
  237. }
  238. s.reuseAsNonZeroed(n)
  239. if a, ok := a.(RawSymmetricer); ok {
  240. if b, ok := b.(RawSymmetricer); ok {
  241. amat, bmat := a.RawSymmetric(), b.RawSymmetric()
  242. if s != a {
  243. s.checkOverlap(generalFromSymmetric(amat))
  244. }
  245. if s != b {
  246. s.checkOverlap(generalFromSymmetric(bmat))
  247. }
  248. for i := 0; i < n; i++ {
  249. btmp := bmat.Data[i*bmat.Stride+i : i*bmat.Stride+n]
  250. stmp := s.mat.Data[i*s.mat.Stride+i : i*s.mat.Stride+n]
  251. for j, v := range amat.Data[i*amat.Stride+i : i*amat.Stride+n] {
  252. stmp[j] = v + btmp[j]
  253. }
  254. }
  255. return
  256. }
  257. }
  258. s.checkOverlapMatrix(a)
  259. s.checkOverlapMatrix(b)
  260. for i := 0; i < n; i++ {
  261. stmp := s.mat.Data[i*s.mat.Stride : i*s.mat.Stride+n]
  262. for j := i; j < n; j++ {
  263. stmp[j] = a.At(i, j) + b.At(i, j)
  264. }
  265. }
  266. }
  267. func (s *SymDense) CopySym(a Symmetric) int {
  268. n := a.Symmetric()
  269. n = min(n, s.mat.N)
  270. if n == 0 {
  271. return 0
  272. }
  273. switch a := a.(type) {
  274. case RawSymmetricer:
  275. amat := a.RawSymmetric()
  276. if amat.Uplo != blas.Upper {
  277. panic(badSymTriangle)
  278. }
  279. for i := 0; i < n; i++ {
  280. copy(s.mat.Data[i*s.mat.Stride+i:i*s.mat.Stride+n], amat.Data[i*amat.Stride+i:i*amat.Stride+n])
  281. }
  282. default:
  283. for i := 0; i < n; i++ {
  284. stmp := s.mat.Data[i*s.mat.Stride : i*s.mat.Stride+n]
  285. for j := i; j < n; j++ {
  286. stmp[j] = a.At(i, j)
  287. }
  288. }
  289. }
  290. return n
  291. }
  292. // SymRankOne performs a symmetric rank-one update to the matrix a with x,
  293. // which is treated as a column vector, and stores the result in the receiver
  294. // s = a + alpha * x * xᵀ
  295. func (s *SymDense) SymRankOne(a Symmetric, alpha float64, x Vector) {
  296. n := x.Len()
  297. if a.Symmetric() != n {
  298. panic(ErrShape)
  299. }
  300. s.reuseAsNonZeroed(n)
  301. if s != a {
  302. if rs, ok := a.(RawSymmetricer); ok {
  303. s.checkOverlap(generalFromSymmetric(rs.RawSymmetric()))
  304. }
  305. s.CopySym(a)
  306. }
  307. xU, _ := untransposeExtract(x)
  308. if rv, ok := xU.(*VecDense); ok {
  309. r, c := xU.Dims()
  310. xmat := rv.mat
  311. s.checkOverlap(generalFromVector(xmat, r, c))
  312. blas64.Syr(alpha, xmat, s.mat)
  313. return
  314. }
  315. for i := 0; i < n; i++ {
  316. for j := i; j < n; j++ {
  317. s.set(i, j, s.at(i, j)+alpha*x.AtVec(i)*x.AtVec(j))
  318. }
  319. }
  320. }
  321. // SymRankK performs a symmetric rank-k update to the matrix a and stores the
  322. // result into the receiver. If a is zero, see SymOuterK.
  323. // s = a + alpha * x * x'
  324. func (s *SymDense) SymRankK(a Symmetric, alpha float64, x Matrix) {
  325. n := a.Symmetric()
  326. r, _ := x.Dims()
  327. if r != n {
  328. panic(ErrShape)
  329. }
  330. xMat, aTrans := untransposeExtract(x)
  331. var g blas64.General
  332. if rm, ok := xMat.(*Dense); ok {
  333. g = rm.mat
  334. } else {
  335. g = DenseCopyOf(x).mat
  336. aTrans = false
  337. }
  338. if a != s {
  339. if rs, ok := a.(RawSymmetricer); ok {
  340. s.checkOverlap(generalFromSymmetric(rs.RawSymmetric()))
  341. }
  342. s.reuseAsNonZeroed(n)
  343. s.CopySym(a)
  344. }
  345. t := blas.NoTrans
  346. if aTrans {
  347. t = blas.Trans
  348. }
  349. blas64.Syrk(t, alpha, g, 1, s.mat)
  350. }
  351. // SymOuterK calculates the outer product of x with itself and stores
  352. // the result into the receiver. It is equivalent to the matrix
  353. // multiplication
  354. // s = alpha * x * x'.
  355. // In order to update an existing matrix, see SymRankOne.
  356. func (s *SymDense) SymOuterK(alpha float64, x Matrix) {
  357. n, _ := x.Dims()
  358. switch {
  359. case s.IsEmpty():
  360. s.mat = blas64.Symmetric{
  361. N: n,
  362. Stride: n,
  363. Data: useZeroed(s.mat.Data, n*n),
  364. Uplo: blas.Upper,
  365. }
  366. s.cap = n
  367. s.SymRankK(s, alpha, x)
  368. case s.mat.Uplo != blas.Upper:
  369. panic(badSymTriangle)
  370. case s.mat.N == n:
  371. if s == x {
  372. w := getWorkspaceSym(n, true)
  373. w.SymRankK(w, alpha, x)
  374. s.CopySym(w)
  375. putWorkspaceSym(w)
  376. } else {
  377. switch r := x.(type) {
  378. case RawMatrixer:
  379. s.checkOverlap(r.RawMatrix())
  380. case RawSymmetricer:
  381. s.checkOverlap(generalFromSymmetric(r.RawSymmetric()))
  382. case RawTriangular:
  383. s.checkOverlap(generalFromTriangular(r.RawTriangular()))
  384. }
  385. // Only zero the upper triangle.
  386. for i := 0; i < n; i++ {
  387. ri := i * s.mat.Stride
  388. zero(s.mat.Data[ri+i : ri+n])
  389. }
  390. s.SymRankK(s, alpha, x)
  391. }
  392. default:
  393. panic(ErrShape)
  394. }
  395. }
  396. // RankTwo performs a symmetric rank-two update to the matrix a with the
  397. // vectors x and y, which are treated as column vectors, and stores the
  398. // result in the receiver
  399. // m = a + alpha * (x * yᵀ + y * xᵀ)
  400. func (s *SymDense) RankTwo(a Symmetric, alpha float64, x, y Vector) {
  401. n := s.mat.N
  402. if x.Len() != n {
  403. panic(ErrShape)
  404. }
  405. if y.Len() != n {
  406. panic(ErrShape)
  407. }
  408. if s != a {
  409. if rs, ok := a.(RawSymmetricer); ok {
  410. s.checkOverlap(generalFromSymmetric(rs.RawSymmetric()))
  411. }
  412. }
  413. var xmat, ymat blas64.Vector
  414. fast := true
  415. xU, _ := untransposeExtract(x)
  416. if rv, ok := xU.(*VecDense); ok {
  417. r, c := xU.Dims()
  418. xmat = rv.mat
  419. s.checkOverlap(generalFromVector(xmat, r, c))
  420. } else {
  421. fast = false
  422. }
  423. yU, _ := untransposeExtract(y)
  424. if rv, ok := yU.(*VecDense); ok {
  425. r, c := yU.Dims()
  426. ymat = rv.mat
  427. s.checkOverlap(generalFromVector(ymat, r, c))
  428. } else {
  429. fast = false
  430. }
  431. if s != a {
  432. if rs, ok := a.(RawSymmetricer); ok {
  433. s.checkOverlap(generalFromSymmetric(rs.RawSymmetric()))
  434. }
  435. s.reuseAsNonZeroed(n)
  436. s.CopySym(a)
  437. }
  438. if fast {
  439. if s != a {
  440. s.reuseAsNonZeroed(n)
  441. s.CopySym(a)
  442. }
  443. blas64.Syr2(alpha, xmat, ymat, s.mat)
  444. return
  445. }
  446. for i := 0; i < n; i++ {
  447. s.reuseAsNonZeroed(n)
  448. for j := i; j < n; j++ {
  449. s.set(i, j, a.At(i, j)+alpha*(x.AtVec(i)*y.AtVec(j)+y.AtVec(i)*x.AtVec(j)))
  450. }
  451. }
  452. }
  453. // ScaleSym multiplies the elements of a by f, placing the result in the receiver.
  454. func (s *SymDense) ScaleSym(f float64, a Symmetric) {
  455. n := a.Symmetric()
  456. s.reuseAsNonZeroed(n)
  457. if a, ok := a.(RawSymmetricer); ok {
  458. amat := a.RawSymmetric()
  459. if s != a {
  460. s.checkOverlap(generalFromSymmetric(amat))
  461. }
  462. for i := 0; i < n; i++ {
  463. for j := i; j < n; j++ {
  464. s.mat.Data[i*s.mat.Stride+j] = f * amat.Data[i*amat.Stride+j]
  465. }
  466. }
  467. return
  468. }
  469. for i := 0; i < n; i++ {
  470. for j := i; j < n; j++ {
  471. s.mat.Data[i*s.mat.Stride+j] = f * a.At(i, j)
  472. }
  473. }
  474. }
  475. // SubsetSym extracts a subset of the rows and columns of the matrix a and stores
  476. // the result in-place into the receiver. The resulting matrix size is
  477. // len(set)×len(set). Specifically, at the conclusion of SubsetSym,
  478. // s.At(i, j) equals a.At(set[i], set[j]). Note that the supplied set does not
  479. // have to be a strict subset, dimension repeats are allowed.
  480. func (s *SymDense) SubsetSym(a Symmetric, set []int) {
  481. n := len(set)
  482. na := a.Symmetric()
  483. s.reuseAsNonZeroed(n)
  484. var restore func()
  485. if a == s {
  486. s, restore = s.isolatedWorkspace(a)
  487. defer restore()
  488. }
  489. if a, ok := a.(RawSymmetricer); ok {
  490. raw := a.RawSymmetric()
  491. if s != a {
  492. s.checkOverlap(generalFromSymmetric(raw))
  493. }
  494. for i := 0; i < n; i++ {
  495. ssub := s.mat.Data[i*s.mat.Stride : i*s.mat.Stride+n]
  496. r := set[i]
  497. rsub := raw.Data[r*raw.Stride : r*raw.Stride+na]
  498. for j := i; j < n; j++ {
  499. c := set[j]
  500. if r <= c {
  501. ssub[j] = rsub[c]
  502. } else {
  503. ssub[j] = raw.Data[c*raw.Stride+r]
  504. }
  505. }
  506. }
  507. return
  508. }
  509. for i := 0; i < n; i++ {
  510. for j := i; j < n; j++ {
  511. s.mat.Data[i*s.mat.Stride+j] = a.At(set[i], set[j])
  512. }
  513. }
  514. }
  515. // SliceSym returns a new Matrix that shares backing data with the receiver.
  516. // The returned matrix starts at {i,i} of the receiver and extends k-i rows
  517. // and columns. The final row and column in the resulting matrix is k-1.
  518. // SliceSym panics with ErrIndexOutOfRange if the slice is outside the
  519. // capacity of the receiver.
  520. func (s *SymDense) SliceSym(i, k int) Symmetric {
  521. return s.sliceSym(i, k)
  522. }
  523. func (s *SymDense) sliceSym(i, k int) *SymDense {
  524. sz := s.cap
  525. if i < 0 || sz < i || k < i || sz < k {
  526. panic(ErrIndexOutOfRange)
  527. }
  528. v := *s
  529. v.mat.Data = s.mat.Data[i*s.mat.Stride+i : (k-1)*s.mat.Stride+k]
  530. v.mat.N = k - i
  531. v.cap = s.cap - i
  532. return &v
  533. }
  534. // Trace returns the trace of the matrix.
  535. func (s *SymDense) Trace() float64 {
  536. // TODO(btracey): could use internal asm sum routine.
  537. var v float64
  538. for i := 0; i < s.mat.N; i++ {
  539. v += s.mat.Data[i*s.mat.Stride+i]
  540. }
  541. return v
  542. }
  543. // GrowSym returns the receiver expanded by n rows and n columns. If the
  544. // dimensions of the expanded matrix are outside the capacity of the receiver
  545. // a new allocation is made, otherwise not. Note that the receiver itself is
  546. // not modified during the call to GrowSquare.
  547. func (s *SymDense) GrowSym(n int) Symmetric {
  548. if n < 0 {
  549. panic(ErrIndexOutOfRange)
  550. }
  551. if n == 0 {
  552. return s
  553. }
  554. var v SymDense
  555. n += s.mat.N
  556. if n > s.cap {
  557. v.mat = blas64.Symmetric{
  558. N: n,
  559. Stride: n,
  560. Uplo: blas.Upper,
  561. Data: make([]float64, n*n),
  562. }
  563. v.cap = n
  564. // Copy elements, including those not currently visible. Use a temporary
  565. // structure to avoid modifying the receiver.
  566. var tmp SymDense
  567. tmp.mat = blas64.Symmetric{
  568. N: s.cap,
  569. Stride: s.mat.Stride,
  570. Data: s.mat.Data,
  571. Uplo: s.mat.Uplo,
  572. }
  573. tmp.cap = s.cap
  574. v.CopySym(&tmp)
  575. return &v
  576. }
  577. v.mat = blas64.Symmetric{
  578. N: n,
  579. Stride: s.mat.Stride,
  580. Uplo: blas.Upper,
  581. Data: s.mat.Data[:(n-1)*s.mat.Stride+n],
  582. }
  583. v.cap = s.cap
  584. return &v
  585. }
  586. // PowPSD computes a^pow where a is a positive symmetric definite matrix.
  587. //
  588. // PowPSD returns an error if the matrix is not not positive symmetric definite
  589. // or the Eigen decomposition is not successful.
  590. func (s *SymDense) PowPSD(a Symmetric, pow float64) error {
  591. dim := a.Symmetric()
  592. s.reuseAsNonZeroed(dim)
  593. var eigen EigenSym
  594. ok := eigen.Factorize(a, true)
  595. if !ok {
  596. return ErrFailedEigen
  597. }
  598. values := eigen.Values(nil)
  599. for i, v := range values {
  600. if v <= 0 {
  601. return ErrNotPSD
  602. }
  603. values[i] = math.Pow(v, pow)
  604. }
  605. var u Dense
  606. eigen.VectorsTo(&u)
  607. s.SymOuterK(values[0], u.ColView(0))
  608. var v VecDense
  609. for i := 1; i < dim; i++ {
  610. v.ColViewOf(&u, i)
  611. s.SymRankOne(s, values[i], &v)
  612. }
  613. return nil
  614. }