github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/mat/band.go (about)

     1  // Copyright ©2017 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  	"github.com/jingcheng-WU/gonum/blas"
     9  	"github.com/jingcheng-WU/gonum/blas/blas64"
    10  )
    11  
    12  var (
    13  	bandDense *BandDense
    14  	_         Matrix      = bandDense
    15  	_         allMatrix   = bandDense
    16  	_         denseMatrix = bandDense
    17  	_         Banded      = bandDense
    18  	_         RawBander   = bandDense
    19  
    20  	_ NonZeroDoer    = bandDense
    21  	_ RowNonZeroDoer = bandDense
    22  	_ ColNonZeroDoer = bandDense
    23  )
    24  
    25  // BandDense represents a band matrix in dense storage format.
    26  type BandDense struct {
    27  	mat blas64.Band
    28  }
    29  
    30  // Banded is a band matrix representation.
    31  type Banded interface {
    32  	Matrix
    33  	// Bandwidth returns the lower and upper bandwidth values for
    34  	// the matrix. The total bandwidth of the matrix is kl+ku+1.
    35  	Bandwidth() (kl, ku int)
    36  
    37  	// TBand is the equivalent of the T() method in the Matrix
    38  	// interface but guarantees the transpose is of banded type.
    39  	TBand() Banded
    40  }
    41  
    42  // A RawBander can return a blas64.Band representation of the receiver.
    43  // Changes to the blas64.Band.Data slice will be reflected in the original
    44  // matrix, changes to the Rows, Cols, KL, KU and Stride fields will not.
    45  type RawBander interface {
    46  	RawBand() blas64.Band
    47  }
    48  
    49  // A MutableBanded can set elements of a band matrix.
    50  type MutableBanded interface {
    51  	Banded
    52  	SetBand(i, j int, v float64)
    53  }
    54  
    55  var (
    56  	_ Matrix            = TransposeBand{}
    57  	_ Banded            = TransposeBand{}
    58  	_ UntransposeBander = TransposeBand{}
    59  )
    60  
    61  // TransposeBand is a type for performing an implicit transpose of a band
    62  // matrix. It implements the Banded interface, returning values from the
    63  // transpose of the matrix within.
    64  type TransposeBand struct {
    65  	Banded Banded
    66  }
    67  
    68  // At returns the value of the element at row i and column j of the transposed
    69  // matrix, that is, row j and column i of the Banded field.
    70  func (t TransposeBand) At(i, j int) float64 {
    71  	return t.Banded.At(j, i)
    72  }
    73  
    74  // Dims returns the dimensions of the transposed matrix.
    75  func (t TransposeBand) Dims() (r, c int) {
    76  	c, r = t.Banded.Dims()
    77  	return r, c
    78  }
    79  
    80  // T performs an implicit transpose by returning the Banded field.
    81  func (t TransposeBand) T() Matrix {
    82  	return t.Banded
    83  }
    84  
    85  // Bandwidth returns the lower and upper bandwidth values for
    86  // the transposed matrix.
    87  func (t TransposeBand) Bandwidth() (kl, ku int) {
    88  	kl, ku = t.Banded.Bandwidth()
    89  	return ku, kl
    90  }
    91  
    92  // TBand performs an implicit transpose by returning the Banded field.
    93  func (t TransposeBand) TBand() Banded {
    94  	return t.Banded
    95  }
    96  
    97  // Untranspose returns the Banded field.
    98  func (t TransposeBand) Untranspose() Matrix {
    99  	return t.Banded
   100  }
   101  
   102  // UntransposeBand returns the Banded field.
   103  func (t TransposeBand) UntransposeBand() Banded {
   104  	return t.Banded
   105  }
   106  
   107  // NewBandDense creates a new Band matrix with r rows and c columns. If data == nil,
   108  // a new slice is allocated for the backing slice. If len(data) == min(r, c+kl)*(kl+ku+1),
   109  // data is used as the backing slice, and changes to the elements of the returned
   110  // BandDense will be reflected in data. If neither of these is true, NewBandDense
   111  // will panic. kl must be at least zero and less r, and ku must be at least zero and
   112  // less than c, otherwise NewBandDense will panic.
   113  // NewBandDense will panic if either r or c is zero.
   114  //
   115  // The data must be arranged in row-major order constructed by removing the zeros
   116  // from the rows outside the band and aligning the diagonals. For example, the matrix
   117  //    1  2  3  0  0  0
   118  //    4  5  6  7  0  0
   119  //    0  8  9 10 11  0
   120  //    0  0 12 13 14 15
   121  //    0  0  0 16 17 18
   122  //    0  0  0  0 19 20
   123  // becomes (* entries are never accessed)
   124  //     *  1  2  3
   125  //     4  5  6  7
   126  //     8  9 10 11
   127  //    12 13 14 15
   128  //    16 17 18  *
   129  //    19 20  *  *
   130  // which is passed to NewBandDense as []float64{*, 1, 2, 3, 4, ...} with kl=1 and ku=2.
   131  // Only the values in the band portion of the matrix are used.
   132  func NewBandDense(r, c, kl, ku int, data []float64) *BandDense {
   133  	if r <= 0 || c <= 0 || kl < 0 || ku < 0 {
   134  		if r == 0 || c == 0 {
   135  			panic(ErrZeroLength)
   136  		}
   137  		panic("mat: negative dimension")
   138  	}
   139  	if kl+1 > r || ku+1 > c {
   140  		panic("mat: band out of range")
   141  	}
   142  	bc := kl + ku + 1
   143  	if data != nil && len(data) != min(r, c+kl)*bc {
   144  		panic(ErrShape)
   145  	}
   146  	if data == nil {
   147  		data = make([]float64, min(r, c+kl)*bc)
   148  	}
   149  	return &BandDense{
   150  		mat: blas64.Band{
   151  			Rows:   r,
   152  			Cols:   c,
   153  			KL:     kl,
   154  			KU:     ku,
   155  			Stride: bc,
   156  			Data:   data,
   157  		},
   158  	}
   159  }
   160  
   161  // NewDiagonalRect is a convenience function that returns a diagonal matrix represented by a
   162  // BandDense. The length of data must be min(r, c) otherwise NewDiagonalRect will panic.
   163  func NewDiagonalRect(r, c int, data []float64) *BandDense {
   164  	return NewBandDense(r, c, 0, 0, data)
   165  }
   166  
   167  // Dims returns the number of rows and columns in the matrix.
   168  func (b *BandDense) Dims() (r, c int) {
   169  	return b.mat.Rows, b.mat.Cols
   170  }
   171  
   172  // Bandwidth returns the upper and lower bandwidths of the matrix.
   173  func (b *BandDense) Bandwidth() (kl, ku int) {
   174  	return b.mat.KL, b.mat.KU
   175  }
   176  
   177  // T performs an implicit transpose by returning the receiver inside a Transpose.
   178  func (b *BandDense) T() Matrix {
   179  	return Transpose{b}
   180  }
   181  
   182  // TBand performs an implicit transpose by returning the receiver inside a TransposeBand.
   183  func (b *BandDense) TBand() Banded {
   184  	return TransposeBand{b}
   185  }
   186  
   187  // RawBand returns the underlying blas64.Band used by the receiver.
   188  // Changes to elements in the receiver following the call will be reflected
   189  // in returned blas64.Band.
   190  func (b *BandDense) RawBand() blas64.Band {
   191  	return b.mat
   192  }
   193  
   194  // SetRawBand sets the underlying blas64.Band used by the receiver.
   195  // Changes to elements in the receiver following the call will be reflected
   196  // in the input.
   197  func (b *BandDense) SetRawBand(mat blas64.Band) {
   198  	b.mat = mat
   199  }
   200  
   201  // IsEmpty returns whether the receiver is empty. Empty matrices can be the
   202  // receiver for size-restricted operations. The receiver can be zeroed using Reset.
   203  func (b *BandDense) IsEmpty() bool {
   204  	return b.mat.Stride == 0
   205  }
   206  
   207  // Reset empties the matrix so that it can be reused as the
   208  // receiver of a dimensionally restricted operation.
   209  //
   210  // Reset should not be used when the matrix shares backing data.
   211  // See the Reseter interface for more information.
   212  func (b *BandDense) Reset() {
   213  	b.mat.Rows = 0
   214  	b.mat.Cols = 0
   215  	b.mat.KL = 0
   216  	b.mat.KU = 0
   217  	b.mat.Stride = 0
   218  	b.mat.Data = b.mat.Data[:0:0]
   219  }
   220  
   221  // DiagView returns the diagonal as a matrix backed by the original data.
   222  func (b *BandDense) DiagView() Diagonal {
   223  	n := min(b.mat.Rows, b.mat.Cols)
   224  	return &DiagDense{
   225  		mat: blas64.Vector{
   226  			N:    n,
   227  			Inc:  b.mat.Stride,
   228  			Data: b.mat.Data[b.mat.KL : (n-1)*b.mat.Stride+b.mat.KL+1],
   229  		},
   230  	}
   231  }
   232  
   233  // DoNonZero calls the function fn for each of the non-zero elements of b. The function fn
   234  // takes a row/column index and the element value of b at (i, j).
   235  func (b *BandDense) DoNonZero(fn func(i, j int, v float64)) {
   236  	for i := 0; i < min(b.mat.Rows, b.mat.Cols+b.mat.KL); i++ {
   237  		for j := max(0, i-b.mat.KL); j < min(b.mat.Cols, i+b.mat.KU+1); j++ {
   238  			v := b.at(i, j)
   239  			if v != 0 {
   240  				fn(i, j, v)
   241  			}
   242  		}
   243  	}
   244  }
   245  
   246  // DoRowNonZero calls the function fn for each of the non-zero elements of row i of b. The function fn
   247  // takes a row/column index and the element value of b at (i, j).
   248  func (b *BandDense) DoRowNonZero(i int, fn func(i, j int, v float64)) {
   249  	if i < 0 || b.mat.Rows <= i {
   250  		panic(ErrRowAccess)
   251  	}
   252  	for j := max(0, i-b.mat.KL); j < min(b.mat.Cols, i+b.mat.KU+1); j++ {
   253  		v := b.at(i, j)
   254  		if v != 0 {
   255  			fn(i, j, v)
   256  		}
   257  	}
   258  }
   259  
   260  // DoColNonZero calls the function fn for each of the non-zero elements of column j of b. The function fn
   261  // takes a row/column index and the element value of b at (i, j).
   262  func (b *BandDense) DoColNonZero(j int, fn func(i, j int, v float64)) {
   263  	if j < 0 || b.mat.Cols <= j {
   264  		panic(ErrColAccess)
   265  	}
   266  	for i := 0; i < min(b.mat.Rows, b.mat.Cols+b.mat.KL); i++ {
   267  		if i-b.mat.KL <= j && j < i+b.mat.KU+1 {
   268  			v := b.at(i, j)
   269  			if v != 0 {
   270  				fn(i, j, v)
   271  			}
   272  		}
   273  	}
   274  }
   275  
   276  // Zero sets all of the matrix elements to zero.
   277  func (b *BandDense) Zero() {
   278  	m := b.mat.Rows
   279  	kL := b.mat.KL
   280  	nCol := b.mat.KU + 1 + kL
   281  	for i := 0; i < m; i++ {
   282  		l := max(0, kL-i)
   283  		u := min(nCol, m+kL-i)
   284  		zero(b.mat.Data[i*b.mat.Stride+l : i*b.mat.Stride+u])
   285  	}
   286  }
   287  
   288  // Trace computes the trace of the matrix.
   289  func (b *BandDense) Trace() float64 {
   290  	r, c := b.Dims()
   291  	if r != c {
   292  		panic(ErrShape)
   293  	}
   294  	rb := b.RawBand()
   295  	var tr float64
   296  	for i := 0; i < r; i++ {
   297  		tr += rb.Data[rb.KL+i*rb.Stride]
   298  	}
   299  	return tr
   300  }
   301  
   302  // MulVecTo computes B⋅x or Bᵀ⋅x storing the result into dst.
   303  func (b *BandDense) MulVecTo(dst *VecDense, trans bool, x Vector) {
   304  	m, n := b.Dims()
   305  	if trans {
   306  		m, n = n, m
   307  	}
   308  	if x.Len() != n {
   309  		panic(ErrShape)
   310  	}
   311  	dst.reuseAsNonZeroed(m)
   312  
   313  	t := blas.NoTrans
   314  	if trans {
   315  		t = blas.Trans
   316  	}
   317  
   318  	xMat, _ := untransposeExtract(x)
   319  	if xVec, ok := xMat.(*VecDense); ok {
   320  		if dst != xVec {
   321  			dst.checkOverlap(xVec.mat)
   322  			blas64.Gbmv(t, 1, b.mat, xVec.mat, 0, dst.mat)
   323  		} else {
   324  			xCopy := getWorkspaceVec(n, false)
   325  			xCopy.CloneFromVec(xVec)
   326  			blas64.Gbmv(t, 1, b.mat, xCopy.mat, 0, dst.mat)
   327  			putWorkspaceVec(xCopy)
   328  		}
   329  	} else {
   330  		xCopy := getWorkspaceVec(n, false)
   331  		xCopy.CloneFromVec(x)
   332  		blas64.Gbmv(t, 1, b.mat, xCopy.mat, 0, dst.mat)
   333  		putWorkspaceVec(xCopy)
   334  	}
   335  }