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