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