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