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