gonum.org/v1/gonum@v0.14.0/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  	"gonum.org/v1/gonum/blas"
    11  	"gonum.org/v1/gonum/blas/blas64"
    12  	"gonum.org/v1/gonum/lapack"
    13  	"gonum.org/v1/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  //
   326  //	s = a + alpha * x * xᵀ
   327  func (s *SymDense) SymRankOne(a Symmetric, alpha float64, x Vector) {
   328  	n := x.Len()
   329  	if a.SymmetricDim() != n {
   330  		panic(ErrShape)
   331  	}
   332  	s.reuseAsNonZeroed(n)
   333  
   334  	if s != a {
   335  		if rs, ok := a.(RawSymmetricer); ok {
   336  			s.checkOverlap(generalFromSymmetric(rs.RawSymmetric()))
   337  		}
   338  		s.CopySym(a)
   339  	}
   340  
   341  	xU, _ := untransposeExtract(x)
   342  	if rv, ok := xU.(*VecDense); ok {
   343  		r, c := xU.Dims()
   344  		xmat := rv.mat
   345  		s.checkOverlap(generalFromVector(xmat, r, c))
   346  		blas64.Syr(alpha, xmat, s.mat)
   347  		return
   348  	}
   349  
   350  	for i := 0; i < n; i++ {
   351  		for j := i; j < n; j++ {
   352  			s.set(i, j, s.at(i, j)+alpha*x.AtVec(i)*x.AtVec(j))
   353  		}
   354  	}
   355  }
   356  
   357  // SymRankK performs a symmetric rank-k update to the matrix a and stores the
   358  // result into the receiver. If a is zero, see SymOuterK.
   359  //
   360  //	s = a + alpha * x * x'
   361  func (s *SymDense) SymRankK(a Symmetric, alpha float64, x Matrix) {
   362  	n := a.SymmetricDim()
   363  	r, _ := x.Dims()
   364  	if r != n {
   365  		panic(ErrShape)
   366  	}
   367  	xMat, aTrans := untransposeExtract(x)
   368  	var g blas64.General
   369  	if rm, ok := xMat.(*Dense); ok {
   370  		g = rm.mat
   371  	} else {
   372  		g = DenseCopyOf(x).mat
   373  		aTrans = false
   374  	}
   375  	if a != s {
   376  		if rs, ok := a.(RawSymmetricer); ok {
   377  			s.checkOverlap(generalFromSymmetric(rs.RawSymmetric()))
   378  		}
   379  		s.reuseAsNonZeroed(n)
   380  		s.CopySym(a)
   381  	}
   382  	t := blas.NoTrans
   383  	if aTrans {
   384  		t = blas.Trans
   385  	}
   386  	blas64.Syrk(t, alpha, g, 1, s.mat)
   387  }
   388  
   389  // SymOuterK calculates the outer product of x with itself and stores
   390  // the result into the receiver. It is equivalent to the matrix
   391  // multiplication
   392  //
   393  //	s = alpha * x * x'.
   394  //
   395  // In order to update an existing matrix, see SymRankOne.
   396  func (s *SymDense) SymOuterK(alpha float64, x Matrix) {
   397  	n, _ := x.Dims()
   398  	switch {
   399  	case s.IsEmpty():
   400  		s.mat = blas64.Symmetric{
   401  			N:      n,
   402  			Stride: n,
   403  			Data:   useZeroed(s.mat.Data, n*n),
   404  			Uplo:   blas.Upper,
   405  		}
   406  		s.cap = n
   407  		s.SymRankK(s, alpha, x)
   408  	case s.mat.Uplo != blas.Upper:
   409  		panic(badSymTriangle)
   410  	case s.mat.N == n:
   411  		if s == x {
   412  			w := getSymDenseWorkspace(n, true)
   413  			w.SymRankK(w, alpha, x)
   414  			s.CopySym(w)
   415  			putSymDenseWorkspace(w)
   416  		} else {
   417  			switch r := x.(type) {
   418  			case RawMatrixer:
   419  				s.checkOverlap(r.RawMatrix())
   420  			case RawSymmetricer:
   421  				s.checkOverlap(generalFromSymmetric(r.RawSymmetric()))
   422  			case RawTriangular:
   423  				s.checkOverlap(generalFromTriangular(r.RawTriangular()))
   424  			}
   425  			// Only zero the upper triangle.
   426  			for i := 0; i < n; i++ {
   427  				ri := i * s.mat.Stride
   428  				zero(s.mat.Data[ri+i : ri+n])
   429  			}
   430  			s.SymRankK(s, alpha, x)
   431  		}
   432  	default:
   433  		panic(ErrShape)
   434  	}
   435  }
   436  
   437  // RankTwo performs a symmetric rank-two update to the matrix a with the
   438  // vectors x and y, which are treated as column vectors, and stores the
   439  // result in the receiver
   440  //
   441  //	m = a + alpha * (x * yᵀ + y * xᵀ)
   442  func (s *SymDense) RankTwo(a Symmetric, alpha float64, x, y Vector) {
   443  	n := s.mat.N
   444  	if x.Len() != n {
   445  		panic(ErrShape)
   446  	}
   447  	if y.Len() != n {
   448  		panic(ErrShape)
   449  	}
   450  
   451  	if s != a {
   452  		if rs, ok := a.(RawSymmetricer); ok {
   453  			s.checkOverlap(generalFromSymmetric(rs.RawSymmetric()))
   454  		}
   455  	}
   456  
   457  	var xmat, ymat blas64.Vector
   458  	fast := true
   459  	xU, _ := untransposeExtract(x)
   460  	if rv, ok := xU.(*VecDense); ok {
   461  		r, c := xU.Dims()
   462  		xmat = rv.mat
   463  		s.checkOverlap(generalFromVector(xmat, r, c))
   464  	} else {
   465  		fast = false
   466  	}
   467  	yU, _ := untransposeExtract(y)
   468  	if rv, ok := yU.(*VecDense); ok {
   469  		r, c := yU.Dims()
   470  		ymat = rv.mat
   471  		s.checkOverlap(generalFromVector(ymat, r, c))
   472  	} else {
   473  		fast = false
   474  	}
   475  
   476  	if s != a {
   477  		if rs, ok := a.(RawSymmetricer); ok {
   478  			s.checkOverlap(generalFromSymmetric(rs.RawSymmetric()))
   479  		}
   480  		s.reuseAsNonZeroed(n)
   481  		s.CopySym(a)
   482  	}
   483  
   484  	if fast {
   485  		if s != a {
   486  			s.reuseAsNonZeroed(n)
   487  			s.CopySym(a)
   488  		}
   489  		blas64.Syr2(alpha, xmat, ymat, s.mat)
   490  		return
   491  	}
   492  
   493  	for i := 0; i < n; i++ {
   494  		s.reuseAsNonZeroed(n)
   495  		for j := i; j < n; j++ {
   496  			s.set(i, j, a.At(i, j)+alpha*(x.AtVec(i)*y.AtVec(j)+y.AtVec(i)*x.AtVec(j)))
   497  		}
   498  	}
   499  }
   500  
   501  // ScaleSym multiplies the elements of a by f, placing the result in the receiver.
   502  func (s *SymDense) ScaleSym(f float64, a Symmetric) {
   503  	n := a.SymmetricDim()
   504  	s.reuseAsNonZeroed(n)
   505  	if a, ok := a.(RawSymmetricer); ok {
   506  		amat := a.RawSymmetric()
   507  		if s != a {
   508  			s.checkOverlap(generalFromSymmetric(amat))
   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 * amat.Data[i*amat.Stride+j]
   513  			}
   514  		}
   515  		return
   516  	}
   517  	for i := 0; i < n; i++ {
   518  		for j := i; j < n; j++ {
   519  			s.mat.Data[i*s.mat.Stride+j] = f * a.At(i, j)
   520  		}
   521  	}
   522  }
   523  
   524  // SubsetSym extracts a subset of the rows and columns of the matrix a and stores
   525  // the result in-place into the receiver. The resulting matrix size is
   526  // len(set)×len(set). Specifically, at the conclusion of SubsetSym,
   527  // s.At(i, j) equals a.At(set[i], set[j]). Note that the supplied set does not
   528  // have to be a strict subset, dimension repeats are allowed.
   529  func (s *SymDense) SubsetSym(a Symmetric, set []int) {
   530  	n := len(set)
   531  	na := a.SymmetricDim()
   532  	s.reuseAsNonZeroed(n)
   533  	var restore func()
   534  	if a == s {
   535  		s, restore = s.isolatedWorkspace(a)
   536  		defer restore()
   537  	}
   538  
   539  	if a, ok := a.(RawSymmetricer); ok {
   540  		raw := a.RawSymmetric()
   541  		if s != a {
   542  			s.checkOverlap(generalFromSymmetric(raw))
   543  		}
   544  		for i := 0; i < n; i++ {
   545  			ssub := s.mat.Data[i*s.mat.Stride : i*s.mat.Stride+n]
   546  			r := set[i]
   547  			rsub := raw.Data[r*raw.Stride : r*raw.Stride+na]
   548  			for j := i; j < n; j++ {
   549  				c := set[j]
   550  				if r <= c {
   551  					ssub[j] = rsub[c]
   552  				} else {
   553  					ssub[j] = raw.Data[c*raw.Stride+r]
   554  				}
   555  			}
   556  		}
   557  		return
   558  	}
   559  	for i := 0; i < n; i++ {
   560  		for j := i; j < n; j++ {
   561  			s.mat.Data[i*s.mat.Stride+j] = a.At(set[i], set[j])
   562  		}
   563  	}
   564  }
   565  
   566  // SliceSym returns a new Matrix that shares backing data with the receiver.
   567  // The returned matrix starts at {i,i} of the receiver and extends k-i rows
   568  // and columns. The final row and column in the resulting matrix is k-1.
   569  // SliceSym panics with ErrIndexOutOfRange if the slice is outside the
   570  // capacity of the receiver.
   571  func (s *SymDense) SliceSym(i, k int) Symmetric {
   572  	return s.sliceSym(i, k)
   573  }
   574  
   575  func (s *SymDense) sliceSym(i, k int) *SymDense {
   576  	sz := s.cap
   577  	if i < 0 || sz < i || k < i || sz < k {
   578  		panic(ErrIndexOutOfRange)
   579  	}
   580  	v := *s
   581  	v.mat.Data = s.mat.Data[i*s.mat.Stride+i : (k-1)*s.mat.Stride+k]
   582  	v.mat.N = k - i
   583  	v.cap = s.cap - i
   584  	return &v
   585  }
   586  
   587  // Norm returns the specified norm of the receiver. Valid norms are:
   588  //
   589  //	1 - The maximum absolute column sum
   590  //	2 - The Frobenius norm, the square root of the sum of the squares of the elements
   591  //	Inf - The maximum absolute row sum
   592  //
   593  // Norm will panic with ErrNormOrder if an illegal norm is specified and with
   594  // ErrZeroLength if the matrix has zero size.
   595  func (s *SymDense) Norm(norm float64) float64 {
   596  	if s.IsEmpty() {
   597  		panic(ErrZeroLength)
   598  	}
   599  	lnorm := normLapack(norm, false)
   600  	if lnorm == lapack.MaxColumnSum || lnorm == lapack.MaxRowSum {
   601  		work := getFloat64s(s.mat.N, false)
   602  		defer putFloat64s(work)
   603  		return lapack64.Lansy(lnorm, s.mat, work)
   604  	}
   605  	return lapack64.Lansy(lnorm, s.mat, nil)
   606  }
   607  
   608  // Trace returns the trace of the matrix.
   609  //
   610  // Trace will panic with ErrZeroLength if the matrix has zero size.
   611  func (s *SymDense) Trace() float64 {
   612  	if s.IsEmpty() {
   613  		panic(ErrZeroLength)
   614  	}
   615  	// TODO(btracey): could use internal asm sum routine.
   616  	var v float64
   617  	for i := 0; i < s.mat.N; i++ {
   618  		v += s.mat.Data[i*s.mat.Stride+i]
   619  	}
   620  	return v
   621  }
   622  
   623  // GrowSym returns the receiver expanded by n rows and n columns. If the
   624  // dimensions of the expanded matrix are outside the capacity of the receiver
   625  // a new allocation is made, otherwise not. Note that the receiver itself is
   626  // not modified during the call to GrowSquare.
   627  func (s *SymDense) GrowSym(n int) Symmetric {
   628  	if n < 0 {
   629  		panic(ErrIndexOutOfRange)
   630  	}
   631  	if n == 0 {
   632  		return s
   633  	}
   634  	var v SymDense
   635  	n += s.mat.N
   636  	if s.IsEmpty() || n > s.cap {
   637  		v.mat = blas64.Symmetric{
   638  			N:      n,
   639  			Stride: n,
   640  			Uplo:   blas.Upper,
   641  			Data:   make([]float64, n*n),
   642  		}
   643  		v.cap = n
   644  		// Copy elements, including those not currently visible. Use a temporary
   645  		// structure to avoid modifying the receiver.
   646  		var tmp SymDense
   647  		tmp.mat = blas64.Symmetric{
   648  			N:      s.cap,
   649  			Stride: s.mat.Stride,
   650  			Data:   s.mat.Data,
   651  			Uplo:   s.mat.Uplo,
   652  		}
   653  		tmp.cap = s.cap
   654  		v.CopySym(&tmp)
   655  		return &v
   656  	}
   657  	v.mat = blas64.Symmetric{
   658  		N:      n,
   659  		Stride: s.mat.Stride,
   660  		Uplo:   blas.Upper,
   661  		Data:   s.mat.Data[:(n-1)*s.mat.Stride+n],
   662  	}
   663  	v.cap = s.cap
   664  	return &v
   665  }
   666  
   667  // PowPSD computes a^pow where a is a positive symmetric definite matrix.
   668  //
   669  // PowPSD returns an error if the matrix is not positive symmetric definite
   670  // or the Eigen decomposition is not successful.
   671  func (s *SymDense) PowPSD(a Symmetric, pow float64) error {
   672  	dim := a.SymmetricDim()
   673  	s.reuseAsNonZeroed(dim)
   674  
   675  	var eigen EigenSym
   676  	ok := eigen.Factorize(a, true)
   677  	if !ok {
   678  		return ErrFailedEigen
   679  	}
   680  	values := eigen.Values(nil)
   681  	for i, v := range values {
   682  		if v <= 0 {
   683  			return ErrNotPSD
   684  		}
   685  		values[i] = math.Pow(v, pow)
   686  	}
   687  	var u Dense
   688  	eigen.VectorsTo(&u)
   689  
   690  	s.SymOuterK(values[0], u.ColView(0))
   691  
   692  	var v VecDense
   693  	for i := 1; i < dim; i++ {
   694  		v.ColViewOf(&u, i)
   695  		s.SymRankOne(s, values[i], &v)
   696  	}
   697  	return nil
   698  }