mirror of
https://github.com/TECHNOFAB11/zfs-localpv.git
synced 2025-12-12 14:30:12 +01:00
feat(modules): migrate to go modules and bump go version 1.14.4
- migrate to go module - bump go version 1.14.4 Signed-off-by: prateekpandey14 <prateek.pandey@mayadata.io>
This commit is contained in:
parent
f5ae3ff476
commit
fa76b346a0
837 changed files with 104140 additions and 158314 deletions
3
vendor/gonum.org/v1/gonum/mat/README.md
generated
vendored
Normal file
3
vendor/gonum.org/v1/gonum/mat/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Gonum matrix [](https://godoc.org/gonum.org/v1/gonum/mat)
|
||||
|
||||
Package mat is a matrix package for the Go language.
|
||||
263
vendor/gonum.org/v1/gonum/mat/band.go
generated
vendored
Normal file
263
vendor/gonum.org/v1/gonum/mat/band.go
generated
vendored
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
// Copyright ©2017 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
)
|
||||
|
||||
var (
|
||||
bandDense *BandDense
|
||||
_ Matrix = bandDense
|
||||
_ Banded = bandDense
|
||||
_ RawBander = bandDense
|
||||
|
||||
_ NonZeroDoer = bandDense
|
||||
_ RowNonZeroDoer = bandDense
|
||||
_ ColNonZeroDoer = bandDense
|
||||
)
|
||||
|
||||
// BandDense represents a band matrix in dense storage format.
|
||||
type BandDense struct {
|
||||
mat blas64.Band
|
||||
}
|
||||
|
||||
// Banded is a band matrix representation.
|
||||
type Banded interface {
|
||||
Matrix
|
||||
// Bandwidth returns the lower and upper bandwidth values for
|
||||
// the matrix. The total bandwidth of the matrix is kl+ku+1.
|
||||
Bandwidth() (kl, ku int)
|
||||
|
||||
// TBand is the equivalent of the T() method in the Matrix
|
||||
// interface but guarantees the transpose is of banded type.
|
||||
TBand() Banded
|
||||
}
|
||||
|
||||
// A RawBander can return a blas64.Band representation of the receiver.
|
||||
// Changes to the blas64.Band.Data slice will be reflected in the original
|
||||
// matrix, changes to the Rows, Cols, KL, KU and Stride fields will not.
|
||||
type RawBander interface {
|
||||
RawBand() blas64.Band
|
||||
}
|
||||
|
||||
// A MutableBanded can set elements of a band matrix.
|
||||
type MutableBanded interface {
|
||||
Banded
|
||||
SetBand(i, j int, v float64)
|
||||
}
|
||||
|
||||
var (
|
||||
_ Matrix = TransposeBand{}
|
||||
_ Banded = TransposeBand{}
|
||||
_ UntransposeBander = TransposeBand{}
|
||||
)
|
||||
|
||||
// TransposeBand is a type for performing an implicit transpose of a band
|
||||
// matrix. It implements the Banded interface, returning values from the
|
||||
// transpose of the matrix within.
|
||||
type TransposeBand struct {
|
||||
Banded Banded
|
||||
}
|
||||
|
||||
// At returns the value of the element at row i and column j of the transposed
|
||||
// matrix, that is, row j and column i of the Banded field.
|
||||
func (t TransposeBand) At(i, j int) float64 {
|
||||
return t.Banded.At(j, i)
|
||||
}
|
||||
|
||||
// Dims returns the dimensions of the transposed matrix.
|
||||
func (t TransposeBand) Dims() (r, c int) {
|
||||
c, r = t.Banded.Dims()
|
||||
return r, c
|
||||
}
|
||||
|
||||
// T performs an implicit transpose by returning the Banded field.
|
||||
func (t TransposeBand) T() Matrix {
|
||||
return t.Banded
|
||||
}
|
||||
|
||||
// Bandwidth returns the lower and upper bandwidth values for
|
||||
// the transposed matrix.
|
||||
func (t TransposeBand) Bandwidth() (kl, ku int) {
|
||||
kl, ku = t.Banded.Bandwidth()
|
||||
return ku, kl
|
||||
}
|
||||
|
||||
// TBand performs an implicit transpose by returning the Banded field.
|
||||
func (t TransposeBand) TBand() Banded {
|
||||
return t.Banded
|
||||
}
|
||||
|
||||
// Untranspose returns the Banded field.
|
||||
func (t TransposeBand) Untranspose() Matrix {
|
||||
return t.Banded
|
||||
}
|
||||
|
||||
// UntransposeBand returns the Banded field.
|
||||
func (t TransposeBand) UntransposeBand() Banded {
|
||||
return t.Banded
|
||||
}
|
||||
|
||||
// NewBandDense creates a new Band matrix with r rows and c columns. If data == nil,
|
||||
// a new slice is allocated for the backing slice. If len(data) == min(r, c+kl)*(kl+ku+1),
|
||||
// data is used as the backing slice, and changes to the elements of the returned
|
||||
// BandDense will be reflected in data. If neither of these is true, NewBandDense
|
||||
// will panic. kl must be at least zero and less r, and ku must be at least zero and
|
||||
// less than c, otherwise NewBandDense will panic.
|
||||
// NewBandDense will panic if either r or c is zero.
|
||||
//
|
||||
// The data must be arranged in row-major order constructed by removing the zeros
|
||||
// from the rows outside the band and aligning the diagonals. For example, the matrix
|
||||
// 1 2 3 0 0 0
|
||||
// 4 5 6 7 0 0
|
||||
// 0 8 9 10 11 0
|
||||
// 0 0 12 13 14 15
|
||||
// 0 0 0 16 17 18
|
||||
// 0 0 0 0 19 20
|
||||
// becomes (* entries are never accessed)
|
||||
// * 1 2 3
|
||||
// 4 5 6 7
|
||||
// 8 9 10 11
|
||||
// 12 13 14 15
|
||||
// 16 17 18 *
|
||||
// 19 20 * *
|
||||
// which is passed to NewBandDense as []float64{*, 1, 2, 3, 4, ...} with kl=1 and ku=2.
|
||||
// Only the values in the band portion of the matrix are used.
|
||||
func NewBandDense(r, c, kl, ku int, data []float64) *BandDense {
|
||||
if r <= 0 || c <= 0 || kl < 0 || ku < 0 {
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic("mat: negative dimension")
|
||||
}
|
||||
if kl+1 > r || ku+1 > c {
|
||||
panic("mat: band out of range")
|
||||
}
|
||||
bc := kl + ku + 1
|
||||
if data != nil && len(data) != min(r, c+kl)*bc {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if data == nil {
|
||||
data = make([]float64, min(r, c+kl)*bc)
|
||||
}
|
||||
return &BandDense{
|
||||
mat: blas64.Band{
|
||||
Rows: r,
|
||||
Cols: c,
|
||||
KL: kl,
|
||||
KU: ku,
|
||||
Stride: bc,
|
||||
Data: data,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewDiagonalRect is a convenience function that returns a diagonal matrix represented by a
|
||||
// BandDense. The length of data must be min(r, c) otherwise NewDiagonalRect will panic.
|
||||
func NewDiagonalRect(r, c int, data []float64) *BandDense {
|
||||
return NewBandDense(r, c, 0, 0, data)
|
||||
}
|
||||
|
||||
// Dims returns the number of rows and columns in the matrix.
|
||||
func (b *BandDense) Dims() (r, c int) {
|
||||
return b.mat.Rows, b.mat.Cols
|
||||
}
|
||||
|
||||
// Bandwidth returns the upper and lower bandwidths of the matrix.
|
||||
func (b *BandDense) Bandwidth() (kl, ku int) {
|
||||
return b.mat.KL, b.mat.KU
|
||||
}
|
||||
|
||||
// T performs an implicit transpose by returning the receiver inside a Transpose.
|
||||
func (b *BandDense) T() Matrix {
|
||||
return Transpose{b}
|
||||
}
|
||||
|
||||
// TBand performs an implicit transpose by returning the receiver inside a TransposeBand.
|
||||
func (b *BandDense) TBand() Banded {
|
||||
return TransposeBand{b}
|
||||
}
|
||||
|
||||
// RawBand returns the underlying blas64.Band used by the receiver.
|
||||
// Changes to elements in the receiver following the call will be reflected
|
||||
// in returned blas64.Band.
|
||||
func (b *BandDense) RawBand() blas64.Band {
|
||||
return b.mat
|
||||
}
|
||||
|
||||
// SetRawBand sets the underlying blas64.Band used by the receiver.
|
||||
// Changes to elements in the receiver following the call will be reflected
|
||||
// in the input.
|
||||
func (b *BandDense) SetRawBand(mat blas64.Band) {
|
||||
b.mat = mat
|
||||
}
|
||||
|
||||
// DiagView returns the diagonal as a matrix backed by the original data.
|
||||
func (b *BandDense) DiagView() Diagonal {
|
||||
n := min(b.mat.Rows, b.mat.Cols)
|
||||
return &DiagDense{
|
||||
mat: blas64.Vector{
|
||||
N: n,
|
||||
Inc: b.mat.Stride,
|
||||
Data: b.mat.Data[b.mat.KL : (n-1)*b.mat.Stride+b.mat.KL+1],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// DoNonZero calls the function fn for each of the non-zero elements of b. The function fn
|
||||
// takes a row/column index and the element value of b at (i, j).
|
||||
func (b *BandDense) DoNonZero(fn func(i, j int, v float64)) {
|
||||
for i := 0; i < min(b.mat.Rows, b.mat.Cols+b.mat.KL); i++ {
|
||||
for j := max(0, i-b.mat.KL); j < min(b.mat.Cols, i+b.mat.KU+1); j++ {
|
||||
v := b.at(i, j)
|
||||
if v != 0 {
|
||||
fn(i, j, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DoRowNonZero calls the function fn for each of the non-zero elements of row i of b. The function fn
|
||||
// takes a row/column index and the element value of b at (i, j).
|
||||
func (b *BandDense) DoRowNonZero(i int, fn func(i, j int, v float64)) {
|
||||
if i < 0 || b.mat.Rows <= i {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
for j := max(0, i-b.mat.KL); j < min(b.mat.Cols, i+b.mat.KU+1); j++ {
|
||||
v := b.at(i, j)
|
||||
if v != 0 {
|
||||
fn(i, j, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DoColNonZero calls the function fn for each of the non-zero elements of column j of b. The function fn
|
||||
// takes a row/column index and the element value of b at (i, j).
|
||||
func (b *BandDense) DoColNonZero(j int, fn func(i, j int, v float64)) {
|
||||
if j < 0 || b.mat.Cols <= j {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
for i := 0; i < min(b.mat.Rows, b.mat.Cols+b.mat.KL); i++ {
|
||||
if i-b.mat.KL <= j && j < i+b.mat.KU+1 {
|
||||
v := b.at(i, j)
|
||||
if v != 0 {
|
||||
fn(i, j, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Zero sets all of the matrix elements to zero.
|
||||
func (b *BandDense) Zero() {
|
||||
m := b.mat.Rows
|
||||
kL := b.mat.KL
|
||||
nCol := b.mat.KU + 1 + kL
|
||||
for i := 0; i < m; i++ {
|
||||
l := max(0, kL-i)
|
||||
u := min(nCol, m+kL-i)
|
||||
zero(b.mat.Data[i*b.mat.Stride+l : i*b.mat.Stride+u])
|
||||
}
|
||||
}
|
||||
168
vendor/gonum.org/v1/gonum/mat/cdense.go
generated
vendored
Normal file
168
vendor/gonum.org/v1/gonum/mat/cdense.go
generated
vendored
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
// Copyright ©2019 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import "gonum.org/v1/gonum/blas/cblas128"
|
||||
|
||||
// Dense is a dense matrix representation with complex data.
|
||||
type CDense struct {
|
||||
mat cblas128.General
|
||||
|
||||
capRows, capCols int
|
||||
}
|
||||
|
||||
// Dims returns the number of rows and columns in the matrix.
|
||||
func (m *CDense) Dims() (r, c int) {
|
||||
return m.mat.Rows, m.mat.Cols
|
||||
}
|
||||
|
||||
// H performs an implicit conjugate transpose by returning the receiver inside a
|
||||
// Conjugate.
|
||||
func (m *CDense) H() CMatrix {
|
||||
return Conjugate{m}
|
||||
}
|
||||
|
||||
// NewCDense creates a new complex Dense matrix with r rows and c columns.
|
||||
// If data == nil, a new slice is allocated for the backing slice.
|
||||
// If len(data) == r*c, data is used as the backing slice, and changes to the
|
||||
// elements of the returned CDense will be reflected in data.
|
||||
// If neither of these is true, NewCDense will panic.
|
||||
// NewCDense will panic if either r or c is zero.
|
||||
//
|
||||
// The data must be arranged in row-major order, i.e. the (i*c + j)-th
|
||||
// element in the data slice is the {i, j}-th element in the matrix.
|
||||
func NewCDense(r, c int, data []complex128) *CDense {
|
||||
if r <= 0 || c <= 0 {
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic("mat: negative dimension")
|
||||
}
|
||||
if data != nil && r*c != len(data) {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if data == nil {
|
||||
data = make([]complex128, r*c)
|
||||
}
|
||||
return &CDense{
|
||||
mat: cblas128.General{
|
||||
Rows: r,
|
||||
Cols: c,
|
||||
Stride: c,
|
||||
Data: data,
|
||||
},
|
||||
capRows: r,
|
||||
capCols: c,
|
||||
}
|
||||
}
|
||||
|
||||
// reuseAs resizes an empty matrix to a r×c matrix,
|
||||
// or checks that a non-empty matrix is r×c.
|
||||
//
|
||||
// reuseAs must be kept in sync with reuseAsZeroed.
|
||||
func (m *CDense) reuseAs(r, c int) {
|
||||
if m.mat.Rows > m.capRows || m.mat.Cols > m.capCols {
|
||||
// Panic as a string, not a mat.Error.
|
||||
panic("mat: caps not correctly set")
|
||||
}
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
if m.IsZero() {
|
||||
m.mat = cblas128.General{
|
||||
Rows: r,
|
||||
Cols: c,
|
||||
Stride: c,
|
||||
Data: useC(m.mat.Data, r*c),
|
||||
}
|
||||
m.capRows = r
|
||||
m.capCols = c
|
||||
return
|
||||
}
|
||||
if r != m.mat.Rows || c != m.mat.Cols {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *CDense) reuseAsZeroed(r, c int) {
|
||||
// This must be kept in-sync with reuseAs.
|
||||
if m.mat.Rows > m.capRows || m.mat.Cols > m.capCols {
|
||||
// Panic as a string, not a mat.Error.
|
||||
panic("mat: caps not correctly set")
|
||||
}
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
if m.IsZero() {
|
||||
m.mat = cblas128.General{
|
||||
Rows: r,
|
||||
Cols: c,
|
||||
Stride: c,
|
||||
Data: useZeroedC(m.mat.Data, r*c),
|
||||
}
|
||||
m.capRows = r
|
||||
m.capCols = c
|
||||
return
|
||||
}
|
||||
if r != m.mat.Rows || c != m.mat.Cols {
|
||||
panic(ErrShape)
|
||||
}
|
||||
m.Zero()
|
||||
}
|
||||
|
||||
// Reset zeros the dimensions of the matrix so that it can be reused as the
|
||||
// receiver of a dimensionally restricted operation.
|
||||
//
|
||||
// See the Reseter interface for more information.
|
||||
func (m *CDense) Reset() {
|
||||
// Row, Cols and Stride must be zeroed in unison.
|
||||
m.mat.Rows, m.mat.Cols, m.mat.Stride = 0, 0, 0
|
||||
m.capRows, m.capCols = 0, 0
|
||||
m.mat.Data = m.mat.Data[:0]
|
||||
}
|
||||
|
||||
// IsZero returns whether the receiver is zero-sized. Zero-sized matrices can be the
|
||||
// receiver for size-restricted operations. CDense matrices can be zeroed using Reset.
|
||||
func (m *CDense) IsZero() bool {
|
||||
// It must be the case that m.Dims() returns
|
||||
// zeros in this case. See comment in Reset().
|
||||
return m.mat.Stride == 0
|
||||
}
|
||||
|
||||
// Zero sets all of the matrix elements to zero.
|
||||
func (m *CDense) Zero() {
|
||||
r := m.mat.Rows
|
||||
c := m.mat.Cols
|
||||
for i := 0; i < r; i++ {
|
||||
zeroC(m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+c])
|
||||
}
|
||||
}
|
||||
|
||||
// Copy makes a copy of elements of a into the receiver. It is similar to the
|
||||
// built-in copy; it copies as much as the overlap between the two matrices and
|
||||
// returns the number of rows and columns it copied. If a aliases the receiver
|
||||
// and is a transposed Dense or VecDense, with a non-unitary increment, Copy will
|
||||
// panic.
|
||||
//
|
||||
// See the Copier interface for more information.
|
||||
func (m *CDense) Copy(a CMatrix) (r, c int) {
|
||||
r, c = a.Dims()
|
||||
if a == m {
|
||||
return r, c
|
||||
}
|
||||
r = min(r, m.mat.Rows)
|
||||
c = min(c, m.mat.Cols)
|
||||
if r == 0 || c == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
// TODO(btracey): Check for overlap when complex version exists.
|
||||
// TODO(btracey): Add fast-paths.
|
||||
for i := 0; i < r; i++ {
|
||||
for j := 0; j < c; j++ {
|
||||
m.set(i, j, a.At(i, j))
|
||||
}
|
||||
}
|
||||
return r, c
|
||||
}
|
||||
673
vendor/gonum.org/v1/gonum/mat/cholesky.go
generated
vendored
Normal file
673
vendor/gonum.org/v1/gonum/mat/cholesky.go
generated
vendored
Normal file
|
|
@ -0,0 +1,673 @@
|
|||
// Copyright ©2013 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
const (
|
||||
badTriangle = "mat: invalid triangle"
|
||||
badCholesky = "mat: invalid Cholesky factorization"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Matrix = (*Cholesky)(nil)
|
||||
_ Symmetric = (*Cholesky)(nil)
|
||||
)
|
||||
|
||||
// Cholesky is a symmetric positive definite matrix represented by its
|
||||
// Cholesky decomposition.
|
||||
//
|
||||
// The decomposition can be constructed using the Factorize method. The
|
||||
// factorization itself can be extracted using the UTo or LTo methods, and the
|
||||
// original symmetric matrix can be recovered with ToSym.
|
||||
//
|
||||
// Note that this matrix representation is useful for certain operations, in
|
||||
// particular finding solutions to linear equations. It is very inefficient
|
||||
// at other operations, in particular At is slow.
|
||||
//
|
||||
// Cholesky methods may only be called on a value that has been successfully
|
||||
// initialized by a call to Factorize that has returned true. Calls to methods
|
||||
// of an unsuccessful Cholesky factorization will panic.
|
||||
type Cholesky struct {
|
||||
// The chol pointer must never be retained as a pointer outside the Cholesky
|
||||
// struct, either by returning chol outside the struct or by setting it to
|
||||
// a pointer coming from outside. The same prohibition applies to the data
|
||||
// slice within chol.
|
||||
chol *TriDense
|
||||
cond float64
|
||||
}
|
||||
|
||||
// updateCond updates the condition number of the Cholesky decomposition. If
|
||||
// norm > 0, then that norm is used as the norm of the original matrix A, otherwise
|
||||
// the norm is estimated from the decomposition.
|
||||
func (c *Cholesky) updateCond(norm float64) {
|
||||
n := c.chol.mat.N
|
||||
work := getFloats(3*n, false)
|
||||
defer putFloats(work)
|
||||
if norm < 0 {
|
||||
// This is an approximation. By the definition of a norm,
|
||||
// |AB| <= |A| |B|.
|
||||
// Since A = U^T*U, we get for the condition number κ that
|
||||
// κ(A) := |A| |A^-1| = |U^T*U| |A^-1| <= |U^T| |U| |A^-1|,
|
||||
// so this will overestimate the condition number somewhat.
|
||||
// The norm of the original factorized matrix cannot be stored
|
||||
// because of update possibilities.
|
||||
unorm := lapack64.Lantr(CondNorm, c.chol.mat, work)
|
||||
lnorm := lapack64.Lantr(CondNormTrans, c.chol.mat, work)
|
||||
norm = unorm * lnorm
|
||||
}
|
||||
sym := c.chol.asSymBlas()
|
||||
iwork := getInts(n, false)
|
||||
v := lapack64.Pocon(sym, norm, work, iwork)
|
||||
putInts(iwork)
|
||||
c.cond = 1 / v
|
||||
}
|
||||
|
||||
// Dims returns the dimensions of the matrix.
|
||||
func (ch *Cholesky) Dims() (r, c int) {
|
||||
if !ch.valid() {
|
||||
panic(badCholesky)
|
||||
}
|
||||
r, c = ch.chol.Dims()
|
||||
return r, c
|
||||
}
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (c *Cholesky) At(i, j int) float64 {
|
||||
if !c.valid() {
|
||||
panic(badCholesky)
|
||||
}
|
||||
n := c.Symmetric()
|
||||
if uint(i) >= uint(n) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(n) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
|
||||
var val float64
|
||||
for k := 0; k <= min(i, j); k++ {
|
||||
val += c.chol.at(k, i) * c.chol.at(k, j)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// T returns the the receiver, the transpose of a symmetric matrix.
|
||||
func (c *Cholesky) T() Matrix {
|
||||
return c
|
||||
}
|
||||
|
||||
// Symmetric implements the Symmetric interface and returns the number of rows
|
||||
// in the matrix (this is also the number of columns).
|
||||
func (c *Cholesky) Symmetric() int {
|
||||
r, _ := c.chol.Dims()
|
||||
return r
|
||||
}
|
||||
|
||||
// Cond returns the condition number of the factorized matrix.
|
||||
func (c *Cholesky) Cond() float64 {
|
||||
if !c.valid() {
|
||||
panic(badCholesky)
|
||||
}
|
||||
return c.cond
|
||||
}
|
||||
|
||||
// Factorize calculates the Cholesky decomposition of the matrix A and returns
|
||||
// whether the matrix is positive definite. If Factorize returns false, the
|
||||
// factorization must not be used.
|
||||
func (c *Cholesky) Factorize(a Symmetric) (ok bool) {
|
||||
n := a.Symmetric()
|
||||
if c.chol == nil {
|
||||
c.chol = NewTriDense(n, Upper, nil)
|
||||
} else {
|
||||
c.chol = NewTriDense(n, Upper, use(c.chol.mat.Data, n*n))
|
||||
}
|
||||
copySymIntoTriangle(c.chol, a)
|
||||
|
||||
sym := c.chol.asSymBlas()
|
||||
work := getFloats(c.chol.mat.N, false)
|
||||
norm := lapack64.Lansy(CondNorm, sym, work)
|
||||
putFloats(work)
|
||||
_, ok = lapack64.Potrf(sym)
|
||||
if ok {
|
||||
c.updateCond(norm)
|
||||
} else {
|
||||
c.Reset()
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// Reset resets the factorization so that it can be reused as the receiver of a
|
||||
// dimensionally restricted operation.
|
||||
func (c *Cholesky) Reset() {
|
||||
if c.chol != nil {
|
||||
c.chol.Reset()
|
||||
}
|
||||
c.cond = math.Inf(1)
|
||||
}
|
||||
|
||||
// SetFromU sets the Cholesky decomposition from the given triangular matrix.
|
||||
// SetFromU panics if t is not upper triangular. Note that t is copied into,
|
||||
// not stored inside, the receiver.
|
||||
func (c *Cholesky) SetFromU(t *TriDense) {
|
||||
n, kind := t.Triangle()
|
||||
if kind != Upper {
|
||||
panic("cholesky: matrix must be upper triangular")
|
||||
}
|
||||
if c.chol == nil {
|
||||
c.chol = NewTriDense(n, Upper, nil)
|
||||
} else {
|
||||
c.chol = NewTriDense(n, Upper, use(c.chol.mat.Data, n*n))
|
||||
}
|
||||
c.chol.Copy(t)
|
||||
c.updateCond(-1)
|
||||
}
|
||||
|
||||
// Clone makes a copy of the input Cholesky into the receiver, overwriting the
|
||||
// previous value of the receiver. Clone does not place any restrictions on receiver
|
||||
// shape. Clone panics if the input Cholesky is not the result of a valid decomposition.
|
||||
func (c *Cholesky) Clone(chol *Cholesky) {
|
||||
if !chol.valid() {
|
||||
panic(badCholesky)
|
||||
}
|
||||
n := chol.Symmetric()
|
||||
if c.chol == nil {
|
||||
c.chol = NewTriDense(n, Upper, nil)
|
||||
} else {
|
||||
c.chol = NewTriDense(n, Upper, use(c.chol.mat.Data, n*n))
|
||||
}
|
||||
c.chol.Copy(chol.chol)
|
||||
c.cond = chol.cond
|
||||
}
|
||||
|
||||
// Det returns the determinant of the matrix that has been factorized.
|
||||
func (c *Cholesky) Det() float64 {
|
||||
if !c.valid() {
|
||||
panic(badCholesky)
|
||||
}
|
||||
return math.Exp(c.LogDet())
|
||||
}
|
||||
|
||||
// LogDet returns the log of the determinant of the matrix that has been factorized.
|
||||
func (c *Cholesky) LogDet() float64 {
|
||||
if !c.valid() {
|
||||
panic(badCholesky)
|
||||
}
|
||||
var det float64
|
||||
for i := 0; i < c.chol.mat.N; i++ {
|
||||
det += 2 * math.Log(c.chol.mat.Data[i*c.chol.mat.Stride+i])
|
||||
}
|
||||
return det
|
||||
}
|
||||
|
||||
// SolveTo finds the matrix X that solves A * X = B where A is represented
|
||||
// by the Cholesky decomposition. The result is stored in-place into dst.
|
||||
func (c *Cholesky) SolveTo(dst *Dense, b Matrix) error {
|
||||
if !c.valid() {
|
||||
panic(badCholesky)
|
||||
}
|
||||
n := c.chol.mat.N
|
||||
bm, bn := b.Dims()
|
||||
if n != bm {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
dst.reuseAs(bm, bn)
|
||||
if b != dst {
|
||||
dst.Copy(b)
|
||||
}
|
||||
lapack64.Potrs(c.chol.mat, dst.mat)
|
||||
if c.cond > ConditionTolerance {
|
||||
return Condition(c.cond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SolveCholTo finds the matrix X that solves A * X = B where A and B are represented
|
||||
// by their Cholesky decompositions a and b. The result is stored in-place into
|
||||
// dst.
|
||||
func (a *Cholesky) SolveCholTo(dst *Dense, b *Cholesky) error {
|
||||
if !a.valid() || !b.valid() {
|
||||
panic(badCholesky)
|
||||
}
|
||||
bn := b.chol.mat.N
|
||||
if a.chol.mat.N != bn {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
dst.reuseAsZeroed(bn, bn)
|
||||
dst.Copy(b.chol.T())
|
||||
blas64.Trsm(blas.Left, blas.Trans, 1, a.chol.mat, dst.mat)
|
||||
blas64.Trsm(blas.Left, blas.NoTrans, 1, a.chol.mat, dst.mat)
|
||||
blas64.Trmm(blas.Right, blas.NoTrans, 1, b.chol.mat, dst.mat)
|
||||
if a.cond > ConditionTolerance {
|
||||
return Condition(a.cond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SolveVecTo finds the vector X that solves A * x = b where A is represented
|
||||
// by the Cholesky decomposition. The result is stored in-place into
|
||||
// dst.
|
||||
func (c *Cholesky) SolveVecTo(dst *VecDense, b Vector) error {
|
||||
if !c.valid() {
|
||||
panic(badCholesky)
|
||||
}
|
||||
n := c.chol.mat.N
|
||||
if br, bc := b.Dims(); br != n || bc != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
switch rv := b.(type) {
|
||||
default:
|
||||
dst.reuseAs(n)
|
||||
return c.SolveTo(dst.asDense(), b)
|
||||
case RawVectorer:
|
||||
bmat := rv.RawVector()
|
||||
if dst != b {
|
||||
dst.checkOverlap(bmat)
|
||||
}
|
||||
dst.reuseAs(n)
|
||||
if dst != b {
|
||||
dst.CopyVec(b)
|
||||
}
|
||||
lapack64.Potrs(c.chol.mat, dst.asGeneral())
|
||||
if c.cond > ConditionTolerance {
|
||||
return Condition(c.cond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// RawU returns the Triangular matrix used to store the Cholesky decomposition of
|
||||
// the original matrix A. The returned matrix should not be modified. If it is
|
||||
// modified, the decomposition is invalid and should not be used.
|
||||
func (c *Cholesky) RawU() Triangular {
|
||||
return c.chol
|
||||
}
|
||||
|
||||
// UTo extracts the n×n upper triangular matrix U from a Cholesky
|
||||
// decomposition into dst and returns the result. If dst is nil a new
|
||||
// TriDense is allocated.
|
||||
// A = U^T * U.
|
||||
func (c *Cholesky) UTo(dst *TriDense) *TriDense {
|
||||
if !c.valid() {
|
||||
panic(badCholesky)
|
||||
}
|
||||
n := c.chol.mat.N
|
||||
if dst == nil {
|
||||
dst = NewTriDense(n, Upper, make([]float64, n*n))
|
||||
} else {
|
||||
dst.reuseAs(n, Upper)
|
||||
}
|
||||
dst.Copy(c.chol)
|
||||
return dst
|
||||
}
|
||||
|
||||
// LTo extracts the n×n lower triangular matrix L from a Cholesky
|
||||
// decomposition into dst and returns the result. If dst is nil a new
|
||||
// TriDense is allocated.
|
||||
// A = L * L^T.
|
||||
func (c *Cholesky) LTo(dst *TriDense) *TriDense {
|
||||
if !c.valid() {
|
||||
panic(badCholesky)
|
||||
}
|
||||
n := c.chol.mat.N
|
||||
if dst == nil {
|
||||
dst = NewTriDense(n, Lower, make([]float64, n*n))
|
||||
} else {
|
||||
dst.reuseAs(n, Lower)
|
||||
}
|
||||
dst.Copy(c.chol.TTri())
|
||||
return dst
|
||||
}
|
||||
|
||||
// ToSym reconstructs the original positive definite matrix given its
|
||||
// Cholesky decomposition into dst and returns the result. If dst is nil
|
||||
// a new SymDense is allocated.
|
||||
func (c *Cholesky) ToSym(dst *SymDense) *SymDense {
|
||||
if !c.valid() {
|
||||
panic(badCholesky)
|
||||
}
|
||||
n := c.chol.mat.N
|
||||
if dst == nil {
|
||||
dst = NewSymDense(n, nil)
|
||||
} else {
|
||||
dst.reuseAs(n)
|
||||
}
|
||||
// Create a TriDense representing the Cholesky factor U with dst's
|
||||
// backing slice.
|
||||
// Operations on u are reflected in s.
|
||||
u := &TriDense{
|
||||
mat: blas64.Triangular{
|
||||
Uplo: blas.Upper,
|
||||
Diag: blas.NonUnit,
|
||||
N: n,
|
||||
Data: dst.mat.Data,
|
||||
Stride: dst.mat.Stride,
|
||||
},
|
||||
cap: n,
|
||||
}
|
||||
u.Copy(c.chol)
|
||||
// Compute the product U^T*U using the algorithm from LAPACK/TESTING/LIN/dpot01.f
|
||||
a := u.mat.Data
|
||||
lda := u.mat.Stride
|
||||
bi := blas64.Implementation()
|
||||
for k := n - 1; k >= 0; k-- {
|
||||
a[k*lda+k] = bi.Ddot(k+1, a[k:], lda, a[k:], lda)
|
||||
if k > 0 {
|
||||
bi.Dtrmv(blas.Upper, blas.Trans, blas.NonUnit, k, a, lda, a[k:], lda)
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// InverseTo computes the inverse of the matrix represented by its Cholesky
|
||||
// factorization and stores the result into s. If the factorized
|
||||
// matrix is ill-conditioned, a Condition error will be returned.
|
||||
// Note that matrix inversion is numerically unstable, and should generally be
|
||||
// avoided where possible, for example by using the Solve routines.
|
||||
func (c *Cholesky) InverseTo(s *SymDense) error {
|
||||
if !c.valid() {
|
||||
panic(badCholesky)
|
||||
}
|
||||
s.reuseAs(c.chol.mat.N)
|
||||
// Create a TriDense representing the Cholesky factor U with the backing
|
||||
// slice from s.
|
||||
// Operations on u are reflected in s.
|
||||
u := &TriDense{
|
||||
mat: blas64.Triangular{
|
||||
Uplo: blas.Upper,
|
||||
Diag: blas.NonUnit,
|
||||
N: s.mat.N,
|
||||
Data: s.mat.Data,
|
||||
Stride: s.mat.Stride,
|
||||
},
|
||||
cap: s.mat.N,
|
||||
}
|
||||
u.Copy(c.chol)
|
||||
|
||||
_, ok := lapack64.Potri(u.mat)
|
||||
if !ok {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
if c.cond > ConditionTolerance {
|
||||
return Condition(c.cond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Scale multiplies the original matrix A by a positive constant using
|
||||
// its Cholesky decomposition, storing the result in-place into the receiver.
|
||||
// That is, if the original Cholesky factorization is
|
||||
// U^T * U = A
|
||||
// the updated factorization is
|
||||
// U'^T * U' = f A = A'
|
||||
// Scale panics if the constant is non-positive, or if the receiver is non-zero
|
||||
// and is of a different size from the input.
|
||||
func (c *Cholesky) Scale(f float64, orig *Cholesky) {
|
||||
if !orig.valid() {
|
||||
panic(badCholesky)
|
||||
}
|
||||
if f <= 0 {
|
||||
panic("cholesky: scaling by a non-positive constant")
|
||||
}
|
||||
n := orig.Symmetric()
|
||||
if c.chol == nil {
|
||||
c.chol = NewTriDense(n, Upper, nil)
|
||||
} else if c.chol.mat.N != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
c.chol.ScaleTri(math.Sqrt(f), orig.chol)
|
||||
c.cond = orig.cond // Scaling by a positive constant does not change the condition number.
|
||||
}
|
||||
|
||||
// ExtendVecSym computes the Cholesky decomposition of the original matrix A,
|
||||
// whose Cholesky decomposition is in a, extended by a the n×1 vector v according to
|
||||
// [A w]
|
||||
// [w' k]
|
||||
// where k = v[n-1] and w = v[:n-1]. The result is stored into the receiver.
|
||||
// In order for the updated matrix to be positive definite, it must be the case
|
||||
// that k > w' A^-1 w. If this condition does not hold then ExtendVecSym will
|
||||
// return false and the receiver will not be updated.
|
||||
//
|
||||
// ExtendVecSym will panic if v.Len() != a.Symmetric()+1 or if a does not contain
|
||||
// a valid decomposition.
|
||||
func (c *Cholesky) ExtendVecSym(a *Cholesky, v Vector) (ok bool) {
|
||||
n := a.Symmetric()
|
||||
|
||||
if v.Len() != n+1 {
|
||||
panic(badSliceLength)
|
||||
}
|
||||
if !a.valid() {
|
||||
panic(badCholesky)
|
||||
}
|
||||
|
||||
// The algorithm is commented here, but see also
|
||||
// https://math.stackexchange.com/questions/955874/cholesky-factor-when-adding-a-row-and-column-to-already-factorized-matrix
|
||||
// We have A and want to compute the Cholesky of
|
||||
// [A w]
|
||||
// [w' k]
|
||||
// We want
|
||||
// [U c]
|
||||
// [0 d]
|
||||
// to be the updated Cholesky, and so it must be that
|
||||
// [A w] = [U' 0] [U c]
|
||||
// [w' k] [c' d] [0 d]
|
||||
// Thus, we need
|
||||
// 1) A = U'U (true by the original decomposition being valid),
|
||||
// 2) U' * c = w => c = U'^-1 w
|
||||
// 3) c'*c + d'*d = k => d = sqrt(k-c'*c)
|
||||
|
||||
// First, compute c = U'^-1 a
|
||||
// TODO(btracey): Replace this with CopyVec when issue 167 is fixed.
|
||||
w := NewVecDense(n, nil)
|
||||
for i := 0; i < n; i++ {
|
||||
w.SetVec(i, v.At(i, 0))
|
||||
}
|
||||
k := v.At(n, 0)
|
||||
|
||||
var t VecDense
|
||||
t.SolveVec(a.chol.T(), w)
|
||||
|
||||
dot := Dot(&t, &t)
|
||||
if dot >= k {
|
||||
return false
|
||||
}
|
||||
d := math.Sqrt(k - dot)
|
||||
|
||||
newU := NewTriDense(n+1, Upper, nil)
|
||||
newU.Copy(a.chol)
|
||||
for i := 0; i < n; i++ {
|
||||
newU.SetTri(i, n, t.At(i, 0))
|
||||
}
|
||||
newU.SetTri(n, n, d)
|
||||
c.chol = newU
|
||||
c.updateCond(-1)
|
||||
return true
|
||||
}
|
||||
|
||||
// SymRankOne performs a rank-1 update of the original matrix A and refactorizes
|
||||
// its Cholesky factorization, storing the result into the receiver. That is, if
|
||||
// in the original Cholesky factorization
|
||||
// U^T * U = A,
|
||||
// in the updated factorization
|
||||
// U'^T * U' = A + alpha * x * x^T = A'.
|
||||
//
|
||||
// Note that when alpha is negative, the updating problem may be ill-conditioned
|
||||
// and the results may be inaccurate, or the updated matrix A' may not be
|
||||
// positive definite and not have a Cholesky factorization. SymRankOne returns
|
||||
// whether the updated matrix A' is positive definite.
|
||||
//
|
||||
// SymRankOne updates a Cholesky factorization in O(n²) time. The Cholesky
|
||||
// factorization computation from scratch is O(n³).
|
||||
func (c *Cholesky) SymRankOne(orig *Cholesky, alpha float64, x Vector) (ok bool) {
|
||||
if !orig.valid() {
|
||||
panic(badCholesky)
|
||||
}
|
||||
n := orig.Symmetric()
|
||||
if r, c := x.Dims(); r != n || c != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if orig != c {
|
||||
if c.chol == nil {
|
||||
c.chol = NewTriDense(n, Upper, nil)
|
||||
} else if c.chol.mat.N != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
c.chol.Copy(orig.chol)
|
||||
}
|
||||
|
||||
if alpha == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// Algorithms for updating and downdating the Cholesky factorization are
|
||||
// described, for example, in
|
||||
// - J. J. Dongarra, J. R. Bunch, C. B. Moler, G. W. Stewart: LINPACK
|
||||
// Users' Guide. SIAM (1979), pages 10.10--10.14
|
||||
// or
|
||||
// - P. E. Gill, G. H. Golub, W. Murray, and M. A. Saunders: Methods for
|
||||
// modifying matrix factorizations. Mathematics of Computation 28(126)
|
||||
// (1974), Method C3 on page 521
|
||||
//
|
||||
// The implementation is based on LINPACK code
|
||||
// http://www.netlib.org/linpack/dchud.f
|
||||
// http://www.netlib.org/linpack/dchdd.f
|
||||
// and
|
||||
// https://icl.cs.utk.edu/lapack-forum/viewtopic.php?f=2&t=2646
|
||||
//
|
||||
// According to http://icl.cs.utk.edu/lapack-forum/archives/lapack/msg00301.html
|
||||
// LINPACK is released under BSD license.
|
||||
//
|
||||
// See also:
|
||||
// - M. A. Saunders: Large-scale Linear Programming Using the Cholesky
|
||||
// Factorization. Technical Report Stanford University (1972)
|
||||
// http://i.stanford.edu/pub/cstr/reports/cs/tr/72/252/CS-TR-72-252.pdf
|
||||
// - Matthias Seeger: Low rank updates for the Cholesky decomposition.
|
||||
// EPFL Technical Report 161468 (2004)
|
||||
// http://infoscience.epfl.ch/record/161468
|
||||
|
||||
work := getFloats(n, false)
|
||||
defer putFloats(work)
|
||||
var xmat blas64.Vector
|
||||
if rv, ok := x.(RawVectorer); ok {
|
||||
xmat = rv.RawVector()
|
||||
} else {
|
||||
var tmp *VecDense
|
||||
tmp.CopyVec(x)
|
||||
xmat = tmp.RawVector()
|
||||
}
|
||||
blas64.Copy(xmat, blas64.Vector{N: n, Data: work, Inc: 1})
|
||||
|
||||
if alpha > 0 {
|
||||
// Compute rank-1 update.
|
||||
if alpha != 1 {
|
||||
blas64.Scal(math.Sqrt(alpha), blas64.Vector{N: n, Data: work, Inc: 1})
|
||||
}
|
||||
umat := c.chol.mat
|
||||
stride := umat.Stride
|
||||
for i := 0; i < n; i++ {
|
||||
// Compute parameters of the Givens matrix that zeroes
|
||||
// the i-th element of x.
|
||||
c, s, r, _ := blas64.Rotg(umat.Data[i*stride+i], work[i])
|
||||
if r < 0 {
|
||||
// Multiply by -1 to have positive diagonal
|
||||
// elemnts.
|
||||
r *= -1
|
||||
c *= -1
|
||||
s *= -1
|
||||
}
|
||||
umat.Data[i*stride+i] = r
|
||||
if i < n-1 {
|
||||
// Multiply the extended factorization matrix by
|
||||
// the Givens matrix from the left. Only
|
||||
// the i-th row and x are modified.
|
||||
blas64.Rot(
|
||||
blas64.Vector{N: n - i - 1, Data: umat.Data[i*stride+i+1 : i*stride+n], Inc: 1},
|
||||
blas64.Vector{N: n - i - 1, Data: work[i+1 : n], Inc: 1},
|
||||
c, s)
|
||||
}
|
||||
}
|
||||
c.updateCond(-1)
|
||||
return true
|
||||
}
|
||||
|
||||
// Compute rank-1 downdate.
|
||||
alpha = math.Sqrt(-alpha)
|
||||
if alpha != 1 {
|
||||
blas64.Scal(alpha, blas64.Vector{N: n, Data: work, Inc: 1})
|
||||
}
|
||||
// Solve U^T * p = x storing the result into work.
|
||||
ok = lapack64.Trtrs(blas.Trans, c.chol.RawTriangular(), blas64.General{
|
||||
Rows: n,
|
||||
Cols: 1,
|
||||
Stride: 1,
|
||||
Data: work,
|
||||
})
|
||||
if !ok {
|
||||
// The original matrix is singular. Should not happen, because
|
||||
// the factorization is valid.
|
||||
panic(badCholesky)
|
||||
}
|
||||
norm := blas64.Nrm2(blas64.Vector{N: n, Data: work, Inc: 1})
|
||||
if norm >= 1 {
|
||||
// The updated matrix is not positive definite.
|
||||
return false
|
||||
}
|
||||
norm = math.Sqrt((1 + norm) * (1 - norm))
|
||||
cos := getFloats(n, false)
|
||||
defer putFloats(cos)
|
||||
sin := getFloats(n, false)
|
||||
defer putFloats(sin)
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
// Compute parameters of Givens matrices that zero elements of p
|
||||
// backwards.
|
||||
cos[i], sin[i], norm, _ = blas64.Rotg(norm, work[i])
|
||||
if norm < 0 {
|
||||
norm *= -1
|
||||
cos[i] *= -1
|
||||
sin[i] *= -1
|
||||
}
|
||||
}
|
||||
umat := c.chol.mat
|
||||
stride := umat.Stride
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
work[i] = 0
|
||||
// Apply Givens matrices to U.
|
||||
// TODO(vladimir-ch): Use workspace to avoid modifying the
|
||||
// receiver in case an invalid factorization is created.
|
||||
blas64.Rot(
|
||||
blas64.Vector{N: n - i, Data: work[i:n], Inc: 1},
|
||||
blas64.Vector{N: n - i, Data: umat.Data[i*stride+i : i*stride+n], Inc: 1},
|
||||
cos[i], sin[i])
|
||||
if umat.Data[i*stride+i] == 0 {
|
||||
// The matrix is singular (may rarely happen due to
|
||||
// floating-point effects?).
|
||||
ok = false
|
||||
} else if umat.Data[i*stride+i] < 0 {
|
||||
// Diagonal elements should be positive. If it happens
|
||||
// that on the i-th row the diagonal is negative,
|
||||
// multiply U from the left by an identity matrix that
|
||||
// has -1 on the i-th row.
|
||||
blas64.Scal(-1, blas64.Vector{N: n - i, Data: umat.Data[i*stride+i : i*stride+n], Inc: 1})
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
c.updateCond(-1)
|
||||
} else {
|
||||
c.Reset()
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
func (c *Cholesky) valid() bool {
|
||||
return c.chol != nil && !c.chol.IsZero()
|
||||
}
|
||||
210
vendor/gonum.org/v1/gonum/mat/cmatrix.go
generated
vendored
Normal file
210
vendor/gonum.org/v1/gonum/mat/cmatrix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
// Copyright ©2013 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/cmplx"
|
||||
|
||||
"gonum.org/v1/gonum/floats"
|
||||
)
|
||||
|
||||
// CMatrix is the basic matrix interface type for complex matrices.
|
||||
type CMatrix interface {
|
||||
// Dims returns the dimensions of a Matrix.
|
||||
Dims() (r, c int)
|
||||
|
||||
// At returns the value of a matrix element at row i, column j.
|
||||
// It will panic if i or j are out of bounds for the matrix.
|
||||
At(i, j int) complex128
|
||||
|
||||
// H returns the conjugate transpose of the Matrix. Whether H
|
||||
// returns a copy of the underlying data is implementation dependent.
|
||||
// This method may be implemented using the Conjugate type, which
|
||||
// provides an implicit matrix conjugate transpose.
|
||||
H() CMatrix
|
||||
}
|
||||
|
||||
var (
|
||||
_ CMatrix = Conjugate{}
|
||||
_ Unconjugator = Conjugate{}
|
||||
)
|
||||
|
||||
// Conjugate is a type for performing an implicit matrix conjugate transpose.
|
||||
// It implements the Matrix interface, returning values from the conjugate
|
||||
// transpose of the matrix within.
|
||||
type Conjugate struct {
|
||||
CMatrix CMatrix
|
||||
}
|
||||
|
||||
// At returns the value of the element at row i and column j of the conjugate
|
||||
// transposed matrix, that is, row j and column i of the Matrix field.
|
||||
func (t Conjugate) At(i, j int) complex128 {
|
||||
z := t.CMatrix.At(j, i)
|
||||
return cmplx.Conj(z)
|
||||
}
|
||||
|
||||
// Dims returns the dimensions of the transposed matrix. The number of rows returned
|
||||
// is the number of columns in the Matrix field, and the number of columns is
|
||||
// the number of rows in the Matrix field.
|
||||
func (t Conjugate) Dims() (r, c int) {
|
||||
c, r = t.CMatrix.Dims()
|
||||
return r, c
|
||||
}
|
||||
|
||||
// H performs an implicit conjugate transpose by returning the Matrix field.
|
||||
func (t Conjugate) H() CMatrix {
|
||||
return t.CMatrix
|
||||
}
|
||||
|
||||
// Unconjugate returns the Matrix field.
|
||||
func (t Conjugate) Unconjugate() CMatrix {
|
||||
return t.CMatrix
|
||||
}
|
||||
|
||||
// Unconjugator is a type that can undo an implicit conjugate transpose.
|
||||
type Unconjugator interface {
|
||||
// Note: This interface is needed to unify all of the Conjugate types. In
|
||||
// the cmat128 methods, we need to test if the Matrix has been implicitly
|
||||
// transposed. If this is checked by testing for the specific Conjugate type
|
||||
// then the behavior will be different if the user uses H() or HTri() for a
|
||||
// triangular matrix.
|
||||
|
||||
// Unconjugate returns the underlying Matrix stored for the implicit
|
||||
// conjugate transpose.
|
||||
Unconjugate() CMatrix
|
||||
}
|
||||
|
||||
// useC returns a complex128 slice with l elements, using c if it
|
||||
// has the necessary capacity, otherwise creating a new slice.
|
||||
func useC(c []complex128, l int) []complex128 {
|
||||
if l <= cap(c) {
|
||||
return c[:l]
|
||||
}
|
||||
return make([]complex128, l)
|
||||
}
|
||||
|
||||
// useZeroedC returns a complex128 slice with l elements, using c if it
|
||||
// has the necessary capacity, otherwise creating a new slice. The
|
||||
// elements of the returned slice are guaranteed to be zero.
|
||||
func useZeroedC(c []complex128, l int) []complex128 {
|
||||
if l <= cap(c) {
|
||||
c = c[:l]
|
||||
zeroC(c)
|
||||
return c
|
||||
}
|
||||
return make([]complex128, l)
|
||||
}
|
||||
|
||||
// zeroC zeros the given slice's elements.
|
||||
func zeroC(c []complex128) {
|
||||
for i := range c {
|
||||
c[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// unconjugate unconjugates a matrix if applicable. If a is an Unconjugator, then
|
||||
// unconjugate returns the underlying matrix and true. If it is not, then it returns
|
||||
// the input matrix and false.
|
||||
func unconjugate(a CMatrix) (CMatrix, bool) {
|
||||
if ut, ok := a.(Unconjugator); ok {
|
||||
return ut.Unconjugate(), true
|
||||
}
|
||||
return a, false
|
||||
}
|
||||
|
||||
// CEqual returns whether the matrices a and b have the same size
|
||||
// and are element-wise equal.
|
||||
func CEqual(a, b CMatrix) bool {
|
||||
ar, ac := a.Dims()
|
||||
br, bc := b.Dims()
|
||||
if ar != br || ac != bc {
|
||||
return false
|
||||
}
|
||||
// TODO(btracey): Add in fast-paths.
|
||||
for i := 0; i < ar; i++ {
|
||||
for j := 0; j < ac; j++ {
|
||||
if a.At(i, j) != b.At(i, j) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// CEqualApprox returns whether the matrices a and b have the same size and contain all equal
|
||||
// elements with tolerance for element-wise equality specified by epsilon. Matrices
|
||||
// with non-equal shapes are not equal.
|
||||
func CEqualApprox(a, b CMatrix, epsilon float64) bool {
|
||||
// TODO(btracey):
|
||||
ar, ac := a.Dims()
|
||||
br, bc := b.Dims()
|
||||
if ar != br || ac != bc {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < ar; i++ {
|
||||
for j := 0; j < ac; j++ {
|
||||
if !cEqualWithinAbsOrRel(a.At(i, j), b.At(i, j), epsilon, epsilon) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// TODO(btracey): Move these into a cmplxs if/when we have one.
|
||||
|
||||
func cEqualWithinAbsOrRel(a, b complex128, absTol, relTol float64) bool {
|
||||
if cEqualWithinAbs(a, b, absTol) {
|
||||
return true
|
||||
}
|
||||
return cEqualWithinRel(a, b, relTol)
|
||||
}
|
||||
|
||||
// cEqualWithinAbs returns true if a and b have an absolute
|
||||
// difference of less than tol.
|
||||
func cEqualWithinAbs(a, b complex128, tol float64) bool {
|
||||
return a == b || cmplx.Abs(a-b) <= tol
|
||||
}
|
||||
|
||||
const minNormalFloat64 = 2.2250738585072014e-308
|
||||
|
||||
// cEqualWithinRel returns true if the difference between a and b
|
||||
// is not greater than tol times the greater value.
|
||||
func cEqualWithinRel(a, b complex128, tol float64) bool {
|
||||
if a == b {
|
||||
return true
|
||||
}
|
||||
if cmplx.IsNaN(a) || cmplx.IsNaN(b) {
|
||||
return false
|
||||
}
|
||||
// Cannot play the same trick as in floats because there are multiple
|
||||
// possible infinities.
|
||||
if cmplx.IsInf(a) {
|
||||
if !cmplx.IsInf(b) {
|
||||
return false
|
||||
}
|
||||
ra := real(a)
|
||||
if math.IsInf(ra, 0) {
|
||||
if ra == real(b) {
|
||||
return floats.EqualWithinRel(imag(a), imag(b), tol)
|
||||
}
|
||||
return false
|
||||
}
|
||||
if imag(a) == imag(b) {
|
||||
return floats.EqualWithinRel(ra, real(b), tol)
|
||||
}
|
||||
return false
|
||||
}
|
||||
if cmplx.IsInf(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
delta := cmplx.Abs(a - b)
|
||||
if delta <= minNormalFloat64 {
|
||||
return delta <= tol*minNormalFloat64
|
||||
}
|
||||
return delta/math.Max(cmplx.Abs(a), cmplx.Abs(b)) <= tol
|
||||
}
|
||||
15
vendor/gonum.org/v1/gonum/mat/consts.go
generated
vendored
Normal file
15
vendor/gonum.org/v1/gonum/mat/consts.go
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright ©2016 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
// TriKind represents the triangularity of the matrix.
|
||||
type TriKind bool
|
||||
|
||||
const (
|
||||
// Upper specifies an upper triangular matrix.
|
||||
Upper TriKind = true
|
||||
// Lower specifies a lower triangular matrix.
|
||||
Lower TriKind = false
|
||||
)
|
||||
558
vendor/gonum.org/v1/gonum/mat/dense.go
generated
vendored
Normal file
558
vendor/gonum.org/v1/gonum/mat/dense.go
generated
vendored
Normal file
|
|
@ -0,0 +1,558 @@
|
|||
// Copyright ©2013 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
)
|
||||
|
||||
var (
|
||||
dense *Dense
|
||||
|
||||
_ Matrix = dense
|
||||
_ Mutable = dense
|
||||
|
||||
_ Cloner = dense
|
||||
_ RowViewer = dense
|
||||
_ ColViewer = dense
|
||||
_ RawRowViewer = dense
|
||||
_ Grower = dense
|
||||
|
||||
_ RawMatrixSetter = dense
|
||||
_ RawMatrixer = dense
|
||||
|
||||
_ Reseter = dense
|
||||
)
|
||||
|
||||
// Dense is a dense matrix representation.
|
||||
type Dense struct {
|
||||
mat blas64.General
|
||||
|
||||
capRows, capCols int
|
||||
}
|
||||
|
||||
// NewDense creates a new Dense matrix with r rows and c columns. If data == nil,
|
||||
// a new slice is allocated for the backing slice. If len(data) == r*c, data is
|
||||
// used as the backing slice, and changes to the elements of the returned Dense
|
||||
// will be reflected in data. If neither of these is true, NewDense will panic.
|
||||
// NewDense will panic if either r or c is zero.
|
||||
//
|
||||
// The data must be arranged in row-major order, i.e. the (i*c + j)-th
|
||||
// element in the data slice is the {i, j}-th element in the matrix.
|
||||
func NewDense(r, c int, data []float64) *Dense {
|
||||
if r <= 0 || c <= 0 {
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic("mat: negative dimension")
|
||||
}
|
||||
if data != nil && r*c != len(data) {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if data == nil {
|
||||
data = make([]float64, r*c)
|
||||
}
|
||||
return &Dense{
|
||||
mat: blas64.General{
|
||||
Rows: r,
|
||||
Cols: c,
|
||||
Stride: c,
|
||||
Data: data,
|
||||
},
|
||||
capRows: r,
|
||||
capCols: c,
|
||||
}
|
||||
}
|
||||
|
||||
// reuseAs resizes an empty matrix to a r×c matrix,
|
||||
// or checks that a non-empty matrix is r×c.
|
||||
//
|
||||
// reuseAs must be kept in sync with reuseAsZeroed.
|
||||
func (m *Dense) reuseAs(r, c int) {
|
||||
if m.mat.Rows > m.capRows || m.mat.Cols > m.capCols {
|
||||
// Panic as a string, not a mat.Error.
|
||||
panic("mat: caps not correctly set")
|
||||
}
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
if m.IsZero() {
|
||||
m.mat = blas64.General{
|
||||
Rows: r,
|
||||
Cols: c,
|
||||
Stride: c,
|
||||
Data: use(m.mat.Data, r*c),
|
||||
}
|
||||
m.capRows = r
|
||||
m.capCols = c
|
||||
return
|
||||
}
|
||||
if r != m.mat.Rows || c != m.mat.Cols {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
|
||||
// reuseAsZeroed resizes an empty matrix to a r×c matrix,
|
||||
// or checks that a non-empty matrix is r×c. It zeroes
|
||||
// all the elements of the matrix.
|
||||
//
|
||||
// reuseAsZeroed must be kept in sync with reuseAs.
|
||||
func (m *Dense) reuseAsZeroed(r, c int) {
|
||||
if m.mat.Rows > m.capRows || m.mat.Cols > m.capCols {
|
||||
// Panic as a string, not a mat.Error.
|
||||
panic("mat: caps not correctly set")
|
||||
}
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
if m.IsZero() {
|
||||
m.mat = blas64.General{
|
||||
Rows: r,
|
||||
Cols: c,
|
||||
Stride: c,
|
||||
Data: useZeroed(m.mat.Data, r*c),
|
||||
}
|
||||
m.capRows = r
|
||||
m.capCols = c
|
||||
return
|
||||
}
|
||||
if r != m.mat.Rows || c != m.mat.Cols {
|
||||
panic(ErrShape)
|
||||
}
|
||||
m.Zero()
|
||||
}
|
||||
|
||||
// Zero sets all of the matrix elements to zero.
|
||||
func (m *Dense) Zero() {
|
||||
r := m.mat.Rows
|
||||
c := m.mat.Cols
|
||||
for i := 0; i < r; i++ {
|
||||
zero(m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+c])
|
||||
}
|
||||
}
|
||||
|
||||
// isolatedWorkspace returns a new dense matrix w with the size of a and
|
||||
// returns a callback to defer which performs cleanup at the return of the call.
|
||||
// This should be used when a method receiver is the same pointer as an input argument.
|
||||
func (m *Dense) isolatedWorkspace(a Matrix) (w *Dense, restore func()) {
|
||||
r, c := a.Dims()
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
w = getWorkspace(r, c, false)
|
||||
return w, func() {
|
||||
m.Copy(w)
|
||||
putWorkspace(w)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset zeros the dimensions of the matrix so that it can be reused as the
|
||||
// receiver of a dimensionally restricted operation.
|
||||
//
|
||||
// See the Reseter interface for more information.
|
||||
func (m *Dense) Reset() {
|
||||
// Row, Cols and Stride must be zeroed in unison.
|
||||
m.mat.Rows, m.mat.Cols, m.mat.Stride = 0, 0, 0
|
||||
m.capRows, m.capCols = 0, 0
|
||||
m.mat.Data = m.mat.Data[:0]
|
||||
}
|
||||
|
||||
// IsZero returns whether the receiver is zero-sized. Zero-sized matrices can be the
|
||||
// receiver for size-restricted operations. Dense matrices can be zeroed using Reset.
|
||||
func (m *Dense) IsZero() bool {
|
||||
// It must be the case that m.Dims() returns
|
||||
// zeros in this case. See comment in Reset().
|
||||
return m.mat.Stride == 0
|
||||
}
|
||||
|
||||
// asTriDense returns a TriDense with the given size and side. The backing data
|
||||
// of the TriDense is the same as the receiver.
|
||||
func (m *Dense) asTriDense(n int, diag blas.Diag, uplo blas.Uplo) *TriDense {
|
||||
return &TriDense{
|
||||
mat: blas64.Triangular{
|
||||
N: n,
|
||||
Stride: m.mat.Stride,
|
||||
Data: m.mat.Data,
|
||||
Uplo: uplo,
|
||||
Diag: diag,
|
||||
},
|
||||
cap: n,
|
||||
}
|
||||
}
|
||||
|
||||
// DenseCopyOf returns a newly allocated copy of the elements of a.
|
||||
func DenseCopyOf(a Matrix) *Dense {
|
||||
d := &Dense{}
|
||||
d.Clone(a)
|
||||
return d
|
||||
}
|
||||
|
||||
// SetRawMatrix sets the underlying blas64.General used by the receiver.
|
||||
// Changes to elements in the receiver following the call will be reflected
|
||||
// in b.
|
||||
func (m *Dense) SetRawMatrix(b blas64.General) {
|
||||
m.capRows, m.capCols = b.Rows, b.Cols
|
||||
m.mat = b
|
||||
}
|
||||
|
||||
// RawMatrix returns the underlying blas64.General used by the receiver.
|
||||
// Changes to elements in the receiver following the call will be reflected
|
||||
// in returned blas64.General.
|
||||
func (m *Dense) RawMatrix() blas64.General { return m.mat }
|
||||
|
||||
// Dims returns the number of rows and columns in the matrix.
|
||||
func (m *Dense) Dims() (r, c int) { return m.mat.Rows, m.mat.Cols }
|
||||
|
||||
// Caps returns the number of rows and columns in the backing matrix.
|
||||
func (m *Dense) Caps() (r, c int) { return m.capRows, m.capCols }
|
||||
|
||||
// T performs an implicit transpose by returning the receiver inside a Transpose.
|
||||
func (m *Dense) T() Matrix {
|
||||
return Transpose{m}
|
||||
}
|
||||
|
||||
// ColView returns a Vector reflecting the column j, backed by the matrix data.
|
||||
//
|
||||
// See ColViewer for more information.
|
||||
func (m *Dense) ColView(j int) Vector {
|
||||
var v VecDense
|
||||
v.ColViewOf(m, j)
|
||||
return &v
|
||||
}
|
||||
|
||||
// SetCol sets the values in the specified column of the matrix to the values
|
||||
// in src. len(src) must equal the number of rows in the receiver.
|
||||
func (m *Dense) SetCol(j int, src []float64) {
|
||||
if j >= m.mat.Cols || j < 0 {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
if len(src) != m.mat.Rows {
|
||||
panic(ErrColLength)
|
||||
}
|
||||
|
||||
blas64.Copy(
|
||||
blas64.Vector{N: m.mat.Rows, Inc: 1, Data: src},
|
||||
blas64.Vector{N: m.mat.Rows, Inc: m.mat.Stride, Data: m.mat.Data[j:]},
|
||||
)
|
||||
}
|
||||
|
||||
// SetRow sets the values in the specified rows of the matrix to the values
|
||||
// in src. len(src) must equal the number of columns in the receiver.
|
||||
func (m *Dense) SetRow(i int, src []float64) {
|
||||
if i >= m.mat.Rows || i < 0 {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if len(src) != m.mat.Cols {
|
||||
panic(ErrRowLength)
|
||||
}
|
||||
|
||||
copy(m.rawRowView(i), src)
|
||||
}
|
||||
|
||||
// RowView returns row i of the matrix data represented as a column vector,
|
||||
// backed by the matrix data.
|
||||
//
|
||||
// See RowViewer for more information.
|
||||
func (m *Dense) RowView(i int) Vector {
|
||||
var v VecDense
|
||||
v.RowViewOf(m, i)
|
||||
return &v
|
||||
}
|
||||
|
||||
// RawRowView returns a slice backed by the same array as backing the
|
||||
// receiver.
|
||||
func (m *Dense) RawRowView(i int) []float64 {
|
||||
if i >= m.mat.Rows || i < 0 {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
return m.rawRowView(i)
|
||||
}
|
||||
|
||||
func (m *Dense) rawRowView(i int) []float64 {
|
||||
return m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+m.mat.Cols]
|
||||
}
|
||||
|
||||
// DiagView returns the diagonal as a matrix backed by the original data.
|
||||
func (m *Dense) DiagView() Diagonal {
|
||||
n := min(m.mat.Rows, m.mat.Cols)
|
||||
return &DiagDense{
|
||||
mat: blas64.Vector{
|
||||
N: n,
|
||||
Inc: m.mat.Stride + 1,
|
||||
Data: m.mat.Data[:(n-1)*m.mat.Stride+n],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Slice returns a new Matrix that shares backing data with the receiver.
|
||||
// The returned matrix starts at {i,j} of the receiver and extends k-i rows
|
||||
// and l-j columns. The final row in the resulting matrix is k-1 and the
|
||||
// final column is l-1.
|
||||
// Slice panics with ErrIndexOutOfRange if the slice is outside the capacity
|
||||
// of the receiver.
|
||||
func (m *Dense) Slice(i, k, j, l int) Matrix {
|
||||
mr, mc := m.Caps()
|
||||
if i < 0 || mr <= i || j < 0 || mc <= j || k < i || mr < k || l < j || mc < l {
|
||||
if i == k || j == l {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic(ErrIndexOutOfRange)
|
||||
}
|
||||
t := *m
|
||||
t.mat.Data = t.mat.Data[i*t.mat.Stride+j : (k-1)*t.mat.Stride+l]
|
||||
t.mat.Rows = k - i
|
||||
t.mat.Cols = l - j
|
||||
t.capRows -= i
|
||||
t.capCols -= j
|
||||
return &t
|
||||
}
|
||||
|
||||
// Grow returns the receiver expanded by r rows and c columns. If the dimensions
|
||||
// of the expanded matrix are outside the capacities of the receiver a new
|
||||
// allocation is made, otherwise not. Note the receiver itself is not modified
|
||||
// during the call to Grow.
|
||||
func (m *Dense) Grow(r, c int) Matrix {
|
||||
if r < 0 || c < 0 {
|
||||
panic(ErrIndexOutOfRange)
|
||||
}
|
||||
if r == 0 && c == 0 {
|
||||
return m
|
||||
}
|
||||
|
||||
r += m.mat.Rows
|
||||
c += m.mat.Cols
|
||||
|
||||
var t Dense
|
||||
switch {
|
||||
case m.mat.Rows == 0 || m.mat.Cols == 0:
|
||||
t.mat = blas64.General{
|
||||
Rows: r,
|
||||
Cols: c,
|
||||
Stride: c,
|
||||
// We zero because we don't know how the matrix will be used.
|
||||
// In other places, the mat is immediately filled with a result;
|
||||
// this is not the case here.
|
||||
Data: useZeroed(m.mat.Data, r*c),
|
||||
}
|
||||
case r > m.capRows || c > m.capCols:
|
||||
cr := max(r, m.capRows)
|
||||
cc := max(c, m.capCols)
|
||||
t.mat = blas64.General{
|
||||
Rows: r,
|
||||
Cols: c,
|
||||
Stride: cc,
|
||||
Data: make([]float64, cr*cc),
|
||||
}
|
||||
t.capRows = cr
|
||||
t.capCols = cc
|
||||
// Copy the complete matrix over to the new matrix.
|
||||
// Including elements not currently visible. Use a temporary structure
|
||||
// to avoid modifying the receiver.
|
||||
var tmp Dense
|
||||
tmp.mat = blas64.General{
|
||||
Rows: m.mat.Rows,
|
||||
Cols: m.mat.Cols,
|
||||
Stride: m.mat.Stride,
|
||||
Data: m.mat.Data,
|
||||
}
|
||||
tmp.capRows = m.capRows
|
||||
tmp.capCols = m.capCols
|
||||
t.Copy(&tmp)
|
||||
return &t
|
||||
default:
|
||||
t.mat = blas64.General{
|
||||
Data: m.mat.Data[:(r-1)*m.mat.Stride+c],
|
||||
Rows: r,
|
||||
Cols: c,
|
||||
Stride: m.mat.Stride,
|
||||
}
|
||||
}
|
||||
t.capRows = r
|
||||
t.capCols = c
|
||||
return &t
|
||||
}
|
||||
|
||||
// Clone makes a copy of a into the receiver, overwriting the previous value of
|
||||
// the receiver. The clone operation does not make any restriction on shape and
|
||||
// will not cause shadowing.
|
||||
//
|
||||
// See the Cloner interface for more information.
|
||||
func (m *Dense) Clone(a Matrix) {
|
||||
r, c := a.Dims()
|
||||
mat := blas64.General{
|
||||
Rows: r,
|
||||
Cols: c,
|
||||
Stride: c,
|
||||
}
|
||||
m.capRows, m.capCols = r, c
|
||||
|
||||
aU, trans := untranspose(a)
|
||||
switch aU := aU.(type) {
|
||||
case RawMatrixer:
|
||||
amat := aU.RawMatrix()
|
||||
mat.Data = make([]float64, r*c)
|
||||
if trans {
|
||||
for i := 0; i < r; i++ {
|
||||
blas64.Copy(blas64.Vector{N: c, Inc: amat.Stride, Data: amat.Data[i : i+(c-1)*amat.Stride+1]},
|
||||
blas64.Vector{N: c, Inc: 1, Data: mat.Data[i*c : (i+1)*c]})
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < r; i++ {
|
||||
copy(mat.Data[i*c:(i+1)*c], amat.Data[i*amat.Stride:i*amat.Stride+c])
|
||||
}
|
||||
}
|
||||
case *VecDense:
|
||||
amat := aU.mat
|
||||
mat.Data = make([]float64, aU.mat.N)
|
||||
blas64.Copy(blas64.Vector{N: aU.mat.N, Inc: amat.Inc, Data: amat.Data},
|
||||
blas64.Vector{N: aU.mat.N, Inc: 1, Data: mat.Data})
|
||||
default:
|
||||
mat.Data = make([]float64, r*c)
|
||||
w := *m
|
||||
w.mat = mat
|
||||
for i := 0; i < r; i++ {
|
||||
for j := 0; j < c; j++ {
|
||||
w.set(i, j, a.At(i, j))
|
||||
}
|
||||
}
|
||||
*m = w
|
||||
return
|
||||
}
|
||||
m.mat = mat
|
||||
}
|
||||
|
||||
// Copy makes a copy of elements of a into the receiver. It is similar to the
|
||||
// built-in copy; it copies as much as the overlap between the two matrices and
|
||||
// returns the number of rows and columns it copied. If a aliases the receiver
|
||||
// and is a transposed Dense or VecDense, with a non-unitary increment, Copy will
|
||||
// panic.
|
||||
//
|
||||
// See the Copier interface for more information.
|
||||
func (m *Dense) Copy(a Matrix) (r, c int) {
|
||||
r, c = a.Dims()
|
||||
if a == m {
|
||||
return r, c
|
||||
}
|
||||
r = min(r, m.mat.Rows)
|
||||
c = min(c, m.mat.Cols)
|
||||
if r == 0 || c == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
aU, trans := untranspose(a)
|
||||
switch aU := aU.(type) {
|
||||
case RawMatrixer:
|
||||
amat := aU.RawMatrix()
|
||||
if trans {
|
||||
if amat.Stride != 1 {
|
||||
m.checkOverlap(amat)
|
||||
}
|
||||
for i := 0; i < r; i++ {
|
||||
blas64.Copy(blas64.Vector{N: c, Inc: amat.Stride, Data: amat.Data[i : i+(c-1)*amat.Stride+1]},
|
||||
blas64.Vector{N: c, Inc: 1, Data: m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+c]})
|
||||
}
|
||||
} else {
|
||||
switch o := offset(m.mat.Data, amat.Data); {
|
||||
case o < 0:
|
||||
for i := r - 1; i >= 0; i-- {
|
||||
copy(m.mat.Data[i*m.mat.Stride:i*m.mat.Stride+c], amat.Data[i*amat.Stride:i*amat.Stride+c])
|
||||
}
|
||||
case o > 0:
|
||||
for i := 0; i < r; i++ {
|
||||
copy(m.mat.Data[i*m.mat.Stride:i*m.mat.Stride+c], amat.Data[i*amat.Stride:i*amat.Stride+c])
|
||||
}
|
||||
default:
|
||||
// Nothing to do.
|
||||
}
|
||||
}
|
||||
case *VecDense:
|
||||
var n, stride int
|
||||
amat := aU.mat
|
||||
if trans {
|
||||
if amat.Inc != 1 {
|
||||
m.checkOverlap(aU.asGeneral())
|
||||
}
|
||||
n = c
|
||||
stride = 1
|
||||
} else {
|
||||
n = r
|
||||
stride = m.mat.Stride
|
||||
}
|
||||
if amat.Inc == 1 && stride == 1 {
|
||||
copy(m.mat.Data, amat.Data[:n])
|
||||
break
|
||||
}
|
||||
switch o := offset(m.mat.Data, amat.Data); {
|
||||
case o < 0:
|
||||
blas64.Copy(blas64.Vector{N: n, Inc: -amat.Inc, Data: amat.Data},
|
||||
blas64.Vector{N: n, Inc: -stride, Data: m.mat.Data})
|
||||
case o > 0:
|
||||
blas64.Copy(blas64.Vector{N: n, Inc: amat.Inc, Data: amat.Data},
|
||||
blas64.Vector{N: n, Inc: stride, Data: m.mat.Data})
|
||||
default:
|
||||
// Nothing to do.
|
||||
}
|
||||
default:
|
||||
m.checkOverlapMatrix(aU)
|
||||
for i := 0; i < r; i++ {
|
||||
for j := 0; j < c; j++ {
|
||||
m.set(i, j, a.At(i, j))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return r, c
|
||||
}
|
||||
|
||||
// Stack appends the rows of b onto the rows of a, placing the result into the
|
||||
// receiver with b placed in the greater indexed rows. Stack will panic if the
|
||||
// two input matrices do not have the same number of columns or the constructed
|
||||
// stacked matrix is not the same shape as the receiver.
|
||||
func (m *Dense) Stack(a, b Matrix) {
|
||||
ar, ac := a.Dims()
|
||||
br, bc := b.Dims()
|
||||
if ac != bc || m == a || m == b {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
m.reuseAs(ar+br, ac)
|
||||
|
||||
m.Copy(a)
|
||||
w := m.Slice(ar, ar+br, 0, bc).(*Dense)
|
||||
w.Copy(b)
|
||||
}
|
||||
|
||||
// Augment creates the augmented matrix of a and b, where b is placed in the
|
||||
// greater indexed columns. Augment will panic if the two input matrices do
|
||||
// not have the same number of rows or the constructed augmented matrix is
|
||||
// not the same shape as the receiver.
|
||||
func (m *Dense) Augment(a, b Matrix) {
|
||||
ar, ac := a.Dims()
|
||||
br, bc := b.Dims()
|
||||
if ar != br || m == a || m == b {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
m.reuseAs(ar, ac+bc)
|
||||
|
||||
m.Copy(a)
|
||||
w := m.Slice(0, br, ac, ac+bc).(*Dense)
|
||||
w.Copy(b)
|
||||
}
|
||||
|
||||
// Trace returns the trace of the matrix. The matrix must be square or Trace
|
||||
// will panic.
|
||||
func (m *Dense) Trace() float64 {
|
||||
if m.mat.Rows != m.mat.Cols {
|
||||
panic(ErrSquare)
|
||||
}
|
||||
// TODO(btracey): could use internal asm sum routine.
|
||||
var v float64
|
||||
for i := 0; i < m.mat.Rows; i++ {
|
||||
v += m.mat.Data[i*m.mat.Stride+i]
|
||||
}
|
||||
return v
|
||||
}
|
||||
886
vendor/gonum.org/v1/gonum/mat/dense_arithmetic.go
generated
vendored
Normal file
886
vendor/gonum.org/v1/gonum/mat/dense_arithmetic.go
generated
vendored
Normal file
|
|
@ -0,0 +1,886 @@
|
|||
// Copyright ©2013 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
// Add adds a and b element-wise, placing the result in the receiver. Add
|
||||
// will panic if the two matrices do not have the same shape.
|
||||
func (m *Dense) Add(a, b Matrix) {
|
||||
ar, ac := a.Dims()
|
||||
br, bc := b.Dims()
|
||||
if ar != br || ac != bc {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
aU, _ := untranspose(a)
|
||||
bU, _ := untranspose(b)
|
||||
m.reuseAs(ar, ac)
|
||||
|
||||
if arm, ok := a.(RawMatrixer); ok {
|
||||
if brm, ok := b.(RawMatrixer); ok {
|
||||
amat, bmat := arm.RawMatrix(), brm.RawMatrix()
|
||||
if m != aU {
|
||||
m.checkOverlap(amat)
|
||||
}
|
||||
if m != bU {
|
||||
m.checkOverlap(bmat)
|
||||
}
|
||||
for ja, jb, jm := 0, 0, 0; ja < ar*amat.Stride; ja, jb, jm = ja+amat.Stride, jb+bmat.Stride, jm+m.mat.Stride {
|
||||
for i, v := range amat.Data[ja : ja+ac] {
|
||||
m.mat.Data[i+jm] = v + bmat.Data[i+jb]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
m.checkOverlapMatrix(aU)
|
||||
m.checkOverlapMatrix(bU)
|
||||
var restore func()
|
||||
if m == aU {
|
||||
m, restore = m.isolatedWorkspace(aU)
|
||||
defer restore()
|
||||
} else if m == bU {
|
||||
m, restore = m.isolatedWorkspace(bU)
|
||||
defer restore()
|
||||
}
|
||||
|
||||
for r := 0; r < ar; r++ {
|
||||
for c := 0; c < ac; c++ {
|
||||
m.set(r, c, a.At(r, c)+b.At(r, c))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sub subtracts the matrix b from a, placing the result in the receiver. Sub
|
||||
// will panic if the two matrices do not have the same shape.
|
||||
func (m *Dense) Sub(a, b Matrix) {
|
||||
ar, ac := a.Dims()
|
||||
br, bc := b.Dims()
|
||||
if ar != br || ac != bc {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
aU, _ := untranspose(a)
|
||||
bU, _ := untranspose(b)
|
||||
m.reuseAs(ar, ac)
|
||||
|
||||
if arm, ok := a.(RawMatrixer); ok {
|
||||
if brm, ok := b.(RawMatrixer); ok {
|
||||
amat, bmat := arm.RawMatrix(), brm.RawMatrix()
|
||||
if m != aU {
|
||||
m.checkOverlap(amat)
|
||||
}
|
||||
if m != bU {
|
||||
m.checkOverlap(bmat)
|
||||
}
|
||||
for ja, jb, jm := 0, 0, 0; ja < ar*amat.Stride; ja, jb, jm = ja+amat.Stride, jb+bmat.Stride, jm+m.mat.Stride {
|
||||
for i, v := range amat.Data[ja : ja+ac] {
|
||||
m.mat.Data[i+jm] = v - bmat.Data[i+jb]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
m.checkOverlapMatrix(aU)
|
||||
m.checkOverlapMatrix(bU)
|
||||
var restore func()
|
||||
if m == aU {
|
||||
m, restore = m.isolatedWorkspace(aU)
|
||||
defer restore()
|
||||
} else if m == bU {
|
||||
m, restore = m.isolatedWorkspace(bU)
|
||||
defer restore()
|
||||
}
|
||||
|
||||
for r := 0; r < ar; r++ {
|
||||
for c := 0; c < ac; c++ {
|
||||
m.set(r, c, a.At(r, c)-b.At(r, c))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MulElem performs element-wise multiplication of a and b, placing the result
|
||||
// in the receiver. MulElem will panic if the two matrices do not have the same
|
||||
// shape.
|
||||
func (m *Dense) MulElem(a, b Matrix) {
|
||||
ar, ac := a.Dims()
|
||||
br, bc := b.Dims()
|
||||
if ar != br || ac != bc {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
aU, _ := untranspose(a)
|
||||
bU, _ := untranspose(b)
|
||||
m.reuseAs(ar, ac)
|
||||
|
||||
if arm, ok := a.(RawMatrixer); ok {
|
||||
if brm, ok := b.(RawMatrixer); ok {
|
||||
amat, bmat := arm.RawMatrix(), brm.RawMatrix()
|
||||
if m != aU {
|
||||
m.checkOverlap(amat)
|
||||
}
|
||||
if m != bU {
|
||||
m.checkOverlap(bmat)
|
||||
}
|
||||
for ja, jb, jm := 0, 0, 0; ja < ar*amat.Stride; ja, jb, jm = ja+amat.Stride, jb+bmat.Stride, jm+m.mat.Stride {
|
||||
for i, v := range amat.Data[ja : ja+ac] {
|
||||
m.mat.Data[i+jm] = v * bmat.Data[i+jb]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
m.checkOverlapMatrix(aU)
|
||||
m.checkOverlapMatrix(bU)
|
||||
var restore func()
|
||||
if m == aU {
|
||||
m, restore = m.isolatedWorkspace(aU)
|
||||
defer restore()
|
||||
} else if m == bU {
|
||||
m, restore = m.isolatedWorkspace(bU)
|
||||
defer restore()
|
||||
}
|
||||
|
||||
for r := 0; r < ar; r++ {
|
||||
for c := 0; c < ac; c++ {
|
||||
m.set(r, c, a.At(r, c)*b.At(r, c))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DivElem performs element-wise division of a by b, placing the result
|
||||
// in the receiver. DivElem will panic if the two matrices do not have the same
|
||||
// shape.
|
||||
func (m *Dense) DivElem(a, b Matrix) {
|
||||
ar, ac := a.Dims()
|
||||
br, bc := b.Dims()
|
||||
if ar != br || ac != bc {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
aU, _ := untranspose(a)
|
||||
bU, _ := untranspose(b)
|
||||
m.reuseAs(ar, ac)
|
||||
|
||||
if arm, ok := a.(RawMatrixer); ok {
|
||||
if brm, ok := b.(RawMatrixer); ok {
|
||||
amat, bmat := arm.RawMatrix(), brm.RawMatrix()
|
||||
if m != aU {
|
||||
m.checkOverlap(amat)
|
||||
}
|
||||
if m != bU {
|
||||
m.checkOverlap(bmat)
|
||||
}
|
||||
for ja, jb, jm := 0, 0, 0; ja < ar*amat.Stride; ja, jb, jm = ja+amat.Stride, jb+bmat.Stride, jm+m.mat.Stride {
|
||||
for i, v := range amat.Data[ja : ja+ac] {
|
||||
m.mat.Data[i+jm] = v / bmat.Data[i+jb]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
m.checkOverlapMatrix(aU)
|
||||
m.checkOverlapMatrix(bU)
|
||||
var restore func()
|
||||
if m == aU {
|
||||
m, restore = m.isolatedWorkspace(aU)
|
||||
defer restore()
|
||||
} else if m == bU {
|
||||
m, restore = m.isolatedWorkspace(bU)
|
||||
defer restore()
|
||||
}
|
||||
|
||||
for r := 0; r < ar; r++ {
|
||||
for c := 0; c < ac; c++ {
|
||||
m.set(r, c, a.At(r, c)/b.At(r, c))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inverse computes the inverse of the matrix a, storing the result into the
|
||||
// receiver. If a is ill-conditioned, a Condition error will be returned.
|
||||
// Note that matrix inversion is numerically unstable, and should generally
|
||||
// be avoided where possible, for example by using the Solve routines.
|
||||
func (m *Dense) Inverse(a Matrix) error {
|
||||
// TODO(btracey): Special case for RawTriangular, etc.
|
||||
r, c := a.Dims()
|
||||
if r != c {
|
||||
panic(ErrSquare)
|
||||
}
|
||||
m.reuseAs(a.Dims())
|
||||
aU, aTrans := untranspose(a)
|
||||
switch rm := aU.(type) {
|
||||
case RawMatrixer:
|
||||
if m != aU || aTrans {
|
||||
if m == aU || m.checkOverlap(rm.RawMatrix()) {
|
||||
tmp := getWorkspace(r, c, false)
|
||||
tmp.Copy(a)
|
||||
m.Copy(tmp)
|
||||
putWorkspace(tmp)
|
||||
break
|
||||
}
|
||||
m.Copy(a)
|
||||
}
|
||||
default:
|
||||
m.Copy(a)
|
||||
}
|
||||
ipiv := getInts(r, false)
|
||||
defer putInts(ipiv)
|
||||
ok := lapack64.Getrf(m.mat, ipiv)
|
||||
if !ok {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
work := getFloats(4*r, false) // must be at least 4*r for cond.
|
||||
lapack64.Getri(m.mat, ipiv, work, -1)
|
||||
if int(work[0]) > 4*r {
|
||||
l := int(work[0])
|
||||
putFloats(work)
|
||||
work = getFloats(l, false)
|
||||
} else {
|
||||
work = work[:4*r]
|
||||
}
|
||||
defer putFloats(work)
|
||||
lapack64.Getri(m.mat, ipiv, work, len(work))
|
||||
norm := lapack64.Lange(CondNorm, m.mat, work)
|
||||
rcond := lapack64.Gecon(CondNorm, m.mat, norm, work, ipiv) // reuse ipiv
|
||||
if rcond == 0 {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
cond := 1 / rcond
|
||||
if cond > ConditionTolerance {
|
||||
return Condition(cond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Mul takes the matrix product of a and b, placing the result in the receiver.
|
||||
// If the number of columns in a does not equal the number of rows in b, Mul will panic.
|
||||
func (m *Dense) Mul(a, b Matrix) {
|
||||
ar, ac := a.Dims()
|
||||
br, bc := b.Dims()
|
||||
|
||||
if ac != br {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
aU, aTrans := untranspose(a)
|
||||
bU, bTrans := untranspose(b)
|
||||
m.reuseAs(ar, bc)
|
||||
var restore func()
|
||||
if m == aU {
|
||||
m, restore = m.isolatedWorkspace(aU)
|
||||
defer restore()
|
||||
} else if m == bU {
|
||||
m, restore = m.isolatedWorkspace(bU)
|
||||
defer restore()
|
||||
}
|
||||
aT := blas.NoTrans
|
||||
if aTrans {
|
||||
aT = blas.Trans
|
||||
}
|
||||
bT := blas.NoTrans
|
||||
if bTrans {
|
||||
bT = blas.Trans
|
||||
}
|
||||
|
||||
// Some of the cases do not have a transpose option, so create
|
||||
// temporary memory.
|
||||
// C = A^T * B = (B^T * A)^T
|
||||
// C^T = B^T * A.
|
||||
if aUrm, ok := aU.(RawMatrixer); ok {
|
||||
amat := aUrm.RawMatrix()
|
||||
if restore == nil {
|
||||
m.checkOverlap(amat)
|
||||
}
|
||||
if bUrm, ok := bU.(RawMatrixer); ok {
|
||||
bmat := bUrm.RawMatrix()
|
||||
if restore == nil {
|
||||
m.checkOverlap(bmat)
|
||||
}
|
||||
blas64.Gemm(aT, bT, 1, amat, bmat, 0, m.mat)
|
||||
return
|
||||
}
|
||||
if bU, ok := bU.(RawSymmetricer); ok {
|
||||
bmat := bU.RawSymmetric()
|
||||
if aTrans {
|
||||
c := getWorkspace(ac, ar, false)
|
||||
blas64.Symm(blas.Left, 1, bmat, amat, 0, c.mat)
|
||||
strictCopy(m, c.T())
|
||||
putWorkspace(c)
|
||||
return
|
||||
}
|
||||
blas64.Symm(blas.Right, 1, bmat, amat, 0, m.mat)
|
||||
return
|
||||
}
|
||||
if bU, ok := bU.(RawTriangular); ok {
|
||||
// Trmm updates in place, so copy aU first.
|
||||
bmat := bU.RawTriangular()
|
||||
if aTrans {
|
||||
c := getWorkspace(ac, ar, false)
|
||||
var tmp Dense
|
||||
tmp.SetRawMatrix(amat)
|
||||
c.Copy(&tmp)
|
||||
bT := blas.Trans
|
||||
if bTrans {
|
||||
bT = blas.NoTrans
|
||||
}
|
||||
blas64.Trmm(blas.Left, bT, 1, bmat, c.mat)
|
||||
strictCopy(m, c.T())
|
||||
putWorkspace(c)
|
||||
return
|
||||
}
|
||||
m.Copy(a)
|
||||
blas64.Trmm(blas.Right, bT, 1, bmat, m.mat)
|
||||
return
|
||||
}
|
||||
if bU, ok := bU.(*VecDense); ok {
|
||||
m.checkOverlap(bU.asGeneral())
|
||||
bvec := bU.RawVector()
|
||||
if bTrans {
|
||||
// {ar,1} x {1,bc}, which is not a vector.
|
||||
// Instead, construct B as a General.
|
||||
bmat := blas64.General{
|
||||
Rows: bc,
|
||||
Cols: 1,
|
||||
Stride: bvec.Inc,
|
||||
Data: bvec.Data,
|
||||
}
|
||||
blas64.Gemm(aT, bT, 1, amat, bmat, 0, m.mat)
|
||||
return
|
||||
}
|
||||
cvec := blas64.Vector{
|
||||
Inc: m.mat.Stride,
|
||||
Data: m.mat.Data,
|
||||
}
|
||||
blas64.Gemv(aT, 1, amat, bvec, 0, cvec)
|
||||
return
|
||||
}
|
||||
}
|
||||
if bUrm, ok := bU.(RawMatrixer); ok {
|
||||
bmat := bUrm.RawMatrix()
|
||||
if restore == nil {
|
||||
m.checkOverlap(bmat)
|
||||
}
|
||||
if aU, ok := aU.(RawSymmetricer); ok {
|
||||
amat := aU.RawSymmetric()
|
||||
if bTrans {
|
||||
c := getWorkspace(bc, br, false)
|
||||
blas64.Symm(blas.Right, 1, amat, bmat, 0, c.mat)
|
||||
strictCopy(m, c.T())
|
||||
putWorkspace(c)
|
||||
return
|
||||
}
|
||||
blas64.Symm(blas.Left, 1, amat, bmat, 0, m.mat)
|
||||
return
|
||||
}
|
||||
if aU, ok := aU.(RawTriangular); ok {
|
||||
// Trmm updates in place, so copy bU first.
|
||||
amat := aU.RawTriangular()
|
||||
if bTrans {
|
||||
c := getWorkspace(bc, br, false)
|
||||
var tmp Dense
|
||||
tmp.SetRawMatrix(bmat)
|
||||
c.Copy(&tmp)
|
||||
aT := blas.Trans
|
||||
if aTrans {
|
||||
aT = blas.NoTrans
|
||||
}
|
||||
blas64.Trmm(blas.Right, aT, 1, amat, c.mat)
|
||||
strictCopy(m, c.T())
|
||||
putWorkspace(c)
|
||||
return
|
||||
}
|
||||
m.Copy(b)
|
||||
blas64.Trmm(blas.Left, aT, 1, amat, m.mat)
|
||||
return
|
||||
}
|
||||
if aU, ok := aU.(*VecDense); ok {
|
||||
m.checkOverlap(aU.asGeneral())
|
||||
avec := aU.RawVector()
|
||||
if aTrans {
|
||||
// {1,ac} x {ac, bc}
|
||||
// Transpose B so that the vector is on the right.
|
||||
cvec := blas64.Vector{
|
||||
Inc: 1,
|
||||
Data: m.mat.Data,
|
||||
}
|
||||
bT := blas.Trans
|
||||
if bTrans {
|
||||
bT = blas.NoTrans
|
||||
}
|
||||
blas64.Gemv(bT, 1, bmat, avec, 0, cvec)
|
||||
return
|
||||
}
|
||||
// {ar,1} x {1,bc} which is not a vector result.
|
||||
// Instead, construct A as a General.
|
||||
amat := blas64.General{
|
||||
Rows: ar,
|
||||
Cols: 1,
|
||||
Stride: avec.Inc,
|
||||
Data: avec.Data,
|
||||
}
|
||||
blas64.Gemm(aT, bT, 1, amat, bmat, 0, m.mat)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
m.checkOverlapMatrix(aU)
|
||||
m.checkOverlapMatrix(bU)
|
||||
row := getFloats(ac, false)
|
||||
defer putFloats(row)
|
||||
for r := 0; r < ar; r++ {
|
||||
for i := range row {
|
||||
row[i] = a.At(r, i)
|
||||
}
|
||||
for c := 0; c < bc; c++ {
|
||||
var v float64
|
||||
for i, e := range row {
|
||||
v += e * b.At(i, c)
|
||||
}
|
||||
m.mat.Data[r*m.mat.Stride+c] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// strictCopy copies a into m panicking if the shape of a and m differ.
|
||||
func strictCopy(m *Dense, a Matrix) {
|
||||
r, c := m.Copy(a)
|
||||
if r != m.mat.Rows || c != m.mat.Cols {
|
||||
// Panic with a string since this
|
||||
// is not a user-facing panic.
|
||||
panic(ErrShape.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Exp calculates the exponential of the matrix a, e^a, placing the result
|
||||
// in the receiver. Exp will panic with matrix.ErrShape if a is not square.
|
||||
func (m *Dense) Exp(a Matrix) {
|
||||
// The implementation used here is from Functions of Matrices: Theory and Computation
|
||||
// Chapter 10, Algorithm 10.20. https://doi.org/10.1137/1.9780898717778.ch10
|
||||
|
||||
r, c := a.Dims()
|
||||
if r != c {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
m.reuseAs(r, r)
|
||||
if r == 1 {
|
||||
m.mat.Data[0] = math.Exp(a.At(0, 0))
|
||||
return
|
||||
}
|
||||
|
||||
pade := []struct {
|
||||
theta float64
|
||||
b []float64
|
||||
}{
|
||||
{theta: 0.015, b: []float64{
|
||||
120, 60, 12, 1,
|
||||
}},
|
||||
{theta: 0.25, b: []float64{
|
||||
30240, 15120, 3360, 420, 30, 1,
|
||||
}},
|
||||
{theta: 0.95, b: []float64{
|
||||
17297280, 8648640, 1995840, 277200, 25200, 1512, 56, 1,
|
||||
}},
|
||||
{theta: 2.1, b: []float64{
|
||||
17643225600, 8821612800, 2075673600, 302702400, 30270240, 2162160, 110880, 3960, 90, 1,
|
||||
}},
|
||||
}
|
||||
|
||||
a1 := m
|
||||
a1.Copy(a)
|
||||
v := getWorkspace(r, r, true)
|
||||
vraw := v.RawMatrix()
|
||||
n := r * r
|
||||
vvec := blas64.Vector{N: n, Inc: 1, Data: vraw.Data}
|
||||
defer putWorkspace(v)
|
||||
|
||||
u := getWorkspace(r, r, true)
|
||||
uraw := u.RawMatrix()
|
||||
uvec := blas64.Vector{N: n, Inc: 1, Data: uraw.Data}
|
||||
defer putWorkspace(u)
|
||||
|
||||
a2 := getWorkspace(r, r, false)
|
||||
defer putWorkspace(a2)
|
||||
|
||||
n1 := Norm(a, 1)
|
||||
for i, t := range pade {
|
||||
if n1 > t.theta {
|
||||
continue
|
||||
}
|
||||
|
||||
// This loop only executes once, so
|
||||
// this is not as horrible as it looks.
|
||||
p := getWorkspace(r, r, true)
|
||||
praw := p.RawMatrix()
|
||||
pvec := blas64.Vector{N: n, Inc: 1, Data: praw.Data}
|
||||
defer putWorkspace(p)
|
||||
|
||||
for k := 0; k < r; k++ {
|
||||
p.set(k, k, 1)
|
||||
v.set(k, k, t.b[0])
|
||||
u.set(k, k, t.b[1])
|
||||
}
|
||||
|
||||
a2.Mul(a1, a1)
|
||||
for j := 0; j <= i; j++ {
|
||||
p.Mul(p, a2)
|
||||
blas64.Axpy(t.b[2*j+2], pvec, vvec)
|
||||
blas64.Axpy(t.b[2*j+3], pvec, uvec)
|
||||
}
|
||||
u.Mul(a1, u)
|
||||
|
||||
// Use p as a workspace here and
|
||||
// rename u for the second call's
|
||||
// receiver.
|
||||
vmu, vpu := u, p
|
||||
vpu.Add(v, u)
|
||||
vmu.Sub(v, u)
|
||||
|
||||
m.Solve(vmu, vpu)
|
||||
return
|
||||
}
|
||||
|
||||
// Remaining Padé table line.
|
||||
const theta13 = 5.4
|
||||
b := [...]float64{
|
||||
64764752532480000, 32382376266240000, 7771770303897600, 1187353796428800,
|
||||
129060195264000, 10559470521600, 670442572800, 33522128640,
|
||||
1323241920, 40840800, 960960, 16380, 182, 1,
|
||||
}
|
||||
|
||||
s := math.Log2(n1 / theta13)
|
||||
if s >= 0 {
|
||||
s = math.Ceil(s)
|
||||
a1.Scale(1/math.Pow(2, s), a1)
|
||||
}
|
||||
a2.Mul(a1, a1)
|
||||
|
||||
i := getWorkspace(r, r, true)
|
||||
for j := 0; j < r; j++ {
|
||||
i.set(j, j, 1)
|
||||
}
|
||||
iraw := i.RawMatrix()
|
||||
ivec := blas64.Vector{N: n, Inc: 1, Data: iraw.Data}
|
||||
defer putWorkspace(i)
|
||||
|
||||
a2raw := a2.RawMatrix()
|
||||
a2vec := blas64.Vector{N: n, Inc: 1, Data: a2raw.Data}
|
||||
|
||||
a4 := getWorkspace(r, r, false)
|
||||
a4raw := a4.RawMatrix()
|
||||
a4vec := blas64.Vector{N: n, Inc: 1, Data: a4raw.Data}
|
||||
defer putWorkspace(a4)
|
||||
a4.Mul(a2, a2)
|
||||
|
||||
a6 := getWorkspace(r, r, false)
|
||||
a6raw := a6.RawMatrix()
|
||||
a6vec := blas64.Vector{N: n, Inc: 1, Data: a6raw.Data}
|
||||
defer putWorkspace(a6)
|
||||
a6.Mul(a2, a4)
|
||||
|
||||
// V = A_6(b_12*A_6 + b_10*A_4 + b_8*A_2) + b_6*A_6 + b_4*A_4 + b_2*A_2 +b_0*I
|
||||
blas64.Axpy(b[12], a6vec, vvec)
|
||||
blas64.Axpy(b[10], a4vec, vvec)
|
||||
blas64.Axpy(b[8], a2vec, vvec)
|
||||
v.Mul(v, a6)
|
||||
blas64.Axpy(b[6], a6vec, vvec)
|
||||
blas64.Axpy(b[4], a4vec, vvec)
|
||||
blas64.Axpy(b[2], a2vec, vvec)
|
||||
blas64.Axpy(b[0], ivec, vvec)
|
||||
|
||||
// U = A(A_6(b_13*A_6 + b_11*A_4 + b_9*A_2) + b_7*A_6 + b_5*A_4 + b_2*A_3 +b_1*I)
|
||||
blas64.Axpy(b[13], a6vec, uvec)
|
||||
blas64.Axpy(b[11], a4vec, uvec)
|
||||
blas64.Axpy(b[9], a2vec, uvec)
|
||||
u.Mul(u, a6)
|
||||
blas64.Axpy(b[7], a6vec, uvec)
|
||||
blas64.Axpy(b[5], a4vec, uvec)
|
||||
blas64.Axpy(b[3], a2vec, uvec)
|
||||
blas64.Axpy(b[1], ivec, uvec)
|
||||
u.Mul(u, a1)
|
||||
|
||||
// Use i as a workspace here and
|
||||
// rename u for the second call's
|
||||
// receiver.
|
||||
vmu, vpu := u, i
|
||||
vpu.Add(v, u)
|
||||
vmu.Sub(v, u)
|
||||
|
||||
m.Solve(vmu, vpu)
|
||||
|
||||
for ; s > 0; s-- {
|
||||
m.Mul(m, m)
|
||||
}
|
||||
}
|
||||
|
||||
// Pow calculates the integral power of the matrix a to n, placing the result
|
||||
// in the receiver. Pow will panic if n is negative or if a is not square.
|
||||
func (m *Dense) Pow(a Matrix, n int) {
|
||||
if n < 0 {
|
||||
panic("matrix: illegal power")
|
||||
}
|
||||
r, c := a.Dims()
|
||||
if r != c {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
m.reuseAs(r, c)
|
||||
|
||||
// Take possible fast paths.
|
||||
switch n {
|
||||
case 0:
|
||||
for i := 0; i < r; i++ {
|
||||
zero(m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+c])
|
||||
m.mat.Data[i*m.mat.Stride+i] = 1
|
||||
}
|
||||
return
|
||||
case 1:
|
||||
m.Copy(a)
|
||||
return
|
||||
case 2:
|
||||
m.Mul(a, a)
|
||||
return
|
||||
}
|
||||
|
||||
// Perform iterative exponentiation by squaring in work space.
|
||||
w := getWorkspace(r, r, false)
|
||||
w.Copy(a)
|
||||
s := getWorkspace(r, r, false)
|
||||
s.Copy(a)
|
||||
x := getWorkspace(r, r, false)
|
||||
for n--; n > 0; n >>= 1 {
|
||||
if n&1 != 0 {
|
||||
x.Mul(w, s)
|
||||
w, x = x, w
|
||||
}
|
||||
if n != 1 {
|
||||
x.Mul(s, s)
|
||||
s, x = x, s
|
||||
}
|
||||
}
|
||||
m.Copy(w)
|
||||
putWorkspace(w)
|
||||
putWorkspace(s)
|
||||
putWorkspace(x)
|
||||
}
|
||||
|
||||
// Scale multiplies the elements of a by f, placing the result in the receiver.
|
||||
//
|
||||
// See the Scaler interface for more information.
|
||||
func (m *Dense) Scale(f float64, a Matrix) {
|
||||
ar, ac := a.Dims()
|
||||
|
||||
m.reuseAs(ar, ac)
|
||||
|
||||
aU, aTrans := untranspose(a)
|
||||
if rm, ok := aU.(RawMatrixer); ok {
|
||||
amat := rm.RawMatrix()
|
||||
if m == aU || m.checkOverlap(amat) {
|
||||
var restore func()
|
||||
m, restore = m.isolatedWorkspace(a)
|
||||
defer restore()
|
||||
}
|
||||
if !aTrans {
|
||||
for ja, jm := 0, 0; ja < ar*amat.Stride; ja, jm = ja+amat.Stride, jm+m.mat.Stride {
|
||||
for i, v := range amat.Data[ja : ja+ac] {
|
||||
m.mat.Data[i+jm] = v * f
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for ja, jm := 0, 0; ja < ac*amat.Stride; ja, jm = ja+amat.Stride, jm+1 {
|
||||
for i, v := range amat.Data[ja : ja+ar] {
|
||||
m.mat.Data[i*m.mat.Stride+jm] = v * f
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
m.checkOverlapMatrix(a)
|
||||
for r := 0; r < ar; r++ {
|
||||
for c := 0; c < ac; c++ {
|
||||
m.set(r, c, f*a.At(r, c))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply applies the function fn to each of the elements of a, placing the
|
||||
// resulting matrix in the receiver. The function fn takes a row/column
|
||||
// index and element value and returns some function of that tuple.
|
||||
func (m *Dense) Apply(fn func(i, j int, v float64) float64, a Matrix) {
|
||||
ar, ac := a.Dims()
|
||||
|
||||
m.reuseAs(ar, ac)
|
||||
|
||||
aU, aTrans := untranspose(a)
|
||||
if rm, ok := aU.(RawMatrixer); ok {
|
||||
amat := rm.RawMatrix()
|
||||
if m == aU || m.checkOverlap(amat) {
|
||||
var restore func()
|
||||
m, restore = m.isolatedWorkspace(a)
|
||||
defer restore()
|
||||
}
|
||||
if !aTrans {
|
||||
for j, ja, jm := 0, 0, 0; ja < ar*amat.Stride; j, ja, jm = j+1, ja+amat.Stride, jm+m.mat.Stride {
|
||||
for i, v := range amat.Data[ja : ja+ac] {
|
||||
m.mat.Data[i+jm] = fn(j, i, v)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for j, ja, jm := 0, 0, 0; ja < ac*amat.Stride; j, ja, jm = j+1, ja+amat.Stride, jm+1 {
|
||||
for i, v := range amat.Data[ja : ja+ar] {
|
||||
m.mat.Data[i*m.mat.Stride+jm] = fn(i, j, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
m.checkOverlapMatrix(a)
|
||||
for r := 0; r < ar; r++ {
|
||||
for c := 0; c < ac; c++ {
|
||||
m.set(r, c, fn(r, c, a.At(r, c)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RankOne performs a rank-one update to the matrix a and stores the result
|
||||
// in the receiver. If a is zero, see Outer.
|
||||
// m = a + alpha * x * y'
|
||||
func (m *Dense) RankOne(a Matrix, alpha float64, x, y Vector) {
|
||||
ar, ac := a.Dims()
|
||||
xr, xc := x.Dims()
|
||||
if xr != ar || xc != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
yr, yc := y.Dims()
|
||||
if yr != ac || yc != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
if a != m {
|
||||
aU, _ := untranspose(a)
|
||||
if rm, ok := aU.(RawMatrixer); ok {
|
||||
m.checkOverlap(rm.RawMatrix())
|
||||
}
|
||||
}
|
||||
|
||||
var xmat, ymat blas64.Vector
|
||||
fast := true
|
||||
xU, _ := untranspose(x)
|
||||
if rv, ok := xU.(RawVectorer); ok {
|
||||
xmat = rv.RawVector()
|
||||
m.checkOverlap((&VecDense{mat: xmat}).asGeneral())
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
yU, _ := untranspose(y)
|
||||
if rv, ok := yU.(RawVectorer); ok {
|
||||
ymat = rv.RawVector()
|
||||
m.checkOverlap((&VecDense{mat: ymat}).asGeneral())
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
|
||||
if fast {
|
||||
if m != a {
|
||||
m.reuseAs(ar, ac)
|
||||
m.Copy(a)
|
||||
}
|
||||
blas64.Ger(alpha, xmat, ymat, m.mat)
|
||||
return
|
||||
}
|
||||
|
||||
m.reuseAs(ar, ac)
|
||||
for i := 0; i < ar; i++ {
|
||||
for j := 0; j < ac; j++ {
|
||||
m.set(i, j, a.At(i, j)+alpha*x.AtVec(i)*y.AtVec(j))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Outer calculates the outer product of the column vectors x and y,
|
||||
// and stores the result in the receiver.
|
||||
// m = alpha * x * y'
|
||||
// In order to update an existing matrix, see RankOne.
|
||||
func (m *Dense) Outer(alpha float64, x, y Vector) {
|
||||
xr, xc := x.Dims()
|
||||
if xc != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
yr, yc := y.Dims()
|
||||
if yc != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
r := xr
|
||||
c := yr
|
||||
|
||||
// Copied from reuseAs with use replaced by useZeroed
|
||||
// and a final zero of the matrix elements if we pass
|
||||
// the shape checks.
|
||||
// TODO(kortschak): Factor out into reuseZeroedAs if
|
||||
// we find another case that needs it.
|
||||
if m.mat.Rows > m.capRows || m.mat.Cols > m.capCols {
|
||||
// Panic as a string, not a mat.Error.
|
||||
panic("mat: caps not correctly set")
|
||||
}
|
||||
if m.IsZero() {
|
||||
m.mat = blas64.General{
|
||||
Rows: r,
|
||||
Cols: c,
|
||||
Stride: c,
|
||||
Data: useZeroed(m.mat.Data, r*c),
|
||||
}
|
||||
m.capRows = r
|
||||
m.capCols = c
|
||||
} else if r != m.mat.Rows || c != m.mat.Cols {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
var xmat, ymat blas64.Vector
|
||||
fast := true
|
||||
xU, _ := untranspose(x)
|
||||
if rv, ok := xU.(RawVectorer); ok {
|
||||
xmat = rv.RawVector()
|
||||
m.checkOverlap((&VecDense{mat: xmat}).asGeneral())
|
||||
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
yU, _ := untranspose(y)
|
||||
if rv, ok := yU.(RawVectorer); ok {
|
||||
ymat = rv.RawVector()
|
||||
m.checkOverlap((&VecDense{mat: ymat}).asGeneral())
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
|
||||
if fast {
|
||||
for i := 0; i < r; i++ {
|
||||
zero(m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+c])
|
||||
}
|
||||
blas64.Ger(alpha, xmat, ymat, m.mat)
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < r; i++ {
|
||||
for j := 0; j < c; j++ {
|
||||
m.set(i, j, alpha*x.AtVec(i)*y.AtVec(j))
|
||||
}
|
||||
}
|
||||
}
|
||||
311
vendor/gonum.org/v1/gonum/mat/diagonal.go
generated
vendored
Normal file
311
vendor/gonum.org/v1/gonum/mat/diagonal.go
generated
vendored
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
// Copyright ©2018 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
)
|
||||
|
||||
var (
|
||||
diagDense *DiagDense
|
||||
_ Matrix = diagDense
|
||||
_ Diagonal = diagDense
|
||||
_ MutableDiagonal = diagDense
|
||||
_ Triangular = diagDense
|
||||
_ TriBanded = diagDense
|
||||
_ Symmetric = diagDense
|
||||
_ SymBanded = diagDense
|
||||
_ Banded = diagDense
|
||||
_ RawBander = diagDense
|
||||
_ RawSymBander = diagDense
|
||||
|
||||
diag Diagonal
|
||||
_ Matrix = diag
|
||||
_ Diagonal = diag
|
||||
_ Triangular = diag
|
||||
_ TriBanded = diag
|
||||
_ Symmetric = diag
|
||||
_ SymBanded = diag
|
||||
_ Banded = diag
|
||||
)
|
||||
|
||||
// Diagonal represents a diagonal matrix, that is a square matrix that only
|
||||
// has non-zero terms on the diagonal.
|
||||
type Diagonal interface {
|
||||
Matrix
|
||||
// Diag returns the number of rows/columns in the matrix.
|
||||
Diag() int
|
||||
|
||||
// Bandwidth and TBand are included in the Diagonal interface
|
||||
// to allow the use of Diagonal types in banded functions.
|
||||
// Bandwidth will always return (0, 0).
|
||||
Bandwidth() (kl, ku int)
|
||||
TBand() Banded
|
||||
|
||||
// Triangle and TTri are included in the Diagonal interface
|
||||
// to allow the use of Diagonal types in triangular functions.
|
||||
Triangle() (int, TriKind)
|
||||
TTri() Triangular
|
||||
|
||||
// Symmetric and SymBand are included in the Diagonal interface
|
||||
// to allow the use of Diagonal types in symmetric and banded symmetric
|
||||
// functions respectively.
|
||||
Symmetric() int
|
||||
SymBand() (n, k int)
|
||||
|
||||
// TriBand and TTriBand are included in the Diagonal interface
|
||||
// to allow the use of Diagonal types in triangular banded functions.
|
||||
TriBand() (n, k int, kind TriKind)
|
||||
TTriBand() TriBanded
|
||||
}
|
||||
|
||||
// MutableDiagonal is a Diagonal matrix whose elements can be set.
|
||||
type MutableDiagonal interface {
|
||||
Diagonal
|
||||
SetDiag(i int, v float64)
|
||||
}
|
||||
|
||||
// DiagDense represents a diagonal matrix in dense storage format.
|
||||
type DiagDense struct {
|
||||
mat blas64.Vector
|
||||
}
|
||||
|
||||
// NewDiagDense creates a new Diagonal matrix with n rows and n columns.
|
||||
// The length of data must be n or data must be nil, otherwise NewDiagDense
|
||||
// will panic. NewDiagDense will panic if n is zero.
|
||||
func NewDiagDense(n int, data []float64) *DiagDense {
|
||||
if n <= 0 {
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic("mat: negative dimension")
|
||||
}
|
||||
if data == nil {
|
||||
data = make([]float64, n)
|
||||
}
|
||||
if len(data) != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
return &DiagDense{
|
||||
mat: blas64.Vector{N: n, Data: data, Inc: 1},
|
||||
}
|
||||
}
|
||||
|
||||
// Diag returns the dimension of the receiver.
|
||||
func (d *DiagDense) Diag() int {
|
||||
return d.mat.N
|
||||
}
|
||||
|
||||
// Dims returns the dimensions of the matrix.
|
||||
func (d *DiagDense) Dims() (r, c int) {
|
||||
return d.mat.N, d.mat.N
|
||||
}
|
||||
|
||||
// T returns the transpose of the matrix.
|
||||
func (d *DiagDense) T() Matrix {
|
||||
return d
|
||||
}
|
||||
|
||||
// TTri returns the transpose of the matrix. Note that Diagonal matrices are
|
||||
// Upper by default.
|
||||
func (d *DiagDense) TTri() Triangular {
|
||||
return TransposeTri{d}
|
||||
}
|
||||
|
||||
// TBand performs an implicit transpose by returning the receiver inside a
|
||||
// TransposeBand.
|
||||
func (d *DiagDense) TBand() Banded {
|
||||
return TransposeBand{d}
|
||||
}
|
||||
|
||||
// TTriBand performs an implicit transpose by returning the receiver inside a
|
||||
// TransposeTriBand. Note that Diagonal matrices are Upper by default.
|
||||
func (d *DiagDense) TTriBand() TriBanded {
|
||||
return TransposeTriBand{d}
|
||||
}
|
||||
|
||||
// Bandwidth returns the upper and lower bandwidths of the matrix.
|
||||
// These values are always zero for diagonal matrices.
|
||||
func (d *DiagDense) Bandwidth() (kl, ku int) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// Symmetric implements the Symmetric interface.
|
||||
func (d *DiagDense) Symmetric() int {
|
||||
return d.mat.N
|
||||
}
|
||||
|
||||
// SymBand returns the number of rows/columns in the matrix, and the size of
|
||||
// the bandwidth.
|
||||
func (d *DiagDense) SymBand() (n, k int) {
|
||||
return d.mat.N, 0
|
||||
}
|
||||
|
||||
// Triangle implements the Triangular interface.
|
||||
func (d *DiagDense) Triangle() (int, TriKind) {
|
||||
return d.mat.N, Upper
|
||||
}
|
||||
|
||||
// TriBand returns the number of rows/columns in the matrix, the
|
||||
// size of the bandwidth, and the orientation. Note that Diagonal matrices are
|
||||
// Upper by default.
|
||||
func (d *DiagDense) TriBand() (n, k int, kind TriKind) {
|
||||
return d.mat.N, 0, Upper
|
||||
}
|
||||
|
||||
// Reset zeros the length of the matrix so that it can be reused as the
|
||||
// receiver of a dimensionally restricted operation.
|
||||
//
|
||||
// See the Reseter interface for more information.
|
||||
func (d *DiagDense) Reset() {
|
||||
// No change of Inc or n to 0 may be
|
||||
// made unless both are set to 0.
|
||||
d.mat.Inc = 0
|
||||
d.mat.N = 0
|
||||
d.mat.Data = d.mat.Data[:0]
|
||||
}
|
||||
|
||||
// Zero sets all of the matrix elements to zero.
|
||||
func (d *DiagDense) Zero() {
|
||||
for i := 0; i < d.mat.N; i++ {
|
||||
d.mat.Data[d.mat.Inc*i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// DiagView returns the diagonal as a matrix backed by the original data.
|
||||
func (d *DiagDense) DiagView() Diagonal {
|
||||
return d
|
||||
}
|
||||
|
||||
// DiagFrom copies the diagonal of m into the receiver. The receiver must
|
||||
// be min(r, c) long or zero. Otherwise DiagFrom will panic.
|
||||
func (d *DiagDense) DiagFrom(m Matrix) {
|
||||
n := min(m.Dims())
|
||||
d.reuseAs(n)
|
||||
|
||||
var vec blas64.Vector
|
||||
switch r := m.(type) {
|
||||
case *DiagDense:
|
||||
vec = r.mat
|
||||
case RawBander:
|
||||
mat := r.RawBand()
|
||||
vec = blas64.Vector{
|
||||
N: n,
|
||||
Inc: mat.Stride,
|
||||
Data: mat.Data[mat.KL : (n-1)*mat.Stride+mat.KL+1],
|
||||
}
|
||||
case RawMatrixer:
|
||||
mat := r.RawMatrix()
|
||||
vec = blas64.Vector{
|
||||
N: n,
|
||||
Inc: mat.Stride + 1,
|
||||
Data: mat.Data[:(n-1)*mat.Stride+n],
|
||||
}
|
||||
case RawSymBander:
|
||||
mat := r.RawSymBand()
|
||||
vec = blas64.Vector{
|
||||
N: n,
|
||||
Inc: mat.Stride,
|
||||
Data: mat.Data[:(n-1)*mat.Stride+1],
|
||||
}
|
||||
case RawSymmetricer:
|
||||
mat := r.RawSymmetric()
|
||||
vec = blas64.Vector{
|
||||
N: n,
|
||||
Inc: mat.Stride + 1,
|
||||
Data: mat.Data[:(n-1)*mat.Stride+n],
|
||||
}
|
||||
case RawTriBander:
|
||||
mat := r.RawTriBand()
|
||||
data := mat.Data
|
||||
if mat.Uplo == blas.Lower {
|
||||
data = data[mat.K:]
|
||||
}
|
||||
vec = blas64.Vector{
|
||||
N: n,
|
||||
Inc: mat.Stride,
|
||||
Data: data[:(n-1)*mat.Stride+1],
|
||||
}
|
||||
case RawTriangular:
|
||||
mat := r.RawTriangular()
|
||||
if mat.Diag == blas.Unit {
|
||||
for i := 0; i < n; i += d.mat.Inc {
|
||||
d.mat.Data[i] = 1
|
||||
}
|
||||
return
|
||||
}
|
||||
vec = blas64.Vector{
|
||||
N: n,
|
||||
Inc: mat.Stride + 1,
|
||||
Data: mat.Data[:(n-1)*mat.Stride+n],
|
||||
}
|
||||
case RawVectorer:
|
||||
d.mat.Data[0] = r.RawVector().Data[0]
|
||||
return
|
||||
default:
|
||||
for i := 0; i < n; i++ {
|
||||
d.setDiag(i, m.At(i, i))
|
||||
}
|
||||
return
|
||||
}
|
||||
blas64.Copy(vec, d.mat)
|
||||
}
|
||||
|
||||
// RawBand returns the underlying data used by the receiver represented
|
||||
// as a blas64.Band.
|
||||
// Changes to elements in the receiver following the call will be reflected
|
||||
// in returned blas64.Band.
|
||||
func (d *DiagDense) RawBand() blas64.Band {
|
||||
return blas64.Band{
|
||||
Rows: d.mat.N,
|
||||
Cols: d.mat.N,
|
||||
KL: 0,
|
||||
KU: 0,
|
||||
Stride: d.mat.Inc,
|
||||
Data: d.mat.Data,
|
||||
}
|
||||
}
|
||||
|
||||
// RawSymBand returns the underlying data used by the receiver represented
|
||||
// as a blas64.SymmetricBand.
|
||||
// Changes to elements in the receiver following the call will be reflected
|
||||
// in returned blas64.Band.
|
||||
func (d *DiagDense) RawSymBand() blas64.SymmetricBand {
|
||||
return blas64.SymmetricBand{
|
||||
N: d.mat.N,
|
||||
K: 0,
|
||||
Stride: d.mat.Inc,
|
||||
Uplo: blas.Upper,
|
||||
Data: d.mat.Data,
|
||||
}
|
||||
}
|
||||
|
||||
// reuseAs resizes an empty diagonal to a r×r diagonal,
|
||||
// or checks that a non-empty matrix is r×r.
|
||||
func (d *DiagDense) reuseAs(r int) {
|
||||
if r == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
if d.IsZero() {
|
||||
d.mat = blas64.Vector{
|
||||
Inc: 1,
|
||||
Data: use(d.mat.Data, r),
|
||||
}
|
||||
d.mat.N = r
|
||||
return
|
||||
}
|
||||
if r != d.mat.N {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
|
||||
// IsZero returns whether the receiver is zero-sized. Zero-sized vectors can be the
|
||||
// receiver for size-restricted operations. DiagDenses can be zeroed using Reset.
|
||||
func (d *DiagDense) IsZero() bool {
|
||||
// It must be the case that d.Dims() returns
|
||||
// zeros in this case. See comment in Reset().
|
||||
return d.mat.Inc == 0
|
||||
}
|
||||
169
vendor/gonum.org/v1/gonum/mat/doc.go
generated
vendored
Normal file
169
vendor/gonum.org/v1/gonum/mat/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
// Copyright ©2015 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package mat provides implementations of float64 and complex128 matrix
|
||||
// structures and linear algebra operations on them.
|
||||
//
|
||||
// Overview
|
||||
//
|
||||
// This section provides a quick overview of the mat package. The following
|
||||
// sections provide more in depth commentary.
|
||||
//
|
||||
// mat provides:
|
||||
// - Interfaces for Matrix classes (Matrix, Symmetric, Triangular)
|
||||
// - Concrete implementations (Dense, SymDense, TriDense)
|
||||
// - Methods and functions for using matrix data (Add, Trace, SymRankOne)
|
||||
// - Types for constructing and using matrix factorizations (QR, LU)
|
||||
// - The complementary types for complex matrices, CMatrix, CSymDense, etc.
|
||||
//
|
||||
// A matrix may be constructed through the corresponding New function. If no
|
||||
// backing array is provided the matrix will be initialized to all zeros.
|
||||
// // Allocate a zeroed real matrix of size 3×5
|
||||
// zero := mat.NewDense(3, 5, nil)
|
||||
// If a backing data slice is provided, the matrix will have those elements.
|
||||
// Matrices are all stored in row-major format.
|
||||
// // Generate a 6×6 matrix of random values.
|
||||
// data := make([]float64, 36)
|
||||
// for i := range data {
|
||||
// data[i] = rand.NormFloat64()
|
||||
// }
|
||||
// a := mat.NewDense(6, 6, data)
|
||||
// Operations involving matrix data are implemented as functions when the values
|
||||
// of the matrix remain unchanged
|
||||
// tr := mat.Trace(a)
|
||||
// and are implemented as methods when the operation modifies the receiver.
|
||||
// zero.Copy(a)
|
||||
//
|
||||
// Receivers must be the correct size for the matrix operations, otherwise the
|
||||
// operation will panic. As a special case for convenience, a zero-value matrix
|
||||
// will be modified to have the correct size, allocating data if necessary.
|
||||
// var c mat.Dense // construct a new zero-sized matrix
|
||||
// c.Mul(a, a) // c is automatically adjusted to be 6×6
|
||||
//
|
||||
// Zero-value of a matrix
|
||||
//
|
||||
// A zero-value matrix is either the Go language definition of a zero-value or
|
||||
// is a zero-sized matrix with zero-length stride. Matrix implementations may have
|
||||
// a Reset method to revert the receiver into a zero-valued matrix and an IsZero
|
||||
// method that returns whether the matrix is zero-valued.
|
||||
// So the following will all result in a zero-value matrix.
|
||||
// - var a mat.Dense
|
||||
// - a := NewDense(0, 0, make([]float64, 0, 100))
|
||||
// - a.Reset()
|
||||
// A zero-value matrix can not be sliced even if it does have an adequately sized
|
||||
// backing data slice, but can be expanded using its Grow method if it exists.
|
||||
//
|
||||
// The Matrix Interfaces
|
||||
//
|
||||
// The Matrix interface is the common link between the concrete types of real
|
||||
// matrices, The Matrix interface is defined by three functions: Dims, which
|
||||
// returns the dimensions of the Matrix, At, which returns the element in the
|
||||
// specified location, and T for returning a Transpose (discussed later). All of
|
||||
// the concrete types can perform these behaviors and so implement the interface.
|
||||
// Methods and functions are designed to use this interface, so in particular the method
|
||||
// func (m *Dense) Mul(a, b Matrix)
|
||||
// constructs a *Dense from the result of a multiplication with any Matrix types,
|
||||
// not just *Dense. Where more restrictive requirements must be met, there are also the
|
||||
// Symmetric and Triangular interfaces. For example, in
|
||||
// func (s *SymDense) AddSym(a, b Symmetric)
|
||||
// the Symmetric interface guarantees a symmetric result.
|
||||
//
|
||||
// The CMatrix interface plays the same role for complex matrices. The difference
|
||||
// is that the CMatrix type has the H method instead T, for returning the conjugate
|
||||
// transpose.
|
||||
//
|
||||
// (Conjugate) Transposes
|
||||
//
|
||||
// The T method is used for transposition on real matrices, and H is used for
|
||||
// conjugate transposition on complex matrices. For example, c.Mul(a.T(), b) computes
|
||||
// c = a^T * b. The mat types implement this method implicitly —
|
||||
// see the Transpose and Conjugate types for more details. Note that some
|
||||
// operations have a transpose as part of their definition, as in *SymDense.SymOuterK.
|
||||
//
|
||||
// Matrix Factorization
|
||||
//
|
||||
// Matrix factorizations, such as the LU decomposition, typically have their own
|
||||
// specific data storage, and so are each implemented as a specific type. The
|
||||
// factorization can be computed through a call to Factorize
|
||||
// var lu mat.LU
|
||||
// lu.Factorize(a)
|
||||
// The elements of the factorization can be extracted through methods on the
|
||||
// factorized type, i.e. *LU.UTo. The factorization types can also be used directly,
|
||||
// as in *Dense.SolveCholesky. Some factorizations can be updated directly,
|
||||
// without needing to update the original matrix and refactorize,
|
||||
// as in *LU.RankOne.
|
||||
//
|
||||
// BLAS and LAPACK
|
||||
//
|
||||
// BLAS and LAPACK are the standard APIs for linear algebra routines. Many
|
||||
// operations in mat are implemented using calls to the wrapper functions
|
||||
// in gonum/blas/blas64 and gonum/lapack/lapack64 and their complex equivalents.
|
||||
// By default, blas64 and lapack64 call the native Go implementations of the
|
||||
// routines. Alternatively, it is possible to use C-based implementations of the
|
||||
// APIs through the respective cgo packages and "Use" functions. The Go
|
||||
// implementation of LAPACK (used by default) makes calls
|
||||
// through blas64, so if a cgo BLAS implementation is registered, the lapack64
|
||||
// calls will be partially executed in Go and partially executed in C.
|
||||
//
|
||||
// Type Switching
|
||||
//
|
||||
// The Matrix abstraction enables efficiency as well as interoperability. Go's
|
||||
// type reflection capabilities are used to choose the most efficient routine
|
||||
// given the specific concrete types. For example, in
|
||||
// c.Mul(a, b)
|
||||
// if a and b both implement RawMatrixer, that is, they can be represented as a
|
||||
// blas64.General, blas64.Gemm (general matrix multiplication) is called, while
|
||||
// instead if b is a RawSymmetricer blas64.Symm is used (general-symmetric
|
||||
// multiplication), and if b is a *VecDense blas64.Gemv is used.
|
||||
//
|
||||
// There are many possible type combinations and special cases. No specific guarantees
|
||||
// are made about the performance of any method, and in particular, note that an
|
||||
// abstract matrix type may be copied into a concrete type of the corresponding
|
||||
// value. If there are specific special cases that are needed, please submit a
|
||||
// pull-request or file an issue.
|
||||
//
|
||||
// Invariants
|
||||
//
|
||||
// Matrix input arguments to functions are never directly modified. If an operation
|
||||
// changes Matrix data, the mutated matrix will be the receiver of a function.
|
||||
//
|
||||
// For convenience, a matrix may be used as both a receiver and as an input, e.g.
|
||||
// a.Pow(a, 6)
|
||||
// v.SolveVec(a.T(), v)
|
||||
// though in many cases this will cause an allocation (see Element Aliasing).
|
||||
// An exception to this rule is Copy, which does not allow a.Copy(a.T()).
|
||||
//
|
||||
// Element Aliasing
|
||||
//
|
||||
// Most methods in mat modify receiver data. It is forbidden for the modified
|
||||
// data region of the receiver to overlap the used data area of the input
|
||||
// arguments. The exception to this rule is when the method receiver is equal to one
|
||||
// of the input arguments, as in the a.Pow(a, 6) call above, or its implicit transpose.
|
||||
//
|
||||
// This prohibition is to help avoid subtle mistakes when the method needs to read
|
||||
// from and write to the same data region. There are ways to make mistakes using the
|
||||
// mat API, and mat functions will detect and complain about those.
|
||||
// There are many ways to make mistakes by excursion from the mat API via
|
||||
// interaction with raw matrix values.
|
||||
//
|
||||
// If you need to read the rest of this section to understand the behavior of
|
||||
// your program, you are being clever. Don't be clever. If you must be clever,
|
||||
// blas64 and lapack64 may be used to call the behavior directly.
|
||||
//
|
||||
// mat will use the following rules to detect overlap between the receiver and one
|
||||
// of the inputs:
|
||||
// - the input implements one of the Raw methods, and
|
||||
// - the address ranges of the backing data slices overlap, and
|
||||
// - the strides differ or there is an overlap in the used data elements.
|
||||
// If such an overlap is detected, the method will panic.
|
||||
//
|
||||
// The following cases will not panic:
|
||||
// - the data slices do not overlap,
|
||||
// - there is pointer identity between the receiver and input values after
|
||||
// the value has been untransposed if necessary.
|
||||
//
|
||||
// mat will not attempt to detect element overlap if the input does not implement a
|
||||
// Raw method. Method behavior is undefined if there is undetected overlap.
|
||||
//
|
||||
package mat // import "gonum.org/v1/gonum/mat"
|
||||
350
vendor/gonum.org/v1/gonum/mat/eigen.go
generated
vendored
Normal file
350
vendor/gonum.org/v1/gonum/mat/eigen.go
generated
vendored
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
// Copyright ©2013 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"gonum.org/v1/gonum/lapack"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
const (
|
||||
badFact = "mat: use without successful factorization"
|
||||
badNoVect = "mat: eigenvectors not computed"
|
||||
)
|
||||
|
||||
// EigenSym is a type for creating and manipulating the Eigen decomposition of
|
||||
// symmetric matrices.
|
||||
type EigenSym struct {
|
||||
vectorsComputed bool
|
||||
|
||||
values []float64
|
||||
vectors *Dense
|
||||
}
|
||||
|
||||
// Factorize computes the eigenvalue decomposition of the symmetric matrix a.
|
||||
// The Eigen decomposition is defined as
|
||||
// A = P * D * P^-1
|
||||
// where D is a diagonal matrix containing the eigenvalues of the matrix, and
|
||||
// P is a matrix of the eigenvectors of A. Factorize computes the eigenvalues
|
||||
// in ascending order. If the vectors input argument is false, the eigenvectors
|
||||
// are not computed.
|
||||
//
|
||||
// Factorize returns whether the decomposition succeeded. If the decomposition
|
||||
// failed, methods that require a successful factorization will panic.
|
||||
func (e *EigenSym) Factorize(a Symmetric, vectors bool) (ok bool) {
|
||||
// kill previous decomposition
|
||||
e.vectorsComputed = false
|
||||
e.values = e.values[:]
|
||||
|
||||
n := a.Symmetric()
|
||||
sd := NewSymDense(n, nil)
|
||||
sd.CopySym(a)
|
||||
|
||||
jobz := lapack.EVNone
|
||||
if vectors {
|
||||
jobz = lapack.EVCompute
|
||||
}
|
||||
w := make([]float64, n)
|
||||
work := []float64{0}
|
||||
lapack64.Syev(jobz, sd.mat, w, work, -1)
|
||||
|
||||
work = getFloats(int(work[0]), false)
|
||||
ok = lapack64.Syev(jobz, sd.mat, w, work, len(work))
|
||||
putFloats(work)
|
||||
if !ok {
|
||||
e.vectorsComputed = false
|
||||
e.values = nil
|
||||
e.vectors = nil
|
||||
return false
|
||||
}
|
||||
e.vectorsComputed = vectors
|
||||
e.values = w
|
||||
e.vectors = NewDense(n, n, sd.mat.Data)
|
||||
return true
|
||||
}
|
||||
|
||||
// succFact returns whether the receiver contains a successful factorization.
|
||||
func (e *EigenSym) succFact() bool {
|
||||
return len(e.values) != 0
|
||||
}
|
||||
|
||||
// Values extracts the eigenvalues of the factorized matrix. If dst is
|
||||
// non-nil, the values are stored in-place into dst. In this case
|
||||
// dst must have length n, otherwise Values will panic. If dst is
|
||||
// nil, then a new slice will be allocated of the proper length and filled
|
||||
// with the eigenvalues.
|
||||
//
|
||||
// Values panics if the Eigen decomposition was not successful.
|
||||
func (e *EigenSym) Values(dst []float64) []float64 {
|
||||
if !e.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if dst == nil {
|
||||
dst = make([]float64, len(e.values))
|
||||
}
|
||||
if len(dst) != len(e.values) {
|
||||
panic(ErrSliceLengthMismatch)
|
||||
}
|
||||
copy(dst, e.values)
|
||||
return dst
|
||||
}
|
||||
|
||||
// VectorsTo returns the eigenvectors of the decomposition. VectorsTo
|
||||
// will panic if the eigenvectors were not computed during the factorization,
|
||||
// or if the factorization was not successful.
|
||||
//
|
||||
// If dst is not nil, the eigenvectors are stored in-place into dst, and dst
|
||||
// must have size n×n and panics otherwise. If dst is nil, a new matrix
|
||||
// is allocated and returned.
|
||||
func (e *EigenSym) VectorsTo(dst *Dense) *Dense {
|
||||
if !e.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if !e.vectorsComputed {
|
||||
panic(badNoVect)
|
||||
}
|
||||
r, c := e.vectors.Dims()
|
||||
if dst == nil {
|
||||
dst = NewDense(r, c, nil)
|
||||
} else {
|
||||
dst.reuseAs(r, c)
|
||||
}
|
||||
dst.Copy(e.vectors)
|
||||
return dst
|
||||
}
|
||||
|
||||
// EigenKind specifies the computation of eigenvectors during factorization.
|
||||
type EigenKind int
|
||||
|
||||
const (
|
||||
// EigenNone specifies to not compute any eigenvectors.
|
||||
EigenNone EigenKind = 0
|
||||
// EigenLeft specifies to compute the left eigenvectors.
|
||||
EigenLeft EigenKind = 1 << iota
|
||||
// EigenRight specifies to compute the right eigenvectors.
|
||||
EigenRight
|
||||
// EigenBoth is a convenience value for computing both eigenvectors.
|
||||
EigenBoth EigenKind = EigenLeft | EigenRight
|
||||
)
|
||||
|
||||
// Eigen is a type for creating and using the eigenvalue decomposition of a dense matrix.
|
||||
type Eigen struct {
|
||||
n int // The size of the factorized matrix.
|
||||
|
||||
kind EigenKind
|
||||
|
||||
values []complex128
|
||||
rVectors *CDense
|
||||
lVectors *CDense
|
||||
}
|
||||
|
||||
// succFact returns whether the receiver contains a successful factorization.
|
||||
func (e *Eigen) succFact() bool {
|
||||
return e.n != 0
|
||||
}
|
||||
|
||||
// Factorize computes the eigenvalues of the square matrix a, and optionally
|
||||
// the eigenvectors.
|
||||
//
|
||||
// A right eigenvalue/eigenvector combination is defined by
|
||||
// A * x_r = λ * x_r
|
||||
// where x_r is the column vector called an eigenvector, and λ is the corresponding
|
||||
// eigenvalue.
|
||||
//
|
||||
// Similarly, a left eigenvalue/eigenvector combination is defined by
|
||||
// x_l * A = λ * x_l
|
||||
// The eigenvalues, but not the eigenvectors, are the same for both decompositions.
|
||||
//
|
||||
// Typically eigenvectors refer to right eigenvectors.
|
||||
//
|
||||
// In all cases, Factorize computes the eigenvalues of the matrix. kind
|
||||
// specifies which of the eigenvectors, if any, to compute. See the EigenKind
|
||||
// documentation for more information.
|
||||
// Eigen panics if the input matrix is not square.
|
||||
//
|
||||
// Factorize returns whether the decomposition succeeded. If the decomposition
|
||||
// failed, methods that require a successful factorization will panic.
|
||||
func (e *Eigen) Factorize(a Matrix, kind EigenKind) (ok bool) {
|
||||
// kill previous factorization.
|
||||
e.n = 0
|
||||
e.kind = 0
|
||||
// Copy a because it is modified during the Lapack call.
|
||||
r, c := a.Dims()
|
||||
if r != c {
|
||||
panic(ErrShape)
|
||||
}
|
||||
var sd Dense
|
||||
sd.Clone(a)
|
||||
|
||||
left := kind&EigenLeft != 0
|
||||
right := kind&EigenRight != 0
|
||||
|
||||
var vl, vr Dense
|
||||
jobvl := lapack.LeftEVNone
|
||||
jobvr := lapack.RightEVNone
|
||||
if left {
|
||||
vl = *NewDense(r, r, nil)
|
||||
jobvl = lapack.LeftEVCompute
|
||||
}
|
||||
if right {
|
||||
vr = *NewDense(c, c, nil)
|
||||
jobvr = lapack.RightEVCompute
|
||||
}
|
||||
|
||||
wr := getFloats(c, false)
|
||||
defer putFloats(wr)
|
||||
wi := getFloats(c, false)
|
||||
defer putFloats(wi)
|
||||
|
||||
work := []float64{0}
|
||||
lapack64.Geev(jobvl, jobvr, sd.mat, wr, wi, vl.mat, vr.mat, work, -1)
|
||||
work = getFloats(int(work[0]), false)
|
||||
first := lapack64.Geev(jobvl, jobvr, sd.mat, wr, wi, vl.mat, vr.mat, work, len(work))
|
||||
putFloats(work)
|
||||
|
||||
if first != 0 {
|
||||
e.values = nil
|
||||
return false
|
||||
}
|
||||
e.n = r
|
||||
e.kind = kind
|
||||
|
||||
// Construct complex eigenvalues from float64 data.
|
||||
values := make([]complex128, r)
|
||||
for i, v := range wr {
|
||||
values[i] = complex(v, wi[i])
|
||||
}
|
||||
e.values = values
|
||||
|
||||
// Construct complex eigenvectors from float64 data.
|
||||
var cvl, cvr CDense
|
||||
if left {
|
||||
cvl = *NewCDense(r, r, nil)
|
||||
e.complexEigenTo(&cvl, &vl)
|
||||
e.lVectors = &cvl
|
||||
} else {
|
||||
e.lVectors = nil
|
||||
}
|
||||
if right {
|
||||
cvr = *NewCDense(c, c, nil)
|
||||
e.complexEigenTo(&cvr, &vr)
|
||||
e.rVectors = &cvr
|
||||
} else {
|
||||
e.rVectors = nil
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Kind returns the EigenKind of the decomposition. If no decomposition has been
|
||||
// computed, Kind returns -1.
|
||||
func (e *Eigen) Kind() EigenKind {
|
||||
if !e.succFact() {
|
||||
return -1
|
||||
}
|
||||
return e.kind
|
||||
}
|
||||
|
||||
// Values extracts the eigenvalues of the factorized matrix. If dst is
|
||||
// non-nil, the values are stored in-place into dst. In this case
|
||||
// dst must have length n, otherwise Values will panic. If dst is
|
||||
// nil, then a new slice will be allocated of the proper length and
|
||||
// filed with the eigenvalues.
|
||||
//
|
||||
// Values panics if the Eigen decomposition was not successful.
|
||||
func (e *Eigen) Values(dst []complex128) []complex128 {
|
||||
if !e.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if dst == nil {
|
||||
dst = make([]complex128, e.n)
|
||||
}
|
||||
if len(dst) != e.n {
|
||||
panic(ErrSliceLengthMismatch)
|
||||
}
|
||||
copy(dst, e.values)
|
||||
return dst
|
||||
}
|
||||
|
||||
// complexEigenTo extracts the complex eigenvectors from the real matrix d
|
||||
// and stores them into the complex matrix dst.
|
||||
//
|
||||
// The columns of the returned n×n dense matrix contain the eigenvectors of the
|
||||
// decomposition in the same order as the eigenvalues.
|
||||
// If the j-th eigenvalue is real, then
|
||||
// dst[:,j] = d[:,j],
|
||||
// and if it is not real, then the elements of the j-th and (j+1)-th columns of d
|
||||
// form complex conjugate pairs and the eigenvectors are recovered as
|
||||
// dst[:,j] = d[:,j] + i*d[:,j+1],
|
||||
// dst[:,j+1] = d[:,j] - i*d[:,j+1],
|
||||
// where i is the imaginary unit.
|
||||
func (e *Eigen) complexEigenTo(dst *CDense, d *Dense) {
|
||||
r, c := d.Dims()
|
||||
cr, cc := dst.Dims()
|
||||
if r != cr {
|
||||
panic("size mismatch")
|
||||
}
|
||||
if c != cc {
|
||||
panic("size mismatch")
|
||||
}
|
||||
for j := 0; j < c; j++ {
|
||||
if imag(e.values[j]) == 0 {
|
||||
for i := 0; i < r; i++ {
|
||||
dst.set(i, j, complex(d.at(i, j), 0))
|
||||
}
|
||||
continue
|
||||
}
|
||||
for i := 0; i < r; i++ {
|
||||
real := d.at(i, j)
|
||||
imag := d.at(i, j+1)
|
||||
dst.set(i, j, complex(real, imag))
|
||||
dst.set(i, j+1, complex(real, -imag))
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
// VectorsTo returns the right eigenvectors of the decomposition. VectorsTo
|
||||
// will panic if the right eigenvectors were not computed during the factorization,
|
||||
// or if the factorization was not successful.
|
||||
//
|
||||
// The computed eigenvectors are normalized to have Euclidean norm equal to 1
|
||||
// and largest component real.
|
||||
func (e *Eigen) VectorsTo(dst *CDense) *CDense {
|
||||
if !e.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if e.kind&EigenRight == 0 {
|
||||
panic(badNoVect)
|
||||
}
|
||||
if dst == nil {
|
||||
dst = NewCDense(e.n, e.n, nil)
|
||||
} else {
|
||||
dst.reuseAs(e.n, e.n)
|
||||
}
|
||||
dst.Copy(e.rVectors)
|
||||
return dst
|
||||
}
|
||||
|
||||
// LeftVectorsTo returns the left eigenvectors of the decomposition. LeftVectorsTo
|
||||
// will panic if the left eigenvectors were not computed during the factorization,
|
||||
// or if the factorization was not successful.
|
||||
//
|
||||
// The computed eigenvectors are normalized to have Euclidean norm equal to 1
|
||||
// and largest component real.
|
||||
func (e *Eigen) LeftVectorsTo(dst *CDense) *CDense {
|
||||
if !e.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if e.kind&EigenLeft == 0 {
|
||||
panic(badNoVect)
|
||||
}
|
||||
if dst == nil {
|
||||
dst = NewCDense(e.n, e.n, nil)
|
||||
} else {
|
||||
dst.reuseAs(e.n, e.n)
|
||||
}
|
||||
dst.Copy(e.lVectors)
|
||||
return dst
|
||||
}
|
||||
149
vendor/gonum.org/v1/gonum/mat/errors.go
generated
vendored
Normal file
149
vendor/gonum.org/v1/gonum/mat/errors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
// Copyright ©2013 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"gonum.org/v1/gonum/lapack"
|
||||
)
|
||||
|
||||
// Condition is the condition number of a matrix. The condition
|
||||
// number is defined as |A| * |A^-1|.
|
||||
//
|
||||
// One important use of Condition is during linear solve routines (finding x such
|
||||
// that A * x = b). The condition number of A indicates the accuracy of
|
||||
// the computed solution. A Condition error will be returned if the condition
|
||||
// number of A is sufficiently large. If A is exactly singular to working precision,
|
||||
// Condition == ∞, and the solve algorithm may have completed early. If Condition
|
||||
// is large and finite the solve algorithm will be performed, but the computed
|
||||
// solution may be innacurate. Due to the nature of finite precision arithmetic,
|
||||
// the value of Condition is only an approximate test of singularity.
|
||||
type Condition float64
|
||||
|
||||
func (c Condition) Error() string {
|
||||
return fmt.Sprintf("matrix singular or near-singular with condition number %.4e", c)
|
||||
}
|
||||
|
||||
// ConditionTolerance is the tolerance limit of the condition number. If the
|
||||
// condition number is above this value, the matrix is considered singular.
|
||||
const ConditionTolerance = 1e16
|
||||
|
||||
const (
|
||||
// CondNorm is the matrix norm used for computing the condition number by routines
|
||||
// in the matrix packages.
|
||||
CondNorm = lapack.MaxRowSum
|
||||
|
||||
// CondNormTrans is the norm used to compute on A^T to get the same result as
|
||||
// computing CondNorm on A.
|
||||
CondNormTrans = lapack.MaxColumnSum
|
||||
)
|
||||
|
||||
const stackTraceBufferSize = 1 << 20
|
||||
|
||||
// Maybe will recover a panic with a type mat.Error from fn, and return this error
|
||||
// as the Err field of an ErrorStack. The stack trace for the panicking function will be
|
||||
// recovered and placed in the StackTrace field. Any other error is re-panicked.
|
||||
func Maybe(fn func()) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if e, ok := r.(Error); ok {
|
||||
if e.string == "" {
|
||||
panic("mat: invalid error")
|
||||
}
|
||||
buf := make([]byte, stackTraceBufferSize)
|
||||
n := runtime.Stack(buf, false)
|
||||
err = ErrorStack{Err: e, StackTrace: string(buf[:n])}
|
||||
return
|
||||
}
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
fn()
|
||||
return
|
||||
}
|
||||
|
||||
// MaybeFloat will recover a panic with a type mat.Error from fn, and return this error
|
||||
// as the Err field of an ErrorStack. The stack trace for the panicking function will be
|
||||
// recovered and placed in the StackTrace field. Any other error is re-panicked.
|
||||
func MaybeFloat(fn func() float64) (f float64, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if e, ok := r.(Error); ok {
|
||||
if e.string == "" {
|
||||
panic("mat: invalid error")
|
||||
}
|
||||
buf := make([]byte, stackTraceBufferSize)
|
||||
n := runtime.Stack(buf, false)
|
||||
err = ErrorStack{Err: e, StackTrace: string(buf[:n])}
|
||||
return
|
||||
}
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
return fn(), nil
|
||||
}
|
||||
|
||||
// MaybeComplex will recover a panic with a type mat.Error from fn, and return this error
|
||||
// as the Err field of an ErrorStack. The stack trace for the panicking function will be
|
||||
// recovered and placed in the StackTrace field. Any other error is re-panicked.
|
||||
func MaybeComplex(fn func() complex128) (f complex128, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if e, ok := r.(Error); ok {
|
||||
if e.string == "" {
|
||||
panic("mat: invalid error")
|
||||
}
|
||||
buf := make([]byte, stackTraceBufferSize)
|
||||
n := runtime.Stack(buf, false)
|
||||
err = ErrorStack{Err: e, StackTrace: string(buf[:n])}
|
||||
return
|
||||
}
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
return fn(), nil
|
||||
}
|
||||
|
||||
// Error represents matrix handling errors. These errors can be recovered by Maybe wrappers.
|
||||
type Error struct{ string }
|
||||
|
||||
func (err Error) Error() string { return err.string }
|
||||
|
||||
var (
|
||||
ErrIndexOutOfRange = Error{"matrix: index out of range"}
|
||||
ErrRowAccess = Error{"matrix: row index out of range"}
|
||||
ErrColAccess = Error{"matrix: column index out of range"}
|
||||
ErrVectorAccess = Error{"matrix: vector index out of range"}
|
||||
ErrZeroLength = Error{"matrix: zero length in matrix dimension"}
|
||||
ErrRowLength = Error{"matrix: row length mismatch"}
|
||||
ErrColLength = Error{"matrix: col length mismatch"}
|
||||
ErrSquare = Error{"matrix: expect square matrix"}
|
||||
ErrNormOrder = Error{"matrix: invalid norm order for matrix"}
|
||||
ErrSingular = Error{"matrix: matrix is singular"}
|
||||
ErrShape = Error{"matrix: dimension mismatch"}
|
||||
ErrIllegalStride = Error{"matrix: illegal stride"}
|
||||
ErrPivot = Error{"matrix: malformed pivot list"}
|
||||
ErrTriangle = Error{"matrix: triangular storage mismatch"}
|
||||
ErrTriangleSet = Error{"matrix: triangular set out of bounds"}
|
||||
ErrBandSet = Error{"matrix: band set out of bounds"}
|
||||
ErrDiagSet = Error{"matrix: diagonal set out of bounds"}
|
||||
ErrSliceLengthMismatch = Error{"matrix: input slice length mismatch"}
|
||||
ErrNotPSD = Error{"matrix: input not positive symmetric definite"}
|
||||
ErrFailedEigen = Error{"matrix: eigendecomposition not successful"}
|
||||
)
|
||||
|
||||
// ErrorStack represents matrix handling errors that have been recovered by Maybe wrappers.
|
||||
type ErrorStack struct {
|
||||
Err error
|
||||
|
||||
// StackTrace is the stack trace
|
||||
// recovered by Maybe, MaybeFloat
|
||||
// or MaybeComplex.
|
||||
StackTrace string
|
||||
}
|
||||
|
||||
func (err ErrorStack) Error() string { return err.Err.Error() }
|
||||
238
vendor/gonum.org/v1/gonum/mat/format.go
generated
vendored
Normal file
238
vendor/gonum.org/v1/gonum/mat/format.go
generated
vendored
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
// Copyright ©2013 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Formatted returns a fmt.Formatter for the matrix m using the given options.
|
||||
func Formatted(m Matrix, options ...FormatOption) fmt.Formatter {
|
||||
f := formatter{
|
||||
matrix: m,
|
||||
dot: '.',
|
||||
}
|
||||
for _, o := range options {
|
||||
o(&f)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
type formatter struct {
|
||||
matrix Matrix
|
||||
prefix string
|
||||
margin int
|
||||
dot byte
|
||||
squeeze bool
|
||||
}
|
||||
|
||||
// FormatOption is a functional option for matrix formatting.
|
||||
type FormatOption func(*formatter)
|
||||
|
||||
// Prefix sets the formatted prefix to the string p. Prefix is a string that is prepended to
|
||||
// each line of output.
|
||||
func Prefix(p string) FormatOption {
|
||||
return func(f *formatter) { f.prefix = p }
|
||||
}
|
||||
|
||||
// Excerpt sets the maximum number of rows and columns to print at the margins of the matrix
|
||||
// to m. If m is zero or less all elements are printed.
|
||||
func Excerpt(m int) FormatOption {
|
||||
return func(f *formatter) { f.margin = m }
|
||||
}
|
||||
|
||||
// DotByte sets the dot character to b. The dot character is used to replace zero elements
|
||||
// if the result is printed with the fmt ' ' verb flag. Without a DotByte option, the default
|
||||
// dot character is '.'.
|
||||
func DotByte(b byte) FormatOption {
|
||||
return func(f *formatter) { f.dot = b }
|
||||
}
|
||||
|
||||
// Squeeze sets the printing behaviour to minimise column width for each individual column.
|
||||
func Squeeze() FormatOption {
|
||||
return func(f *formatter) { f.squeeze = true }
|
||||
}
|
||||
|
||||
// Format satisfies the fmt.Formatter interface.
|
||||
func (f formatter) Format(fs fmt.State, c rune) {
|
||||
if c == 'v' && fs.Flag('#') {
|
||||
fmt.Fprintf(fs, "%#v", f.matrix)
|
||||
return
|
||||
}
|
||||
format(f.matrix, f.prefix, f.margin, f.dot, f.squeeze, fs, c)
|
||||
}
|
||||
|
||||
// format prints a pretty representation of m to the fs io.Writer. The format character c
|
||||
// specifies the numerical representation of elements; valid values are those for float64
|
||||
// specified in the fmt package, with their associated flags. In addition to this, a space
|
||||
// preceding a verb indicates that zero values should be represented by the dot character.
|
||||
// The printed range of the matrix can be limited by specifying a positive value for margin;
|
||||
// If margin is greater than zero, only the first and last margin rows/columns of the matrix
|
||||
// are output. If squeeze is true, column widths are determined on a per-column basis.
|
||||
//
|
||||
// format will not provide Go syntax output.
|
||||
func format(m Matrix, prefix string, margin int, dot byte, squeeze bool, fs fmt.State, c rune) {
|
||||
rows, cols := m.Dims()
|
||||
|
||||
var printed int
|
||||
if margin <= 0 {
|
||||
printed = rows
|
||||
if cols > printed {
|
||||
printed = cols
|
||||
}
|
||||
} else {
|
||||
printed = margin
|
||||
}
|
||||
|
||||
prec, pOk := fs.Precision()
|
||||
if !pOk {
|
||||
prec = -1
|
||||
}
|
||||
|
||||
var (
|
||||
maxWidth int
|
||||
widths widther
|
||||
buf, pad []byte
|
||||
)
|
||||
if squeeze {
|
||||
widths = make(columnWidth, cols)
|
||||
} else {
|
||||
widths = new(uniformWidth)
|
||||
}
|
||||
switch c {
|
||||
case 'v', 'e', 'E', 'f', 'F', 'g', 'G':
|
||||
if c == 'v' {
|
||||
buf, maxWidth = maxCellWidth(m, 'g', printed, prec, widths)
|
||||
} else {
|
||||
buf, maxWidth = maxCellWidth(m, c, printed, prec, widths)
|
||||
}
|
||||
default:
|
||||
fmt.Fprintf(fs, "%%!%c(%T=Dims(%d, %d))", c, m, rows, cols)
|
||||
return
|
||||
}
|
||||
width, _ := fs.Width()
|
||||
width = max(width, maxWidth)
|
||||
pad = make([]byte, max(width, 2))
|
||||
for i := range pad {
|
||||
pad[i] = ' '
|
||||
}
|
||||
|
||||
first := true
|
||||
if rows > 2*printed || cols > 2*printed {
|
||||
first = false
|
||||
fmt.Fprintf(fs, "Dims(%d, %d)\n", rows, cols)
|
||||
}
|
||||
|
||||
skipZero := fs.Flag(' ')
|
||||
for i := 0; i < rows; i++ {
|
||||
if !first {
|
||||
fmt.Fprint(fs, prefix)
|
||||
}
|
||||
first = false
|
||||
var el string
|
||||
switch {
|
||||
case rows == 1:
|
||||
fmt.Fprint(fs, "[")
|
||||
el = "]"
|
||||
case i == 0:
|
||||
fmt.Fprint(fs, "⎡")
|
||||
el = "⎤\n"
|
||||
case i < rows-1:
|
||||
fmt.Fprint(fs, "⎢")
|
||||
el = "⎥\n"
|
||||
default:
|
||||
fmt.Fprint(fs, "⎣")
|
||||
el = "⎦"
|
||||
}
|
||||
|
||||
for j := 0; j < cols; j++ {
|
||||
if j >= printed && j < cols-printed {
|
||||
j = cols - printed - 1
|
||||
if i == 0 || i == rows-1 {
|
||||
fmt.Fprint(fs, "... ... ")
|
||||
} else {
|
||||
fmt.Fprint(fs, " ")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
v := m.At(i, j)
|
||||
if v == 0 && skipZero {
|
||||
buf = buf[:1]
|
||||
buf[0] = dot
|
||||
} else {
|
||||
if c == 'v' {
|
||||
buf = strconv.AppendFloat(buf[:0], v, 'g', prec, 64)
|
||||
} else {
|
||||
buf = strconv.AppendFloat(buf[:0], v, byte(c), prec, 64)
|
||||
}
|
||||
}
|
||||
if fs.Flag('-') {
|
||||
fs.Write(buf)
|
||||
fs.Write(pad[:widths.width(j)-len(buf)])
|
||||
} else {
|
||||
fs.Write(pad[:widths.width(j)-len(buf)])
|
||||
fs.Write(buf)
|
||||
}
|
||||
|
||||
if j < cols-1 {
|
||||
fs.Write(pad[:2])
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprint(fs, el)
|
||||
|
||||
if i >= printed-1 && i < rows-printed && 2*printed < rows {
|
||||
i = rows - printed - 1
|
||||
fmt.Fprintf(fs, "%s .\n%[1]s .\n%[1]s .\n", prefix)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func maxCellWidth(m Matrix, c rune, printed, prec int, w widther) ([]byte, int) {
|
||||
var (
|
||||
buf = make([]byte, 0, 64)
|
||||
rows, cols = m.Dims()
|
||||
max int
|
||||
)
|
||||
for i := 0; i < rows; i++ {
|
||||
if i >= printed-1 && i < rows-printed && 2*printed < rows {
|
||||
i = rows - printed - 1
|
||||
continue
|
||||
}
|
||||
for j := 0; j < cols; j++ {
|
||||
if j >= printed && j < cols-printed {
|
||||
continue
|
||||
}
|
||||
|
||||
buf = strconv.AppendFloat(buf, m.At(i, j), byte(c), prec, 64)
|
||||
if len(buf) > max {
|
||||
max = len(buf)
|
||||
}
|
||||
if len(buf) > w.width(j) {
|
||||
w.setWidth(j, len(buf))
|
||||
}
|
||||
buf = buf[:0]
|
||||
}
|
||||
}
|
||||
return buf, max
|
||||
}
|
||||
|
||||
type widther interface {
|
||||
width(i int) int
|
||||
setWidth(i, w int)
|
||||
}
|
||||
|
||||
type uniformWidth int
|
||||
|
||||
func (u *uniformWidth) width(_ int) int { return int(*u) }
|
||||
func (u *uniformWidth) setWidth(_, w int) { *u = uniformWidth(w) }
|
||||
|
||||
type columnWidth []int
|
||||
|
||||
func (c columnWidth) width(i int) int { return c[i] }
|
||||
func (c columnWidth) setWidth(i, w int) { c[i] = w }
|
||||
415
vendor/gonum.org/v1/gonum/mat/gsvd.go
generated
vendored
Normal file
415
vendor/gonum.org/v1/gonum/mat/gsvd.go
generated
vendored
Normal file
|
|
@ -0,0 +1,415 @@
|
|||
// Copyright ©2017 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
"gonum.org/v1/gonum/floats"
|
||||
"gonum.org/v1/gonum/lapack"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
// GSVDKind specifies the treatment of singular vectors during a GSVD
|
||||
// factorization.
|
||||
type GSVDKind int
|
||||
|
||||
const (
|
||||
// GSVDNone specifies that no singular vectors should be computed during
|
||||
// the decomposition.
|
||||
GSVDNone GSVDKind = 0
|
||||
|
||||
// GSVDU specifies that the U singular vectors should be computed during
|
||||
// the decomposition.
|
||||
GSVDU GSVDKind = 1 << iota
|
||||
// GSVDV specifies that the V singular vectors should be computed during
|
||||
// the decomposition.
|
||||
GSVDV
|
||||
// GSVDQ specifies that the Q singular vectors should be computed during
|
||||
// the decomposition.
|
||||
GSVDQ
|
||||
|
||||
// GSVDAll is a convenience value for computing all of the singular vectors.
|
||||
GSVDAll = GSVDU | GSVDV | GSVDQ
|
||||
)
|
||||
|
||||
// GSVD is a type for creating and using the Generalized Singular Value Decomposition
|
||||
// (GSVD) of a matrix.
|
||||
//
|
||||
// The factorization is a linear transformation of the data sets from the given
|
||||
// variable×sample spaces to reduced and diagonalized "eigenvariable"×"eigensample"
|
||||
// spaces.
|
||||
type GSVD struct {
|
||||
kind GSVDKind
|
||||
|
||||
r, p, c, k, l int
|
||||
s1, s2 []float64
|
||||
a, b, u, v, q blas64.General
|
||||
|
||||
work []float64
|
||||
iwork []int
|
||||
}
|
||||
|
||||
// succFact returns whether the receiver contains a successful factorization.
|
||||
func (gsvd *GSVD) succFact() bool {
|
||||
return gsvd.r != 0
|
||||
}
|
||||
|
||||
// Factorize computes the generalized singular value decomposition (GSVD) of the input
|
||||
// the r×c matrix A and the p×c matrix B. The singular values of A and B are computed
|
||||
// in all cases, while the singular vectors are optionally computed depending on the
|
||||
// input kind.
|
||||
//
|
||||
// The full singular value decomposition (kind == GSVDAll) deconstructs A and B as
|
||||
// A = U * Σ₁ * [ 0 R ] * Q^T
|
||||
//
|
||||
// B = V * Σ₂ * [ 0 R ] * Q^T
|
||||
// where Σ₁ and Σ₂ are r×(k+l) and p×(k+l) diagonal matrices of singular values, and
|
||||
// U, V and Q are r×r, p×p and c×c orthogonal matrices of singular vectors. k+l is the
|
||||
// effective numerical rank of the matrix [ A^T B^T ]^T.
|
||||
//
|
||||
// It is frequently not necessary to compute the full GSVD. Computation time and
|
||||
// storage costs can be reduced using the appropriate kind. Either only the singular
|
||||
// values can be computed (kind == SVDNone), or in conjunction with specific singular
|
||||
// vectors (kind bit set according to matrix.GSVDU, matrix.GSVDV and matrix.GSVDQ).
|
||||
//
|
||||
// Factorize returns whether the decomposition succeeded. If the decomposition
|
||||
// failed, routines that require a successful factorization will panic.
|
||||
func (gsvd *GSVD) Factorize(a, b Matrix, kind GSVDKind) (ok bool) {
|
||||
// kill the previous decomposition
|
||||
gsvd.r = 0
|
||||
gsvd.kind = 0
|
||||
|
||||
r, c := a.Dims()
|
||||
gsvd.r, gsvd.c = r, c
|
||||
p, c := b.Dims()
|
||||
gsvd.p = p
|
||||
if gsvd.c != c {
|
||||
panic(ErrShape)
|
||||
}
|
||||
var jobU, jobV, jobQ lapack.GSVDJob
|
||||
switch {
|
||||
default:
|
||||
panic("gsvd: bad input kind")
|
||||
case kind == GSVDNone:
|
||||
jobU = lapack.GSVDNone
|
||||
jobV = lapack.GSVDNone
|
||||
jobQ = lapack.GSVDNone
|
||||
case GSVDAll&kind != 0:
|
||||
if GSVDU&kind != 0 {
|
||||
jobU = lapack.GSVDU
|
||||
gsvd.u = blas64.General{
|
||||
Rows: r,
|
||||
Cols: r,
|
||||
Stride: r,
|
||||
Data: use(gsvd.u.Data, r*r),
|
||||
}
|
||||
}
|
||||
if GSVDV&kind != 0 {
|
||||
jobV = lapack.GSVDV
|
||||
gsvd.v = blas64.General{
|
||||
Rows: p,
|
||||
Cols: p,
|
||||
Stride: p,
|
||||
Data: use(gsvd.v.Data, p*p),
|
||||
}
|
||||
}
|
||||
if GSVDQ&kind != 0 {
|
||||
jobQ = lapack.GSVDQ
|
||||
gsvd.q = blas64.General{
|
||||
Rows: c,
|
||||
Cols: c,
|
||||
Stride: c,
|
||||
Data: use(gsvd.q.Data, c*c),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A and B are destroyed on call, so copy the matrices.
|
||||
aCopy := DenseCopyOf(a)
|
||||
bCopy := DenseCopyOf(b)
|
||||
|
||||
gsvd.s1 = use(gsvd.s1, c)
|
||||
gsvd.s2 = use(gsvd.s2, c)
|
||||
|
||||
gsvd.iwork = useInt(gsvd.iwork, c)
|
||||
|
||||
gsvd.work = use(gsvd.work, 1)
|
||||
lapack64.Ggsvd3(jobU, jobV, jobQ, aCopy.mat, bCopy.mat, gsvd.s1, gsvd.s2, gsvd.u, gsvd.v, gsvd.q, gsvd.work, -1, gsvd.iwork)
|
||||
gsvd.work = use(gsvd.work, int(gsvd.work[0]))
|
||||
gsvd.k, gsvd.l, ok = lapack64.Ggsvd3(jobU, jobV, jobQ, aCopy.mat, bCopy.mat, gsvd.s1, gsvd.s2, gsvd.u, gsvd.v, gsvd.q, gsvd.work, len(gsvd.work), gsvd.iwork)
|
||||
if ok {
|
||||
gsvd.a = aCopy.mat
|
||||
gsvd.b = bCopy.mat
|
||||
gsvd.kind = kind
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// Kind returns the GSVDKind of the decomposition. If no decomposition has been
|
||||
// computed, Kind returns -1.
|
||||
func (gsvd *GSVD) Kind() GSVDKind {
|
||||
if !gsvd.succFact() {
|
||||
return -1
|
||||
}
|
||||
return gsvd.kind
|
||||
}
|
||||
|
||||
// Rank returns the k and l terms of the rank of [ A^T B^T ]^T.
|
||||
func (gsvd *GSVD) Rank() (k, l int) {
|
||||
return gsvd.k, gsvd.l
|
||||
}
|
||||
|
||||
// GeneralizedValues returns the generalized singular values of the factorized matrices.
|
||||
// If the input slice is non-nil, the values will be stored in-place into the slice.
|
||||
// In this case, the slice must have length min(r,c)-k, and GeneralizedValues will
|
||||
// panic with matrix.ErrSliceLengthMismatch otherwise. If the input slice is nil,
|
||||
// a new slice of the appropriate length will be allocated and returned.
|
||||
//
|
||||
// GeneralizedValues will panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *GSVD) GeneralizedValues(v []float64) []float64 {
|
||||
if !gsvd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
r := gsvd.r
|
||||
c := gsvd.c
|
||||
k := gsvd.k
|
||||
d := min(r, c)
|
||||
if v == nil {
|
||||
v = make([]float64, d-k)
|
||||
}
|
||||
if len(v) != d-k {
|
||||
panic(ErrSliceLengthMismatch)
|
||||
}
|
||||
floats.DivTo(v, gsvd.s1[k:d], gsvd.s2[k:d])
|
||||
return v
|
||||
}
|
||||
|
||||
// ValuesA returns the singular values of the factorized A matrix.
|
||||
// If the input slice is non-nil, the values will be stored in-place into the slice.
|
||||
// In this case, the slice must have length min(r,c)-k, and ValuesA will panic with
|
||||
// matrix.ErrSliceLengthMismatch otherwise. If the input slice is nil,
|
||||
// a new slice of the appropriate length will be allocated and returned.
|
||||
//
|
||||
// ValuesA will panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *GSVD) ValuesA(s []float64) []float64 {
|
||||
if !gsvd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
r := gsvd.r
|
||||
c := gsvd.c
|
||||
k := gsvd.k
|
||||
d := min(r, c)
|
||||
if s == nil {
|
||||
s = make([]float64, d-k)
|
||||
}
|
||||
if len(s) != d-k {
|
||||
panic(ErrSliceLengthMismatch)
|
||||
}
|
||||
copy(s, gsvd.s1[k:min(r, c)])
|
||||
return s
|
||||
}
|
||||
|
||||
// ValuesB returns the singular values of the factorized B matrix.
|
||||
// If the input slice is non-nil, the values will be stored in-place into the slice.
|
||||
// In this case, the slice must have length min(r,c)-k, and ValuesB will panic with
|
||||
// matrix.ErrSliceLengthMismatch otherwise. If the input slice is nil,
|
||||
// a new slice of the appropriate length will be allocated and returned.
|
||||
//
|
||||
// ValuesB will panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *GSVD) ValuesB(s []float64) []float64 {
|
||||
if !gsvd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
r := gsvd.r
|
||||
c := gsvd.c
|
||||
k := gsvd.k
|
||||
d := min(r, c)
|
||||
if s == nil {
|
||||
s = make([]float64, d-k)
|
||||
}
|
||||
if len(s) != d-k {
|
||||
panic(ErrSliceLengthMismatch)
|
||||
}
|
||||
copy(s, gsvd.s2[k:d])
|
||||
return s
|
||||
}
|
||||
|
||||
// ZeroRTo extracts the matrix [ 0 R ] from the singular value decomposition, storing
|
||||
// the result in-place into dst. [ 0 R ] is size (k+l)×c.
|
||||
// If dst is nil, a new matrix is allocated. The resulting ZeroR matrix is returned.
|
||||
//
|
||||
// ZeroRTo will panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *GSVD) ZeroRTo(dst *Dense) *Dense {
|
||||
if !gsvd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
r := gsvd.r
|
||||
c := gsvd.c
|
||||
k := gsvd.k
|
||||
l := gsvd.l
|
||||
h := min(k+l, r)
|
||||
if dst == nil {
|
||||
dst = NewDense(k+l, c, nil)
|
||||
} else {
|
||||
dst.reuseAsZeroed(k+l, c)
|
||||
}
|
||||
a := Dense{
|
||||
mat: gsvd.a,
|
||||
capRows: r,
|
||||
capCols: c,
|
||||
}
|
||||
dst.Slice(0, h, c-k-l, c).(*Dense).
|
||||
Copy(a.Slice(0, h, c-k-l, c))
|
||||
if r < k+l {
|
||||
b := Dense{
|
||||
mat: gsvd.b,
|
||||
capRows: gsvd.p,
|
||||
capCols: c,
|
||||
}
|
||||
dst.Slice(r, k+l, c+r-k-l, c).(*Dense).
|
||||
Copy(b.Slice(r-k, l, c+r-k-l, c))
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// SigmaATo extracts the matrix Σ₁ from the singular value decomposition, storing
|
||||
// the result in-place into dst. Σ₁ is size r×(k+l).
|
||||
// If dst is nil, a new matrix is allocated. The resulting SigmaA matrix is returned.
|
||||
//
|
||||
// SigmaATo will panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *GSVD) SigmaATo(dst *Dense) *Dense {
|
||||
if !gsvd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
r := gsvd.r
|
||||
k := gsvd.k
|
||||
l := gsvd.l
|
||||
if dst == nil {
|
||||
dst = NewDense(r, k+l, nil)
|
||||
} else {
|
||||
dst.reuseAsZeroed(r, k+l)
|
||||
}
|
||||
for i := 0; i < k; i++ {
|
||||
dst.set(i, i, 1)
|
||||
}
|
||||
for i := k; i < min(r, k+l); i++ {
|
||||
dst.set(i, i, gsvd.s1[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// SigmaBTo extracts the matrix Σ₂ from the singular value decomposition, storing
|
||||
// the result in-place into dst. Σ₂ is size p×(k+l).
|
||||
// If dst is nil, a new matrix is allocated. The resulting SigmaB matrix is returned.
|
||||
//
|
||||
// SigmaBTo will panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *GSVD) SigmaBTo(dst *Dense) *Dense {
|
||||
if !gsvd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
r := gsvd.r
|
||||
p := gsvd.p
|
||||
k := gsvd.k
|
||||
l := gsvd.l
|
||||
if dst == nil {
|
||||
dst = NewDense(p, k+l, nil)
|
||||
} else {
|
||||
dst.reuseAsZeroed(p, k+l)
|
||||
}
|
||||
for i := 0; i < min(l, r-k); i++ {
|
||||
dst.set(i, i+k, gsvd.s2[k+i])
|
||||
}
|
||||
for i := r - k; i < l; i++ {
|
||||
dst.set(i, i+k, 1)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// UTo extracts the matrix U from the singular value decomposition, storing
|
||||
// the result in-place into dst. U is size r×r.
|
||||
// If dst is nil, a new matrix is allocated. The resulting U matrix is returned.
|
||||
//
|
||||
// UTo will panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *GSVD) UTo(dst *Dense) *Dense {
|
||||
if !gsvd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if gsvd.kind&GSVDU == 0 {
|
||||
panic("mat: improper GSVD kind")
|
||||
}
|
||||
r := gsvd.u.Rows
|
||||
c := gsvd.u.Cols
|
||||
if dst == nil {
|
||||
dst = NewDense(r, c, nil)
|
||||
} else {
|
||||
dst.reuseAs(r, c)
|
||||
}
|
||||
|
||||
tmp := &Dense{
|
||||
mat: gsvd.u,
|
||||
capRows: r,
|
||||
capCols: c,
|
||||
}
|
||||
dst.Copy(tmp)
|
||||
return dst
|
||||
}
|
||||
|
||||
// VTo extracts the matrix V from the singular value decomposition, storing
|
||||
// the result in-place into dst. V is size p×p.
|
||||
// If dst is nil, a new matrix is allocated. The resulting V matrix is returned.
|
||||
//
|
||||
// VTo will panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *GSVD) VTo(dst *Dense) *Dense {
|
||||
if !gsvd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if gsvd.kind&GSVDV == 0 {
|
||||
panic("mat: improper GSVD kind")
|
||||
}
|
||||
r := gsvd.v.Rows
|
||||
c := gsvd.v.Cols
|
||||
if dst == nil {
|
||||
dst = NewDense(r, c, nil)
|
||||
} else {
|
||||
dst.reuseAs(r, c)
|
||||
}
|
||||
|
||||
tmp := &Dense{
|
||||
mat: gsvd.v,
|
||||
capRows: r,
|
||||
capCols: c,
|
||||
}
|
||||
dst.Copy(tmp)
|
||||
return dst
|
||||
}
|
||||
|
||||
// QTo extracts the matrix Q from the singular value decomposition, storing
|
||||
// the result in-place into dst. Q is size c×c.
|
||||
// If dst is nil, a new matrix is allocated. The resulting Q matrix is returned.
|
||||
//
|
||||
// QTo will panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *GSVD) QTo(dst *Dense) *Dense {
|
||||
if !gsvd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if gsvd.kind&GSVDQ == 0 {
|
||||
panic("mat: improper GSVD kind")
|
||||
}
|
||||
r := gsvd.q.Rows
|
||||
c := gsvd.q.Cols
|
||||
if dst == nil {
|
||||
dst = NewDense(r, c, nil)
|
||||
} else {
|
||||
dst.reuseAs(r, c)
|
||||
}
|
||||
|
||||
tmp := &Dense{
|
||||
mat: gsvd.q,
|
||||
capRows: r,
|
||||
capCols: c,
|
||||
}
|
||||
dst.Copy(tmp)
|
||||
return dst
|
||||
}
|
||||
233
vendor/gonum.org/v1/gonum/mat/hogsvd.go
generated
vendored
Normal file
233
vendor/gonum.org/v1/gonum/mat/hogsvd.go
generated
vendored
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
// Copyright ©2017 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
)
|
||||
|
||||
// HOGSVD is a type for creating and using the Higher Order Generalized Singular Value
|
||||
// Decomposition (HOGSVD) of a set of matrices.
|
||||
//
|
||||
// The factorization is a linear transformation of the data sets from the given
|
||||
// variable×sample spaces to reduced and diagonalized "eigenvariable"×"eigensample"
|
||||
// spaces.
|
||||
type HOGSVD struct {
|
||||
n int
|
||||
v *Dense
|
||||
b []Dense
|
||||
|
||||
err error
|
||||
}
|
||||
|
||||
// succFact returns whether the receiver contains a successful factorization.
|
||||
func (gsvd *HOGSVD) succFact() bool {
|
||||
return gsvd.n != 0
|
||||
}
|
||||
|
||||
// Factorize computes the higher order generalized singular value decomposition (HOGSVD)
|
||||
// of the n input r_i×c column tall matrices in m. HOGSV extends the GSVD case from 2 to n
|
||||
// input matrices.
|
||||
//
|
||||
// M_0 = U_0 * Σ_0 * V^T
|
||||
// M_1 = U_1 * Σ_1 * V^T
|
||||
// .
|
||||
// .
|
||||
// .
|
||||
// M_{n-1} = U_{n-1} * Σ_{n-1} * V^T
|
||||
//
|
||||
// where U_i are r_i×c matrices of singular vectors, Σ are c×c matrices singular values, and V
|
||||
// is a c×c matrix of singular vectors.
|
||||
//
|
||||
// Factorize returns whether the decomposition succeeded. If the decomposition
|
||||
// failed, routines that require a successful factorization will panic.
|
||||
func (gsvd *HOGSVD) Factorize(m ...Matrix) (ok bool) {
|
||||
// Factorize performs the HOGSVD factorisation
|
||||
// essentially as described by Ponnapalli et al.
|
||||
// https://doi.org/10.1371/journal.pone.0028072
|
||||
|
||||
if len(m) < 2 {
|
||||
panic("hogsvd: too few matrices")
|
||||
}
|
||||
gsvd.n = 0
|
||||
|
||||
r, c := m[0].Dims()
|
||||
a := make([]Cholesky, len(m))
|
||||
var ts SymDense
|
||||
for i, d := range m {
|
||||
rd, cd := d.Dims()
|
||||
if rd < cd {
|
||||
gsvd.err = ErrShape
|
||||
return false
|
||||
}
|
||||
if rd > r {
|
||||
r = rd
|
||||
}
|
||||
if cd != c {
|
||||
panic(ErrShape)
|
||||
}
|
||||
ts.Reset()
|
||||
ts.SymOuterK(1, d.T())
|
||||
ok = a[i].Factorize(&ts)
|
||||
if !ok {
|
||||
gsvd.err = errors.New("hogsvd: cholesky decomposition failed")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
s := getWorkspace(c, c, true)
|
||||
defer putWorkspace(s)
|
||||
sij := getWorkspace(c, c, false)
|
||||
defer putWorkspace(sij)
|
||||
for i, ai := range a {
|
||||
for _, aj := range a[i+1:] {
|
||||
gsvd.err = ai.SolveCholTo(sij, &aj)
|
||||
if gsvd.err != nil {
|
||||
return false
|
||||
}
|
||||
s.Add(s, sij)
|
||||
|
||||
gsvd.err = aj.SolveCholTo(sij, &ai)
|
||||
if gsvd.err != nil {
|
||||
return false
|
||||
}
|
||||
s.Add(s, sij)
|
||||
}
|
||||
}
|
||||
s.Scale(1/float64(len(m)*(len(m)-1)), s)
|
||||
|
||||
var eig Eigen
|
||||
ok = eig.Factorize(s.T(), EigenRight)
|
||||
if !ok {
|
||||
gsvd.err = errors.New("hogsvd: eigen decomposition failed")
|
||||
return false
|
||||
}
|
||||
vc := eig.VectorsTo(nil)
|
||||
// vc is guaranteed to have real eigenvalues.
|
||||
rc, cc := vc.Dims()
|
||||
v := NewDense(rc, cc, nil)
|
||||
for i := 0; i < rc; i++ {
|
||||
for j := 0; j < cc; j++ {
|
||||
a := vc.At(i, j)
|
||||
v.set(i, j, real(a))
|
||||
}
|
||||
}
|
||||
// Rescale the columns of v by their Frobenius norms.
|
||||
// Work done in cv is reflected in v.
|
||||
var cv VecDense
|
||||
for j := 0; j < c; j++ {
|
||||
cv.ColViewOf(v, j)
|
||||
cv.ScaleVec(1/blas64.Nrm2(cv.mat), &cv)
|
||||
}
|
||||
|
||||
b := make([]Dense, len(m))
|
||||
biT := getWorkspace(c, r, false)
|
||||
defer putWorkspace(biT)
|
||||
for i, d := range m {
|
||||
// All calls to reset will leave a zeroed
|
||||
// matrix with capacity to store the result
|
||||
// without additional allocation.
|
||||
biT.Reset()
|
||||
gsvd.err = biT.Solve(v, d.T())
|
||||
if gsvd.err != nil {
|
||||
return false
|
||||
}
|
||||
b[i].Clone(biT.T())
|
||||
}
|
||||
|
||||
gsvd.n = len(m)
|
||||
gsvd.v = v
|
||||
gsvd.b = b
|
||||
return true
|
||||
}
|
||||
|
||||
// Err returns the reason for a factorization failure.
|
||||
func (gsvd *HOGSVD) Err() error {
|
||||
return gsvd.err
|
||||
}
|
||||
|
||||
// Len returns the number of matrices that have been factorized. If Len returns
|
||||
// zero, the factorization was not successful.
|
||||
func (gsvd *HOGSVD) Len() int {
|
||||
return gsvd.n
|
||||
}
|
||||
|
||||
// UTo extracts the matrix U_n from the singular value decomposition, storing
|
||||
// the result in-place into dst. U_n is size r×c.
|
||||
// If dst is nil, a new matrix is allocated. The resulting U matrix is returned.
|
||||
//
|
||||
// UTo will panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *HOGSVD) UTo(dst *Dense, n int) *Dense {
|
||||
if !gsvd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if n < 0 || gsvd.n <= n {
|
||||
panic("hogsvd: invalid index")
|
||||
}
|
||||
|
||||
if dst == nil {
|
||||
r, c := gsvd.b[n].Dims()
|
||||
dst = NewDense(r, c, nil)
|
||||
} else {
|
||||
dst.reuseAs(gsvd.b[n].Dims())
|
||||
}
|
||||
dst.Copy(&gsvd.b[n])
|
||||
var v VecDense
|
||||
for j, f := range gsvd.Values(nil, n) {
|
||||
v.ColViewOf(dst, j)
|
||||
v.ScaleVec(1/f, &v)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Values returns the nth set of singular values of the factorized system.
|
||||
// If the input slice is non-nil, the values will be stored in-place into the slice.
|
||||
// In this case, the slice must have length c, and Values will panic with
|
||||
// matrix.ErrSliceLengthMismatch otherwise. If the input slice is nil,
|
||||
// a new slice of the appropriate length will be allocated and returned.
|
||||
//
|
||||
// Values will panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *HOGSVD) Values(s []float64, n int) []float64 {
|
||||
if !gsvd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if n < 0 || gsvd.n <= n {
|
||||
panic("hogsvd: invalid index")
|
||||
}
|
||||
|
||||
_, c := gsvd.b[n].Dims()
|
||||
if s == nil {
|
||||
s = make([]float64, c)
|
||||
} else if len(s) != c {
|
||||
panic(ErrSliceLengthMismatch)
|
||||
}
|
||||
var v VecDense
|
||||
for j := 0; j < c; j++ {
|
||||
v.ColViewOf(&gsvd.b[n], j)
|
||||
s[j] = blas64.Nrm2(v.mat)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// VTo extracts the matrix V from the singular value decomposition, storing
|
||||
// the result in-place into dst. V is size c×c.
|
||||
// If dst is nil, a new matrix is allocated. The resulting V matrix is returned.
|
||||
//
|
||||
// VTo will panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *HOGSVD) VTo(dst *Dense) *Dense {
|
||||
if !gsvd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if dst == nil {
|
||||
r, c := gsvd.v.Dims()
|
||||
dst = NewDense(r, c, nil)
|
||||
} else {
|
||||
dst.reuseAs(gsvd.v.Dims())
|
||||
}
|
||||
dst.Copy(gsvd.v)
|
||||
return dst
|
||||
}
|
||||
348
vendor/gonum.org/v1/gonum/mat/index_bound_checks.go
generated
vendored
Normal file
348
vendor/gonum.org/v1/gonum/mat/index_bound_checks.go
generated
vendored
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
// Copyright ©2014 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file must be kept in sync with index_no_bound_checks.go.
|
||||
|
||||
// +build bounds
|
||||
|
||||
package mat
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (m *Dense) At(i, j int) float64 {
|
||||
return m.at(i, j)
|
||||
}
|
||||
|
||||
func (m *Dense) at(i, j int) float64 {
|
||||
if uint(i) >= uint(m.mat.Rows) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(m.mat.Cols) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
return m.mat.Data[i*m.mat.Stride+j]
|
||||
}
|
||||
|
||||
// Set sets the element at row i, column j to the value v.
|
||||
func (m *Dense) Set(i, j int, v float64) {
|
||||
m.set(i, j, v)
|
||||
}
|
||||
|
||||
func (m *Dense) set(i, j int, v float64) {
|
||||
if uint(i) >= uint(m.mat.Rows) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(m.mat.Cols) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
m.mat.Data[i*m.mat.Stride+j] = v
|
||||
}
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (m *CDense) At(i, j int) complex128 {
|
||||
return m.at(i, j)
|
||||
}
|
||||
|
||||
func (m *CDense) at(i, j int) complex128 {
|
||||
if uint(i) >= uint(m.mat.Rows) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(m.mat.Cols) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
return m.mat.Data[i*m.mat.Stride+j]
|
||||
}
|
||||
|
||||
// Set sets the element at row i, column j to the value v.
|
||||
func (m *CDense) Set(i, j int, v complex128) {
|
||||
m.set(i, j, v)
|
||||
}
|
||||
|
||||
func (m *CDense) set(i, j int, v complex128) {
|
||||
if uint(i) >= uint(m.mat.Rows) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(m.mat.Cols) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
m.mat.Data[i*m.mat.Stride+j] = v
|
||||
}
|
||||
|
||||
// At returns the element at row i.
|
||||
// It panics if i is out of bounds or if j is not zero.
|
||||
func (v *VecDense) At(i, j int) float64 {
|
||||
if j != 0 {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
return v.at(i)
|
||||
}
|
||||
|
||||
// AtVec returns the element at row i.
|
||||
// It panics if i is out of bounds.
|
||||
func (v *VecDense) AtVec(i int) float64 {
|
||||
return v.at(i)
|
||||
}
|
||||
|
||||
func (v *VecDense) at(i int) float64 {
|
||||
if uint(i) >= uint(v.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
return v.mat.Data[i*v.mat.Inc]
|
||||
}
|
||||
|
||||
// SetVec sets the element at row i to the value val.
|
||||
// It panics if i is out of bounds.
|
||||
func (v *VecDense) SetVec(i int, val float64) {
|
||||
v.setVec(i, val)
|
||||
}
|
||||
|
||||
func (v *VecDense) setVec(i int, val float64) {
|
||||
if uint(i) >= uint(v.mat.N) {
|
||||
panic(ErrVectorAccess)
|
||||
}
|
||||
v.mat.Data[i*v.mat.Inc] = val
|
||||
}
|
||||
|
||||
// At returns the element at row i and column j.
|
||||
func (t *SymDense) At(i, j int) float64 {
|
||||
return t.at(i, j)
|
||||
}
|
||||
|
||||
func (t *SymDense) at(i, j int) float64 {
|
||||
if uint(i) >= uint(t.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(t.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
if i > j {
|
||||
i, j = j, i
|
||||
}
|
||||
return t.mat.Data[i*t.mat.Stride+j]
|
||||
}
|
||||
|
||||
// SetSym sets the elements at (i,j) and (j,i) to the value v.
|
||||
func (t *SymDense) SetSym(i, j int, v float64) {
|
||||
t.set(i, j, v)
|
||||
}
|
||||
|
||||
func (t *SymDense) set(i, j int, v float64) {
|
||||
if uint(i) >= uint(t.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(t.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
if i > j {
|
||||
i, j = j, i
|
||||
}
|
||||
t.mat.Data[i*t.mat.Stride+j] = v
|
||||
}
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (t *TriDense) At(i, j int) float64 {
|
||||
return t.at(i, j)
|
||||
}
|
||||
|
||||
func (t *TriDense) at(i, j int) float64 {
|
||||
if uint(i) >= uint(t.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(t.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
isUpper := t.isUpper()
|
||||
if (isUpper && i > j) || (!isUpper && i < j) {
|
||||
return 0
|
||||
}
|
||||
return t.mat.Data[i*t.mat.Stride+j]
|
||||
}
|
||||
|
||||
// SetTri sets the element of the triangular matrix at row i, column j to the value v.
|
||||
// It panics if the location is outside the appropriate half of the matrix.
|
||||
func (t *TriDense) SetTri(i, j int, v float64) {
|
||||
t.set(i, j, v)
|
||||
}
|
||||
|
||||
func (t *TriDense) set(i, j int, v float64) {
|
||||
if uint(i) >= uint(t.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(t.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
isUpper := t.isUpper()
|
||||
if (isUpper && i > j) || (!isUpper && i < j) {
|
||||
panic(ErrTriangleSet)
|
||||
}
|
||||
t.mat.Data[i*t.mat.Stride+j] = v
|
||||
}
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (b *BandDense) At(i, j int) float64 {
|
||||
return b.at(i, j)
|
||||
}
|
||||
|
||||
func (b *BandDense) at(i, j int) float64 {
|
||||
if uint(i) >= uint(b.mat.Rows) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(b.mat.Cols) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
pj := j + b.mat.KL - i
|
||||
if pj < 0 || b.mat.KL+b.mat.KU+1 <= pj {
|
||||
return 0
|
||||
}
|
||||
return b.mat.Data[i*b.mat.Stride+pj]
|
||||
}
|
||||
|
||||
// SetBand sets the element at row i, column j to the value v.
|
||||
// It panics if the location is outside the appropriate region of the matrix.
|
||||
func (b *BandDense) SetBand(i, j int, v float64) {
|
||||
b.set(i, j, v)
|
||||
}
|
||||
|
||||
func (b *BandDense) set(i, j int, v float64) {
|
||||
if uint(i) >= uint(b.mat.Rows) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(b.mat.Cols) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
pj := j + b.mat.KL - i
|
||||
if pj < 0 || b.mat.KL+b.mat.KU+1 <= pj {
|
||||
panic(ErrBandSet)
|
||||
}
|
||||
b.mat.Data[i*b.mat.Stride+pj] = v
|
||||
}
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (s *SymBandDense) At(i, j int) float64 {
|
||||
return s.at(i, j)
|
||||
}
|
||||
|
||||
func (s *SymBandDense) at(i, j int) float64 {
|
||||
if uint(i) >= uint(s.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(s.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
if i > j {
|
||||
i, j = j, i
|
||||
}
|
||||
pj := j - i
|
||||
if s.mat.K+1 <= pj {
|
||||
return 0
|
||||
}
|
||||
return s.mat.Data[i*s.mat.Stride+pj]
|
||||
}
|
||||
|
||||
// SetSymBand sets the element at row i, column j to the value v.
|
||||
// It panics if the location is outside the appropriate region of the matrix.
|
||||
func (s *SymBandDense) SetSymBand(i, j int, v float64) {
|
||||
s.set(i, j, v)
|
||||
}
|
||||
|
||||
func (s *SymBandDense) set(i, j int, v float64) {
|
||||
if uint(i) >= uint(s.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(s.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
if i > j {
|
||||
i, j = j, i
|
||||
}
|
||||
pj := j - i
|
||||
if s.mat.K+1 <= pj {
|
||||
panic(ErrBandSet)
|
||||
}
|
||||
s.mat.Data[i*s.mat.Stride+pj] = v
|
||||
}
|
||||
|
||||
func (t *TriBandDense) At(i, j int) float64 {
|
||||
return t.at(i, j)
|
||||
}
|
||||
|
||||
func (t *TriBandDense) at(i, j int) float64 {
|
||||
// TODO(btracey): Support Diag field, see #692.
|
||||
if uint(i) >= uint(t.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(t.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
isUpper := t.isUpper()
|
||||
if (isUpper && i > j) || (!isUpper && i < j) {
|
||||
return 0
|
||||
}
|
||||
kl, ku := t.mat.K, 0
|
||||
if isUpper {
|
||||
kl, ku = 0, t.mat.K
|
||||
}
|
||||
pj := j + kl - i
|
||||
if pj < 0 || kl+ku+1 <= pj {
|
||||
return 0
|
||||
}
|
||||
return t.mat.Data[i*t.mat.Stride+pj]
|
||||
}
|
||||
|
||||
func (t *TriBandDense) SetTriBand(i, j int, v float64) {
|
||||
t.setTriBand(i, j, v)
|
||||
}
|
||||
|
||||
func (t *TriBandDense) setTriBand(i, j int, v float64) {
|
||||
if uint(i) >= uint(t.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(t.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
isUpper := t.isUpper()
|
||||
if (isUpper && i > j) || (!isUpper && i < j) {
|
||||
panic(ErrTriangleSet)
|
||||
}
|
||||
kl, ku := t.mat.K, 0
|
||||
if isUpper {
|
||||
kl, ku = 0, t.mat.K
|
||||
}
|
||||
pj := j + kl - i
|
||||
if pj < 0 || kl+ku+1 <= pj {
|
||||
panic(ErrBandSet)
|
||||
}
|
||||
// TODO(btracey): Support Diag field, see #692.
|
||||
t.mat.Data[i*t.mat.Stride+pj] = v
|
||||
}
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (d *DiagDense) At(i, j int) float64 {
|
||||
return d.at(i, j)
|
||||
}
|
||||
|
||||
func (d *DiagDense) at(i, j int) float64 {
|
||||
if uint(i) >= uint(d.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(d.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
if i != j {
|
||||
return 0
|
||||
}
|
||||
return d.mat.Data[i*d.mat.Inc]
|
||||
}
|
||||
|
||||
// SetDiag sets the element at row i, column i to the value v.
|
||||
// It panics if the location is outside the appropriate region of the matrix.
|
||||
func (d *DiagDense) SetDiag(i int, v float64) {
|
||||
d.setDiag(i, v)
|
||||
}
|
||||
|
||||
func (d *DiagDense) setDiag(i int, v float64) {
|
||||
if uint(i) >= uint(d.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
d.mat.Data[i*d.mat.Inc] = v
|
||||
}
|
||||
359
vendor/gonum.org/v1/gonum/mat/index_no_bound_checks.go
generated
vendored
Normal file
359
vendor/gonum.org/v1/gonum/mat/index_no_bound_checks.go
generated
vendored
Normal file
|
|
@ -0,0 +1,359 @@
|
|||
// Copyright ©2014 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file must be kept in sync with index_bound_checks.go.
|
||||
|
||||
// +build !bounds
|
||||
|
||||
package mat
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (m *Dense) At(i, j int) float64 {
|
||||
if uint(i) >= uint(m.mat.Rows) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(m.mat.Cols) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
return m.at(i, j)
|
||||
}
|
||||
|
||||
func (m *Dense) at(i, j int) float64 {
|
||||
return m.mat.Data[i*m.mat.Stride+j]
|
||||
}
|
||||
|
||||
// Set sets the element at row i, column j to the value v.
|
||||
func (m *Dense) Set(i, j int, v float64) {
|
||||
if uint(i) >= uint(m.mat.Rows) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(m.mat.Cols) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
m.set(i, j, v)
|
||||
}
|
||||
|
||||
func (m *Dense) set(i, j int, v float64) {
|
||||
m.mat.Data[i*m.mat.Stride+j] = v
|
||||
}
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (m *CDense) At(i, j int) complex128 {
|
||||
if uint(i) >= uint(m.mat.Rows) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(m.mat.Cols) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
return m.at(i, j)
|
||||
}
|
||||
|
||||
func (m *CDense) at(i, j int) complex128 {
|
||||
return m.mat.Data[i*m.mat.Stride+j]
|
||||
}
|
||||
|
||||
// Set sets the element at row i, column j to the value v.
|
||||
func (m *CDense) Set(i, j int, v complex128) {
|
||||
if uint(i) >= uint(m.mat.Rows) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(m.mat.Cols) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
m.set(i, j, v)
|
||||
}
|
||||
|
||||
func (m *CDense) set(i, j int, v complex128) {
|
||||
m.mat.Data[i*m.mat.Stride+j] = v
|
||||
}
|
||||
|
||||
// At returns the element at row i.
|
||||
// It panics if i is out of bounds or if j is not zero.
|
||||
func (v *VecDense) At(i, j int) float64 {
|
||||
if uint(i) >= uint(v.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if j != 0 {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
return v.at(i)
|
||||
}
|
||||
|
||||
// AtVec returns the element at row i.
|
||||
// It panics if i is out of bounds.
|
||||
func (v *VecDense) AtVec(i int) float64 {
|
||||
if uint(i) >= uint(v.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
return v.at(i)
|
||||
}
|
||||
|
||||
func (v *VecDense) at(i int) float64 {
|
||||
return v.mat.Data[i*v.mat.Inc]
|
||||
}
|
||||
|
||||
// SetVec sets the element at row i to the value val.
|
||||
// It panics if i is out of bounds.
|
||||
func (v *VecDense) SetVec(i int, val float64) {
|
||||
if uint(i) >= uint(v.mat.N) {
|
||||
panic(ErrVectorAccess)
|
||||
}
|
||||
v.setVec(i, val)
|
||||
}
|
||||
|
||||
func (v *VecDense) setVec(i int, val float64) {
|
||||
v.mat.Data[i*v.mat.Inc] = val
|
||||
}
|
||||
|
||||
// At returns the element at row i and column j.
|
||||
func (s *SymDense) At(i, j int) float64 {
|
||||
if uint(i) >= uint(s.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(s.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
return s.at(i, j)
|
||||
}
|
||||
|
||||
func (s *SymDense) at(i, j int) float64 {
|
||||
if i > j {
|
||||
i, j = j, i
|
||||
}
|
||||
return s.mat.Data[i*s.mat.Stride+j]
|
||||
}
|
||||
|
||||
// SetSym sets the elements at (i,j) and (j,i) to the value v.
|
||||
func (s *SymDense) SetSym(i, j int, v float64) {
|
||||
if uint(i) >= uint(s.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(s.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
s.set(i, j, v)
|
||||
}
|
||||
|
||||
func (s *SymDense) set(i, j int, v float64) {
|
||||
if i > j {
|
||||
i, j = j, i
|
||||
}
|
||||
s.mat.Data[i*s.mat.Stride+j] = v
|
||||
}
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (t *TriDense) At(i, j int) float64 {
|
||||
if uint(i) >= uint(t.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(t.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
return t.at(i, j)
|
||||
}
|
||||
|
||||
func (t *TriDense) at(i, j int) float64 {
|
||||
isUpper := t.triKind()
|
||||
if (isUpper && i > j) || (!isUpper && i < j) {
|
||||
return 0
|
||||
}
|
||||
return t.mat.Data[i*t.mat.Stride+j]
|
||||
}
|
||||
|
||||
// SetTri sets the element at row i, column j to the value v.
|
||||
// It panics if the location is outside the appropriate half of the matrix.
|
||||
func (t *TriDense) SetTri(i, j int, v float64) {
|
||||
if uint(i) >= uint(t.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(t.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
isUpper := t.isUpper()
|
||||
if (isUpper && i > j) || (!isUpper && i < j) {
|
||||
panic(ErrTriangleSet)
|
||||
}
|
||||
t.set(i, j, v)
|
||||
}
|
||||
|
||||
func (t *TriDense) set(i, j int, v float64) {
|
||||
t.mat.Data[i*t.mat.Stride+j] = v
|
||||
}
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (b *BandDense) At(i, j int) float64 {
|
||||
if uint(i) >= uint(b.mat.Rows) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(b.mat.Cols) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
return b.at(i, j)
|
||||
}
|
||||
|
||||
func (b *BandDense) at(i, j int) float64 {
|
||||
pj := j + b.mat.KL - i
|
||||
if pj < 0 || b.mat.KL+b.mat.KU+1 <= pj {
|
||||
return 0
|
||||
}
|
||||
return b.mat.Data[i*b.mat.Stride+pj]
|
||||
}
|
||||
|
||||
// SetBand sets the element at row i, column j to the value v.
|
||||
// It panics if the location is outside the appropriate region of the matrix.
|
||||
func (b *BandDense) SetBand(i, j int, v float64) {
|
||||
if uint(i) >= uint(b.mat.Rows) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(b.mat.Cols) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
pj := j + b.mat.KL - i
|
||||
if pj < 0 || b.mat.KL+b.mat.KU+1 <= pj {
|
||||
panic(ErrBandSet)
|
||||
}
|
||||
b.set(i, j, v)
|
||||
}
|
||||
|
||||
func (b *BandDense) set(i, j int, v float64) {
|
||||
pj := j + b.mat.KL - i
|
||||
b.mat.Data[i*b.mat.Stride+pj] = v
|
||||
}
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (s *SymBandDense) At(i, j int) float64 {
|
||||
if uint(i) >= uint(s.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(s.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
return s.at(i, j)
|
||||
}
|
||||
|
||||
func (s *SymBandDense) at(i, j int) float64 {
|
||||
if i > j {
|
||||
i, j = j, i
|
||||
}
|
||||
pj := j - i
|
||||
if s.mat.K+1 <= pj {
|
||||
return 0
|
||||
}
|
||||
return s.mat.Data[i*s.mat.Stride+pj]
|
||||
}
|
||||
|
||||
// SetSymBand sets the element at row i, column j to the value v.
|
||||
// It panics if the location is outside the appropriate region of the matrix.
|
||||
func (s *SymBandDense) SetSymBand(i, j int, v float64) {
|
||||
if uint(i) >= uint(s.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(s.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
s.set(i, j, v)
|
||||
}
|
||||
|
||||
func (s *SymBandDense) set(i, j int, v float64) {
|
||||
if i > j {
|
||||
i, j = j, i
|
||||
}
|
||||
pj := j - i
|
||||
if s.mat.K+1 <= pj {
|
||||
panic(ErrBandSet)
|
||||
}
|
||||
s.mat.Data[i*s.mat.Stride+pj] = v
|
||||
}
|
||||
|
||||
func (t *TriBandDense) At(i, j int) float64 {
|
||||
if uint(i) >= uint(t.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(t.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
return t.at(i, j)
|
||||
}
|
||||
|
||||
func (t *TriBandDense) at(i, j int) float64 {
|
||||
// TODO(btracey): Support Diag field, see #692.
|
||||
isUpper := t.isUpper()
|
||||
if (isUpper && i > j) || (!isUpper && i < j) {
|
||||
return 0
|
||||
}
|
||||
kl := t.mat.K
|
||||
ku := 0
|
||||
if isUpper {
|
||||
ku = t.mat.K
|
||||
kl = 0
|
||||
}
|
||||
pj := j + kl - i
|
||||
if pj < 0 || kl+ku+1 <= pj {
|
||||
return 0
|
||||
}
|
||||
return t.mat.Data[i*t.mat.Stride+pj]
|
||||
}
|
||||
|
||||
func (t *TriBandDense) SetTriBand(i, j int, v float64) {
|
||||
if uint(i) >= uint(t.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(t.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
isUpper := t.isUpper()
|
||||
if (isUpper && i > j) || (!isUpper && i < j) {
|
||||
panic(ErrTriangleSet)
|
||||
}
|
||||
kl, ku := t.mat.K, 0
|
||||
if isUpper {
|
||||
kl, ku = 0, t.mat.K
|
||||
}
|
||||
pj := j + kl - i
|
||||
if pj < 0 || kl+ku+1 <= pj {
|
||||
panic(ErrBandSet)
|
||||
}
|
||||
// TODO(btracey): Support Diag field, see #692.
|
||||
t.mat.Data[i*t.mat.Stride+pj] = v
|
||||
}
|
||||
|
||||
func (t *TriBandDense) setTriBand(i, j int, v float64) {
|
||||
var kl int
|
||||
if !t.isUpper() {
|
||||
kl = t.mat.K
|
||||
}
|
||||
pj := j + kl - i
|
||||
t.mat.Data[i*t.mat.Stride+pj] = v
|
||||
}
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (d *DiagDense) At(i, j int) float64 {
|
||||
if uint(i) >= uint(d.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(d.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
return d.at(i, j)
|
||||
}
|
||||
|
||||
func (d *DiagDense) at(i, j int) float64 {
|
||||
if i != j {
|
||||
return 0
|
||||
}
|
||||
return d.mat.Data[i*d.mat.Inc]
|
||||
}
|
||||
|
||||
// SetDiag sets the element at row i, column i to the value v.
|
||||
// It panics if the location is outside the appropriate region of the matrix.
|
||||
func (d *DiagDense) SetDiag(i int, v float64) {
|
||||
if uint(i) >= uint(d.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
d.setDiag(i, v)
|
||||
}
|
||||
|
||||
func (d *DiagDense) setDiag(i int, v float64) {
|
||||
d.mat.Data[i*d.mat.Inc] = v
|
||||
}
|
||||
121
vendor/gonum.org/v1/gonum/mat/inner.go
generated
vendored
Normal file
121
vendor/gonum.org/v1/gonum/mat/inner.go
generated
vendored
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
// Copyright ©2014 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
"gonum.org/v1/gonum/internal/asm/f64"
|
||||
)
|
||||
|
||||
// Inner computes the generalized inner product
|
||||
// x^T A y
|
||||
// between column vectors x and y with matrix A. This is only a true inner product if
|
||||
// A is symmetric positive definite, though the operation works for any matrix A.
|
||||
//
|
||||
// Inner panics if x.Len != m or y.Len != n when A is an m x n matrix.
|
||||
func Inner(x Vector, a Matrix, y Vector) float64 {
|
||||
m, n := a.Dims()
|
||||
if x.Len() != m {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if y.Len() != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if m == 0 || n == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var sum float64
|
||||
|
||||
switch a := a.(type) {
|
||||
case RawSymmetricer:
|
||||
amat := a.RawSymmetric()
|
||||
if amat.Uplo != blas.Upper {
|
||||
// Panic as a string not a mat.Error.
|
||||
panic(badSymTriangle)
|
||||
}
|
||||
var xmat, ymat blas64.Vector
|
||||
if xrv, ok := x.(RawVectorer); ok {
|
||||
xmat = xrv.RawVector()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
if yrv, ok := y.(RawVectorer); ok {
|
||||
ymat = yrv.RawVector()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
for i := 0; i < x.Len(); i++ {
|
||||
xi := x.AtVec(i)
|
||||
if xi != 0 {
|
||||
if ymat.Inc == 1 {
|
||||
sum += xi * f64.DotUnitary(
|
||||
amat.Data[i*amat.Stride+i:i*amat.Stride+n],
|
||||
ymat.Data[i:],
|
||||
)
|
||||
} else {
|
||||
sum += xi * f64.DotInc(
|
||||
amat.Data[i*amat.Stride+i:i*amat.Stride+n],
|
||||
ymat.Data[i*ymat.Inc:], uintptr(n-i),
|
||||
1, uintptr(ymat.Inc),
|
||||
0, 0,
|
||||
)
|
||||
}
|
||||
}
|
||||
yi := y.AtVec(i)
|
||||
if i != n-1 && yi != 0 {
|
||||
if xmat.Inc == 1 {
|
||||
sum += yi * f64.DotUnitary(
|
||||
amat.Data[i*amat.Stride+i+1:i*amat.Stride+n],
|
||||
xmat.Data[i+1:],
|
||||
)
|
||||
} else {
|
||||
sum += yi * f64.DotInc(
|
||||
amat.Data[i*amat.Stride+i+1:i*amat.Stride+n],
|
||||
xmat.Data[(i+1)*xmat.Inc:], uintptr(n-i-1),
|
||||
1, uintptr(xmat.Inc),
|
||||
0, 0,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum
|
||||
case RawMatrixer:
|
||||
amat := a.RawMatrix()
|
||||
var ymat blas64.Vector
|
||||
if yrv, ok := y.(RawVectorer); ok {
|
||||
ymat = yrv.RawVector()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
for i := 0; i < x.Len(); i++ {
|
||||
xi := x.AtVec(i)
|
||||
if xi != 0 {
|
||||
if ymat.Inc == 1 {
|
||||
sum += xi * f64.DotUnitary(
|
||||
amat.Data[i*amat.Stride:i*amat.Stride+n],
|
||||
ymat.Data,
|
||||
)
|
||||
} else {
|
||||
sum += xi * f64.DotInc(
|
||||
amat.Data[i*amat.Stride:i*amat.Stride+n],
|
||||
ymat.Data, uintptr(n),
|
||||
1, uintptr(ymat.Inc),
|
||||
0, 0,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
for i := 0; i < x.Len(); i++ {
|
||||
xi := x.AtVec(i)
|
||||
for j := 0; j < y.Len(); j++ {
|
||||
sum += xi * a.At(i, j) * y.AtVec(j)
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
492
vendor/gonum.org/v1/gonum/mat/io.go
generated
vendored
Normal file
492
vendor/gonum.org/v1/gonum/mat/io.go
generated
vendored
Normal file
|
|
@ -0,0 +1,492 @@
|
|||
// Copyright ©2015 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
)
|
||||
|
||||
// version is the current on-disk codec version.
|
||||
const version uint32 = 0x1
|
||||
|
||||
// maxLen is the biggest slice/array len one can create on a 32/64b platform.
|
||||
const maxLen = int64(int(^uint(0) >> 1))
|
||||
|
||||
var (
|
||||
headerSize = binary.Size(storage{})
|
||||
sizeInt64 = binary.Size(int64(0))
|
||||
sizeFloat64 = binary.Size(float64(0))
|
||||
|
||||
errWrongType = errors.New("mat: wrong data type")
|
||||
|
||||
errTooBig = errors.New("mat: resulting data slice too big")
|
||||
errTooSmall = errors.New("mat: input slice too small")
|
||||
errBadBuffer = errors.New("mat: data buffer size mismatch")
|
||||
errBadSize = errors.New("mat: invalid dimension")
|
||||
)
|
||||
|
||||
// Type encoding scheme:
|
||||
//
|
||||
// Type Form Packing Uplo Unit Rows Columns kU kL
|
||||
// uint8 [GST] uint8 [BPF] uint8 [AUL] bool int64 int64 int64 int64
|
||||
// General 'G' 'F' 'A' false r c 0 0
|
||||
// Band 'G' 'B' 'A' false r c kU kL
|
||||
// Symmetric 'S' 'F' ul false n n 0 0
|
||||
// SymmetricBand 'S' 'B' ul false n n k k
|
||||
// SymmetricPacked 'S' 'P' ul false n n 0 0
|
||||
// Triangular 'T' 'F' ul Diag==Unit n n 0 0
|
||||
// TriangularBand 'T' 'B' ul Diag==Unit n n k k
|
||||
// TriangularPacked 'T' 'P' ul Diag==Unit n n 0 0
|
||||
//
|
||||
// G - general, S - symmetric, T - triangular
|
||||
// F - full, B - band, P - packed
|
||||
// A - all, U - upper, L - lower
|
||||
|
||||
// MarshalBinary encodes the receiver into a binary form and returns the result.
|
||||
//
|
||||
// Dense is little-endian encoded as follows:
|
||||
// 0 - 3 Version = 1 (uint32)
|
||||
// 4 'G' (byte)
|
||||
// 5 'F' (byte)
|
||||
// 6 'A' (byte)
|
||||
// 7 0 (byte)
|
||||
// 8 - 15 number of rows (int64)
|
||||
// 16 - 23 number of columns (int64)
|
||||
// 24 - 31 0 (int64)
|
||||
// 32 - 39 0 (int64)
|
||||
// 40 - .. matrix data elements (float64)
|
||||
// [0,0] [0,1] ... [0,ncols-1]
|
||||
// [1,0] [1,1] ... [1,ncols-1]
|
||||
// ...
|
||||
// [nrows-1,0] ... [nrows-1,ncols-1]
|
||||
func (m Dense) MarshalBinary() ([]byte, error) {
|
||||
bufLen := int64(headerSize) + int64(m.mat.Rows)*int64(m.mat.Cols)*int64(sizeFloat64)
|
||||
if bufLen <= 0 {
|
||||
// bufLen is too big and has wrapped around.
|
||||
return nil, errTooBig
|
||||
}
|
||||
|
||||
header := storage{
|
||||
Form: 'G', Packing: 'F', Uplo: 'A',
|
||||
Rows: int64(m.mat.Rows), Cols: int64(m.mat.Cols),
|
||||
Version: version,
|
||||
}
|
||||
buf := make([]byte, bufLen)
|
||||
n, err := header.marshalBinaryTo(bytes.NewBuffer(buf[:0]))
|
||||
if err != nil {
|
||||
return buf[:n], err
|
||||
}
|
||||
|
||||
p := headerSize
|
||||
r, c := m.Dims()
|
||||
for i := 0; i < r; i++ {
|
||||
for j := 0; j < c; j++ {
|
||||
binary.LittleEndian.PutUint64(buf[p:p+sizeFloat64], math.Float64bits(m.at(i, j)))
|
||||
p += sizeFloat64
|
||||
}
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// MarshalBinaryTo encodes the receiver into a binary form and writes it into w.
|
||||
// MarshalBinaryTo returns the number of bytes written into w and an error, if any.
|
||||
//
|
||||
// See MarshalBinary for the on-disk layout.
|
||||
func (m Dense) MarshalBinaryTo(w io.Writer) (int, error) {
|
||||
header := storage{
|
||||
Form: 'G', Packing: 'F', Uplo: 'A',
|
||||
Rows: int64(m.mat.Rows), Cols: int64(m.mat.Cols),
|
||||
Version: version,
|
||||
}
|
||||
n, err := header.marshalBinaryTo(w)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
r, c := m.Dims()
|
||||
var b [8]byte
|
||||
for i := 0; i < r; i++ {
|
||||
for j := 0; j < c; j++ {
|
||||
binary.LittleEndian.PutUint64(b[:], math.Float64bits(m.at(i, j)))
|
||||
nn, err := w.Write(b[:])
|
||||
n += nn
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary form into the receiver.
|
||||
// It panics if the receiver is a non-zero Dense matrix.
|
||||
//
|
||||
// See MarshalBinary for the on-disk layout.
|
||||
//
|
||||
// Limited checks on the validity of the binary input are performed:
|
||||
// - matrix.ErrShape is returned if the number of rows or columns is negative,
|
||||
// - an error is returned if the resulting Dense matrix is too
|
||||
// big for the current architecture (e.g. a 16GB matrix written by a
|
||||
// 64b application and read back from a 32b application.)
|
||||
// UnmarshalBinary does not limit the size of the unmarshaled matrix, and so
|
||||
// it should not be used on untrusted data.
|
||||
func (m *Dense) UnmarshalBinary(data []byte) error {
|
||||
if !m.IsZero() {
|
||||
panic("mat: unmarshal into non-zero matrix")
|
||||
}
|
||||
|
||||
if len(data) < headerSize {
|
||||
return errTooSmall
|
||||
}
|
||||
|
||||
var header storage
|
||||
err := header.unmarshalBinary(data[:headerSize])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rows := header.Rows
|
||||
cols := header.Cols
|
||||
header.Version = 0
|
||||
header.Rows = 0
|
||||
header.Cols = 0
|
||||
if (header != storage{Form: 'G', Packing: 'F', Uplo: 'A'}) {
|
||||
return errWrongType
|
||||
}
|
||||
if rows < 0 || cols < 0 {
|
||||
return errBadSize
|
||||
}
|
||||
size := rows * cols
|
||||
if size == 0 {
|
||||
return ErrZeroLength
|
||||
}
|
||||
if int(size) < 0 || size > maxLen {
|
||||
return errTooBig
|
||||
}
|
||||
if len(data) != headerSize+int(rows*cols)*sizeFloat64 {
|
||||
return errBadBuffer
|
||||
}
|
||||
|
||||
p := headerSize
|
||||
m.reuseAs(int(rows), int(cols))
|
||||
for i := range m.mat.Data {
|
||||
m.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(data[p : p+sizeFloat64]))
|
||||
p += sizeFloat64
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBinaryFrom decodes the binary form into the receiver and returns
|
||||
// the number of bytes read and an error if any.
|
||||
// It panics if the receiver is a non-zero Dense matrix.
|
||||
//
|
||||
// See MarshalBinary for the on-disk layout.
|
||||
//
|
||||
// Limited checks on the validity of the binary input are performed:
|
||||
// - matrix.ErrShape is returned if the number of rows or columns is negative,
|
||||
// - an error is returned if the resulting Dense matrix is too
|
||||
// big for the current architecture (e.g. a 16GB matrix written by a
|
||||
// 64b application and read back from a 32b application.)
|
||||
// UnmarshalBinary does not limit the size of the unmarshaled matrix, and so
|
||||
// it should not be used on untrusted data.
|
||||
func (m *Dense) UnmarshalBinaryFrom(r io.Reader) (int, error) {
|
||||
if !m.IsZero() {
|
||||
panic("mat: unmarshal into non-zero matrix")
|
||||
}
|
||||
|
||||
var header storage
|
||||
n, err := header.unmarshalBinaryFrom(r)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
rows := header.Rows
|
||||
cols := header.Cols
|
||||
header.Version = 0
|
||||
header.Rows = 0
|
||||
header.Cols = 0
|
||||
if (header != storage{Form: 'G', Packing: 'F', Uplo: 'A'}) {
|
||||
return n, errWrongType
|
||||
}
|
||||
if rows < 0 || cols < 0 {
|
||||
return n, errBadSize
|
||||
}
|
||||
size := rows * cols
|
||||
if size == 0 {
|
||||
return n, ErrZeroLength
|
||||
}
|
||||
if int(size) < 0 || size > maxLen {
|
||||
return n, errTooBig
|
||||
}
|
||||
|
||||
m.reuseAs(int(rows), int(cols))
|
||||
var b [8]byte
|
||||
for i := range m.mat.Data {
|
||||
nn, err := readFull(r, b[:])
|
||||
n += nn
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return n, io.ErrUnexpectedEOF
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
m.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(b[:]))
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// MarshalBinary encodes the receiver into a binary form and returns the result.
|
||||
//
|
||||
// VecDense is little-endian encoded as follows:
|
||||
//
|
||||
// 0 - 3 Version = 1 (uint32)
|
||||
// 4 'G' (byte)
|
||||
// 5 'F' (byte)
|
||||
// 6 'A' (byte)
|
||||
// 7 0 (byte)
|
||||
// 8 - 15 number of elements (int64)
|
||||
// 16 - 23 1 (int64)
|
||||
// 24 - 31 0 (int64)
|
||||
// 32 - 39 0 (int64)
|
||||
// 40 - .. vector's data elements (float64)
|
||||
func (v VecDense) MarshalBinary() ([]byte, error) {
|
||||
bufLen := int64(headerSize) + int64(v.mat.N)*int64(sizeFloat64)
|
||||
if bufLen <= 0 {
|
||||
// bufLen is too big and has wrapped around.
|
||||
return nil, errTooBig
|
||||
}
|
||||
|
||||
header := storage{
|
||||
Form: 'G', Packing: 'F', Uplo: 'A',
|
||||
Rows: int64(v.mat.N), Cols: 1,
|
||||
Version: version,
|
||||
}
|
||||
buf := make([]byte, bufLen)
|
||||
n, err := header.marshalBinaryTo(bytes.NewBuffer(buf[:0]))
|
||||
if err != nil {
|
||||
return buf[:n], err
|
||||
}
|
||||
|
||||
p := headerSize
|
||||
for i := 0; i < v.mat.N; i++ {
|
||||
binary.LittleEndian.PutUint64(buf[p:p+sizeFloat64], math.Float64bits(v.at(i)))
|
||||
p += sizeFloat64
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// MarshalBinaryTo encodes the receiver into a binary form, writes it to w and
|
||||
// returns the number of bytes written and an error if any.
|
||||
//
|
||||
// See MarshalBainry for the on-disk format.
|
||||
func (v VecDense) MarshalBinaryTo(w io.Writer) (int, error) {
|
||||
header := storage{
|
||||
Form: 'G', Packing: 'F', Uplo: 'A',
|
||||
Rows: int64(v.mat.N), Cols: 1,
|
||||
Version: version,
|
||||
}
|
||||
n, err := header.marshalBinaryTo(w)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
var buf [8]byte
|
||||
for i := 0; i < v.mat.N; i++ {
|
||||
binary.LittleEndian.PutUint64(buf[:], math.Float64bits(v.at(i)))
|
||||
nn, err := w.Write(buf[:])
|
||||
n += nn
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary form into the receiver.
|
||||
// It panics if the receiver is a non-zero VecDense.
|
||||
//
|
||||
// See MarshalBinary for the on-disk layout.
|
||||
//
|
||||
// Limited checks on the validity of the binary input are performed:
|
||||
// - matrix.ErrShape is returned if the number of rows is negative,
|
||||
// - an error is returned if the resulting VecDense is too
|
||||
// big for the current architecture (e.g. a 16GB vector written by a
|
||||
// 64b application and read back from a 32b application.)
|
||||
// UnmarshalBinary does not limit the size of the unmarshaled vector, and so
|
||||
// it should not be used on untrusted data.
|
||||
func (v *VecDense) UnmarshalBinary(data []byte) error {
|
||||
if !v.IsZero() {
|
||||
panic("mat: unmarshal into non-zero vector")
|
||||
}
|
||||
|
||||
if len(data) < headerSize {
|
||||
return errTooSmall
|
||||
}
|
||||
|
||||
var header storage
|
||||
err := header.unmarshalBinary(data[:headerSize])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if header.Cols != 1 {
|
||||
return ErrShape
|
||||
}
|
||||
n := header.Rows
|
||||
header.Version = 0
|
||||
header.Rows = 0
|
||||
header.Cols = 0
|
||||
if (header != storage{Form: 'G', Packing: 'F', Uplo: 'A'}) {
|
||||
return errWrongType
|
||||
}
|
||||
if n == 0 {
|
||||
return ErrZeroLength
|
||||
}
|
||||
if n < 0 {
|
||||
return errBadSize
|
||||
}
|
||||
if int64(maxLen) < n {
|
||||
return errTooBig
|
||||
}
|
||||
if len(data) != headerSize+int(n)*sizeFloat64 {
|
||||
return errBadBuffer
|
||||
}
|
||||
|
||||
p := headerSize
|
||||
v.reuseAs(int(n))
|
||||
for i := range v.mat.Data {
|
||||
v.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(data[p : p+sizeFloat64]))
|
||||
p += sizeFloat64
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBinaryFrom decodes the binary form into the receiver, from the
|
||||
// io.Reader and returns the number of bytes read and an error if any.
|
||||
// It panics if the receiver is a non-zero VecDense.
|
||||
//
|
||||
// See MarshalBinary for the on-disk layout.
|
||||
// See UnmarshalBinary for the list of sanity checks performed on the input.
|
||||
func (v *VecDense) UnmarshalBinaryFrom(r io.Reader) (int, error) {
|
||||
if !v.IsZero() {
|
||||
panic("mat: unmarshal into non-zero vector")
|
||||
}
|
||||
|
||||
var header storage
|
||||
n, err := header.unmarshalBinaryFrom(r)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
if header.Cols != 1 {
|
||||
return n, ErrShape
|
||||
}
|
||||
l := header.Rows
|
||||
header.Version = 0
|
||||
header.Rows = 0
|
||||
header.Cols = 0
|
||||
if (header != storage{Form: 'G', Packing: 'F', Uplo: 'A'}) {
|
||||
return n, errWrongType
|
||||
}
|
||||
if l == 0 {
|
||||
return n, ErrZeroLength
|
||||
}
|
||||
if l < 0 {
|
||||
return n, errBadSize
|
||||
}
|
||||
if int64(maxLen) < l {
|
||||
return n, errTooBig
|
||||
}
|
||||
|
||||
v.reuseAs(int(l))
|
||||
var b [8]byte
|
||||
for i := range v.mat.Data {
|
||||
nn, err := readFull(r, b[:])
|
||||
n += nn
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return n, io.ErrUnexpectedEOF
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
v.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(b[:]))
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// storage is the internal representation of the storage format of a
|
||||
// serialised matrix.
|
||||
type storage struct {
|
||||
Version uint32 // Keep this first.
|
||||
Form byte // [GST]
|
||||
Packing byte // [BPF]
|
||||
Uplo byte // [AUL]
|
||||
Unit bool
|
||||
Rows int64
|
||||
Cols int64
|
||||
KU int64
|
||||
KL int64
|
||||
}
|
||||
|
||||
// TODO(kortschak): Consider replacing these with calls to direct
|
||||
// encoding/decoding of fields rather than to binary.Write/binary.Read.
|
||||
|
||||
func (s storage) marshalBinaryTo(w io.Writer) (int, error) {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, headerSize))
|
||||
err := binary.Write(buf, binary.LittleEndian, s)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return w.Write(buf.Bytes())
|
||||
}
|
||||
|
||||
func (s *storage) unmarshalBinary(buf []byte) error {
|
||||
err := binary.Read(bytes.NewReader(buf), binary.LittleEndian, s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.Version != version {
|
||||
return fmt.Errorf("mat: incorrect version: %d", s.Version)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *storage) unmarshalBinaryFrom(r io.Reader) (int, error) {
|
||||
buf := make([]byte, headerSize)
|
||||
n, err := readFull(r, buf)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
return n, s.unmarshalBinary(buf[:n])
|
||||
}
|
||||
|
||||
// readFull reads from r into buf until it has read len(buf).
|
||||
// It returns the number of bytes copied and an error if fewer bytes were read.
|
||||
// If an EOF happens after reading fewer than len(buf) bytes, io.ErrUnexpectedEOF is returned.
|
||||
func readFull(r io.Reader, buf []byte) (int, error) {
|
||||
var n int
|
||||
var err error
|
||||
for n < len(buf) && err == nil {
|
||||
var nn int
|
||||
nn, err = r.Read(buf[n:])
|
||||
n += nn
|
||||
}
|
||||
if n == len(buf) {
|
||||
return n, nil
|
||||
}
|
||||
if err == io.EOF {
|
||||
return n, io.ErrUnexpectedEOF
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
262
vendor/gonum.org/v1/gonum/mat/lq.go
generated
vendored
Normal file
262
vendor/gonum.org/v1/gonum/mat/lq.go
generated
vendored
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
// Copyright ©2013 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
"gonum.org/v1/gonum/lapack"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
const badLQ = "mat: invalid LQ factorization"
|
||||
|
||||
// LQ is a type for creating and using the LQ factorization of a matrix.
|
||||
type LQ struct {
|
||||
lq *Dense
|
||||
tau []float64
|
||||
cond float64
|
||||
}
|
||||
|
||||
func (lq *LQ) updateCond(norm lapack.MatrixNorm) {
|
||||
// Since A = L*Q, and Q is orthogonal, we get for the condition number κ
|
||||
// κ(A) := |A| |A^-1| = |L*Q| |(L*Q)^-1| = |L| |Q^T * L^-1|
|
||||
// = |L| |L^-1| = κ(L),
|
||||
// where we used that fact that Q^-1 = Q^T. However, this assumes that
|
||||
// the matrix norm is invariant under orthogonal transformations which
|
||||
// is not the case for CondNorm. Hopefully the error is negligible: κ
|
||||
// is only a qualitative measure anyway.
|
||||
m := lq.lq.mat.Rows
|
||||
work := getFloats(3*m, false)
|
||||
iwork := getInts(m, false)
|
||||
l := lq.lq.asTriDense(m, blas.NonUnit, blas.Lower)
|
||||
v := lapack64.Trcon(norm, l.mat, work, iwork)
|
||||
lq.cond = 1 / v
|
||||
putFloats(work)
|
||||
putInts(iwork)
|
||||
}
|
||||
|
||||
// Factorize computes the LQ factorization of an m×n matrix a where n <= m. The LQ
|
||||
// factorization always exists even if A is singular.
|
||||
//
|
||||
// The LQ decomposition is a factorization of the matrix A such that A = L * Q.
|
||||
// The matrix Q is an orthonormal n×n matrix, and L is an m×n upper triangular matrix.
|
||||
// L and Q can be extracted from the LTo and QTo methods.
|
||||
func (lq *LQ) Factorize(a Matrix) {
|
||||
lq.factorize(a, CondNorm)
|
||||
}
|
||||
|
||||
func (lq *LQ) factorize(a Matrix, norm lapack.MatrixNorm) {
|
||||
m, n := a.Dims()
|
||||
if m > n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
k := min(m, n)
|
||||
if lq.lq == nil {
|
||||
lq.lq = &Dense{}
|
||||
}
|
||||
lq.lq.Clone(a)
|
||||
work := []float64{0}
|
||||
lq.tau = make([]float64, k)
|
||||
lapack64.Gelqf(lq.lq.mat, lq.tau, work, -1)
|
||||
work = getFloats(int(work[0]), false)
|
||||
lapack64.Gelqf(lq.lq.mat, lq.tau, work, len(work))
|
||||
putFloats(work)
|
||||
lq.updateCond(norm)
|
||||
}
|
||||
|
||||
// isValid returns whether the receiver contains a factorization.
|
||||
func (lq *LQ) isValid() bool {
|
||||
return lq.lq != nil && !lq.lq.IsZero()
|
||||
}
|
||||
|
||||
// Cond returns the condition number for the factorized matrix.
|
||||
// Cond will panic if the receiver does not contain a factorization.
|
||||
func (lq *LQ) Cond() float64 {
|
||||
if !lq.isValid() {
|
||||
panic(badLQ)
|
||||
}
|
||||
return lq.cond
|
||||
}
|
||||
|
||||
// TODO(btracey): Add in the "Reduced" forms for extracting the m×m orthogonal
|
||||
// and upper triangular matrices.
|
||||
|
||||
// LTo extracts the m×n lower trapezoidal matrix from a LQ decomposition.
|
||||
// If dst is nil, a new matrix is allocated. The resulting L matrix is returned.
|
||||
// LTo will panic if the receiver does not contain a factorization.
|
||||
func (lq *LQ) LTo(dst *Dense) *Dense {
|
||||
if !lq.isValid() {
|
||||
panic(badLQ)
|
||||
}
|
||||
|
||||
r, c := lq.lq.Dims()
|
||||
if dst == nil {
|
||||
dst = NewDense(r, c, nil)
|
||||
} else {
|
||||
dst.reuseAs(r, c)
|
||||
}
|
||||
|
||||
// Disguise the LQ as a lower triangular.
|
||||
t := &TriDense{
|
||||
mat: blas64.Triangular{
|
||||
N: r,
|
||||
Stride: lq.lq.mat.Stride,
|
||||
Data: lq.lq.mat.Data,
|
||||
Uplo: blas.Lower,
|
||||
Diag: blas.NonUnit,
|
||||
},
|
||||
cap: lq.lq.capCols,
|
||||
}
|
||||
dst.Copy(t)
|
||||
|
||||
if r == c {
|
||||
return dst
|
||||
}
|
||||
// Zero right of the triangular.
|
||||
for i := 0; i < r; i++ {
|
||||
zero(dst.mat.Data[i*dst.mat.Stride+r : i*dst.mat.Stride+c])
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// QTo extracts the n×n orthonormal matrix Q from an LQ decomposition.
|
||||
// If dst is nil, a new matrix is allocated. The resulting Q matrix is returned.
|
||||
// QTo will panic if the receiver does not contain a factorization.
|
||||
func (lq *LQ) QTo(dst *Dense) *Dense {
|
||||
if !lq.isValid() {
|
||||
panic(badLQ)
|
||||
}
|
||||
|
||||
_, c := lq.lq.Dims()
|
||||
if dst == nil {
|
||||
dst = NewDense(c, c, nil)
|
||||
} else {
|
||||
dst.reuseAsZeroed(c, c)
|
||||
}
|
||||
q := dst.mat
|
||||
|
||||
// Set Q = I.
|
||||
ldq := q.Stride
|
||||
for i := 0; i < c; i++ {
|
||||
q.Data[i*ldq+i] = 1
|
||||
}
|
||||
|
||||
// Construct Q from the elementary reflectors.
|
||||
work := []float64{0}
|
||||
lapack64.Ormlq(blas.Left, blas.NoTrans, lq.lq.mat, lq.tau, q, work, -1)
|
||||
work = getFloats(int(work[0]), false)
|
||||
lapack64.Ormlq(blas.Left, blas.NoTrans, lq.lq.mat, lq.tau, q, work, len(work))
|
||||
putFloats(work)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// SolveTo finds a minimum-norm solution to a system of linear equations defined
|
||||
// by the matrices A and b, where A is an m×n matrix represented in its LQ factorized
|
||||
// form. If A is singular or near-singular a Condition error is returned.
|
||||
// See the documentation for Condition for more information.
|
||||
//
|
||||
// The minimization problem solved depends on the input parameters.
|
||||
// If trans == false, find the minimum norm solution of A * X = B.
|
||||
// If trans == true, find X such that ||A*X - B||_2 is minimized.
|
||||
// The solution matrix, X, is stored in place into dst.
|
||||
// SolveTo will panic if the receiver does not contain a factorization.
|
||||
func (lq *LQ) SolveTo(dst *Dense, trans bool, b Matrix) error {
|
||||
if !lq.isValid() {
|
||||
panic(badLQ)
|
||||
}
|
||||
|
||||
r, c := lq.lq.Dims()
|
||||
br, bc := b.Dims()
|
||||
|
||||
// The LQ solve algorithm stores the result in-place into the right hand side.
|
||||
// The storage for the answer must be large enough to hold both b and x.
|
||||
// However, this method's receiver must be the size of x. Copy b, and then
|
||||
// copy the result into x at the end.
|
||||
if trans {
|
||||
if c != br {
|
||||
panic(ErrShape)
|
||||
}
|
||||
dst.reuseAs(r, bc)
|
||||
} else {
|
||||
if r != br {
|
||||
panic(ErrShape)
|
||||
}
|
||||
dst.reuseAs(c, bc)
|
||||
}
|
||||
// Do not need to worry about overlap between x and b because w has its own
|
||||
// independent storage.
|
||||
w := getWorkspace(max(r, c), bc, false)
|
||||
w.Copy(b)
|
||||
t := lq.lq.asTriDense(lq.lq.mat.Rows, blas.NonUnit, blas.Lower).mat
|
||||
if trans {
|
||||
work := []float64{0}
|
||||
lapack64.Ormlq(blas.Left, blas.NoTrans, lq.lq.mat, lq.tau, w.mat, work, -1)
|
||||
work = getFloats(int(work[0]), false)
|
||||
lapack64.Ormlq(blas.Left, blas.NoTrans, lq.lq.mat, lq.tau, w.mat, work, len(work))
|
||||
putFloats(work)
|
||||
|
||||
ok := lapack64.Trtrs(blas.Trans, t, w.mat)
|
||||
if !ok {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
} else {
|
||||
ok := lapack64.Trtrs(blas.NoTrans, t, w.mat)
|
||||
if !ok {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
for i := r; i < c; i++ {
|
||||
zero(w.mat.Data[i*w.mat.Stride : i*w.mat.Stride+bc])
|
||||
}
|
||||
work := []float64{0}
|
||||
lapack64.Ormlq(blas.Left, blas.Trans, lq.lq.mat, lq.tau, w.mat, work, -1)
|
||||
work = getFloats(int(work[0]), false)
|
||||
lapack64.Ormlq(blas.Left, blas.Trans, lq.lq.mat, lq.tau, w.mat, work, len(work))
|
||||
putFloats(work)
|
||||
}
|
||||
// x was set above to be the correct size for the result.
|
||||
dst.Copy(w)
|
||||
putWorkspace(w)
|
||||
if lq.cond > ConditionTolerance {
|
||||
return Condition(lq.cond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SolveVecTo finds a minimum-norm solution to a system of linear equations.
|
||||
// See LQ.SolveTo for the full documentation.
|
||||
// SolveToVec will panic if the receiver does not contain a factorization.
|
||||
func (lq *LQ) SolveVecTo(dst *VecDense, trans bool, b Vector) error {
|
||||
if !lq.isValid() {
|
||||
panic(badLQ)
|
||||
}
|
||||
|
||||
r, c := lq.lq.Dims()
|
||||
if _, bc := b.Dims(); bc != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
// The Solve implementation is non-trivial, so rather than duplicate the code,
|
||||
// instead recast the VecDenses as Dense and call the matrix code.
|
||||
bm := Matrix(b)
|
||||
if rv, ok := b.(RawVectorer); ok {
|
||||
bmat := rv.RawVector()
|
||||
if dst != b {
|
||||
dst.checkOverlap(bmat)
|
||||
}
|
||||
b := VecDense{mat: bmat}
|
||||
bm = b.asDense()
|
||||
}
|
||||
if trans {
|
||||
dst.reuseAs(r)
|
||||
} else {
|
||||
dst.reuseAs(c)
|
||||
}
|
||||
return lq.SolveTo(dst.asDense(), trans, bm)
|
||||
}
|
||||
422
vendor/gonum.org/v1/gonum/mat/lu.go
generated
vendored
Normal file
422
vendor/gonum.org/v1/gonum/mat/lu.go
generated
vendored
Normal file
|
|
@ -0,0 +1,422 @@
|
|||
// Copyright ©2013 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
"gonum.org/v1/gonum/floats"
|
||||
"gonum.org/v1/gonum/lapack"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
const (
|
||||
badSliceLength = "mat: improper slice length"
|
||||
badLU = "mat: invalid LU factorization"
|
||||
)
|
||||
|
||||
// LU is a type for creating and using the LU factorization of a matrix.
|
||||
type LU struct {
|
||||
lu *Dense
|
||||
pivot []int
|
||||
cond float64
|
||||
}
|
||||
|
||||
// updateCond updates the stored condition number of the matrix. anorm is the
|
||||
// norm of the original matrix. If anorm is negative it will be estimated.
|
||||
func (lu *LU) updateCond(anorm float64, norm lapack.MatrixNorm) {
|
||||
n := lu.lu.mat.Cols
|
||||
work := getFloats(4*n, false)
|
||||
defer putFloats(work)
|
||||
iwork := getInts(n, false)
|
||||
defer putInts(iwork)
|
||||
if anorm < 0 {
|
||||
// This is an approximation. By the definition of a norm,
|
||||
// |AB| <= |A| |B|.
|
||||
// Since A = L*U, we get for the condition number κ that
|
||||
// κ(A) := |A| |A^-1| = |L*U| |A^-1| <= |L| |U| |A^-1|,
|
||||
// so this will overestimate the condition number somewhat.
|
||||
// The norm of the original factorized matrix cannot be stored
|
||||
// because of update possibilities.
|
||||
u := lu.lu.asTriDense(n, blas.NonUnit, blas.Upper)
|
||||
l := lu.lu.asTriDense(n, blas.Unit, blas.Lower)
|
||||
unorm := lapack64.Lantr(norm, u.mat, work)
|
||||
lnorm := lapack64.Lantr(norm, l.mat, work)
|
||||
anorm = unorm * lnorm
|
||||
}
|
||||
v := lapack64.Gecon(norm, lu.lu.mat, anorm, work, iwork)
|
||||
lu.cond = 1 / v
|
||||
}
|
||||
|
||||
// Factorize computes the LU factorization of the square matrix a and stores the
|
||||
// result. The LU decomposition will complete regardless of the singularity of a.
|
||||
//
|
||||
// The LU factorization is computed with pivoting, and so really the decomposition
|
||||
// is a PLU decomposition where P is a permutation matrix. The individual matrix
|
||||
// factors can be extracted from the factorization using the Permutation method
|
||||
// on Dense, and the LU LTo and UTo methods.
|
||||
func (lu *LU) Factorize(a Matrix) {
|
||||
lu.factorize(a, CondNorm)
|
||||
}
|
||||
|
||||
func (lu *LU) factorize(a Matrix, norm lapack.MatrixNorm) {
|
||||
r, c := a.Dims()
|
||||
if r != c {
|
||||
panic(ErrSquare)
|
||||
}
|
||||
if lu.lu == nil {
|
||||
lu.lu = NewDense(r, r, nil)
|
||||
} else {
|
||||
lu.lu.Reset()
|
||||
lu.lu.reuseAs(r, r)
|
||||
}
|
||||
lu.lu.Copy(a)
|
||||
if cap(lu.pivot) < r {
|
||||
lu.pivot = make([]int, r)
|
||||
}
|
||||
lu.pivot = lu.pivot[:r]
|
||||
work := getFloats(r, false)
|
||||
anorm := lapack64.Lange(norm, lu.lu.mat, work)
|
||||
putFloats(work)
|
||||
lapack64.Getrf(lu.lu.mat, lu.pivot)
|
||||
lu.updateCond(anorm, norm)
|
||||
}
|
||||
|
||||
// isValid returns whether the receiver contains a factorization.
|
||||
func (lu *LU) isValid() bool {
|
||||
return lu.lu != nil && !lu.lu.IsZero()
|
||||
}
|
||||
|
||||
// Cond returns the condition number for the factorized matrix.
|
||||
// Cond will panic if the receiver does not contain a factorization.
|
||||
func (lu *LU) Cond() float64 {
|
||||
if !lu.isValid() {
|
||||
panic(badLU)
|
||||
}
|
||||
return lu.cond
|
||||
}
|
||||
|
||||
// Reset resets the factorization so that it can be reused as the receiver of a
|
||||
// dimensionally restricted operation.
|
||||
func (lu *LU) Reset() {
|
||||
if lu.lu != nil {
|
||||
lu.lu.Reset()
|
||||
}
|
||||
lu.pivot = lu.pivot[:0]
|
||||
}
|
||||
|
||||
func (lu *LU) isZero() bool {
|
||||
return len(lu.pivot) == 0
|
||||
}
|
||||
|
||||
// Det returns the determinant of the matrix that has been factorized. In many
|
||||
// expressions, using LogDet will be more numerically stable.
|
||||
// Det will panic if the receiver does not contain a factorization.
|
||||
func (lu *LU) Det() float64 {
|
||||
det, sign := lu.LogDet()
|
||||
return math.Exp(det) * sign
|
||||
}
|
||||
|
||||
// LogDet returns the log of the determinant and the sign of the determinant
|
||||
// for the matrix that has been factorized. Numerical stability in product and
|
||||
// division expressions is generally improved by working in log space.
|
||||
// LogDet will panic if the receiver does not contain a factorization.
|
||||
func (lu *LU) LogDet() (det float64, sign float64) {
|
||||
if !lu.isValid() {
|
||||
panic(badLU)
|
||||
}
|
||||
|
||||
_, n := lu.lu.Dims()
|
||||
logDiag := getFloats(n, false)
|
||||
defer putFloats(logDiag)
|
||||
sign = 1.0
|
||||
for i := 0; i < n; i++ {
|
||||
v := lu.lu.at(i, i)
|
||||
if v < 0 {
|
||||
sign *= -1
|
||||
}
|
||||
if lu.pivot[i] != i {
|
||||
sign *= -1
|
||||
}
|
||||
logDiag[i] = math.Log(math.Abs(v))
|
||||
}
|
||||
return floats.Sum(logDiag), sign
|
||||
}
|
||||
|
||||
// Pivot returns pivot indices that enable the construction of the permutation
|
||||
// matrix P (see Dense.Permutation). If swaps == nil, then new memory will be
|
||||
// allocated, otherwise the length of the input must be equal to the size of the
|
||||
// factorized matrix.
|
||||
// Pivot will panic if the receiver does not contain a factorization.
|
||||
func (lu *LU) Pivot(swaps []int) []int {
|
||||
if !lu.isValid() {
|
||||
panic(badLU)
|
||||
}
|
||||
|
||||
_, n := lu.lu.Dims()
|
||||
if swaps == nil {
|
||||
swaps = make([]int, n)
|
||||
}
|
||||
if len(swaps) != n {
|
||||
panic(badSliceLength)
|
||||
}
|
||||
// Perform the inverse of the row swaps in order to find the final
|
||||
// row swap position.
|
||||
for i := range swaps {
|
||||
swaps[i] = i
|
||||
}
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
v := lu.pivot[i]
|
||||
swaps[i], swaps[v] = swaps[v], swaps[i]
|
||||
}
|
||||
return swaps
|
||||
}
|
||||
|
||||
// RankOne updates an LU factorization as if a rank-one update had been applied to
|
||||
// the original matrix A, storing the result into the receiver. That is, if in
|
||||
// the original LU decomposition P * L * U = A, in the updated decomposition
|
||||
// P * L * U = A + alpha * x * y^T.
|
||||
// RankOne will panic if orig does not contain a factorization.
|
||||
func (lu *LU) RankOne(orig *LU, alpha float64, x, y Vector) {
|
||||
if !orig.isValid() {
|
||||
panic(badLU)
|
||||
}
|
||||
|
||||
// RankOne uses algorithm a1 on page 28 of "Multiple-Rank Updates to Matrix
|
||||
// Factorizations for Nonlinear Analysis and Circuit Design" by Linzhong Deng.
|
||||
// http://web.stanford.edu/group/SOL/dissertations/Linzhong-Deng-thesis.pdf
|
||||
_, n := orig.lu.Dims()
|
||||
if r, c := x.Dims(); r != n || c != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if r, c := y.Dims(); r != n || c != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if orig != lu {
|
||||
if lu.isZero() {
|
||||
if cap(lu.pivot) < n {
|
||||
lu.pivot = make([]int, n)
|
||||
}
|
||||
lu.pivot = lu.pivot[:n]
|
||||
if lu.lu == nil {
|
||||
lu.lu = NewDense(n, n, nil)
|
||||
} else {
|
||||
lu.lu.reuseAs(n, n)
|
||||
}
|
||||
} else if len(lu.pivot) != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
copy(lu.pivot, orig.pivot)
|
||||
lu.lu.Copy(orig.lu)
|
||||
}
|
||||
|
||||
xs := getFloats(n, false)
|
||||
defer putFloats(xs)
|
||||
ys := getFloats(n, false)
|
||||
defer putFloats(ys)
|
||||
for i := 0; i < n; i++ {
|
||||
xs[i] = x.AtVec(i)
|
||||
ys[i] = y.AtVec(i)
|
||||
}
|
||||
|
||||
// Adjust for the pivoting in the LU factorization
|
||||
for i, v := range lu.pivot {
|
||||
xs[i], xs[v] = xs[v], xs[i]
|
||||
}
|
||||
|
||||
lum := lu.lu.mat
|
||||
omega := alpha
|
||||
for j := 0; j < n; j++ {
|
||||
ujj := lum.Data[j*lum.Stride+j]
|
||||
ys[j] /= ujj
|
||||
theta := 1 + xs[j]*ys[j]*omega
|
||||
beta := omega * ys[j] / theta
|
||||
gamma := omega * xs[j]
|
||||
omega -= beta * gamma
|
||||
lum.Data[j*lum.Stride+j] *= theta
|
||||
for i := j + 1; i < n; i++ {
|
||||
xs[i] -= lum.Data[i*lum.Stride+j] * xs[j]
|
||||
tmp := ys[i]
|
||||
ys[i] -= lum.Data[j*lum.Stride+i] * ys[j]
|
||||
lum.Data[i*lum.Stride+j] += beta * xs[i]
|
||||
lum.Data[j*lum.Stride+i] += gamma * tmp
|
||||
}
|
||||
}
|
||||
lu.updateCond(-1, CondNorm)
|
||||
}
|
||||
|
||||
// LTo extracts the lower triangular matrix from an LU factorization.
|
||||
// If dst is nil, a new matrix is allocated. The resulting L matrix is returned.
|
||||
// LTo will panic if the receiver does not contain a factorization.
|
||||
func (lu *LU) LTo(dst *TriDense) *TriDense {
|
||||
if !lu.isValid() {
|
||||
panic(badLU)
|
||||
}
|
||||
|
||||
_, n := lu.lu.Dims()
|
||||
if dst == nil {
|
||||
dst = NewTriDense(n, Lower, nil)
|
||||
} else {
|
||||
dst.reuseAs(n, Lower)
|
||||
}
|
||||
// Extract the lower triangular elements.
|
||||
for i := 0; i < n; i++ {
|
||||
for j := 0; j < i; j++ {
|
||||
dst.mat.Data[i*dst.mat.Stride+j] = lu.lu.mat.Data[i*lu.lu.mat.Stride+j]
|
||||
}
|
||||
}
|
||||
// Set ones on the diagonal.
|
||||
for i := 0; i < n; i++ {
|
||||
dst.mat.Data[i*dst.mat.Stride+i] = 1
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// UTo extracts the upper triangular matrix from an LU factorization.
|
||||
// If dst is nil, a new matrix is allocated. The resulting U matrix is returned.
|
||||
// UTo will panic if the receiver does not contain a factorization.
|
||||
func (lu *LU) UTo(dst *TriDense) *TriDense {
|
||||
if !lu.isValid() {
|
||||
panic(badLU)
|
||||
}
|
||||
|
||||
_, n := lu.lu.Dims()
|
||||
if dst == nil {
|
||||
dst = NewTriDense(n, Upper, nil)
|
||||
} else {
|
||||
dst.reuseAs(n, Upper)
|
||||
}
|
||||
// Extract the upper triangular elements.
|
||||
for i := 0; i < n; i++ {
|
||||
for j := i; j < n; j++ {
|
||||
dst.mat.Data[i*dst.mat.Stride+j] = lu.lu.mat.Data[i*lu.lu.mat.Stride+j]
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Permutation constructs an r×r permutation matrix with the given row swaps.
|
||||
// A permutation matrix has exactly one element equal to one in each row and column
|
||||
// and all other elements equal to zero. swaps[i] specifies the row with which
|
||||
// i will be swapped, which is equivalent to the non-zero column of row i.
|
||||
func (m *Dense) Permutation(r int, swaps []int) {
|
||||
m.reuseAs(r, r)
|
||||
for i := 0; i < r; i++ {
|
||||
zero(m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+r])
|
||||
v := swaps[i]
|
||||
if v < 0 || v >= r {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
m.mat.Data[i*m.mat.Stride+v] = 1
|
||||
}
|
||||
}
|
||||
|
||||
// SolveTo solves a system of linear equations using the LU decomposition of a matrix.
|
||||
// It computes
|
||||
// A * X = B if trans == false
|
||||
// A^T * X = B if trans == true
|
||||
// In both cases, A is represented in LU factorized form, and the matrix X is
|
||||
// stored into dst.
|
||||
//
|
||||
// If A is singular or near-singular a Condition error is returned. See
|
||||
// the documentation for Condition for more information.
|
||||
// SolveTo will panic if the receiver does not contain a factorization.
|
||||
func (lu *LU) SolveTo(dst *Dense, trans bool, b Matrix) error {
|
||||
if !lu.isValid() {
|
||||
panic(badLU)
|
||||
}
|
||||
|
||||
_, n := lu.lu.Dims()
|
||||
br, bc := b.Dims()
|
||||
if br != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
// TODO(btracey): Should test the condition number instead of testing that
|
||||
// the determinant is exactly zero.
|
||||
if lu.Det() == 0 {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
|
||||
dst.reuseAs(n, bc)
|
||||
bU, _ := untranspose(b)
|
||||
var restore func()
|
||||
if dst == bU {
|
||||
dst, restore = dst.isolatedWorkspace(bU)
|
||||
defer restore()
|
||||
} else if rm, ok := bU.(RawMatrixer); ok {
|
||||
dst.checkOverlap(rm.RawMatrix())
|
||||
}
|
||||
|
||||
dst.Copy(b)
|
||||
t := blas.NoTrans
|
||||
if trans {
|
||||
t = blas.Trans
|
||||
}
|
||||
lapack64.Getrs(t, lu.lu.mat, dst.mat, lu.pivot)
|
||||
if lu.cond > ConditionTolerance {
|
||||
return Condition(lu.cond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SolveVecTo solves a system of linear equations using the LU decomposition of a matrix.
|
||||
// It computes
|
||||
// A * x = b if trans == false
|
||||
// A^T * x = b if trans == true
|
||||
// In both cases, A is represented in LU factorized form, and the vector x is
|
||||
// stored into dst.
|
||||
//
|
||||
// If A is singular or near-singular a Condition error is returned. See
|
||||
// the documentation for Condition for more information.
|
||||
// SolveVecTo will panic if the receiver does not contain a factorization.
|
||||
func (lu *LU) SolveVecTo(dst *VecDense, trans bool, b Vector) error {
|
||||
if !lu.isValid() {
|
||||
panic(badLU)
|
||||
}
|
||||
|
||||
_, n := lu.lu.Dims()
|
||||
if br, bc := b.Dims(); br != n || bc != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
switch rv := b.(type) {
|
||||
default:
|
||||
dst.reuseAs(n)
|
||||
return lu.SolveTo(dst.asDense(), trans, b)
|
||||
case RawVectorer:
|
||||
if dst != b {
|
||||
dst.checkOverlap(rv.RawVector())
|
||||
}
|
||||
// TODO(btracey): Should test the condition number instead of testing that
|
||||
// the determinant is exactly zero.
|
||||
if lu.Det() == 0 {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
|
||||
dst.reuseAs(n)
|
||||
var restore func()
|
||||
if dst == b {
|
||||
dst, restore = dst.isolatedWorkspace(b)
|
||||
defer restore()
|
||||
}
|
||||
dst.CopyVec(b)
|
||||
vMat := blas64.General{
|
||||
Rows: n,
|
||||
Cols: 1,
|
||||
Stride: dst.mat.Inc,
|
||||
Data: dst.mat.Data,
|
||||
}
|
||||
t := blas.NoTrans
|
||||
if trans {
|
||||
t = blas.Trans
|
||||
}
|
||||
lapack64.Getrs(t, lu.lu.mat, vMat, lu.pivot)
|
||||
if lu.cond > ConditionTolerance {
|
||||
return Condition(lu.cond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
946
vendor/gonum.org/v1/gonum/mat/matrix.go
generated
vendored
Normal file
946
vendor/gonum.org/v1/gonum/mat/matrix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,946 @@
|
|||
// Copyright ©2013 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
"gonum.org/v1/gonum/floats"
|
||||
"gonum.org/v1/gonum/lapack"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
// Matrix is the basic matrix interface type.
|
||||
type Matrix interface {
|
||||
// Dims returns the dimensions of a Matrix.
|
||||
Dims() (r, c int)
|
||||
|
||||
// At returns the value of a matrix element at row i, column j.
|
||||
// It will panic if i or j are out of bounds for the matrix.
|
||||
At(i, j int) float64
|
||||
|
||||
// T returns the transpose of the Matrix. Whether T returns a copy of the
|
||||
// underlying data is implementation dependent.
|
||||
// This method may be implemented using the Transpose type, which
|
||||
// provides an implicit matrix transpose.
|
||||
T() Matrix
|
||||
}
|
||||
|
||||
var (
|
||||
_ Matrix = Transpose{}
|
||||
_ Untransposer = Transpose{}
|
||||
)
|
||||
|
||||
// Transpose is a type for performing an implicit matrix transpose. It implements
|
||||
// the Matrix interface, returning values from the transpose of the matrix within.
|
||||
type Transpose struct {
|
||||
Matrix Matrix
|
||||
}
|
||||
|
||||
// At returns the value of the element at row i and column j of the transposed
|
||||
// matrix, that is, row j and column i of the Matrix field.
|
||||
func (t Transpose) At(i, j int) float64 {
|
||||
return t.Matrix.At(j, i)
|
||||
}
|
||||
|
||||
// Dims returns the dimensions of the transposed matrix. The number of rows returned
|
||||
// is the number of columns in the Matrix field, and the number of columns is
|
||||
// the number of rows in the Matrix field.
|
||||
func (t Transpose) Dims() (r, c int) {
|
||||
c, r = t.Matrix.Dims()
|
||||
return r, c
|
||||
}
|
||||
|
||||
// T performs an implicit transpose by returning the Matrix field.
|
||||
func (t Transpose) T() Matrix {
|
||||
return t.Matrix
|
||||
}
|
||||
|
||||
// Untranspose returns the Matrix field.
|
||||
func (t Transpose) Untranspose() Matrix {
|
||||
return t.Matrix
|
||||
}
|
||||
|
||||
// Untransposer is a type that can undo an implicit transpose.
|
||||
type Untransposer interface {
|
||||
// Note: This interface is needed to unify all of the Transpose types. In
|
||||
// the mat methods, we need to test if the Matrix has been implicitly
|
||||
// transposed. If this is checked by testing for the specific Transpose type
|
||||
// then the behavior will be different if the user uses T() or TTri() for a
|
||||
// triangular matrix.
|
||||
|
||||
// Untranspose returns the underlying Matrix stored for the implicit transpose.
|
||||
Untranspose() Matrix
|
||||
}
|
||||
|
||||
// UntransposeBander is a type that can undo an implicit band transpose.
|
||||
type UntransposeBander interface {
|
||||
// Untranspose returns the underlying Banded stored for the implicit transpose.
|
||||
UntransposeBand() Banded
|
||||
}
|
||||
|
||||
// UntransposeTrier is a type that can undo an implicit triangular transpose.
|
||||
type UntransposeTrier interface {
|
||||
// Untranspose returns the underlying Triangular stored for the implicit transpose.
|
||||
UntransposeTri() Triangular
|
||||
}
|
||||
|
||||
// UntransposeTriBander is a type that can undo an implicit triangular banded
|
||||
// transpose.
|
||||
type UntransposeTriBander interface {
|
||||
// Untranspose returns the underlying Triangular stored for the implicit transpose.
|
||||
UntransposeTriBand() TriBanded
|
||||
}
|
||||
|
||||
// Mutable is a matrix interface type that allows elements to be altered.
|
||||
type Mutable interface {
|
||||
// Set alters the matrix element at row i, column j to v.
|
||||
// It will panic if i or j are out of bounds for the matrix.
|
||||
Set(i, j int, v float64)
|
||||
|
||||
Matrix
|
||||
}
|
||||
|
||||
// A RowViewer can return a Vector reflecting a row that is backed by the matrix
|
||||
// data. The Vector returned will have length equal to the number of columns.
|
||||
type RowViewer interface {
|
||||
RowView(i int) Vector
|
||||
}
|
||||
|
||||
// A RawRowViewer can return a slice of float64 reflecting a row that is backed by the matrix
|
||||
// data.
|
||||
type RawRowViewer interface {
|
||||
RawRowView(i int) []float64
|
||||
}
|
||||
|
||||
// A ColViewer can return a Vector reflecting a column that is backed by the matrix
|
||||
// data. The Vector returned will have length equal to the number of rows.
|
||||
type ColViewer interface {
|
||||
ColView(j int) Vector
|
||||
}
|
||||
|
||||
// A RawColViewer can return a slice of float64 reflecting a column that is backed by the matrix
|
||||
// data.
|
||||
type RawColViewer interface {
|
||||
RawColView(j int) []float64
|
||||
}
|
||||
|
||||
// A Cloner can make a copy of a into the receiver, overwriting the previous value of the
|
||||
// receiver. The clone operation does not make any restriction on shape and will not cause
|
||||
// shadowing.
|
||||
type Cloner interface {
|
||||
Clone(a Matrix)
|
||||
}
|
||||
|
||||
// A Reseter can reset the matrix so that it can be reused as the receiver of a dimensionally
|
||||
// restricted operation. This is commonly used when the matrix is being used as a workspace
|
||||
// or temporary matrix.
|
||||
//
|
||||
// If the matrix is a view, using the reset matrix may result in data corruption in elements
|
||||
// outside the view.
|
||||
type Reseter interface {
|
||||
Reset()
|
||||
}
|
||||
|
||||
// A Copier can make a copy of elements of a into the receiver. The submatrix copied
|
||||
// starts at row and column 0 and has dimensions equal to the minimum dimensions of
|
||||
// the two matrices. The number of row and columns copied is returned.
|
||||
// Copy will copy from a source that aliases the receiver unless the source is transposed;
|
||||
// an aliasing transpose copy will panic with the exception for a special case when
|
||||
// the source data has a unitary increment or stride.
|
||||
type Copier interface {
|
||||
Copy(a Matrix) (r, c int)
|
||||
}
|
||||
|
||||
// A Grower can grow the size of the represented matrix by the given number of rows and columns.
|
||||
// Growing beyond the size given by the Caps method will result in the allocation of a new
|
||||
// matrix and copying of the elements. If Grow is called with negative increments it will
|
||||
// panic with ErrIndexOutOfRange.
|
||||
type Grower interface {
|
||||
Caps() (r, c int)
|
||||
Grow(r, c int) Matrix
|
||||
}
|
||||
|
||||
// A BandWidther represents a banded matrix and can return the left and right half-bandwidths, k1 and
|
||||
// k2.
|
||||
type BandWidther interface {
|
||||
BandWidth() (k1, k2 int)
|
||||
}
|
||||
|
||||
// A RawMatrixSetter can set the underlying blas64.General used by the receiver. There is no restriction
|
||||
// on the shape of the receiver. Changes to the receiver's elements will be reflected in the blas64.General.Data.
|
||||
type RawMatrixSetter interface {
|
||||
SetRawMatrix(a blas64.General)
|
||||
}
|
||||
|
||||
// A RawMatrixer can return a blas64.General representation of the receiver. Changes to the blas64.General.Data
|
||||
// slice will be reflected in the original matrix, changes to the Rows, Cols and Stride fields will not.
|
||||
type RawMatrixer interface {
|
||||
RawMatrix() blas64.General
|
||||
}
|
||||
|
||||
// A RawVectorer can return a blas64.Vector representation of the receiver. Changes to the blas64.Vector.Data
|
||||
// slice will be reflected in the original matrix, changes to the Inc field will not.
|
||||
type RawVectorer interface {
|
||||
RawVector() blas64.Vector
|
||||
}
|
||||
|
||||
// A NonZeroDoer can call a function for each non-zero element of the receiver.
|
||||
// The parameters of the function are the element indices and its value.
|
||||
type NonZeroDoer interface {
|
||||
DoNonZero(func(i, j int, v float64))
|
||||
}
|
||||
|
||||
// A RowNonZeroDoer can call a function for each non-zero element of a row of the receiver.
|
||||
// The parameters of the function are the element indices and its value.
|
||||
type RowNonZeroDoer interface {
|
||||
DoRowNonZero(i int, fn func(i, j int, v float64))
|
||||
}
|
||||
|
||||
// A ColNonZeroDoer can call a function for each non-zero element of a column of the receiver.
|
||||
// The parameters of the function are the element indices and its value.
|
||||
type ColNonZeroDoer interface {
|
||||
DoColNonZero(j int, fn func(i, j int, v float64))
|
||||
}
|
||||
|
||||
// untranspose untransposes a matrix if applicable. If a is an Untransposer, then
|
||||
// untranspose returns the underlying matrix and true. If it is not, then it returns
|
||||
// the input matrix and false.
|
||||
func untranspose(a Matrix) (Matrix, bool) {
|
||||
if ut, ok := a.(Untransposer); ok {
|
||||
return ut.Untranspose(), true
|
||||
}
|
||||
return a, false
|
||||
}
|
||||
|
||||
// untransposeExtract returns an untransposed matrix in a built-in matrix type.
|
||||
//
|
||||
// The untransposed matrix is returned unaltered if it is a built-in matrix type.
|
||||
// Otherwise, if it implements a Raw method, an appropriate built-in type value
|
||||
// is returned holding the raw matrix value of the input. If neither of these
|
||||
// is possible, the untransposed matrix is returned.
|
||||
func untransposeExtract(a Matrix) (Matrix, bool) {
|
||||
ut, trans := untranspose(a)
|
||||
switch m := ut.(type) {
|
||||
case *DiagDense, *SymBandDense, *TriBandDense, *BandDense, *TriDense, *SymDense, *Dense:
|
||||
return m, trans
|
||||
// TODO(btracey): Add here if we ever have an equivalent of RawDiagDense.
|
||||
case RawSymBander:
|
||||
rsb := m.RawSymBand()
|
||||
if rsb.Uplo != blas.Upper {
|
||||
return ut, trans
|
||||
}
|
||||
var sb SymBandDense
|
||||
sb.SetRawSymBand(rsb)
|
||||
return &sb, trans
|
||||
case RawTriBander:
|
||||
rtb := m.RawTriBand()
|
||||
if rtb.Diag == blas.Unit {
|
||||
return ut, trans
|
||||
}
|
||||
var tb TriBandDense
|
||||
tb.SetRawTriBand(rtb)
|
||||
return &tb, trans
|
||||
case RawBander:
|
||||
var b BandDense
|
||||
b.SetRawBand(m.RawBand())
|
||||
return &b, trans
|
||||
case RawTriangular:
|
||||
rt := m.RawTriangular()
|
||||
if rt.Diag == blas.Unit {
|
||||
return ut, trans
|
||||
}
|
||||
var t TriDense
|
||||
t.SetRawTriangular(rt)
|
||||
return &t, trans
|
||||
case RawSymmetricer:
|
||||
rs := m.RawSymmetric()
|
||||
if rs.Uplo != blas.Upper {
|
||||
return ut, trans
|
||||
}
|
||||
var s SymDense
|
||||
s.SetRawSymmetric(rs)
|
||||
return &s, trans
|
||||
case RawMatrixer:
|
||||
var d Dense
|
||||
d.SetRawMatrix(m.RawMatrix())
|
||||
return &d, trans
|
||||
default:
|
||||
return ut, trans
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(btracey): Consider adding CopyCol/CopyRow if the behavior seems useful.
|
||||
// TODO(btracey): Add in fast paths to Row/Col for the other concrete types
|
||||
// (TriDense, etc.) as well as relevant interfaces (RowColer, RawRowViewer, etc.)
|
||||
|
||||
// Col copies the elements in the jth column of the matrix into the slice dst.
|
||||
// The length of the provided slice must equal the number of rows, unless the
|
||||
// slice is nil in which case a new slice is first allocated.
|
||||
func Col(dst []float64, j int, a Matrix) []float64 {
|
||||
r, c := a.Dims()
|
||||
if j < 0 || j >= c {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
if dst == nil {
|
||||
dst = make([]float64, r)
|
||||
} else {
|
||||
if len(dst) != r {
|
||||
panic(ErrColLength)
|
||||
}
|
||||
}
|
||||
aU, aTrans := untranspose(a)
|
||||
if rm, ok := aU.(RawMatrixer); ok {
|
||||
m := rm.RawMatrix()
|
||||
if aTrans {
|
||||
copy(dst, m.Data[j*m.Stride:j*m.Stride+m.Cols])
|
||||
return dst
|
||||
}
|
||||
blas64.Copy(blas64.Vector{N: r, Inc: m.Stride, Data: m.Data[j:]},
|
||||
blas64.Vector{N: r, Inc: 1, Data: dst},
|
||||
)
|
||||
return dst
|
||||
}
|
||||
for i := 0; i < r; i++ {
|
||||
dst[i] = a.At(i, j)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Row copies the elements in the ith row of the matrix into the slice dst.
|
||||
// The length of the provided slice must equal the number of columns, unless the
|
||||
// slice is nil in which case a new slice is first allocated.
|
||||
func Row(dst []float64, i int, a Matrix) []float64 {
|
||||
r, c := a.Dims()
|
||||
if i < 0 || i >= r {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
if dst == nil {
|
||||
dst = make([]float64, c)
|
||||
} else {
|
||||
if len(dst) != c {
|
||||
panic(ErrRowLength)
|
||||
}
|
||||
}
|
||||
aU, aTrans := untranspose(a)
|
||||
if rm, ok := aU.(RawMatrixer); ok {
|
||||
m := rm.RawMatrix()
|
||||
if aTrans {
|
||||
blas64.Copy(blas64.Vector{N: c, Inc: m.Stride, Data: m.Data[i:]},
|
||||
blas64.Vector{N: c, Inc: 1, Data: dst},
|
||||
)
|
||||
return dst
|
||||
}
|
||||
copy(dst, m.Data[i*m.Stride:i*m.Stride+m.Cols])
|
||||
return dst
|
||||
}
|
||||
for j := 0; j < c; j++ {
|
||||
dst[j] = a.At(i, j)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Cond returns the condition number of the given matrix under the given norm.
|
||||
// The condition number must be based on the 1-norm, 2-norm or ∞-norm.
|
||||
// Cond will panic with matrix.ErrShape if the matrix has zero size.
|
||||
//
|
||||
// BUG(btracey): The computation of the 1-norm and ∞-norm for non-square matrices
|
||||
// is innacurate, although is typically the right order of magnitude. See
|
||||
// https://github.com/xianyi/OpenBLAS/issues/636. While the value returned will
|
||||
// change with the resolution of this bug, the result from Cond will match the
|
||||
// condition number used internally.
|
||||
func Cond(a Matrix, norm float64) float64 {
|
||||
m, n := a.Dims()
|
||||
if m == 0 || n == 0 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
var lnorm lapack.MatrixNorm
|
||||
switch norm {
|
||||
default:
|
||||
panic("mat: bad norm value")
|
||||
case 1:
|
||||
lnorm = lapack.MaxColumnSum
|
||||
case 2:
|
||||
var svd SVD
|
||||
ok := svd.Factorize(a, SVDNone)
|
||||
if !ok {
|
||||
return math.Inf(1)
|
||||
}
|
||||
return svd.Cond()
|
||||
case math.Inf(1):
|
||||
lnorm = lapack.MaxRowSum
|
||||
}
|
||||
|
||||
if m == n {
|
||||
// Use the LU decomposition to compute the condition number.
|
||||
var lu LU
|
||||
lu.factorize(a, lnorm)
|
||||
return lu.Cond()
|
||||
}
|
||||
if m > n {
|
||||
// Use the QR factorization to compute the condition number.
|
||||
var qr QR
|
||||
qr.factorize(a, lnorm)
|
||||
return qr.Cond()
|
||||
}
|
||||
// Use the LQ factorization to compute the condition number.
|
||||
var lq LQ
|
||||
lq.factorize(a, lnorm)
|
||||
return lq.Cond()
|
||||
}
|
||||
|
||||
// Det returns the determinant of the matrix a. In many expressions using LogDet
|
||||
// will be more numerically stable.
|
||||
func Det(a Matrix) float64 {
|
||||
det, sign := LogDet(a)
|
||||
return math.Exp(det) * sign
|
||||
}
|
||||
|
||||
// Dot returns the sum of the element-wise product of a and b.
|
||||
// Dot panics if the matrix sizes are unequal.
|
||||
func Dot(a, b Vector) float64 {
|
||||
la := a.Len()
|
||||
lb := b.Len()
|
||||
if la != lb {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if arv, ok := a.(RawVectorer); ok {
|
||||
if brv, ok := b.(RawVectorer); ok {
|
||||
return blas64.Dot(arv.RawVector(), brv.RawVector())
|
||||
}
|
||||
}
|
||||
var sum float64
|
||||
for i := 0; i < la; i++ {
|
||||
sum += a.At(i, 0) * b.At(i, 0)
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
// Equal returns whether the matrices a and b have the same size
|
||||
// and are element-wise equal.
|
||||
func Equal(a, b Matrix) bool {
|
||||
ar, ac := a.Dims()
|
||||
br, bc := b.Dims()
|
||||
if ar != br || ac != bc {
|
||||
return false
|
||||
}
|
||||
aU, aTrans := untranspose(a)
|
||||
bU, bTrans := untranspose(b)
|
||||
if rma, ok := aU.(RawMatrixer); ok {
|
||||
if rmb, ok := bU.(RawMatrixer); ok {
|
||||
ra := rma.RawMatrix()
|
||||
rb := rmb.RawMatrix()
|
||||
if aTrans == bTrans {
|
||||
for i := 0; i < ra.Rows; i++ {
|
||||
for j := 0; j < ra.Cols; j++ {
|
||||
if ra.Data[i*ra.Stride+j] != rb.Data[i*rb.Stride+j] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
for i := 0; i < ra.Rows; i++ {
|
||||
for j := 0; j < ra.Cols; j++ {
|
||||
if ra.Data[i*ra.Stride+j] != rb.Data[j*rb.Stride+i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
if rma, ok := aU.(RawSymmetricer); ok {
|
||||
if rmb, ok := bU.(RawSymmetricer); ok {
|
||||
ra := rma.RawSymmetric()
|
||||
rb := rmb.RawSymmetric()
|
||||
// Symmetric matrices are always upper and equal to their transpose.
|
||||
for i := 0; i < ra.N; i++ {
|
||||
for j := i; j < ra.N; j++ {
|
||||
if ra.Data[i*ra.Stride+j] != rb.Data[i*rb.Stride+j] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
if ra, ok := aU.(*VecDense); ok {
|
||||
if rb, ok := bU.(*VecDense); ok {
|
||||
// If the raw vectors are the same length they must either both be
|
||||
// transposed or both not transposed (or have length 1).
|
||||
for i := 0; i < ra.mat.N; i++ {
|
||||
if ra.mat.Data[i*ra.mat.Inc] != rb.mat.Data[i*rb.mat.Inc] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
for i := 0; i < ar; i++ {
|
||||
for j := 0; j < ac; j++ {
|
||||
if a.At(i, j) != b.At(i, j) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// EqualApprox returns whether the matrices a and b have the same size and contain all equal
|
||||
// elements with tolerance for element-wise equality specified by epsilon. Matrices
|
||||
// with non-equal shapes are not equal.
|
||||
func EqualApprox(a, b Matrix, epsilon float64) bool {
|
||||
ar, ac := a.Dims()
|
||||
br, bc := b.Dims()
|
||||
if ar != br || ac != bc {
|
||||
return false
|
||||
}
|
||||
aU, aTrans := untranspose(a)
|
||||
bU, bTrans := untranspose(b)
|
||||
if rma, ok := aU.(RawMatrixer); ok {
|
||||
if rmb, ok := bU.(RawMatrixer); ok {
|
||||
ra := rma.RawMatrix()
|
||||
rb := rmb.RawMatrix()
|
||||
if aTrans == bTrans {
|
||||
for i := 0; i < ra.Rows; i++ {
|
||||
for j := 0; j < ra.Cols; j++ {
|
||||
if !floats.EqualWithinAbsOrRel(ra.Data[i*ra.Stride+j], rb.Data[i*rb.Stride+j], epsilon, epsilon) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
for i := 0; i < ra.Rows; i++ {
|
||||
for j := 0; j < ra.Cols; j++ {
|
||||
if !floats.EqualWithinAbsOrRel(ra.Data[i*ra.Stride+j], rb.Data[j*rb.Stride+i], epsilon, epsilon) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
if rma, ok := aU.(RawSymmetricer); ok {
|
||||
if rmb, ok := bU.(RawSymmetricer); ok {
|
||||
ra := rma.RawSymmetric()
|
||||
rb := rmb.RawSymmetric()
|
||||
// Symmetric matrices are always upper and equal to their transpose.
|
||||
for i := 0; i < ra.N; i++ {
|
||||
for j := i; j < ra.N; j++ {
|
||||
if !floats.EqualWithinAbsOrRel(ra.Data[i*ra.Stride+j], rb.Data[i*rb.Stride+j], epsilon, epsilon) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
if ra, ok := aU.(*VecDense); ok {
|
||||
if rb, ok := bU.(*VecDense); ok {
|
||||
// If the raw vectors are the same length they must either both be
|
||||
// transposed or both not transposed (or have length 1).
|
||||
for i := 0; i < ra.mat.N; i++ {
|
||||
if !floats.EqualWithinAbsOrRel(ra.mat.Data[i*ra.mat.Inc], rb.mat.Data[i*rb.mat.Inc], epsilon, epsilon) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
for i := 0; i < ar; i++ {
|
||||
for j := 0; j < ac; j++ {
|
||||
if !floats.EqualWithinAbsOrRel(a.At(i, j), b.At(i, j), epsilon, epsilon) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// LogDet returns the log of the determinant and the sign of the determinant
|
||||
// for the matrix that has been factorized. Numerical stability in product and
|
||||
// division expressions is generally improved by working in log space.
|
||||
func LogDet(a Matrix) (det float64, sign float64) {
|
||||
// TODO(btracey): Add specialized routines for TriDense, etc.
|
||||
var lu LU
|
||||
lu.Factorize(a)
|
||||
return lu.LogDet()
|
||||
}
|
||||
|
||||
// Max returns the largest element value of the matrix A.
|
||||
// Max will panic with matrix.ErrShape if the matrix has zero size.
|
||||
func Max(a Matrix) float64 {
|
||||
r, c := a.Dims()
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
// Max(A) = Max(A^T)
|
||||
aU, _ := untranspose(a)
|
||||
switch m := aU.(type) {
|
||||
case RawMatrixer:
|
||||
rm := m.RawMatrix()
|
||||
max := math.Inf(-1)
|
||||
for i := 0; i < rm.Rows; i++ {
|
||||
for _, v := range rm.Data[i*rm.Stride : i*rm.Stride+rm.Cols] {
|
||||
if v > max {
|
||||
max = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return max
|
||||
case RawTriangular:
|
||||
rm := m.RawTriangular()
|
||||
// The max of a triangular is at least 0 unless the size is 1.
|
||||
if rm.N == 1 {
|
||||
return rm.Data[0]
|
||||
}
|
||||
max := 0.0
|
||||
if rm.Uplo == blas.Upper {
|
||||
for i := 0; i < rm.N; i++ {
|
||||
for _, v := range rm.Data[i*rm.Stride+i : i*rm.Stride+rm.N] {
|
||||
if v > max {
|
||||
max = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
for i := 0; i < rm.N; i++ {
|
||||
for _, v := range rm.Data[i*rm.Stride : i*rm.Stride+i+1] {
|
||||
if v > max {
|
||||
max = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return max
|
||||
case RawSymmetricer:
|
||||
rm := m.RawSymmetric()
|
||||
if rm.Uplo != blas.Upper {
|
||||
panic(badSymTriangle)
|
||||
}
|
||||
max := math.Inf(-1)
|
||||
for i := 0; i < rm.N; i++ {
|
||||
for _, v := range rm.Data[i*rm.Stride+i : i*rm.Stride+rm.N] {
|
||||
if v > max {
|
||||
max = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return max
|
||||
default:
|
||||
r, c := aU.Dims()
|
||||
max := math.Inf(-1)
|
||||
for i := 0; i < r; i++ {
|
||||
for j := 0; j < c; j++ {
|
||||
v := aU.At(i, j)
|
||||
if v > max {
|
||||
max = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
}
|
||||
|
||||
// Min returns the smallest element value of the matrix A.
|
||||
// Min will panic with matrix.ErrShape if the matrix has zero size.
|
||||
func Min(a Matrix) float64 {
|
||||
r, c := a.Dims()
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
// Min(A) = Min(A^T)
|
||||
aU, _ := untranspose(a)
|
||||
switch m := aU.(type) {
|
||||
case RawMatrixer:
|
||||
rm := m.RawMatrix()
|
||||
min := math.Inf(1)
|
||||
for i := 0; i < rm.Rows; i++ {
|
||||
for _, v := range rm.Data[i*rm.Stride : i*rm.Stride+rm.Cols] {
|
||||
if v < min {
|
||||
min = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return min
|
||||
case RawTriangular:
|
||||
rm := m.RawTriangular()
|
||||
// The min of a triangular is at most 0 unless the size is 1.
|
||||
if rm.N == 1 {
|
||||
return rm.Data[0]
|
||||
}
|
||||
min := 0.0
|
||||
if rm.Uplo == blas.Upper {
|
||||
for i := 0; i < rm.N; i++ {
|
||||
for _, v := range rm.Data[i*rm.Stride+i : i*rm.Stride+rm.N] {
|
||||
if v < min {
|
||||
min = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return min
|
||||
}
|
||||
for i := 0; i < rm.N; i++ {
|
||||
for _, v := range rm.Data[i*rm.Stride : i*rm.Stride+i+1] {
|
||||
if v < min {
|
||||
min = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return min
|
||||
case RawSymmetricer:
|
||||
rm := m.RawSymmetric()
|
||||
if rm.Uplo != blas.Upper {
|
||||
panic(badSymTriangle)
|
||||
}
|
||||
min := math.Inf(1)
|
||||
for i := 0; i < rm.N; i++ {
|
||||
for _, v := range rm.Data[i*rm.Stride+i : i*rm.Stride+rm.N] {
|
||||
if v < min {
|
||||
min = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return min
|
||||
default:
|
||||
r, c := aU.Dims()
|
||||
min := math.Inf(1)
|
||||
for i := 0; i < r; i++ {
|
||||
for j := 0; j < c; j++ {
|
||||
v := aU.At(i, j)
|
||||
if v < min {
|
||||
min = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return min
|
||||
}
|
||||
}
|
||||
|
||||
// Norm returns the specified (induced) norm of the matrix a. See
|
||||
// https://en.wikipedia.org/wiki/Matrix_norm for the definition of an induced norm.
|
||||
//
|
||||
// Valid norms are:
|
||||
// 1 - The maximum absolute column sum
|
||||
// 2 - Frobenius norm, the square root of the sum of the squares of the elements.
|
||||
// Inf - The maximum absolute row sum.
|
||||
// Norm will panic with ErrNormOrder if an illegal norm order is specified and
|
||||
// with matrix.ErrShape if the matrix has zero size.
|
||||
func Norm(a Matrix, norm float64) float64 {
|
||||
r, c := a.Dims()
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
aU, aTrans := untranspose(a)
|
||||
var work []float64
|
||||
switch rma := aU.(type) {
|
||||
case RawMatrixer:
|
||||
rm := rma.RawMatrix()
|
||||
n := normLapack(norm, aTrans)
|
||||
if n == lapack.MaxColumnSum {
|
||||
work = getFloats(rm.Cols, false)
|
||||
defer putFloats(work)
|
||||
}
|
||||
return lapack64.Lange(n, rm, work)
|
||||
case RawTriangular:
|
||||
rm := rma.RawTriangular()
|
||||
n := normLapack(norm, aTrans)
|
||||
if n == lapack.MaxRowSum || n == lapack.MaxColumnSum {
|
||||
work = getFloats(rm.N, false)
|
||||
defer putFloats(work)
|
||||
}
|
||||
return lapack64.Lantr(n, rm, work)
|
||||
case RawSymmetricer:
|
||||
rm := rma.RawSymmetric()
|
||||
n := normLapack(norm, aTrans)
|
||||
if n == lapack.MaxRowSum || n == lapack.MaxColumnSum {
|
||||
work = getFloats(rm.N, false)
|
||||
defer putFloats(work)
|
||||
}
|
||||
return lapack64.Lansy(n, rm, work)
|
||||
case *VecDense:
|
||||
rv := rma.RawVector()
|
||||
switch norm {
|
||||
default:
|
||||
panic("unreachable")
|
||||
case 1:
|
||||
if aTrans {
|
||||
imax := blas64.Iamax(rv)
|
||||
return math.Abs(rma.At(imax, 0))
|
||||
}
|
||||
return blas64.Asum(rv)
|
||||
case 2:
|
||||
return blas64.Nrm2(rv)
|
||||
case math.Inf(1):
|
||||
if aTrans {
|
||||
return blas64.Asum(rv)
|
||||
}
|
||||
imax := blas64.Iamax(rv)
|
||||
return math.Abs(rma.At(imax, 0))
|
||||
}
|
||||
}
|
||||
switch norm {
|
||||
default:
|
||||
panic("unreachable")
|
||||
case 1:
|
||||
var max float64
|
||||
for j := 0; j < c; j++ {
|
||||
var sum float64
|
||||
for i := 0; i < r; i++ {
|
||||
sum += math.Abs(a.At(i, j))
|
||||
}
|
||||
if sum > max {
|
||||
max = sum
|
||||
}
|
||||
}
|
||||
return max
|
||||
case 2:
|
||||
var sum float64
|
||||
for i := 0; i < r; i++ {
|
||||
for j := 0; j < c; j++ {
|
||||
v := a.At(i, j)
|
||||
sum += v * v
|
||||
}
|
||||
}
|
||||
return math.Sqrt(sum)
|
||||
case math.Inf(1):
|
||||
var max float64
|
||||
for i := 0; i < r; i++ {
|
||||
var sum float64
|
||||
for j := 0; j < c; j++ {
|
||||
sum += math.Abs(a.At(i, j))
|
||||
}
|
||||
if sum > max {
|
||||
max = sum
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
}
|
||||
|
||||
// normLapack converts the float64 norm input in Norm to a lapack.MatrixNorm.
|
||||
func normLapack(norm float64, aTrans bool) lapack.MatrixNorm {
|
||||
switch norm {
|
||||
case 1:
|
||||
n := lapack.MaxColumnSum
|
||||
if aTrans {
|
||||
n = lapack.MaxRowSum
|
||||
}
|
||||
return n
|
||||
case 2:
|
||||
return lapack.Frobenius
|
||||
case math.Inf(1):
|
||||
n := lapack.MaxRowSum
|
||||
if aTrans {
|
||||
n = lapack.MaxColumnSum
|
||||
}
|
||||
return n
|
||||
default:
|
||||
panic(ErrNormOrder)
|
||||
}
|
||||
}
|
||||
|
||||
// Sum returns the sum of the elements of the matrix.
|
||||
func Sum(a Matrix) float64 {
|
||||
// TODO(btracey): Add a fast path for the other supported matrix types.
|
||||
|
||||
r, c := a.Dims()
|
||||
var sum float64
|
||||
aU, _ := untranspose(a)
|
||||
if rma, ok := aU.(RawMatrixer); ok {
|
||||
rm := rma.RawMatrix()
|
||||
for i := 0; i < rm.Rows; i++ {
|
||||
for _, v := range rm.Data[i*rm.Stride : i*rm.Stride+rm.Cols] {
|
||||
sum += v
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
for i := 0; i < r; i++ {
|
||||
for j := 0; j < c; j++ {
|
||||
sum += a.At(i, j)
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
// A Tracer can compute the trace of the matrix. Trace must panic if the
|
||||
// matrix is not square.
|
||||
type Tracer interface {
|
||||
Trace() float64
|
||||
}
|
||||
|
||||
// Trace returns the trace of the matrix. Trace will panic if the
|
||||
// matrix is not square.
|
||||
func Trace(a Matrix) float64 {
|
||||
m, _ := untransposeExtract(a)
|
||||
if t, ok := m.(Tracer); ok {
|
||||
return t.Trace()
|
||||
}
|
||||
r, c := a.Dims()
|
||||
if r != c {
|
||||
panic(ErrSquare)
|
||||
}
|
||||
var v float64
|
||||
for i := 0; i < r; i++ {
|
||||
v += a.At(i, i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// use returns a float64 slice with l elements, using f if it
|
||||
// has the necessary capacity, otherwise creating a new slice.
|
||||
func use(f []float64, l int) []float64 {
|
||||
if l <= cap(f) {
|
||||
return f[:l]
|
||||
}
|
||||
return make([]float64, l)
|
||||
}
|
||||
|
||||
// useZeroed returns a float64 slice with l elements, using f if it
|
||||
// has the necessary capacity, otherwise creating a new slice. The
|
||||
// elements of the returned slice are guaranteed to be zero.
|
||||
func useZeroed(f []float64, l int) []float64 {
|
||||
if l <= cap(f) {
|
||||
f = f[:l]
|
||||
zero(f)
|
||||
return f
|
||||
}
|
||||
return make([]float64, l)
|
||||
}
|
||||
|
||||
// zero zeros the given slice's elements.
|
||||
func zero(f []float64) {
|
||||
for i := range f {
|
||||
f[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// useInt returns an int slice with l elements, using i if it
|
||||
// has the necessary capacity, otherwise creating a new slice.
|
||||
func useInt(i []int, l int) []int {
|
||||
if l <= cap(i) {
|
||||
return i[:l]
|
||||
}
|
||||
return make([]int, l)
|
||||
}
|
||||
20
vendor/gonum.org/v1/gonum/mat/offset.go
generated
vendored
Normal file
20
vendor/gonum.org/v1/gonum/mat/offset.go
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright ©2015 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine,!safe
|
||||
|
||||
package mat
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// offset returns the number of float64 values b[0] is after a[0].
|
||||
func offset(a, b []float64) int {
|
||||
if &a[0] == &b[0] {
|
||||
return 0
|
||||
}
|
||||
// This expression must be atomic with respect to GC moves.
|
||||
// At this stage this is true, because the GC does not
|
||||
// move. See https://golang.org/issue/12445.
|
||||
return int(uintptr(unsafe.Pointer(&b[0]))-uintptr(unsafe.Pointer(&a[0]))) / int(unsafe.Sizeof(float64(0)))
|
||||
}
|
||||
24
vendor/gonum.org/v1/gonum/mat/offset_appengine.go
generated
vendored
Normal file
24
vendor/gonum.org/v1/gonum/mat/offset_appengine.go
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright ©2015 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build appengine safe
|
||||
|
||||
package mat
|
||||
|
||||
import "reflect"
|
||||
|
||||
var sizeOfFloat64 = int(reflect.TypeOf(float64(0)).Size())
|
||||
|
||||
// offset returns the number of float64 values b[0] is after a[0].
|
||||
func offset(a, b []float64) int {
|
||||
va0 := reflect.ValueOf(a).Index(0)
|
||||
vb0 := reflect.ValueOf(b).Index(0)
|
||||
if va0.Addr() == vb0.Addr() {
|
||||
return 0
|
||||
}
|
||||
// This expression must be atomic with respect to GC moves.
|
||||
// At this stage this is true, because the GC does not
|
||||
// move. See https://golang.org/issue/12445.
|
||||
return int(vb0.UnsafeAddr()-va0.UnsafeAddr()) / sizeOfFloat64
|
||||
}
|
||||
236
vendor/gonum.org/v1/gonum/mat/pool.go
generated
vendored
Normal file
236
vendor/gonum.org/v1/gonum/mat/pool.go
generated
vendored
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
// Copyright ©2014 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
)
|
||||
|
||||
var tab64 = [64]byte{
|
||||
0x3f, 0x00, 0x3a, 0x01, 0x3b, 0x2f, 0x35, 0x02,
|
||||
0x3c, 0x27, 0x30, 0x1b, 0x36, 0x21, 0x2a, 0x03,
|
||||
0x3d, 0x33, 0x25, 0x28, 0x31, 0x12, 0x1c, 0x14,
|
||||
0x37, 0x1e, 0x22, 0x0b, 0x2b, 0x0e, 0x16, 0x04,
|
||||
0x3e, 0x39, 0x2e, 0x34, 0x26, 0x1a, 0x20, 0x29,
|
||||
0x32, 0x24, 0x11, 0x13, 0x1d, 0x0a, 0x0d, 0x15,
|
||||
0x38, 0x2d, 0x19, 0x1f, 0x23, 0x10, 0x09, 0x0c,
|
||||
0x2c, 0x18, 0x0f, 0x08, 0x17, 0x07, 0x06, 0x05,
|
||||
}
|
||||
|
||||
// bits returns the ceiling of base 2 log of v.
|
||||
// Approach based on http://stackoverflow.com/a/11398748.
|
||||
func bits(v uint64) byte {
|
||||
if v == 0 {
|
||||
return 0
|
||||
}
|
||||
v <<= 2
|
||||
v--
|
||||
v |= v >> 1
|
||||
v |= v >> 2
|
||||
v |= v >> 4
|
||||
v |= v >> 8
|
||||
v |= v >> 16
|
||||
v |= v >> 32
|
||||
return tab64[((v-(v>>1))*0x07EDD5E59A4E28C2)>>58] - 1
|
||||
}
|
||||
|
||||
var (
|
||||
// pool contains size stratified workspace Dense pools.
|
||||
// Each pool element i returns sized matrices with a data
|
||||
// slice capped at 1<<i.
|
||||
pool [63]sync.Pool
|
||||
|
||||
// poolSym is the SymDense equivalent of pool.
|
||||
poolSym [63]sync.Pool
|
||||
|
||||
// poolTri is the TriDense equivalent of pool.
|
||||
poolTri [63]sync.Pool
|
||||
|
||||
// poolVec is the VecDense equivalent of pool.
|
||||
poolVec [63]sync.Pool
|
||||
|
||||
// poolFloats is the []float64 equivalent of pool.
|
||||
poolFloats [63]sync.Pool
|
||||
|
||||
// poolInts is the []int equivalent of pool.
|
||||
poolInts [63]sync.Pool
|
||||
)
|
||||
|
||||
func init() {
|
||||
for i := range pool {
|
||||
l := 1 << uint(i)
|
||||
pool[i].New = func() interface{} {
|
||||
return &Dense{mat: blas64.General{
|
||||
Data: make([]float64, l),
|
||||
}}
|
||||
}
|
||||
poolSym[i].New = func() interface{} {
|
||||
return &SymDense{mat: blas64.Symmetric{
|
||||
Uplo: blas.Upper,
|
||||
Data: make([]float64, l),
|
||||
}}
|
||||
}
|
||||
poolTri[i].New = func() interface{} {
|
||||
return &TriDense{mat: blas64.Triangular{
|
||||
Data: make([]float64, l),
|
||||
}}
|
||||
}
|
||||
poolVec[i].New = func() interface{} {
|
||||
return &VecDense{mat: blas64.Vector{
|
||||
Inc: 1,
|
||||
Data: make([]float64, l),
|
||||
}}
|
||||
}
|
||||
poolFloats[i].New = func() interface{} {
|
||||
return make([]float64, l)
|
||||
}
|
||||
poolInts[i].New = func() interface{} {
|
||||
return make([]int, l)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getWorkspace returns a *Dense of size r×c and a data slice
|
||||
// with a cap that is less than 2*r*c. If clear is true, the
|
||||
// data slice visible through the Matrix interface is zeroed.
|
||||
func getWorkspace(r, c int, clear bool) *Dense {
|
||||
l := uint64(r * c)
|
||||
w := pool[bits(l)].Get().(*Dense)
|
||||
w.mat.Data = w.mat.Data[:l]
|
||||
if clear {
|
||||
zero(w.mat.Data)
|
||||
}
|
||||
w.mat.Rows = r
|
||||
w.mat.Cols = c
|
||||
w.mat.Stride = c
|
||||
w.capRows = r
|
||||
w.capCols = c
|
||||
return w
|
||||
}
|
||||
|
||||
// putWorkspace replaces a used *Dense into the appropriate size
|
||||
// workspace pool. putWorkspace must not be called with a matrix
|
||||
// where references to the underlying data slice have been kept.
|
||||
func putWorkspace(w *Dense) {
|
||||
pool[bits(uint64(cap(w.mat.Data)))].Put(w)
|
||||
}
|
||||
|
||||
// getWorkspaceSym returns a *SymDense of size n and a cap that
|
||||
// is less than 2*n. If clear is true, the data slice visible
|
||||
// through the Matrix interface is zeroed.
|
||||
func getWorkspaceSym(n int, clear bool) *SymDense {
|
||||
l := uint64(n)
|
||||
l *= l
|
||||
s := poolSym[bits(l)].Get().(*SymDense)
|
||||
s.mat.Data = s.mat.Data[:l]
|
||||
if clear {
|
||||
zero(s.mat.Data)
|
||||
}
|
||||
s.mat.N = n
|
||||
s.mat.Stride = n
|
||||
s.cap = n
|
||||
return s
|
||||
}
|
||||
|
||||
// putWorkspaceSym replaces a used *SymDense into the appropriate size
|
||||
// workspace pool. putWorkspaceSym must not be called with a matrix
|
||||
// where references to the underlying data slice have been kept.
|
||||
func putWorkspaceSym(s *SymDense) {
|
||||
poolSym[bits(uint64(cap(s.mat.Data)))].Put(s)
|
||||
}
|
||||
|
||||
// getWorkspaceTri returns a *TriDense of size n and a cap that
|
||||
// is less than 2*n. If clear is true, the data slice visible
|
||||
// through the Matrix interface is zeroed.
|
||||
func getWorkspaceTri(n int, kind TriKind, clear bool) *TriDense {
|
||||
l := uint64(n)
|
||||
l *= l
|
||||
t := poolTri[bits(l)].Get().(*TriDense)
|
||||
t.mat.Data = t.mat.Data[:l]
|
||||
if clear {
|
||||
zero(t.mat.Data)
|
||||
}
|
||||
t.mat.N = n
|
||||
t.mat.Stride = n
|
||||
if kind == Upper {
|
||||
t.mat.Uplo = blas.Upper
|
||||
} else if kind == Lower {
|
||||
t.mat.Uplo = blas.Lower
|
||||
} else {
|
||||
panic(ErrTriangle)
|
||||
}
|
||||
t.mat.Diag = blas.NonUnit
|
||||
t.cap = n
|
||||
return t
|
||||
}
|
||||
|
||||
// putWorkspaceTri replaces a used *TriDense into the appropriate size
|
||||
// workspace pool. putWorkspaceTri must not be called with a matrix
|
||||
// where references to the underlying data slice have been kept.
|
||||
func putWorkspaceTri(t *TriDense) {
|
||||
poolTri[bits(uint64(cap(t.mat.Data)))].Put(t)
|
||||
}
|
||||
|
||||
// getWorkspaceVec returns a *VecDense of length n and a cap that
|
||||
// is less than 2*n. If clear is true, the data slice visible
|
||||
// through the Matrix interface is zeroed.
|
||||
func getWorkspaceVec(n int, clear bool) *VecDense {
|
||||
l := uint64(n)
|
||||
v := poolVec[bits(l)].Get().(*VecDense)
|
||||
v.mat.Data = v.mat.Data[:l]
|
||||
if clear {
|
||||
zero(v.mat.Data)
|
||||
}
|
||||
v.mat.N = n
|
||||
return v
|
||||
}
|
||||
|
||||
// putWorkspaceVec replaces a used *VecDense into the appropriate size
|
||||
// workspace pool. putWorkspaceVec must not be called with a matrix
|
||||
// where references to the underlying data slice have been kept.
|
||||
func putWorkspaceVec(v *VecDense) {
|
||||
poolVec[bits(uint64(cap(v.mat.Data)))].Put(v)
|
||||
}
|
||||
|
||||
// getFloats returns a []float64 of length l and a cap that is
|
||||
// less than 2*l. If clear is true, the slice visible is zeroed.
|
||||
func getFloats(l int, clear bool) []float64 {
|
||||
w := poolFloats[bits(uint64(l))].Get().([]float64)
|
||||
w = w[:l]
|
||||
if clear {
|
||||
zero(w)
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// putFloats replaces a used []float64 into the appropriate size
|
||||
// workspace pool. putFloats must not be called with a slice
|
||||
// where references to the underlying data have been kept.
|
||||
func putFloats(w []float64) {
|
||||
poolFloats[bits(uint64(cap(w)))].Put(w)
|
||||
}
|
||||
|
||||
// getInts returns a []ints of length l and a cap that is
|
||||
// less than 2*l. If clear is true, the slice visible is zeroed.
|
||||
func getInts(l int, clear bool) []int {
|
||||
w := poolInts[bits(uint64(l))].Get().([]int)
|
||||
w = w[:l]
|
||||
if clear {
|
||||
for i := range w {
|
||||
w[i] = 0
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// putInts replaces a used []int into the appropriate size
|
||||
// workspace pool. putInts must not be called with a slice
|
||||
// where references to the underlying data have been kept.
|
||||
func putInts(w []int) {
|
||||
poolInts[bits(uint64(cap(w)))].Put(w)
|
||||
}
|
||||
193
vendor/gonum.org/v1/gonum/mat/product.go
generated
vendored
Normal file
193
vendor/gonum.org/v1/gonum/mat/product.go
generated
vendored
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
// Copyright ©2015 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Product calculates the product of the given factors and places the result in
|
||||
// the receiver. The order of multiplication operations is optimized to minimize
|
||||
// the number of floating point operations on the basis that all matrix
|
||||
// multiplications are general.
|
||||
func (m *Dense) Product(factors ...Matrix) {
|
||||
// The operation order optimisation is the naive O(n^3) dynamic
|
||||
// programming approach and does not take into consideration
|
||||
// finer-grained optimisations that might be available.
|
||||
//
|
||||
// TODO(kortschak) Consider using the O(nlogn) or O(mlogn)
|
||||
// algorithms that are available. e.g.
|
||||
//
|
||||
// e.g. http://www.jofcis.com/publishedpapers/2014_10_10_4299_4306.pdf
|
||||
//
|
||||
// In the case that this is replaced, retain this code in
|
||||
// tests to compare against.
|
||||
|
||||
r, c := m.Dims()
|
||||
switch len(factors) {
|
||||
case 0:
|
||||
if r != 0 || c != 0 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
return
|
||||
case 1:
|
||||
m.reuseAs(factors[0].Dims())
|
||||
m.Copy(factors[0])
|
||||
return
|
||||
case 2:
|
||||
// Don't do work that we know the answer to.
|
||||
m.Mul(factors[0], factors[1])
|
||||
return
|
||||
}
|
||||
|
||||
p := newMultiplier(m, factors)
|
||||
p.optimize()
|
||||
result := p.multiply()
|
||||
m.reuseAs(result.Dims())
|
||||
m.Copy(result)
|
||||
putWorkspace(result)
|
||||
}
|
||||
|
||||
// debugProductWalk enables debugging output for Product.
|
||||
const debugProductWalk = false
|
||||
|
||||
// multiplier performs operation order optimisation and tree traversal.
|
||||
type multiplier struct {
|
||||
// factors is the ordered set of
|
||||
// factors to multiply.
|
||||
factors []Matrix
|
||||
// dims is the chain of factor
|
||||
// dimensions.
|
||||
dims []int
|
||||
|
||||
// table contains the dynamic
|
||||
// programming costs and subchain
|
||||
// division indices.
|
||||
table table
|
||||
}
|
||||
|
||||
func newMultiplier(m *Dense, factors []Matrix) *multiplier {
|
||||
// Check size early, but don't yet
|
||||
// allocate data for m.
|
||||
r, c := m.Dims()
|
||||
fr, fc := factors[0].Dims() // newMultiplier is only called with len(factors) > 2.
|
||||
if !m.IsZero() {
|
||||
if fr != r {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if _, lc := factors[len(factors)-1].Dims(); lc != c {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
|
||||
dims := make([]int, len(factors)+1)
|
||||
dims[0] = r
|
||||
dims[len(dims)-1] = c
|
||||
pc := fc
|
||||
for i, f := range factors[1:] {
|
||||
cr, cc := f.Dims()
|
||||
dims[i+1] = cr
|
||||
if pc != cr {
|
||||
panic(ErrShape)
|
||||
}
|
||||
pc = cc
|
||||
}
|
||||
|
||||
return &multiplier{
|
||||
factors: factors,
|
||||
dims: dims,
|
||||
table: newTable(len(factors)),
|
||||
}
|
||||
}
|
||||
|
||||
// optimize determines an optimal matrix multiply operation order.
|
||||
func (p *multiplier) optimize() {
|
||||
if debugProductWalk {
|
||||
fmt.Printf("chain dims: %v\n", p.dims)
|
||||
}
|
||||
const maxInt = int(^uint(0) >> 1)
|
||||
for f := 1; f < len(p.factors); f++ {
|
||||
for i := 0; i < len(p.factors)-f; i++ {
|
||||
j := i + f
|
||||
p.table.set(i, j, entry{cost: maxInt})
|
||||
for k := i; k < j; k++ {
|
||||
cost := p.table.at(i, k).cost + p.table.at(k+1, j).cost + p.dims[i]*p.dims[k+1]*p.dims[j+1]
|
||||
if cost < p.table.at(i, j).cost {
|
||||
p.table.set(i, j, entry{cost: cost, k: k})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// multiply walks the optimal operation tree found by optimize,
|
||||
// leaving the final result in the stack. It returns the
|
||||
// product, which may be copied but should be returned to
|
||||
// the workspace pool.
|
||||
func (p *multiplier) multiply() *Dense {
|
||||
result, _ := p.multiplySubchain(0, len(p.factors)-1)
|
||||
if debugProductWalk {
|
||||
r, c := result.Dims()
|
||||
fmt.Printf("\tpop result (%d×%d) cost=%d\n", r, c, p.table.at(0, len(p.factors)-1).cost)
|
||||
}
|
||||
return result.(*Dense)
|
||||
}
|
||||
|
||||
func (p *multiplier) multiplySubchain(i, j int) (m Matrix, intermediate bool) {
|
||||
if i == j {
|
||||
return p.factors[i], false
|
||||
}
|
||||
|
||||
a, aTmp := p.multiplySubchain(i, p.table.at(i, j).k)
|
||||
b, bTmp := p.multiplySubchain(p.table.at(i, j).k+1, j)
|
||||
|
||||
ar, ac := a.Dims()
|
||||
br, bc := b.Dims()
|
||||
if ac != br {
|
||||
// Panic with a string since this
|
||||
// is not a user-facing panic.
|
||||
panic(ErrShape.Error())
|
||||
}
|
||||
|
||||
if debugProductWalk {
|
||||
fmt.Printf("\tpush f[%d] (%d×%d)%s * f[%d] (%d×%d)%s\n",
|
||||
i, ar, ac, result(aTmp), j, br, bc, result(bTmp))
|
||||
}
|
||||
|
||||
r := getWorkspace(ar, bc, false)
|
||||
r.Mul(a, b)
|
||||
if aTmp {
|
||||
putWorkspace(a.(*Dense))
|
||||
}
|
||||
if bTmp {
|
||||
putWorkspace(b.(*Dense))
|
||||
}
|
||||
return r, true
|
||||
}
|
||||
|
||||
type entry struct {
|
||||
k int // is the chain subdivision index.
|
||||
cost int // cost is the cost of the operation.
|
||||
}
|
||||
|
||||
// table is a row major n×n dynamic programming table.
|
||||
type table struct {
|
||||
n int
|
||||
entries []entry
|
||||
}
|
||||
|
||||
func newTable(n int) table {
|
||||
return table{n: n, entries: make([]entry, n*n)}
|
||||
}
|
||||
|
||||
func (t table) at(i, j int) entry { return t.entries[i*t.n+j] }
|
||||
func (t table) set(i, j int, e entry) { t.entries[i*t.n+j] = e }
|
||||
|
||||
type result bool
|
||||
|
||||
func (r result) String() string {
|
||||
if r {
|
||||
return " (popped result)"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
260
vendor/gonum.org/v1/gonum/mat/qr.go
generated
vendored
Normal file
260
vendor/gonum.org/v1/gonum/mat/qr.go
generated
vendored
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
// Copyright ©2013 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
"gonum.org/v1/gonum/lapack"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
const badQR = "mat: invalid QR factorization"
|
||||
|
||||
// QR is a type for creating and using the QR factorization of a matrix.
|
||||
type QR struct {
|
||||
qr *Dense
|
||||
tau []float64
|
||||
cond float64
|
||||
}
|
||||
|
||||
func (qr *QR) updateCond(norm lapack.MatrixNorm) {
|
||||
// Since A = Q*R, and Q is orthogonal, we get for the condition number κ
|
||||
// κ(A) := |A| |A^-1| = |Q*R| |(Q*R)^-1| = |R| |R^-1 * Q^T|
|
||||
// = |R| |R^-1| = κ(R),
|
||||
// where we used that fact that Q^-1 = Q^T. However, this assumes that
|
||||
// the matrix norm is invariant under orthogonal transformations which
|
||||
// is not the case for CondNorm. Hopefully the error is negligible: κ
|
||||
// is only a qualitative measure anyway.
|
||||
n := qr.qr.mat.Cols
|
||||
work := getFloats(3*n, false)
|
||||
iwork := getInts(n, false)
|
||||
r := qr.qr.asTriDense(n, blas.NonUnit, blas.Upper)
|
||||
v := lapack64.Trcon(norm, r.mat, work, iwork)
|
||||
putFloats(work)
|
||||
putInts(iwork)
|
||||
qr.cond = 1 / v
|
||||
}
|
||||
|
||||
// Factorize computes the QR factorization of an m×n matrix a where m >= n. The QR
|
||||
// factorization always exists even if A is singular.
|
||||
//
|
||||
// The QR decomposition is a factorization of the matrix A such that A = Q * R.
|
||||
// The matrix Q is an orthonormal m×m matrix, and R is an m×n upper triangular matrix.
|
||||
// Q and R can be extracted using the QTo and RTo methods.
|
||||
func (qr *QR) Factorize(a Matrix) {
|
||||
qr.factorize(a, CondNorm)
|
||||
}
|
||||
|
||||
func (qr *QR) factorize(a Matrix, norm lapack.MatrixNorm) {
|
||||
m, n := a.Dims()
|
||||
if m < n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
k := min(m, n)
|
||||
if qr.qr == nil {
|
||||
qr.qr = &Dense{}
|
||||
}
|
||||
qr.qr.Clone(a)
|
||||
work := []float64{0}
|
||||
qr.tau = make([]float64, k)
|
||||
lapack64.Geqrf(qr.qr.mat, qr.tau, work, -1)
|
||||
|
||||
work = getFloats(int(work[0]), false)
|
||||
lapack64.Geqrf(qr.qr.mat, qr.tau, work, len(work))
|
||||
putFloats(work)
|
||||
qr.updateCond(norm)
|
||||
}
|
||||
|
||||
// isValid returns whether the receiver contains a factorization.
|
||||
func (qr *QR) isValid() bool {
|
||||
return qr.qr != nil && !qr.qr.IsZero()
|
||||
}
|
||||
|
||||
// Cond returns the condition number for the factorized matrix.
|
||||
// Cond will panic if the receiver does not contain a factorization.
|
||||
func (qr *QR) Cond() float64 {
|
||||
if !qr.isValid() {
|
||||
panic(badQR)
|
||||
}
|
||||
return qr.cond
|
||||
}
|
||||
|
||||
// TODO(btracey): Add in the "Reduced" forms for extracting the n×n orthogonal
|
||||
// and upper triangular matrices.
|
||||
|
||||
// RTo extracts the m×n upper trapezoidal matrix from a QR decomposition.
|
||||
// If dst is nil, a new matrix is allocated. The resulting dst matrix is returned.
|
||||
// RTo will panic if the receiver does not contain a factorization.
|
||||
func (qr *QR) RTo(dst *Dense) *Dense {
|
||||
if !qr.isValid() {
|
||||
panic(badQR)
|
||||
}
|
||||
|
||||
r, c := qr.qr.Dims()
|
||||
if dst == nil {
|
||||
dst = NewDense(r, c, nil)
|
||||
} else {
|
||||
dst.reuseAs(r, c)
|
||||
}
|
||||
|
||||
// Disguise the QR as an upper triangular
|
||||
t := &TriDense{
|
||||
mat: blas64.Triangular{
|
||||
N: c,
|
||||
Stride: qr.qr.mat.Stride,
|
||||
Data: qr.qr.mat.Data,
|
||||
Uplo: blas.Upper,
|
||||
Diag: blas.NonUnit,
|
||||
},
|
||||
cap: qr.qr.capCols,
|
||||
}
|
||||
dst.Copy(t)
|
||||
|
||||
// Zero below the triangular.
|
||||
for i := r; i < c; i++ {
|
||||
zero(dst.mat.Data[i*dst.mat.Stride : i*dst.mat.Stride+c])
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// QTo extracts the m×m orthonormal matrix Q from a QR decomposition.
|
||||
// If dst is nil, a new matrix is allocated. The resulting Q matrix is returned.
|
||||
// QTo will panic if the receiver does not contain a factorization.
|
||||
func (qr *QR) QTo(dst *Dense) *Dense {
|
||||
if !qr.isValid() {
|
||||
panic(badQR)
|
||||
}
|
||||
|
||||
r, _ := qr.qr.Dims()
|
||||
if dst == nil {
|
||||
dst = NewDense(r, r, nil)
|
||||
} else {
|
||||
dst.reuseAsZeroed(r, r)
|
||||
}
|
||||
|
||||
// Set Q = I.
|
||||
for i := 0; i < r*r; i += r + 1 {
|
||||
dst.mat.Data[i] = 1
|
||||
}
|
||||
|
||||
// Construct Q from the elementary reflectors.
|
||||
work := []float64{0}
|
||||
lapack64.Ormqr(blas.Left, blas.NoTrans, qr.qr.mat, qr.tau, dst.mat, work, -1)
|
||||
work = getFloats(int(work[0]), false)
|
||||
lapack64.Ormqr(blas.Left, blas.NoTrans, qr.qr.mat, qr.tau, dst.mat, work, len(work))
|
||||
putFloats(work)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// SolveTo finds a minimum-norm solution to a system of linear equations defined
|
||||
// by the matrices A and b, where A is an m×n matrix represented in its QR factorized
|
||||
// form. If A is singular or near-singular a Condition error is returned.
|
||||
// See the documentation for Condition for more information.
|
||||
//
|
||||
// The minimization problem solved depends on the input parameters.
|
||||
// If trans == false, find X such that ||A*X - B||_2 is minimized.
|
||||
// If trans == true, find the minimum norm solution of A^T * X = B.
|
||||
// The solution matrix, X, is stored in place into dst.
|
||||
// SolveTo will panic if the receiver does not contain a factorization.
|
||||
func (qr *QR) SolveTo(dst *Dense, trans bool, b Matrix) error {
|
||||
if !qr.isValid() {
|
||||
panic(badQR)
|
||||
}
|
||||
|
||||
r, c := qr.qr.Dims()
|
||||
br, bc := b.Dims()
|
||||
|
||||
// The QR solve algorithm stores the result in-place into the right hand side.
|
||||
// The storage for the answer must be large enough to hold both b and x.
|
||||
// However, this method's receiver must be the size of x. Copy b, and then
|
||||
// copy the result into m at the end.
|
||||
if trans {
|
||||
if c != br {
|
||||
panic(ErrShape)
|
||||
}
|
||||
dst.reuseAs(r, bc)
|
||||
} else {
|
||||
if r != br {
|
||||
panic(ErrShape)
|
||||
}
|
||||
dst.reuseAs(c, bc)
|
||||
}
|
||||
// Do not need to worry about overlap between m and b because x has its own
|
||||
// independent storage.
|
||||
w := getWorkspace(max(r, c), bc, false)
|
||||
w.Copy(b)
|
||||
t := qr.qr.asTriDense(qr.qr.mat.Cols, blas.NonUnit, blas.Upper).mat
|
||||
if trans {
|
||||
ok := lapack64.Trtrs(blas.Trans, t, w.mat)
|
||||
if !ok {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
for i := c; i < r; i++ {
|
||||
zero(w.mat.Data[i*w.mat.Stride : i*w.mat.Stride+bc])
|
||||
}
|
||||
work := []float64{0}
|
||||
lapack64.Ormqr(blas.Left, blas.NoTrans, qr.qr.mat, qr.tau, w.mat, work, -1)
|
||||
work = getFloats(int(work[0]), false)
|
||||
lapack64.Ormqr(blas.Left, blas.NoTrans, qr.qr.mat, qr.tau, w.mat, work, len(work))
|
||||
putFloats(work)
|
||||
} else {
|
||||
work := []float64{0}
|
||||
lapack64.Ormqr(blas.Left, blas.Trans, qr.qr.mat, qr.tau, w.mat, work, -1)
|
||||
work = getFloats(int(work[0]), false)
|
||||
lapack64.Ormqr(blas.Left, blas.Trans, qr.qr.mat, qr.tau, w.mat, work, len(work))
|
||||
putFloats(work)
|
||||
|
||||
ok := lapack64.Trtrs(blas.NoTrans, t, w.mat)
|
||||
if !ok {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
}
|
||||
// X was set above to be the correct size for the result.
|
||||
dst.Copy(w)
|
||||
putWorkspace(w)
|
||||
if qr.cond > ConditionTolerance {
|
||||
return Condition(qr.cond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SolveVecTo finds a minimum-norm solution to a system of linear equations,
|
||||
// Ax = b.
|
||||
// See QR.SolveTo for the full documentation.
|
||||
// SolveVecTo will panic if the receiver does not contain a factorization.
|
||||
func (qr *QR) SolveVecTo(dst *VecDense, trans bool, b Vector) error {
|
||||
if !qr.isValid() {
|
||||
panic(badQR)
|
||||
}
|
||||
|
||||
r, c := qr.qr.Dims()
|
||||
if _, bc := b.Dims(); bc != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
// The Solve implementation is non-trivial, so rather than duplicate the code,
|
||||
// instead recast the VecDenses as Dense and call the matrix code.
|
||||
bm := Matrix(b)
|
||||
if rv, ok := b.(RawVectorer); ok {
|
||||
bmat := rv.RawVector()
|
||||
if dst != b {
|
||||
dst.checkOverlap(bmat)
|
||||
}
|
||||
b := VecDense{mat: bmat}
|
||||
bm = b.asDense()
|
||||
}
|
||||
if trans {
|
||||
dst.reuseAs(r)
|
||||
} else {
|
||||
dst.reuseAs(c)
|
||||
}
|
||||
return qr.SolveTo(dst.asDense(), trans, bm)
|
||||
|
||||
}
|
||||
226
vendor/gonum.org/v1/gonum/mat/shadow.go
generated
vendored
Normal file
226
vendor/gonum.org/v1/gonum/mat/shadow.go
generated
vendored
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
// Copyright ©2015 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
)
|
||||
|
||||
const (
|
||||
// regionOverlap is the panic string used for the general case
|
||||
// of a matrix region overlap between a source and destination.
|
||||
regionOverlap = "mat: bad region: overlap"
|
||||
|
||||
// regionIdentity is the panic string used for the specific
|
||||
// case of complete agreement between a source and a destination.
|
||||
regionIdentity = "mat: bad region: identical"
|
||||
|
||||
// mismatchedStrides is the panic string used for overlapping
|
||||
// data slices with differing strides.
|
||||
mismatchedStrides = "mat: bad region: different strides"
|
||||
)
|
||||
|
||||
// checkOverlap returns false if the receiver does not overlap data elements
|
||||
// referenced by the parameter and panics otherwise.
|
||||
//
|
||||
// checkOverlap methods return a boolean to allow the check call to be added to a
|
||||
// boolean expression, making use of short-circuit operators.
|
||||
func checkOverlap(a, b blas64.General) bool {
|
||||
if cap(a.Data) == 0 || cap(b.Data) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
off := offset(a.Data[:1], b.Data[:1])
|
||||
|
||||
if off == 0 {
|
||||
// At least one element overlaps.
|
||||
if a.Cols == b.Cols && a.Rows == b.Rows && a.Stride == b.Stride {
|
||||
panic(regionIdentity)
|
||||
}
|
||||
panic(regionOverlap)
|
||||
}
|
||||
|
||||
if off > 0 && len(a.Data) <= off {
|
||||
// We know a is completely before b.
|
||||
return false
|
||||
}
|
||||
if off < 0 && len(b.Data) <= -off {
|
||||
// We know a is completely after b.
|
||||
return false
|
||||
}
|
||||
|
||||
if a.Stride != b.Stride {
|
||||
// Too hard, so assume the worst.
|
||||
panic(mismatchedStrides)
|
||||
}
|
||||
|
||||
if off < 0 {
|
||||
off = -off
|
||||
a.Cols, b.Cols = b.Cols, a.Cols
|
||||
}
|
||||
if rectanglesOverlap(off, a.Cols, b.Cols, a.Stride) {
|
||||
panic(regionOverlap)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Dense) checkOverlap(a blas64.General) bool {
|
||||
return checkOverlap(m.RawMatrix(), a)
|
||||
}
|
||||
|
||||
func (m *Dense) checkOverlapMatrix(a Matrix) bool {
|
||||
if m == a {
|
||||
return false
|
||||
}
|
||||
var amat blas64.General
|
||||
switch a := a.(type) {
|
||||
default:
|
||||
return false
|
||||
case RawMatrixer:
|
||||
amat = a.RawMatrix()
|
||||
case RawSymmetricer:
|
||||
amat = generalFromSymmetric(a.RawSymmetric())
|
||||
case RawTriangular:
|
||||
amat = generalFromTriangular(a.RawTriangular())
|
||||
}
|
||||
return m.checkOverlap(amat)
|
||||
}
|
||||
|
||||
func (s *SymDense) checkOverlap(a blas64.General) bool {
|
||||
return checkOverlap(generalFromSymmetric(s.RawSymmetric()), a)
|
||||
}
|
||||
|
||||
func (s *SymDense) checkOverlapMatrix(a Matrix) bool {
|
||||
if s == a {
|
||||
return false
|
||||
}
|
||||
var amat blas64.General
|
||||
switch a := a.(type) {
|
||||
default:
|
||||
return false
|
||||
case RawMatrixer:
|
||||
amat = a.RawMatrix()
|
||||
case RawSymmetricer:
|
||||
amat = generalFromSymmetric(a.RawSymmetric())
|
||||
case RawTriangular:
|
||||
amat = generalFromTriangular(a.RawTriangular())
|
||||
}
|
||||
return s.checkOverlap(amat)
|
||||
}
|
||||
|
||||
// generalFromSymmetric returns a blas64.General with the backing
|
||||
// data and dimensions of a.
|
||||
func generalFromSymmetric(a blas64.Symmetric) blas64.General {
|
||||
return blas64.General{
|
||||
Rows: a.N,
|
||||
Cols: a.N,
|
||||
Stride: a.Stride,
|
||||
Data: a.Data,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TriDense) checkOverlap(a blas64.General) bool {
|
||||
return checkOverlap(generalFromTriangular(t.RawTriangular()), a)
|
||||
}
|
||||
|
||||
func (t *TriDense) checkOverlapMatrix(a Matrix) bool {
|
||||
if t == a {
|
||||
return false
|
||||
}
|
||||
var amat blas64.General
|
||||
switch a := a.(type) {
|
||||
default:
|
||||
return false
|
||||
case RawMatrixer:
|
||||
amat = a.RawMatrix()
|
||||
case RawSymmetricer:
|
||||
amat = generalFromSymmetric(a.RawSymmetric())
|
||||
case RawTriangular:
|
||||
amat = generalFromTriangular(a.RawTriangular())
|
||||
}
|
||||
return t.checkOverlap(amat)
|
||||
}
|
||||
|
||||
// generalFromTriangular returns a blas64.General with the backing
|
||||
// data and dimensions of a.
|
||||
func generalFromTriangular(a blas64.Triangular) blas64.General {
|
||||
return blas64.General{
|
||||
Rows: a.N,
|
||||
Cols: a.N,
|
||||
Stride: a.Stride,
|
||||
Data: a.Data,
|
||||
}
|
||||
}
|
||||
|
||||
func (v *VecDense) checkOverlap(a blas64.Vector) bool {
|
||||
mat := v.mat
|
||||
if cap(mat.Data) == 0 || cap(a.Data) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
off := offset(mat.Data[:1], a.Data[:1])
|
||||
|
||||
if off == 0 {
|
||||
// At least one element overlaps.
|
||||
if mat.Inc == a.Inc && len(mat.Data) == len(a.Data) {
|
||||
panic(regionIdentity)
|
||||
}
|
||||
panic(regionOverlap)
|
||||
}
|
||||
|
||||
if off > 0 && len(mat.Data) <= off {
|
||||
// We know v is completely before a.
|
||||
return false
|
||||
}
|
||||
if off < 0 && len(a.Data) <= -off {
|
||||
// We know v is completely after a.
|
||||
return false
|
||||
}
|
||||
|
||||
if mat.Inc != a.Inc {
|
||||
// Too hard, so assume the worst.
|
||||
panic(mismatchedStrides)
|
||||
}
|
||||
|
||||
if mat.Inc == 1 || off&mat.Inc == 0 {
|
||||
panic(regionOverlap)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// rectanglesOverlap returns whether the strided rectangles a and b overlap
|
||||
// when b is offset by off elements after a but has at least one element before
|
||||
// the end of a. off must be positive. a and b have aCols and bCols respectively.
|
||||
//
|
||||
// rectanglesOverlap works by shifting both matrices left such that the left
|
||||
// column of a is at 0. The column indexes are flattened by obtaining the shifted
|
||||
// relative left and right column positions modulo the common stride. This allows
|
||||
// direct comparison of the column offsets when the matrix backing data slices
|
||||
// are known to overlap.
|
||||
func rectanglesOverlap(off, aCols, bCols, stride int) bool {
|
||||
if stride == 1 {
|
||||
// Unit stride means overlapping data
|
||||
// slices must overlap as matrices.
|
||||
return true
|
||||
}
|
||||
|
||||
// Flatten the shifted matrix column positions
|
||||
// so a starts at 0, modulo the common stride.
|
||||
aTo := aCols
|
||||
// The mod stride operations here make the from
|
||||
// and to indexes comparable between a and b when
|
||||
// the data slices of a and b overlap.
|
||||
bFrom := off % stride
|
||||
bTo := (bFrom + bCols) % stride
|
||||
|
||||
if bTo == 0 || bFrom < bTo {
|
||||
// b matrix is not wrapped: compare for
|
||||
// simple overlap.
|
||||
return bFrom < aTo
|
||||
}
|
||||
|
||||
// b strictly wraps and so must overlap with a.
|
||||
return true
|
||||
}
|
||||
140
vendor/gonum.org/v1/gonum/mat/solve.go
generated
vendored
Normal file
140
vendor/gonum.org/v1/gonum/mat/solve.go
generated
vendored
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
// Copyright ©2015 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
// Solve finds a minimum-norm solution to a system of linear equations defined
|
||||
// by the matrices A and B. If A is singular or near-singular, a Condition error
|
||||
// is returned. See the documentation for Condition for more information.
|
||||
//
|
||||
// The minimization problem solved depends on the input parameters:
|
||||
// - if m >= n, find X such that ||A*X - B||_2 is minimized,
|
||||
// - if m < n, find the minimum norm solution of A * X = B.
|
||||
// The solution matrix, X, is stored in-place into the receiver.
|
||||
func (m *Dense) Solve(a, b Matrix) error {
|
||||
ar, ac := a.Dims()
|
||||
br, bc := b.Dims()
|
||||
if ar != br {
|
||||
panic(ErrShape)
|
||||
}
|
||||
m.reuseAs(ac, bc)
|
||||
|
||||
// TODO(btracey): Add special cases for SymDense, etc.
|
||||
aU, aTrans := untranspose(a)
|
||||
bU, bTrans := untranspose(b)
|
||||
switch rma := aU.(type) {
|
||||
case RawTriangular:
|
||||
side := blas.Left
|
||||
tA := blas.NoTrans
|
||||
if aTrans {
|
||||
tA = blas.Trans
|
||||
}
|
||||
|
||||
switch rm := bU.(type) {
|
||||
case RawMatrixer:
|
||||
if m != bU || bTrans {
|
||||
if m == bU || m.checkOverlap(rm.RawMatrix()) {
|
||||
tmp := getWorkspace(br, bc, false)
|
||||
tmp.Copy(b)
|
||||
m.Copy(tmp)
|
||||
putWorkspace(tmp)
|
||||
break
|
||||
}
|
||||
m.Copy(b)
|
||||
}
|
||||
default:
|
||||
if m != bU {
|
||||
m.Copy(b)
|
||||
} else if bTrans {
|
||||
// m and b share data so Copy cannot be used directly.
|
||||
tmp := getWorkspace(br, bc, false)
|
||||
tmp.Copy(b)
|
||||
m.Copy(tmp)
|
||||
putWorkspace(tmp)
|
||||
}
|
||||
}
|
||||
|
||||
rm := rma.RawTriangular()
|
||||
blas64.Trsm(side, tA, 1, rm, m.mat)
|
||||
work := getFloats(3*rm.N, false)
|
||||
iwork := getInts(rm.N, false)
|
||||
cond := lapack64.Trcon(CondNorm, rm, work, iwork)
|
||||
putFloats(work)
|
||||
putInts(iwork)
|
||||
if cond > ConditionTolerance {
|
||||
return Condition(cond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch {
|
||||
case ar == ac:
|
||||
if a == b {
|
||||
// x = I.
|
||||
if ar == 1 {
|
||||
m.mat.Data[0] = 1
|
||||
return nil
|
||||
}
|
||||
for i := 0; i < ar; i++ {
|
||||
v := m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+ac]
|
||||
zero(v)
|
||||
v[i] = 1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
var lu LU
|
||||
lu.Factorize(a)
|
||||
return lu.SolveTo(m, false, b)
|
||||
case ar > ac:
|
||||
var qr QR
|
||||
qr.Factorize(a)
|
||||
return qr.SolveTo(m, false, b)
|
||||
default:
|
||||
var lq LQ
|
||||
lq.Factorize(a)
|
||||
return lq.SolveTo(m, false, b)
|
||||
}
|
||||
}
|
||||
|
||||
// SolveVec finds a minimum-norm solution to a system of linear equations defined
|
||||
// by the matrix a and the right-hand side column vector b. If A is singular or
|
||||
// near-singular, a Condition error is returned. See the documentation for
|
||||
// Dense.Solve for more information.
|
||||
func (v *VecDense) SolveVec(a Matrix, b Vector) error {
|
||||
if _, bc := b.Dims(); bc != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
_, c := a.Dims()
|
||||
|
||||
// The Solve implementation is non-trivial, so rather than duplicate the code,
|
||||
// instead recast the VecDenses as Dense and call the matrix code.
|
||||
|
||||
if rv, ok := b.(RawVectorer); ok {
|
||||
bmat := rv.RawVector()
|
||||
if v != b {
|
||||
v.checkOverlap(bmat)
|
||||
}
|
||||
v.reuseAs(c)
|
||||
m := v.asDense()
|
||||
// We conditionally create bm as m when b and v are identical
|
||||
// to prevent the overlap detection code from identifying m
|
||||
// and bm as overlapping but not identical.
|
||||
bm := m
|
||||
if v != b {
|
||||
b := VecDense{mat: bmat}
|
||||
bm = b.asDense()
|
||||
}
|
||||
return m.Solve(a, bm)
|
||||
}
|
||||
|
||||
v.reuseAs(c)
|
||||
m := v.asDense()
|
||||
return m.Solve(a, b)
|
||||
}
|
||||
247
vendor/gonum.org/v1/gonum/mat/svd.go
generated
vendored
Normal file
247
vendor/gonum.org/v1/gonum/mat/svd.go
generated
vendored
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
// Copyright ©2013 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
"gonum.org/v1/gonum/lapack"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
// SVD is a type for creating and using the Singular Value Decomposition (SVD)
|
||||
// of a matrix.
|
||||
type SVD struct {
|
||||
kind SVDKind
|
||||
|
||||
s []float64
|
||||
u blas64.General
|
||||
vt blas64.General
|
||||
}
|
||||
|
||||
// SVDKind specifies the treatment of singular vectors during an SVD
|
||||
// factorization.
|
||||
type SVDKind int
|
||||
|
||||
const (
|
||||
// SVDNone specifies that no singular vectors should be computed during
|
||||
// the decomposition.
|
||||
SVDNone SVDKind = 0
|
||||
|
||||
// SVDThinU specifies the thin decomposition for U should be computed.
|
||||
SVDThinU SVDKind = 1 << (iota - 1)
|
||||
// SVDFullU specifies the full decomposition for U should be computed.
|
||||
SVDFullU
|
||||
// SVDThinV specifies the thin decomposition for V should be computed.
|
||||
SVDThinV
|
||||
// SVDFullV specifies the full decomposition for V should be computed.
|
||||
SVDFullV
|
||||
|
||||
// SVDThin is a convenience value for computing both thin vectors.
|
||||
SVDThin SVDKind = SVDThinU | SVDThinV
|
||||
// SVDThin is a convenience value for computing both full vectors.
|
||||
SVDFull SVDKind = SVDFullU | SVDFullV
|
||||
)
|
||||
|
||||
// succFact returns whether the receiver contains a successful factorization.
|
||||
func (svd *SVD) succFact() bool {
|
||||
return len(svd.s) != 0
|
||||
}
|
||||
|
||||
// Factorize computes the singular value decomposition (SVD) of the input matrix A.
|
||||
// The singular values of A are computed in all cases, while the singular
|
||||
// vectors are optionally computed depending on the input kind.
|
||||
//
|
||||
// The full singular value decomposition (kind == SVDFull) is a factorization
|
||||
// of an m×n matrix A of the form
|
||||
// A = U * Σ * V^T
|
||||
// where Σ is an m×n diagonal matrix, U is an m×m orthogonal matrix, and V is an
|
||||
// n×n orthogonal matrix. The diagonal elements of Σ are the singular values of A.
|
||||
// The first min(m,n) columns of U and V are, respectively, the left and right
|
||||
// singular vectors of A.
|
||||
//
|
||||
// Significant storage space can be saved by using the thin representation of
|
||||
// the SVD (kind == SVDThin) instead of the full SVD, especially if
|
||||
// m >> n or m << n. The thin SVD finds
|
||||
// A = U~ * Σ * V~^T
|
||||
// where U~ is of size m×min(m,n), Σ is a diagonal matrix of size min(m,n)×min(m,n)
|
||||
// and V~ is of size n×min(m,n).
|
||||
//
|
||||
// Factorize returns whether the decomposition succeeded. If the decomposition
|
||||
// failed, routines that require a successful factorization will panic.
|
||||
func (svd *SVD) Factorize(a Matrix, kind SVDKind) (ok bool) {
|
||||
// kill previous factorization
|
||||
svd.s = svd.s[:0]
|
||||
svd.kind = kind
|
||||
|
||||
m, n := a.Dims()
|
||||
var jobU, jobVT lapack.SVDJob
|
||||
|
||||
// TODO(btracey): This code should be modified to have the smaller
|
||||
// matrix written in-place into aCopy when the lapack/native/dgesvd
|
||||
// implementation is complete.
|
||||
switch {
|
||||
case kind&SVDFullU != 0:
|
||||
jobU = lapack.SVDAll
|
||||
svd.u = blas64.General{
|
||||
Rows: m,
|
||||
Cols: m,
|
||||
Stride: m,
|
||||
Data: use(svd.u.Data, m*m),
|
||||
}
|
||||
case kind&SVDThinU != 0:
|
||||
jobU = lapack.SVDStore
|
||||
svd.u = blas64.General{
|
||||
Rows: m,
|
||||
Cols: min(m, n),
|
||||
Stride: min(m, n),
|
||||
Data: use(svd.u.Data, m*min(m, n)),
|
||||
}
|
||||
default:
|
||||
jobU = lapack.SVDNone
|
||||
}
|
||||
switch {
|
||||
case kind&SVDFullV != 0:
|
||||
svd.vt = blas64.General{
|
||||
Rows: n,
|
||||
Cols: n,
|
||||
Stride: n,
|
||||
Data: use(svd.vt.Data, n*n),
|
||||
}
|
||||
jobVT = lapack.SVDAll
|
||||
case kind&SVDThinV != 0:
|
||||
svd.vt = blas64.General{
|
||||
Rows: min(m, n),
|
||||
Cols: n,
|
||||
Stride: n,
|
||||
Data: use(svd.vt.Data, min(m, n)*n),
|
||||
}
|
||||
jobVT = lapack.SVDStore
|
||||
default:
|
||||
jobVT = lapack.SVDNone
|
||||
}
|
||||
|
||||
// A is destroyed on call, so copy the matrix.
|
||||
aCopy := DenseCopyOf(a)
|
||||
svd.kind = kind
|
||||
svd.s = use(svd.s, min(m, n))
|
||||
|
||||
work := []float64{0}
|
||||
lapack64.Gesvd(jobU, jobVT, aCopy.mat, svd.u, svd.vt, svd.s, work, -1)
|
||||
work = getFloats(int(work[0]), false)
|
||||
ok = lapack64.Gesvd(jobU, jobVT, aCopy.mat, svd.u, svd.vt, svd.s, work, len(work))
|
||||
putFloats(work)
|
||||
if !ok {
|
||||
svd.kind = 0
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// Kind returns the SVDKind of the decomposition. If no decomposition has been
|
||||
// computed, Kind returns -1.
|
||||
func (svd *SVD) Kind() SVDKind {
|
||||
if !svd.succFact() {
|
||||
return -1
|
||||
}
|
||||
return svd.kind
|
||||
}
|
||||
|
||||
// Cond returns the 2-norm condition number for the factorized matrix. Cond will
|
||||
// panic if the receiver does not contain a successful factorization.
|
||||
func (svd *SVD) Cond() float64 {
|
||||
if !svd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
return svd.s[0] / svd.s[len(svd.s)-1]
|
||||
}
|
||||
|
||||
// Values returns the singular values of the factorized matrix in descending order.
|
||||
//
|
||||
// If the input slice is non-nil, the values will be stored in-place into
|
||||
// the slice. In this case, the slice must have length min(m,n), and Values will
|
||||
// panic with ErrSliceLengthMismatch otherwise. If the input slice is nil, a new
|
||||
// slice of the appropriate length will be allocated and returned.
|
||||
//
|
||||
// Values will panic if the receiver does not contain a successful factorization.
|
||||
func (svd *SVD) Values(s []float64) []float64 {
|
||||
if !svd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if s == nil {
|
||||
s = make([]float64, len(svd.s))
|
||||
}
|
||||
if len(s) != len(svd.s) {
|
||||
panic(ErrSliceLengthMismatch)
|
||||
}
|
||||
copy(s, svd.s)
|
||||
return s
|
||||
}
|
||||
|
||||
// UTo extracts the matrix U from the singular value decomposition. The first
|
||||
// min(m,n) columns are the left singular vectors and correspond to the singular
|
||||
// values as returned from SVD.Values.
|
||||
//
|
||||
// If dst is not nil, U is stored in-place into dst, and dst must have size
|
||||
// m×m if the full U was computed, size m×min(m,n) if the thin U was computed,
|
||||
// and UTo panics otherwise. If dst is nil, a new matrix of the appropriate size
|
||||
// is allocated and returned.
|
||||
func (svd *SVD) UTo(dst *Dense) *Dense {
|
||||
if !svd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
kind := svd.kind
|
||||
if kind&SVDThinU == 0 && kind&SVDFullU == 0 {
|
||||
panic("svd: u not computed during factorization")
|
||||
}
|
||||
r := svd.u.Rows
|
||||
c := svd.u.Cols
|
||||
if dst == nil {
|
||||
dst = NewDense(r, c, nil)
|
||||
} else {
|
||||
dst.reuseAs(r, c)
|
||||
}
|
||||
|
||||
tmp := &Dense{
|
||||
mat: svd.u,
|
||||
capRows: r,
|
||||
capCols: c,
|
||||
}
|
||||
dst.Copy(tmp)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// VTo extracts the matrix V from the singular value decomposition. The first
|
||||
// min(m,n) columns are the right singular vectors and correspond to the singular
|
||||
// values as returned from SVD.Values.
|
||||
//
|
||||
// If dst is not nil, V is stored in-place into dst, and dst must have size
|
||||
// n×n if the full V was computed, size n×min(m,n) if the thin V was computed,
|
||||
// and VTo panics otherwise. If dst is nil, a new matrix of the appropriate size
|
||||
// is allocated and returned.
|
||||
func (svd *SVD) VTo(dst *Dense) *Dense {
|
||||
if !svd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
kind := svd.kind
|
||||
if kind&SVDThinU == 0 && kind&SVDFullV == 0 {
|
||||
panic("svd: v not computed during factorization")
|
||||
}
|
||||
r := svd.vt.Rows
|
||||
c := svd.vt.Cols
|
||||
if dst == nil {
|
||||
dst = NewDense(c, r, nil)
|
||||
} else {
|
||||
dst.reuseAs(c, r)
|
||||
}
|
||||
|
||||
tmp := &Dense{
|
||||
mat: svd.vt,
|
||||
capRows: r,
|
||||
capCols: c,
|
||||
}
|
||||
dst.Copy(tmp.T())
|
||||
|
||||
return dst
|
||||
}
|
||||
221
vendor/gonum.org/v1/gonum/mat/symband.go
generated
vendored
Normal file
221
vendor/gonum.org/v1/gonum/mat/symband.go
generated
vendored
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
// Copyright ©2017 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
)
|
||||
|
||||
var (
|
||||
symBandDense *SymBandDense
|
||||
_ Matrix = symBandDense
|
||||
_ Symmetric = symBandDense
|
||||
_ Banded = symBandDense
|
||||
_ SymBanded = symBandDense
|
||||
_ RawSymBander = symBandDense
|
||||
_ MutableSymBanded = symBandDense
|
||||
|
||||
_ NonZeroDoer = symBandDense
|
||||
_ RowNonZeroDoer = symBandDense
|
||||
_ ColNonZeroDoer = symBandDense
|
||||
)
|
||||
|
||||
// SymBandDense represents a symmetric band matrix in dense storage format.
|
||||
type SymBandDense struct {
|
||||
mat blas64.SymmetricBand
|
||||
}
|
||||
|
||||
// SymBanded is a symmetric band matrix interface type.
|
||||
type SymBanded interface {
|
||||
Banded
|
||||
|
||||
// Symmetric returns the number of rows/columns in the matrix.
|
||||
Symmetric() int
|
||||
|
||||
// SymBand returns the number of rows/columns in the matrix, and the size of
|
||||
// the bandwidth.
|
||||
SymBand() (n, k int)
|
||||
}
|
||||
|
||||
// MutableSymBanded is a symmetric band matrix interface type that allows elements
|
||||
// to be altered.
|
||||
type MutableSymBanded interface {
|
||||
SymBanded
|
||||
SetSymBand(i, j int, v float64)
|
||||
}
|
||||
|
||||
// A RawSymBander can return a blas64.SymmetricBand representation of the receiver.
|
||||
// Changes to the blas64.SymmetricBand.Data slice will be reflected in the original
|
||||
// matrix, changes to the N, K, Stride and Uplo fields will not.
|
||||
type RawSymBander interface {
|
||||
RawSymBand() blas64.SymmetricBand
|
||||
}
|
||||
|
||||
// NewSymBandDense creates a new SymBand matrix with n rows and columns. If data == nil,
|
||||
// a new slice is allocated for the backing slice. If len(data) == n*(k+1),
|
||||
// data is used as the backing slice, and changes to the elements of the returned
|
||||
// SymBandDense will be reflected in data. If neither of these is true, NewSymBandDense
|
||||
// will panic. k must be at least zero and less than n, otherwise NewSymBandDense will panic.
|
||||
//
|
||||
// The data must be arranged in row-major order constructed by removing the zeros
|
||||
// from the rows outside the band and aligning the diagonals. SymBandDense matrices
|
||||
// are stored in the upper triangle. For example, the matrix
|
||||
// 1 2 3 0 0 0
|
||||
// 2 4 5 6 0 0
|
||||
// 3 5 7 8 9 0
|
||||
// 0 6 8 10 11 12
|
||||
// 0 0 9 11 13 14
|
||||
// 0 0 0 12 14 15
|
||||
// becomes (* entries are never accessed)
|
||||
// 1 2 3
|
||||
// 4 5 6
|
||||
// 7 8 9
|
||||
// 10 11 12
|
||||
// 13 14 *
|
||||
// 15 * *
|
||||
// which is passed to NewSymBandDense as []float64{1, 2, ..., 15, *, *, *} with k=2.
|
||||
// Only the values in the band portion of the matrix are used.
|
||||
func NewSymBandDense(n, k int, data []float64) *SymBandDense {
|
||||
if n <= 0 || k < 0 {
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic("mat: negative dimension")
|
||||
}
|
||||
if k+1 > n {
|
||||
panic("mat: band out of range")
|
||||
}
|
||||
bc := k + 1
|
||||
if data != nil && len(data) != n*bc {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if data == nil {
|
||||
data = make([]float64, n*bc)
|
||||
}
|
||||
return &SymBandDense{
|
||||
mat: blas64.SymmetricBand{
|
||||
N: n,
|
||||
K: k,
|
||||
Stride: bc,
|
||||
Uplo: blas.Upper,
|
||||
Data: data,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Dims returns the number of rows and columns in the matrix.
|
||||
func (s *SymBandDense) Dims() (r, c int) {
|
||||
return s.mat.N, s.mat.N
|
||||
}
|
||||
|
||||
// Symmetric returns the size of the receiver.
|
||||
func (s *SymBandDense) Symmetric() int {
|
||||
return s.mat.N
|
||||
}
|
||||
|
||||
// Bandwidth returns the bandwidths of the matrix.
|
||||
func (s *SymBandDense) Bandwidth() (kl, ku int) {
|
||||
return s.mat.K, s.mat.K
|
||||
}
|
||||
|
||||
// SymBand returns the number of rows/columns in the matrix, and the size of
|
||||
// the bandwidth.
|
||||
func (s *SymBandDense) SymBand() (n, k int) {
|
||||
return s.mat.N, s.mat.K
|
||||
}
|
||||
|
||||
// T implements the Matrix interface. Symmetric matrices, by definition, are
|
||||
// equal to their transpose, and this is a no-op.
|
||||
func (s *SymBandDense) T() Matrix {
|
||||
return s
|
||||
}
|
||||
|
||||
// TBand implements the Banded interface.
|
||||
func (s *SymBandDense) TBand() Banded {
|
||||
return s
|
||||
}
|
||||
|
||||
// RawSymBand returns the underlying blas64.SymBand used by the receiver.
|
||||
// Changes to elements in the receiver following the call will be reflected
|
||||
// in returned blas64.SymBand.
|
||||
func (s *SymBandDense) RawSymBand() blas64.SymmetricBand {
|
||||
return s.mat
|
||||
}
|
||||
|
||||
// SetRawSymBand sets the underlying blas64.SymmetricBand used by the receiver.
|
||||
// Changes to elements in the receiver following the call will be reflected
|
||||
// in the input.
|
||||
//
|
||||
// The supplied SymmetricBand must use blas.Upper storage format.
|
||||
func (s *SymBandDense) SetRawSymBand(mat blas64.SymmetricBand) {
|
||||
if mat.Uplo != blas.Upper {
|
||||
panic("mat: blas64.SymmetricBand does not have blas.Upper storage")
|
||||
}
|
||||
s.mat = mat
|
||||
}
|
||||
|
||||
// Zero sets all of the matrix elements to zero.
|
||||
func (s *SymBandDense) Zero() {
|
||||
for i := 0; i < s.mat.N; i++ {
|
||||
u := min(1+s.mat.K, s.mat.N-i)
|
||||
zero(s.mat.Data[i*s.mat.Stride : i*s.mat.Stride+u])
|
||||
}
|
||||
}
|
||||
|
||||
// DiagView returns the diagonal as a matrix backed by the original data.
|
||||
func (s *SymBandDense) DiagView() Diagonal {
|
||||
n := s.mat.N
|
||||
return &DiagDense{
|
||||
mat: blas64.Vector{
|
||||
N: n,
|
||||
Inc: s.mat.Stride,
|
||||
Data: s.mat.Data[:(n-1)*s.mat.Stride+1],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// DoNonZero calls the function fn for each of the non-zero elements of s. The function fn
|
||||
// takes a row/column index and the element value of s at (i, j).
|
||||
func (s *SymBandDense) DoNonZero(fn func(i, j int, v float64)) {
|
||||
for i := 0; i < s.mat.N; i++ {
|
||||
for j := max(0, i-s.mat.K); j < min(s.mat.N, i+s.mat.K+1); j++ {
|
||||
v := s.at(i, j)
|
||||
if v != 0 {
|
||||
fn(i, j, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DoRowNonZero calls the function fn for each of the non-zero elements of row i of s. The function fn
|
||||
// takes a row/column index and the element value of s at (i, j).
|
||||
func (s *SymBandDense) DoRowNonZero(i int, fn func(i, j int, v float64)) {
|
||||
if i < 0 || s.mat.N <= i {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
for j := max(0, i-s.mat.K); j < min(s.mat.N, i+s.mat.K+1); j++ {
|
||||
v := s.at(i, j)
|
||||
if v != 0 {
|
||||
fn(i, j, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DoColNonZero calls the function fn for each of the non-zero elements of column j of s. The function fn
|
||||
// takes a row/column index and the element value of s at (i, j).
|
||||
func (s *SymBandDense) DoColNonZero(j int, fn func(i, j int, v float64)) {
|
||||
if j < 0 || s.mat.N <= j {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
for i := 0; i < s.mat.N; i++ {
|
||||
if i-s.mat.K <= j && j < i+s.mat.K+1 {
|
||||
v := s.at(i, j)
|
||||
if v != 0 {
|
||||
fn(i, j, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
602
vendor/gonum.org/v1/gonum/mat/symmetric.go
generated
vendored
Normal file
602
vendor/gonum.org/v1/gonum/mat/symmetric.go
generated
vendored
Normal file
|
|
@ -0,0 +1,602 @@
|
|||
// Copyright ©2015 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
)
|
||||
|
||||
var (
|
||||
symDense *SymDense
|
||||
|
||||
_ Matrix = symDense
|
||||
_ Symmetric = symDense
|
||||
_ RawSymmetricer = symDense
|
||||
_ MutableSymmetric = symDense
|
||||
)
|
||||
|
||||
const (
|
||||
badSymTriangle = "mat: blas64.Symmetric not upper"
|
||||
badSymCap = "mat: bad capacity for SymDense"
|
||||
)
|
||||
|
||||
// SymDense is a symmetric matrix that uses dense storage. SymDense
|
||||
// matrices are stored in the upper triangle.
|
||||
type SymDense struct {
|
||||
mat blas64.Symmetric
|
||||
cap int
|
||||
}
|
||||
|
||||
// Symmetric represents a symmetric matrix (where the element at {i, j} equals
|
||||
// the element at {j, i}). Symmetric matrices are always square.
|
||||
type Symmetric interface {
|
||||
Matrix
|
||||
// Symmetric returns the number of rows/columns in the matrix.
|
||||
Symmetric() int
|
||||
}
|
||||
|
||||
// A RawSymmetricer can return a view of itself as a BLAS Symmetric matrix.
|
||||
type RawSymmetricer interface {
|
||||
RawSymmetric() blas64.Symmetric
|
||||
}
|
||||
|
||||
// A MutableSymmetric can set elements of a symmetric matrix.
|
||||
type MutableSymmetric interface {
|
||||
Symmetric
|
||||
SetSym(i, j int, v float64)
|
||||
}
|
||||
|
||||
// NewSymDense creates a new Symmetric matrix with n rows and columns. If data == nil,
|
||||
// a new slice is allocated for the backing slice. If len(data) == n*n, data is
|
||||
// used as the backing slice, and changes to the elements of the returned SymDense
|
||||
// will be reflected in data. If neither of these is true, NewSymDense will panic.
|
||||
// NewSymDense will panic if n is zero.
|
||||
//
|
||||
// The data must be arranged in row-major order, i.e. the (i*c + j)-th
|
||||
// element in the data slice is the {i, j}-th element in the matrix.
|
||||
// Only the values in the upper triangular portion of the matrix are used.
|
||||
func NewSymDense(n int, data []float64) *SymDense {
|
||||
if n <= 0 {
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic("mat: negative dimension")
|
||||
}
|
||||
if data != nil && n*n != len(data) {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if data == nil {
|
||||
data = make([]float64, n*n)
|
||||
}
|
||||
return &SymDense{
|
||||
mat: blas64.Symmetric{
|
||||
N: n,
|
||||
Stride: n,
|
||||
Data: data,
|
||||
Uplo: blas.Upper,
|
||||
},
|
||||
cap: n,
|
||||
}
|
||||
}
|
||||
|
||||
// Dims returns the number of rows and columns in the matrix.
|
||||
func (s *SymDense) Dims() (r, c int) {
|
||||
return s.mat.N, s.mat.N
|
||||
}
|
||||
|
||||
// Caps returns the number of rows and columns in the backing matrix.
|
||||
func (s *SymDense) Caps() (r, c int) {
|
||||
return s.cap, s.cap
|
||||
}
|
||||
|
||||
// T returns the receiver, the transpose of a symmetric matrix.
|
||||
func (s *SymDense) T() Matrix {
|
||||
return s
|
||||
}
|
||||
|
||||
// Symmetric implements the Symmetric interface and returns the number of rows
|
||||
// and columns in the matrix.
|
||||
func (s *SymDense) Symmetric() int {
|
||||
return s.mat.N
|
||||
}
|
||||
|
||||
// RawSymmetric returns the matrix as a blas64.Symmetric. The returned
|
||||
// value must be stored in upper triangular format.
|
||||
func (s *SymDense) RawSymmetric() blas64.Symmetric {
|
||||
return s.mat
|
||||
}
|
||||
|
||||
// SetRawSymmetric sets the underlying blas64.Symmetric used by the receiver.
|
||||
// Changes to elements in the receiver following the call will be reflected
|
||||
// in the input.
|
||||
//
|
||||
// The supplied Symmetric must use blas.Upper storage format.
|
||||
func (s *SymDense) SetRawSymmetric(mat blas64.Symmetric) {
|
||||
if mat.Uplo != blas.Upper {
|
||||
panic(badSymTriangle)
|
||||
}
|
||||
s.mat = mat
|
||||
}
|
||||
|
||||
// Reset zeros the dimensions of the matrix so that it can be reused as the
|
||||
// receiver of a dimensionally restricted operation.
|
||||
//
|
||||
// See the Reseter interface for more information.
|
||||
func (s *SymDense) Reset() {
|
||||
// N and Stride must be zeroed in unison.
|
||||
s.mat.N, s.mat.Stride = 0, 0
|
||||
s.mat.Data = s.mat.Data[:0]
|
||||
}
|
||||
|
||||
// Zero sets all of the matrix elements to zero.
|
||||
func (s *SymDense) Zero() {
|
||||
for i := 0; i < s.mat.N; i++ {
|
||||
zero(s.mat.Data[i*s.mat.Stride+i : i*s.mat.Stride+s.mat.N])
|
||||
}
|
||||
}
|
||||
|
||||
// IsZero returns whether the receiver is zero-sized. Zero-sized matrices can be the
|
||||
// receiver for size-restricted operations. SymDense matrices can be zeroed using Reset.
|
||||
func (s *SymDense) IsZero() bool {
|
||||
// It must be the case that m.Dims() returns
|
||||
// zeros in this case. See comment in Reset().
|
||||
return s.mat.N == 0
|
||||
}
|
||||
|
||||
// reuseAs resizes an empty matrix to a n×n matrix,
|
||||
// or checks that a non-empty matrix is n×n.
|
||||
func (s *SymDense) reuseAs(n int) {
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
if s.mat.N > s.cap {
|
||||
panic(badSymCap)
|
||||
}
|
||||
if s.IsZero() {
|
||||
s.mat = blas64.Symmetric{
|
||||
N: n,
|
||||
Stride: n,
|
||||
Data: use(s.mat.Data, n*n),
|
||||
Uplo: blas.Upper,
|
||||
}
|
||||
s.cap = n
|
||||
return
|
||||
}
|
||||
if s.mat.Uplo != blas.Upper {
|
||||
panic(badSymTriangle)
|
||||
}
|
||||
if s.mat.N != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SymDense) isolatedWorkspace(a Symmetric) (w *SymDense, restore func()) {
|
||||
n := a.Symmetric()
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
w = getWorkspaceSym(n, false)
|
||||
return w, func() {
|
||||
s.CopySym(w)
|
||||
putWorkspaceSym(w)
|
||||
}
|
||||
}
|
||||
|
||||
// DiagView returns the diagonal as a matrix backed by the original data.
|
||||
func (s *SymDense) DiagView() Diagonal {
|
||||
n := s.mat.N
|
||||
return &DiagDense{
|
||||
mat: blas64.Vector{
|
||||
N: n,
|
||||
Inc: s.mat.Stride + 1,
|
||||
Data: s.mat.Data[:(n-1)*s.mat.Stride+n],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SymDense) AddSym(a, b Symmetric) {
|
||||
n := a.Symmetric()
|
||||
if n != b.Symmetric() {
|
||||
panic(ErrShape)
|
||||
}
|
||||
s.reuseAs(n)
|
||||
|
||||
if a, ok := a.(RawSymmetricer); ok {
|
||||
if b, ok := b.(RawSymmetricer); ok {
|
||||
amat, bmat := a.RawSymmetric(), b.RawSymmetric()
|
||||
if s != a {
|
||||
s.checkOverlap(generalFromSymmetric(amat))
|
||||
}
|
||||
if s != b {
|
||||
s.checkOverlap(generalFromSymmetric(bmat))
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
btmp := bmat.Data[i*bmat.Stride+i : i*bmat.Stride+n]
|
||||
stmp := s.mat.Data[i*s.mat.Stride+i : i*s.mat.Stride+n]
|
||||
for j, v := range amat.Data[i*amat.Stride+i : i*amat.Stride+n] {
|
||||
stmp[j] = v + btmp[j]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
s.checkOverlapMatrix(a)
|
||||
s.checkOverlapMatrix(b)
|
||||
for i := 0; i < n; i++ {
|
||||
stmp := s.mat.Data[i*s.mat.Stride : i*s.mat.Stride+n]
|
||||
for j := i; j < n; j++ {
|
||||
stmp[j] = a.At(i, j) + b.At(i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SymDense) CopySym(a Symmetric) int {
|
||||
n := a.Symmetric()
|
||||
n = min(n, s.mat.N)
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
switch a := a.(type) {
|
||||
case RawSymmetricer:
|
||||
amat := a.RawSymmetric()
|
||||
if amat.Uplo != blas.Upper {
|
||||
panic(badSymTriangle)
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
copy(s.mat.Data[i*s.mat.Stride+i:i*s.mat.Stride+n], amat.Data[i*amat.Stride+i:i*amat.Stride+n])
|
||||
}
|
||||
default:
|
||||
for i := 0; i < n; i++ {
|
||||
stmp := s.mat.Data[i*s.mat.Stride : i*s.mat.Stride+n]
|
||||
for j := i; j < n; j++ {
|
||||
stmp[j] = a.At(i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// SymRankOne performs a symetric rank-one update to the matrix a and stores
|
||||
// the result in the receiver
|
||||
// s = a + alpha * x * x'
|
||||
func (s *SymDense) SymRankOne(a Symmetric, alpha float64, x Vector) {
|
||||
n, c := x.Dims()
|
||||
if a.Symmetric() != n || c != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
s.reuseAs(n)
|
||||
|
||||
if s != a {
|
||||
if rs, ok := a.(RawSymmetricer); ok {
|
||||
s.checkOverlap(generalFromSymmetric(rs.RawSymmetric()))
|
||||
}
|
||||
s.CopySym(a)
|
||||
}
|
||||
|
||||
xU, _ := untranspose(x)
|
||||
if rv, ok := xU.(RawVectorer); ok {
|
||||
xmat := rv.RawVector()
|
||||
s.checkOverlap((&VecDense{mat: xmat}).asGeneral())
|
||||
blas64.Syr(alpha, xmat, s.mat)
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
for j := i; j < n; j++ {
|
||||
s.set(i, j, s.at(i, j)+alpha*x.AtVec(i)*x.AtVec(j))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SymRankK performs a symmetric rank-k update to the matrix a and stores the
|
||||
// result into the receiver. If a is zero, see SymOuterK.
|
||||
// s = a + alpha * x * x'
|
||||
func (s *SymDense) SymRankK(a Symmetric, alpha float64, x Matrix) {
|
||||
n := a.Symmetric()
|
||||
r, _ := x.Dims()
|
||||
if r != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
xMat, aTrans := untranspose(x)
|
||||
var g blas64.General
|
||||
if rm, ok := xMat.(RawMatrixer); ok {
|
||||
g = rm.RawMatrix()
|
||||
} else {
|
||||
g = DenseCopyOf(x).mat
|
||||
aTrans = false
|
||||
}
|
||||
if a != s {
|
||||
if rs, ok := a.(RawSymmetricer); ok {
|
||||
s.checkOverlap(generalFromSymmetric(rs.RawSymmetric()))
|
||||
}
|
||||
s.reuseAs(n)
|
||||
s.CopySym(a)
|
||||
}
|
||||
t := blas.NoTrans
|
||||
if aTrans {
|
||||
t = blas.Trans
|
||||
}
|
||||
blas64.Syrk(t, alpha, g, 1, s.mat)
|
||||
}
|
||||
|
||||
// SymOuterK calculates the outer product of x with itself and stores
|
||||
// the result into the receiver. It is equivalent to the matrix
|
||||
// multiplication
|
||||
// s = alpha * x * x'.
|
||||
// In order to update an existing matrix, see SymRankOne.
|
||||
func (s *SymDense) SymOuterK(alpha float64, x Matrix) {
|
||||
n, _ := x.Dims()
|
||||
switch {
|
||||
case s.IsZero():
|
||||
s.mat = blas64.Symmetric{
|
||||
N: n,
|
||||
Stride: n,
|
||||
Data: useZeroed(s.mat.Data, n*n),
|
||||
Uplo: blas.Upper,
|
||||
}
|
||||
s.cap = n
|
||||
s.SymRankK(s, alpha, x)
|
||||
case s.mat.Uplo != blas.Upper:
|
||||
panic(badSymTriangle)
|
||||
case s.mat.N == n:
|
||||
if s == x {
|
||||
w := getWorkspaceSym(n, true)
|
||||
w.SymRankK(w, alpha, x)
|
||||
s.CopySym(w)
|
||||
putWorkspaceSym(w)
|
||||
} else {
|
||||
switch r := x.(type) {
|
||||
case RawMatrixer:
|
||||
s.checkOverlap(r.RawMatrix())
|
||||
case RawSymmetricer:
|
||||
s.checkOverlap(generalFromSymmetric(r.RawSymmetric()))
|
||||
case RawTriangular:
|
||||
s.checkOverlap(generalFromTriangular(r.RawTriangular()))
|
||||
}
|
||||
// Only zero the upper triangle.
|
||||
for i := 0; i < n; i++ {
|
||||
ri := i * s.mat.Stride
|
||||
zero(s.mat.Data[ri+i : ri+n])
|
||||
}
|
||||
s.SymRankK(s, alpha, x)
|
||||
}
|
||||
default:
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
|
||||
// RankTwo performs a symmmetric rank-two update to the matrix a and stores
|
||||
// the result in the receiver
|
||||
// m = a + alpha * (x * y' + y * x')
|
||||
func (s *SymDense) RankTwo(a Symmetric, alpha float64, x, y Vector) {
|
||||
n := s.mat.N
|
||||
xr, xc := x.Dims()
|
||||
if xr != n || xc != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
yr, yc := y.Dims()
|
||||
if yr != n || yc != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
if s != a {
|
||||
if rs, ok := a.(RawSymmetricer); ok {
|
||||
s.checkOverlap(generalFromSymmetric(rs.RawSymmetric()))
|
||||
}
|
||||
}
|
||||
|
||||
var xmat, ymat blas64.Vector
|
||||
fast := true
|
||||
xU, _ := untranspose(x)
|
||||
if rv, ok := xU.(RawVectorer); ok {
|
||||
xmat = rv.RawVector()
|
||||
s.checkOverlap((&VecDense{mat: xmat}).asGeneral())
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
yU, _ := untranspose(y)
|
||||
if rv, ok := yU.(RawVectorer); ok {
|
||||
ymat = rv.RawVector()
|
||||
s.checkOverlap((&VecDense{mat: ymat}).asGeneral())
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
|
||||
if s != a {
|
||||
if rs, ok := a.(RawSymmetricer); ok {
|
||||
s.checkOverlap(generalFromSymmetric(rs.RawSymmetric()))
|
||||
}
|
||||
s.reuseAs(n)
|
||||
s.CopySym(a)
|
||||
}
|
||||
|
||||
if fast {
|
||||
if s != a {
|
||||
s.reuseAs(n)
|
||||
s.CopySym(a)
|
||||
}
|
||||
blas64.Syr2(alpha, xmat, ymat, s.mat)
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
s.reuseAs(n)
|
||||
for j := i; j < n; j++ {
|
||||
s.set(i, j, a.At(i, j)+alpha*(x.AtVec(i)*y.AtVec(j)+y.AtVec(i)*x.AtVec(j)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ScaleSym multiplies the elements of a by f, placing the result in the receiver.
|
||||
func (s *SymDense) ScaleSym(f float64, a Symmetric) {
|
||||
n := a.Symmetric()
|
||||
s.reuseAs(n)
|
||||
if a, ok := a.(RawSymmetricer); ok {
|
||||
amat := a.RawSymmetric()
|
||||
if s != a {
|
||||
s.checkOverlap(generalFromSymmetric(amat))
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
for j := i; j < n; j++ {
|
||||
s.mat.Data[i*s.mat.Stride+j] = f * amat.Data[i*amat.Stride+j]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
for j := i; j < n; j++ {
|
||||
s.mat.Data[i*s.mat.Stride+j] = f * a.At(i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SubsetSym extracts a subset of the rows and columns of the matrix a and stores
|
||||
// the result in-place into the receiver. The resulting matrix size is
|
||||
// len(set)×len(set). Specifically, at the conclusion of SubsetSym,
|
||||
// s.At(i, j) equals a.At(set[i], set[j]). Note that the supplied set does not
|
||||
// have to be a strict subset, dimension repeats are allowed.
|
||||
func (s *SymDense) SubsetSym(a Symmetric, set []int) {
|
||||
n := len(set)
|
||||
na := a.Symmetric()
|
||||
s.reuseAs(n)
|
||||
var restore func()
|
||||
if a == s {
|
||||
s, restore = s.isolatedWorkspace(a)
|
||||
defer restore()
|
||||
}
|
||||
|
||||
if a, ok := a.(RawSymmetricer); ok {
|
||||
raw := a.RawSymmetric()
|
||||
if s != a {
|
||||
s.checkOverlap(generalFromSymmetric(raw))
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
ssub := s.mat.Data[i*s.mat.Stride : i*s.mat.Stride+n]
|
||||
r := set[i]
|
||||
rsub := raw.Data[r*raw.Stride : r*raw.Stride+na]
|
||||
for j := i; j < n; j++ {
|
||||
c := set[j]
|
||||
if r <= c {
|
||||
ssub[j] = rsub[c]
|
||||
} else {
|
||||
ssub[j] = raw.Data[c*raw.Stride+r]
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
for j := i; j < n; j++ {
|
||||
s.mat.Data[i*s.mat.Stride+j] = a.At(set[i], set[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SliceSym returns a new Matrix that shares backing data with the receiver.
|
||||
// The returned matrix starts at {i,i} of the receiver and extends k-i rows
|
||||
// and columns. The final row and column in the resulting matrix is k-1.
|
||||
// SliceSym panics with ErrIndexOutOfRange if the slice is outside the
|
||||
// capacity of the receiver.
|
||||
func (s *SymDense) SliceSym(i, k int) Symmetric {
|
||||
sz := s.cap
|
||||
if i < 0 || sz < i || k < i || sz < k {
|
||||
panic(ErrIndexOutOfRange)
|
||||
}
|
||||
v := *s
|
||||
v.mat.Data = s.mat.Data[i*s.mat.Stride+i : (k-1)*s.mat.Stride+k]
|
||||
v.mat.N = k - i
|
||||
v.cap = s.cap - i
|
||||
return &v
|
||||
}
|
||||
|
||||
// Trace returns the trace of the matrix.
|
||||
func (s *SymDense) Trace() float64 {
|
||||
// TODO(btracey): could use internal asm sum routine.
|
||||
var v float64
|
||||
for i := 0; i < s.mat.N; i++ {
|
||||
v += s.mat.Data[i*s.mat.Stride+i]
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// GrowSym returns the receiver expanded by n rows and n columns. If the
|
||||
// dimensions of the expanded matrix are outside the capacity of the receiver
|
||||
// a new allocation is made, otherwise not. Note that the receiver itself is
|
||||
// not modified during the call to GrowSquare.
|
||||
func (s *SymDense) GrowSym(n int) Symmetric {
|
||||
if n < 0 {
|
||||
panic(ErrIndexOutOfRange)
|
||||
}
|
||||
if n == 0 {
|
||||
return s
|
||||
}
|
||||
var v SymDense
|
||||
n += s.mat.N
|
||||
if n > s.cap {
|
||||
v.mat = blas64.Symmetric{
|
||||
N: n,
|
||||
Stride: n,
|
||||
Uplo: blas.Upper,
|
||||
Data: make([]float64, n*n),
|
||||
}
|
||||
v.cap = n
|
||||
// Copy elements, including those not currently visible. Use a temporary
|
||||
// structure to avoid modifying the receiver.
|
||||
var tmp SymDense
|
||||
tmp.mat = blas64.Symmetric{
|
||||
N: s.cap,
|
||||
Stride: s.mat.Stride,
|
||||
Data: s.mat.Data,
|
||||
Uplo: s.mat.Uplo,
|
||||
}
|
||||
tmp.cap = s.cap
|
||||
v.CopySym(&tmp)
|
||||
return &v
|
||||
}
|
||||
v.mat = blas64.Symmetric{
|
||||
N: n,
|
||||
Stride: s.mat.Stride,
|
||||
Uplo: blas.Upper,
|
||||
Data: s.mat.Data[:(n-1)*s.mat.Stride+n],
|
||||
}
|
||||
v.cap = s.cap
|
||||
return &v
|
||||
}
|
||||
|
||||
// PowPSD computes a^pow where a is a positive symmetric definite matrix.
|
||||
//
|
||||
// PowPSD returns an error if the matrix is not not positive symmetric definite
|
||||
// or the Eigendecomposition is not successful.
|
||||
func (s *SymDense) PowPSD(a Symmetric, pow float64) error {
|
||||
dim := a.Symmetric()
|
||||
s.reuseAs(dim)
|
||||
|
||||
var eigen EigenSym
|
||||
ok := eigen.Factorize(a, true)
|
||||
if !ok {
|
||||
return ErrFailedEigen
|
||||
}
|
||||
values := eigen.Values(nil)
|
||||
for i, v := range values {
|
||||
if v <= 0 {
|
||||
return ErrNotPSD
|
||||
}
|
||||
values[i] = math.Pow(v, pow)
|
||||
}
|
||||
u := eigen.VectorsTo(nil)
|
||||
|
||||
s.SymOuterK(values[0], u.ColView(0))
|
||||
|
||||
var v VecDense
|
||||
for i := 1; i < dim; i++ {
|
||||
v.ColViewOf(u, i)
|
||||
s.SymRankOne(s, values[i], &v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
659
vendor/gonum.org/v1/gonum/mat/triangular.go
generated
vendored
Normal file
659
vendor/gonum.org/v1/gonum/mat/triangular.go
generated
vendored
Normal file
|
|
@ -0,0 +1,659 @@
|
|||
// Copyright ©2015 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
var (
|
||||
triDense *TriDense
|
||||
_ Matrix = triDense
|
||||
_ Triangular = triDense
|
||||
_ RawTriangular = triDense
|
||||
_ MutableTriangular = triDense
|
||||
|
||||
_ NonZeroDoer = triDense
|
||||
_ RowNonZeroDoer = triDense
|
||||
_ ColNonZeroDoer = triDense
|
||||
)
|
||||
|
||||
const badTriCap = "mat: bad capacity for TriDense"
|
||||
|
||||
// TriDense represents an upper or lower triangular matrix in dense storage
|
||||
// format.
|
||||
type TriDense struct {
|
||||
mat blas64.Triangular
|
||||
cap int
|
||||
}
|
||||
|
||||
// Triangular represents a triangular matrix. Triangular matrices are always square.
|
||||
type Triangular interface {
|
||||
Matrix
|
||||
// Triangle returns the number of rows/columns in the matrix and its
|
||||
// orientation.
|
||||
Triangle() (n int, kind TriKind)
|
||||
|
||||
// TTri is the equivalent of the T() method in the Matrix interface but
|
||||
// guarantees the transpose is of triangular type.
|
||||
TTri() Triangular
|
||||
}
|
||||
|
||||
// A RawTriangular can return a blas64.Triangular representation of the receiver.
|
||||
// Changes to the blas64.Triangular.Data slice will be reflected in the original
|
||||
// matrix, changes to the N, Stride, Uplo and Diag fields will not.
|
||||
type RawTriangular interface {
|
||||
RawTriangular() blas64.Triangular
|
||||
}
|
||||
|
||||
// A MutableTriangular can set elements of a triangular matrix.
|
||||
type MutableTriangular interface {
|
||||
Triangular
|
||||
SetTri(i, j int, v float64)
|
||||
}
|
||||
|
||||
var (
|
||||
_ Matrix = TransposeTri{}
|
||||
_ Triangular = TransposeTri{}
|
||||
_ UntransposeTrier = TransposeTri{}
|
||||
)
|
||||
|
||||
// TransposeTri is a type for performing an implicit transpose of a Triangular
|
||||
// matrix. It implements the Triangular interface, returning values from the
|
||||
// transpose of the matrix within.
|
||||
type TransposeTri struct {
|
||||
Triangular Triangular
|
||||
}
|
||||
|
||||
// At returns the value of the element at row i and column j of the transposed
|
||||
// matrix, that is, row j and column i of the Triangular field.
|
||||
func (t TransposeTri) At(i, j int) float64 {
|
||||
return t.Triangular.At(j, i)
|
||||
}
|
||||
|
||||
// Dims returns the dimensions of the transposed matrix. Triangular matrices are
|
||||
// square and thus this is the same size as the original Triangular.
|
||||
func (t TransposeTri) Dims() (r, c int) {
|
||||
c, r = t.Triangular.Dims()
|
||||
return r, c
|
||||
}
|
||||
|
||||
// T performs an implicit transpose by returning the Triangular field.
|
||||
func (t TransposeTri) T() Matrix {
|
||||
return t.Triangular
|
||||
}
|
||||
|
||||
// Triangle returns the number of rows/columns in the matrix and its orientation.
|
||||
func (t TransposeTri) Triangle() (int, TriKind) {
|
||||
n, upper := t.Triangular.Triangle()
|
||||
return n, !upper
|
||||
}
|
||||
|
||||
// TTri performs an implicit transpose by returning the Triangular field.
|
||||
func (t TransposeTri) TTri() Triangular {
|
||||
return t.Triangular
|
||||
}
|
||||
|
||||
// Untranspose returns the Triangular field.
|
||||
func (t TransposeTri) Untranspose() Matrix {
|
||||
return t.Triangular
|
||||
}
|
||||
|
||||
func (t TransposeTri) UntransposeTri() Triangular {
|
||||
return t.Triangular
|
||||
}
|
||||
|
||||
// NewTriDense creates a new Triangular matrix with n rows and columns. If data == nil,
|
||||
// a new slice is allocated for the backing slice. If len(data) == n*n, data is
|
||||
// used as the backing slice, and changes to the elements of the returned TriDense
|
||||
// will be reflected in data. If neither of these is true, NewTriDense will panic.
|
||||
// NewTriDense will panic if n is zero.
|
||||
//
|
||||
// The data must be arranged in row-major order, i.e. the (i*c + j)-th
|
||||
// element in the data slice is the {i, j}-th element in the matrix.
|
||||
// Only the values in the triangular portion corresponding to kind are used.
|
||||
func NewTriDense(n int, kind TriKind, data []float64) *TriDense {
|
||||
if n <= 0 {
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic("mat: negative dimension")
|
||||
}
|
||||
if data != nil && len(data) != n*n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if data == nil {
|
||||
data = make([]float64, n*n)
|
||||
}
|
||||
uplo := blas.Lower
|
||||
if kind == Upper {
|
||||
uplo = blas.Upper
|
||||
}
|
||||
return &TriDense{
|
||||
mat: blas64.Triangular{
|
||||
N: n,
|
||||
Stride: n,
|
||||
Data: data,
|
||||
Uplo: uplo,
|
||||
Diag: blas.NonUnit,
|
||||
},
|
||||
cap: n,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TriDense) Dims() (r, c int) {
|
||||
return t.mat.N, t.mat.N
|
||||
}
|
||||
|
||||
// Triangle returns the dimension of t and its orientation. The returned
|
||||
// orientation is only valid when n is not zero.
|
||||
func (t *TriDense) Triangle() (n int, kind TriKind) {
|
||||
return t.mat.N, t.triKind()
|
||||
}
|
||||
|
||||
func (t *TriDense) isUpper() bool {
|
||||
return isUpperUplo(t.mat.Uplo)
|
||||
}
|
||||
|
||||
func (t *TriDense) triKind() TriKind {
|
||||
return TriKind(isUpperUplo(t.mat.Uplo))
|
||||
}
|
||||
|
||||
func isUpperUplo(u blas.Uplo) bool {
|
||||
switch u {
|
||||
case blas.Upper:
|
||||
return true
|
||||
case blas.Lower:
|
||||
return false
|
||||
default:
|
||||
panic(badTriangle)
|
||||
}
|
||||
}
|
||||
|
||||
func uploToTriKind(u blas.Uplo) TriKind {
|
||||
switch u {
|
||||
case blas.Upper:
|
||||
return Upper
|
||||
case blas.Lower:
|
||||
return Lower
|
||||
default:
|
||||
panic(badTriangle)
|
||||
}
|
||||
}
|
||||
|
||||
// asSymBlas returns the receiver restructured as a blas64.Symmetric with the
|
||||
// same backing memory. Panics if the receiver is unit.
|
||||
// This returns a blas64.Symmetric and not a *SymDense because SymDense can only
|
||||
// be upper triangular.
|
||||
func (t *TriDense) asSymBlas() blas64.Symmetric {
|
||||
if t.mat.Diag == blas.Unit {
|
||||
panic("mat: cannot convert unit TriDense into blas64.Symmetric")
|
||||
}
|
||||
return blas64.Symmetric{
|
||||
N: t.mat.N,
|
||||
Stride: t.mat.Stride,
|
||||
Data: t.mat.Data,
|
||||
Uplo: t.mat.Uplo,
|
||||
}
|
||||
}
|
||||
|
||||
// T performs an implicit transpose by returning the receiver inside a Transpose.
|
||||
func (t *TriDense) T() Matrix {
|
||||
return Transpose{t}
|
||||
}
|
||||
|
||||
// TTri performs an implicit transpose by returning the receiver inside a TransposeTri.
|
||||
func (t *TriDense) TTri() Triangular {
|
||||
return TransposeTri{t}
|
||||
}
|
||||
|
||||
func (t *TriDense) RawTriangular() blas64.Triangular {
|
||||
return t.mat
|
||||
}
|
||||
|
||||
// SetRawTriangular sets the underlying blas64.Triangular used by the receiver.
|
||||
// Changes to elements in the receiver following the call will be reflected
|
||||
// in the input.
|
||||
//
|
||||
// The supplied Triangular must not use blas.Unit storage format.
|
||||
func (t *TriDense) SetRawTriangular(mat blas64.Triangular) {
|
||||
if mat.Diag == blas.Unit {
|
||||
panic("mat: cannot set TriDense with Unit storage format")
|
||||
}
|
||||
t.mat = mat
|
||||
}
|
||||
|
||||
// Reset zeros the dimensions of the matrix so that it can be reused as the
|
||||
// receiver of a dimensionally restricted operation.
|
||||
//
|
||||
// See the Reseter interface for more information.
|
||||
func (t *TriDense) Reset() {
|
||||
// N and Stride must be zeroed in unison.
|
||||
t.mat.N, t.mat.Stride = 0, 0
|
||||
// Defensively zero Uplo to ensure
|
||||
// it is set correctly later.
|
||||
t.mat.Uplo = 0
|
||||
t.mat.Data = t.mat.Data[:0]
|
||||
}
|
||||
|
||||
// Zero sets all of the matrix elements to zero.
|
||||
func (t *TriDense) Zero() {
|
||||
if t.isUpper() {
|
||||
for i := 0; i < t.mat.N; i++ {
|
||||
zero(t.mat.Data[i*t.mat.Stride+i : i*t.mat.Stride+t.mat.N])
|
||||
}
|
||||
return
|
||||
}
|
||||
for i := 0; i < t.mat.N; i++ {
|
||||
zero(t.mat.Data[i*t.mat.Stride : i*t.mat.Stride+i+1])
|
||||
}
|
||||
}
|
||||
|
||||
// IsZero returns whether the receiver is zero-sized. Zero-sized matrices can be the
|
||||
// receiver for size-restricted operations. TriDense matrices can be zeroed using Reset.
|
||||
func (t *TriDense) IsZero() bool {
|
||||
// It must be the case that t.Dims() returns
|
||||
// zeros in this case. See comment in Reset().
|
||||
return t.mat.Stride == 0
|
||||
}
|
||||
|
||||
// untranspose untransposes a matrix if applicable. If a is an Untransposer, then
|
||||
// untranspose returns the underlying matrix and true. If it is not, then it returns
|
||||
// the input matrix and false.
|
||||
func untransposeTri(a Triangular) (Triangular, bool) {
|
||||
if ut, ok := a.(UntransposeTrier); ok {
|
||||
return ut.UntransposeTri(), true
|
||||
}
|
||||
return a, false
|
||||
}
|
||||
|
||||
// reuseAs resizes a zero receiver to an n×n triangular matrix with the given
|
||||
// orientation. If the receiver is non-zero, reuseAs checks that the receiver
|
||||
// is the correct size and orientation.
|
||||
func (t *TriDense) reuseAs(n int, kind TriKind) {
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
ul := blas.Lower
|
||||
if kind == Upper {
|
||||
ul = blas.Upper
|
||||
}
|
||||
if t.mat.N > t.cap {
|
||||
panic(badTriCap)
|
||||
}
|
||||
if t.IsZero() {
|
||||
t.mat = blas64.Triangular{
|
||||
N: n,
|
||||
Stride: n,
|
||||
Diag: blas.NonUnit,
|
||||
Data: use(t.mat.Data, n*n),
|
||||
Uplo: ul,
|
||||
}
|
||||
t.cap = n
|
||||
return
|
||||
}
|
||||
if t.mat.N != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if t.mat.Uplo != ul {
|
||||
panic(ErrTriangle)
|
||||
}
|
||||
}
|
||||
|
||||
// isolatedWorkspace returns a new TriDense matrix w with the size of a and
|
||||
// returns a callback to defer which performs cleanup at the return of the call.
|
||||
// This should be used when a method receiver is the same pointer as an input argument.
|
||||
func (t *TriDense) isolatedWorkspace(a Triangular) (w *TriDense, restore func()) {
|
||||
n, kind := a.Triangle()
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
w = getWorkspaceTri(n, kind, false)
|
||||
return w, func() {
|
||||
t.Copy(w)
|
||||
putWorkspaceTri(w)
|
||||
}
|
||||
}
|
||||
|
||||
// DiagView returns the diagonal as a matrix backed by the original data.
|
||||
func (t *TriDense) DiagView() Diagonal {
|
||||
if t.mat.Diag == blas.Unit {
|
||||
panic("mat: cannot take view of Unit diagonal")
|
||||
}
|
||||
n := t.mat.N
|
||||
return &DiagDense{
|
||||
mat: blas64.Vector{
|
||||
N: n,
|
||||
Inc: t.mat.Stride + 1,
|
||||
Data: t.mat.Data[:(n-1)*t.mat.Stride+n],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Copy makes a copy of elements of a into the receiver. It is similar to the
|
||||
// built-in copy; it copies as much as the overlap between the two matrices and
|
||||
// returns the number of rows and columns it copied. Only elements within the
|
||||
// receiver's non-zero triangle are set.
|
||||
//
|
||||
// See the Copier interface for more information.
|
||||
func (t *TriDense) Copy(a Matrix) (r, c int) {
|
||||
r, c = a.Dims()
|
||||
r = min(r, t.mat.N)
|
||||
c = min(c, t.mat.N)
|
||||
if r == 0 || c == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
switch a := a.(type) {
|
||||
case RawMatrixer:
|
||||
amat := a.RawMatrix()
|
||||
if t.isUpper() {
|
||||
for i := 0; i < r; i++ {
|
||||
copy(t.mat.Data[i*t.mat.Stride+i:i*t.mat.Stride+c], amat.Data[i*amat.Stride+i:i*amat.Stride+c])
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < r; i++ {
|
||||
copy(t.mat.Data[i*t.mat.Stride:i*t.mat.Stride+i+1], amat.Data[i*amat.Stride:i*amat.Stride+i+1])
|
||||
}
|
||||
}
|
||||
case RawTriangular:
|
||||
amat := a.RawTriangular()
|
||||
aIsUpper := isUpperUplo(amat.Uplo)
|
||||
tIsUpper := t.isUpper()
|
||||
switch {
|
||||
case tIsUpper && aIsUpper:
|
||||
for i := 0; i < r; i++ {
|
||||
copy(t.mat.Data[i*t.mat.Stride+i:i*t.mat.Stride+c], amat.Data[i*amat.Stride+i:i*amat.Stride+c])
|
||||
}
|
||||
case !tIsUpper && !aIsUpper:
|
||||
for i := 0; i < r; i++ {
|
||||
copy(t.mat.Data[i*t.mat.Stride:i*t.mat.Stride+i+1], amat.Data[i*amat.Stride:i*amat.Stride+i+1])
|
||||
}
|
||||
default:
|
||||
for i := 0; i < r; i++ {
|
||||
t.set(i, i, amat.Data[i*amat.Stride+i])
|
||||
}
|
||||
}
|
||||
default:
|
||||
isUpper := t.isUpper()
|
||||
for i := 0; i < r; i++ {
|
||||
if isUpper {
|
||||
for j := i; j < c; j++ {
|
||||
t.set(i, j, a.At(i, j))
|
||||
}
|
||||
} else {
|
||||
for j := 0; j <= i; j++ {
|
||||
t.set(i, j, a.At(i, j))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return r, c
|
||||
}
|
||||
|
||||
// InverseTri computes the inverse of the triangular matrix a, storing the result
|
||||
// into the receiver. If a is ill-conditioned, a Condition error will be returned.
|
||||
// Note that matrix inversion is numerically unstable, and should generally be
|
||||
// avoided where possible, for example by using the Solve routines.
|
||||
func (t *TriDense) InverseTri(a Triangular) error {
|
||||
t.checkOverlapMatrix(a)
|
||||
n, _ := a.Triangle()
|
||||
t.reuseAs(a.Triangle())
|
||||
t.Copy(a)
|
||||
work := getFloats(3*n, false)
|
||||
iwork := getInts(n, false)
|
||||
cond := lapack64.Trcon(CondNorm, t.mat, work, iwork)
|
||||
putFloats(work)
|
||||
putInts(iwork)
|
||||
if math.IsInf(cond, 1) {
|
||||
return Condition(cond)
|
||||
}
|
||||
ok := lapack64.Trtri(t.mat)
|
||||
if !ok {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
if cond > ConditionTolerance {
|
||||
return Condition(cond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MulTri takes the product of triangular matrices a and b and places the result
|
||||
// in the receiver. The size of a and b must match, and they both must have the
|
||||
// same TriKind, or Mul will panic.
|
||||
func (t *TriDense) MulTri(a, b Triangular) {
|
||||
n, kind := a.Triangle()
|
||||
nb, kindb := b.Triangle()
|
||||
if n != nb {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if kind != kindb {
|
||||
panic(ErrTriangle)
|
||||
}
|
||||
|
||||
aU, _ := untransposeTri(a)
|
||||
bU, _ := untransposeTri(b)
|
||||
t.checkOverlapMatrix(bU)
|
||||
t.checkOverlapMatrix(aU)
|
||||
t.reuseAs(n, kind)
|
||||
var restore func()
|
||||
if t == aU {
|
||||
t, restore = t.isolatedWorkspace(aU)
|
||||
defer restore()
|
||||
} else if t == bU {
|
||||
t, restore = t.isolatedWorkspace(bU)
|
||||
defer restore()
|
||||
}
|
||||
|
||||
// TODO(btracey): Improve the set of fast-paths.
|
||||
if kind == Upper {
|
||||
for i := 0; i < n; i++ {
|
||||
for j := i; j < n; j++ {
|
||||
var v float64
|
||||
for k := i; k <= j; k++ {
|
||||
v += a.At(i, k) * b.At(k, j)
|
||||
}
|
||||
t.SetTri(i, j, v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
for j := 0; j <= i; j++ {
|
||||
var v float64
|
||||
for k := j; k <= i; k++ {
|
||||
v += a.At(i, k) * b.At(k, j)
|
||||
}
|
||||
t.SetTri(i, j, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ScaleTri multiplies the elements of a by f, placing the result in the receiver.
|
||||
// If the receiver is non-zero, the size and kind of the receiver must match
|
||||
// the input, or ScaleTri will panic.
|
||||
func (t *TriDense) ScaleTri(f float64, a Triangular) {
|
||||
n, kind := a.Triangle()
|
||||
t.reuseAs(n, kind)
|
||||
|
||||
// TODO(btracey): Improve the set of fast-paths.
|
||||
switch a := a.(type) {
|
||||
case RawTriangular:
|
||||
amat := a.RawTriangular()
|
||||
if t != a {
|
||||
t.checkOverlap(generalFromTriangular(amat))
|
||||
}
|
||||
if kind == Upper {
|
||||
for i := 0; i < n; i++ {
|
||||
ts := t.mat.Data[i*t.mat.Stride+i : i*t.mat.Stride+n]
|
||||
as := amat.Data[i*amat.Stride+i : i*amat.Stride+n]
|
||||
for i, v := range as {
|
||||
ts[i] = v * f
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
ts := t.mat.Data[i*t.mat.Stride : i*t.mat.Stride+i+1]
|
||||
as := amat.Data[i*amat.Stride : i*amat.Stride+i+1]
|
||||
for i, v := range as {
|
||||
ts[i] = v * f
|
||||
}
|
||||
}
|
||||
return
|
||||
default:
|
||||
t.checkOverlapMatrix(a)
|
||||
isUpper := kind == Upper
|
||||
for i := 0; i < n; i++ {
|
||||
if isUpper {
|
||||
for j := i; j < n; j++ {
|
||||
t.set(i, j, f*a.At(i, j))
|
||||
}
|
||||
} else {
|
||||
for j := 0; j <= i; j++ {
|
||||
t.set(i, j, f*a.At(i, j))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trace returns the trace of the matrix.
|
||||
func (t *TriDense) Trace() float64 {
|
||||
// TODO(btracey): could use internal asm sum routine.
|
||||
var v float64
|
||||
for i := 0; i < t.mat.N; i++ {
|
||||
v += t.mat.Data[i*t.mat.Stride+i]
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// copySymIntoTriangle copies a symmetric matrix into a TriDense
|
||||
func copySymIntoTriangle(t *TriDense, s Symmetric) {
|
||||
n, upper := t.Triangle()
|
||||
ns := s.Symmetric()
|
||||
if n != ns {
|
||||
panic("mat: triangle size mismatch")
|
||||
}
|
||||
ts := t.mat.Stride
|
||||
if rs, ok := s.(RawSymmetricer); ok {
|
||||
sd := rs.RawSymmetric()
|
||||
ss := sd.Stride
|
||||
if upper {
|
||||
if sd.Uplo == blas.Upper {
|
||||
for i := 0; i < n; i++ {
|
||||
copy(t.mat.Data[i*ts+i:i*ts+n], sd.Data[i*ss+i:i*ss+n])
|
||||
}
|
||||
return
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
for j := i; j < n; j++ {
|
||||
t.mat.Data[i*ts+j] = sd.Data[j*ss+i]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if sd.Uplo == blas.Upper {
|
||||
for i := 0; i < n; i++ {
|
||||
for j := 0; j <= i; j++ {
|
||||
t.mat.Data[i*ts+j] = sd.Data[j*ss+i]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
copy(t.mat.Data[i*ts:i*ts+i+1], sd.Data[i*ss:i*ss+i+1])
|
||||
}
|
||||
return
|
||||
}
|
||||
if upper {
|
||||
for i := 0; i < n; i++ {
|
||||
for j := i; j < n; j++ {
|
||||
t.mat.Data[i*ts+j] = s.At(i, j)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
for j := 0; j <= i; j++ {
|
||||
t.mat.Data[i*ts+j] = s.At(i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DoNonZero calls the function fn for each of the non-zero elements of t. The function fn
|
||||
// takes a row/column index and the element value of t at (i, j).
|
||||
func (t *TriDense) DoNonZero(fn func(i, j int, v float64)) {
|
||||
if t.isUpper() {
|
||||
for i := 0; i < t.mat.N; i++ {
|
||||
for j := i; j < t.mat.N; j++ {
|
||||
v := t.at(i, j)
|
||||
if v != 0 {
|
||||
fn(i, j, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
for i := 0; i < t.mat.N; i++ {
|
||||
for j := 0; j <= i; j++ {
|
||||
v := t.at(i, j)
|
||||
if v != 0 {
|
||||
fn(i, j, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DoRowNonZero calls the function fn for each of the non-zero elements of row i of t. The function fn
|
||||
// takes a row/column index and the element value of t at (i, j).
|
||||
func (t *TriDense) DoRowNonZero(i int, fn func(i, j int, v float64)) {
|
||||
if i < 0 || t.mat.N <= i {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if t.isUpper() {
|
||||
for j := i; j < t.mat.N; j++ {
|
||||
v := t.at(i, j)
|
||||
if v != 0 {
|
||||
fn(i, j, v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
for j := 0; j <= i; j++ {
|
||||
v := t.at(i, j)
|
||||
if v != 0 {
|
||||
fn(i, j, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DoColNonZero calls the function fn for each of the non-zero elements of column j of t. The function fn
|
||||
// takes a row/column index and the element value of t at (i, j).
|
||||
func (t *TriDense) DoColNonZero(j int, fn func(i, j int, v float64)) {
|
||||
if j < 0 || t.mat.N <= j {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
if t.isUpper() {
|
||||
for i := 0; i <= j; i++ {
|
||||
v := t.at(i, j)
|
||||
if v != 0 {
|
||||
fn(i, j, v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
for i := j; i < t.mat.N; i++ {
|
||||
v := t.at(i, j)
|
||||
if v != 0 {
|
||||
fn(i, j, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
353
vendor/gonum.org/v1/gonum/mat/triband.go
generated
vendored
Normal file
353
vendor/gonum.org/v1/gonum/mat/triband.go
generated
vendored
Normal file
|
|
@ -0,0 +1,353 @@
|
|||
// Copyright ©2018 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
)
|
||||
|
||||
var (
|
||||
triBand TriBanded
|
||||
_ Banded = triBand
|
||||
_ Triangular = triBand
|
||||
|
||||
triBandDense *TriBandDense
|
||||
_ Matrix = triBandDense
|
||||
_ Triangular = triBandDense
|
||||
_ Banded = triBandDense
|
||||
_ TriBanded = triBandDense
|
||||
_ RawTriBander = triBandDense
|
||||
_ MutableTriBanded = triBandDense
|
||||
)
|
||||
|
||||
// TriBanded is a triangular band matrix interface type.
|
||||
type TriBanded interface {
|
||||
Banded
|
||||
|
||||
// Triangle returns the number of rows/columns in the matrix and its
|
||||
// orientation.
|
||||
Triangle() (n int, kind TriKind)
|
||||
|
||||
// TTri is the equivalent of the T() method in the Matrix interface but
|
||||
// guarantees the transpose is of triangular type.
|
||||
TTri() Triangular
|
||||
|
||||
// TriBand returns the number of rows/columns in the matrix, the
|
||||
// size of the bandwidth, and the orientation.
|
||||
TriBand() (n, k int, kind TriKind)
|
||||
|
||||
// TTriBand is the equivalent of the T() method in the Matrix interface but
|
||||
// guarantees the transpose is of banded triangular type.
|
||||
TTriBand() TriBanded
|
||||
}
|
||||
|
||||
// A RawTriBander can return a blas64.TriangularBand representation of the receiver.
|
||||
// Changes to the blas64.TriangularBand.Data slice will be reflected in the original
|
||||
// matrix, changes to the N, K, Stride, Uplo and Diag fields will not.
|
||||
type RawTriBander interface {
|
||||
RawTriBand() blas64.TriangularBand
|
||||
}
|
||||
|
||||
// MutableTriBanded is a triangular band matrix interface type that allows
|
||||
// elements to be altered.
|
||||
type MutableTriBanded interface {
|
||||
TriBanded
|
||||
SetTriBand(i, j int, v float64)
|
||||
}
|
||||
|
||||
var (
|
||||
tTriBand TransposeTriBand
|
||||
_ Matrix = tTriBand
|
||||
_ TriBanded = tTriBand
|
||||
_ Untransposer = tTriBand
|
||||
_ UntransposeTrier = tTriBand
|
||||
_ UntransposeBander = tTriBand
|
||||
_ UntransposeTriBander = tTriBand
|
||||
)
|
||||
|
||||
// TransposeTriBand is a type for performing an implicit transpose of a TriBanded
|
||||
// matrix. It implements the TriBanded interface, returning values from the
|
||||
// transpose of the matrix within.
|
||||
type TransposeTriBand struct {
|
||||
TriBanded TriBanded
|
||||
}
|
||||
|
||||
// At returns the value of the element at row i and column j of the transposed
|
||||
// matrix, that is, row j and column i of the TriBanded field.
|
||||
func (t TransposeTriBand) At(i, j int) float64 {
|
||||
return t.TriBanded.At(j, i)
|
||||
}
|
||||
|
||||
// Dims returns the dimensions of the transposed matrix. TriBanded matrices are
|
||||
// square and thus this is the same size as the original TriBanded.
|
||||
func (t TransposeTriBand) Dims() (r, c int) {
|
||||
c, r = t.TriBanded.Dims()
|
||||
return r, c
|
||||
}
|
||||
|
||||
// T performs an implicit transpose by returning the TriBand field.
|
||||
func (t TransposeTriBand) T() Matrix {
|
||||
return t.TriBanded
|
||||
}
|
||||
|
||||
// Triangle returns the number of rows/columns in the matrix and its orientation.
|
||||
func (t TransposeTriBand) Triangle() (int, TriKind) {
|
||||
n, upper := t.TriBanded.Triangle()
|
||||
return n, !upper
|
||||
}
|
||||
|
||||
// TTri performs an implicit transpose by returning the TriBand field.
|
||||
func (t TransposeTriBand) TTri() Triangular {
|
||||
return t.TriBanded
|
||||
}
|
||||
|
||||
// Bandwidth returns the upper and lower bandwidths of the matrix.
|
||||
func (t TransposeTriBand) Bandwidth() (kl, ku int) {
|
||||
kl, ku = t.TriBanded.Bandwidth()
|
||||
return ku, kl
|
||||
}
|
||||
|
||||
// TBand performs an implicit transpose by returning the TriBand field.
|
||||
func (t TransposeTriBand) TBand() Banded {
|
||||
return t.TriBanded
|
||||
}
|
||||
|
||||
// TriBand returns the number of rows/columns in the matrix, the
|
||||
// size of the bandwidth, and the orientation.
|
||||
func (t TransposeTriBand) TriBand() (n, k int, kind TriKind) {
|
||||
n, k, kind = t.TriBanded.TriBand()
|
||||
return n, k, !kind
|
||||
}
|
||||
|
||||
// TTriBand performs an implicit transpose by returning the TriBand field.
|
||||
func (t TransposeTriBand) TTriBand() TriBanded {
|
||||
return t.TriBanded
|
||||
}
|
||||
|
||||
// Untranspose returns the Triangular field.
|
||||
func (t TransposeTriBand) Untranspose() Matrix {
|
||||
return t.TriBanded
|
||||
}
|
||||
|
||||
// UntransposeTri returns the underlying Triangular matrix.
|
||||
func (t TransposeTriBand) UntransposeTri() Triangular {
|
||||
return t.TriBanded
|
||||
}
|
||||
|
||||
// UntransposeBand returns the underlying Banded matrix.
|
||||
func (t TransposeTriBand) UntransposeBand() Banded {
|
||||
return t.TriBanded
|
||||
}
|
||||
|
||||
// UntransposeTriBand returns the underlying TriBanded matrix.
|
||||
func (t TransposeTriBand) UntransposeTriBand() TriBanded {
|
||||
return t.TriBanded
|
||||
}
|
||||
|
||||
// TriBandDense represents a triangular band matrix in dense storage format.
|
||||
type TriBandDense struct {
|
||||
mat blas64.TriangularBand
|
||||
}
|
||||
|
||||
// NewTriBandDense creates a new triangular banded matrix with n rows and columns,
|
||||
// k bands in the direction of the specified kind. If data == nil,
|
||||
// a new slice is allocated for the backing slice. If len(data) == n*(k+1),
|
||||
// data is used as the backing slice, and changes to the elements of the returned
|
||||
// TriBandDense will be reflected in data. If neither of these is true, NewTriBandDense
|
||||
// will panic. k must be at least zero and less than n, otherwise NewTriBandDense will panic.
|
||||
//
|
||||
// The data must be arranged in row-major order constructed by removing the zeros
|
||||
// from the rows outside the band and aligning the diagonals. For example, if
|
||||
// the upper-triangular banded matrix
|
||||
// 1 2 3 0 0 0
|
||||
// 0 4 5 6 0 0
|
||||
// 0 0 7 8 9 0
|
||||
// 0 0 0 10 11 12
|
||||
// 0 0 0 0 13 14
|
||||
// 0 0 0 0 0 15
|
||||
// becomes (* entries are never accessed)
|
||||
// 1 2 3
|
||||
// 4 5 6
|
||||
// 7 8 9
|
||||
// 10 11 12
|
||||
// 13 14 *
|
||||
// 15 * *
|
||||
// which is passed to NewTriBandDense as []float64{1, 2, ..., 15, *, *, *}
|
||||
// with k=2 and kind = mat.Upper.
|
||||
// The lower triangular banded matrix
|
||||
// 1 0 0 0 0 0
|
||||
// 2 3 0 0 0 0
|
||||
// 4 5 6 0 0 0
|
||||
// 0 7 8 9 0 0
|
||||
// 0 0 10 11 12 0
|
||||
// 0 0 0 13 14 15
|
||||
// becomes (* entries are never accessed)
|
||||
// * * 1
|
||||
// * 2 3
|
||||
// 4 5 6
|
||||
// 7 8 9
|
||||
// 10 11 12
|
||||
// 13 14 15
|
||||
// which is passed to NewTriBandDense as []float64{*, *, *, 1, 2, ..., 15}
|
||||
// with k=2 and kind = mat.Lower.
|
||||
// Only the values in the band portion of the matrix are used.
|
||||
func NewTriBandDense(n, k int, kind TriKind, data []float64) *TriBandDense {
|
||||
if n <= 0 || k < 0 {
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic("mat: negative dimension")
|
||||
}
|
||||
if k+1 > n {
|
||||
panic("mat: band out of range")
|
||||
}
|
||||
bc := k + 1
|
||||
if data != nil && len(data) != n*bc {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if data == nil {
|
||||
data = make([]float64, n*bc)
|
||||
}
|
||||
uplo := blas.Lower
|
||||
if kind {
|
||||
uplo = blas.Upper
|
||||
}
|
||||
return &TriBandDense{
|
||||
mat: blas64.TriangularBand{
|
||||
Uplo: uplo,
|
||||
Diag: blas.NonUnit,
|
||||
N: n,
|
||||
K: k,
|
||||
Data: data,
|
||||
Stride: bc,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Dims returns the number of rows and columns in the matrix.
|
||||
func (t *TriBandDense) Dims() (r, c int) {
|
||||
return t.mat.N, t.mat.N
|
||||
}
|
||||
|
||||
// T performs an implicit transpose by returning the receiver inside a Transpose.
|
||||
func (t *TriBandDense) T() Matrix {
|
||||
return Transpose{t}
|
||||
}
|
||||
|
||||
// IsZero returns whether the receiver is zero-sized. Zero-sized matrices can be the
|
||||
// receiver for size-restricted operations. TriBandDense matrices can be zeroed using Reset.
|
||||
func (t *TriBandDense) IsZero() bool {
|
||||
// It must be the case that t.Dims() returns
|
||||
// zeros in this case. See comment in Reset().
|
||||
return t.mat.Stride == 0
|
||||
}
|
||||
|
||||
// Reset zeros the dimensions of the matrix so that it can be reused as the
|
||||
// receiver of a dimensionally restricted operation.
|
||||
//
|
||||
// See the Reseter interface for more information.
|
||||
func (t *TriBandDense) Reset() {
|
||||
t.mat.N = 0
|
||||
t.mat.Stride = 0
|
||||
t.mat.K = 0
|
||||
t.mat.Data = t.mat.Data[:0]
|
||||
}
|
||||
|
||||
// Zero sets all of the matrix elements to zero.
|
||||
func (t *TriBandDense) Zero() {
|
||||
if t.isUpper() {
|
||||
for i := 0; i < t.mat.N; i++ {
|
||||
u := min(1+t.mat.K, t.mat.N-i)
|
||||
zero(t.mat.Data[i*t.mat.Stride : i*t.mat.Stride+u])
|
||||
}
|
||||
return
|
||||
}
|
||||
for i := 0; i < t.mat.N; i++ {
|
||||
l := max(0, t.mat.K-i)
|
||||
zero(t.mat.Data[i*t.mat.Stride+l : i*t.mat.Stride+t.mat.K+1])
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TriBandDense) isUpper() bool {
|
||||
return isUpperUplo(t.mat.Uplo)
|
||||
}
|
||||
|
||||
func (t *TriBandDense) triKind() TriKind {
|
||||
return TriKind(isUpperUplo(t.mat.Uplo))
|
||||
}
|
||||
|
||||
// Triangle returns the dimension of t and its orientation. The returned
|
||||
// orientation is only valid when n is not zero.
|
||||
func (t *TriBandDense) Triangle() (n int, kind TriKind) {
|
||||
return t.mat.N, t.triKind()
|
||||
}
|
||||
|
||||
// TTri performs an implicit transpose by returning the receiver inside a TransposeTri.
|
||||
func (t *TriBandDense) TTri() Triangular {
|
||||
return TransposeTri{t}
|
||||
}
|
||||
|
||||
// Bandwidth returns the upper and lower bandwidths of the matrix.
|
||||
func (t *TriBandDense) Bandwidth() (kl, ku int) {
|
||||
if t.isUpper() {
|
||||
return 0, t.mat.K
|
||||
}
|
||||
return t.mat.K, 0
|
||||
}
|
||||
|
||||
// TBand performs an implicit transpose by returning the receiver inside a TransposeBand.
|
||||
func (t *TriBandDense) TBand() Banded {
|
||||
return TransposeBand{t}
|
||||
}
|
||||
|
||||
// TriBand returns the number of rows/columns in the matrix, the
|
||||
// size of the bandwidth, and the orientation.
|
||||
func (t *TriBandDense) TriBand() (n, k int, kind TriKind) {
|
||||
return t.mat.N, t.mat.K, TriKind(!t.IsZero()) && t.triKind()
|
||||
}
|
||||
|
||||
// TTriBand performs an implicit transpose by returning the receiver inside a TransposeTriBand.
|
||||
func (t *TriBandDense) TTriBand() TriBanded {
|
||||
return TransposeTriBand{t}
|
||||
}
|
||||
|
||||
// RawTriBand returns the underlying blas64.TriangularBand used by the receiver.
|
||||
// Changes to the blas64.TriangularBand.Data slice will be reflected in the original
|
||||
// matrix, changes to the N, K, Stride, Uplo and Diag fields will not.
|
||||
func (t *TriBandDense) RawTriBand() blas64.TriangularBand {
|
||||
return t.mat
|
||||
}
|
||||
|
||||
// SetRawTriBand sets the underlying blas64.TriangularBand used by the receiver.
|
||||
// Changes to elements in the receiver following the call will be reflected
|
||||
// in the input.
|
||||
//
|
||||
// The supplied TriangularBand must not use blas.Unit storage format.
|
||||
func (t *TriBandDense) SetRawTriBand(mat blas64.TriangularBand) {
|
||||
if mat.Diag == blas.Unit {
|
||||
panic("mat: cannot set TriBand with Unit storage")
|
||||
}
|
||||
t.mat = mat
|
||||
}
|
||||
|
||||
// DiagView returns the diagonal as a matrix backed by the original data.
|
||||
func (t *TriBandDense) DiagView() Diagonal {
|
||||
if t.mat.Diag == blas.Unit {
|
||||
panic("mat: cannot take view of Unit diagonal")
|
||||
}
|
||||
n := t.mat.N
|
||||
data := t.mat.Data
|
||||
if !t.isUpper() {
|
||||
data = data[t.mat.K:]
|
||||
}
|
||||
return &DiagDense{
|
||||
mat: blas64.Vector{
|
||||
N: n,
|
||||
Inc: t.mat.Stride,
|
||||
Data: data[:(n-1)*t.mat.Stride+1],
|
||||
},
|
||||
}
|
||||
}
|
||||
741
vendor/gonum.org/v1/gonum/mat/vector.go
generated
vendored
Normal file
741
vendor/gonum.org/v1/gonum/mat/vector.go
generated
vendored
Normal file
|
|
@ -0,0 +1,741 @@
|
|||
// Copyright ©2013 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mat
|
||||
|
||||
import (
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
"gonum.org/v1/gonum/internal/asm/f64"
|
||||
)
|
||||
|
||||
var (
|
||||
vector *VecDense
|
||||
|
||||
_ Matrix = vector
|
||||
_ Vector = vector
|
||||
_ Reseter = vector
|
||||
)
|
||||
|
||||
// Vector is a vector.
|
||||
type Vector interface {
|
||||
Matrix
|
||||
AtVec(int) float64
|
||||
Len() int
|
||||
}
|
||||
|
||||
// TransposeVec is a type for performing an implicit transpose of a Vector.
|
||||
// It implements the Vector interface, returning values from the transpose
|
||||
// of the vector within.
|
||||
type TransposeVec struct {
|
||||
Vector Vector
|
||||
}
|
||||
|
||||
// At returns the value of the element at row i and column j of the transposed
|
||||
// matrix, that is, row j and column i of the Vector field.
|
||||
func (t TransposeVec) At(i, j int) float64 {
|
||||
return t.Vector.At(j, i)
|
||||
}
|
||||
|
||||
// AtVec returns the element at position i. It panics if i is out of bounds.
|
||||
func (t TransposeVec) AtVec(i int) float64 {
|
||||
return t.Vector.AtVec(i)
|
||||
}
|
||||
|
||||
// Dims returns the dimensions of the transposed vector.
|
||||
func (t TransposeVec) Dims() (r, c int) {
|
||||
c, r = t.Vector.Dims()
|
||||
return r, c
|
||||
}
|
||||
|
||||
// T performs an implicit transpose by returning the Vector field.
|
||||
func (t TransposeVec) T() Matrix {
|
||||
return t.Vector
|
||||
}
|
||||
|
||||
// Len returns the number of columns in the vector.
|
||||
func (t TransposeVec) Len() int {
|
||||
return t.Vector.Len()
|
||||
}
|
||||
|
||||
// TVec performs an implicit transpose by returning the Vector field.
|
||||
func (t TransposeVec) TVec() Vector {
|
||||
return t.Vector
|
||||
}
|
||||
|
||||
// Untranspose returns the Vector field.
|
||||
func (t TransposeVec) Untranspose() Matrix {
|
||||
return t.Vector
|
||||
}
|
||||
|
||||
func (t TransposeVec) UntransposeVec() Vector {
|
||||
return t.Vector
|
||||
}
|
||||
|
||||
// VecDense represents a column vector.
|
||||
type VecDense struct {
|
||||
mat blas64.Vector
|
||||
// A BLAS vector can have a negative increment, but allowing this
|
||||
// in the mat type complicates a lot of code, and doesn't gain anything.
|
||||
// VecDense must have positive increment in this package.
|
||||
}
|
||||
|
||||
// NewVecDense creates a new VecDense of length n. If data == nil,
|
||||
// a new slice is allocated for the backing slice. If len(data) == n, data is
|
||||
// used as the backing slice, and changes to the elements of the returned VecDense
|
||||
// will be reflected in data. If neither of these is true, NewVecDense will panic.
|
||||
// NewVecDense will panic if n is zero.
|
||||
func NewVecDense(n int, data []float64) *VecDense {
|
||||
if n <= 0 {
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic("mat: negative dimension")
|
||||
}
|
||||
if len(data) != n && data != nil {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if data == nil {
|
||||
data = make([]float64, n)
|
||||
}
|
||||
return &VecDense{
|
||||
mat: blas64.Vector{
|
||||
N: n,
|
||||
Inc: 1,
|
||||
Data: data,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SliceVec returns a new Vector that shares backing data with the receiver.
|
||||
// The returned matrix starts at i of the receiver and extends k-i elements.
|
||||
// SliceVec panics with ErrIndexOutOfRange if the slice is outside the capacity
|
||||
// of the receiver.
|
||||
func (v *VecDense) SliceVec(i, k int) Vector {
|
||||
if i < 0 || k <= i || v.Cap() < k {
|
||||
panic(ErrIndexOutOfRange)
|
||||
}
|
||||
return &VecDense{
|
||||
mat: blas64.Vector{
|
||||
N: k - i,
|
||||
Inc: v.mat.Inc,
|
||||
Data: v.mat.Data[i*v.mat.Inc : (k-1)*v.mat.Inc+1],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Dims returns the number of rows and columns in the matrix. Columns is always 1
|
||||
// for a non-Reset vector.
|
||||
func (v *VecDense) Dims() (r, c int) {
|
||||
if v.IsZero() {
|
||||
return 0, 0
|
||||
}
|
||||
return v.mat.N, 1
|
||||
}
|
||||
|
||||
// Caps returns the number of rows and columns in the backing matrix. Columns is always 1
|
||||
// for a non-Reset vector.
|
||||
func (v *VecDense) Caps() (r, c int) {
|
||||
if v.IsZero() {
|
||||
return 0, 0
|
||||
}
|
||||
return v.Cap(), 1
|
||||
}
|
||||
|
||||
// Len returns the length of the vector.
|
||||
func (v *VecDense) Len() int {
|
||||
return v.mat.N
|
||||
}
|
||||
|
||||
// Cap returns the capacity of the vector.
|
||||
func (v *VecDense) Cap() int {
|
||||
if v.IsZero() {
|
||||
return 0
|
||||
}
|
||||
return (cap(v.mat.Data)-1)/v.mat.Inc + 1
|
||||
}
|
||||
|
||||
// T performs an implicit transpose by returning the receiver inside a Transpose.
|
||||
func (v *VecDense) T() Matrix {
|
||||
return Transpose{v}
|
||||
}
|
||||
|
||||
// TVec performs an implicit transpose by returning the receiver inside a TransposeVec.
|
||||
func (v *VecDense) TVec() Vector {
|
||||
return TransposeVec{v}
|
||||
}
|
||||
|
||||
// Reset zeros the length of the vector so that it can be reused as the
|
||||
// receiver of a dimensionally restricted operation.
|
||||
//
|
||||
// See the Reseter interface for more information.
|
||||
func (v *VecDense) Reset() {
|
||||
// No change of Inc or N to 0 may be
|
||||
// made unless both are set to 0.
|
||||
v.mat.Inc = 0
|
||||
v.mat.N = 0
|
||||
v.mat.Data = v.mat.Data[:0]
|
||||
}
|
||||
|
||||
// Zero sets all of the matrix elements to zero.
|
||||
func (v *VecDense) Zero() {
|
||||
for i := 0; i < v.mat.N; i++ {
|
||||
v.mat.Data[v.mat.Inc*i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// CloneVec makes a copy of a into the receiver, overwriting the previous value
|
||||
// of the receiver.
|
||||
func (v *VecDense) CloneVec(a Vector) {
|
||||
if v == a {
|
||||
return
|
||||
}
|
||||
n := a.Len()
|
||||
v.mat = blas64.Vector{
|
||||
N: n,
|
||||
Inc: 1,
|
||||
Data: use(v.mat.Data, n),
|
||||
}
|
||||
if r, ok := a.(RawVectorer); ok {
|
||||
blas64.Copy(r.RawVector(), v.mat)
|
||||
return
|
||||
}
|
||||
for i := 0; i < a.Len(); i++ {
|
||||
v.SetVec(i, a.AtVec(i))
|
||||
}
|
||||
}
|
||||
|
||||
// VecDenseCopyOf returns a newly allocated copy of the elements of a.
|
||||
func VecDenseCopyOf(a Vector) *VecDense {
|
||||
v := &VecDense{}
|
||||
v.CloneVec(a)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *VecDense) RawVector() blas64.Vector {
|
||||
return v.mat
|
||||
}
|
||||
|
||||
// CopyVec makes a copy of elements of a into the receiver. It is similar to the
|
||||
// built-in copy; it copies as much as the overlap between the two vectors and
|
||||
// returns the number of elements it copied.
|
||||
func (v *VecDense) CopyVec(a Vector) int {
|
||||
n := min(v.Len(), a.Len())
|
||||
if v == a {
|
||||
return n
|
||||
}
|
||||
if r, ok := a.(RawVectorer); ok {
|
||||
blas64.Copy(r.RawVector(), v.mat)
|
||||
return n
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
v.setVec(i, a.AtVec(i))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// ScaleVec scales the vector a by alpha, placing the result in the receiver.
|
||||
func (v *VecDense) ScaleVec(alpha float64, a Vector) {
|
||||
n := a.Len()
|
||||
|
||||
if v == a {
|
||||
if v.mat.Inc == 1 {
|
||||
f64.ScalUnitary(alpha, v.mat.Data)
|
||||
return
|
||||
}
|
||||
f64.ScalInc(alpha, v.mat.Data, uintptr(n), uintptr(v.mat.Inc))
|
||||
return
|
||||
}
|
||||
|
||||
v.reuseAs(n)
|
||||
|
||||
if rv, ok := a.(RawVectorer); ok {
|
||||
mat := rv.RawVector()
|
||||
v.checkOverlap(mat)
|
||||
if v.mat.Inc == 1 && mat.Inc == 1 {
|
||||
f64.ScalUnitaryTo(v.mat.Data, alpha, mat.Data)
|
||||
return
|
||||
}
|
||||
f64.ScalIncTo(v.mat.Data, uintptr(v.mat.Inc),
|
||||
alpha, mat.Data, uintptr(n), uintptr(mat.Inc))
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
v.setVec(i, alpha*a.AtVec(i))
|
||||
}
|
||||
}
|
||||
|
||||
// AddScaledVec adds the vectors a and alpha*b, placing the result in the receiver.
|
||||
func (v *VecDense) AddScaledVec(a Vector, alpha float64, b Vector) {
|
||||
if alpha == 1 {
|
||||
v.AddVec(a, b)
|
||||
return
|
||||
}
|
||||
if alpha == -1 {
|
||||
v.SubVec(a, b)
|
||||
return
|
||||
}
|
||||
|
||||
ar := a.Len()
|
||||
br := b.Len()
|
||||
|
||||
if ar != br {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
var amat, bmat blas64.Vector
|
||||
fast := true
|
||||
aU, _ := untranspose(a)
|
||||
if rv, ok := aU.(RawVectorer); ok {
|
||||
amat = rv.RawVector()
|
||||
if v != a {
|
||||
v.checkOverlap(amat)
|
||||
}
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
bU, _ := untranspose(b)
|
||||
if rv, ok := bU.(RawVectorer); ok {
|
||||
bmat = rv.RawVector()
|
||||
if v != b {
|
||||
v.checkOverlap(bmat)
|
||||
}
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
|
||||
v.reuseAs(ar)
|
||||
|
||||
switch {
|
||||
case alpha == 0: // v <- a
|
||||
if v == a {
|
||||
return
|
||||
}
|
||||
v.CopyVec(a)
|
||||
case v == a && v == b: // v <- v + alpha * v = (alpha + 1) * v
|
||||
blas64.Scal(alpha+1, v.mat)
|
||||
case !fast: // v <- a + alpha * b without blas64 support.
|
||||
for i := 0; i < ar; i++ {
|
||||
v.setVec(i, a.AtVec(i)+alpha*b.AtVec(i))
|
||||
}
|
||||
case v == a && v != b: // v <- v + alpha * b
|
||||
if v.mat.Inc == 1 && bmat.Inc == 1 {
|
||||
// Fast path for a common case.
|
||||
f64.AxpyUnitaryTo(v.mat.Data, alpha, bmat.Data, amat.Data)
|
||||
} else {
|
||||
f64.AxpyInc(alpha, bmat.Data, v.mat.Data,
|
||||
uintptr(ar), uintptr(bmat.Inc), uintptr(v.mat.Inc), 0, 0)
|
||||
}
|
||||
default: // v <- a + alpha * b or v <- a + alpha * v
|
||||
if v.mat.Inc == 1 && amat.Inc == 1 && bmat.Inc == 1 {
|
||||
// Fast path for a common case.
|
||||
f64.AxpyUnitaryTo(v.mat.Data, alpha, bmat.Data, amat.Data)
|
||||
} else {
|
||||
f64.AxpyIncTo(v.mat.Data, uintptr(v.mat.Inc), 0,
|
||||
alpha, bmat.Data, amat.Data,
|
||||
uintptr(ar), uintptr(bmat.Inc), uintptr(amat.Inc), 0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddVec adds the vectors a and b, placing the result in the receiver.
|
||||
func (v *VecDense) AddVec(a, b Vector) {
|
||||
ar := a.Len()
|
||||
br := b.Len()
|
||||
|
||||
if ar != br {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
v.reuseAs(ar)
|
||||
|
||||
aU, _ := untranspose(a)
|
||||
bU, _ := untranspose(b)
|
||||
|
||||
if arv, ok := aU.(RawVectorer); ok {
|
||||
if brv, ok := bU.(RawVectorer); ok {
|
||||
amat := arv.RawVector()
|
||||
bmat := brv.RawVector()
|
||||
|
||||
if v != a {
|
||||
v.checkOverlap(amat)
|
||||
}
|
||||
if v != b {
|
||||
v.checkOverlap(bmat)
|
||||
}
|
||||
|
||||
if v.mat.Inc == 1 && amat.Inc == 1 && bmat.Inc == 1 {
|
||||
// Fast path for a common case.
|
||||
f64.AxpyUnitaryTo(v.mat.Data, 1, bmat.Data, amat.Data)
|
||||
return
|
||||
}
|
||||
f64.AxpyIncTo(v.mat.Data, uintptr(v.mat.Inc), 0,
|
||||
1, bmat.Data, amat.Data,
|
||||
uintptr(ar), uintptr(bmat.Inc), uintptr(amat.Inc), 0, 0)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < ar; i++ {
|
||||
v.setVec(i, a.AtVec(i)+b.AtVec(i))
|
||||
}
|
||||
}
|
||||
|
||||
// SubVec subtracts the vector b from a, placing the result in the receiver.
|
||||
func (v *VecDense) SubVec(a, b Vector) {
|
||||
ar := a.Len()
|
||||
br := b.Len()
|
||||
|
||||
if ar != br {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
v.reuseAs(ar)
|
||||
|
||||
aU, _ := untranspose(a)
|
||||
bU, _ := untranspose(b)
|
||||
|
||||
if arv, ok := aU.(RawVectorer); ok {
|
||||
if brv, ok := bU.(RawVectorer); ok {
|
||||
amat := arv.RawVector()
|
||||
bmat := brv.RawVector()
|
||||
|
||||
if v != a {
|
||||
v.checkOverlap(amat)
|
||||
}
|
||||
if v != b {
|
||||
v.checkOverlap(bmat)
|
||||
}
|
||||
|
||||
if v.mat.Inc == 1 && amat.Inc == 1 && bmat.Inc == 1 {
|
||||
// Fast path for a common case.
|
||||
f64.AxpyUnitaryTo(v.mat.Data, -1, bmat.Data, amat.Data)
|
||||
return
|
||||
}
|
||||
f64.AxpyIncTo(v.mat.Data, uintptr(v.mat.Inc), 0,
|
||||
-1, bmat.Data, amat.Data,
|
||||
uintptr(ar), uintptr(bmat.Inc), uintptr(amat.Inc), 0, 0)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < ar; i++ {
|
||||
v.setVec(i, a.AtVec(i)-b.AtVec(i))
|
||||
}
|
||||
}
|
||||
|
||||
// MulElemVec performs element-wise multiplication of a and b, placing the result
|
||||
// in the receiver.
|
||||
func (v *VecDense) MulElemVec(a, b Vector) {
|
||||
ar := a.Len()
|
||||
br := b.Len()
|
||||
|
||||
if ar != br {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
v.reuseAs(ar)
|
||||
|
||||
aU, _ := untranspose(a)
|
||||
bU, _ := untranspose(b)
|
||||
|
||||
if arv, ok := aU.(RawVectorer); ok {
|
||||
if brv, ok := bU.(RawVectorer); ok {
|
||||
amat := arv.RawVector()
|
||||
bmat := brv.RawVector()
|
||||
|
||||
if v != a {
|
||||
v.checkOverlap(amat)
|
||||
}
|
||||
if v != b {
|
||||
v.checkOverlap(bmat)
|
||||
}
|
||||
|
||||
if v.mat.Inc == 1 && amat.Inc == 1 && bmat.Inc == 1 {
|
||||
// Fast path for a common case.
|
||||
for i, a := range amat.Data {
|
||||
v.mat.Data[i] = a * bmat.Data[i]
|
||||
}
|
||||
return
|
||||
}
|
||||
var ia, ib int
|
||||
for i := 0; i < ar; i++ {
|
||||
v.setVec(i, amat.Data[ia]*bmat.Data[ib])
|
||||
ia += amat.Inc
|
||||
ib += bmat.Inc
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < ar; i++ {
|
||||
v.setVec(i, a.AtVec(i)*b.AtVec(i))
|
||||
}
|
||||
}
|
||||
|
||||
// DivElemVec performs element-wise division of a by b, placing the result
|
||||
// in the receiver.
|
||||
func (v *VecDense) DivElemVec(a, b Vector) {
|
||||
ar := a.Len()
|
||||
br := b.Len()
|
||||
|
||||
if ar != br {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
v.reuseAs(ar)
|
||||
|
||||
aU, _ := untranspose(a)
|
||||
bU, _ := untranspose(b)
|
||||
|
||||
if arv, ok := aU.(RawVectorer); ok {
|
||||
if brv, ok := bU.(RawVectorer); ok {
|
||||
amat := arv.RawVector()
|
||||
bmat := brv.RawVector()
|
||||
|
||||
if v != a {
|
||||
v.checkOverlap(amat)
|
||||
}
|
||||
if v != b {
|
||||
v.checkOverlap(bmat)
|
||||
}
|
||||
|
||||
if v.mat.Inc == 1 && amat.Inc == 1 && bmat.Inc == 1 {
|
||||
// Fast path for a common case.
|
||||
for i, a := range amat.Data {
|
||||
v.setVec(i, a/bmat.Data[i])
|
||||
}
|
||||
return
|
||||
}
|
||||
var ia, ib int
|
||||
for i := 0; i < ar; i++ {
|
||||
v.setVec(i, amat.Data[ia]/bmat.Data[ib])
|
||||
ia += amat.Inc
|
||||
ib += bmat.Inc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < ar; i++ {
|
||||
v.setVec(i, a.AtVec(i)/b.AtVec(i))
|
||||
}
|
||||
}
|
||||
|
||||
// MulVec computes a * b. The result is stored into the receiver.
|
||||
// MulVec panics if the number of columns in a does not equal the number of rows in b
|
||||
// or if the number of columns in b does not equal 1.
|
||||
func (v *VecDense) MulVec(a Matrix, b Vector) {
|
||||
r, c := a.Dims()
|
||||
br, bc := b.Dims()
|
||||
if c != br || bc != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
aU, trans := untranspose(a)
|
||||
var bmat blas64.Vector
|
||||
fast := true
|
||||
bU, _ := untranspose(b)
|
||||
if rv, ok := bU.(RawVectorer); ok {
|
||||
bmat = rv.RawVector()
|
||||
if v != b {
|
||||
v.checkOverlap(bmat)
|
||||
}
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
|
||||
v.reuseAs(r)
|
||||
var restore func()
|
||||
if v == aU {
|
||||
v, restore = v.isolatedWorkspace(aU.(*VecDense))
|
||||
defer restore()
|
||||
} else if v == b {
|
||||
v, restore = v.isolatedWorkspace(b)
|
||||
defer restore()
|
||||
}
|
||||
|
||||
// TODO(kortschak): Improve the non-fast paths.
|
||||
switch aU := aU.(type) {
|
||||
case Vector:
|
||||
if b.Len() == 1 {
|
||||
// {n,1} x {1,1}
|
||||
v.ScaleVec(b.AtVec(0), aU)
|
||||
return
|
||||
}
|
||||
|
||||
// {1,n} x {n,1}
|
||||
if fast {
|
||||
if rv, ok := aU.(RawVectorer); ok {
|
||||
amat := rv.RawVector()
|
||||
if v != aU {
|
||||
v.checkOverlap(amat)
|
||||
}
|
||||
|
||||
if amat.Inc == 1 && bmat.Inc == 1 {
|
||||
// Fast path for a common case.
|
||||
v.setVec(0, f64.DotUnitary(amat.Data, bmat.Data))
|
||||
return
|
||||
}
|
||||
v.setVec(0, f64.DotInc(amat.Data, bmat.Data,
|
||||
uintptr(c), uintptr(amat.Inc), uintptr(bmat.Inc), 0, 0))
|
||||
return
|
||||
}
|
||||
}
|
||||
var sum float64
|
||||
for i := 0; i < c; i++ {
|
||||
sum += aU.AtVec(i) * b.AtVec(i)
|
||||
}
|
||||
v.setVec(0, sum)
|
||||
return
|
||||
case RawSymmetricer:
|
||||
if fast {
|
||||
amat := aU.RawSymmetric()
|
||||
// We don't know that a is a *SymDense, so make
|
||||
// a temporary SymDense to check overlap.
|
||||
(&SymDense{mat: amat}).checkOverlap(v.asGeneral())
|
||||
blas64.Symv(1, amat, bmat, 0, v.mat)
|
||||
return
|
||||
}
|
||||
case RawTriangular:
|
||||
v.CopyVec(b)
|
||||
amat := aU.RawTriangular()
|
||||
// We don't know that a is a *TriDense, so make
|
||||
// a temporary TriDense to check overlap.
|
||||
(&TriDense{mat: amat}).checkOverlap(v.asGeneral())
|
||||
ta := blas.NoTrans
|
||||
if trans {
|
||||
ta = blas.Trans
|
||||
}
|
||||
blas64.Trmv(ta, amat, v.mat)
|
||||
case RawMatrixer:
|
||||
if fast {
|
||||
amat := aU.RawMatrix()
|
||||
// We don't know that a is a *Dense, so make
|
||||
// a temporary Dense to check overlap.
|
||||
(&Dense{mat: amat}).checkOverlap(v.asGeneral())
|
||||
t := blas.NoTrans
|
||||
if trans {
|
||||
t = blas.Trans
|
||||
}
|
||||
blas64.Gemv(t, 1, amat, bmat, 0, v.mat)
|
||||
return
|
||||
}
|
||||
default:
|
||||
if fast {
|
||||
for i := 0; i < r; i++ {
|
||||
var f float64
|
||||
for j := 0; j < c; j++ {
|
||||
f += a.At(i, j) * bmat.Data[j*bmat.Inc]
|
||||
}
|
||||
v.setVec(i, f)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < r; i++ {
|
||||
var f float64
|
||||
for j := 0; j < c; j++ {
|
||||
f += a.At(i, j) * b.AtVec(j)
|
||||
}
|
||||
v.setVec(i, f)
|
||||
}
|
||||
}
|
||||
|
||||
// reuseAs resizes an empty vector to a r×1 vector,
|
||||
// or checks that a non-empty matrix is r×1.
|
||||
func (v *VecDense) reuseAs(r int) {
|
||||
if r == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
if v.IsZero() {
|
||||
v.mat = blas64.Vector{
|
||||
N: r,
|
||||
Inc: 1,
|
||||
Data: use(v.mat.Data, r),
|
||||
}
|
||||
return
|
||||
}
|
||||
if r != v.mat.N {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
|
||||
// IsZero returns whether the receiver is zero-sized. Zero-sized vectors can be the
|
||||
// receiver for size-restricted operations. VecDenses can be zeroed using Reset.
|
||||
func (v *VecDense) IsZero() bool {
|
||||
// It must be the case that v.Dims() returns
|
||||
// zeros in this case. See comment in Reset().
|
||||
return v.mat.Inc == 0
|
||||
}
|
||||
|
||||
func (v *VecDense) isolatedWorkspace(a Vector) (n *VecDense, restore func()) {
|
||||
l := a.Len()
|
||||
if l == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
n = getWorkspaceVec(l, false)
|
||||
return n, func() {
|
||||
v.CopyVec(n)
|
||||
putWorkspaceVec(n)
|
||||
}
|
||||
}
|
||||
|
||||
// asDense returns a Dense representation of the receiver with the same
|
||||
// underlying data.
|
||||
func (v *VecDense) asDense() *Dense {
|
||||
return &Dense{
|
||||
mat: v.asGeneral(),
|
||||
capRows: v.mat.N,
|
||||
capCols: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// asGeneral returns a blas64.General representation of the receiver with the
|
||||
// same underlying data.
|
||||
func (v *VecDense) asGeneral() blas64.General {
|
||||
return blas64.General{
|
||||
Rows: v.mat.N,
|
||||
Cols: 1,
|
||||
Stride: v.mat.Inc,
|
||||
Data: v.mat.Data,
|
||||
}
|
||||
}
|
||||
|
||||
// ColViewOf reflects the column j of the RawMatrixer m, into the receiver
|
||||
// backed by the same underlying data. The length of the receiver must either be
|
||||
// zero or match the number of rows in m.
|
||||
func (v *VecDense) ColViewOf(m RawMatrixer, j int) {
|
||||
rm := m.RawMatrix()
|
||||
|
||||
if j >= rm.Cols || j < 0 {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
if !v.IsZero() && v.mat.N != rm.Rows {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
v.mat.Inc = rm.Stride
|
||||
v.mat.Data = rm.Data[j : (rm.Rows-1)*rm.Stride+j+1]
|
||||
v.mat.N = rm.Rows
|
||||
}
|
||||
|
||||
// RowViewOf reflects the row i of the RawMatrixer m, into the receiver
|
||||
// backed by the same underlying data. The length of the receiver must either be
|
||||
// zero or match the number of columns in m.
|
||||
func (v *VecDense) RowViewOf(m RawMatrixer, i int) {
|
||||
rm := m.RawMatrix()
|
||||
|
||||
if i >= rm.Rows || i < 0 {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if !v.IsZero() && v.mat.N != rm.Cols {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
v.mat.Inc = 1
|
||||
v.mat.Data = rm.Data[i*rm.Stride : i*rm.Stride+rm.Cols]
|
||||
v.mat.N = rm.Cols
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue