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  }