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 }