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