github.com/gopherd/gonum@v0.0.4/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/gopherd/gonum/blas"
    11  	"github.com/gopherd/gonum/blas/blas64"
    12  	"github.com/gopherd/gonum/lapack"
    13  	"github.com/gopherd/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  //    1  2  3  0  0  0
   172  //    0  4  5  6  0  0
   173  //    0  0  7  8  9  0
   174  //    0  0  0 10 11 12
   175  //    0  0  0 0  13 14
   176  //    0  0  0 0  0  15
   177  // becomes (* entries are never accessed)
   178  //     1  2  3
   179  //     4  5  6
   180  //     7  8  9
   181  //    10 11 12
   182  //    13 14  *
   183  //    15  *  *
   184  // which is passed to NewTriBandDense as []float64{1, 2, ..., 15, *, *, *}
   185  // with k=2 and kind = mat.Upper.
   186  // The lower triangular banded matrix
   187  //    1  0  0  0  0  0
   188  //    2  3  0  0  0  0
   189  //    4  5  6  0  0  0
   190  //    0  7  8  9  0  0
   191  //    0  0 10 11 12  0
   192  //    0  0  0 13 14 15
   193  // becomes (* entries are never accessed)
   194  //     *  *  1
   195  //     *  2  3
   196  //     4  5  6
   197  //     7  8  9
   198  //    10 11 12
   199  //    13 14 15
   200  // which is passed to NewTriBandDense as []float64{*, *, *, 1, 2, ..., 15}
   201  // with k=2 and kind = mat.Lower.
   202  // Only the values in the band portion of the matrix are used.
   203  func NewTriBandDense(n, k int, kind TriKind, data []float64) *TriBandDense {
   204  	if n <= 0 || k < 0 {
   205  		if n == 0 {
   206  			panic(ErrZeroLength)
   207  		}
   208  		panic(ErrNegativeDimension)
   209  	}
   210  	if k+1 > n {
   211  		panic(ErrBandwidth)
   212  	}
   213  	bc := k + 1
   214  	if data != nil && len(data) != n*bc {
   215  		panic(ErrShape)
   216  	}
   217  	if data == nil {
   218  		data = make([]float64, n*bc)
   219  	}
   220  	uplo := blas.Lower
   221  	if kind {
   222  		uplo = blas.Upper
   223  	}
   224  	return &TriBandDense{
   225  		mat: blas64.TriangularBand{
   226  			Uplo:   uplo,
   227  			Diag:   blas.NonUnit,
   228  			N:      n,
   229  			K:      k,
   230  			Data:   data,
   231  			Stride: bc,
   232  		},
   233  	}
   234  }
   235  
   236  // Dims returns the number of rows and columns in the matrix.
   237  func (t *TriBandDense) Dims() (r, c int) {
   238  	return t.mat.N, t.mat.N
   239  }
   240  
   241  // T performs an implicit transpose by returning the receiver inside a Transpose.
   242  func (t *TriBandDense) T() Matrix {
   243  	return Transpose{t}
   244  }
   245  
   246  // IsEmpty returns whether the receiver is empty. Empty matrices can be the
   247  // receiver for size-restricted operations. The receiver can be emptied using
   248  // Reset.
   249  func (t *TriBandDense) IsEmpty() bool {
   250  	// It must be the case that t.Dims() returns
   251  	// zeros in this case. See comment in Reset().
   252  	return t.mat.Stride == 0
   253  }
   254  
   255  // Reset empties the matrix so that it can be reused as the
   256  // receiver of a dimensionally restricted operation.
   257  //
   258  // Reset should not be used when the matrix shares backing data.
   259  // See the Reseter interface for more information.
   260  func (t *TriBandDense) Reset() {
   261  	t.mat.N = 0
   262  	t.mat.Stride = 0
   263  	t.mat.K = 0
   264  	t.mat.Data = t.mat.Data[:0]
   265  }
   266  
   267  // ReuseAsTriBand changes the receiver to be of size n×n, bandwidth k+1 and of
   268  // the given kind, re-using the backing data slice if it has sufficient capacity
   269  // and allocating a new slice otherwise. The backing data is zero on return.
   270  //
   271  // The receiver must be empty, n must be positive and k must be non-negative and
   272  // less than n, otherwise ReuseAsTriBand will panic. To empty the receiver for
   273  // re-use, Reset should be used.
   274  func (t *TriBandDense) ReuseAsTriBand(n, k int, kind TriKind) {
   275  	if n <= 0 || k < 0 {
   276  		if n == 0 {
   277  			panic(ErrZeroLength)
   278  		}
   279  		panic(ErrNegativeDimension)
   280  	}
   281  	if k+1 > n {
   282  		panic(ErrBandwidth)
   283  	}
   284  	if !t.IsEmpty() {
   285  		panic(ErrReuseNonEmpty)
   286  	}
   287  	t.reuseAsZeroed(n, k, kind)
   288  }
   289  
   290  // reuseAsZeroed resizes an empty receiver to an n×n triangular band matrix with
   291  // the given bandwidth and orientation. If the receiver is not empty,
   292  // reuseAsZeroed checks that the receiver has the correct size, bandwidth and
   293  // orientation. It then zeros out the matrix data.
   294  func (t *TriBandDense) reuseAsZeroed(n, k int, kind TriKind) {
   295  	// reuseAsZeroed must be kept in sync with reuseAsNonZeroed.
   296  	if n == 0 {
   297  		panic(ErrZeroLength)
   298  	}
   299  	ul := blas.Lower
   300  	if kind == Upper {
   301  		ul = blas.Upper
   302  	}
   303  	if t.IsEmpty() {
   304  		t.mat = blas64.TriangularBand{
   305  			Uplo:   ul,
   306  			Diag:   blas.NonUnit,
   307  			N:      n,
   308  			K:      k,
   309  			Data:   useZeroed(t.mat.Data, n*(k+1)),
   310  			Stride: k + 1,
   311  		}
   312  		return
   313  	}
   314  	if t.mat.N != n || t.mat.K != k {
   315  		panic(ErrShape)
   316  	}
   317  	if t.mat.Uplo != ul {
   318  		panic(ErrTriangle)
   319  	}
   320  	t.Zero()
   321  }
   322  
   323  // reuseAsNonZeroed resizes an empty receiver to an n×n triangular band matrix
   324  // with the given bandwidth and orientation. If the receiver is not empty,
   325  // reuseAsZeroed checks that the receiver has the correct size, bandwidth and
   326  // orientation.
   327  func (t *TriBandDense) reuseAsNonZeroed(n, k int, kind TriKind) {
   328  	// reuseAsNonZeroed must be kept in sync with reuseAsZeroed.
   329  	if n == 0 {
   330  		panic(ErrZeroLength)
   331  	}
   332  	ul := blas.Lower
   333  	if kind == Upper {
   334  		ul = blas.Upper
   335  	}
   336  	if t.IsEmpty() {
   337  		t.mat = blas64.TriangularBand{
   338  			Uplo:   ul,
   339  			Diag:   blas.NonUnit,
   340  			N:      n,
   341  			K:      k,
   342  			Data:   use(t.mat.Data, n*(k+1)),
   343  			Stride: k + 1,
   344  		}
   345  		return
   346  	}
   347  	if t.mat.N != n || t.mat.K != k {
   348  		panic(ErrShape)
   349  	}
   350  	if t.mat.Uplo != ul {
   351  		panic(ErrTriangle)
   352  	}
   353  }
   354  
   355  // Zero sets all of the matrix elements to zero.
   356  func (t *TriBandDense) Zero() {
   357  	if t.isUpper() {
   358  		for i := 0; i < t.mat.N; i++ {
   359  			u := min(1+t.mat.K, t.mat.N-i)
   360  			zero(t.mat.Data[i*t.mat.Stride : i*t.mat.Stride+u])
   361  		}
   362  		return
   363  	}
   364  	for i := 0; i < t.mat.N; i++ {
   365  		l := max(0, t.mat.K-i)
   366  		zero(t.mat.Data[i*t.mat.Stride+l : i*t.mat.Stride+t.mat.K+1])
   367  	}
   368  }
   369  
   370  func (t *TriBandDense) isUpper() bool {
   371  	return isUpperUplo(t.mat.Uplo)
   372  }
   373  
   374  func (t *TriBandDense) triKind() TriKind {
   375  	return TriKind(isUpperUplo(t.mat.Uplo))
   376  }
   377  
   378  // Triangle returns the dimension of t and its orientation. The returned
   379  // orientation is only valid when n is not zero.
   380  func (t *TriBandDense) Triangle() (n int, kind TriKind) {
   381  	return t.mat.N, t.triKind()
   382  }
   383  
   384  // TTri performs an implicit transpose by returning the receiver inside a TransposeTri.
   385  func (t *TriBandDense) TTri() Triangular {
   386  	return TransposeTri{t}
   387  }
   388  
   389  // Bandwidth returns the upper and lower bandwidths of the matrix.
   390  func (t *TriBandDense) Bandwidth() (kl, ku int) {
   391  	if t.isUpper() {
   392  		return 0, t.mat.K
   393  	}
   394  	return t.mat.K, 0
   395  }
   396  
   397  // TBand performs an implicit transpose by returning the receiver inside a TransposeBand.
   398  func (t *TriBandDense) TBand() Banded {
   399  	return TransposeBand{t}
   400  }
   401  
   402  // TriBand returns the number of rows/columns in the matrix, the
   403  // size of the bandwidth, and the orientation.
   404  func (t *TriBandDense) TriBand() (n, k int, kind TriKind) {
   405  	return t.mat.N, t.mat.K, TriKind(!t.IsEmpty()) && t.triKind()
   406  }
   407  
   408  // TTriBand performs an implicit transpose by returning the receiver inside a TransposeTriBand.
   409  func (t *TriBandDense) TTriBand() TriBanded {
   410  	return TransposeTriBand{t}
   411  }
   412  
   413  // RawTriBand returns the underlying blas64.TriangularBand used by the receiver.
   414  // Changes to the blas64.TriangularBand.Data slice will be reflected in the original
   415  // matrix, changes to the N, K, Stride, Uplo and Diag fields will not.
   416  func (t *TriBandDense) RawTriBand() blas64.TriangularBand {
   417  	return t.mat
   418  }
   419  
   420  // SetRawTriBand sets the underlying blas64.TriangularBand used by the receiver.
   421  // Changes to elements in the receiver following the call will be reflected
   422  // in the input.
   423  //
   424  // The supplied TriangularBand must not use blas.Unit storage format.
   425  func (t *TriBandDense) SetRawTriBand(mat blas64.TriangularBand) {
   426  	if mat.Diag == blas.Unit {
   427  		panic("mat: cannot set TriBand with Unit storage")
   428  	}
   429  	t.mat = mat
   430  }
   431  
   432  // DiagView returns the diagonal as a matrix backed by the original data.
   433  func (t *TriBandDense) DiagView() Diagonal {
   434  	if t.mat.Diag == blas.Unit {
   435  		panic("mat: cannot take view of Unit diagonal")
   436  	}
   437  	n := t.mat.N
   438  	data := t.mat.Data
   439  	if !t.isUpper() {
   440  		data = data[t.mat.K:]
   441  	}
   442  	return &DiagDense{
   443  		mat: blas64.Vector{
   444  			N:    n,
   445  			Inc:  t.mat.Stride,
   446  			Data: data[:(n-1)*t.mat.Stride+1],
   447  		},
   448  	}
   449  }
   450  
   451  // Norm returns the specified norm of the receiver. Valid norms are:
   452  //  1 - The maximum absolute column sum
   453  //  2 - The Frobenius norm, the square root of the sum of the squares of the elements
   454  //  Inf - The maximum absolute row sum
   455  //
   456  // Norm will panic with ErrNormOrder if an illegal norm is specified and with
   457  // ErrZeroLength if the matrix has zero size.
   458  func (t *TriBandDense) Norm(norm float64) float64 {
   459  	if t.IsEmpty() {
   460  		panic(ErrZeroLength)
   461  	}
   462  	lnorm := normLapack(norm, false)
   463  	if lnorm == lapack.MaxColumnSum {
   464  		work := getFloat64s(t.mat.N, false)
   465  		defer putFloat64s(work)
   466  		return lapack64.Lantb(lnorm, t.mat, work)
   467  	}
   468  	return lapack64.Lantb(lnorm, t.mat, nil)
   469  }
   470  
   471  // Trace returns the trace of the matrix.
   472  //
   473  // Trace will panic with ErrZeroLength if the matrix has zero size.
   474  func (t *TriBandDense) Trace() float64 {
   475  	if t.IsEmpty() {
   476  		panic(ErrZeroLength)
   477  	}
   478  	rb := t.RawTriBand()
   479  	var tr float64
   480  	var offsetIndex int
   481  	if rb.Uplo == blas.Lower {
   482  		offsetIndex = rb.K
   483  	}
   484  	for i := 0; i < rb.N; i++ {
   485  		tr += rb.Data[offsetIndex+i*rb.Stride]
   486  	}
   487  	return tr
   488  }
   489  
   490  // SolveTo solves a triangular system T * X = B  or  Tᵀ * X = B where T is an
   491  // n×n triangular band matrix represented by the receiver and B is a given
   492  // n×nrhs matrix. If T is non-singular, the result will be stored into dst and
   493  // nil will be returned. If T is singular, the contents of dst will be undefined
   494  // and a Condition error will be returned.
   495  func (t *TriBandDense) SolveTo(dst *Dense, trans bool, b Matrix) error {
   496  	n, nrhs := b.Dims()
   497  	if n != t.mat.N {
   498  		panic(ErrShape)
   499  	}
   500  	if b, ok := b.(RawMatrixer); ok && dst != b {
   501  		dst.checkOverlap(b.RawMatrix())
   502  	}
   503  	dst.reuseAsNonZeroed(n, nrhs)
   504  	if dst != b {
   505  		dst.Copy(b)
   506  	}
   507  	var ok bool
   508  	if trans {
   509  		ok = lapack64.Tbtrs(blas.Trans, t.mat, dst.mat)
   510  	} else {
   511  		ok = lapack64.Tbtrs(blas.NoTrans, t.mat, dst.mat)
   512  	}
   513  	if !ok {
   514  		return Condition(math.Inf(1))
   515  	}
   516  	return nil
   517  }
   518  
   519  // SolveVecTo solves a triangular system T * x = b  or  Tᵀ * x = b where T is an
   520  // n×n triangular band matrix represented by the receiver and b is a given
   521  // n-vector. If T is non-singular, the result will be stored into dst and nil
   522  // will be returned. If T is singular, the contents of dst will be undefined and
   523  // a Condition error will be returned.
   524  func (t *TriBandDense) SolveVecTo(dst *VecDense, trans bool, b Vector) error {
   525  	n, nrhs := b.Dims()
   526  	if n != t.mat.N || nrhs != 1 {
   527  		panic(ErrShape)
   528  	}
   529  	if b, ok := b.(RawVectorer); ok && dst != b {
   530  		dst.checkOverlap(b.RawVector())
   531  	}
   532  	dst.reuseAsNonZeroed(n)
   533  	if dst != b {
   534  		dst.CopyVec(b)
   535  	}
   536  	var ok bool
   537  	if trans {
   538  		ok = lapack64.Tbtrs(blas.Trans, t.mat, dst.asGeneral())
   539  	} else {
   540  		ok = lapack64.Tbtrs(blas.NoTrans, t.mat, dst.asGeneral())
   541  	}
   542  	if !ok {
   543  		return Condition(math.Inf(1))
   544  	}
   545  	return nil
   546  }
   547  
   548  func copySymBandIntoTriBand(dst *TriBandDense, s SymBanded) {
   549  	n, k, upper := dst.TriBand()
   550  	ns, ks := s.SymBand()
   551  	if n != ns {
   552  		panic("mat: triangle size mismatch")
   553  	}
   554  	if k != ks {
   555  		panic("mat: triangle bandwidth mismatch")
   556  	}
   557  
   558  	// TODO(vladimir-ch): implement the missing cases below as needed.
   559  	t := dst.mat
   560  	sU, _ := untransposeExtract(s)
   561  	if sbd, ok := sU.(*SymBandDense); ok {
   562  		s := sbd.RawSymBand()
   563  		if upper {
   564  			if s.Uplo == blas.Upper {
   565  				// dst is upper triangular, s is stored in upper triangle.
   566  				for i := 0; i < n; i++ {
   567  					ilen := min(k+1, n-i)
   568  					copy(t.Data[i*t.Stride:i*t.Stride+ilen], s.Data[i*s.Stride:i*s.Stride+ilen])
   569  				}
   570  			} else {
   571  				// dst is upper triangular, s is stored in lower triangle.
   572  				//
   573  				// The following is a possible implementation for this case but
   574  				// is commented out due to lack of test coverage.
   575  				// for i := 0; i < n; i++ {
   576  				//  ilen := min(k+1, n-i)
   577  				//  for j := 0; j < ilen; j++ {
   578  				//      t.Data[i*t.Stride+j] = s.Data[(i+j)*s.Stride+k-j]
   579  				//  }
   580  				// }
   581  				panic("not implemented")
   582  			}
   583  		} else {
   584  			if s.Uplo == blas.Upper {
   585  				// dst is lower triangular, s is stored in upper triangle.
   586  				panic("not implemented")
   587  			} else {
   588  				// dst is lower triangular, s is stored in lower triangle.
   589  				panic("not implemented")
   590  			}
   591  		}
   592  		return
   593  	}
   594  	if upper {
   595  		for i := 0; i < n; i++ {
   596  			ilen := min(k+1, n-i)
   597  			for j := 0; j < ilen; j++ {
   598  				t.Data[i*t.Stride+j] = s.At(i, i+j)
   599  			}
   600  		}
   601  	} else {
   602  		panic("not implemented")
   603  	}
   604  }