github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/mat/symmetric.go (about)

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