gonum.org/v1/gonum@v0.14.0/mat/triband.go (about)

     1  // Copyright ©2018 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package mat
     6  
     7  import (
     8  	"math"
     9  
    10  	"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  	triBand TriBanded
    18  	_       Banded     = triBand
    19  	_       Triangular = triBand
    20  
    21  	triBandDense *TriBandDense
    22  	_            Matrix           = triBandDense
    23  	_            allMatrix        = triBandDense
    24  	_            denseMatrix      = triBandDense
    25  	_            Triangular       = triBandDense
    26  	_            Banded           = triBandDense
    27  	_            TriBanded        = triBandDense
    28  	_            RawTriBander     = triBandDense
    29  	_            MutableTriBanded = triBandDense
    30  )
    31  
    32  // TriBanded is a triangular band matrix interface type.
    33  type TriBanded interface {
    34  	Banded
    35  
    36  	// Triangle returns the number of rows/columns in the matrix and its
    37  	// orientation.
    38  	Triangle() (n int, kind TriKind)
    39  
    40  	// TTri is the equivalent of the T() method in the Matrix interface but
    41  	// guarantees the transpose is of triangular type.
    42  	TTri() Triangular
    43  
    44  	// TriBand returns the number of rows/columns in the matrix, the
    45  	// size of the bandwidth, and the orientation.
    46  	TriBand() (n, k int, kind TriKind)
    47  
    48  	// TTriBand is the equivalent of the T() method in the Matrix interface but
    49  	// guarantees the transpose is of banded triangular type.
    50  	TTriBand() TriBanded
    51  }
    52  
    53  // A RawTriBander can return a blas64.TriangularBand representation of the receiver.
    54  // Changes to the blas64.TriangularBand.Data slice will be reflected in the original
    55  // matrix, changes to the N, K, Stride, Uplo and Diag fields will not.
    56  type RawTriBander interface {
    57  	RawTriBand() blas64.TriangularBand
    58  }
    59  
    60  // MutableTriBanded is a triangular band matrix interface type that allows
    61  // elements to be altered.
    62  type MutableTriBanded interface {
    63  	TriBanded
    64  	SetTriBand(i, j int, v float64)
    65  }
    66  
    67  var (
    68  	tTriBand TransposeTriBand
    69  	_        Matrix               = tTriBand
    70  	_        TriBanded            = tTriBand
    71  	_        Untransposer         = tTriBand
    72  	_        UntransposeTrier     = tTriBand
    73  	_        UntransposeBander    = tTriBand
    74  	_        UntransposeTriBander = tTriBand
    75  )
    76  
    77  // TransposeTriBand is a type for performing an implicit transpose of a TriBanded
    78  // matrix. It implements the TriBanded interface, returning values from the
    79  // transpose of the matrix within.
    80  type TransposeTriBand struct {
    81  	TriBanded TriBanded
    82  }
    83  
    84  // At returns the value of the element at row i and column j of the transposed
    85  // matrix, that is, row j and column i of the TriBanded field.
    86  func (t TransposeTriBand) At(i, j int) float64 {
    87  	return t.TriBanded.At(j, i)
    88  }
    89  
    90  // Dims returns the dimensions of the transposed matrix. TriBanded matrices are
    91  // square and thus this is the same size as the original TriBanded.
    92  func (t TransposeTriBand) Dims() (r, c int) {
    93  	c, r = t.TriBanded.Dims()
    94  	return r, c
    95  }
    96  
    97  // T performs an implicit transpose by returning the TriBand field.
    98  func (t TransposeTriBand) T() Matrix {
    99  	return t.TriBanded
   100  }
   101  
   102  // Triangle returns the number of rows/columns in the matrix and its orientation.
   103  func (t TransposeTriBand) Triangle() (int, TriKind) {
   104  	n, upper := t.TriBanded.Triangle()
   105  	return n, !upper
   106  }
   107  
   108  // TTri performs an implicit transpose by returning the TriBand field.
   109  func (t TransposeTriBand) TTri() Triangular {
   110  	return t.TriBanded
   111  }
   112  
   113  // Bandwidth returns the upper and lower bandwidths of the matrix.
   114  func (t TransposeTriBand) Bandwidth() (kl, ku int) {
   115  	kl, ku = t.TriBanded.Bandwidth()
   116  	return ku, kl
   117  }
   118  
   119  // TBand performs an implicit transpose by returning the TriBand field.
   120  func (t TransposeTriBand) TBand() Banded {
   121  	return t.TriBanded
   122  }
   123  
   124  // TriBand returns the number of rows/columns in the matrix, the
   125  // size of the bandwidth, and the orientation.
   126  func (t TransposeTriBand) TriBand() (n, k int, kind TriKind) {
   127  	n, k, kind = t.TriBanded.TriBand()
   128  	return n, k, !kind
   129  }
   130  
   131  // TTriBand performs an implicit transpose by returning the TriBand field.
   132  func (t TransposeTriBand) TTriBand() TriBanded {
   133  	return t.TriBanded
   134  }
   135  
   136  // Untranspose returns the Triangular field.
   137  func (t TransposeTriBand) Untranspose() Matrix {
   138  	return t.TriBanded
   139  }
   140  
   141  // UntransposeTri returns the underlying Triangular matrix.
   142  func (t TransposeTriBand) UntransposeTri() Triangular {
   143  	return t.TriBanded
   144  }
   145  
   146  // UntransposeBand returns the underlying Banded matrix.
   147  func (t TransposeTriBand) UntransposeBand() Banded {
   148  	return t.TriBanded
   149  }
   150  
   151  // UntransposeTriBand returns the underlying TriBanded matrix.
   152  func (t TransposeTriBand) UntransposeTriBand() TriBanded {
   153  	return t.TriBanded
   154  }
   155  
   156  // TriBandDense represents a triangular band matrix in dense storage format.
   157  type TriBandDense struct {
   158  	mat blas64.TriangularBand
   159  }
   160  
   161  // NewTriBandDense creates a new triangular banded matrix with n rows and columns,
   162  // k bands in the direction of the specified kind. If data == nil,
   163  // a new slice is allocated for the backing slice. If len(data) == n*(k+1),
   164  // data is used as the backing slice, and changes to the elements of the returned
   165  // TriBandDense will be reflected in data. If neither of these is true, NewTriBandDense
   166  // will panic. k must be at least zero and less than n, otherwise NewTriBandDense will panic.
   167  //
   168  // The data must be arranged in row-major order constructed by removing the zeros
   169  // from the rows outside the band and aligning the diagonals. For example, if
   170  // the upper-triangular banded matrix
   171  //
   172  //	1  2  3  0  0  0
   173  //	0  4  5  6  0  0
   174  //	0  0  7  8  9  0
   175  //	0  0  0 10 11 12
   176  //	0  0  0 0  13 14
   177  //	0  0  0 0  0  15
   178  //
   179  // becomes (* entries are never accessed)
   180  //
   181  //	 1  2  3
   182  //	 4  5  6
   183  //	 7  8  9
   184  //	10 11 12
   185  //	13 14  *
   186  //	15  *  *
   187  //
   188  // which is passed to NewTriBandDense as []float64{1, 2, ..., 15, *, *, *}
   189  // with k=2 and kind = mat.Upper.
   190  // The lower triangular banded matrix
   191  //
   192  //	1  0  0  0  0  0
   193  //	2  3  0  0  0  0
   194  //	4  5  6  0  0  0
   195  //	0  7  8  9  0  0
   196  //	0  0 10 11 12  0
   197  //	0  0  0 13 14 15
   198  //
   199  // becomes (* entries are never accessed)
   200  //   - *  1
   201  //   - 2  3
   202  //     4  5  6
   203  //     7  8  9
   204  //     10 11 12
   205  //     13 14 15
   206  //
   207  // which is passed to NewTriBandDense as []float64{*, *, *, 1, 2, ..., 15}
   208  // with k=2 and kind = mat.Lower.
   209  // Only the values in the band portion of the matrix are used.
   210  func NewTriBandDense(n, k int, kind TriKind, data []float64) *TriBandDense {
   211  	if n <= 0 || k < 0 {
   212  		if n == 0 {
   213  			panic(ErrZeroLength)
   214  		}
   215  		panic(ErrNegativeDimension)
   216  	}
   217  	if k+1 > n {
   218  		panic(ErrBandwidth)
   219  	}
   220  	bc := k + 1
   221  	if data != nil && len(data) != n*bc {
   222  		panic(ErrShape)
   223  	}
   224  	if data == nil {
   225  		data = make([]float64, n*bc)
   226  	}
   227  	uplo := blas.Lower
   228  	if kind {
   229  		uplo = blas.Upper
   230  	}
   231  	return &TriBandDense{
   232  		mat: blas64.TriangularBand{
   233  			Uplo:   uplo,
   234  			Diag:   blas.NonUnit,
   235  			N:      n,
   236  			K:      k,
   237  			Data:   data,
   238  			Stride: bc,
   239  		},
   240  	}
   241  }
   242  
   243  // Dims returns the number of rows and columns in the matrix.
   244  func (t *TriBandDense) Dims() (r, c int) {
   245  	return t.mat.N, t.mat.N
   246  }
   247  
   248  // T performs an implicit transpose by returning the receiver inside a Transpose.
   249  func (t *TriBandDense) T() Matrix {
   250  	return Transpose{t}
   251  }
   252  
   253  // IsEmpty returns whether the receiver is empty. Empty matrices can be the
   254  // receiver for size-restricted operations. The receiver can be emptied using
   255  // Reset.
   256  func (t *TriBandDense) IsEmpty() bool {
   257  	// It must be the case that t.Dims() returns
   258  	// zeros in this case. See comment in Reset().
   259  	return t.mat.Stride == 0
   260  }
   261  
   262  // Reset empties the matrix so that it can be reused as the
   263  // receiver of a dimensionally restricted operation.
   264  //
   265  // Reset should not be used when the matrix shares backing data.
   266  // See the Reseter interface for more information.
   267  func (t *TriBandDense) Reset() {
   268  	t.mat.N = 0
   269  	t.mat.Stride = 0
   270  	t.mat.K = 0
   271  	t.mat.Data = t.mat.Data[:0]
   272  }
   273  
   274  // ReuseAsTriBand changes the receiver to be of size n×n, bandwidth k+1 and of
   275  // the given kind, re-using the backing data slice if it has sufficient capacity
   276  // and allocating a new slice otherwise. The backing data is zero on return.
   277  //
   278  // The receiver must be empty, n must be positive and k must be non-negative and
   279  // less than n, otherwise ReuseAsTriBand will panic. To empty the receiver for
   280  // re-use, Reset should be used.
   281  func (t *TriBandDense) ReuseAsTriBand(n, k int, kind TriKind) {
   282  	if n <= 0 || k < 0 {
   283  		if n == 0 {
   284  			panic(ErrZeroLength)
   285  		}
   286  		panic(ErrNegativeDimension)
   287  	}
   288  	if k+1 > n {
   289  		panic(ErrBandwidth)
   290  	}
   291  	if !t.IsEmpty() {
   292  		panic(ErrReuseNonEmpty)
   293  	}
   294  	t.reuseAsZeroed(n, k, kind)
   295  }
   296  
   297  // reuseAsZeroed resizes an empty receiver to an n×n triangular band matrix with
   298  // the given bandwidth and orientation. If the receiver is not empty,
   299  // reuseAsZeroed checks that the receiver has the correct size, bandwidth and
   300  // orientation. It then zeros out the matrix data.
   301  func (t *TriBandDense) reuseAsZeroed(n, k int, kind TriKind) {
   302  	// reuseAsZeroed must be kept in sync with reuseAsNonZeroed.
   303  	if n == 0 {
   304  		panic(ErrZeroLength)
   305  	}
   306  	ul := blas.Lower
   307  	if kind == Upper {
   308  		ul = blas.Upper
   309  	}
   310  	if t.IsEmpty() {
   311  		t.mat = blas64.TriangularBand{
   312  			Uplo:   ul,
   313  			Diag:   blas.NonUnit,
   314  			N:      n,
   315  			K:      k,
   316  			Data:   useZeroed(t.mat.Data, n*(k+1)),
   317  			Stride: k + 1,
   318  		}
   319  		return
   320  	}
   321  	if t.mat.N != n || t.mat.K != k {
   322  		panic(ErrShape)
   323  	}
   324  	if t.mat.Uplo != ul {
   325  		panic(ErrTriangle)
   326  	}
   327  	t.Zero()
   328  }
   329  
   330  // reuseAsNonZeroed resizes an empty receiver to an n×n triangular band matrix
   331  // with the given bandwidth and orientation. If the receiver is not empty,
   332  // reuseAsZeroed checks that the receiver has the correct size, bandwidth and
   333  // orientation.
   334  //
   335  //lint:ignore U1000 This will be used later.
   336  func (t *TriBandDense) reuseAsNonZeroed(n, k int, kind TriKind) {
   337  	// reuseAsNonZeroed must be kept in sync with reuseAsZeroed.
   338  	if n == 0 {
   339  		panic(ErrZeroLength)
   340  	}
   341  	ul := blas.Lower
   342  	if kind == Upper {
   343  		ul = blas.Upper
   344  	}
   345  	if t.IsEmpty() {
   346  		t.mat = blas64.TriangularBand{
   347  			Uplo:   ul,
   348  			Diag:   blas.NonUnit,
   349  			N:      n,
   350  			K:      k,
   351  			Data:   use(t.mat.Data, n*(k+1)),
   352  			Stride: k + 1,
   353  		}
   354  		return
   355  	}
   356  	if t.mat.N != n || t.mat.K != k {
   357  		panic(ErrShape)
   358  	}
   359  	if t.mat.Uplo != ul {
   360  		panic(ErrTriangle)
   361  	}
   362  }
   363  
   364  // DoNonZero calls the function fn for each of the non-zero elements of t. The function fn
   365  // takes a row/column index and the element value of t at (i, j).
   366  func (t *TriBandDense) DoNonZero(fn func(i, j int, v float64)) {
   367  	if t.isUpper() {
   368  		for i := 0; i < t.mat.N; i++ {
   369  			for j := i; j < min(i+t.mat.K+1, t.mat.N); j++ {
   370  				v := t.at(i, j)
   371  				if v != 0 {
   372  					fn(i, j, v)
   373  				}
   374  			}
   375  		}
   376  	} else {
   377  		for i := 0; i < t.mat.N; i++ {
   378  			for j := max(0, i-t.mat.K); j <= i; j++ {
   379  				v := t.at(i, j)
   380  				if v != 0 {
   381  					fn(i, j, v)
   382  				}
   383  			}
   384  		}
   385  	}
   386  }
   387  
   388  // DoRowNonZero calls the function fn for each of the non-zero elements of row i of t. The function fn
   389  // takes a row/column index and the element value of t at (i, j).
   390  func (t *TriBandDense) DoRowNonZero(i int, fn func(i, j int, v float64)) {
   391  	if i < 0 || t.mat.N <= i {
   392  		panic(ErrRowAccess)
   393  	}
   394  	if t.isUpper() {
   395  		for j := i; j < min(i+t.mat.K+1, t.mat.N); j++ {
   396  			v := t.at(i, j)
   397  			if v != 0 {
   398  				fn(i, j, v)
   399  			}
   400  		}
   401  	} else {
   402  		for j := max(0, i-t.mat.K); j <= i; j++ {
   403  			v := t.at(i, j)
   404  			if v != 0 {
   405  				fn(i, j, v)
   406  			}
   407  		}
   408  	}
   409  }
   410  
   411  // DoColNonZero calls the function fn for each of the non-zero elements of column j of t. The function fn
   412  // takes a row/column index and the element value of t at (i, j).
   413  func (t *TriBandDense) DoColNonZero(j int, fn func(i, j int, v float64)) {
   414  	if j < 0 || t.mat.N <= j {
   415  		panic(ErrColAccess)
   416  	}
   417  	if t.isUpper() {
   418  		for i := 0; i < t.mat.N; i++ {
   419  			v := t.at(i, j)
   420  			if v != 0 {
   421  				fn(i, j, v)
   422  			}
   423  		}
   424  	} else {
   425  		for i := 0; i < t.mat.N; i++ {
   426  			v := t.at(i, j)
   427  			if v != 0 {
   428  				fn(i, j, v)
   429  			}
   430  		}
   431  	}
   432  }
   433  
   434  // Zero sets all of the matrix elements to zero.
   435  func (t *TriBandDense) Zero() {
   436  	if t.isUpper() {
   437  		for i := 0; i < t.mat.N; i++ {
   438  			u := min(1+t.mat.K, t.mat.N-i)
   439  			zero(t.mat.Data[i*t.mat.Stride : i*t.mat.Stride+u])
   440  		}
   441  		return
   442  	}
   443  	for i := 0; i < t.mat.N; i++ {
   444  		l := max(0, t.mat.K-i)
   445  		zero(t.mat.Data[i*t.mat.Stride+l : i*t.mat.Stride+t.mat.K+1])
   446  	}
   447  }
   448  
   449  func (t *TriBandDense) isUpper() bool {
   450  	return isUpperUplo(t.mat.Uplo)
   451  }
   452  
   453  func (t *TriBandDense) triKind() TriKind {
   454  	return TriKind(isUpperUplo(t.mat.Uplo))
   455  }
   456  
   457  // Triangle returns the dimension of t and its orientation. The returned
   458  // orientation is only valid when n is not zero.
   459  func (t *TriBandDense) Triangle() (n int, kind TriKind) {
   460  	return t.mat.N, t.triKind()
   461  }
   462  
   463  // TTri performs an implicit transpose by returning the receiver inside a TransposeTri.
   464  func (t *TriBandDense) TTri() Triangular {
   465  	return TransposeTri{t}
   466  }
   467  
   468  // Bandwidth returns the upper and lower bandwidths of the matrix.
   469  func (t *TriBandDense) Bandwidth() (kl, ku int) {
   470  	if t.isUpper() {
   471  		return 0, t.mat.K
   472  	}
   473  	return t.mat.K, 0
   474  }
   475  
   476  // TBand performs an implicit transpose by returning the receiver inside a TransposeBand.
   477  func (t *TriBandDense) TBand() Banded {
   478  	return TransposeBand{t}
   479  }
   480  
   481  // TriBand returns the number of rows/columns in the matrix, the
   482  // size of the bandwidth, and the orientation.
   483  func (t *TriBandDense) TriBand() (n, k int, kind TriKind) {
   484  	return t.mat.N, t.mat.K, TriKind(!t.IsEmpty()) && t.triKind()
   485  }
   486  
   487  // TTriBand performs an implicit transpose by returning the receiver inside a TransposeTriBand.
   488  func (t *TriBandDense) TTriBand() TriBanded {
   489  	return TransposeTriBand{t}
   490  }
   491  
   492  // RawTriBand returns the underlying blas64.TriangularBand used by the receiver.
   493  // Changes to the blas64.TriangularBand.Data slice will be reflected in the original
   494  // matrix, changes to the N, K, Stride, Uplo and Diag fields will not.
   495  func (t *TriBandDense) RawTriBand() blas64.TriangularBand {
   496  	return t.mat
   497  }
   498  
   499  // SetRawTriBand sets the underlying blas64.TriangularBand used by the receiver.
   500  // Changes to elements in the receiver following the call will be reflected
   501  // in the input.
   502  //
   503  // The supplied TriangularBand must not use blas.Unit storage format.
   504  func (t *TriBandDense) SetRawTriBand(mat blas64.TriangularBand) {
   505  	if mat.Diag == blas.Unit {
   506  		panic("mat: cannot set TriBand with Unit storage")
   507  	}
   508  	t.mat = mat
   509  }
   510  
   511  // DiagView returns the diagonal as a matrix backed by the original data.
   512  func (t *TriBandDense) DiagView() Diagonal {
   513  	if t.mat.Diag == blas.Unit {
   514  		panic("mat: cannot take view of Unit diagonal")
   515  	}
   516  	n := t.mat.N
   517  	data := t.mat.Data
   518  	if !t.isUpper() {
   519  		data = data[t.mat.K:]
   520  	}
   521  	return &DiagDense{
   522  		mat: blas64.Vector{
   523  			N:    n,
   524  			Inc:  t.mat.Stride,
   525  			Data: data[:(n-1)*t.mat.Stride+1],
   526  		},
   527  	}
   528  }
   529  
   530  // Norm returns the specified norm of the receiver. Valid norms are:
   531  //
   532  //	1 - The maximum absolute column sum
   533  //	2 - The Frobenius norm, the square root of the sum of the squares of the elements
   534  //	Inf - The maximum absolute row sum
   535  //
   536  // Norm will panic with ErrNormOrder if an illegal norm is specified and with
   537  // ErrZeroLength if the matrix has zero size.
   538  func (t *TriBandDense) Norm(norm float64) float64 {
   539  	if t.IsEmpty() {
   540  		panic(ErrZeroLength)
   541  	}
   542  	lnorm := normLapack(norm, false)
   543  	if lnorm == lapack.MaxColumnSum {
   544  		work := getFloat64s(t.mat.N, false)
   545  		defer putFloat64s(work)
   546  		return lapack64.Lantb(lnorm, t.mat, work)
   547  	}
   548  	return lapack64.Lantb(lnorm, t.mat, nil)
   549  }
   550  
   551  // Trace returns the trace of the matrix.
   552  //
   553  // Trace will panic with ErrZeroLength if the matrix has zero size.
   554  func (t *TriBandDense) Trace() float64 {
   555  	if t.IsEmpty() {
   556  		panic(ErrZeroLength)
   557  	}
   558  	rb := t.RawTriBand()
   559  	var tr float64
   560  	var offsetIndex int
   561  	if rb.Uplo == blas.Lower {
   562  		offsetIndex = rb.K
   563  	}
   564  	for i := 0; i < rb.N; i++ {
   565  		tr += rb.Data[offsetIndex+i*rb.Stride]
   566  	}
   567  	return tr
   568  }
   569  
   570  // SolveTo solves a triangular system T * X = B  or  Tᵀ * X = B where T is an
   571  // n×n triangular band matrix represented by the receiver and B is a given
   572  // n×nrhs matrix. If T is non-singular, the result will be stored into dst and
   573  // nil will be returned. If T is singular, the contents of dst will be undefined
   574  // and a Condition error will be returned.
   575  func (t *TriBandDense) SolveTo(dst *Dense, trans bool, b Matrix) error {
   576  	n, nrhs := b.Dims()
   577  	if n != t.mat.N {
   578  		panic(ErrShape)
   579  	}
   580  	if b, ok := b.(RawMatrixer); ok && dst != b {
   581  		dst.checkOverlap(b.RawMatrix())
   582  	}
   583  	dst.reuseAsNonZeroed(n, nrhs)
   584  	if dst != b {
   585  		dst.Copy(b)
   586  	}
   587  	var ok bool
   588  	if trans {
   589  		ok = lapack64.Tbtrs(blas.Trans, t.mat, dst.mat)
   590  	} else {
   591  		ok = lapack64.Tbtrs(blas.NoTrans, t.mat, dst.mat)
   592  	}
   593  	if !ok {
   594  		return Condition(math.Inf(1))
   595  	}
   596  	return nil
   597  }
   598  
   599  // SolveVecTo solves a triangular system T * x = b  or  Tᵀ * x = b where T is an
   600  // n×n triangular band matrix represented by the receiver and b is a given
   601  // n-vector. If T is non-singular, the result will be stored into dst and nil
   602  // will be returned. If T is singular, the contents of dst will be undefined and
   603  // a Condition error will be returned.
   604  func (t *TriBandDense) SolveVecTo(dst *VecDense, trans bool, b Vector) error {
   605  	n, nrhs := b.Dims()
   606  	if n != t.mat.N || nrhs != 1 {
   607  		panic(ErrShape)
   608  	}
   609  	if b, ok := b.(RawVectorer); ok && dst != b {
   610  		dst.checkOverlap(b.RawVector())
   611  	}
   612  	dst.reuseAsNonZeroed(n)
   613  	if dst != b {
   614  		dst.CopyVec(b)
   615  	}
   616  	var ok bool
   617  	if trans {
   618  		ok = lapack64.Tbtrs(blas.Trans, t.mat, dst.asGeneral())
   619  	} else {
   620  		ok = lapack64.Tbtrs(blas.NoTrans, t.mat, dst.asGeneral())
   621  	}
   622  	if !ok {
   623  		return Condition(math.Inf(1))
   624  	}
   625  	return nil
   626  }
   627  
   628  func copySymBandIntoTriBand(dst *TriBandDense, s SymBanded) {
   629  	n, k, upper := dst.TriBand()
   630  	ns, ks := s.SymBand()
   631  	if n != ns {
   632  		panic("mat: triangle size mismatch")
   633  	}
   634  	if k != ks {
   635  		panic("mat: triangle bandwidth mismatch")
   636  	}
   637  
   638  	// TODO(vladimir-ch): implement the missing cases below as needed.
   639  	t := dst.mat
   640  	sU, _ := untransposeExtract(s)
   641  	if sbd, ok := sU.(*SymBandDense); ok {
   642  		s := sbd.RawSymBand()
   643  		if upper {
   644  			if s.Uplo == blas.Upper {
   645  				// dst is upper triangular, s is stored in upper triangle.
   646  				for i := 0; i < n; i++ {
   647  					ilen := min(k+1, n-i)
   648  					copy(t.Data[i*t.Stride:i*t.Stride+ilen], s.Data[i*s.Stride:i*s.Stride+ilen])
   649  				}
   650  			} else {
   651  				// dst is upper triangular, s is stored in lower triangle.
   652  				//
   653  				// The following is a possible implementation for this case but
   654  				// is commented out due to lack of test coverage.
   655  				// for i := 0; i < n; i++ {
   656  				//  ilen := min(k+1, n-i)
   657  				//  for j := 0; j < ilen; j++ {
   658  				//      t.Data[i*t.Stride+j] = s.Data[(i+j)*s.Stride+k-j]
   659  				//  }
   660  				// }
   661  				panic("not implemented")
   662  			}
   663  		} else {
   664  			if s.Uplo == blas.Upper {
   665  				// dst is lower triangular, s is stored in upper triangle.
   666  				panic("not implemented")
   667  			} else {
   668  				// dst is lower triangular, s is stored in lower triangle.
   669  				panic("not implemented")
   670  			}
   671  		}
   672  		return
   673  	}
   674  	if upper {
   675  		for i := 0; i < n; i++ {
   676  			ilen := min(k+1, n-i)
   677  			for j := 0; j < ilen; j++ {
   678  				t.Data[i*t.Stride+j] = s.At(i, i+j)
   679  			}
   680  		}
   681  	} else {
   682  		panic("not implemented")
   683  	}
   684  }