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