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