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