github.com/gopherd/gonum@v0.0.4/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/gopherd/gonum/blas" 11 "github.com/gopherd/gonum/blas/blas64" 12 "github.com/gopherd/gonum/lapack" 13 "github.com/gopherd/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 // 1 2 3 0 0 0 172 // 0 4 5 6 0 0 173 // 0 0 7 8 9 0 174 // 0 0 0 10 11 12 175 // 0 0 0 0 13 14 176 // 0 0 0 0 0 15 177 // becomes (* entries are never accessed) 178 // 1 2 3 179 // 4 5 6 180 // 7 8 9 181 // 10 11 12 182 // 13 14 * 183 // 15 * * 184 // which is passed to NewTriBandDense as []float64{1, 2, ..., 15, *, *, *} 185 // with k=2 and kind = mat.Upper. 186 // The lower triangular banded matrix 187 // 1 0 0 0 0 0 188 // 2 3 0 0 0 0 189 // 4 5 6 0 0 0 190 // 0 7 8 9 0 0 191 // 0 0 10 11 12 0 192 // 0 0 0 13 14 15 193 // becomes (* entries are never accessed) 194 // * * 1 195 // * 2 3 196 // 4 5 6 197 // 7 8 9 198 // 10 11 12 199 // 13 14 15 200 // which is passed to NewTriBandDense as []float64{*, *, *, 1, 2, ..., 15} 201 // with k=2 and kind = mat.Lower. 202 // Only the values in the band portion of the matrix are used. 203 func NewTriBandDense(n, k int, kind TriKind, data []float64) *TriBandDense { 204 if n <= 0 || k < 0 { 205 if n == 0 { 206 panic(ErrZeroLength) 207 } 208 panic(ErrNegativeDimension) 209 } 210 if k+1 > n { 211 panic(ErrBandwidth) 212 } 213 bc := k + 1 214 if data != nil && len(data) != n*bc { 215 panic(ErrShape) 216 } 217 if data == nil { 218 data = make([]float64, n*bc) 219 } 220 uplo := blas.Lower 221 if kind { 222 uplo = blas.Upper 223 } 224 return &TriBandDense{ 225 mat: blas64.TriangularBand{ 226 Uplo: uplo, 227 Diag: blas.NonUnit, 228 N: n, 229 K: k, 230 Data: data, 231 Stride: bc, 232 }, 233 } 234 } 235 236 // Dims returns the number of rows and columns in the matrix. 237 func (t *TriBandDense) Dims() (r, c int) { 238 return t.mat.N, t.mat.N 239 } 240 241 // T performs an implicit transpose by returning the receiver inside a Transpose. 242 func (t *TriBandDense) T() Matrix { 243 return Transpose{t} 244 } 245 246 // IsEmpty returns whether the receiver is empty. Empty matrices can be the 247 // receiver for size-restricted operations. The receiver can be emptied using 248 // Reset. 249 func (t *TriBandDense) IsEmpty() bool { 250 // It must be the case that t.Dims() returns 251 // zeros in this case. See comment in Reset(). 252 return t.mat.Stride == 0 253 } 254 255 // Reset empties the matrix so that it can be reused as the 256 // receiver of a dimensionally restricted operation. 257 // 258 // Reset should not be used when the matrix shares backing data. 259 // See the Reseter interface for more information. 260 func (t *TriBandDense) Reset() { 261 t.mat.N = 0 262 t.mat.Stride = 0 263 t.mat.K = 0 264 t.mat.Data = t.mat.Data[:0] 265 } 266 267 // ReuseAsTriBand changes the receiver to be of size n×n, bandwidth k+1 and of 268 // the given kind, re-using the backing data slice if it has sufficient capacity 269 // and allocating a new slice otherwise. The backing data is zero on return. 270 // 271 // The receiver must be empty, n must be positive and k must be non-negative and 272 // less than n, otherwise ReuseAsTriBand will panic. To empty the receiver for 273 // re-use, Reset should be used. 274 func (t *TriBandDense) ReuseAsTriBand(n, k int, kind TriKind) { 275 if n <= 0 || k < 0 { 276 if n == 0 { 277 panic(ErrZeroLength) 278 } 279 panic(ErrNegativeDimension) 280 } 281 if k+1 > n { 282 panic(ErrBandwidth) 283 } 284 if !t.IsEmpty() { 285 panic(ErrReuseNonEmpty) 286 } 287 t.reuseAsZeroed(n, k, kind) 288 } 289 290 // reuseAsZeroed resizes an empty receiver to an n×n triangular band matrix with 291 // the given bandwidth and orientation. If the receiver is not empty, 292 // reuseAsZeroed checks that the receiver has the correct size, bandwidth and 293 // orientation. It then zeros out the matrix data. 294 func (t *TriBandDense) reuseAsZeroed(n, k int, kind TriKind) { 295 // reuseAsZeroed must be kept in sync with reuseAsNonZeroed. 296 if n == 0 { 297 panic(ErrZeroLength) 298 } 299 ul := blas.Lower 300 if kind == Upper { 301 ul = blas.Upper 302 } 303 if t.IsEmpty() { 304 t.mat = blas64.TriangularBand{ 305 Uplo: ul, 306 Diag: blas.NonUnit, 307 N: n, 308 K: k, 309 Data: useZeroed(t.mat.Data, n*(k+1)), 310 Stride: k + 1, 311 } 312 return 313 } 314 if t.mat.N != n || t.mat.K != k { 315 panic(ErrShape) 316 } 317 if t.mat.Uplo != ul { 318 panic(ErrTriangle) 319 } 320 t.Zero() 321 } 322 323 // reuseAsNonZeroed resizes an empty receiver to an n×n triangular band matrix 324 // with the given bandwidth and orientation. If the receiver is not empty, 325 // reuseAsZeroed checks that the receiver has the correct size, bandwidth and 326 // orientation. 327 func (t *TriBandDense) reuseAsNonZeroed(n, k int, kind TriKind) { 328 // reuseAsNonZeroed must be kept in sync with reuseAsZeroed. 329 if n == 0 { 330 panic(ErrZeroLength) 331 } 332 ul := blas.Lower 333 if kind == Upper { 334 ul = blas.Upper 335 } 336 if t.IsEmpty() { 337 t.mat = blas64.TriangularBand{ 338 Uplo: ul, 339 Diag: blas.NonUnit, 340 N: n, 341 K: k, 342 Data: use(t.mat.Data, n*(k+1)), 343 Stride: k + 1, 344 } 345 return 346 } 347 if t.mat.N != n || t.mat.K != k { 348 panic(ErrShape) 349 } 350 if t.mat.Uplo != ul { 351 panic(ErrTriangle) 352 } 353 } 354 355 // Zero sets all of the matrix elements to zero. 356 func (t *TriBandDense) Zero() { 357 if t.isUpper() { 358 for i := 0; i < t.mat.N; i++ { 359 u := min(1+t.mat.K, t.mat.N-i) 360 zero(t.mat.Data[i*t.mat.Stride : i*t.mat.Stride+u]) 361 } 362 return 363 } 364 for i := 0; i < t.mat.N; i++ { 365 l := max(0, t.mat.K-i) 366 zero(t.mat.Data[i*t.mat.Stride+l : i*t.mat.Stride+t.mat.K+1]) 367 } 368 } 369 370 func (t *TriBandDense) isUpper() bool { 371 return isUpperUplo(t.mat.Uplo) 372 } 373 374 func (t *TriBandDense) triKind() TriKind { 375 return TriKind(isUpperUplo(t.mat.Uplo)) 376 } 377 378 // Triangle returns the dimension of t and its orientation. The returned 379 // orientation is only valid when n is not zero. 380 func (t *TriBandDense) Triangle() (n int, kind TriKind) { 381 return t.mat.N, t.triKind() 382 } 383 384 // TTri performs an implicit transpose by returning the receiver inside a TransposeTri. 385 func (t *TriBandDense) TTri() Triangular { 386 return TransposeTri{t} 387 } 388 389 // Bandwidth returns the upper and lower bandwidths of the matrix. 390 func (t *TriBandDense) Bandwidth() (kl, ku int) { 391 if t.isUpper() { 392 return 0, t.mat.K 393 } 394 return t.mat.K, 0 395 } 396 397 // TBand performs an implicit transpose by returning the receiver inside a TransposeBand. 398 func (t *TriBandDense) TBand() Banded { 399 return TransposeBand{t} 400 } 401 402 // TriBand returns the number of rows/columns in the matrix, the 403 // size of the bandwidth, and the orientation. 404 func (t *TriBandDense) TriBand() (n, k int, kind TriKind) { 405 return t.mat.N, t.mat.K, TriKind(!t.IsEmpty()) && t.triKind() 406 } 407 408 // TTriBand performs an implicit transpose by returning the receiver inside a TransposeTriBand. 409 func (t *TriBandDense) TTriBand() TriBanded { 410 return TransposeTriBand{t} 411 } 412 413 // RawTriBand returns the underlying blas64.TriangularBand used by the receiver. 414 // Changes to the blas64.TriangularBand.Data slice will be reflected in the original 415 // matrix, changes to the N, K, Stride, Uplo and Diag fields will not. 416 func (t *TriBandDense) RawTriBand() blas64.TriangularBand { 417 return t.mat 418 } 419 420 // SetRawTriBand sets the underlying blas64.TriangularBand used by the receiver. 421 // Changes to elements in the receiver following the call will be reflected 422 // in the input. 423 // 424 // The supplied TriangularBand must not use blas.Unit storage format. 425 func (t *TriBandDense) SetRawTriBand(mat blas64.TriangularBand) { 426 if mat.Diag == blas.Unit { 427 panic("mat: cannot set TriBand with Unit storage") 428 } 429 t.mat = mat 430 } 431 432 // DiagView returns the diagonal as a matrix backed by the original data. 433 func (t *TriBandDense) DiagView() Diagonal { 434 if t.mat.Diag == blas.Unit { 435 panic("mat: cannot take view of Unit diagonal") 436 } 437 n := t.mat.N 438 data := t.mat.Data 439 if !t.isUpper() { 440 data = data[t.mat.K:] 441 } 442 return &DiagDense{ 443 mat: blas64.Vector{ 444 N: n, 445 Inc: t.mat.Stride, 446 Data: data[:(n-1)*t.mat.Stride+1], 447 }, 448 } 449 } 450 451 // Norm returns the specified norm of the receiver. Valid norms are: 452 // 1 - The maximum absolute column sum 453 // 2 - The Frobenius norm, the square root of the sum of the squares of the elements 454 // Inf - The maximum absolute row sum 455 // 456 // Norm will panic with ErrNormOrder if an illegal norm is specified and with 457 // ErrZeroLength if the matrix has zero size. 458 func (t *TriBandDense) Norm(norm float64) float64 { 459 if t.IsEmpty() { 460 panic(ErrZeroLength) 461 } 462 lnorm := normLapack(norm, false) 463 if lnorm == lapack.MaxColumnSum { 464 work := getFloat64s(t.mat.N, false) 465 defer putFloat64s(work) 466 return lapack64.Lantb(lnorm, t.mat, work) 467 } 468 return lapack64.Lantb(lnorm, t.mat, nil) 469 } 470 471 // Trace returns the trace of the matrix. 472 // 473 // Trace will panic with ErrZeroLength if the matrix has zero size. 474 func (t *TriBandDense) Trace() float64 { 475 if t.IsEmpty() { 476 panic(ErrZeroLength) 477 } 478 rb := t.RawTriBand() 479 var tr float64 480 var offsetIndex int 481 if rb.Uplo == blas.Lower { 482 offsetIndex = rb.K 483 } 484 for i := 0; i < rb.N; i++ { 485 tr += rb.Data[offsetIndex+i*rb.Stride] 486 } 487 return tr 488 } 489 490 // SolveTo solves a triangular system T * X = B or Tᵀ * X = B where T is an 491 // n×n triangular band matrix represented by the receiver and B is a given 492 // n×nrhs matrix. If T is non-singular, the result will be stored into dst and 493 // nil will be returned. If T is singular, the contents of dst will be undefined 494 // and a Condition error will be returned. 495 func (t *TriBandDense) SolveTo(dst *Dense, trans bool, b Matrix) error { 496 n, nrhs := b.Dims() 497 if n != t.mat.N { 498 panic(ErrShape) 499 } 500 if b, ok := b.(RawMatrixer); ok && dst != b { 501 dst.checkOverlap(b.RawMatrix()) 502 } 503 dst.reuseAsNonZeroed(n, nrhs) 504 if dst != b { 505 dst.Copy(b) 506 } 507 var ok bool 508 if trans { 509 ok = lapack64.Tbtrs(blas.Trans, t.mat, dst.mat) 510 } else { 511 ok = lapack64.Tbtrs(blas.NoTrans, t.mat, dst.mat) 512 } 513 if !ok { 514 return Condition(math.Inf(1)) 515 } 516 return nil 517 } 518 519 // SolveVecTo solves a triangular system T * x = b or Tᵀ * x = b where T is an 520 // n×n triangular band matrix represented by the receiver and b is a given 521 // n-vector. If T is non-singular, the result will be stored into dst and nil 522 // will be returned. If T is singular, the contents of dst will be undefined and 523 // a Condition error will be returned. 524 func (t *TriBandDense) SolveVecTo(dst *VecDense, trans bool, b Vector) error { 525 n, nrhs := b.Dims() 526 if n != t.mat.N || nrhs != 1 { 527 panic(ErrShape) 528 } 529 if b, ok := b.(RawVectorer); ok && dst != b { 530 dst.checkOverlap(b.RawVector()) 531 } 532 dst.reuseAsNonZeroed(n) 533 if dst != b { 534 dst.CopyVec(b) 535 } 536 var ok bool 537 if trans { 538 ok = lapack64.Tbtrs(blas.Trans, t.mat, dst.asGeneral()) 539 } else { 540 ok = lapack64.Tbtrs(blas.NoTrans, t.mat, dst.asGeneral()) 541 } 542 if !ok { 543 return Condition(math.Inf(1)) 544 } 545 return nil 546 } 547 548 func copySymBandIntoTriBand(dst *TriBandDense, s SymBanded) { 549 n, k, upper := dst.TriBand() 550 ns, ks := s.SymBand() 551 if n != ns { 552 panic("mat: triangle size mismatch") 553 } 554 if k != ks { 555 panic("mat: triangle bandwidth mismatch") 556 } 557 558 // TODO(vladimir-ch): implement the missing cases below as needed. 559 t := dst.mat 560 sU, _ := untransposeExtract(s) 561 if sbd, ok := sU.(*SymBandDense); ok { 562 s := sbd.RawSymBand() 563 if upper { 564 if s.Uplo == blas.Upper { 565 // dst is upper triangular, s is stored in upper triangle. 566 for i := 0; i < n; i++ { 567 ilen := min(k+1, n-i) 568 copy(t.Data[i*t.Stride:i*t.Stride+ilen], s.Data[i*s.Stride:i*s.Stride+ilen]) 569 } 570 } else { 571 // dst is upper triangular, s is stored in lower triangle. 572 // 573 // The following is a possible implementation for this case but 574 // is commented out due to lack of test coverage. 575 // for i := 0; i < n; i++ { 576 // ilen := min(k+1, n-i) 577 // for j := 0; j < ilen; j++ { 578 // t.Data[i*t.Stride+j] = s.Data[(i+j)*s.Stride+k-j] 579 // } 580 // } 581 panic("not implemented") 582 } 583 } else { 584 if s.Uplo == blas.Upper { 585 // dst is lower triangular, s is stored in upper triangle. 586 panic("not implemented") 587 } else { 588 // dst is lower triangular, s is stored in lower triangle. 589 panic("not implemented") 590 } 591 } 592 return 593 } 594 if upper { 595 for i := 0; i < n; i++ { 596 ilen := min(k+1, n-i) 597 for j := 0; j < ilen; j++ { 598 t.Data[i*t.Stride+j] = s.At(i, i+j) 599 } 600 } 601 } else { 602 panic("not implemented") 603 } 604 }