gonum.org/v1/gonum@v0.14.0/mat/dense.go (about) 1 // Copyright ©2013 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 "gonum.org/v1/gonum/blas" 9 "gonum.org/v1/gonum/blas/blas64" 10 "gonum.org/v1/gonum/lapack" 11 "gonum.org/v1/gonum/lapack/lapack64" 12 ) 13 14 var ( 15 dense *Dense 16 17 _ Matrix = dense 18 _ allMatrix = dense 19 _ denseMatrix = dense 20 _ Mutable = dense 21 22 _ ClonerFrom = dense 23 _ RowViewer = dense 24 _ ColViewer = dense 25 _ RawRowViewer = dense 26 _ Grower = dense 27 28 _ RawMatrixSetter = dense 29 _ RawMatrixer = dense 30 31 _ Reseter = dense 32 ) 33 34 // Dense is a dense matrix representation. 35 type Dense struct { 36 mat blas64.General 37 38 capRows, capCols int 39 } 40 41 // NewDense creates a new Dense matrix with r rows and c columns. If data == nil, 42 // a new slice is allocated for the backing slice. If len(data) == r*c, data is 43 // used as the backing slice, and changes to the elements of the returned Dense 44 // will be reflected in data. If neither of these is true, NewDense will panic. 45 // NewDense will panic if either r or c is zero. 46 // 47 // The data must be arranged in row-major order, i.e. the (i*c + j)-th 48 // element in the data slice is the {i, j}-th element in the matrix. 49 func NewDense(r, c int, data []float64) *Dense { 50 if r <= 0 || c <= 0 { 51 if r == 0 || c == 0 { 52 panic(ErrZeroLength) 53 } 54 panic(ErrNegativeDimension) 55 } 56 if data != nil && r*c != len(data) { 57 panic(ErrShape) 58 } 59 if data == nil { 60 data = make([]float64, r*c) 61 } 62 return &Dense{ 63 mat: blas64.General{ 64 Rows: r, 65 Cols: c, 66 Stride: c, 67 Data: data, 68 }, 69 capRows: r, 70 capCols: c, 71 } 72 } 73 74 // ReuseAs changes the receiver if it IsEmpty() to be of size r×c. 75 // 76 // ReuseAs re-uses the backing data slice if it has sufficient capacity, 77 // otherwise a new slice is allocated. The backing data is zero on return. 78 // 79 // ReuseAs panics if the receiver is not empty, and panics if 80 // the input sizes are less than one. To empty the receiver for re-use, 81 // Reset should be used. 82 func (m *Dense) ReuseAs(r, c int) { 83 if r <= 0 || c <= 0 { 84 if r == 0 || c == 0 { 85 panic(ErrZeroLength) 86 } 87 panic(ErrNegativeDimension) 88 } 89 if !m.IsEmpty() { 90 panic(ErrReuseNonEmpty) 91 } 92 m.reuseAsZeroed(r, c) 93 } 94 95 // reuseAsNonZeroed resizes an empty matrix to a r×c matrix, 96 // or checks that a non-empty matrix is r×c. It does not zero 97 // the data in the receiver. 98 func (m *Dense) reuseAsNonZeroed(r, c int) { 99 // reuseAs must be kept in sync with reuseAsZeroed. 100 if m.mat.Rows > m.capRows || m.mat.Cols > m.capCols { 101 // Panic as a string, not a mat.Error. 102 panic(badCap) 103 } 104 if r == 0 || c == 0 { 105 panic(ErrZeroLength) 106 } 107 if m.IsEmpty() { 108 m.mat = blas64.General{ 109 Rows: r, 110 Cols: c, 111 Stride: c, 112 Data: use(m.mat.Data, r*c), 113 } 114 m.capRows = r 115 m.capCols = c 116 return 117 } 118 if r != m.mat.Rows || c != m.mat.Cols { 119 panic(ErrShape) 120 } 121 } 122 123 // reuseAsZeroed resizes an empty matrix to a r×c matrix, 124 // or checks that a non-empty matrix is r×c. It zeroes 125 // all the elements of the matrix. 126 func (m *Dense) reuseAsZeroed(r, c int) { 127 // reuseAsZeroed must be kept in sync with reuseAsNonZeroed. 128 if m.mat.Rows > m.capRows || m.mat.Cols > m.capCols { 129 // Panic as a string, not a mat.Error. 130 panic(badCap) 131 } 132 if r == 0 || c == 0 { 133 panic(ErrZeroLength) 134 } 135 if m.IsEmpty() { 136 m.mat = blas64.General{ 137 Rows: r, 138 Cols: c, 139 Stride: c, 140 Data: useZeroed(m.mat.Data, r*c), 141 } 142 m.capRows = r 143 m.capCols = c 144 return 145 } 146 if r != m.mat.Rows || c != m.mat.Cols { 147 panic(ErrShape) 148 } 149 m.Zero() 150 } 151 152 // Zero sets all of the matrix elements to zero. 153 func (m *Dense) Zero() { 154 r := m.mat.Rows 155 c := m.mat.Cols 156 for i := 0; i < r; i++ { 157 zero(m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+c]) 158 } 159 } 160 161 // isolatedWorkspace returns a new dense matrix w with the size of a and 162 // returns a callback to defer which performs cleanup at the return of the call. 163 // This should be used when a method receiver is the same pointer as an input argument. 164 func (m *Dense) isolatedWorkspace(a Matrix) (w *Dense, restore func()) { 165 r, c := a.Dims() 166 if r == 0 || c == 0 { 167 panic(ErrZeroLength) 168 } 169 w = getDenseWorkspace(r, c, false) 170 return w, func() { 171 m.Copy(w) 172 putDenseWorkspace(w) 173 } 174 } 175 176 // Reset empties the matrix so that it can be reused as the 177 // receiver of a dimensionally restricted operation. 178 // 179 // Reset should not be used when the matrix shares backing data. 180 // See the Reseter interface for more information. 181 func (m *Dense) Reset() { 182 // Row, Cols and Stride must be zeroed in unison. 183 m.mat.Rows, m.mat.Cols, m.mat.Stride = 0, 0, 0 184 m.capRows, m.capCols = 0, 0 185 m.mat.Data = m.mat.Data[:0] 186 } 187 188 // IsEmpty returns whether the receiver is empty. Empty matrices can be the 189 // receiver for size-restricted operations. The receiver can be emptied using 190 // Reset. 191 func (m *Dense) IsEmpty() bool { 192 // It must be the case that m.Dims() returns 193 // zeros in this case. See comment in Reset(). 194 return m.mat.Stride == 0 195 } 196 197 // asTriDense returns a TriDense with the given size and side. The backing data 198 // of the TriDense is the same as the receiver. 199 func (m *Dense) asTriDense(n int, diag blas.Diag, uplo blas.Uplo) *TriDense { 200 return &TriDense{ 201 mat: blas64.Triangular{ 202 N: n, 203 Stride: m.mat.Stride, 204 Data: m.mat.Data, 205 Uplo: uplo, 206 Diag: diag, 207 }, 208 cap: n, 209 } 210 } 211 212 // DenseCopyOf returns a newly allocated copy of the elements of a. 213 func DenseCopyOf(a Matrix) *Dense { 214 d := &Dense{} 215 d.CloneFrom(a) 216 return d 217 } 218 219 // SetRawMatrix sets the underlying blas64.General used by the receiver. 220 // Changes to elements in the receiver following the call will be reflected 221 // in b. 222 func (m *Dense) SetRawMatrix(b blas64.General) { 223 m.capRows, m.capCols = b.Rows, b.Cols 224 m.mat = b 225 } 226 227 // RawMatrix returns the underlying blas64.General used by the receiver. 228 // Changes to elements in the receiver following the call will be reflected 229 // in returned blas64.General. 230 func (m *Dense) RawMatrix() blas64.General { return m.mat } 231 232 // Dims returns the number of rows and columns in the matrix. 233 func (m *Dense) Dims() (r, c int) { return m.mat.Rows, m.mat.Cols } 234 235 // Caps returns the number of rows and columns in the backing matrix. 236 func (m *Dense) Caps() (r, c int) { return m.capRows, m.capCols } 237 238 // T performs an implicit transpose by returning the receiver inside a Transpose. 239 func (m *Dense) T() Matrix { 240 return Transpose{m} 241 } 242 243 // ColView returns a Vector reflecting the column j, backed by the matrix data. 244 // 245 // See ColViewer for more information. 246 func (m *Dense) ColView(j int) Vector { 247 var v VecDense 248 v.ColViewOf(m, j) 249 return &v 250 } 251 252 // SetCol sets the values in the specified column of the matrix to the values 253 // in src. len(src) must equal the number of rows in the receiver. 254 func (m *Dense) SetCol(j int, src []float64) { 255 if j >= m.mat.Cols || j < 0 { 256 panic(ErrColAccess) 257 } 258 if len(src) != m.mat.Rows { 259 panic(ErrColLength) 260 } 261 262 blas64.Copy( 263 blas64.Vector{N: m.mat.Rows, Inc: 1, Data: src}, 264 blas64.Vector{N: m.mat.Rows, Inc: m.mat.Stride, Data: m.mat.Data[j:]}, 265 ) 266 } 267 268 // SetRow sets the values in the specified rows of the matrix to the values 269 // in src. len(src) must equal the number of columns in the receiver. 270 func (m *Dense) SetRow(i int, src []float64) { 271 if i >= m.mat.Rows || i < 0 { 272 panic(ErrRowAccess) 273 } 274 if len(src) != m.mat.Cols { 275 panic(ErrRowLength) 276 } 277 278 copy(m.rawRowView(i), src) 279 } 280 281 // RowView returns row i of the matrix data represented as a column vector, 282 // backed by the matrix data. 283 // 284 // See RowViewer for more information. 285 func (m *Dense) RowView(i int) Vector { 286 var v VecDense 287 v.RowViewOf(m, i) 288 return &v 289 } 290 291 // RawRowView returns a slice backed by the same array as backing the 292 // receiver. 293 func (m *Dense) RawRowView(i int) []float64 { 294 if i >= m.mat.Rows || i < 0 { 295 panic(ErrRowAccess) 296 } 297 return m.rawRowView(i) 298 } 299 300 func (m *Dense) rawRowView(i int) []float64 { 301 return m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+m.mat.Cols] 302 } 303 304 // DiagView returns the diagonal as a matrix backed by the original data. 305 func (m *Dense) DiagView() Diagonal { 306 n := min(m.mat.Rows, m.mat.Cols) 307 return &DiagDense{ 308 mat: blas64.Vector{ 309 N: n, 310 Inc: m.mat.Stride + 1, 311 Data: m.mat.Data[:(n-1)*m.mat.Stride+n], 312 }, 313 } 314 } 315 316 // Slice returns a new Matrix that shares backing data with the receiver. 317 // The returned matrix starts at {i,j} of the receiver and extends k-i rows 318 // and l-j columns. The final row in the resulting matrix is k-1 and the 319 // final column is l-1. 320 // Slice panics with ErrIndexOutOfRange if the slice is outside the capacity 321 // of the receiver. 322 func (m *Dense) Slice(i, k, j, l int) Matrix { 323 return m.slice(i, k, j, l) 324 } 325 326 func (m *Dense) slice(i, k, j, l int) *Dense { 327 mr, mc := m.Caps() 328 if i < 0 || mr <= i || j < 0 || mc <= j || k < i || mr < k || l < j || mc < l { 329 if i == k || j == l { 330 panic(ErrZeroLength) 331 } 332 panic(ErrIndexOutOfRange) 333 } 334 t := *m 335 t.mat.Data = t.mat.Data[i*t.mat.Stride+j : (k-1)*t.mat.Stride+l] 336 t.mat.Rows = k - i 337 t.mat.Cols = l - j 338 t.capRows -= i 339 t.capCols -= j 340 return &t 341 } 342 343 // Grow returns the receiver expanded by r rows and c columns. If the dimensions 344 // of the expanded matrix are outside the capacities of the receiver a new 345 // allocation is made, otherwise not. Note the receiver itself is not modified 346 // during the call to Grow. 347 func (m *Dense) Grow(r, c int) Matrix { 348 if r < 0 || c < 0 { 349 panic(ErrIndexOutOfRange) 350 } 351 if r == 0 && c == 0 { 352 return m 353 } 354 355 r += m.mat.Rows 356 c += m.mat.Cols 357 358 var t Dense 359 switch { 360 case m.mat.Rows == 0 || m.mat.Cols == 0: 361 t.mat = blas64.General{ 362 Rows: r, 363 Cols: c, 364 Stride: c, 365 // We zero because we don't know how the matrix will be used. 366 // In other places, the mat is immediately filled with a result; 367 // this is not the case here. 368 Data: useZeroed(m.mat.Data, r*c), 369 } 370 case r > m.capRows || c > m.capCols: 371 cr := max(r, m.capRows) 372 cc := max(c, m.capCols) 373 t.mat = blas64.General{ 374 Rows: r, 375 Cols: c, 376 Stride: cc, 377 Data: make([]float64, cr*cc), 378 } 379 t.capRows = cr 380 t.capCols = cc 381 // Copy the complete matrix over to the new matrix. 382 // Including elements not currently visible. Use a temporary structure 383 // to avoid modifying the receiver. 384 var tmp Dense 385 tmp.mat = blas64.General{ 386 Rows: m.mat.Rows, 387 Cols: m.mat.Cols, 388 Stride: m.mat.Stride, 389 Data: m.mat.Data, 390 } 391 tmp.capRows = m.capRows 392 tmp.capCols = m.capCols 393 t.Copy(&tmp) 394 return &t 395 default: 396 t.mat = blas64.General{ 397 Data: m.mat.Data[:(r-1)*m.mat.Stride+c], 398 Rows: r, 399 Cols: c, 400 Stride: m.mat.Stride, 401 } 402 } 403 t.capRows = r 404 t.capCols = c 405 return &t 406 } 407 408 // CloneFrom makes a copy of a into the receiver, overwriting the previous value of 409 // the receiver. The clone from operation does not make any restriction on shape and 410 // will not cause shadowing. 411 // 412 // See the ClonerFrom interface for more information. 413 func (m *Dense) CloneFrom(a Matrix) { 414 r, c := a.Dims() 415 mat := blas64.General{ 416 Rows: r, 417 Cols: c, 418 Stride: c, 419 } 420 m.capRows, m.capCols = r, c 421 422 aU, trans := untransposeExtract(a) 423 switch aU := aU.(type) { 424 case *Dense: 425 amat := aU.mat 426 mat.Data = make([]float64, r*c) 427 if trans { 428 for i := 0; i < r; i++ { 429 blas64.Copy(blas64.Vector{N: c, Inc: amat.Stride, Data: amat.Data[i : i+(c-1)*amat.Stride+1]}, 430 blas64.Vector{N: c, Inc: 1, Data: mat.Data[i*c : (i+1)*c]}) 431 } 432 } else { 433 for i := 0; i < r; i++ { 434 copy(mat.Data[i*c:(i+1)*c], amat.Data[i*amat.Stride:i*amat.Stride+c]) 435 } 436 } 437 case *VecDense: 438 amat := aU.mat 439 mat.Data = make([]float64, aU.mat.N) 440 blas64.Copy(blas64.Vector{N: aU.mat.N, Inc: amat.Inc, Data: amat.Data}, 441 blas64.Vector{N: aU.mat.N, Inc: 1, Data: mat.Data}) 442 default: 443 mat.Data = make([]float64, r*c) 444 w := *m 445 w.mat = mat 446 for i := 0; i < r; i++ { 447 for j := 0; j < c; j++ { 448 w.set(i, j, a.At(i, j)) 449 } 450 } 451 *m = w 452 return 453 } 454 m.mat = mat 455 } 456 457 // Copy makes a copy of elements of a into the receiver. It is similar to the 458 // built-in copy; it copies as much as the overlap between the two matrices and 459 // returns the number of rows and columns it copied. If a aliases the receiver 460 // and is a transposed Dense or VecDense, with a non-unitary increment, Copy will 461 // panic. 462 // 463 // See the Copier interface for more information. 464 func (m *Dense) Copy(a Matrix) (r, c int) { 465 r, c = a.Dims() 466 if a == m { 467 return r, c 468 } 469 r = min(r, m.mat.Rows) 470 c = min(c, m.mat.Cols) 471 if r == 0 || c == 0 { 472 return 0, 0 473 } 474 475 aU, trans := untransposeExtract(a) 476 switch aU := aU.(type) { 477 case *Dense: 478 amat := aU.mat 479 if trans { 480 if amat.Stride != 1 { 481 m.checkOverlap(amat) 482 } 483 for i := 0; i < r; i++ { 484 blas64.Copy(blas64.Vector{N: c, Inc: amat.Stride, Data: amat.Data[i : i+(c-1)*amat.Stride+1]}, 485 blas64.Vector{N: c, Inc: 1, Data: m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+c]}) 486 } 487 } else { 488 switch o := offset(m.mat.Data, amat.Data); { 489 case o < 0: 490 for i := r - 1; i >= 0; i-- { 491 copy(m.mat.Data[i*m.mat.Stride:i*m.mat.Stride+c], amat.Data[i*amat.Stride:i*amat.Stride+c]) 492 } 493 case o > 0: 494 for i := 0; i < r; i++ { 495 copy(m.mat.Data[i*m.mat.Stride:i*m.mat.Stride+c], amat.Data[i*amat.Stride:i*amat.Stride+c]) 496 } 497 default: 498 // Nothing to do. 499 } 500 } 501 case *VecDense: 502 var n, stride int 503 amat := aU.mat 504 if trans { 505 if amat.Inc != 1 { 506 m.checkOverlap(aU.asGeneral()) 507 } 508 n = c 509 stride = 1 510 } else { 511 n = r 512 stride = m.mat.Stride 513 } 514 if amat.Inc == 1 && stride == 1 { 515 copy(m.mat.Data, amat.Data[:n]) 516 break 517 } 518 switch o := offset(m.mat.Data, amat.Data); { 519 case o < 0: 520 blas64.Copy(blas64.Vector{N: n, Inc: -amat.Inc, Data: amat.Data}, 521 blas64.Vector{N: n, Inc: -stride, Data: m.mat.Data}) 522 case o > 0: 523 blas64.Copy(blas64.Vector{N: n, Inc: amat.Inc, Data: amat.Data}, 524 blas64.Vector{N: n, Inc: stride, Data: m.mat.Data}) 525 default: 526 // Nothing to do. 527 } 528 default: 529 m.checkOverlapMatrix(aU) 530 for i := 0; i < r; i++ { 531 for j := 0; j < c; j++ { 532 m.set(i, j, a.At(i, j)) 533 } 534 } 535 } 536 537 return r, c 538 } 539 540 // Stack appends the rows of b onto the rows of a, placing the result into the 541 // receiver with b placed in the greater indexed rows. Stack will panic if the 542 // two input matrices do not have the same number of columns or the constructed 543 // stacked matrix is not the same shape as the receiver. 544 func (m *Dense) Stack(a, b Matrix) { 545 ar, ac := a.Dims() 546 br, bc := b.Dims() 547 if ac != bc || m == a || m == b { 548 panic(ErrShape) 549 } 550 551 m.reuseAsNonZeroed(ar+br, ac) 552 553 m.Copy(a) 554 w := m.slice(ar, ar+br, 0, bc) 555 w.Copy(b) 556 } 557 558 // Augment creates the augmented matrix of a and b, where b is placed in the 559 // greater indexed columns. Augment will panic if the two input matrices do 560 // not have the same number of rows or the constructed augmented matrix is 561 // not the same shape as the receiver. 562 func (m *Dense) Augment(a, b Matrix) { 563 ar, ac := a.Dims() 564 br, bc := b.Dims() 565 if ar != br || m == a || m == b { 566 panic(ErrShape) 567 } 568 569 m.reuseAsNonZeroed(ar, ac+bc) 570 571 m.Copy(a) 572 w := m.slice(0, br, ac, ac+bc) 573 w.Copy(b) 574 } 575 576 // Trace returns the trace of the matrix. 577 // 578 // Trace will panic with ErrSquare if the matrix is not square and with 579 // ErrZeroLength if the matrix has zero size. 580 func (m *Dense) Trace() float64 { 581 r, c := m.Dims() 582 if r != c { 583 panic(ErrSquare) 584 } 585 if m.IsEmpty() { 586 panic(ErrZeroLength) 587 } 588 // TODO(btracey): could use internal asm sum routine. 589 var v float64 590 for i := 0; i < m.mat.Rows; i++ { 591 v += m.mat.Data[i*m.mat.Stride+i] 592 } 593 return v 594 } 595 596 // Norm returns the specified norm of the receiver. Valid norms are: 597 // 598 // 1 - The maximum absolute column sum 599 // 2 - The Frobenius norm, the square root of the sum of the squares of the elements 600 // Inf - The maximum absolute row sum 601 // 602 // Norm will panic with ErrNormOrder if an illegal norm is specified and with 603 // ErrShape if the matrix has zero size. 604 func (m *Dense) Norm(norm float64) float64 { 605 if m.IsEmpty() { 606 panic(ErrZeroLength) 607 } 608 lnorm := normLapack(norm, false) 609 if lnorm == lapack.MaxColumnSum { 610 work := getFloat64s(m.mat.Cols, false) 611 defer putFloat64s(work) 612 return lapack64.Lange(lnorm, m.mat, work) 613 } 614 return lapack64.Lange(lnorm, m.mat, nil) 615 }