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

     1  // Copyright ©2018 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  	diagDense *DiagDense
    14  	_         Matrix          = diagDense
    15  	_         allMatrix       = diagDense
    16  	_         denseMatrix     = diagDense
    17  	_         Diagonal        = diagDense
    18  	_         MutableDiagonal = diagDense
    19  	_         Triangular      = diagDense
    20  	_         TriBanded       = diagDense
    21  	_         Symmetric       = diagDense
    22  	_         SymBanded       = diagDense
    23  	_         Banded          = diagDense
    24  	_         RawBander       = diagDense
    25  	_         RawSymBander    = diagDense
    26  
    27  	diag Diagonal
    28  	_    Matrix     = diag
    29  	_    Diagonal   = diag
    30  	_    Triangular = diag
    31  	_    TriBanded  = diag
    32  	_    Symmetric  = diag
    33  	_    SymBanded  = diag
    34  	_    Banded     = diag
    35  )
    36  
    37  // Diagonal represents a diagonal matrix, that is a square matrix that only
    38  // has non-zero terms on the diagonal.
    39  type Diagonal interface {
    40  	Matrix
    41  	// Diag returns the number of rows/columns in the matrix.
    42  	Diag() int
    43  
    44  	// Bandwidth and TBand are included in the Diagonal interface
    45  	// to allow the use of Diagonal types in banded functions.
    46  	// Bandwidth will always return (0, 0).
    47  	Bandwidth() (kl, ku int)
    48  	TBand() Banded
    49  
    50  	// Triangle and TTri are included in the Diagonal interface
    51  	// to allow the use of Diagonal types in triangular functions.
    52  	Triangle() (int, TriKind)
    53  	TTri() Triangular
    54  
    55  	// Symmetric and SymBand are included in the Diagonal interface
    56  	// to allow the use of Diagonal types in symmetric and banded symmetric
    57  	// functions respectively.
    58  	Symmetric() int
    59  	SymBand() (n, k int)
    60  
    61  	// TriBand and TTriBand are included in the Diagonal interface
    62  	// to allow the use of Diagonal types in triangular banded functions.
    63  	TriBand() (n, k int, kind TriKind)
    64  	TTriBand() TriBanded
    65  }
    66  
    67  // MutableDiagonal is a Diagonal matrix whose elements can be set.
    68  type MutableDiagonal interface {
    69  	Diagonal
    70  	SetDiag(i int, v float64)
    71  }
    72  
    73  // DiagDense represents a diagonal matrix in dense storage format.
    74  type DiagDense struct {
    75  	mat blas64.Vector
    76  }
    77  
    78  // NewDiagDense creates a new Diagonal matrix with n rows and n columns.
    79  // The length of data must be n or data must be nil, otherwise NewDiagDense
    80  // will panic. NewDiagDense will panic if n is zero.
    81  func NewDiagDense(n int, data []float64) *DiagDense {
    82  	if n <= 0 {
    83  		if n == 0 {
    84  			panic(ErrZeroLength)
    85  		}
    86  		panic("mat: negative dimension")
    87  	}
    88  	if data == nil {
    89  		data = make([]float64, n)
    90  	}
    91  	if len(data) != n {
    92  		panic(ErrShape)
    93  	}
    94  	return &DiagDense{
    95  		mat: blas64.Vector{N: n, Data: data, Inc: 1},
    96  	}
    97  }
    98  
    99  // Diag returns the dimension of the receiver.
   100  func (d *DiagDense) Diag() int {
   101  	return d.mat.N
   102  }
   103  
   104  // Dims returns the dimensions of the matrix.
   105  func (d *DiagDense) Dims() (r, c int) {
   106  	return d.mat.N, d.mat.N
   107  }
   108  
   109  // T returns the transpose of the matrix.
   110  func (d *DiagDense) T() Matrix {
   111  	return d
   112  }
   113  
   114  // TTri returns the transpose of the matrix. Note that Diagonal matrices are
   115  // Upper by default.
   116  func (d *DiagDense) TTri() Triangular {
   117  	return TransposeTri{d}
   118  }
   119  
   120  // TBand performs an implicit transpose by returning the receiver inside a
   121  // TransposeBand.
   122  func (d *DiagDense) TBand() Banded {
   123  	return TransposeBand{d}
   124  }
   125  
   126  // TTriBand performs an implicit transpose by returning the receiver inside a
   127  // TransposeTriBand. Note that Diagonal matrices are Upper by default.
   128  func (d *DiagDense) TTriBand() TriBanded {
   129  	return TransposeTriBand{d}
   130  }
   131  
   132  // Bandwidth returns the upper and lower bandwidths of the matrix.
   133  // These values are always zero for diagonal matrices.
   134  func (d *DiagDense) Bandwidth() (kl, ku int) {
   135  	return 0, 0
   136  }
   137  
   138  // Symmetric implements the Symmetric interface.
   139  func (d *DiagDense) Symmetric() int {
   140  	return d.mat.N
   141  }
   142  
   143  // SymBand returns the number of rows/columns in the matrix, and the size of
   144  // the bandwidth.
   145  func (d *DiagDense) SymBand() (n, k int) {
   146  	return d.mat.N, 0
   147  }
   148  
   149  // Triangle implements the Triangular interface.
   150  func (d *DiagDense) Triangle() (int, TriKind) {
   151  	return d.mat.N, Upper
   152  }
   153  
   154  // TriBand returns the number of rows/columns in the matrix, the
   155  // size of the bandwidth, and the orientation. Note that Diagonal matrices are
   156  // Upper by default.
   157  func (d *DiagDense) TriBand() (n, k int, kind TriKind) {
   158  	return d.mat.N, 0, Upper
   159  }
   160  
   161  // Reset empties the matrix so that it can be reused as the
   162  // receiver of a dimensionally restricted operation.
   163  //
   164  // Reset should not be used when the matrix shares backing data.
   165  // See the Reseter interface for more information.
   166  func (d *DiagDense) Reset() {
   167  	// No change of Inc or n to 0 may be
   168  	// made unless both are set to 0.
   169  	d.mat.Inc = 0
   170  	d.mat.N = 0
   171  	d.mat.Data = d.mat.Data[:0]
   172  }
   173  
   174  // Zero sets all of the matrix elements to zero.
   175  func (d *DiagDense) Zero() {
   176  	for i := 0; i < d.mat.N; i++ {
   177  		d.mat.Data[d.mat.Inc*i] = 0
   178  	}
   179  }
   180  
   181  // DiagView returns the diagonal as a matrix backed by the original data.
   182  func (d *DiagDense) DiagView() Diagonal {
   183  	return d
   184  }
   185  
   186  // DiagFrom copies the diagonal of m into the receiver. The receiver must
   187  // be min(r, c) long or empty, otherwise DiagFrom will panic.
   188  func (d *DiagDense) DiagFrom(m Matrix) {
   189  	n := min(m.Dims())
   190  	d.reuseAsNonZeroed(n)
   191  
   192  	var vec blas64.Vector
   193  	switch r := m.(type) {
   194  	case *DiagDense:
   195  		vec = r.mat
   196  	case RawBander:
   197  		mat := r.RawBand()
   198  		vec = blas64.Vector{
   199  			N:    n,
   200  			Inc:  mat.Stride,
   201  			Data: mat.Data[mat.KL : (n-1)*mat.Stride+mat.KL+1],
   202  		}
   203  	case RawMatrixer:
   204  		mat := r.RawMatrix()
   205  		vec = blas64.Vector{
   206  			N:    n,
   207  			Inc:  mat.Stride + 1,
   208  			Data: mat.Data[:(n-1)*mat.Stride+n],
   209  		}
   210  	case RawSymBander:
   211  		mat := r.RawSymBand()
   212  		vec = blas64.Vector{
   213  			N:    n,
   214  			Inc:  mat.Stride,
   215  			Data: mat.Data[:(n-1)*mat.Stride+1],
   216  		}
   217  	case RawSymmetricer:
   218  		mat := r.RawSymmetric()
   219  		vec = blas64.Vector{
   220  			N:    n,
   221  			Inc:  mat.Stride + 1,
   222  			Data: mat.Data[:(n-1)*mat.Stride+n],
   223  		}
   224  	case RawTriBander:
   225  		mat := r.RawTriBand()
   226  		data := mat.Data
   227  		if mat.Uplo == blas.Lower {
   228  			data = data[mat.K:]
   229  		}
   230  		vec = blas64.Vector{
   231  			N:    n,
   232  			Inc:  mat.Stride,
   233  			Data: data[:(n-1)*mat.Stride+1],
   234  		}
   235  	case RawTriangular:
   236  		mat := r.RawTriangular()
   237  		if mat.Diag == blas.Unit {
   238  			for i := 0; i < n; i += d.mat.Inc {
   239  				d.mat.Data[i] = 1
   240  			}
   241  			return
   242  		}
   243  		vec = blas64.Vector{
   244  			N:    n,
   245  			Inc:  mat.Stride + 1,
   246  			Data: mat.Data[:(n-1)*mat.Stride+n],
   247  		}
   248  	case RawVectorer:
   249  		d.mat.Data[0] = r.RawVector().Data[0]
   250  		return
   251  	default:
   252  		for i := 0; i < n; i++ {
   253  			d.setDiag(i, m.At(i, i))
   254  		}
   255  		return
   256  	}
   257  	blas64.Copy(vec, d.mat)
   258  }
   259  
   260  // RawBand returns the underlying data used by the receiver represented
   261  // as a blas64.Band.
   262  // Changes to elements in the receiver following the call will be reflected
   263  // in returned blas64.Band.
   264  func (d *DiagDense) RawBand() blas64.Band {
   265  	return blas64.Band{
   266  		Rows:   d.mat.N,
   267  		Cols:   d.mat.N,
   268  		KL:     0,
   269  		KU:     0,
   270  		Stride: d.mat.Inc,
   271  		Data:   d.mat.Data,
   272  	}
   273  }
   274  
   275  // RawSymBand returns the underlying data used by the receiver represented
   276  // as a blas64.SymmetricBand.
   277  // Changes to elements in the receiver following the call will be reflected
   278  // in returned blas64.Band.
   279  func (d *DiagDense) RawSymBand() blas64.SymmetricBand {
   280  	return blas64.SymmetricBand{
   281  		N:      d.mat.N,
   282  		K:      0,
   283  		Stride: d.mat.Inc,
   284  		Uplo:   blas.Upper,
   285  		Data:   d.mat.Data,
   286  	}
   287  }
   288  
   289  // reuseAsNonZeroed resizes an empty diagonal to a r×r diagonal,
   290  // or checks that a non-empty matrix is r×r.
   291  func (d *DiagDense) reuseAsNonZeroed(r int) {
   292  	if r == 0 {
   293  		panic(ErrZeroLength)
   294  	}
   295  	if d.IsEmpty() {
   296  		d.mat = blas64.Vector{
   297  			Inc:  1,
   298  			Data: use(d.mat.Data, r),
   299  		}
   300  		d.mat.N = r
   301  		return
   302  	}
   303  	if r != d.mat.N {
   304  		panic(ErrShape)
   305  	}
   306  }
   307  
   308  // IsEmpty returns whether the receiver is empty. Empty matrices can be the
   309  // receiver for size-restricted operations. The receiver can be emptied using
   310  // Reset.
   311  func (d *DiagDense) IsEmpty() bool {
   312  	// It must be the case that d.Dims() returns
   313  	// zeros in this case. See comment in Reset().
   314  	return d.mat.Inc == 0
   315  }
   316  
   317  // Trace returns the trace.
   318  func (d *DiagDense) Trace() float64 {
   319  	rb := d.RawBand()
   320  	var tr float64
   321  	for i := 0; i < rb.Rows; i++ {
   322  		tr += rb.Data[rb.KL+i*rb.Stride]
   323  	}
   324  	return tr
   325  
   326  }