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