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  }