github.com/gopherd/gonum@v0.0.4/mat/symband.go (about) 1 // Copyright ©2017 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 "github.com/gopherd/gonum/blas" 9 "github.com/gopherd/gonum/blas/blas64" 10 "github.com/gopherd/gonum/lapack" 11 "github.com/gopherd/gonum/lapack/lapack64" 12 ) 13 14 var ( 15 symBandDense *SymBandDense 16 _ Matrix = symBandDense 17 _ allMatrix = symBandDense 18 _ denseMatrix = symBandDense 19 _ Symmetric = symBandDense 20 _ Banded = symBandDense 21 _ SymBanded = symBandDense 22 _ RawSymBander = symBandDense 23 _ MutableSymBanded = symBandDense 24 25 _ NonZeroDoer = symBandDense 26 _ RowNonZeroDoer = symBandDense 27 _ ColNonZeroDoer = symBandDense 28 ) 29 30 // SymBandDense represents a symmetric band matrix in dense storage format. 31 type SymBandDense struct { 32 mat blas64.SymmetricBand 33 } 34 35 // SymBanded is a symmetric band matrix interface type. 36 type SymBanded interface { 37 Banded 38 39 // SymmetricDim returns the number of rows/columns in the matrix. 40 SymmetricDim() int 41 42 // SymBand returns the number of rows/columns in the matrix, and the size of 43 // the bandwidth. 44 SymBand() (n, k int) 45 } 46 47 // MutableSymBanded is a symmetric band matrix interface type that allows elements 48 // to be altered. 49 type MutableSymBanded interface { 50 SymBanded 51 SetSymBand(i, j int, v float64) 52 } 53 54 // A RawSymBander can return a blas64.SymmetricBand representation of the receiver. 55 // Changes to the blas64.SymmetricBand.Data slice will be reflected in the original 56 // matrix, changes to the N, K, Stride and Uplo fields will not. 57 type RawSymBander interface { 58 RawSymBand() blas64.SymmetricBand 59 } 60 61 // NewSymBandDense creates a new SymBand matrix with n rows and columns. If data == nil, 62 // a new slice is allocated for the backing slice. If len(data) == n*(k+1), 63 // data is used as the backing slice, and changes to the elements of the returned 64 // SymBandDense will be reflected in data. If neither of these is true, NewSymBandDense 65 // will panic. k must be at least zero and less than n, otherwise NewSymBandDense will panic. 66 // 67 // The data must be arranged in row-major order constructed by removing the zeros 68 // from the rows outside the band and aligning the diagonals. SymBandDense matrices 69 // are stored in the upper triangle. For example, the matrix 70 // 1 2 3 0 0 0 71 // 2 4 5 6 0 0 72 // 3 5 7 8 9 0 73 // 0 6 8 10 11 12 74 // 0 0 9 11 13 14 75 // 0 0 0 12 14 15 76 // becomes (* entries are never accessed) 77 // 1 2 3 78 // 4 5 6 79 // 7 8 9 80 // 10 11 12 81 // 13 14 * 82 // 15 * * 83 // which is passed to NewSymBandDense as []float64{1, 2, ..., 15, *, *, *} with k=2. 84 // Only the values in the band portion of the matrix are used. 85 func NewSymBandDense(n, k int, data []float64) *SymBandDense { 86 if n <= 0 || k < 0 { 87 if n == 0 { 88 panic(ErrZeroLength) 89 } 90 panic("mat: negative dimension") 91 } 92 if k+1 > n { 93 panic("mat: band out of range") 94 } 95 bc := k + 1 96 if data != nil && len(data) != n*bc { 97 panic(ErrShape) 98 } 99 if data == nil { 100 data = make([]float64, n*bc) 101 } 102 return &SymBandDense{ 103 mat: blas64.SymmetricBand{ 104 N: n, 105 K: k, 106 Stride: bc, 107 Uplo: blas.Upper, 108 Data: data, 109 }, 110 } 111 } 112 113 // Dims returns the number of rows and columns in the matrix. 114 func (s *SymBandDense) Dims() (r, c int) { 115 return s.mat.N, s.mat.N 116 } 117 118 // SymmetricDim returns the size of the receiver. 119 func (s *SymBandDense) SymmetricDim() int { 120 return s.mat.N 121 } 122 123 // Bandwidth returns the bandwidths of the matrix. 124 func (s *SymBandDense) Bandwidth() (kl, ku int) { 125 return s.mat.K, s.mat.K 126 } 127 128 // SymBand returns the number of rows/columns in the matrix, and the size of 129 // the bandwidth. 130 func (s *SymBandDense) SymBand() (n, k int) { 131 return s.mat.N, s.mat.K 132 } 133 134 // T implements the Matrix interface. Symmetric matrices, by definition, are 135 // equal to their transpose, and this is a no-op. 136 func (s *SymBandDense) T() Matrix { 137 return s 138 } 139 140 // TBand implements the Banded interface. 141 func (s *SymBandDense) TBand() Banded { 142 return s 143 } 144 145 // RawSymBand returns the underlying blas64.SymBand used by the receiver. 146 // Changes to elements in the receiver following the call will be reflected 147 // in returned blas64.SymBand. 148 func (s *SymBandDense) RawSymBand() blas64.SymmetricBand { 149 return s.mat 150 } 151 152 // SetRawSymBand sets the underlying blas64.SymmetricBand used by the receiver. 153 // Changes to elements in the receiver following the call will be reflected 154 // in the input. 155 // 156 // The supplied SymmetricBand must use blas.Upper storage format. 157 func (s *SymBandDense) SetRawSymBand(mat blas64.SymmetricBand) { 158 if mat.Uplo != blas.Upper { 159 panic("mat: blas64.SymmetricBand does not have blas.Upper storage") 160 } 161 s.mat = mat 162 } 163 164 // IsEmpty returns whether the receiver is empty. Empty matrices can be the 165 // receiver for size-restricted operations. The receiver can be emptied using 166 // Reset. 167 func (s *SymBandDense) IsEmpty() bool { 168 return s.mat.Stride == 0 169 } 170 171 // Reset empties the matrix so that it can be reused as the 172 // receiver of a dimensionally restricted operation. 173 // 174 // Reset should not be used when the matrix shares backing data. 175 // See the Reseter interface for more information. 176 func (s *SymBandDense) Reset() { 177 s.mat.N = 0 178 s.mat.K = 0 179 s.mat.Stride = 0 180 s.mat.Uplo = 0 181 s.mat.Data = s.mat.Data[:0] 182 } 183 184 // Zero sets all of the matrix elements to zero. 185 func (s *SymBandDense) Zero() { 186 for i := 0; i < s.mat.N; i++ { 187 u := min(1+s.mat.K, s.mat.N-i) 188 zero(s.mat.Data[i*s.mat.Stride : i*s.mat.Stride+u]) 189 } 190 } 191 192 // DiagView returns the diagonal as a matrix backed by the original data. 193 func (s *SymBandDense) DiagView() Diagonal { 194 n := s.mat.N 195 return &DiagDense{ 196 mat: blas64.Vector{ 197 N: n, 198 Inc: s.mat.Stride, 199 Data: s.mat.Data[:(n-1)*s.mat.Stride+1], 200 }, 201 } 202 } 203 204 // DoNonZero calls the function fn for each of the non-zero elements of s. The function fn 205 // takes a row/column index and the element value of s at (i, j). 206 func (s *SymBandDense) DoNonZero(fn func(i, j int, v float64)) { 207 for i := 0; i < s.mat.N; i++ { 208 for j := max(0, i-s.mat.K); j < min(s.mat.N, i+s.mat.K+1); j++ { 209 v := s.at(i, j) 210 if v != 0 { 211 fn(i, j, v) 212 } 213 } 214 } 215 } 216 217 // DoRowNonZero calls the function fn for each of the non-zero elements of row i of s. The function fn 218 // takes a row/column index and the element value of s at (i, j). 219 func (s *SymBandDense) DoRowNonZero(i int, fn func(i, j int, v float64)) { 220 if i < 0 || s.mat.N <= i { 221 panic(ErrRowAccess) 222 } 223 for j := max(0, i-s.mat.K); j < min(s.mat.N, i+s.mat.K+1); j++ { 224 v := s.at(i, j) 225 if v != 0 { 226 fn(i, j, v) 227 } 228 } 229 } 230 231 // DoColNonZero calls the function fn for each of the non-zero elements of column j of s. The function fn 232 // takes a row/column index and the element value of s at (i, j). 233 func (s *SymBandDense) DoColNonZero(j int, fn func(i, j int, v float64)) { 234 if j < 0 || s.mat.N <= j { 235 panic(ErrColAccess) 236 } 237 for i := 0; i < s.mat.N; i++ { 238 if i-s.mat.K <= j && j < i+s.mat.K+1 { 239 v := s.at(i, j) 240 if v != 0 { 241 fn(i, j, v) 242 } 243 } 244 } 245 } 246 247 // Norm returns the specified norm of the receiver. Valid norms are: 248 // 1 - The maximum absolute column sum 249 // 2 - The Frobenius norm, the square root of the sum of the squares of the elements 250 // Inf - The maximum absolute row sum 251 // 252 // Norm will panic with ErrNormOrder if an illegal norm is specified and with 253 // ErrZeroLength if the matrix has zero size. 254 func (s *SymBandDense) Norm(norm float64) float64 { 255 if s.IsEmpty() { 256 panic(ErrZeroLength) 257 } 258 lnorm := normLapack(norm, false) 259 if lnorm == lapack.MaxColumnSum || lnorm == lapack.MaxRowSum { 260 work := getFloat64s(s.mat.N, false) 261 defer putFloat64s(work) 262 return lapack64.Lansb(lnorm, s.mat, work) 263 } 264 return lapack64.Lansb(lnorm, s.mat, nil) 265 } 266 267 // Trace returns the trace of the matrix. 268 // 269 // Trace will panic with ErrZeroLength if the matrix has zero size. 270 func (s *SymBandDense) Trace() float64 { 271 if s.IsEmpty() { 272 panic(ErrZeroLength) 273 } 274 rb := s.RawSymBand() 275 var tr float64 276 for i := 0; i < rb.N; i++ { 277 tr += rb.Data[i*rb.Stride] 278 } 279 return tr 280 } 281 282 // MulVecTo computes S⋅x storing the result into dst. 283 func (s *SymBandDense) MulVecTo(dst *VecDense, _ bool, x Vector) { 284 n := s.mat.N 285 if x.Len() != n { 286 panic(ErrShape) 287 } 288 dst.reuseAsNonZeroed(n) 289 290 xMat, _ := untransposeExtract(x) 291 if xVec, ok := xMat.(*VecDense); ok { 292 if dst != xVec { 293 dst.checkOverlap(xVec.mat) 294 blas64.Sbmv(1, s.mat, xVec.mat, 0, dst.mat) 295 } else { 296 xCopy := getVecDenseWorkspace(n, false) 297 xCopy.CloneFromVec(xVec) 298 blas64.Sbmv(1, s.mat, xCopy.mat, 0, dst.mat) 299 putVecDenseWorkspace(xCopy) 300 } 301 } else { 302 xCopy := getVecDenseWorkspace(n, false) 303 xCopy.CloneFromVec(x) 304 blas64.Sbmv(1, s.mat, xCopy.mat, 0, dst.mat) 305 putVecDenseWorkspace(xCopy) 306 } 307 }