gonum.org/v1/gonum@v0.14.0/mat/band.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 bandDense *BandDense 16 _ Matrix = bandDense 17 _ allMatrix = bandDense 18 _ denseMatrix = bandDense 19 _ Banded = bandDense 20 _ RawBander = bandDense 21 22 _ NonZeroDoer = bandDense 23 _ RowNonZeroDoer = bandDense 24 _ ColNonZeroDoer = bandDense 25 ) 26 27 // BandDense represents a band matrix in dense storage format. 28 type BandDense struct { 29 mat blas64.Band 30 } 31 32 // Banded is a band matrix representation. 33 type Banded interface { 34 Matrix 35 // Bandwidth returns the lower and upper bandwidth values for 36 // the matrix. The total bandwidth of the matrix is kl+ku+1. 37 Bandwidth() (kl, ku int) 38 39 // TBand is the equivalent of the T() method in the Matrix 40 // interface but guarantees the transpose is of banded type. 41 TBand() Banded 42 } 43 44 // A RawBander can return a blas64.Band representation of the receiver. 45 // Changes to the blas64.Band.Data slice will be reflected in the original 46 // matrix, changes to the Rows, Cols, KL, KU and Stride fields will not. 47 type RawBander interface { 48 RawBand() blas64.Band 49 } 50 51 // A MutableBanded can set elements of a band matrix. 52 type MutableBanded interface { 53 Banded 54 55 // SetBand sets the element at row i, column j to the value v. 56 // It panics if the location is outside the appropriate region of the matrix. 57 SetBand(i, j int, v float64) 58 } 59 60 var ( 61 _ Matrix = TransposeBand{} 62 _ Banded = TransposeBand{} 63 _ UntransposeBander = TransposeBand{} 64 ) 65 66 // TransposeBand is a type for performing an implicit transpose of a band 67 // matrix. It implements the Banded interface, returning values from the 68 // transpose of the matrix within. 69 type TransposeBand struct { 70 Banded Banded 71 } 72 73 // At returns the value of the element at row i and column j of the transposed 74 // matrix, that is, row j and column i of the Banded field. 75 func (t TransposeBand) At(i, j int) float64 { 76 return t.Banded.At(j, i) 77 } 78 79 // Dims returns the dimensions of the transposed matrix. 80 func (t TransposeBand) Dims() (r, c int) { 81 c, r = t.Banded.Dims() 82 return r, c 83 } 84 85 // T performs an implicit transpose by returning the Banded field. 86 func (t TransposeBand) T() Matrix { 87 return t.Banded 88 } 89 90 // Bandwidth returns the lower and upper bandwidth values for 91 // the transposed matrix. 92 func (t TransposeBand) Bandwidth() (kl, ku int) { 93 kl, ku = t.Banded.Bandwidth() 94 return ku, kl 95 } 96 97 // TBand performs an implicit transpose by returning the Banded field. 98 func (t TransposeBand) TBand() Banded { 99 return t.Banded 100 } 101 102 // Untranspose returns the Banded field. 103 func (t TransposeBand) Untranspose() Matrix { 104 return t.Banded 105 } 106 107 // UntransposeBand returns the Banded field. 108 func (t TransposeBand) UntransposeBand() Banded { 109 return t.Banded 110 } 111 112 // NewBandDense creates a new Band matrix with r rows and c columns. If data == nil, 113 // a new slice is allocated for the backing slice. If len(data) == min(r, c+kl)*(kl+ku+1), 114 // data is used as the backing slice, and changes to the elements of the returned 115 // BandDense will be reflected in data. If neither of these is true, NewBandDense 116 // will panic. kl must be at least zero and less r, and ku must be at least zero and 117 // less than c, otherwise NewBandDense will panic. 118 // NewBandDense will panic if either r or c is zero. 119 // 120 // The data must be arranged in row-major order constructed by removing the zeros 121 // from the rows outside the band and aligning the diagonals. For example, the matrix 122 // 123 // 1 2 3 0 0 0 124 // 4 5 6 7 0 0 125 // 0 8 9 10 11 0 126 // 0 0 12 13 14 15 127 // 0 0 0 16 17 18 128 // 0 0 0 0 19 20 129 // 130 // becomes (* entries are never accessed) 131 // - 1 2 3 132 // 4 5 6 7 133 // 8 9 10 11 134 // 12 13 14 15 135 // 16 17 18 * 136 // 19 20 * * 137 // 138 // which is passed to NewBandDense as []float64{*, 1, 2, 3, 4, ...} with kl=1 and ku=2. 139 // Only the values in the band portion of the matrix are used. 140 func NewBandDense(r, c, kl, ku int, data []float64) *BandDense { 141 if r <= 0 || c <= 0 || kl < 0 || ku < 0 { 142 if r == 0 || c == 0 { 143 panic(ErrZeroLength) 144 } 145 panic(ErrNegativeDimension) 146 } 147 if kl+1 > r || ku+1 > c { 148 panic(ErrBandwidth) 149 } 150 bc := kl + ku + 1 151 if data != nil && len(data) != min(r, c+kl)*bc { 152 panic(ErrShape) 153 } 154 if data == nil { 155 data = make([]float64, min(r, c+kl)*bc) 156 } 157 return &BandDense{ 158 mat: blas64.Band{ 159 Rows: r, 160 Cols: c, 161 KL: kl, 162 KU: ku, 163 Stride: bc, 164 Data: data, 165 }, 166 } 167 } 168 169 // NewDiagonalRect is a convenience function that returns a diagonal matrix represented by a 170 // BandDense. The length of data must be min(r, c) otherwise NewDiagonalRect will panic. 171 func NewDiagonalRect(r, c int, data []float64) *BandDense { 172 return NewBandDense(r, c, 0, 0, data) 173 } 174 175 // Dims returns the number of rows and columns in the matrix. 176 func (b *BandDense) Dims() (r, c int) { 177 return b.mat.Rows, b.mat.Cols 178 } 179 180 // Bandwidth returns the upper and lower bandwidths of the matrix. 181 func (b *BandDense) Bandwidth() (kl, ku int) { 182 return b.mat.KL, b.mat.KU 183 } 184 185 // T performs an implicit transpose by returning the receiver inside a Transpose. 186 func (b *BandDense) T() Matrix { 187 return Transpose{b} 188 } 189 190 // TBand performs an implicit transpose by returning the receiver inside a TransposeBand. 191 func (b *BandDense) TBand() Banded { 192 return TransposeBand{b} 193 } 194 195 // RawBand returns the underlying blas64.Band used by the receiver. 196 // Changes to elements in the receiver following the call will be reflected 197 // in returned blas64.Band. 198 func (b *BandDense) RawBand() blas64.Band { 199 return b.mat 200 } 201 202 // SetRawBand sets the underlying blas64.Band used by the receiver. 203 // Changes to elements in the receiver following the call will be reflected 204 // in the input. 205 func (b *BandDense) SetRawBand(mat blas64.Band) { 206 b.mat = mat 207 } 208 209 // IsEmpty returns whether the receiver is empty. Empty matrices can be the 210 // receiver for size-restricted operations. The receiver can be zeroed using Reset. 211 func (b *BandDense) IsEmpty() bool { 212 return b.mat.Stride == 0 213 } 214 215 // Reset empties the matrix so that it can be reused as the 216 // receiver of a dimensionally restricted operation. 217 // 218 // Reset should not be used when the matrix shares backing data. 219 // See the Reseter interface for more information. 220 func (b *BandDense) Reset() { 221 b.mat.Rows = 0 222 b.mat.Cols = 0 223 b.mat.KL = 0 224 b.mat.KU = 0 225 b.mat.Stride = 0 226 b.mat.Data = b.mat.Data[:0] 227 } 228 229 // DiagView returns the diagonal as a matrix backed by the original data. 230 func (b *BandDense) DiagView() Diagonal { 231 n := min(b.mat.Rows, b.mat.Cols) 232 return &DiagDense{ 233 mat: blas64.Vector{ 234 N: n, 235 Inc: b.mat.Stride, 236 Data: b.mat.Data[b.mat.KL : (n-1)*b.mat.Stride+b.mat.KL+1], 237 }, 238 } 239 } 240 241 // DoNonZero calls the function fn for each of the non-zero elements of b. The function fn 242 // takes a row/column index and the element value of b at (i, j). 243 func (b *BandDense) DoNonZero(fn func(i, j int, v float64)) { 244 for i := 0; i < min(b.mat.Rows, b.mat.Cols+b.mat.KL); i++ { 245 for j := max(0, i-b.mat.KL); j < min(b.mat.Cols, i+b.mat.KU+1); j++ { 246 v := b.at(i, j) 247 if v != 0 { 248 fn(i, j, v) 249 } 250 } 251 } 252 } 253 254 // DoRowNonZero calls the function fn for each of the non-zero elements of row i of b. The function fn 255 // takes a row/column index and the element value of b at (i, j). 256 func (b *BandDense) DoRowNonZero(i int, fn func(i, j int, v float64)) { 257 if i < 0 || b.mat.Rows <= i { 258 panic(ErrRowAccess) 259 } 260 for j := max(0, i-b.mat.KL); j < min(b.mat.Cols, i+b.mat.KU+1); j++ { 261 v := b.at(i, j) 262 if v != 0 { 263 fn(i, j, v) 264 } 265 } 266 } 267 268 // DoColNonZero calls the function fn for each of the non-zero elements of column j of b. The function fn 269 // takes a row/column index and the element value of b at (i, j). 270 func (b *BandDense) DoColNonZero(j int, fn func(i, j int, v float64)) { 271 if j < 0 || b.mat.Cols <= j { 272 panic(ErrColAccess) 273 } 274 for i := 0; i < min(b.mat.Rows, b.mat.Cols+b.mat.KL); i++ { 275 if i-b.mat.KL <= j && j < i+b.mat.KU+1 { 276 v := b.at(i, j) 277 if v != 0 { 278 fn(i, j, v) 279 } 280 } 281 } 282 } 283 284 // Zero sets all of the matrix elements to zero. 285 func (b *BandDense) Zero() { 286 m := b.mat.Rows 287 kL := b.mat.KL 288 nCol := b.mat.KU + 1 + kL 289 for i := 0; i < m; i++ { 290 l := max(0, kL-i) 291 u := min(nCol, m+kL-i) 292 zero(b.mat.Data[i*b.mat.Stride+l : i*b.mat.Stride+u]) 293 } 294 } 295 296 // Norm returns the specified norm of the receiver. Valid norms are: 297 // 298 // 1 - The maximum absolute column sum 299 // 2 - The Frobenius norm, the square root of the sum of the squares of the elements 300 // Inf - The maximum absolute row sum 301 // 302 // Norm will panic with ErrNormOrder if an illegal norm is specified and with 303 // ErrZeroLength if the matrix has zero size. 304 func (b *BandDense) Norm(norm float64) float64 { 305 if b.IsEmpty() { 306 panic(ErrZeroLength) 307 } 308 lnorm := normLapack(norm, false) 309 if lnorm == lapack.MaxColumnSum || lnorm == lapack.MaxRowSum { 310 return lapack64.Langb(lnorm, b.mat) 311 } 312 return lapack64.Langb(lnorm, b.mat) 313 } 314 315 // Trace returns the trace of the matrix. 316 // 317 // Trace will panic with ErrSquare if the matrix is not square and with 318 // ErrZeroLength if the matrix has zero size. 319 func (b *BandDense) Trace() float64 { 320 r, c := b.Dims() 321 if r != c { 322 panic(ErrSquare) 323 } 324 if b.IsEmpty() { 325 panic(ErrZeroLength) 326 } 327 rb := b.RawBand() 328 var tr float64 329 for i := 0; i < r; i++ { 330 tr += rb.Data[rb.KL+i*rb.Stride] 331 } 332 return tr 333 } 334 335 // MulVecTo computes B⋅x or Bᵀ⋅x storing the result into dst. 336 func (b *BandDense) MulVecTo(dst *VecDense, trans bool, x Vector) { 337 m, n := b.Dims() 338 if trans { 339 m, n = n, m 340 } 341 if x.Len() != n { 342 panic(ErrShape) 343 } 344 dst.reuseAsNonZeroed(m) 345 346 t := blas.NoTrans 347 if trans { 348 t = blas.Trans 349 } 350 351 xMat, _ := untransposeExtract(x) 352 if xVec, ok := xMat.(*VecDense); ok { 353 if dst != xVec { 354 dst.checkOverlap(xVec.mat) 355 blas64.Gbmv(t, 1, b.mat, xVec.mat, 0, dst.mat) 356 } else { 357 xCopy := getVecDenseWorkspace(n, false) 358 xCopy.CloneFromVec(xVec) 359 blas64.Gbmv(t, 1, b.mat, xCopy.mat, 0, dst.mat) 360 putVecDenseWorkspace(xCopy) 361 } 362 } else { 363 xCopy := getVecDenseWorkspace(n, false) 364 xCopy.CloneFromVec(x) 365 blas64.Gbmv(t, 1, b.mat, xCopy.mat, 0, dst.mat) 366 putVecDenseWorkspace(xCopy) 367 } 368 }