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