github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/mat/triband.go (about) 1 // Copyright ©2018 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 "github.com/jingcheng-WU/gonum/blas" 11 "github.com/jingcheng-WU/gonum/blas/blas64" 12 "github.com/jingcheng-WU/gonum/lapack/lapack64" 13 ) 14 15 var ( 16 triBand TriBanded 17 _ Banded = triBand 18 _ Triangular = triBand 19 20 triBandDense *TriBandDense 21 _ Matrix = triBandDense 22 _ allMatrix = triBandDense 23 _ denseMatrix = triBandDense 24 _ Triangular = triBandDense 25 _ Banded = triBandDense 26 _ TriBanded = triBandDense 27 _ RawTriBander = triBandDense 28 _ MutableTriBanded = triBandDense 29 ) 30 31 // TriBanded is a triangular band matrix interface type. 32 type TriBanded interface { 33 Banded 34 35 // Triangle returns the number of rows/columns in the matrix and its 36 // orientation. 37 Triangle() (n int, kind TriKind) 38 39 // TTri is the equivalent of the T() method in the Matrix interface but 40 // guarantees the transpose is of triangular type. 41 TTri() Triangular 42 43 // TriBand returns the number of rows/columns in the matrix, the 44 // size of the bandwidth, and the orientation. 45 TriBand() (n, k int, kind TriKind) 46 47 // TTriBand is the equivalent of the T() method in the Matrix interface but 48 // guarantees the transpose is of banded triangular type. 49 TTriBand() TriBanded 50 } 51 52 // A RawTriBander can return a blas64.TriangularBand representation of the receiver. 53 // Changes to the blas64.TriangularBand.Data slice will be reflected in the original 54 // matrix, changes to the N, K, Stride, Uplo and Diag fields will not. 55 type RawTriBander interface { 56 RawTriBand() blas64.TriangularBand 57 } 58 59 // MutableTriBanded is a triangular band matrix interface type that allows 60 // elements to be altered. 61 type MutableTriBanded interface { 62 TriBanded 63 SetTriBand(i, j int, v float64) 64 } 65 66 var ( 67 tTriBand TransposeTriBand 68 _ Matrix = tTriBand 69 _ TriBanded = tTriBand 70 _ Untransposer = tTriBand 71 _ UntransposeTrier = tTriBand 72 _ UntransposeBander = tTriBand 73 _ UntransposeTriBander = tTriBand 74 ) 75 76 // TransposeTriBand is a type for performing an implicit transpose of a TriBanded 77 // matrix. It implements the TriBanded interface, returning values from the 78 // transpose of the matrix within. 79 type TransposeTriBand struct { 80 TriBanded TriBanded 81 } 82 83 // At returns the value of the element at row i and column j of the transposed 84 // matrix, that is, row j and column i of the TriBanded field. 85 func (t TransposeTriBand) At(i, j int) float64 { 86 return t.TriBanded.At(j, i) 87 } 88 89 // Dims returns the dimensions of the transposed matrix. TriBanded matrices are 90 // square and thus this is the same size as the original TriBanded. 91 func (t TransposeTriBand) Dims() (r, c int) { 92 c, r = t.TriBanded.Dims() 93 return r, c 94 } 95 96 // T performs an implicit transpose by returning the TriBand field. 97 func (t TransposeTriBand) T() Matrix { 98 return t.TriBanded 99 } 100 101 // Triangle returns the number of rows/columns in the matrix and its orientation. 102 func (t TransposeTriBand) Triangle() (int, TriKind) { 103 n, upper := t.TriBanded.Triangle() 104 return n, !upper 105 } 106 107 // TTri performs an implicit transpose by returning the TriBand field. 108 func (t TransposeTriBand) TTri() Triangular { 109 return t.TriBanded 110 } 111 112 // Bandwidth returns the upper and lower bandwidths of the matrix. 113 func (t TransposeTriBand) Bandwidth() (kl, ku int) { 114 kl, ku = t.TriBanded.Bandwidth() 115 return ku, kl 116 } 117 118 // TBand performs an implicit transpose by returning the TriBand field. 119 func (t TransposeTriBand) TBand() Banded { 120 return t.TriBanded 121 } 122 123 // TriBand returns the number of rows/columns in the matrix, the 124 // size of the bandwidth, and the orientation. 125 func (t TransposeTriBand) TriBand() (n, k int, kind TriKind) { 126 n, k, kind = t.TriBanded.TriBand() 127 return n, k, !kind 128 } 129 130 // TTriBand performs an implicit transpose by returning the TriBand field. 131 func (t TransposeTriBand) TTriBand() TriBanded { 132 return t.TriBanded 133 } 134 135 // Untranspose returns the Triangular field. 136 func (t TransposeTriBand) Untranspose() Matrix { 137 return t.TriBanded 138 } 139 140 // UntransposeTri returns the underlying Triangular matrix. 141 func (t TransposeTriBand) UntransposeTri() Triangular { 142 return t.TriBanded 143 } 144 145 // UntransposeBand returns the underlying Banded matrix. 146 func (t TransposeTriBand) UntransposeBand() Banded { 147 return t.TriBanded 148 } 149 150 // UntransposeTriBand returns the underlying TriBanded matrix. 151 func (t TransposeTriBand) UntransposeTriBand() TriBanded { 152 return t.TriBanded 153 } 154 155 // TriBandDense represents a triangular band matrix in dense storage format. 156 type TriBandDense struct { 157 mat blas64.TriangularBand 158 } 159 160 // NewTriBandDense creates a new triangular banded matrix with n rows and columns, 161 // k bands in the direction of the specified kind. If data == nil, 162 // a new slice is allocated for the backing slice. If len(data) == n*(k+1), 163 // data is used as the backing slice, and changes to the elements of the returned 164 // TriBandDense will be reflected in data. If neither of these is true, NewTriBandDense 165 // will panic. k must be at least zero and less than n, otherwise NewTriBandDense will panic. 166 // 167 // The data must be arranged in row-major order constructed by removing the zeros 168 // from the rows outside the band and aligning the diagonals. For example, if 169 // the upper-triangular banded matrix 170 // 1 2 3 0 0 0 171 // 0 4 5 6 0 0 172 // 0 0 7 8 9 0 173 // 0 0 0 10 11 12 174 // 0 0 0 0 13 14 175 // 0 0 0 0 0 15 176 // becomes (* entries are never accessed) 177 // 1 2 3 178 // 4 5 6 179 // 7 8 9 180 // 10 11 12 181 // 13 14 * 182 // 15 * * 183 // which is passed to NewTriBandDense as []float64{1, 2, ..., 15, *, *, *} 184 // with k=2 and kind = mat.Upper. 185 // The lower triangular banded matrix 186 // 1 0 0 0 0 0 187 // 2 3 0 0 0 0 188 // 4 5 6 0 0 0 189 // 0 7 8 9 0 0 190 // 0 0 10 11 12 0 191 // 0 0 0 13 14 15 192 // becomes (* entries are never accessed) 193 // * * 1 194 // * 2 3 195 // 4 5 6 196 // 7 8 9 197 // 10 11 12 198 // 13 14 15 199 // which is passed to NewTriBandDense as []float64{*, *, *, 1, 2, ..., 15} 200 // with k=2 and kind = mat.Lower. 201 // Only the values in the band portion of the matrix are used. 202 func NewTriBandDense(n, k int, kind TriKind, data []float64) *TriBandDense { 203 if n <= 0 || k < 0 { 204 if n == 0 { 205 panic(ErrZeroLength) 206 } 207 panic(ErrNegativeDimension) 208 } 209 if k+1 > n { 210 panic(ErrBandwidth) 211 } 212 bc := k + 1 213 if data != nil && len(data) != n*bc { 214 panic(ErrShape) 215 } 216 if data == nil { 217 data = make([]float64, n*bc) 218 } 219 uplo := blas.Lower 220 if kind { 221 uplo = blas.Upper 222 } 223 return &TriBandDense{ 224 mat: blas64.TriangularBand{ 225 Uplo: uplo, 226 Diag: blas.NonUnit, 227 N: n, 228 K: k, 229 Data: data, 230 Stride: bc, 231 }, 232 } 233 } 234 235 // Dims returns the number of rows and columns in the matrix. 236 func (t *TriBandDense) Dims() (r, c int) { 237 return t.mat.N, t.mat.N 238 } 239 240 // T performs an implicit transpose by returning the receiver inside a Transpose. 241 func (t *TriBandDense) T() Matrix { 242 return Transpose{t} 243 } 244 245 // IsEmpty returns whether the receiver is empty. Empty matrices can be the 246 // receiver for size-restricted operations. The receiver can be emptied using 247 // Reset. 248 func (t *TriBandDense) IsEmpty() bool { 249 // It must be the case that t.Dims() returns 250 // zeros in this case. See comment in Reset(). 251 return t.mat.Stride == 0 252 } 253 254 // Reset empties the matrix so that it can be reused as the 255 // receiver of a dimensionally restricted operation. 256 // 257 // Reset should not be used when the matrix shares backing data. 258 // See the Reseter interface for more information. 259 func (t *TriBandDense) Reset() { 260 t.mat.N = 0 261 t.mat.Stride = 0 262 t.mat.K = 0 263 t.mat.Data = t.mat.Data[:0] 264 } 265 266 // ReuseAsTriBand changes the receiver to be of size n×n, bandwidth k+1 and of 267 // the given kind, re-using the backing data slice if it has sufficient capacity 268 // and allocating a new slice otherwise. The backing data is zero on return. 269 // 270 // The receiver must be empty, n must be positive and k must be non-negative and 271 // less than n, otherwise ReuseAsTriBand will panic. To empty the receiver for 272 // re-use, Reset should be used. 273 func (t *TriBandDense) ReuseAsTriBand(n, k int, kind TriKind) { 274 if n <= 0 || k < 0 { 275 if n == 0 { 276 panic(ErrZeroLength) 277 } 278 panic(ErrNegativeDimension) 279 } 280 if k+1 > n { 281 panic(ErrBandwidth) 282 } 283 if !t.IsEmpty() { 284 panic(ErrReuseNonEmpty) 285 } 286 t.reuseAsZeroed(n, k, kind) 287 } 288 289 // reuseAsZeroed resizes an empty receiver to an n×n triangular band matrix with 290 // the given bandwidth and orientation. If the receiver is not empty, 291 // reuseAsZeroed checks that the receiver has the correct size, bandwidth and 292 // orientation. It then zeros out the matrix data. 293 func (t *TriBandDense) reuseAsZeroed(n, k int, kind TriKind) { 294 // reuseAsZeroed must be kept in sync with reuseAsNonZeroed. 295 if n == 0 { 296 panic(ErrZeroLength) 297 } 298 ul := blas.Lower 299 if kind == Upper { 300 ul = blas.Upper 301 } 302 if t.IsEmpty() { 303 t.mat = blas64.TriangularBand{ 304 Uplo: ul, 305 Diag: blas.NonUnit, 306 N: n, 307 K: k, 308 Data: useZeroed(t.mat.Data, n*(k+1)), 309 Stride: k + 1, 310 } 311 return 312 } 313 if t.mat.N != n || t.mat.K != k { 314 panic(ErrShape) 315 } 316 if t.mat.Uplo != ul { 317 panic(ErrTriangle) 318 } 319 t.Zero() 320 } 321 322 // reuseAsNonZeroed resizes an empty receiver to an n×n triangular band matrix 323 // with the given bandwidth and orientation. If the receiver is not empty, 324 // reuseAsZeroed checks that the receiver has the correct size, bandwidth and 325 // orientation. 326 func (t *TriBandDense) reuseAsNonZeroed(n, k int, kind TriKind) { 327 // reuseAsNonZeroed must be kept in sync with reuseAsZeroed. 328 if n == 0 { 329 panic(ErrZeroLength) 330 } 331 ul := blas.Lower 332 if kind == Upper { 333 ul = blas.Upper 334 } 335 if t.IsEmpty() { 336 t.mat = blas64.TriangularBand{ 337 Uplo: ul, 338 Diag: blas.NonUnit, 339 N: n, 340 K: k, 341 Data: use(t.mat.Data, n*(k+1)), 342 Stride: k + 1, 343 } 344 return 345 } 346 if t.mat.N != n || t.mat.K != k { 347 panic(ErrShape) 348 } 349 if t.mat.Uplo != ul { 350 panic(ErrTriangle) 351 } 352 } 353 354 // Zero sets all of the matrix elements to zero. 355 func (t *TriBandDense) Zero() { 356 if t.isUpper() { 357 for i := 0; i < t.mat.N; i++ { 358 u := min(1+t.mat.K, t.mat.N-i) 359 zero(t.mat.Data[i*t.mat.Stride : i*t.mat.Stride+u]) 360 } 361 return 362 } 363 for i := 0; i < t.mat.N; i++ { 364 l := max(0, t.mat.K-i) 365 zero(t.mat.Data[i*t.mat.Stride+l : i*t.mat.Stride+t.mat.K+1]) 366 } 367 } 368 369 func (t *TriBandDense) isUpper() bool { 370 return isUpperUplo(t.mat.Uplo) 371 } 372 373 func (t *TriBandDense) triKind() TriKind { 374 return TriKind(isUpperUplo(t.mat.Uplo)) 375 } 376 377 // Triangle returns the dimension of t and its orientation. The returned 378 // orientation is only valid when n is not zero. 379 func (t *TriBandDense) Triangle() (n int, kind TriKind) { 380 return t.mat.N, t.triKind() 381 } 382 383 // TTri performs an implicit transpose by returning the receiver inside a TransposeTri. 384 func (t *TriBandDense) TTri() Triangular { 385 return TransposeTri{t} 386 } 387 388 // Bandwidth returns the upper and lower bandwidths of the matrix. 389 func (t *TriBandDense) Bandwidth() (kl, ku int) { 390 if t.isUpper() { 391 return 0, t.mat.K 392 } 393 return t.mat.K, 0 394 } 395 396 // TBand performs an implicit transpose by returning the receiver inside a TransposeBand. 397 func (t *TriBandDense) TBand() Banded { 398 return TransposeBand{t} 399 } 400 401 // TriBand returns the number of rows/columns in the matrix, the 402 // size of the bandwidth, and the orientation. 403 func (t *TriBandDense) TriBand() (n, k int, kind TriKind) { 404 return t.mat.N, t.mat.K, TriKind(!t.IsEmpty()) && t.triKind() 405 } 406 407 // TTriBand performs an implicit transpose by returning the receiver inside a TransposeTriBand. 408 func (t *TriBandDense) TTriBand() TriBanded { 409 return TransposeTriBand{t} 410 } 411 412 // RawTriBand returns the underlying blas64.TriangularBand used by the receiver. 413 // Changes to the blas64.TriangularBand.Data slice will be reflected in the original 414 // matrix, changes to the N, K, Stride, Uplo and Diag fields will not. 415 func (t *TriBandDense) RawTriBand() blas64.TriangularBand { 416 return t.mat 417 } 418 419 // SetRawTriBand sets the underlying blas64.TriangularBand used by the receiver. 420 // Changes to elements in the receiver following the call will be reflected 421 // in the input. 422 // 423 // The supplied TriangularBand must not use blas.Unit storage format. 424 func (t *TriBandDense) SetRawTriBand(mat blas64.TriangularBand) { 425 if mat.Diag == blas.Unit { 426 panic("mat: cannot set TriBand with Unit storage") 427 } 428 t.mat = mat 429 } 430 431 // DiagView returns the diagonal as a matrix backed by the original data. 432 func (t *TriBandDense) DiagView() Diagonal { 433 if t.mat.Diag == blas.Unit { 434 panic("mat: cannot take view of Unit diagonal") 435 } 436 n := t.mat.N 437 data := t.mat.Data 438 if !t.isUpper() { 439 data = data[t.mat.K:] 440 } 441 return &DiagDense{ 442 mat: blas64.Vector{ 443 N: n, 444 Inc: t.mat.Stride, 445 Data: data[:(n-1)*t.mat.Stride+1], 446 }, 447 } 448 } 449 450 // Trace returns the trace. 451 func (t *TriBandDense) Trace() float64 { 452 rb := t.RawTriBand() 453 var tr float64 454 var offsetIndex int 455 if rb.Uplo == blas.Lower { 456 offsetIndex = rb.K 457 } 458 for i := 0; i < rb.N; i++ { 459 tr += rb.Data[offsetIndex+i*rb.Stride] 460 } 461 return tr 462 } 463 464 // SolveTo solves a triangular system T * X = B or Tᵀ * X = B where T is an 465 // n×n triangular band matrix represented by the receiver and B is a given 466 // n×nrhs matrix. If T is non-singular, the result will be stored into dst and 467 // nil will be returned. If T is singular, the contents of dst will be undefined 468 // and a Condition error will be returned. 469 func (t *TriBandDense) SolveTo(dst *Dense, trans bool, b Matrix) error { 470 n, nrhs := b.Dims() 471 if n != t.mat.N { 472 panic(ErrShape) 473 } 474 if b, ok := b.(RawMatrixer); ok && dst != b { 475 dst.checkOverlap(b.RawMatrix()) 476 } 477 dst.reuseAsNonZeroed(n, nrhs) 478 if dst != b { 479 dst.Copy(b) 480 } 481 var ok bool 482 if trans { 483 ok = lapack64.Tbtrs(blas.Trans, t.mat, dst.mat) 484 } else { 485 ok = lapack64.Tbtrs(blas.NoTrans, t.mat, dst.mat) 486 } 487 if !ok { 488 return Condition(math.Inf(1)) 489 } 490 return nil 491 } 492 493 // SolveVecTo solves a triangular system T * x = b or Tᵀ * x = b where T is an 494 // n×n triangular band matrix represented by the receiver and b is a given 495 // n-vector. If T is non-singular, the result will be stored into dst and nil 496 // will be returned. If T is singular, the contents of dst will be undefined and 497 // a Condition error will be returned. 498 func (t *TriBandDense) SolveVecTo(dst *VecDense, trans bool, b Vector) error { 499 n, nrhs := b.Dims() 500 if n != t.mat.N || nrhs != 1 { 501 panic(ErrShape) 502 } 503 if b, ok := b.(RawVectorer); ok && dst != b { 504 dst.checkOverlap(b.RawVector()) 505 } 506 dst.reuseAsNonZeroed(n) 507 if dst != b { 508 dst.CopyVec(b) 509 } 510 var ok bool 511 if trans { 512 ok = lapack64.Tbtrs(blas.Trans, t.mat, dst.asGeneral()) 513 } else { 514 ok = lapack64.Tbtrs(blas.NoTrans, t.mat, dst.asGeneral()) 515 } 516 if !ok { 517 return Condition(math.Inf(1)) 518 } 519 return nil 520 } 521 522 func copySymBandIntoTriBand(dst *TriBandDense, s SymBanded) { 523 n, k, upper := dst.TriBand() 524 ns, ks := s.SymBand() 525 if n != ns { 526 panic("mat: triangle size mismatch") 527 } 528 if k != ks { 529 panic("mat: triangle bandwidth mismatch") 530 } 531 532 // TODO(vladimir-ch): implement the missing cases below as needed. 533 t := dst.mat 534 sU, _ := untransposeExtract(s) 535 if sbd, ok := sU.(*SymBandDense); ok { 536 s := sbd.RawSymBand() 537 if upper { 538 if s.Uplo == blas.Upper { 539 // dst is upper triangular, s is stored in upper triangle. 540 for i := 0; i < n; i++ { 541 ilen := min(k+1, n-i) 542 copy(t.Data[i*t.Stride:i*t.Stride+ilen], s.Data[i*s.Stride:i*s.Stride+ilen]) 543 } 544 } else { 545 // dst is upper triangular, s is stored in lower triangle. 546 // 547 // The following is a possible implementation for this case but 548 // is commented out due to lack of test coverage. 549 // for i := 0; i < n; i++ { 550 // ilen := min(k+1, n-i) 551 // for j := 0; j < ilen; j++ { 552 // t.Data[i*t.Stride+j] = s.Data[(i+j)*s.Stride+k-j] 553 // } 554 // } 555 panic("not implemented") 556 } 557 } else { 558 if s.Uplo == blas.Upper { 559 // dst is lower triangular, s is stored in upper triangle. 560 panic("not implemented") 561 } else { 562 // dst is lower triangular, s is stored in lower triangle. 563 panic("not implemented") 564 } 565 } 566 return 567 } 568 if upper { 569 for i := 0; i < n; i++ { 570 ilen := min(k+1, n-i) 571 for j := 0; j < ilen; j++ { 572 t.Data[i*t.Stride+j] = s.At(i, i+j) 573 } 574 } 575 } else { 576 panic("not implemented") 577 } 578 }