gonum.org/v1/gonum@v0.14.0/mat/triangular.go (about) 1 // Copyright ©2015 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 triDense *TriDense 18 _ Matrix = triDense 19 _ allMatrix = triDense 20 _ denseMatrix = triDense 21 _ Triangular = triDense 22 _ RawTriangular = triDense 23 _ MutableTriangular = triDense 24 25 _ NonZeroDoer = triDense 26 _ RowNonZeroDoer = triDense 27 _ ColNonZeroDoer = triDense 28 ) 29 30 // TriDense represents an upper or lower triangular matrix in dense storage 31 // format. 32 type TriDense struct { 33 mat blas64.Triangular 34 cap int 35 } 36 37 // Triangular represents a triangular matrix. Triangular matrices are always square. 38 type Triangular interface { 39 Matrix 40 // Triangle returns the number of rows/columns in the matrix and its 41 // orientation. 42 Triangle() (n int, kind TriKind) 43 44 // TTri is the equivalent of the T() method in the Matrix interface but 45 // guarantees the transpose is of triangular type. 46 TTri() Triangular 47 } 48 49 // A RawTriangular can return a blas64.Triangular representation of the receiver. 50 // Changes to the blas64.Triangular.Data slice will be reflected in the original 51 // matrix, changes to the N, Stride, Uplo and Diag fields will not. 52 type RawTriangular interface { 53 RawTriangular() blas64.Triangular 54 } 55 56 // A MutableTriangular can set elements of a triangular matrix. 57 type MutableTriangular interface { 58 Triangular 59 SetTri(i, j int, v float64) 60 } 61 62 var ( 63 _ Matrix = TransposeTri{} 64 _ Triangular = TransposeTri{} 65 _ UntransposeTrier = TransposeTri{} 66 ) 67 68 // TransposeTri is a type for performing an implicit transpose of a Triangular 69 // matrix. It implements the Triangular interface, returning values from the 70 // transpose of the matrix within. 71 type TransposeTri struct { 72 Triangular Triangular 73 } 74 75 // At returns the value of the element at row i and column j of the transposed 76 // matrix, that is, row j and column i of the Triangular field. 77 func (t TransposeTri) At(i, j int) float64 { 78 return t.Triangular.At(j, i) 79 } 80 81 // Dims returns the dimensions of the transposed matrix. Triangular matrices are 82 // square and thus this is the same size as the original Triangular. 83 func (t TransposeTri) Dims() (r, c int) { 84 c, r = t.Triangular.Dims() 85 return r, c 86 } 87 88 // T performs an implicit transpose by returning the Triangular field. 89 func (t TransposeTri) T() Matrix { 90 return t.Triangular 91 } 92 93 // Triangle returns the number of rows/columns in the matrix and its orientation. 94 func (t TransposeTri) Triangle() (int, TriKind) { 95 n, upper := t.Triangular.Triangle() 96 return n, !upper 97 } 98 99 // TTri performs an implicit transpose by returning the Triangular field. 100 func (t TransposeTri) TTri() Triangular { 101 return t.Triangular 102 } 103 104 // Untranspose returns the Triangular field. 105 func (t TransposeTri) Untranspose() Matrix { 106 return t.Triangular 107 } 108 109 func (t TransposeTri) UntransposeTri() Triangular { 110 return t.Triangular 111 } 112 113 // NewTriDense creates a new Triangular matrix with n rows and columns. If data == nil, 114 // a new slice is allocated for the backing slice. If len(data) == n*n, data is 115 // used as the backing slice, and changes to the elements of the returned TriDense 116 // will be reflected in data. If neither of these is true, NewTriDense will panic. 117 // NewTriDense will panic if n is zero. 118 // 119 // The data must be arranged in row-major order, i.e. the (i*c + j)-th 120 // element in the data slice is the {i, j}-th element in the matrix. 121 // Only the values in the triangular portion corresponding to kind are used. 122 func NewTriDense(n int, kind TriKind, data []float64) *TriDense { 123 if n <= 0 { 124 if n == 0 { 125 panic(ErrZeroLength) 126 } 127 panic("mat: negative dimension") 128 } 129 if data != nil && len(data) != n*n { 130 panic(ErrShape) 131 } 132 if data == nil { 133 data = make([]float64, n*n) 134 } 135 uplo := blas.Lower 136 if kind == Upper { 137 uplo = blas.Upper 138 } 139 return &TriDense{ 140 mat: blas64.Triangular{ 141 N: n, 142 Stride: n, 143 Data: data, 144 Uplo: uplo, 145 Diag: blas.NonUnit, 146 }, 147 cap: n, 148 } 149 } 150 151 func (t *TriDense) Dims() (r, c int) { 152 return t.mat.N, t.mat.N 153 } 154 155 // Triangle returns the dimension of t and its orientation. The returned 156 // orientation is only valid when n is not empty. 157 func (t *TriDense) Triangle() (n int, kind TriKind) { 158 return t.mat.N, t.triKind() 159 } 160 161 func (t *TriDense) isUpper() bool { 162 return isUpperUplo(t.mat.Uplo) 163 } 164 165 func (t *TriDense) triKind() TriKind { 166 return TriKind(isUpperUplo(t.mat.Uplo)) 167 } 168 169 func isUpperUplo(u blas.Uplo) bool { 170 switch u { 171 case blas.Upper: 172 return true 173 case blas.Lower: 174 return false 175 default: 176 panic(badTriangle) 177 } 178 } 179 180 // asSymBlas returns the receiver restructured as a blas64.Symmetric with the 181 // same backing memory. Panics if the receiver is unit. 182 // This returns a blas64.Symmetric and not a *SymDense because SymDense can only 183 // be upper triangular. 184 func (t *TriDense) asSymBlas() blas64.Symmetric { 185 if t.mat.Diag == blas.Unit { 186 panic("mat: cannot convert unit TriDense into blas64.Symmetric") 187 } 188 return blas64.Symmetric{ 189 N: t.mat.N, 190 Stride: t.mat.Stride, 191 Data: t.mat.Data, 192 Uplo: t.mat.Uplo, 193 } 194 } 195 196 // T performs an implicit transpose by returning the receiver inside a Transpose. 197 func (t *TriDense) T() Matrix { 198 return Transpose{t} 199 } 200 201 // TTri performs an implicit transpose by returning the receiver inside a TransposeTri. 202 func (t *TriDense) TTri() Triangular { 203 return TransposeTri{t} 204 } 205 206 func (t *TriDense) RawTriangular() blas64.Triangular { 207 return t.mat 208 } 209 210 // SetRawTriangular sets the underlying blas64.Triangular used by the receiver. 211 // Changes to elements in the receiver following the call will be reflected 212 // in the input. 213 // 214 // The supplied Triangular must not use blas.Unit storage format. 215 func (t *TriDense) SetRawTriangular(mat blas64.Triangular) { 216 if mat.Diag == blas.Unit { 217 panic("mat: cannot set TriDense with Unit storage format") 218 } 219 t.cap = mat.N 220 t.mat = mat 221 } 222 223 // Reset empties the matrix so that it can be reused as the 224 // receiver of a dimensionally restricted operation. 225 // 226 // Reset should not be used when the matrix shares backing data. 227 // See the Reseter interface for more information. 228 func (t *TriDense) Reset() { 229 // N and Stride must be zeroed in unison. 230 t.mat.N, t.mat.Stride = 0, 0 231 // Defensively zero Uplo to ensure 232 // it is set correctly later. 233 t.mat.Uplo = 0 234 t.mat.Data = t.mat.Data[:0] 235 } 236 237 // Zero sets all of the matrix elements to zero. 238 func (t *TriDense) Zero() { 239 if t.isUpper() { 240 for i := 0; i < t.mat.N; i++ { 241 zero(t.mat.Data[i*t.mat.Stride+i : i*t.mat.Stride+t.mat.N]) 242 } 243 return 244 } 245 for i := 0; i < t.mat.N; i++ { 246 zero(t.mat.Data[i*t.mat.Stride : i*t.mat.Stride+i+1]) 247 } 248 } 249 250 // IsEmpty returns whether the receiver is empty. Empty matrices can be the 251 // receiver for size-restricted operations. The receiver can be emptied using 252 // Reset. 253 func (t *TriDense) IsEmpty() bool { 254 // It must be the case that t.Dims() returns 255 // zeros in this case. See comment in Reset(). 256 return t.mat.Stride == 0 257 } 258 259 // untransposeTri untransposes a matrix if applicable. If a is an UntransposeTrier, then 260 // untransposeTri returns the underlying matrix and true. If it is not, then it returns 261 // the input matrix and false. 262 func untransposeTri(a Triangular) (Triangular, bool) { 263 if ut, ok := a.(UntransposeTrier); ok { 264 return ut.UntransposeTri(), true 265 } 266 return a, false 267 } 268 269 // ReuseAsTri changes the receiver if it IsEmpty() to be of size n×n. 270 // 271 // ReuseAsTri re-uses the backing data slice if it has sufficient capacity, 272 // otherwise a new slice is allocated. The backing data is zero on return. 273 // 274 // ReuseAsTri panics if the receiver is not empty, and panics if 275 // the input size is less than one. To empty the receiver for re-use, 276 // Reset should be used. 277 func (t *TriDense) ReuseAsTri(n int, kind TriKind) { 278 if n <= 0 { 279 if n == 0 { 280 panic(ErrZeroLength) 281 } 282 panic(ErrNegativeDimension) 283 } 284 if !t.IsEmpty() { 285 panic(ErrReuseNonEmpty) 286 } 287 t.reuseAsZeroed(n, kind) 288 } 289 290 // reuseAsNonZeroed resizes an empty receiver to an n×n triangular matrix with the given 291 // orientation. If the receiver is not empty, reuseAsNonZeroed checks that the receiver 292 // is the correct size and orientation. 293 func (t *TriDense) reuseAsNonZeroed(n int, kind TriKind) { 294 // reuseAsNonZeroed must be kept in sync with reuseAsZeroed. 295 if n == 0 { 296 panic(ErrZeroLength) 297 } 298 ul := blas.Lower 299 if kind == Upper { 300 ul = blas.Upper 301 } 302 if t.mat.N > t.cap { 303 // Panic as a string, not a mat.Error. 304 panic(badCap) 305 } 306 if t.IsEmpty() { 307 t.mat = blas64.Triangular{ 308 N: n, 309 Stride: n, 310 Diag: blas.NonUnit, 311 Data: use(t.mat.Data, n*n), 312 Uplo: ul, 313 } 314 t.cap = n 315 return 316 } 317 if t.mat.N != n { 318 panic(ErrShape) 319 } 320 if t.mat.Uplo != ul { 321 panic(ErrTriangle) 322 } 323 } 324 325 // reuseAsZeroed resizes an empty receiver to an n×n triangular matrix with the given 326 // orientation. If the receiver is not empty, reuseAsZeroed checks that the receiver 327 // is the correct size and orientation. It then zeros out the matrix data. 328 func (t *TriDense) reuseAsZeroed(n int, kind TriKind) { 329 // reuseAsZeroed must be kept in sync with reuseAsNonZeroed. 330 if n == 0 { 331 panic(ErrZeroLength) 332 } 333 ul := blas.Lower 334 if kind == Upper { 335 ul = blas.Upper 336 } 337 if t.mat.N > t.cap { 338 // Panic as a string, not a mat.Error. 339 panic(badCap) 340 } 341 if t.IsEmpty() { 342 t.mat = blas64.Triangular{ 343 N: n, 344 Stride: n, 345 Diag: blas.NonUnit, 346 Data: useZeroed(t.mat.Data, n*n), 347 Uplo: ul, 348 } 349 t.cap = n 350 return 351 } 352 if t.mat.N != n { 353 panic(ErrShape) 354 } 355 if t.mat.Uplo != ul { 356 panic(ErrTriangle) 357 } 358 t.Zero() 359 } 360 361 // isolatedWorkspace returns a new TriDense matrix w with the size of a and 362 // returns a callback to defer which performs cleanup at the return of the call. 363 // This should be used when a method receiver is the same pointer as an input argument. 364 func (t *TriDense) isolatedWorkspace(a Triangular) (w *TriDense, restore func()) { 365 n, kind := a.Triangle() 366 if n == 0 { 367 panic(ErrZeroLength) 368 } 369 w = getTriDenseWorkspace(n, kind, false) 370 return w, func() { 371 t.Copy(w) 372 putTriWorkspace(w) 373 } 374 } 375 376 // DiagView returns the diagonal as a matrix backed by the original data. 377 func (t *TriDense) DiagView() Diagonal { 378 if t.mat.Diag == blas.Unit { 379 panic("mat: cannot take view of Unit diagonal") 380 } 381 n := t.mat.N 382 return &DiagDense{ 383 mat: blas64.Vector{ 384 N: n, 385 Inc: t.mat.Stride + 1, 386 Data: t.mat.Data[:(n-1)*t.mat.Stride+n], 387 }, 388 } 389 } 390 391 // Copy makes a copy of elements of a into the receiver. It is similar to the 392 // built-in copy; it copies as much as the overlap between the two matrices and 393 // returns the number of rows and columns it copied. Only elements within the 394 // receiver's non-zero triangle are set. 395 // 396 // See the Copier interface for more information. 397 func (t *TriDense) Copy(a Matrix) (r, c int) { 398 r, c = a.Dims() 399 r = min(r, t.mat.N) 400 c = min(c, t.mat.N) 401 if r == 0 || c == 0 { 402 return 0, 0 403 } 404 405 switch a := a.(type) { 406 case RawMatrixer: 407 amat := a.RawMatrix() 408 if t.isUpper() { 409 for i := 0; i < r; i++ { 410 copy(t.mat.Data[i*t.mat.Stride+i:i*t.mat.Stride+c], amat.Data[i*amat.Stride+i:i*amat.Stride+c]) 411 } 412 } else { 413 for i := 0; i < r; i++ { 414 copy(t.mat.Data[i*t.mat.Stride:i*t.mat.Stride+i+1], amat.Data[i*amat.Stride:i*amat.Stride+i+1]) 415 } 416 } 417 case RawTriangular: 418 amat := a.RawTriangular() 419 aIsUpper := isUpperUplo(amat.Uplo) 420 tIsUpper := t.isUpper() 421 switch { 422 case tIsUpper && aIsUpper: 423 for i := 0; i < r; i++ { 424 copy(t.mat.Data[i*t.mat.Stride+i:i*t.mat.Stride+c], amat.Data[i*amat.Stride+i:i*amat.Stride+c]) 425 } 426 case !tIsUpper && !aIsUpper: 427 for i := 0; i < r; i++ { 428 copy(t.mat.Data[i*t.mat.Stride:i*t.mat.Stride+i+1], amat.Data[i*amat.Stride:i*amat.Stride+i+1]) 429 } 430 default: 431 for i := 0; i < r; i++ { 432 t.set(i, i, amat.Data[i*amat.Stride+i]) 433 } 434 } 435 default: 436 isUpper := t.isUpper() 437 for i := 0; i < r; i++ { 438 if isUpper { 439 for j := i; j < c; j++ { 440 t.set(i, j, a.At(i, j)) 441 } 442 } else { 443 for j := 0; j <= i; j++ { 444 t.set(i, j, a.At(i, j)) 445 } 446 } 447 } 448 } 449 450 return r, c 451 } 452 453 // InverseTri computes the inverse of the triangular matrix a, storing the result 454 // into the receiver. If a is ill-conditioned, a Condition error will be returned. 455 // Note that matrix inversion is numerically unstable, and should generally be 456 // avoided where possible, for example by using the Solve routines. 457 func (t *TriDense) InverseTri(a Triangular) error { 458 t.checkOverlapMatrix(a) 459 n, _ := a.Triangle() 460 t.reuseAsNonZeroed(a.Triangle()) 461 t.Copy(a) 462 work := getFloat64s(3*n, false) 463 iwork := getInts(n, false) 464 cond := lapack64.Trcon(CondNorm, t.mat, work, iwork) 465 putFloat64s(work) 466 putInts(iwork) 467 if math.IsInf(cond, 1) { 468 return Condition(cond) 469 } 470 ok := lapack64.Trtri(t.mat) 471 if !ok { 472 return Condition(math.Inf(1)) 473 } 474 if cond > ConditionTolerance { 475 return Condition(cond) 476 } 477 return nil 478 } 479 480 // MulTri takes the product of triangular matrices a and b and places the result 481 // in the receiver. The size of a and b must match, and they both must have the 482 // same TriKind, or Mul will panic. 483 func (t *TriDense) MulTri(a, b Triangular) { 484 n, kind := a.Triangle() 485 nb, kindb := b.Triangle() 486 if n != nb { 487 panic(ErrShape) 488 } 489 if kind != kindb { 490 panic(ErrTriangle) 491 } 492 493 aU, _ := untransposeTri(a) 494 bU, _ := untransposeTri(b) 495 t.checkOverlapMatrix(bU) 496 t.checkOverlapMatrix(aU) 497 t.reuseAsNonZeroed(n, kind) 498 var restore func() 499 if t == aU { 500 t, restore = t.isolatedWorkspace(aU) 501 defer restore() 502 } else if t == bU { 503 t, restore = t.isolatedWorkspace(bU) 504 defer restore() 505 } 506 507 // Inspect types here, helps keep the loops later clean(er). 508 _, aDiag := aU.(Diagonal) 509 _, bDiag := bU.(Diagonal) 510 // If they are both diagonal only need 1 loop. 511 // All diagonal matrices are Upper. 512 // TODO: Add fast paths for DiagDense. 513 if aDiag && bDiag { 514 t.Zero() 515 for i := 0; i < n; i++ { 516 t.SetTri(i, i, a.At(i, i)*b.At(i, i)) 517 } 518 return 519 } 520 521 // Now we know at least one matrix is non-diagonal. 522 // And all diagonal matrices are all Upper. 523 // The both-diagonal case is handled above. 524 // TODO: Add fast paths for Dense variants. 525 if kind == Upper { 526 for i := 0; i < n; i++ { 527 for j := i; j < n; j++ { 528 switch { 529 case aDiag: 530 t.SetTri(i, j, a.At(i, i)*b.At(i, j)) 531 case bDiag: 532 t.SetTri(i, j, a.At(i, j)*b.At(j, j)) 533 default: 534 var v float64 535 for k := i; k <= j; k++ { 536 v += a.At(i, k) * b.At(k, j) 537 } 538 t.SetTri(i, j, v) 539 } 540 } 541 } 542 return 543 } 544 for i := 0; i < n; i++ { 545 for j := 0; j <= i; j++ { 546 var v float64 547 for k := j; k <= i; k++ { 548 v += a.At(i, k) * b.At(k, j) 549 } 550 t.SetTri(i, j, v) 551 } 552 } 553 } 554 555 // ScaleTri multiplies the elements of a by f, placing the result in the receiver. 556 // If the receiver is non-zero, the size and kind of the receiver must match 557 // the input, or ScaleTri will panic. 558 func (t *TriDense) ScaleTri(f float64, a Triangular) { 559 n, kind := a.Triangle() 560 t.reuseAsNonZeroed(n, kind) 561 562 // TODO(btracey): Improve the set of fast-paths. 563 switch a := a.(type) { 564 case RawTriangular: 565 amat := a.RawTriangular() 566 if t != a { 567 t.checkOverlap(generalFromTriangular(amat)) 568 } 569 if kind == Upper { 570 for i := 0; i < n; i++ { 571 ts := t.mat.Data[i*t.mat.Stride+i : i*t.mat.Stride+n] 572 as := amat.Data[i*amat.Stride+i : i*amat.Stride+n] 573 for i, v := range as { 574 ts[i] = v * f 575 } 576 } 577 return 578 } 579 for i := 0; i < n; i++ { 580 ts := t.mat.Data[i*t.mat.Stride : i*t.mat.Stride+i+1] 581 as := amat.Data[i*amat.Stride : i*amat.Stride+i+1] 582 for i, v := range as { 583 ts[i] = v * f 584 } 585 } 586 return 587 default: 588 t.checkOverlapMatrix(a) 589 isUpper := kind == Upper 590 for i := 0; i < n; i++ { 591 if isUpper { 592 for j := i; j < n; j++ { 593 t.set(i, j, f*a.At(i, j)) 594 } 595 } else { 596 for j := 0; j <= i; j++ { 597 t.set(i, j, f*a.At(i, j)) 598 } 599 } 600 } 601 } 602 } 603 604 // SliceTri returns a new Triangular that shares backing data with the receiver. 605 // The returned matrix starts at {i,i} of the receiver and extends k-i rows and 606 // columns. The final row and column in the resulting matrix is k-1. 607 // SliceTri panics with ErrIndexOutOfRange if the slice is outside the capacity 608 // of the receiver. 609 func (t *TriDense) SliceTri(i, k int) Triangular { 610 return t.sliceTri(i, k) 611 } 612 613 func (t *TriDense) sliceTri(i, k int) *TriDense { 614 if i < 0 || t.cap < i || k < i || t.cap < k { 615 panic(ErrIndexOutOfRange) 616 } 617 v := *t 618 v.mat.Data = t.mat.Data[i*t.mat.Stride+i : (k-1)*t.mat.Stride+k] 619 v.mat.N = k - i 620 v.cap = t.cap - i 621 return &v 622 } 623 624 // Norm returns the specified norm of the receiver. Valid norms are: 625 // 626 // 1 - The maximum absolute column sum 627 // 2 - The Frobenius norm, the square root of the sum of the squares of the elements 628 // Inf - The maximum absolute row sum 629 // 630 // Norm will panic with ErrNormOrder if an illegal norm is specified and with 631 // ErrZeroLength if the matrix has zero size. 632 func (t *TriDense) Norm(norm float64) float64 { 633 if t.IsEmpty() { 634 panic(ErrZeroLength) 635 } 636 lnorm := normLapack(norm, false) 637 if lnorm == lapack.MaxColumnSum { 638 work := getFloat64s(t.mat.N, false) 639 defer putFloat64s(work) 640 return lapack64.Lantr(lnorm, t.mat, work) 641 } 642 return lapack64.Lantr(lnorm, t.mat, nil) 643 } 644 645 // Trace returns the trace of the matrix. 646 // 647 // Trace will panic with ErrZeroLength if the matrix has zero size. 648 func (t *TriDense) Trace() float64 { 649 if t.IsEmpty() { 650 panic(ErrZeroLength) 651 } 652 // TODO(btracey): could use internal asm sum routine. 653 var v float64 654 for i := 0; i < t.mat.N; i++ { 655 v += t.mat.Data[i*t.mat.Stride+i] 656 } 657 return v 658 } 659 660 // copySymIntoTriangle copies a symmetric matrix into a TriDense 661 func copySymIntoTriangle(t *TriDense, s Symmetric) { 662 n, upper := t.Triangle() 663 ns := s.SymmetricDim() 664 if n != ns { 665 panic("mat: triangle size mismatch") 666 } 667 ts := t.mat.Stride 668 if rs, ok := s.(RawSymmetricer); ok { 669 sd := rs.RawSymmetric() 670 ss := sd.Stride 671 if upper { 672 if sd.Uplo == blas.Upper { 673 for i := 0; i < n; i++ { 674 copy(t.mat.Data[i*ts+i:i*ts+n], sd.Data[i*ss+i:i*ss+n]) 675 } 676 return 677 } 678 for i := 0; i < n; i++ { 679 for j := i; j < n; j++ { 680 t.mat.Data[i*ts+j] = sd.Data[j*ss+i] 681 } 682 } 683 return 684 } 685 if sd.Uplo == blas.Upper { 686 for i := 0; i < n; i++ { 687 for j := 0; j <= i; j++ { 688 t.mat.Data[i*ts+j] = sd.Data[j*ss+i] 689 } 690 } 691 return 692 } 693 for i := 0; i < n; i++ { 694 copy(t.mat.Data[i*ts:i*ts+i+1], sd.Data[i*ss:i*ss+i+1]) 695 } 696 return 697 } 698 if upper { 699 for i := 0; i < n; i++ { 700 for j := i; j < n; j++ { 701 t.mat.Data[i*ts+j] = s.At(i, j) 702 } 703 } 704 return 705 } 706 for i := 0; i < n; i++ { 707 for j := 0; j <= i; j++ { 708 t.mat.Data[i*ts+j] = s.At(i, j) 709 } 710 } 711 } 712 713 // DoNonZero calls the function fn for each of the non-zero elements of t. The function fn 714 // takes a row/column index and the element value of t at (i, j). 715 func (t *TriDense) DoNonZero(fn func(i, j int, v float64)) { 716 if t.isUpper() { 717 for i := 0; i < t.mat.N; i++ { 718 for j := i; j < t.mat.N; j++ { 719 v := t.at(i, j) 720 if v != 0 { 721 fn(i, j, v) 722 } 723 } 724 } 725 return 726 } 727 for i := 0; i < t.mat.N; i++ { 728 for j := 0; j <= i; j++ { 729 v := t.at(i, j) 730 if v != 0 { 731 fn(i, j, v) 732 } 733 } 734 } 735 } 736 737 // DoRowNonZero calls the function fn for each of the non-zero elements of row i of t. The function fn 738 // takes a row/column index and the element value of t at (i, j). 739 func (t *TriDense) DoRowNonZero(i int, fn func(i, j int, v float64)) { 740 if i < 0 || t.mat.N <= i { 741 panic(ErrRowAccess) 742 } 743 if t.isUpper() { 744 for j := i; j < t.mat.N; j++ { 745 v := t.at(i, j) 746 if v != 0 { 747 fn(i, j, v) 748 } 749 } 750 return 751 } 752 for j := 0; j <= i; j++ { 753 v := t.at(i, j) 754 if v != 0 { 755 fn(i, j, v) 756 } 757 } 758 } 759 760 // DoColNonZero calls the function fn for each of the non-zero elements of column j of t. The function fn 761 // takes a row/column index and the element value of t at (i, j). 762 func (t *TriDense) DoColNonZero(j int, fn func(i, j int, v float64)) { 763 if j < 0 || t.mat.N <= j { 764 panic(ErrColAccess) 765 } 766 if t.isUpper() { 767 for i := 0; i <= j; i++ { 768 v := t.at(i, j) 769 if v != 0 { 770 fn(i, j, v) 771 } 772 } 773 return 774 } 775 for i := j; i < t.mat.N; i++ { 776 v := t.at(i, j) 777 if v != 0 { 778 fn(i, j, v) 779 } 780 } 781 }