gonum.org/v1/gonum@v0.14.0/mat/symband.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  	symBandDense *SymBandDense
    16  	_            Matrix           = symBandDense
    17  	_            allMatrix        = symBandDense
    18  	_            denseMatrix      = symBandDense
    19  	_            Symmetric        = symBandDense
    20  	_            Banded           = symBandDense
    21  	_            SymBanded        = symBandDense
    22  	_            RawSymBander     = symBandDense
    23  	_            MutableSymBanded = symBandDense
    24  
    25  	_ NonZeroDoer    = symBandDense
    26  	_ RowNonZeroDoer = symBandDense
    27  	_ ColNonZeroDoer = symBandDense
    28  )
    29  
    30  // SymBandDense represents a symmetric band matrix in dense storage format.
    31  type SymBandDense struct {
    32  	mat blas64.SymmetricBand
    33  }
    34  
    35  // SymBanded is a symmetric band matrix interface type.
    36  type SymBanded interface {
    37  	Banded
    38  
    39  	// SymmetricDim returns the number of rows/columns in the matrix.
    40  	SymmetricDim() int
    41  
    42  	// SymBand returns the number of rows/columns in the matrix, and the size of
    43  	// the bandwidth.
    44  	SymBand() (n, k int)
    45  }
    46  
    47  // MutableSymBanded is a symmetric band matrix interface type that allows elements
    48  // to be altered.
    49  type MutableSymBanded interface {
    50  	SymBanded
    51  	SetSymBand(i, j int, v float64)
    52  }
    53  
    54  // A RawSymBander can return a blas64.SymmetricBand representation of the receiver.
    55  // Changes to the blas64.SymmetricBand.Data slice will be reflected in the original
    56  // matrix, changes to the N, K, Stride and Uplo fields will not.
    57  type RawSymBander interface {
    58  	RawSymBand() blas64.SymmetricBand
    59  }
    60  
    61  // NewSymBandDense creates a new SymBand matrix with n rows and columns. If data == nil,
    62  // a new slice is allocated for the backing slice. If len(data) == n*(k+1),
    63  // data is used as the backing slice, and changes to the elements of the returned
    64  // SymBandDense will be reflected in data. If neither of these is true, NewSymBandDense
    65  // will panic. k must be at least zero and less than n, otherwise NewSymBandDense will panic.
    66  //
    67  // The data must be arranged in row-major order constructed by removing the zeros
    68  // from the rows outside the band and aligning the diagonals. SymBandDense matrices
    69  // are stored in the upper triangle. For example, the matrix
    70  //
    71  //	1  2  3  0  0  0
    72  //	2  4  5  6  0  0
    73  //	3  5  7  8  9  0
    74  //	0  6  8 10 11 12
    75  //	0  0  9 11 13 14
    76  //	0  0  0 12 14 15
    77  //
    78  // becomes (* entries are never accessed)
    79  //
    80  //	 1  2  3
    81  //	 4  5  6
    82  //	 7  8  9
    83  //	10 11 12
    84  //	13 14  *
    85  //	15  *  *
    86  //
    87  // which is passed to NewSymBandDense as []float64{1, 2, ..., 15, *, *, *} with k=2.
    88  // Only the values in the band portion of the matrix are used.
    89  func NewSymBandDense(n, k int, data []float64) *SymBandDense {
    90  	if n <= 0 || k < 0 {
    91  		if n == 0 {
    92  			panic(ErrZeroLength)
    93  		}
    94  		panic("mat: negative dimension")
    95  	}
    96  	if k+1 > n {
    97  		panic("mat: band out of range")
    98  	}
    99  	bc := k + 1
   100  	if data != nil && len(data) != n*bc {
   101  		panic(ErrShape)
   102  	}
   103  	if data == nil {
   104  		data = make([]float64, n*bc)
   105  	}
   106  	return &SymBandDense{
   107  		mat: blas64.SymmetricBand{
   108  			N:      n,
   109  			K:      k,
   110  			Stride: bc,
   111  			Uplo:   blas.Upper,
   112  			Data:   data,
   113  		},
   114  	}
   115  }
   116  
   117  // Dims returns the number of rows and columns in the matrix.
   118  func (s *SymBandDense) Dims() (r, c int) {
   119  	return s.mat.N, s.mat.N
   120  }
   121  
   122  // SymmetricDim returns the size of the receiver.
   123  func (s *SymBandDense) SymmetricDim() int {
   124  	return s.mat.N
   125  }
   126  
   127  // Bandwidth returns the bandwidths of the matrix.
   128  func (s *SymBandDense) Bandwidth() (kl, ku int) {
   129  	return s.mat.K, s.mat.K
   130  }
   131  
   132  // SymBand returns the number of rows/columns in the matrix, and the size of
   133  // the bandwidth.
   134  func (s *SymBandDense) SymBand() (n, k int) {
   135  	return s.mat.N, s.mat.K
   136  }
   137  
   138  // T implements the Matrix interface. Symmetric matrices, by definition, are
   139  // equal to their transpose, and this is a no-op.
   140  func (s *SymBandDense) T() Matrix {
   141  	return s
   142  }
   143  
   144  // TBand implements the Banded interface.
   145  func (s *SymBandDense) TBand() Banded {
   146  	return s
   147  }
   148  
   149  // RawSymBand returns the underlying blas64.SymBand used by the receiver.
   150  // Changes to elements in the receiver following the call will be reflected
   151  // in returned blas64.SymBand.
   152  func (s *SymBandDense) RawSymBand() blas64.SymmetricBand {
   153  	return s.mat
   154  }
   155  
   156  // SetRawSymBand sets the underlying blas64.SymmetricBand used by the receiver.
   157  // Changes to elements in the receiver following the call will be reflected
   158  // in the input.
   159  //
   160  // The supplied SymmetricBand must use blas.Upper storage format.
   161  func (s *SymBandDense) SetRawSymBand(mat blas64.SymmetricBand) {
   162  	if mat.Uplo != blas.Upper {
   163  		panic("mat: blas64.SymmetricBand does not have blas.Upper storage")
   164  	}
   165  	s.mat = mat
   166  }
   167  
   168  // IsEmpty returns whether the receiver is empty. Empty matrices can be the
   169  // receiver for size-restricted operations. The receiver can be emptied using
   170  // Reset.
   171  func (s *SymBandDense) IsEmpty() bool {
   172  	return s.mat.Stride == 0
   173  }
   174  
   175  // Reset empties the matrix so that it can be reused as the
   176  // receiver of a dimensionally restricted operation.
   177  //
   178  // Reset should not be used when the matrix shares backing data.
   179  // See the Reseter interface for more information.
   180  func (s *SymBandDense) Reset() {
   181  	s.mat.N = 0
   182  	s.mat.K = 0
   183  	s.mat.Stride = 0
   184  	s.mat.Uplo = 0
   185  	s.mat.Data = s.mat.Data[:0]
   186  }
   187  
   188  // Zero sets all of the matrix elements to zero.
   189  func (s *SymBandDense) Zero() {
   190  	for i := 0; i < s.mat.N; i++ {
   191  		u := min(1+s.mat.K, s.mat.N-i)
   192  		zero(s.mat.Data[i*s.mat.Stride : i*s.mat.Stride+u])
   193  	}
   194  }
   195  
   196  // DiagView returns the diagonal as a matrix backed by the original data.
   197  func (s *SymBandDense) DiagView() Diagonal {
   198  	n := s.mat.N
   199  	return &DiagDense{
   200  		mat: blas64.Vector{
   201  			N:    n,
   202  			Inc:  s.mat.Stride,
   203  			Data: s.mat.Data[:(n-1)*s.mat.Stride+1],
   204  		},
   205  	}
   206  }
   207  
   208  // DoNonZero calls the function fn for each of the non-zero elements of s. The function fn
   209  // takes a row/column index and the element value of s at (i, j).
   210  func (s *SymBandDense) DoNonZero(fn func(i, j int, v float64)) {
   211  	for i := 0; i < s.mat.N; i++ {
   212  		for j := max(0, i-s.mat.K); j < min(s.mat.N, i+s.mat.K+1); j++ {
   213  			v := s.at(i, j)
   214  			if v != 0 {
   215  				fn(i, j, v)
   216  			}
   217  		}
   218  	}
   219  }
   220  
   221  // DoRowNonZero calls the function fn for each of the non-zero elements of row i of s. The function fn
   222  // takes a row/column index and the element value of s at (i, j).
   223  func (s *SymBandDense) DoRowNonZero(i int, fn func(i, j int, v float64)) {
   224  	if i < 0 || s.mat.N <= i {
   225  		panic(ErrRowAccess)
   226  	}
   227  	for j := max(0, i-s.mat.K); j < min(s.mat.N, i+s.mat.K+1); j++ {
   228  		v := s.at(i, j)
   229  		if v != 0 {
   230  			fn(i, j, v)
   231  		}
   232  	}
   233  }
   234  
   235  // DoColNonZero calls the function fn for each of the non-zero elements of column j of s. The function fn
   236  // takes a row/column index and the element value of s at (i, j).
   237  func (s *SymBandDense) DoColNonZero(j int, fn func(i, j int, v float64)) {
   238  	if j < 0 || s.mat.N <= j {
   239  		panic(ErrColAccess)
   240  	}
   241  	for i := 0; i < s.mat.N; i++ {
   242  		if i-s.mat.K <= j && j < i+s.mat.K+1 {
   243  			v := s.at(i, j)
   244  			if v != 0 {
   245  				fn(i, j, v)
   246  			}
   247  		}
   248  	}
   249  }
   250  
   251  // Norm returns the specified norm of the receiver. Valid norms are:
   252  //
   253  //	1 - The maximum absolute column sum
   254  //	2 - The Frobenius norm, the square root of the sum of the squares of the elements
   255  //	Inf - The maximum absolute row sum
   256  //
   257  // Norm will panic with ErrNormOrder if an illegal norm is specified and with
   258  // ErrZeroLength if the matrix has zero size.
   259  func (s *SymBandDense) Norm(norm float64) float64 {
   260  	if s.IsEmpty() {
   261  		panic(ErrZeroLength)
   262  	}
   263  	lnorm := normLapack(norm, false)
   264  	if lnorm == lapack.MaxColumnSum || lnorm == lapack.MaxRowSum {
   265  		work := getFloat64s(s.mat.N, false)
   266  		defer putFloat64s(work)
   267  		return lapack64.Lansb(lnorm, s.mat, work)
   268  	}
   269  	return lapack64.Lansb(lnorm, s.mat, nil)
   270  }
   271  
   272  // Trace returns the trace of the matrix.
   273  //
   274  // Trace will panic with ErrZeroLength if the matrix has zero size.
   275  func (s *SymBandDense) Trace() float64 {
   276  	if s.IsEmpty() {
   277  		panic(ErrZeroLength)
   278  	}
   279  	rb := s.RawSymBand()
   280  	var tr float64
   281  	for i := 0; i < rb.N; i++ {
   282  		tr += rb.Data[i*rb.Stride]
   283  	}
   284  	return tr
   285  }
   286  
   287  // MulVecTo computes S⋅x storing the result into dst.
   288  func (s *SymBandDense) MulVecTo(dst *VecDense, _ bool, x Vector) {
   289  	n := s.mat.N
   290  	if x.Len() != n {
   291  		panic(ErrShape)
   292  	}
   293  	dst.reuseAsNonZeroed(n)
   294  
   295  	xMat, _ := untransposeExtract(x)
   296  	if xVec, ok := xMat.(*VecDense); ok {
   297  		if dst != xVec {
   298  			dst.checkOverlap(xVec.mat)
   299  			blas64.Sbmv(1, s.mat, xVec.mat, 0, dst.mat)
   300  		} else {
   301  			xCopy := getVecDenseWorkspace(n, false)
   302  			xCopy.CloneFromVec(xVec)
   303  			blas64.Sbmv(1, s.mat, xCopy.mat, 0, dst.mat)
   304  			putVecDenseWorkspace(xCopy)
   305  		}
   306  	} else {
   307  		xCopy := getVecDenseWorkspace(n, false)
   308  		xCopy.CloneFromVec(x)
   309  		blas64.Sbmv(1, s.mat, xCopy.mat, 0, dst.mat)
   310  		putVecDenseWorkspace(xCopy)
   311  	}
   312  }