fixed dependencies
This commit is contained in:
6
vendor/gonum.org/v1/gonum/mat/README.md
generated
vendored
Normal file
6
vendor/gonum.org/v1/gonum/mat/README.md
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# Gonum matrix
|
||||
|
||||
[](https://pkg.go.dev/gonum.org/v1/gonum/mat)
|
||||
[](https://godocs.io/gonum.org/v1/gonum/mat)
|
||||
|
||||
Package mat is a matrix package for the Go language.
|
||||
368
vendor/gonum.org/v1/gonum/mat/band.go
generated
vendored
Normal file
368
vendor/gonum.org/v1/gonum/mat/band.go
generated
vendored
Normal file
@@ -0,0 +1,368 @@
|
||||
// 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"
|
||||
"gonum.org/v1/gonum/lapack"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
var (
|
||||
bandDense *BandDense
|
||||
_ Matrix = bandDense
|
||||
_ allMatrix = bandDense
|
||||
_ denseMatrix = 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 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.
|
||||
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(ErrNegativeDimension)
|
||||
}
|
||||
if kl+1 > r || ku+1 > c {
|
||||
panic(ErrBandwidth)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// IsEmpty returns whether the receiver is empty. Empty matrices can be the
|
||||
// receiver for size-restricted operations. The receiver can be zeroed using Reset.
|
||||
func (b *BandDense) IsEmpty() bool {
|
||||
return b.mat.Stride == 0
|
||||
}
|
||||
|
||||
// Reset empties the matrix so that it can be reused as the
|
||||
// receiver of a dimensionally restricted operation.
|
||||
//
|
||||
// Reset should not be used when the matrix shares backing data.
|
||||
// See the Reseter interface for more information.
|
||||
func (b *BandDense) Reset() {
|
||||
b.mat.Rows = 0
|
||||
b.mat.Cols = 0
|
||||
b.mat.KL = 0
|
||||
b.mat.KU = 0
|
||||
b.mat.Stride = 0
|
||||
b.mat.Data = b.mat.Data[:0]
|
||||
}
|
||||
|
||||
// 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])
|
||||
}
|
||||
}
|
||||
|
||||
// Norm returns the specified norm of the receiver. Valid norms are:
|
||||
//
|
||||
// 1 - The maximum absolute column sum
|
||||
// 2 - The 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 is specified and with
|
||||
// ErrZeroLength if the matrix has zero size.
|
||||
func (b *BandDense) Norm(norm float64) float64 {
|
||||
if b.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
lnorm := normLapack(norm, false)
|
||||
if lnorm == lapack.MaxColumnSum || lnorm == lapack.MaxRowSum {
|
||||
return lapack64.Langb(lnorm, b.mat)
|
||||
}
|
||||
return lapack64.Langb(lnorm, b.mat)
|
||||
}
|
||||
|
||||
// Trace returns the trace of the matrix.
|
||||
//
|
||||
// Trace will panic with ErrSquare if the matrix is not square and with
|
||||
// ErrZeroLength if the matrix has zero size.
|
||||
func (b *BandDense) Trace() float64 {
|
||||
r, c := b.Dims()
|
||||
if r != c {
|
||||
panic(ErrSquare)
|
||||
}
|
||||
if b.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
rb := b.RawBand()
|
||||
var tr float64
|
||||
for i := 0; i < r; i++ {
|
||||
tr += rb.Data[rb.KL+i*rb.Stride]
|
||||
}
|
||||
return tr
|
||||
}
|
||||
|
||||
// MulVecTo computes B⋅x or Bᵀ⋅x storing the result into dst.
|
||||
func (b *BandDense) MulVecTo(dst *VecDense, trans bool, x Vector) {
|
||||
m, n := b.Dims()
|
||||
if trans {
|
||||
m, n = n, m
|
||||
}
|
||||
if x.Len() != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
dst.reuseAsNonZeroed(m)
|
||||
|
||||
t := blas.NoTrans
|
||||
if trans {
|
||||
t = blas.Trans
|
||||
}
|
||||
|
||||
xMat, _ := untransposeExtract(x)
|
||||
if xVec, ok := xMat.(*VecDense); ok {
|
||||
if dst != xVec {
|
||||
dst.checkOverlap(xVec.mat)
|
||||
blas64.Gbmv(t, 1, b.mat, xVec.mat, 0, dst.mat)
|
||||
} else {
|
||||
xCopy := getVecDenseWorkspace(n, false)
|
||||
xCopy.CloneFromVec(xVec)
|
||||
blas64.Gbmv(t, 1, b.mat, xCopy.mat, 0, dst.mat)
|
||||
putVecDenseWorkspace(xCopy)
|
||||
}
|
||||
} else {
|
||||
xCopy := getVecDenseWorkspace(n, false)
|
||||
xCopy.CloneFromVec(x)
|
||||
blas64.Gbmv(t, 1, b.mat, xCopy.mat, 0, dst.mat)
|
||||
putVecDenseWorkspace(xCopy)
|
||||
}
|
||||
}
|
||||
368
vendor/gonum.org/v1/gonum/mat/cdense.go
generated
vendored
Normal file
368
vendor/gonum.org/v1/gonum/mat/cdense.go
generated
vendored
Normal file
@@ -0,0 +1,368 @@
|
||||
// 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 (
|
||||
"math/cmplx"
|
||||
|
||||
"gonum.org/v1/gonum/blas/cblas128"
|
||||
)
|
||||
|
||||
var (
|
||||
cDense *CDense
|
||||
|
||||
_ CMatrix = cDense
|
||||
_ allMatrix = cDense
|
||||
)
|
||||
|
||||
// CDense 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
|
||||
}
|
||||
|
||||
// Caps returns the number of rows and columns in the backing matrix.
|
||||
func (m *CDense) Caps() (r, c int) { return m.capRows, m.capCols }
|
||||
|
||||
// H performs an implicit conjugate transpose by returning the receiver inside a
|
||||
// ConjTranspose.
|
||||
func (m *CDense) H() CMatrix {
|
||||
return ConjTranspose{m}
|
||||
}
|
||||
|
||||
// T performs an implicit transpose by returning the receiver inside a
|
||||
// CTranspose.
|
||||
func (m *CDense) T() CMatrix {
|
||||
return CTranspose{m}
|
||||
}
|
||||
|
||||
// Conj calculates the element-wise conjugate of a and stores the result in the
|
||||
// receiver.
|
||||
// Conj will panic if m and a do not have the same dimension unless m is empty.
|
||||
func (m *CDense) Conj(a CMatrix) {
|
||||
ar, ac := a.Dims()
|
||||
aU, aTrans, aConj := untransposeExtractCmplx(a)
|
||||
m.reuseAsNonZeroed(ar, ac)
|
||||
|
||||
if arm, ok := a.(*CDense); ok {
|
||||
amat := arm.mat
|
||||
if m != aU {
|
||||
m.checkOverlap(amat)
|
||||
}
|
||||
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] = cmplx.Conj(v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
m.checkOverlapMatrix(aU)
|
||||
if aTrans != aConj && m == aU {
|
||||
// Only make workspace if the destination is transposed
|
||||
// with respect to the source and they are the same
|
||||
// matrix.
|
||||
var restore func()
|
||||
m, restore = m.isolatedWorkspace(aU)
|
||||
defer restore()
|
||||
}
|
||||
|
||||
for r := 0; r < ar; r++ {
|
||||
for c := 0; c < ac; c++ {
|
||||
m.set(r, c, cmplx.Conj(a.At(r, c)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Slice returns a new CMatrix 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 *CDense) Slice(i, k, j, l int) CMatrix {
|
||||
return m.slice(i, k, j, l)
|
||||
}
|
||||
|
||||
func (m *CDense) slice(i, k, j, l int) *CDense {
|
||||
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
|
||||
}
|
||||
|
||||
// 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 changes the receiver if it IsEmpty() to be of size r×c.
|
||||
//
|
||||
// ReuseAs re-uses the backing data slice if it has sufficient capacity,
|
||||
// otherwise a new slice is allocated. The backing data is zero on return.
|
||||
//
|
||||
// ReuseAs panics if the receiver is not empty, and panics if
|
||||
// the input sizes are less than one. To empty the receiver for re-use,
|
||||
// Reset should be used.
|
||||
func (m *CDense) ReuseAs(r, c int) {
|
||||
if r <= 0 || c <= 0 {
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic(ErrNegativeDimension)
|
||||
}
|
||||
if !m.IsEmpty() {
|
||||
panic(ErrReuseNonEmpty)
|
||||
}
|
||||
m.reuseAsZeroed(r, 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) reuseAsNonZeroed(r, c int) {
|
||||
if m.mat.Rows > m.capRows || m.mat.Cols > m.capCols {
|
||||
// Panic as a string, not a mat.Error.
|
||||
panic(badCap)
|
||||
}
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
if m.IsEmpty() {
|
||||
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(badCap)
|
||||
}
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
if m.IsEmpty() {
|
||||
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()
|
||||
}
|
||||
|
||||
// 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 *CDense) isolatedWorkspace(a CMatrix) (w *CDense, restore func()) {
|
||||
r, c := a.Dims()
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
w = getCDenseWorkspace(r, c, false)
|
||||
return w, func() {
|
||||
m.Copy(w)
|
||||
putCDenseWorkspace(w)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset zeros the dimensions of the matrix so that it can be reused as the
|
||||
// receiver of a dimensionally restricted operation.
|
||||
//
|
||||
// Reset should not be used when the matrix shares backing data.
|
||||
// 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]
|
||||
}
|
||||
|
||||
// IsEmpty returns whether the receiver is empty. Empty matrices can be the
|
||||
// receiver for size-restricted operations. The receiver can be zeroed using Reset.
|
||||
func (m *CDense) IsEmpty() 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
|
||||
}
|
||||
|
||||
// SetRawCMatrix sets the underlying cblas128.General used by the receiver.
|
||||
// Changes to elements in the receiver following the call will be reflected
|
||||
// in b.
|
||||
func (m *CDense) SetRawCMatrix(b cblas128.General) {
|
||||
m.capRows, m.capCols = b.Rows, b.Cols
|
||||
m.mat = b
|
||||
}
|
||||
|
||||
// RawCMatrix returns the underlying cblas128.General used by the receiver.
|
||||
// Changes to elements in the receiver following the call will be reflected
|
||||
// in returned cblas128.General.
|
||||
func (m *CDense) RawCMatrix() cblas128.General { return m.mat }
|
||||
|
||||
// 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 *CDense) Grow(r, c int) CMatrix {
|
||||
if r < 0 || c < 0 {
|
||||
panic(ErrIndexOutOfRange)
|
||||
}
|
||||
if r == 0 && c == 0 {
|
||||
return m
|
||||
}
|
||||
|
||||
r += m.mat.Rows
|
||||
c += m.mat.Cols
|
||||
|
||||
var t CDense
|
||||
switch {
|
||||
case m.mat.Rows == 0 || m.mat.Cols == 0:
|
||||
t.mat = cblas128.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: useZeroedC(m.mat.Data, r*c),
|
||||
}
|
||||
case r > m.capRows || c > m.capCols:
|
||||
cr := max(r, m.capRows)
|
||||
cc := max(c, m.capCols)
|
||||
t.mat = cblas128.General{
|
||||
Rows: r,
|
||||
Cols: c,
|
||||
Stride: cc,
|
||||
Data: make([]complex128, 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 CDense
|
||||
tmp.mat = cblas128.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 = cblas128.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
|
||||
}
|
||||
1203
vendor/gonum.org/v1/gonum/mat/cholesky.go
generated
vendored
Normal file
1203
vendor/gonum.org/v1/gonum/mat/cholesky.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
314
vendor/gonum.org/v1/gonum/mat/cmatrix.go
generated
vendored
Normal file
314
vendor/gonum.org/v1/gonum/mat/cmatrix.go
generated
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
// 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/blas/cblas128"
|
||||
"gonum.org/v1/gonum/floats/scalar"
|
||||
)
|
||||
|
||||
// CMatrix is the basic matrix interface type for complex matrices.
|
||||
type CMatrix interface {
|
||||
// Dims returns the dimensions of a CMatrix.
|
||||
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 CMatrix. Whether H
|
||||
// returns a copy of the underlying data is implementation dependent.
|
||||
// This method may be implemented using the ConjTranspose type, which
|
||||
// provides an implicit matrix conjugate transpose.
|
||||
H() CMatrix
|
||||
|
||||
// T returns the transpose of the CMatrix. Whether T returns a copy of the
|
||||
// underlying data is implementation dependent.
|
||||
// This method may be implemented using the CTranspose type, which
|
||||
// provides an implicit matrix transpose.
|
||||
T() CMatrix
|
||||
}
|
||||
|
||||
// A RawCMatrixer can return a cblas128.General representation of the receiver. Changes to the cblas128.General.Data
|
||||
// slice will be reflected in the original matrix, changes to the Rows, Cols and Stride fields will not.
|
||||
type RawCMatrixer interface {
|
||||
RawCMatrix() cblas128.General
|
||||
}
|
||||
|
||||
var (
|
||||
_ CMatrix = ConjTranspose{}
|
||||
_ UnConjTransposer = ConjTranspose{}
|
||||
)
|
||||
|
||||
// ConjTranspose is a type for performing an implicit matrix conjugate transpose.
|
||||
// It implements the CMatrix interface, returning values from the conjugate
|
||||
// transpose of the matrix within.
|
||||
type ConjTranspose 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 CMatrix field.
|
||||
func (t ConjTranspose) 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 CMatrix field, and the number of columns is
|
||||
// the number of rows in the CMatrix field.
|
||||
func (t ConjTranspose) Dims() (r, c int) {
|
||||
c, r = t.CMatrix.Dims()
|
||||
return r, c
|
||||
}
|
||||
|
||||
// H performs an implicit conjugate transpose by returning the CMatrix field.
|
||||
func (t ConjTranspose) H() CMatrix {
|
||||
return t.CMatrix
|
||||
}
|
||||
|
||||
// T performs an implicit transpose by returning the receiver inside a
|
||||
// CTranspose.
|
||||
func (t ConjTranspose) T() CMatrix {
|
||||
return CTranspose{t}
|
||||
}
|
||||
|
||||
// UnConjTranspose returns the CMatrix field.
|
||||
func (t ConjTranspose) UnConjTranspose() CMatrix {
|
||||
return t.CMatrix
|
||||
}
|
||||
|
||||
// CTranspose is a type for performing an implicit matrix conjugate transpose.
|
||||
// It implements the CMatrix interface, returning values from the conjugate
|
||||
// transpose of the matrix within.
|
||||
type CTranspose 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 CMatrix field.
|
||||
func (t CTranspose) At(i, j int) complex128 {
|
||||
return t.CMatrix.At(j, i)
|
||||
}
|
||||
|
||||
// Dims returns the dimensions of the transposed matrix. The number of rows returned
|
||||
// is the number of columns in the CMatrix field, and the number of columns is
|
||||
// the number of rows in the CMatrix field.
|
||||
func (t CTranspose) Dims() (r, c int) {
|
||||
c, r = t.CMatrix.Dims()
|
||||
return r, c
|
||||
}
|
||||
|
||||
// H performs an implicit transpose by returning the receiver inside a
|
||||
// ConjTranspose.
|
||||
func (t CTranspose) H() CMatrix {
|
||||
return ConjTranspose{t}
|
||||
}
|
||||
|
||||
// T performs an implicit conjugate transpose by returning the CMatrix field.
|
||||
func (t CTranspose) T() CMatrix {
|
||||
return t.CMatrix
|
||||
}
|
||||
|
||||
// Untranspose returns the CMatrix field.
|
||||
func (t CTranspose) Untranspose() CMatrix {
|
||||
return t.CMatrix
|
||||
}
|
||||
|
||||
// UnConjTransposer is a type that can undo an implicit conjugate transpose.
|
||||
type UnConjTransposer interface {
|
||||
// UnConjTranspose returns the underlying CMatrix stored for the implicit
|
||||
// conjugate transpose.
|
||||
UnConjTranspose() CMatrix
|
||||
|
||||
// Note: This interface is needed to unify all of the Conjugate types. In
|
||||
// the cmat128 methods, we need to test if the CMatrix 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.
|
||||
}
|
||||
|
||||
// CUntransposer is a type that can undo an implicit transpose.
|
||||
type CUntransposer interface {
|
||||
// Untranspose returns the underlying CMatrix stored for the implicit
|
||||
// transpose.
|
||||
Untranspose() CMatrix
|
||||
|
||||
// Note: This interface is needed to unify all of the CTranspose types. In
|
||||
// the cmat128 methods, we need to test if the CMatrix has been implicitly
|
||||
// transposed. If this is checked by testing for the specific CTranspose type
|
||||
// then the behavior will be different if the user uses T() or TTri() for a
|
||||
// triangular matrix.
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// untransposeCmplx untransposes a matrix if applicable. If a is an CUntransposer
|
||||
// or an UnConjTransposer, then untranspose returns the underlying matrix and true for
|
||||
// the kind of transpose (potentially both).
|
||||
// If it is not, then it returns the input matrix and false for trans and conj.
|
||||
func untransposeCmplx(a CMatrix) (u CMatrix, trans, conj bool) {
|
||||
switch ut := a.(type) {
|
||||
case CUntransposer:
|
||||
trans = true
|
||||
u := ut.Untranspose()
|
||||
if uc, ok := u.(UnConjTransposer); ok {
|
||||
return uc.UnConjTranspose(), trans, true
|
||||
}
|
||||
return u, trans, false
|
||||
case UnConjTransposer:
|
||||
conj = true
|
||||
u := ut.UnConjTranspose()
|
||||
if ut, ok := u.(CUntransposer); ok {
|
||||
return ut.Untranspose(), true, conj
|
||||
}
|
||||
return u, false, conj
|
||||
default:
|
||||
return a, false, false
|
||||
}
|
||||
}
|
||||
|
||||
// untransposeExtractCmplx 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 untransposeExtractCmplx(a CMatrix) (u CMatrix, trans, conj bool) {
|
||||
ut, trans, conj := untransposeCmplx(a)
|
||||
switch m := ut.(type) {
|
||||
case *CDense:
|
||||
return m, trans, conj
|
||||
case RawCMatrixer:
|
||||
var d CDense
|
||||
d.SetRawCMatrix(m.RawCMatrix())
|
||||
return &d, trans, conj
|
||||
default:
|
||||
return ut, trans, conj
|
||||
}
|
||||
}
|
||||
|
||||
// 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/scalar 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 scalar.EqualWithinRel(imag(a), imag(b), tol)
|
||||
}
|
||||
return false
|
||||
}
|
||||
if imag(a) == imag(b) {
|
||||
return scalar.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
|
||||
)
|
||||
670
vendor/gonum.org/v1/gonum/mat/dense.go
generated
vendored
Normal file
670
vendor/gonum.org/v1/gonum/mat/dense.go
generated
vendored
Normal file
@@ -0,0 +1,670 @@
|
||||
// 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/lapack"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
var (
|
||||
dense *Dense
|
||||
|
||||
_ Matrix = dense
|
||||
_ allMatrix = dense
|
||||
_ denseMatrix = dense
|
||||
_ Mutable = dense
|
||||
|
||||
_ ClonerFrom = 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(ErrNegativeDimension)
|
||||
}
|
||||
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 changes the receiver if it IsEmpty() to be of size r×c.
|
||||
//
|
||||
// ReuseAs re-uses the backing data slice if it has sufficient capacity,
|
||||
// otherwise a new slice is allocated. The backing data is zero on return.
|
||||
//
|
||||
// ReuseAs panics if the receiver is not empty, and panics if
|
||||
// the input sizes are less than one. To empty the receiver for re-use,
|
||||
// Reset should be used.
|
||||
func (m *Dense) ReuseAs(r, c int) {
|
||||
if r <= 0 || c <= 0 {
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic(ErrNegativeDimension)
|
||||
}
|
||||
if !m.IsEmpty() {
|
||||
panic(ErrReuseNonEmpty)
|
||||
}
|
||||
m.reuseAsZeroed(r, c)
|
||||
}
|
||||
|
||||
// reuseAsNonZeroed resizes an empty matrix to a r×c matrix,
|
||||
// or checks that a non-empty matrix is r×c. It does not zero
|
||||
// the data in the receiver.
|
||||
func (m *Dense) reuseAsNonZeroed(r, c int) {
|
||||
// reuseAs must be kept in sync with reuseAsZeroed.
|
||||
if m.mat.Rows > m.capRows || m.mat.Cols > m.capCols {
|
||||
// Panic as a string, not a mat.Error.
|
||||
panic(badCap)
|
||||
}
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
if m.IsEmpty() {
|
||||
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.
|
||||
func (m *Dense) reuseAsZeroed(r, c int) {
|
||||
// reuseAsZeroed must be kept in sync with reuseAsNonZeroed.
|
||||
if m.mat.Rows > m.capRows || m.mat.Cols > m.capCols {
|
||||
// Panic as a string, not a mat.Error.
|
||||
panic(badCap)
|
||||
}
|
||||
if r == 0 || c == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
if m.IsEmpty() {
|
||||
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 = getDenseWorkspace(r, c, false)
|
||||
return w, func() {
|
||||
m.Copy(w)
|
||||
putDenseWorkspace(w)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset empties the matrix so that it can be reused as the
|
||||
// receiver of a dimensionally restricted operation.
|
||||
//
|
||||
// Reset should not be used when the matrix shares backing data.
|
||||
// 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]
|
||||
}
|
||||
|
||||
// IsEmpty returns whether the receiver is empty. Empty matrices can be the
|
||||
// receiver for size-restricted operations. The receiver can be emptied using
|
||||
// Reset.
|
||||
func (m *Dense) IsEmpty() 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.CloneFrom(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 {
|
||||
return m.slice(i, k, j, l)
|
||||
}
|
||||
|
||||
func (m *Dense) slice(i, k, j, l int) *Dense {
|
||||
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
|
||||
}
|
||||
|
||||
// CloneFrom makes a copy of a into the receiver, overwriting the previous value of
|
||||
// the receiver. The clone from operation does not make any restriction on shape and
|
||||
// will not cause shadowing.
|
||||
//
|
||||
// See the ClonerFrom interface for more information.
|
||||
func (m *Dense) CloneFrom(a Matrix) {
|
||||
r, c := a.Dims()
|
||||
mat := blas64.General{
|
||||
Rows: r,
|
||||
Cols: c,
|
||||
Stride: c,
|
||||
}
|
||||
m.capRows, m.capCols = r, c
|
||||
|
||||
aU, trans := untransposeExtract(a)
|
||||
switch aU := aU.(type) {
|
||||
case *Dense:
|
||||
amat := aU.mat
|
||||
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 := untransposeExtract(a)
|
||||
switch aU := aU.(type) {
|
||||
case *Dense:
|
||||
amat := aU.mat
|
||||
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.reuseAsNonZeroed(ar+br, ac)
|
||||
|
||||
m.Copy(a)
|
||||
w := m.slice(ar, ar+br, 0, bc)
|
||||
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.reuseAsNonZeroed(ar, ac+bc)
|
||||
|
||||
m.Copy(a)
|
||||
w := m.slice(0, br, ac, ac+bc)
|
||||
w.Copy(b)
|
||||
}
|
||||
|
||||
// Trace returns the trace of the matrix.
|
||||
//
|
||||
// Trace will panic with ErrSquare if the matrix is not square and with
|
||||
// ErrZeroLength if the matrix has zero size.
|
||||
func (m *Dense) Trace() float64 {
|
||||
r, c := m.Dims()
|
||||
if r != c {
|
||||
panic(ErrSquare)
|
||||
}
|
||||
if m.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
// 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
|
||||
}
|
||||
|
||||
// Norm returns the specified norm of the receiver. Valid norms are:
|
||||
//
|
||||
// 1 - The maximum absolute column sum
|
||||
// 2 - The 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 is specified and with
|
||||
// ErrShape if the matrix has zero size.
|
||||
func (m *Dense) Norm(norm float64) float64 {
|
||||
if m.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
lnorm := normLapack(norm, false)
|
||||
if lnorm == lapack.MaxColumnSum {
|
||||
work := getFloat64s(m.mat.Cols, false)
|
||||
defer putFloat64s(work)
|
||||
return lapack64.Lange(lnorm, m.mat, work)
|
||||
}
|
||||
return lapack64.Lange(lnorm, m.mat, nil)
|
||||
}
|
||||
|
||||
// Permutation constructs an n×n permutation matrix P from the given
|
||||
// row permutation such that the nonzero entries are P[i,p[i]] = 1.
|
||||
func (m *Dense) Permutation(n int, p []int) {
|
||||
if len(p) != n {
|
||||
panic(badSliceLength)
|
||||
}
|
||||
m.reuseAsZeroed(n, n)
|
||||
for i, v := range p {
|
||||
if v < 0 || v >= n {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
m.mat.Data[i*m.mat.Stride+v] = 1
|
||||
}
|
||||
}
|
||||
|
||||
// PermuteRows rearranges the rows of the m×n matrix A in the receiver as
|
||||
// specified by the permutation p[0],p[1],...,p[m-1] of the integers 0,...,m-1.
|
||||
//
|
||||
// If inverse is false, the given permutation is applied:
|
||||
//
|
||||
// A[p[i],0:n] is moved to A[i,0:n] for i=0,1,...,m-1.
|
||||
//
|
||||
// If inverse is true, the inverse permutation is applied:
|
||||
//
|
||||
// A[i,0:n] is moved to A[p[i],0:n] for i=0,1,...,m-1.
|
||||
//
|
||||
// p must have length m, otherwise PermuteRows will panic.
|
||||
func (m *Dense) PermuteRows(p []int, inverse bool) {
|
||||
r, _ := m.Dims()
|
||||
if len(p) != r {
|
||||
panic(badSliceLength)
|
||||
}
|
||||
lapack64.Lapmr(!inverse, m.mat, p)
|
||||
}
|
||||
|
||||
// PermuteCols rearranges the columns of the m×n matrix A in the reciever as
|
||||
// specified by the permutation p[0],p[1],...,p[n-1] of the integers 0,...,n-1.
|
||||
//
|
||||
// If inverse is false, the given permutation is applied:
|
||||
//
|
||||
// A[0:m,p[j]] is moved to A[0:m,j] for j = 0, 1, ..., n-1.
|
||||
//
|
||||
// If inverse is true, the inverse permutation is applied:
|
||||
//
|
||||
// A[0:m,j] is moved to A[0:m,p[j]] for j = 0, 1, ..., n-1.
|
||||
//
|
||||
// p must have length n, otherwise PermuteCols will panic.
|
||||
func (m *Dense) PermuteCols(p []int, inverse bool) {
|
||||
_, c := m.Dims()
|
||||
if len(p) != c {
|
||||
panic(badSliceLength)
|
||||
}
|
||||
lapack64.Lapmt(!inverse, m.mat, p)
|
||||
}
|
||||
877
vendor/gonum.org/v1/gonum/mat/dense_arithmetic.go
generated
vendored
Normal file
877
vendor/gonum.org/v1/gonum/mat/dense_arithmetic.go
generated
vendored
Normal file
@@ -0,0 +1,877 @@
|
||||
// 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, aTrans := untransposeExtract(a)
|
||||
bU, bTrans := untransposeExtract(b)
|
||||
m.reuseAsNonZeroed(ar, ac)
|
||||
|
||||
if arm, ok := a.(*Dense); ok {
|
||||
if brm, ok := b.(*Dense); ok {
|
||||
amat, bmat := arm.mat, brm.mat
|
||||
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 aTrans && m == aU {
|
||||
m, restore = m.isolatedWorkspace(aU)
|
||||
defer restore()
|
||||
} else if bTrans && 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, aTrans := untransposeExtract(a)
|
||||
bU, bTrans := untransposeExtract(b)
|
||||
m.reuseAsNonZeroed(ar, ac)
|
||||
|
||||
if arm, ok := a.(*Dense); ok {
|
||||
if brm, ok := b.(*Dense); ok {
|
||||
amat, bmat := arm.mat, brm.mat
|
||||
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 aTrans && m == aU {
|
||||
m, restore = m.isolatedWorkspace(aU)
|
||||
defer restore()
|
||||
} else if bTrans && 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, aTrans := untransposeExtract(a)
|
||||
bU, bTrans := untransposeExtract(b)
|
||||
m.reuseAsNonZeroed(ar, ac)
|
||||
|
||||
if arm, ok := a.(*Dense); ok {
|
||||
if brm, ok := b.(*Dense); ok {
|
||||
amat, bmat := arm.mat, brm.mat
|
||||
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 aTrans && m == aU {
|
||||
m, restore = m.isolatedWorkspace(aU)
|
||||
defer restore()
|
||||
} else if bTrans && 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, aTrans := untransposeExtract(a)
|
||||
bU, bTrans := untransposeExtract(b)
|
||||
m.reuseAsNonZeroed(ar, ac)
|
||||
|
||||
if arm, ok := a.(*Dense); ok {
|
||||
if brm, ok := b.(*Dense); ok {
|
||||
amat, bmat := arm.mat, brm.mat
|
||||
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 aTrans && m == aU {
|
||||
m, restore = m.isolatedWorkspace(aU)
|
||||
defer restore()
|
||||
} else if bTrans && 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.reuseAsNonZeroed(a.Dims())
|
||||
aU, aTrans := untransposeExtract(a)
|
||||
switch rm := aU.(type) {
|
||||
case *Dense:
|
||||
if m != aU || aTrans {
|
||||
if m == aU || m.checkOverlap(rm.mat) {
|
||||
tmp := getDenseWorkspace(r, c, false)
|
||||
tmp.Copy(a)
|
||||
m.Copy(tmp)
|
||||
putDenseWorkspace(tmp)
|
||||
break
|
||||
}
|
||||
m.Copy(a)
|
||||
}
|
||||
default:
|
||||
m.Copy(a)
|
||||
}
|
||||
// Compute the norm of A.
|
||||
work := getFloat64s(4*r, false) // Length must be at least 4*r for Gecon.
|
||||
norm := lapack64.Lange(CondNorm, m.mat, work)
|
||||
// Compute the LU factorization of A.
|
||||
ipiv := getInts(r, false)
|
||||
defer putInts(ipiv)
|
||||
ok := lapack64.Getrf(m.mat, ipiv)
|
||||
if !ok {
|
||||
// A is exactly singular.
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
// Compute the condition number of A using the LU factorization.
|
||||
iwork := getInts(r, false)
|
||||
defer putInts(iwork)
|
||||
rcond := lapack64.Gecon(CondNorm, m.mat, norm, work, iwork)
|
||||
// Compute A^{-1} from the LU factorization regardless of the value of rcond.
|
||||
lapack64.Getri(m.mat, ipiv, work, -1)
|
||||
if int(work[0]) > len(work) {
|
||||
l := int(work[0])
|
||||
putFloat64s(work)
|
||||
work = getFloat64s(l, false)
|
||||
}
|
||||
defer putFloat64s(work)
|
||||
ok = lapack64.Getri(m.mat, ipiv, work, len(work))
|
||||
if !ok || rcond == 0 {
|
||||
// A is exactly singular.
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
// Check whether A is singular for computational purposes.
|
||||
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 := untransposeExtract(a)
|
||||
bU, bTrans := untransposeExtract(b)
|
||||
m.reuseAsNonZeroed(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ᵀ * B = (Bᵀ * A)ᵀ
|
||||
// Cᵀ = Bᵀ * A.
|
||||
if aU, ok := aU.(*Dense); ok {
|
||||
if restore == nil {
|
||||
m.checkOverlap(aU.mat)
|
||||
}
|
||||
switch bU := bU.(type) {
|
||||
case *Dense:
|
||||
if restore == nil {
|
||||
m.checkOverlap(bU.mat)
|
||||
}
|
||||
blas64.Gemm(aT, bT, 1, aU.mat, bU.mat, 0, m.mat)
|
||||
return
|
||||
|
||||
case *SymDense:
|
||||
if aTrans {
|
||||
c := getDenseWorkspace(ac, ar, false)
|
||||
blas64.Symm(blas.Left, 1, bU.mat, aU.mat, 0, c.mat)
|
||||
strictCopy(m, c.T())
|
||||
putDenseWorkspace(c)
|
||||
return
|
||||
}
|
||||
blas64.Symm(blas.Right, 1, bU.mat, aU.mat, 0, m.mat)
|
||||
return
|
||||
|
||||
case *TriDense:
|
||||
// Trmm updates in place, so copy aU first.
|
||||
if aTrans {
|
||||
c := getDenseWorkspace(ac, ar, false)
|
||||
var tmp Dense
|
||||
tmp.SetRawMatrix(aU.mat)
|
||||
c.Copy(&tmp)
|
||||
bT := blas.Trans
|
||||
if bTrans {
|
||||
bT = blas.NoTrans
|
||||
}
|
||||
blas64.Trmm(blas.Left, bT, 1, bU.mat, c.mat)
|
||||
strictCopy(m, c.T())
|
||||
putDenseWorkspace(c)
|
||||
return
|
||||
}
|
||||
m.Copy(a)
|
||||
blas64.Trmm(blas.Right, bT, 1, bU.mat, m.mat)
|
||||
return
|
||||
|
||||
case *VecDense:
|
||||
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, aU.mat, bmat, 0, m.mat)
|
||||
return
|
||||
}
|
||||
cvec := blas64.Vector{
|
||||
Inc: m.mat.Stride,
|
||||
Data: m.mat.Data,
|
||||
}
|
||||
blas64.Gemv(aT, 1, aU.mat, bvec, 0, cvec)
|
||||
return
|
||||
}
|
||||
}
|
||||
if bU, ok := bU.(*Dense); ok {
|
||||
if restore == nil {
|
||||
m.checkOverlap(bU.mat)
|
||||
}
|
||||
switch aU := aU.(type) {
|
||||
case *SymDense:
|
||||
if bTrans {
|
||||
c := getDenseWorkspace(bc, br, false)
|
||||
blas64.Symm(blas.Right, 1, aU.mat, bU.mat, 0, c.mat)
|
||||
strictCopy(m, c.T())
|
||||
putDenseWorkspace(c)
|
||||
return
|
||||
}
|
||||
blas64.Symm(blas.Left, 1, aU.mat, bU.mat, 0, m.mat)
|
||||
return
|
||||
|
||||
case *TriDense:
|
||||
// Trmm updates in place, so copy bU first.
|
||||
if bTrans {
|
||||
c := getDenseWorkspace(bc, br, false)
|
||||
var tmp Dense
|
||||
tmp.SetRawMatrix(bU.mat)
|
||||
c.Copy(&tmp)
|
||||
aT := blas.Trans
|
||||
if aTrans {
|
||||
aT = blas.NoTrans
|
||||
}
|
||||
blas64.Trmm(blas.Right, aT, 1, aU.mat, c.mat)
|
||||
strictCopy(m, c.T())
|
||||
putDenseWorkspace(c)
|
||||
return
|
||||
}
|
||||
m.Copy(b)
|
||||
blas64.Trmm(blas.Left, aT, 1, aU.mat, m.mat)
|
||||
return
|
||||
|
||||
case *VecDense:
|
||||
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, bU.mat, 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, bU.mat, 0, m.mat)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
m.checkOverlapMatrix(aU)
|
||||
m.checkOverlapMatrix(bU)
|
||||
row := getFloat64s(ac, false)
|
||||
defer putFloat64s(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 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.reuseAsNonZeroed(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 := getDenseWorkspace(r, r, true)
|
||||
vraw := v.RawMatrix()
|
||||
n := r * r
|
||||
vvec := blas64.Vector{N: n, Inc: 1, Data: vraw.Data}
|
||||
defer putDenseWorkspace(v)
|
||||
|
||||
u := getDenseWorkspace(r, r, true)
|
||||
uraw := u.RawMatrix()
|
||||
uvec := blas64.Vector{N: n, Inc: 1, Data: uraw.Data}
|
||||
defer putDenseWorkspace(u)
|
||||
|
||||
a2 := getDenseWorkspace(r, r, false)
|
||||
defer putDenseWorkspace(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 := getDenseWorkspace(r, r, true)
|
||||
praw := p.RawMatrix()
|
||||
pvec := blas64.Vector{N: n, Inc: 1, Data: praw.Data}
|
||||
defer putDenseWorkspace(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 := getDenseWorkspace(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 putDenseWorkspace(i)
|
||||
|
||||
a2raw := a2.RawMatrix()
|
||||
a2vec := blas64.Vector{N: n, Inc: 1, Data: a2raw.Data}
|
||||
|
||||
a4 := getDenseWorkspace(r, r, false)
|
||||
a4raw := a4.RawMatrix()
|
||||
a4vec := blas64.Vector{N: n, Inc: 1, Data: a4raw.Data}
|
||||
defer putDenseWorkspace(a4)
|
||||
a4.Mul(a2, a2)
|
||||
|
||||
a6 := getDenseWorkspace(r, r, false)
|
||||
a6raw := a6.RawMatrix()
|
||||
a6vec := blas64.Vector{N: n, Inc: 1, Data: a6raw.Data}
|
||||
defer putDenseWorkspace(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("mat: illegal power")
|
||||
}
|
||||
r, c := a.Dims()
|
||||
if r != c {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
m.reuseAsNonZeroed(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 := getDenseWorkspace(r, r, false)
|
||||
w.Copy(a)
|
||||
s := getDenseWorkspace(r, r, false)
|
||||
s.Copy(a)
|
||||
x := getDenseWorkspace(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)
|
||||
putDenseWorkspace(w)
|
||||
putDenseWorkspace(s)
|
||||
putDenseWorkspace(x)
|
||||
}
|
||||
|
||||
// Kronecker calculates the Kronecker product of a and b, placing the result in
|
||||
// the receiver.
|
||||
func (m *Dense) Kronecker(a, b Matrix) {
|
||||
ra, ca := a.Dims()
|
||||
rb, cb := b.Dims()
|
||||
|
||||
m.reuseAsNonZeroed(ra*rb, ca*cb)
|
||||
for i := 0; i < ra; i++ {
|
||||
for j := 0; j < ca; j++ {
|
||||
m.slice(i*rb, (i+1)*rb, j*cb, (j+1)*cb).Scale(a.At(i, j), b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.reuseAsNonZeroed(ar, ac)
|
||||
|
||||
aU, aTrans := untransposeExtract(a)
|
||||
if rm, ok := aU.(*Dense); ok {
|
||||
amat := rm.mat
|
||||
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.reuseAsNonZeroed(ar, ac)
|
||||
|
||||
aU, aTrans := untransposeExtract(a)
|
||||
if rm, ok := aU.(*Dense); ok {
|
||||
amat := rm.mat
|
||||
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 with the vectors x and
|
||||
// y, where x and y are treated as column vectors. The result is stored in the
|
||||
// receiver. The Outer method can be used instead of RankOne if a is not needed.
|
||||
//
|
||||
// m = a + alpha * x * yᵀ
|
||||
func (m *Dense) RankOne(a Matrix, alpha float64, x, y Vector) {
|
||||
ar, ac := a.Dims()
|
||||
if x.Len() != ar {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if y.Len() != ac {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
if a != m {
|
||||
aU, _ := untransposeExtract(a)
|
||||
if rm, ok := aU.(*Dense); ok {
|
||||
m.checkOverlap(rm.RawMatrix())
|
||||
}
|
||||
}
|
||||
|
||||
var xmat, ymat blas64.Vector
|
||||
fast := true
|
||||
xU, _ := untransposeExtract(x)
|
||||
if rv, ok := xU.(*VecDense); ok {
|
||||
r, c := xU.Dims()
|
||||
xmat = rv.mat
|
||||
m.checkOverlap(generalFromVector(xmat, r, c))
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
yU, _ := untransposeExtract(y)
|
||||
if rv, ok := yU.(*VecDense); ok {
|
||||
r, c := yU.Dims()
|
||||
ymat = rv.mat
|
||||
m.checkOverlap(generalFromVector(ymat, r, c))
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
|
||||
if fast {
|
||||
if m != a {
|
||||
m.reuseAsNonZeroed(ar, ac)
|
||||
m.Copy(a)
|
||||
}
|
||||
blas64.Ger(alpha, xmat, ymat, m.mat)
|
||||
return
|
||||
}
|
||||
|
||||
m.reuseAsNonZeroed(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 vectors x and y, where x and y
|
||||
// are treated as column vectors, 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) {
|
||||
r, c := x.Len(), y.Len()
|
||||
|
||||
m.reuseAsZeroed(r, c)
|
||||
|
||||
var xmat, ymat blas64.Vector
|
||||
fast := true
|
||||
xU, _ := untransposeExtract(x)
|
||||
if rv, ok := xU.(*VecDense); ok {
|
||||
r, c := xU.Dims()
|
||||
xmat = rv.mat
|
||||
m.checkOverlap(generalFromVector(xmat, r, c))
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
yU, _ := untransposeExtract(y)
|
||||
if rv, ok := yU.(*VecDense); ok {
|
||||
r, c := yU.Dims()
|
||||
ymat = rv.mat
|
||||
m.checkOverlap(generalFromVector(ymat, r, c))
|
||||
} 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
342
vendor/gonum.org/v1/gonum/mat/diagonal.go
generated
vendored
Normal file
342
vendor/gonum.org/v1/gonum/mat/diagonal.go
generated
vendored
Normal file
@@ -0,0 +1,342 @@
|
||||
// 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 (
|
||||
"math"
|
||||
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
)
|
||||
|
||||
var (
|
||||
diagDense *DiagDense
|
||||
_ Matrix = diagDense
|
||||
_ allMatrix = diagDense
|
||||
_ denseMatrix = 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
|
||||
|
||||
// The following interfaces are included in the Diagonal
|
||||
// interface to allow the use of Diagonal types in
|
||||
// functions operating on these types.
|
||||
Banded
|
||||
SymBanded
|
||||
Symmetric
|
||||
Triangular
|
||||
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
|
||||
}
|
||||
|
||||
// SymmetricDim implements the Symmetric interface.
|
||||
func (d *DiagDense) SymmetricDim() 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 empties the matrix so that it can be reused as the
|
||||
// receiver of a dimensionally restricted operation.
|
||||
//
|
||||
// Reset should not be used when the matrix shares backing data.
|
||||
// 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 empty, otherwise DiagFrom will panic.
|
||||
func (d *DiagDense) DiagFrom(m Matrix) {
|
||||
n := min(m.Dims())
|
||||
d.reuseAsNonZeroed(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,
|
||||
}
|
||||
}
|
||||
|
||||
// reuseAsNonZeroed resizes an empty diagonal to a r×r diagonal,
|
||||
// or checks that a non-empty matrix is r×r.
|
||||
func (d *DiagDense) reuseAsNonZeroed(r int) {
|
||||
if r == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
if d.IsEmpty() {
|
||||
d.mat = blas64.Vector{
|
||||
Inc: 1,
|
||||
Data: use(d.mat.Data, r),
|
||||
}
|
||||
d.mat.N = r
|
||||
return
|
||||
}
|
||||
if r != d.mat.N {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
|
||||
// IsEmpty returns whether the receiver is empty. Empty matrices can be the
|
||||
// receiver for size-restricted operations. The receiver can be emptied using
|
||||
// Reset.
|
||||
func (d *DiagDense) IsEmpty() bool {
|
||||
// It must be the case that d.Dims() returns
|
||||
// zeros in this case. See comment in Reset().
|
||||
return d.mat.Inc == 0
|
||||
}
|
||||
|
||||
// Trace returns the trace of the matrix.
|
||||
//
|
||||
// Trace will panic with ErrZeroLength if the matrix has zero size.
|
||||
func (d *DiagDense) Trace() float64 {
|
||||
if d.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
rb := d.RawBand()
|
||||
var tr float64
|
||||
for i := 0; i < rb.Rows; i++ {
|
||||
tr += rb.Data[rb.KL+i*rb.Stride]
|
||||
}
|
||||
return tr
|
||||
}
|
||||
|
||||
// Norm returns the specified norm of the receiver. Valid norms are:
|
||||
//
|
||||
// 1 or Inf - The maximum diagonal element magnitude
|
||||
// 2 - The Frobenius norm, the square root of the sum of the squares of
|
||||
// the diagonal elements
|
||||
//
|
||||
// Norm will panic with ErrNormOrder if an illegal norm is specified and with
|
||||
// ErrZeroLength if the receiver has zero size.
|
||||
func (d *DiagDense) Norm(norm float64) float64 {
|
||||
if d.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
switch norm {
|
||||
default:
|
||||
panic(ErrNormOrder)
|
||||
case 1, math.Inf(1):
|
||||
imax := blas64.Iamax(d.mat)
|
||||
return math.Abs(d.at(imax, imax))
|
||||
case 2:
|
||||
return blas64.Nrm2(d.mat)
|
||||
}
|
||||
}
|
||||
200
vendor/gonum.org/v1/gonum/mat/doc.go
generated
vendored
Normal file
200
vendor/gonum.org/v1/gonum/mat/doc.go
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
// 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, VecDense)
|
||||
// - Methods and functions for using matrix data (Add, Trace, SymRankOne)
|
||||
// - Types for constructing and using matrix factorizations (QR, LU, etc.)
|
||||
// - The complementary types for complex matrices, CMatrix, CSymDense, etc.
|
||||
//
|
||||
// In the documentation below, we use "matrix" as a short-hand for all of
|
||||
// the FooDense types implemented in this package. We use "Matrix" to
|
||||
// refer to the Matrix interface.
|
||||
//
|
||||
// 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.
|
||||
// All matrices are stored in row-major format and users should consider
|
||||
// this when expressing matrix arithmetic to ensure optimal performance.
|
||||
//
|
||||
// // 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)
|
||||
//
|
||||
// Note that the input arguments to most functions and methods are interfaces
|
||||
// rather than concrete types `func Trace(Matrix)` rather than
|
||||
// `func Trace(*Dense)` allowing flexible use of internal and external
|
||||
// Matrix types.
|
||||
//
|
||||
// When a matrix is the destination or receiver for a function or method,
|
||||
// the operation will panic if the matrix is not the correct size.
|
||||
// An exception to this is when the destination is empty (see below).
|
||||
//
|
||||
// # Empty matrix
|
||||
//
|
||||
// An empty matrix is one that has zero size. Empty matrices are used to allow
|
||||
// the destination of a matrix operation to assume the correct size automatically.
|
||||
// This operation will re-use the backing data, if available, or will allocate
|
||||
// new data if necessary. The IsEmpty method returns whether the given matrix
|
||||
// is empty. The zero-value of a matrix is empty, and is useful for easily
|
||||
// getting the result of matrix operations.
|
||||
//
|
||||
// var c mat.Dense // construct a new zero-value matrix
|
||||
// c.Mul(a, a) // c is automatically adjusted to be the right size
|
||||
//
|
||||
// The Reset method can be used to revert a matrix to an empty matrix.
|
||||
// Reset should not be used when multiple different matrices share the same backing
|
||||
// data slice. This can cause unexpected data modifications after being resized.
|
||||
// An empty 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 matrix 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
|
||||
// additional interfaces like Symmetric and Triangular. 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ᵀ * 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, for example *LU.UTo. The factorization types can also be used
|
||||
// directly, as in *Cholesky.SolveTo. Some factorizations can be updated directly,
|
||||
// without needing to update the original matrix and refactorize, for example with
|
||||
// *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 the wrapper packages' "Use"
|
||||
// functions. The Go implementation of LAPACK 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 package functions are never directly modified. If an
|
||||
// operation changes Matrix data, the mutated matrix will be the receiver of a
|
||||
// method, or will be the first, dst, argument to a method named with a To suffix.
|
||||
//
|
||||
// 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"
|
||||
450
vendor/gonum.org/v1/gonum/mat/eigen.go
generated
vendored
Normal file
450
vendor/gonum.org/v1/gonum/mat/eigen.go
generated
vendored
Normal file
@@ -0,0 +1,450 @@
|
||||
// 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"
|
||||
noVectors = "mat: eigenvectors not computed"
|
||||
)
|
||||
|
||||
// EigenSym is a type for computing all eigenvalues and, optionally,
|
||||
// eigenvectors of a symmetric matrix A.
|
||||
//
|
||||
// It is a Symmetric matrix represented by its spectral factorization. Once
|
||||
// computed, this representation is useful for extracting eigenvalues and
|
||||
// eigenvector, but At is slow.
|
||||
type EigenSym struct {
|
||||
vectorsComputed bool
|
||||
|
||||
values []float64
|
||||
vectors *Dense
|
||||
}
|
||||
|
||||
// Dims returns the dimensions of the matrix.
|
||||
func (e *EigenSym) Dims() (r, c int) {
|
||||
n := e.SymmetricDim()
|
||||
return n, n
|
||||
}
|
||||
|
||||
// SymmetricDim implements the Symmetric interface.
|
||||
func (e *EigenSym) SymmetricDim() int {
|
||||
return len(e.values)
|
||||
}
|
||||
|
||||
// At returns the element at row i, column j of the matrix A.
|
||||
//
|
||||
// At will panic if the eigenvectors have not been computed.
|
||||
func (e *EigenSym) At(i, j int) float64 {
|
||||
if !e.vectorsComputed {
|
||||
panic(noVectors)
|
||||
}
|
||||
n, _ := e.Dims()
|
||||
if uint(i) >= uint(n) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(n) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
|
||||
var val float64
|
||||
for k := 0; k < n; k++ {
|
||||
val += e.values[k] * e.vectors.at(i, k) * e.vectors.at(j, k)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// T returns the receiver, the transpose of a symmetric matrix.
|
||||
func (e *EigenSym) T() Matrix {
|
||||
return e
|
||||
}
|
||||
|
||||
// Factorize computes the spectral factorization (eigendecomposition) of the
|
||||
// symmetric matrix A.
|
||||
//
|
||||
// The spectral factorization of A can be written as
|
||||
//
|
||||
// A = Q * Λ * Qᵀ
|
||||
//
|
||||
// where Λ is a diagonal matrix whose entries are the eigenvalues, and Q is an
|
||||
// orthogonal matrix whose columns are the eigenvectors.
|
||||
//
|
||||
// If vectors is false, the eigenvectors are not computed and later calls to
|
||||
// VectorsTo and At will panic.
|
||||
//
|
||||
// Factorize returns whether the factorization succeeded. If it returns false,
|
||||
// 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.SymmetricDim()
|
||||
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 = getFloat64s(int(work[0]), false)
|
||||
ok = lapack64.Syev(jobz, sd.mat, w, work, len(work))
|
||||
putFloat64s(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 n×n matrix A in ascending
|
||||
// order.
|
||||
//
|
||||
// If dst is not nil, the values are stored in-place into dst and returned,
|
||||
// otherwise a new slice is allocated first. If dst is not nil, it must have
|
||||
// length equal to n.
|
||||
//
|
||||
// If the receiver does not contain a successful factorization, Values will
|
||||
// panic.
|
||||
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
|
||||
}
|
||||
|
||||
// RawValues returns the slice storing the eigenvalues of A in ascending order.
|
||||
//
|
||||
// If the returned slice is modified, the factorization is invalid and should
|
||||
// not be used.
|
||||
//
|
||||
// If the receiver does not contain a successful factorization, RawValues will
|
||||
// return nil.
|
||||
func (e *EigenSym) RawValues() []float64 {
|
||||
if !e.succFact() {
|
||||
return nil
|
||||
}
|
||||
return e.values
|
||||
}
|
||||
|
||||
// VectorsTo stores the orthonormal eigenvectors of the factorized n×n matrix A
|
||||
// into the columns of dst.
|
||||
//
|
||||
// If dst is empty, VectorsTo will resize dst to be n×n. When dst is non-empty,
|
||||
// VectorsTo will panic if dst is not n×n. VectorsTo will also panic if the
|
||||
// eigenvectors were not computed during the factorization, or if the receiver
|
||||
// does not contain a successful factorization.
|
||||
func (e *EigenSym) VectorsTo(dst *Dense) {
|
||||
if !e.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if !e.vectorsComputed {
|
||||
panic(noVectors)
|
||||
}
|
||||
r, c := e.vectors.Dims()
|
||||
if dst.IsEmpty() {
|
||||
dst.ReuseAs(r, c)
|
||||
} else {
|
||||
r2, c2 := dst.Dims()
|
||||
if r != r2 || c != c2 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
dst.Copy(e.vectors)
|
||||
}
|
||||
|
||||
// RawQ returns the orthogonal matrix Q from the spectral factorization of the
|
||||
// original matrix A
|
||||
//
|
||||
// A = Q * Λ * Qᵀ
|
||||
//
|
||||
// The columns of Q contain the eigenvectors of A.
|
||||
//
|
||||
// If the returned matrix is modified, the factorization is invalid and should
|
||||
// not be used.
|
||||
//
|
||||
// If the receiver does not contain a successful factorization or eigenvectors
|
||||
// not computed, RawU will return nil.
|
||||
func (e *EigenSym) RawQ() Matrix {
|
||||
if !e.succFact() || !e.vectorsComputed {
|
||||
return nil
|
||||
}
|
||||
return e.vectors
|
||||
}
|
||||
|
||||
// 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.CloneFrom(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 := getFloat64s(c, false)
|
||||
defer putFloat64s(wr)
|
||||
wi := getFloat64s(c, false)
|
||||
defer putFloat64s(wi)
|
||||
|
||||
work := []float64{0}
|
||||
lapack64.Geev(jobvl, jobvr, sd.mat, wr, wi, vl.mat, vr.mat, work, -1)
|
||||
work = getFloat64s(int(work[0]), false)
|
||||
first := lapack64.Geev(jobvl, jobvr, sd.mat, wr, wi, vl.mat, vr.mat, work, len(work))
|
||||
putFloat64s(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 stores the right eigenvectors of the decomposition into the columns
|
||||
// of dst. The computed eigenvectors are normalized to have Euclidean norm equal
|
||||
// to 1 and largest component real.
|
||||
//
|
||||
// If dst is empty, VectorsTo will resize dst to be n×n. When dst is
|
||||
// non-empty, VectorsTo will panic if dst is not n×n. VectorsTo will also
|
||||
// panic if the eigenvectors were not computed during the factorization,
|
||||
// or if the receiver does not contain a successful factorization.
|
||||
func (e *Eigen) VectorsTo(dst *CDense) {
|
||||
if !e.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if e.kind&EigenRight == 0 {
|
||||
panic(noVectors)
|
||||
}
|
||||
if dst.IsEmpty() {
|
||||
dst.ReuseAs(e.n, e.n)
|
||||
} else {
|
||||
r, c := dst.Dims()
|
||||
if r != e.n || c != e.n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
dst.Copy(e.rVectors)
|
||||
}
|
||||
|
||||
// LeftVectorsTo stores the left eigenvectors of the decomposition into the
|
||||
// columns of dst. The computed eigenvectors are normalized to have Euclidean
|
||||
// norm equal to 1 and largest component real.
|
||||
//
|
||||
// If dst is empty, LeftVectorsTo will resize dst to be n×n. When dst is
|
||||
// non-empty, LeftVectorsTo will panic if dst is not n×n. LeftVectorsTo will also
|
||||
// panic if the left eigenvectors were not computed during the factorization,
|
||||
// or if the receiver does not contain a successful factorization
|
||||
func (e *Eigen) LeftVectorsTo(dst *CDense) {
|
||||
if !e.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if e.kind&EigenLeft == 0 {
|
||||
panic(noVectors)
|
||||
}
|
||||
if dst.IsEmpty() {
|
||||
dst.ReuseAs(e.n, e.n)
|
||||
} else {
|
||||
r, c := dst.Dims()
|
||||
if r != e.n || c != e.n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
dst.Copy(e.lVectors)
|
||||
}
|
||||
154
vendor/gonum.org/v1/gonum/mat/errors.go
generated
vendored
Normal file
154
vendor/gonum.org/v1/gonum/mat/errors.go
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
// 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 inaccurate. 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ᵀ 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 (
|
||||
ErrNegativeDimension = Error{"mat: negative dimension"}
|
||||
ErrIndexOutOfRange = Error{"mat: index out of range"}
|
||||
ErrReuseNonEmpty = Error{"mat: reuse of non-empty matrix"}
|
||||
ErrRowAccess = Error{"mat: row index out of range"}
|
||||
ErrColAccess = Error{"mat: column index out of range"}
|
||||
ErrVectorAccess = Error{"mat: vector index out of range"}
|
||||
ErrZeroLength = Error{"mat: zero length in matrix dimension"}
|
||||
ErrRowLength = Error{"mat: row length mismatch"}
|
||||
ErrColLength = Error{"mat: col length mismatch"}
|
||||
ErrSquare = Error{"mat: expect square matrix"}
|
||||
ErrNormOrder = Error{"mat: invalid norm order for matrix"}
|
||||
ErrSingular = Error{"mat: matrix is singular"}
|
||||
ErrShape = Error{"mat: dimension mismatch"}
|
||||
ErrIllegalStride = Error{"mat: illegal stride"}
|
||||
ErrPivot = Error{"mat: malformed pivot list"}
|
||||
ErrTriangle = Error{"mat: triangular storage mismatch"}
|
||||
ErrTriangleSet = Error{"mat: triangular set out of bounds"}
|
||||
ErrBandwidth = Error{"mat: bandwidth out of range"}
|
||||
ErrBandSet = Error{"mat: band set out of bounds"}
|
||||
ErrDiagSet = Error{"mat: diagonal set out of bounds"}
|
||||
ErrSliceLengthMismatch = Error{"mat: input slice length mismatch"}
|
||||
ErrNotPSD = Error{"mat: input not positive symmetric definite"}
|
||||
ErrFailedEigen = Error{"mat: 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() }
|
||||
|
||||
const badCap = "mat: bad capacity"
|
||||
516
vendor/gonum.org/v1/gonum/mat/format.go
generated
vendored
Normal file
516
vendor/gonum.org/v1/gonum/mat/format.go
generated
vendored
Normal file
@@ -0,0 +1,516 @@
|
||||
// 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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
format func(m Matrix, prefix string, margin int, dot byte, squeeze bool, fs fmt.State, c rune)
|
||||
}
|
||||
|
||||
// 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 after the first line.
|
||||
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 behavior to minimise column width for each individual column.
|
||||
func Squeeze() FormatOption {
|
||||
return func(f *formatter) { f.squeeze = true }
|
||||
}
|
||||
|
||||
// FormatMATLAB sets the printing behavior to output MATLAB syntax. If MATLAB syntax is
|
||||
// specified, the ' ' verb flag and Excerpt option are ignored. If the alternative syntax
|
||||
// verb flag, '#' is used the matrix is formatted in rows and columns.
|
||||
func FormatMATLAB() FormatOption {
|
||||
return func(f *formatter) { f.format = formatMATLAB }
|
||||
}
|
||||
|
||||
// FormatPython sets the printing behavior to output Python syntax. If Python syntax is
|
||||
// specified, the ' ' verb flag and Excerpt option are ignored. If the alternative syntax
|
||||
// verb flag, '#' is used the matrix is formatted in rows and columns.
|
||||
func FormatPython() FormatOption {
|
||||
return func(f *formatter) { f.format = formatPython }
|
||||
}
|
||||
|
||||
// Format satisfies the fmt.Formatter interface.
|
||||
func (f formatter) Format(fs fmt.State, c rune) {
|
||||
if c == 'v' && fs.Flag('#') && f.format == nil {
|
||||
fmt.Fprintf(fs, "%#v", f.matrix)
|
||||
return
|
||||
}
|
||||
if f.format == nil {
|
||||
f.format = format
|
||||
}
|
||||
f.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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// formatMATLAB prints a MATLAB 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.
|
||||
// The printed range of the matrix can be limited by specifying a positive value for margin;
|
||||
// If squeeze is true, column widths are determined on a per-column basis.
|
||||
//
|
||||
// formatMATLAB will not provide Go syntax output.
|
||||
func formatMATLAB(m Matrix, prefix string, _ int, _ byte, squeeze bool, fs fmt.State, c rune) {
|
||||
rows, cols := m.Dims()
|
||||
|
||||
prec, pOk := fs.Precision()
|
||||
width, _ := fs.Width()
|
||||
if !fs.Flag('#') {
|
||||
switch c {
|
||||
case 'v', 'e', 'E', 'f', 'F', 'g', 'G':
|
||||
default:
|
||||
fmt.Fprintf(fs, "%%!%c(%T=Dims(%d, %d))", c, m, rows, cols)
|
||||
return
|
||||
}
|
||||
format := fmtString(fs, c, prec, width)
|
||||
fs.Write([]byte{'['})
|
||||
for i := 0; i < rows; i++ {
|
||||
if i != 0 {
|
||||
fs.Write([]byte("; "))
|
||||
}
|
||||
for j := 0; j < cols; j++ {
|
||||
if j != 0 {
|
||||
fs.Write([]byte{' '})
|
||||
}
|
||||
fmt.Fprintf(fs, format, m.At(i, j))
|
||||
}
|
||||
}
|
||||
fs.Write([]byte{']'})
|
||||
return
|
||||
}
|
||||
|
||||
if !pOk {
|
||||
prec = -1
|
||||
}
|
||||
|
||||
printed := rows
|
||||
if cols > printed {
|
||||
printed = cols
|
||||
}
|
||||
|
||||
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 = max(width, maxWidth)
|
||||
pad = make([]byte, max(width, 1))
|
||||
for i := range pad {
|
||||
pad[i] = ' '
|
||||
}
|
||||
|
||||
for i := 0; i < rows; i++ {
|
||||
var el string
|
||||
switch {
|
||||
case rows == 1:
|
||||
fmt.Fprint(fs, "[")
|
||||
el = "]"
|
||||
case i == 0:
|
||||
fmt.Fprint(fs, "[\n"+prefix+" ")
|
||||
el = "\n"
|
||||
case i < rows-1:
|
||||
fmt.Fprint(fs, prefix+" ")
|
||||
el = "\n"
|
||||
default:
|
||||
fmt.Fprint(fs, prefix+" ")
|
||||
el = "\n" + prefix + "]"
|
||||
}
|
||||
|
||||
for j := 0; j < cols; j++ {
|
||||
v := m.At(i, j)
|
||||
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[:1])
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprint(fs, el)
|
||||
}
|
||||
}
|
||||
|
||||
// formatPython prints a Python 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.
|
||||
// The printed range of the matrix can be limited by specifying a positive value for margin;
|
||||
// If squeeze is true, column widths are determined on a per-column basis.
|
||||
//
|
||||
// formatPython will not provide Go syntax output.
|
||||
func formatPython(m Matrix, prefix string, _ int, _ byte, squeeze bool, fs fmt.State, c rune) {
|
||||
rows, cols := m.Dims()
|
||||
|
||||
prec, pOk := fs.Precision()
|
||||
width, _ := fs.Width()
|
||||
if !fs.Flag('#') {
|
||||
switch c {
|
||||
case 'v', 'e', 'E', 'f', 'F', 'g', 'G':
|
||||
default:
|
||||
fmt.Fprintf(fs, "%%!%c(%T=Dims(%d, %d))", c, m, rows, cols)
|
||||
return
|
||||
}
|
||||
format := fmtString(fs, c, prec, width)
|
||||
fs.Write([]byte{'['})
|
||||
if rows > 1 {
|
||||
fs.Write([]byte{'['})
|
||||
}
|
||||
for i := 0; i < rows; i++ {
|
||||
if i != 0 {
|
||||
fs.Write([]byte("], ["))
|
||||
}
|
||||
for j := 0; j < cols; j++ {
|
||||
if j != 0 {
|
||||
fs.Write([]byte(", "))
|
||||
}
|
||||
fmt.Fprintf(fs, format, m.At(i, j))
|
||||
}
|
||||
}
|
||||
if rows > 1 {
|
||||
fs.Write([]byte{']'})
|
||||
}
|
||||
fs.Write([]byte{']'})
|
||||
return
|
||||
}
|
||||
|
||||
if !pOk {
|
||||
prec = -1
|
||||
}
|
||||
|
||||
printed := rows
|
||||
if cols > printed {
|
||||
printed = cols
|
||||
}
|
||||
|
||||
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 = max(width, maxWidth)
|
||||
pad = make([]byte, max(width, 1))
|
||||
for i := range pad {
|
||||
pad[i] = ' '
|
||||
}
|
||||
|
||||
for i := 0; i < rows; i++ {
|
||||
if i != 0 {
|
||||
fmt.Fprint(fs, prefix)
|
||||
}
|
||||
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++ {
|
||||
v := m.At(i, j)
|
||||
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([]byte{','})
|
||||
fs.Write(pad[:1])
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprint(fs, el)
|
||||
}
|
||||
}
|
||||
|
||||
// This is horrible, but it's what we have.
|
||||
func fmtString(fs fmt.State, c rune, prec, width int) string {
|
||||
var b strings.Builder
|
||||
b.WriteByte('%')
|
||||
for _, f := range "0+- " {
|
||||
if fs.Flag(int(f)) {
|
||||
b.WriteByte(byte(f))
|
||||
}
|
||||
}
|
||||
if width >= 0 {
|
||||
fmt.Fprint(&b, width)
|
||||
}
|
||||
if prec >= 0 {
|
||||
b.WriteByte('.')
|
||||
if prec > 0 {
|
||||
fmt.Fprint(&b, prec)
|
||||
}
|
||||
}
|
||||
b.WriteRune(c)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
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 }
|
||||
436
vendor/gonum.org/v1/gonum/mat/gsvd.go
generated
vendored
Normal file
436
vendor/gonum.org/v1/gonum/mat/gsvd.go
generated
vendored
Normal file
@@ -0,0 +1,436 @@
|
||||
// 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ᵀ
|
||||
//
|
||||
// B = V * Σ₂ * [ 0 R ] * Qᵀ
|
||||
//
|
||||
// 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ᵀ Bᵀ ]ᵀ.
|
||||
//
|
||||
// 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 GSVDU, GSVDV and 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ᵀ Bᵀ ]ᵀ.
|
||||
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 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
|
||||
// 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
|
||||
// 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 into dst. [ 0 R ] is of size (k+l)×c.
|
||||
//
|
||||
// If dst is empty, ZeroRTo will resize dst to be (k+l)×c. When dst is
|
||||
// non-empty, ZeroRTo will panic if dst is not (k+l)×c. ZeroRTo will also panic
|
||||
// if the receiver does not contain a successful factorization.
|
||||
func (gsvd *GSVD) ZeroRTo(dst *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.IsEmpty() {
|
||||
dst.ReuseAs(k+l, c)
|
||||
} else {
|
||||
r2, c2 := dst.Dims()
|
||||
if r2 != k+l || c != c2 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
dst.Zero()
|
||||
}
|
||||
a := Dense{
|
||||
mat: gsvd.a,
|
||||
capRows: r,
|
||||
capCols: c,
|
||||
}
|
||||
dst.slice(0, h, c-k-l, c).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).Copy(b.Slice(r-k, l, c+r-k-l, c))
|
||||
}
|
||||
}
|
||||
|
||||
// SigmaATo extracts the matrix Σ₁ from the singular value decomposition, storing
|
||||
// the result into dst. Σ₁ is size r×(k+l).
|
||||
//
|
||||
// If dst is empty, SigmaATo will resize dst to be r×(k+l). When dst is
|
||||
// non-empty, SigmATo will panic if dst is not r×(k+l). SigmaATo will also
|
||||
// panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *GSVD) SigmaATo(dst *Dense) {
|
||||
if !gsvd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
r := gsvd.r
|
||||
k := gsvd.k
|
||||
l := gsvd.l
|
||||
if dst.IsEmpty() {
|
||||
dst.ReuseAs(r, k+l)
|
||||
} else {
|
||||
r2, c := dst.Dims()
|
||||
if r2 != r || c != k+l {
|
||||
panic(ErrShape)
|
||||
}
|
||||
dst.Zero()
|
||||
}
|
||||
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])
|
||||
}
|
||||
}
|
||||
|
||||
// SigmaBTo extracts the matrix Σ₂ from the singular value decomposition, storing
|
||||
// the result into dst. Σ₂ is size p×(k+l).
|
||||
//
|
||||
// If dst is empty, SigmaBTo will resize dst to be p×(k+l). When dst is
|
||||
// non-empty, SigmBTo will panic if dst is not p×(k+l). SigmaBTo will also
|
||||
// panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *GSVD) SigmaBTo(dst *Dense) {
|
||||
if !gsvd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
r := gsvd.r
|
||||
p := gsvd.p
|
||||
k := gsvd.k
|
||||
l := gsvd.l
|
||||
if dst.IsEmpty() {
|
||||
dst.ReuseAs(p, k+l)
|
||||
} else {
|
||||
r, c := dst.Dims()
|
||||
if r != p || c != k+l {
|
||||
panic(ErrShape)
|
||||
}
|
||||
dst.Zero()
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// UTo extracts the matrix U from the singular value decomposition, storing
|
||||
// the result into dst. U is size r×r.
|
||||
//
|
||||
// If dst is empty, UTo will resize dst to be r×r. When dst is
|
||||
// non-empty, UTo will panic if dst is not r×r. UTo will also
|
||||
// panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *GSVD) UTo(dst *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.IsEmpty() {
|
||||
dst.ReuseAs(r, c)
|
||||
} else {
|
||||
r2, c2 := dst.Dims()
|
||||
if r != r2 || c != c2 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
|
||||
tmp := &Dense{
|
||||
mat: gsvd.u,
|
||||
capRows: r,
|
||||
capCols: c,
|
||||
}
|
||||
dst.Copy(tmp)
|
||||
}
|
||||
|
||||
// VTo extracts the matrix V from the singular value decomposition, storing
|
||||
// the result into dst. V is size p×p.
|
||||
//
|
||||
// If dst is empty, VTo will resize dst to be p×p. When dst is
|
||||
// non-empty, VTo will panic if dst is not p×p. VTo will also
|
||||
// panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *GSVD) VTo(dst *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.IsEmpty() {
|
||||
dst.ReuseAs(r, c)
|
||||
} else {
|
||||
r2, c2 := dst.Dims()
|
||||
if r != r2 || c != c2 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
|
||||
tmp := &Dense{
|
||||
mat: gsvd.v,
|
||||
capRows: r,
|
||||
capCols: c,
|
||||
}
|
||||
dst.Copy(tmp)
|
||||
}
|
||||
|
||||
// QTo extracts the matrix Q from the singular value decomposition, storing
|
||||
// the result into dst. Q is size c×c.
|
||||
//
|
||||
// If dst is empty, QTo will resize dst to be c×c. When dst is
|
||||
// non-empty, QTo will panic if dst is not c×c. QTo will also
|
||||
// panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *GSVD) QTo(dst *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.IsEmpty() {
|
||||
dst.ReuseAs(r, c)
|
||||
} else {
|
||||
r2, c2 := dst.Dims()
|
||||
if r != r2 || c != c2 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
|
||||
tmp := &Dense{
|
||||
mat: gsvd.q,
|
||||
capRows: r,
|
||||
capCols: c,
|
||||
}
|
||||
dst.Copy(tmp)
|
||||
}
|
||||
239
vendor/gonum.org/v1/gonum/mat/hogsvd.go
generated
vendored
Normal file
239
vendor/gonum.org/v1/gonum/mat/hogsvd.go
generated
vendored
Normal file
@@ -0,0 +1,239 @@
|
||||
// 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ᵀ
|
||||
// M_1 = U_1 * Σ_1 * Vᵀ
|
||||
// .
|
||||
// .
|
||||
// .
|
||||
// M_{n-1} = U_{n-1} * Σ_{n-1} * Vᵀ
|
||||
//
|
||||
// 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 := getDenseWorkspace(c, c, true)
|
||||
defer putDenseWorkspace(s)
|
||||
sij := getDenseWorkspace(c, c, false)
|
||||
defer putDenseWorkspace(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
|
||||
}
|
||||
var vc CDense
|
||||
eig.VectorsTo(&vc)
|
||||
// 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 := getDenseWorkspace(c, r, false)
|
||||
defer putDenseWorkspace(biT)
|
||||
for i, d := range m {
|
||||
// All calls to reset will leave an emptied
|
||||
// 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].CloneFrom(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 empty, UTo will resize dst to be r×c. When dst is
|
||||
// non-empty, UTo will panic if dst is not r×c. UTo will also
|
||||
// panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *HOGSVD) UTo(dst *Dense, n int) {
|
||||
if !gsvd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if n < 0 || gsvd.n <= n {
|
||||
panic("hogsvd: invalid index")
|
||||
}
|
||||
r, c := gsvd.b[n].Dims()
|
||||
if dst.IsEmpty() {
|
||||
dst.ReuseAs(r, c)
|
||||
} else {
|
||||
r2, c2 := dst.Dims()
|
||||
if r != r2 || c != c2 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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 empty, VTo will resize dst to be c×c. When dst is
|
||||
// non-empty, VTo will panic if dst is not c×c. VTo will also
|
||||
// panic if the receiver does not contain a successful factorization.
|
||||
func (gsvd *HOGSVD) VTo(dst *Dense) {
|
||||
if !gsvd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
r, c := gsvd.v.Dims()
|
||||
if dst.IsEmpty() {
|
||||
dst.ReuseAs(r, c)
|
||||
} else {
|
||||
r2, c2 := dst.Dims()
|
||||
if r != r2 || c != c2 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
dst.Copy(gsvd.v)
|
||||
}
|
||||
398
vendor/gonum.org/v1/gonum/mat/index_bound_checks.go
generated
vendored
Normal file
398
vendor/gonum.org/v1/gonum/mat/index_bound_checks.go
generated
vendored
Normal file
@@ -0,0 +1,398 @@
|
||||
// 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.
|
||||
|
||||
//go:build bounds
|
||||
// +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
|
||||
}
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (a *Tridiag) At(i, j int) float64 {
|
||||
return a.at(i, j)
|
||||
}
|
||||
|
||||
func (a *Tridiag) at(i, j int) float64 {
|
||||
if uint(i) >= uint(a.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(a.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
switch i - j {
|
||||
case -1:
|
||||
return a.mat.DU[i]
|
||||
case 0:
|
||||
return a.mat.D[i]
|
||||
case 1:
|
||||
return a.mat.DL[j]
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (a *Tridiag) SetBand(i, j int, v float64) {
|
||||
a.set(i, j, v)
|
||||
}
|
||||
|
||||
func (a *Tridiag) set(i, j int, v float64) {
|
||||
if uint(i) >= uint(a.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(a.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
switch i - j {
|
||||
case -1:
|
||||
a.mat.DU[i] = v
|
||||
case 0:
|
||||
a.mat.D[i] = v
|
||||
case 1:
|
||||
a.mat.DL[j] = v
|
||||
default:
|
||||
panic(ErrBandSet)
|
||||
}
|
||||
}
|
||||
400
vendor/gonum.org/v1/gonum/mat/index_no_bound_checks.go
generated
vendored
Normal file
400
vendor/gonum.org/v1/gonum/mat/index_no_bound_checks.go
generated
vendored
Normal file
@@ -0,0 +1,400 @@
|
||||
// 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.
|
||||
|
||||
//go:build !bounds
|
||||
// +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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (a *Tridiag) At(i, j int) float64 {
|
||||
if uint(i) >= uint(a.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(a.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
return a.at(i, j)
|
||||
}
|
||||
|
||||
func (a *Tridiag) at(i, j int) float64 {
|
||||
switch i - j {
|
||||
case -1:
|
||||
return a.mat.DU[i]
|
||||
case 0:
|
||||
return a.mat.D[i]
|
||||
case 1:
|
||||
return a.mat.DL[j]
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (a *Tridiag) SetBand(i, j int, v float64) {
|
||||
if uint(i) >= uint(a.mat.N) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(a.mat.N) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
a.set(i, j, v)
|
||||
}
|
||||
|
||||
func (a *Tridiag) set(i, j int, v float64) {
|
||||
switch i - j {
|
||||
case -1:
|
||||
a.mat.DU[i] = v
|
||||
case 0:
|
||||
a.mat.D[i] = v
|
||||
case 1:
|
||||
a.mat.DL[j] = v
|
||||
default:
|
||||
panic(ErrBandSet)
|
||||
}
|
||||
}
|
||||
126
vendor/gonum.org/v1/gonum/mat/inner.go
generated
vendored
Normal file
126
vendor/gonum.org/v1/gonum/mat/inner.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
// 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ᵀ A y
|
||||
//
|
||||
// between the vectors x and y with matrix A, where x and y are treated as
|
||||
// column vectors.
|
||||
//
|
||||
// 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
|
||||
}
|
||||
495
vendor/gonum.org/v1/gonum/mat/io.go
generated
vendored
Normal file
495
vendor/gonum.org/v1/gonum/mat/io.go
generated
vendored
Normal file
@@ -0,0 +1,495 @@
|
||||
// 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{})
|
||||
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-empty Dense matrix.
|
||||
//
|
||||
// See MarshalBinary for the on-disk layout.
|
||||
//
|
||||
// Limited checks on the validity of the binary input are performed:
|
||||
// - 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.IsEmpty() {
|
||||
panic("mat: unmarshal into non-empty 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.reuseAsNonZeroed(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-empty Dense matrix.
|
||||
//
|
||||
// See MarshalBinary for the on-disk layout.
|
||||
//
|
||||
// Limited checks on the validity of the binary input are performed:
|
||||
// - 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.IsEmpty() {
|
||||
panic("mat: unmarshal into non-empty 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.reuseAsNonZeroed(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 MarshalBinary 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-empty VecDense.
|
||||
//
|
||||
// See MarshalBinary for the on-disk layout.
|
||||
//
|
||||
// Limited checks on the validity of the binary input are performed:
|
||||
// - 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.IsEmpty() {
|
||||
panic("mat: unmarshal into non-empty 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.reuseAsNonZeroed(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-empty 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.IsEmpty() {
|
||||
panic("mat: unmarshal into non-empty 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.reuseAsNonZeroed(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
|
||||
}
|
||||
305
vendor/gonum.org/v1/gonum/mat/lq.go
generated
vendored
Normal file
305
vendor/gonum.org/v1/gonum/mat/lq.go
generated
vendored
Normal file
@@ -0,0 +1,305 @@
|
||||
// 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
|
||||
q *Dense
|
||||
tau []float64
|
||||
cond float64
|
||||
}
|
||||
|
||||
// Dims returns the dimensions of the matrix.
|
||||
func (lq *LQ) Dims() (r, c int) {
|
||||
if lq.lq == nil {
|
||||
return 0, 0
|
||||
}
|
||||
return lq.lq.Dims()
|
||||
}
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (lq *LQ) At(i, j int) float64 {
|
||||
m, n := lq.Dims()
|
||||
if uint(i) >= uint(m) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(n) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
|
||||
var val float64
|
||||
for k := 0; k <= i; k++ {
|
||||
val += lq.lq.at(i, k) * lq.q.at(k, j)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// T performs an implicit transpose by returning the receiver inside a
|
||||
// Transpose.
|
||||
func (lq *LQ) T() Matrix {
|
||||
return Transpose{lq}
|
||||
}
|
||||
|
||||
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ᵀ * L^-1|
|
||||
// = |L| |L^-1| = κ(L),
|
||||
// where we used that fact that Q^-1 = Qᵀ. 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 := getFloat64s(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
|
||||
putFloat64s(work)
|
||||
putInts(iwork)
|
||||
}
|
||||
|
||||
// Factorize computes the LQ factorization of an m×n matrix a where m <= n. 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 lower triangular matrix.
|
||||
// L and Q can be extracted using 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)
|
||||
}
|
||||
if lq.lq == nil {
|
||||
lq.lq = &Dense{}
|
||||
}
|
||||
lq.lq.CloneFrom(a)
|
||||
work := []float64{0}
|
||||
lq.tau = make([]float64, m)
|
||||
lapack64.Gelqf(lq.lq.mat, lq.tau, work, -1)
|
||||
work = getFloat64s(int(work[0]), false)
|
||||
lapack64.Gelqf(lq.lq.mat, lq.tau, work, len(work))
|
||||
putFloat64s(work)
|
||||
lq.updateCond(norm)
|
||||
lq.updateQ()
|
||||
}
|
||||
|
||||
func (lq *LQ) updateQ() {
|
||||
_, n := lq.Dims()
|
||||
if lq.q == nil {
|
||||
lq.q = NewDense(n, n, nil)
|
||||
} else {
|
||||
lq.q.reuseAsNonZeroed(n, n)
|
||||
}
|
||||
// Construct Q from the elementary reflectors.
|
||||
lq.q.Copy(lq.lq)
|
||||
work := []float64{0}
|
||||
lapack64.Orglq(lq.q.mat, lq.tau, work, -1)
|
||||
work = getFloat64s(int(work[0]), false)
|
||||
lapack64.Orglq(lq.q.mat, lq.tau, work, len(work))
|
||||
putFloat64s(work)
|
||||
}
|
||||
|
||||
// isValid returns whether the receiver contains a factorization.
|
||||
func (lq *LQ) isValid() bool {
|
||||
return lq.lq != nil && !lq.lq.IsEmpty()
|
||||
}
|
||||
|
||||
// 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 empty, LTo will resize dst to be r×c. When dst is
|
||||
// non-empty, LTo will panic if dst is not r×c. LTo will also panic
|
||||
// if the receiver does not contain a successful factorization.
|
||||
func (lq *LQ) LTo(dst *Dense) {
|
||||
if !lq.isValid() {
|
||||
panic(badLQ)
|
||||
}
|
||||
|
||||
r, c := lq.lq.Dims()
|
||||
if dst.IsEmpty() {
|
||||
dst.ReuseAs(r, c)
|
||||
} else {
|
||||
r2, c2 := dst.Dims()
|
||||
if r != r2 || c != c2 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
// 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])
|
||||
}
|
||||
}
|
||||
|
||||
// QTo extracts the n×n orthonormal matrix Q from an LQ decomposition.
|
||||
//
|
||||
// If dst is empty, QTo will resize dst to be n×n. When dst is
|
||||
// non-empty, QTo will panic if dst is not n×n. QTo will also panic
|
||||
// if the receiver does not contain a successful factorization.
|
||||
func (lq *LQ) QTo(dst *Dense) {
|
||||
if !lq.isValid() {
|
||||
panic(badLQ)
|
||||
}
|
||||
|
||||
_, n := lq.lq.Dims()
|
||||
if dst.IsEmpty() {
|
||||
dst.ReuseAs(n, n)
|
||||
} else {
|
||||
m2, n2 := dst.Dims()
|
||||
if n != m2 || n != n2 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
dst.Copy(lq.q)
|
||||
}
|
||||
|
||||
// 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.reuseAsNonZeroed(r, bc)
|
||||
} else {
|
||||
if r != br {
|
||||
panic(ErrShape)
|
||||
}
|
||||
dst.reuseAsNonZeroed(c, bc)
|
||||
}
|
||||
// Do not need to worry about overlap between x and b because w has its own
|
||||
// independent storage.
|
||||
w := getDenseWorkspace(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 = getFloat64s(int(work[0]), false)
|
||||
lapack64.Ormlq(blas.Left, blas.NoTrans, lq.lq.mat, lq.tau, w.mat, work, len(work))
|
||||
putFloat64s(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 = getFloat64s(int(work[0]), false)
|
||||
lapack64.Ormlq(blas.Left, blas.Trans, lq.lq.mat, lq.tau, w.mat, work, len(work))
|
||||
putFloat64s(work)
|
||||
}
|
||||
// x was set above to be the correct size for the result.
|
||||
dst.Copy(w)
|
||||
putDenseWorkspace(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.reuseAsNonZeroed(r)
|
||||
} else {
|
||||
dst.reuseAsNonZeroed(c)
|
||||
}
|
||||
return lq.SolveTo(dst.asDense(), trans, bm)
|
||||
}
|
||||
485
vendor/gonum.org/v1/gonum/mat/lu.go
generated
vendored
Normal file
485
vendor/gonum.org/v1/gonum/mat/lu.go
generated
vendored
Normal file
@@ -0,0 +1,485 @@
|
||||
// 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 square n×n matrix represented by its LU factorization with partial
|
||||
// pivoting.
|
||||
//
|
||||
// The factorization has the form
|
||||
//
|
||||
// A = P * L * U
|
||||
//
|
||||
// where P is a permutation matrix, L is lower triangular with unit diagonal
|
||||
// elements, and U is upper triangular.
|
||||
//
|
||||
// Note that this matrix representation is useful for certain operations, in
|
||||
// particular for solving linear systems of equations. It is very inefficient at
|
||||
// other operations, in particular At is slow.
|
||||
type LU struct {
|
||||
lu *Dense
|
||||
swaps []int
|
||||
piv []int
|
||||
cond float64
|
||||
ok bool // Whether A is nonsingular
|
||||
}
|
||||
|
||||
var _ Matrix = (*LU)(nil)
|
||||
|
||||
// Dims returns the dimensions of the matrix A.
|
||||
func (lu *LU) Dims() (r, c int) {
|
||||
if lu.lu == nil {
|
||||
return 0, 0
|
||||
}
|
||||
return lu.lu.Dims()
|
||||
}
|
||||
|
||||
// At returns the element of A at row i, column j.
|
||||
func (lu *LU) At(i, j int) float64 {
|
||||
n, _ := lu.Dims()
|
||||
if uint(i) >= uint(n) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(n) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
|
||||
i = lu.piv[i]
|
||||
var val float64
|
||||
for k := 0; k < min(i, j+1); k++ {
|
||||
val += lu.lu.at(i, k) * lu.lu.at(k, j)
|
||||
}
|
||||
if i <= j {
|
||||
val += lu.lu.at(i, j)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// T performs an implicit transpose by returning the receiver inside a
|
||||
// Transpose.
|
||||
func (lu *LU) T() Matrix {
|
||||
return Transpose{lu}
|
||||
}
|
||||
|
||||
// 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 := getFloat64s(4*n, false)
|
||||
defer putFloat64s(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 in the receiver. The LU decomposition will complete regardless of the
|
||||
// singularity of a.
|
||||
//
|
||||
// The L and U matrix factors can be extracted from the factorization using the
|
||||
// LTo and UTo methods. The matrix P can be extracted as a row permutation using
|
||||
// the RowPivots method and applied using Dense.PermuteRows.
|
||||
func (lu *LU) Factorize(a Matrix) {
|
||||
lu.factorize(a, CondNorm)
|
||||
}
|
||||
|
||||
func (lu *LU) factorize(a Matrix, norm lapack.MatrixNorm) {
|
||||
m, n := a.Dims()
|
||||
if m != n {
|
||||
panic(ErrSquare)
|
||||
}
|
||||
if lu.lu == nil {
|
||||
lu.lu = NewDense(n, n, nil)
|
||||
} else {
|
||||
lu.lu.Reset()
|
||||
lu.lu.reuseAsNonZeroed(n, n)
|
||||
}
|
||||
lu.lu.Copy(a)
|
||||
lu.swaps = useInt(lu.swaps, n)
|
||||
lu.piv = useInt(lu.piv, n)
|
||||
work := getFloat64s(n, false)
|
||||
anorm := lapack64.Lange(norm, lu.lu.mat, work)
|
||||
putFloat64s(work)
|
||||
lu.ok = lapack64.Getrf(lu.lu.mat, lu.swaps)
|
||||
lu.updatePivots(lu.swaps)
|
||||
lu.updateCond(anorm, norm)
|
||||
}
|
||||
|
||||
func (lu *LU) updatePivots(swaps []int) {
|
||||
// Replay the sequence of row swaps in order to find the row permutation.
|
||||
for i := range lu.piv {
|
||||
lu.piv[i] = i
|
||||
}
|
||||
n, _ := lu.Dims()
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
v := swaps[i]
|
||||
lu.piv[i], lu.piv[v] = lu.piv[v], lu.piv[i]
|
||||
}
|
||||
}
|
||||
|
||||
// isValid returns whether the receiver contains a factorization.
|
||||
func (lu *LU) isValid() bool {
|
||||
return lu.lu != nil && !lu.lu.IsEmpty()
|
||||
}
|
||||
|
||||
// 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.swaps = lu.swaps[:0]
|
||||
lu.piv = lu.piv[:0]
|
||||
}
|
||||
|
||||
func (lu *LU) isZero() bool {
|
||||
return len(lu.swaps) == 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 {
|
||||
if !lu.ok {
|
||||
return 0
|
||||
}
|
||||
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 := getFloat64s(n, false)
|
||||
defer putFloat64s(logDiag)
|
||||
sign = 1.0
|
||||
for i := 0; i < n; i++ {
|
||||
v := lu.lu.at(i, i)
|
||||
if v < 0 {
|
||||
sign *= -1
|
||||
}
|
||||
if lu.swaps[i] != i {
|
||||
sign *= -1
|
||||
}
|
||||
logDiag[i] = math.Log(math.Abs(v))
|
||||
}
|
||||
return floats.Sum(logDiag), sign
|
||||
}
|
||||
|
||||
// RowPivots returns the row permutation that represents the permutation matrix
|
||||
// P from the LU factorization
|
||||
//
|
||||
// A = P * L * U.
|
||||
//
|
||||
// If dst is nil, a new slice is allocated and returned. If dst is not nil and
|
||||
// the length of dst does not equal the size of the factorized matrix, RowPivots
|
||||
// will panic. RowPivots will panic if the receiver does not contain a
|
||||
// factorization.
|
||||
func (lu *LU) RowPivots(dst []int) []int {
|
||||
if !lu.isValid() {
|
||||
panic(badLU)
|
||||
}
|
||||
_, n := lu.lu.Dims()
|
||||
if dst == nil {
|
||||
dst = make([]int, n)
|
||||
}
|
||||
if len(dst) != n {
|
||||
panic(badSliceLength)
|
||||
}
|
||||
copy(dst, lu.piv)
|
||||
return dst
|
||||
}
|
||||
|
||||
// Deprecated: Use RowPivots instead.
|
||||
func (lu *LU) Pivot(dst []int) []int {
|
||||
return lu.RowPivots(dst)
|
||||
}
|
||||
|
||||
// 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ᵀ.
|
||||
// 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() {
|
||||
lu.swaps = useInt(lu.swaps, n)
|
||||
lu.piv = useInt(lu.piv, n)
|
||||
if lu.lu == nil {
|
||||
lu.lu = NewDense(n, n, nil)
|
||||
} else {
|
||||
lu.lu.reuseAsNonZeroed(n, n)
|
||||
}
|
||||
} else if len(lu.swaps) != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
copy(lu.swaps, orig.swaps)
|
||||
lu.updatePivots(lu.swaps)
|
||||
lu.lu.Copy(orig.lu)
|
||||
}
|
||||
|
||||
xs := getFloat64s(n, false)
|
||||
defer putFloat64s(xs)
|
||||
ys := getFloat64s(n, false)
|
||||
defer putFloat64s(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.swaps {
|
||||
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 empty, LTo will resize dst to be a lower-triangular n×n matrix.
|
||||
// When dst is non-empty, LTo will panic if dst is not n×n or not Lower.
|
||||
// LTo will also panic if the receiver does not contain a successful
|
||||
// factorization.
|
||||
func (lu *LU) LTo(dst *TriDense) *TriDense {
|
||||
if !lu.isValid() {
|
||||
panic(badLU)
|
||||
}
|
||||
|
||||
_, n := lu.lu.Dims()
|
||||
if dst.IsEmpty() {
|
||||
dst.ReuseAsTri(n, Lower)
|
||||
} else {
|
||||
n2, kind := dst.Triangle()
|
||||
if n != n2 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if kind != Lower {
|
||||
panic(ErrTriangle)
|
||||
}
|
||||
}
|
||||
// Extract the lower triangular elements.
|
||||
for i := 1; i < n; i++ {
|
||||
copy(dst.mat.Data[i*dst.mat.Stride:i*dst.mat.Stride+i], lu.lu.mat.Data[i*lu.lu.mat.Stride:i*lu.lu.mat.Stride+i])
|
||||
}
|
||||
// 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 empty, UTo will resize dst to be an upper-triangular n×n matrix.
|
||||
// When dst is non-empty, UTo will panic if dst is not n×n or not Upper.
|
||||
// UTo will also panic if the receiver does not contain a successful
|
||||
// factorization.
|
||||
func (lu *LU) UTo(dst *TriDense) {
|
||||
if !lu.isValid() {
|
||||
panic(badLU)
|
||||
}
|
||||
|
||||
_, n := lu.lu.Dims()
|
||||
if dst.IsEmpty() {
|
||||
dst.ReuseAsTri(n, Upper)
|
||||
} else {
|
||||
n2, kind := dst.Triangle()
|
||||
if n != n2 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if kind != Upper {
|
||||
panic(ErrTriangle)
|
||||
}
|
||||
}
|
||||
// Extract the upper triangular elements.
|
||||
for i := 0; i < n; i++ {
|
||||
copy(dst.mat.Data[i*dst.mat.Stride+i:i*dst.mat.Stride+n], lu.lu.mat.Data[i*lu.lu.mat.Stride+i:i*lu.lu.mat.Stride+n])
|
||||
}
|
||||
}
|
||||
|
||||
// SolveTo solves a system of linear equations
|
||||
//
|
||||
// A * X = B if trans == false
|
||||
// Aᵀ * X = B if trans == true
|
||||
//
|
||||
// using the LU factorization of A stored in the receiver. The solution 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)
|
||||
}
|
||||
|
||||
if !lu.ok {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
|
||||
dst.reuseAsNonZeroed(n, bc)
|
||||
bU, _ := untranspose(b)
|
||||
if dst == bU {
|
||||
var restore func()
|
||||
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.swaps)
|
||||
if lu.cond > ConditionTolerance {
|
||||
return Condition(lu.cond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SolveVecTo solves a system of linear equations
|
||||
//
|
||||
// A * x = b if trans == false
|
||||
// Aᵀ * x = b if trans == true
|
||||
//
|
||||
// using the LU factorization of A stored in the receiver. The solution 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. 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.reuseAsNonZeroed(n)
|
||||
return lu.SolveTo(dst.asDense(), trans, b)
|
||||
case RawVectorer:
|
||||
if dst != b {
|
||||
dst.checkOverlap(rv.RawVector())
|
||||
}
|
||||
|
||||
if !lu.ok {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
|
||||
dst.reuseAsNonZeroed(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.swaps)
|
||||
if lu.cond > ConditionTolerance {
|
||||
return Condition(lu.cond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
1014
vendor/gonum.org/v1/gonum/mat/matrix.go
generated
vendored
Normal file
1014
vendor/gonum.org/v1/gonum/mat/matrix.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
32
vendor/gonum.org/v1/gonum/mat/offset.go
generated
vendored
Normal file
32
vendor/gonum.org/v1/gonum/mat/offset.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// 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.
|
||||
|
||||
//go:build !safe
|
||||
// +build !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)))
|
||||
}
|
||||
|
||||
// offsetComplex returns the number of complex128 values b[0] is after a[0].
|
||||
func offsetComplex(a, b []complex128) 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(complex128(0)))
|
||||
}
|
||||
40
vendor/gonum.org/v1/gonum/mat/offset_appengine.go
generated
vendored
Normal file
40
vendor/gonum.org/v1/gonum/mat/offset_appengine.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
// 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.
|
||||
|
||||
//go:build safe
|
||||
// +build 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
|
||||
}
|
||||
|
||||
var sizeOfComplex128 = int(reflect.TypeOf(complex128(0)).Size())
|
||||
|
||||
// offsetComplex returns the number of complex128 values b[0] is after a[0].
|
||||
func offsetComplex(a, b []complex128) 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()) / sizeOfComplex128
|
||||
}
|
||||
260
vendor/gonum.org/v1/gonum/mat/pool.go
generated
vendored
Normal file
260
vendor/gonum.org/v1/gonum/mat/pool.go
generated
vendored
Normal file
@@ -0,0 +1,260 @@
|
||||
// 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 (
|
||||
"math/bits"
|
||||
"sync"
|
||||
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
"gonum.org/v1/gonum/blas/cblas128"
|
||||
)
|
||||
|
||||
// poolFor returns the ceiling of base 2 log of size. It provides an index
|
||||
// into a pool array to a sync.Pool that will return values able to hold
|
||||
// size elements.
|
||||
func poolFor(size uint) int {
|
||||
if size == 0 {
|
||||
return 0
|
||||
}
|
||||
return bits.Len(size - 1)
|
||||
}
|
||||
|
||||
var (
|
||||
// poolDense contains size stratified workspace Dense pools.
|
||||
// Each poolDense element i returns sized matrices with a data
|
||||
// slice capped at 1<<i.
|
||||
poolDense [63]sync.Pool
|
||||
|
||||
// poolSymDense is the SymDense equivalent of poolDense.
|
||||
poolSymDense [63]sync.Pool
|
||||
|
||||
// poolTriDense is the TriDense equivalent of poolDense.
|
||||
poolTriDense [63]sync.Pool
|
||||
|
||||
// poolVecDense is the VecDense equivalent of poolDense.
|
||||
poolVecDense [63]sync.Pool
|
||||
|
||||
// poolCDense is the CDense equivalent of poolDense.
|
||||
poolCDense [63]sync.Pool
|
||||
|
||||
// poolFloat64s is the []float64 equivalent of poolDense.
|
||||
poolFloat64s [63]sync.Pool
|
||||
|
||||
// poolInts is the []int equivalent of poolDense.
|
||||
poolInts [63]sync.Pool
|
||||
)
|
||||
|
||||
func init() {
|
||||
for i := range poolDense {
|
||||
l := 1 << uint(i)
|
||||
// Real matrix pools.
|
||||
poolDense[i].New = func() interface{} {
|
||||
return &Dense{mat: blas64.General{
|
||||
Data: make([]float64, l),
|
||||
}}
|
||||
}
|
||||
poolSymDense[i].New = func() interface{} {
|
||||
return &SymDense{mat: blas64.Symmetric{
|
||||
Uplo: blas.Upper,
|
||||
Data: make([]float64, l),
|
||||
}}
|
||||
}
|
||||
poolTriDense[i].New = func() interface{} {
|
||||
return &TriDense{mat: blas64.Triangular{
|
||||
Data: make([]float64, l),
|
||||
}}
|
||||
}
|
||||
poolVecDense[i].New = func() interface{} {
|
||||
return &VecDense{mat: blas64.Vector{
|
||||
Inc: 1,
|
||||
Data: make([]float64, l),
|
||||
}}
|
||||
}
|
||||
|
||||
// Complex matrix pools.
|
||||
poolCDense[i].New = func() interface{} {
|
||||
return &CDense{mat: cblas128.General{
|
||||
Data: make([]complex128, l),
|
||||
}}
|
||||
}
|
||||
|
||||
// Helper pools.
|
||||
poolFloat64s[i].New = func() interface{} {
|
||||
s := make([]float64, l)
|
||||
return &s
|
||||
}
|
||||
poolInts[i].New = func() interface{} {
|
||||
s := make([]int, l)
|
||||
return &s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getDenseWorkspace 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 getDenseWorkspace(r, c int, clear bool) *Dense {
|
||||
l := uint(r * c)
|
||||
w := poolDense[poolFor(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
|
||||
}
|
||||
|
||||
// putDenseWorkspace replaces a used *Dense into the appropriate size
|
||||
// workspace pool. putDenseWorkspace must not be called with a matrix
|
||||
// where references to the underlying data slice have been kept.
|
||||
func putDenseWorkspace(w *Dense) {
|
||||
poolDense[poolFor(uint(cap(w.mat.Data)))].Put(w)
|
||||
}
|
||||
|
||||
// getSymDenseWorkspace 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 getSymDenseWorkspace(n int, clear bool) *SymDense {
|
||||
l := uint(n)
|
||||
l *= l
|
||||
s := poolSymDense[poolFor(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
|
||||
}
|
||||
|
||||
// putSymDenseWorkspace replaces a used *SymDense into the appropriate size
|
||||
// workspace pool. putSymDenseWorkspace must not be called with a matrix
|
||||
// where references to the underlying data slice have been kept.
|
||||
func putSymDenseWorkspace(s *SymDense) {
|
||||
poolSymDense[poolFor(uint(cap(s.mat.Data)))].Put(s)
|
||||
}
|
||||
|
||||
// getTriDenseWorkspace 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 getTriDenseWorkspace(n int, kind TriKind, clear bool) *TriDense {
|
||||
l := uint(n)
|
||||
l *= l
|
||||
t := poolTriDense[poolFor(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
|
||||
}
|
||||
|
||||
// putTriWorkspace replaces a used *TriDense into the appropriate size
|
||||
// workspace pool. putTriWorkspace must not be called with a matrix
|
||||
// where references to the underlying data slice have been kept.
|
||||
func putTriWorkspace(t *TriDense) {
|
||||
poolTriDense[poolFor(uint(cap(t.mat.Data)))].Put(t)
|
||||
}
|
||||
|
||||
// getVecDenseWorkspace 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 getVecDenseWorkspace(n int, clear bool) *VecDense {
|
||||
l := uint(n)
|
||||
v := poolVecDense[poolFor(l)].Get().(*VecDense)
|
||||
v.mat.Data = v.mat.Data[:l]
|
||||
if clear {
|
||||
zero(v.mat.Data)
|
||||
}
|
||||
v.mat.N = n
|
||||
return v
|
||||
}
|
||||
|
||||
// putVecDenseWorkspace replaces a used *VecDense into the appropriate size
|
||||
// workspace pool. putVecDenseWorkspace must not be called with a matrix
|
||||
// where references to the underlying data slice have been kept.
|
||||
func putVecDenseWorkspace(v *VecDense) {
|
||||
poolVecDense[poolFor(uint(cap(v.mat.Data)))].Put(v)
|
||||
}
|
||||
|
||||
// getCDenseWorkspace returns a *CDense 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 CMatrix interface is zeroed.
|
||||
func getCDenseWorkspace(r, c int, clear bool) *CDense {
|
||||
l := uint(r * c)
|
||||
w := poolCDense[poolFor(l)].Get().(*CDense)
|
||||
w.mat.Data = w.mat.Data[:l]
|
||||
if clear {
|
||||
zeroC(w.mat.Data)
|
||||
}
|
||||
w.mat.Rows = r
|
||||
w.mat.Cols = c
|
||||
w.mat.Stride = c
|
||||
w.capRows = r
|
||||
w.capCols = c
|
||||
return w
|
||||
}
|
||||
|
||||
// putCDenseWorkspace replaces a used *CDense 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 putCDenseWorkspace(w *CDense) {
|
||||
poolCDense[poolFor(uint(cap(w.mat.Data)))].Put(w)
|
||||
}
|
||||
|
||||
// getFloat64s 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 getFloat64s(l int, clear bool) []float64 {
|
||||
w := *poolFloat64s[poolFor(uint(l))].Get().(*[]float64)
|
||||
w = w[:l]
|
||||
if clear {
|
||||
zero(w)
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// putFloat64s replaces a used []float64 into the appropriate size
|
||||
// workspace pool. putFloat64s must not be called with a slice
|
||||
// where references to the underlying data have been kept.
|
||||
func putFloat64s(w []float64) {
|
||||
poolFloat64s[poolFor(uint(cap(w)))].Put(&w)
|
||||
}
|
||||
|
||||
// getInts returns a []int 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[poolFor(uint(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[poolFor(uint(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.reuseAsNonZeroed(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.reuseAsNonZeroed(result.Dims())
|
||||
m.Copy(result)
|
||||
putDenseWorkspace(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.IsEmpty() {
|
||||
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 := getDenseWorkspace(ar, bc, false)
|
||||
r.Mul(a, b)
|
||||
if aTmp {
|
||||
putDenseWorkspace(a.(*Dense))
|
||||
}
|
||||
if bTmp {
|
||||
putDenseWorkspace(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 ""
|
||||
}
|
||||
305
vendor/gonum.org/v1/gonum/mat/qr.go
generated
vendored
Normal file
305
vendor/gonum.org/v1/gonum/mat/qr.go
generated
vendored
Normal file
@@ -0,0 +1,305 @@
|
||||
// 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
|
||||
q *Dense
|
||||
tau []float64
|
||||
cond float64
|
||||
}
|
||||
|
||||
// Dims returns the dimensions of the matrix.
|
||||
func (qr *QR) Dims() (r, c int) {
|
||||
if qr.qr == nil {
|
||||
return 0, 0
|
||||
}
|
||||
return qr.qr.Dims()
|
||||
}
|
||||
|
||||
// At returns the element at row i, column j.
|
||||
func (qr *QR) At(i, j int) float64 {
|
||||
m, n := qr.Dims()
|
||||
if uint(i) >= uint(m) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if uint(j) >= uint(n) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
|
||||
var val float64
|
||||
for k := 0; k <= j; k++ {
|
||||
val += qr.q.at(i, k) * qr.qr.at(k, j)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// T performs an implicit transpose by returning the receiver inside a
|
||||
// Transpose.
|
||||
func (qr *QR) T() Matrix {
|
||||
return Transpose{qr}
|
||||
}
|
||||
|
||||
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ᵀ|
|
||||
// = |R| |R^-1| = κ(R),
|
||||
// where we used that fact that Q^-1 = Qᵀ. 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 := getFloat64s(3*n, false)
|
||||
iwork := getInts(n, false)
|
||||
r := qr.qr.asTriDense(n, blas.NonUnit, blas.Upper)
|
||||
v := lapack64.Trcon(norm, r.mat, work, iwork)
|
||||
putFloat64s(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)
|
||||
}
|
||||
if qr.qr == nil {
|
||||
qr.qr = &Dense{}
|
||||
}
|
||||
qr.qr.CloneFrom(a)
|
||||
work := []float64{0}
|
||||
qr.tau = make([]float64, n)
|
||||
lapack64.Geqrf(qr.qr.mat, qr.tau, work, -1)
|
||||
work = getFloat64s(int(work[0]), false)
|
||||
lapack64.Geqrf(qr.qr.mat, qr.tau, work, len(work))
|
||||
putFloat64s(work)
|
||||
qr.updateCond(norm)
|
||||
qr.updateQ()
|
||||
}
|
||||
|
||||
func (qr *QR) updateQ() {
|
||||
m, _ := qr.Dims()
|
||||
if qr.q == nil {
|
||||
qr.q = NewDense(m, m, nil)
|
||||
} else {
|
||||
qr.q.reuseAsNonZeroed(m, m)
|
||||
}
|
||||
// Construct Q from the elementary reflectors.
|
||||
qr.q.Copy(qr.qr)
|
||||
work := []float64{0}
|
||||
lapack64.Orgqr(qr.q.mat, qr.tau, work, -1)
|
||||
work = getFloat64s(int(work[0]), false)
|
||||
lapack64.Orgqr(qr.q.mat, qr.tau, work, len(work))
|
||||
putFloat64s(work)
|
||||
}
|
||||
|
||||
// isValid returns whether the receiver contains a factorization.
|
||||
func (qr *QR) isValid() bool {
|
||||
return qr.qr != nil && !qr.qr.IsEmpty()
|
||||
}
|
||||
|
||||
// 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 empty, RTo will resize dst to be r×c. When dst is non-empty,
|
||||
// RTo will panic if dst is not r×c. RTo will also panic if the receiver
|
||||
// does not contain a successful factorization.
|
||||
func (qr *QR) RTo(dst *Dense) {
|
||||
if !qr.isValid() {
|
||||
panic(badQR)
|
||||
}
|
||||
|
||||
r, c := qr.qr.Dims()
|
||||
if dst.IsEmpty() {
|
||||
dst.ReuseAs(r, c)
|
||||
} else {
|
||||
r2, c2 := dst.Dims()
|
||||
if c != r2 || c != c2 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
|
||||
// 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])
|
||||
}
|
||||
}
|
||||
|
||||
// QTo extracts the r×r orthonormal matrix Q from a QR decomposition.
|
||||
//
|
||||
// If dst is empty, QTo will resize dst to be r×r. When dst is non-empty,
|
||||
// QTo will panic if dst is not r×r. QTo will also panic if the receiver
|
||||
// does not contain a successful factorization.
|
||||
func (qr *QR) QTo(dst *Dense) {
|
||||
if !qr.isValid() {
|
||||
panic(badQR)
|
||||
}
|
||||
|
||||
r, _ := qr.qr.Dims()
|
||||
if dst.IsEmpty() {
|
||||
dst.ReuseAs(r, r)
|
||||
} else {
|
||||
r2, c2 := dst.Dims()
|
||||
if r != r2 || r != c2 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
dst.Copy(qr.q)
|
||||
}
|
||||
|
||||
// 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ᵀ * 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.reuseAsNonZeroed(r, bc)
|
||||
} else {
|
||||
if r != br {
|
||||
panic(ErrShape)
|
||||
}
|
||||
dst.reuseAsNonZeroed(c, bc)
|
||||
}
|
||||
// Do not need to worry about overlap between m and b because x has its own
|
||||
// independent storage.
|
||||
w := getDenseWorkspace(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 = getFloat64s(int(work[0]), false)
|
||||
lapack64.Ormqr(blas.Left, blas.NoTrans, qr.qr.mat, qr.tau, w.mat, work, len(work))
|
||||
putFloat64s(work)
|
||||
} else {
|
||||
work := []float64{0}
|
||||
lapack64.Ormqr(blas.Left, blas.Trans, qr.qr.mat, qr.tau, w.mat, work, -1)
|
||||
work = getFloat64s(int(work[0]), false)
|
||||
lapack64.Ormqr(blas.Left, blas.Trans, qr.qr.mat, qr.tau, w.mat, work, len(work))
|
||||
putFloat64s(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)
|
||||
putDenseWorkspace(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.reuseAsNonZeroed(r)
|
||||
} else {
|
||||
dst.reuseAsNonZeroed(c)
|
||||
}
|
||||
return qr.SolveTo(dst.asDense(), trans, bm)
|
||||
}
|
||||
243
vendor/gonum.org/v1/gonum/mat/shadow.go
generated
vendored
Normal file
243
vendor/gonum.org/v1/gonum/mat/shadow.go
generated
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
// 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"
|
||||
|
||||
// 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 && a.Stride != 1 && b.Stride != 1 {
|
||||
// Too hard, so assume the worst; if either stride
|
||||
// is one it will be caught in rectanglesOverlap.
|
||||
panic(mismatchedStrides)
|
||||
}
|
||||
|
||||
if off < 0 {
|
||||
off = -off
|
||||
a.Cols, b.Cols = b.Cols, a.Cols
|
||||
}
|
||||
if rectanglesOverlap(off, a.Cols, b.Cols, min(a.Stride, b.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 ar := a.(type) {
|
||||
default:
|
||||
return false
|
||||
case RawMatrixer:
|
||||
amat = ar.RawMatrix()
|
||||
case RawSymmetricer:
|
||||
amat = generalFromSymmetric(ar.RawSymmetric())
|
||||
case RawSymBander:
|
||||
amat = generalFromSymmetricBand(ar.RawSymBand())
|
||||
case RawTriangular:
|
||||
amat = generalFromTriangular(ar.RawTriangular())
|
||||
case RawVectorer:
|
||||
r, c := a.Dims()
|
||||
amat = generalFromVector(ar.RawVector(), r, c)
|
||||
}
|
||||
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 ar := a.(type) {
|
||||
default:
|
||||
return false
|
||||
case RawMatrixer:
|
||||
amat = ar.RawMatrix()
|
||||
case RawSymmetricer:
|
||||
amat = generalFromSymmetric(ar.RawSymmetric())
|
||||
case RawSymBander:
|
||||
amat = generalFromSymmetricBand(ar.RawSymBand())
|
||||
case RawTriangular:
|
||||
amat = generalFromTriangular(ar.RawTriangular())
|
||||
case RawVectorer:
|
||||
r, c := a.Dims()
|
||||
amat = generalFromVector(ar.RawVector(), r, c)
|
||||
}
|
||||
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 ar := a.(type) {
|
||||
default:
|
||||
return false
|
||||
case RawMatrixer:
|
||||
amat = ar.RawMatrix()
|
||||
case RawSymmetricer:
|
||||
amat = generalFromSymmetric(ar.RawSymmetric())
|
||||
case RawSymBander:
|
||||
amat = generalFromSymmetricBand(ar.RawSymBand())
|
||||
case RawTriangular:
|
||||
amat = generalFromTriangular(ar.RawTriangular())
|
||||
case RawVectorer:
|
||||
r, c := a.Dims()
|
||||
amat = generalFromVector(ar.RawVector(), r, c)
|
||||
}
|
||||
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 && mat.Inc != 1 && a.Inc != 1 {
|
||||
// Too hard, so assume the worst; if either
|
||||
// increment is one it will be caught below.
|
||||
panic(mismatchedStrides)
|
||||
}
|
||||
inc := min(mat.Inc, a.Inc)
|
||||
|
||||
if inc == 1 || off&inc == 0 {
|
||||
panic(regionOverlap)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// generalFromVector returns a blas64.General with the backing
|
||||
// data and dimensions of a.
|
||||
func generalFromVector(a blas64.Vector, r, c int) blas64.General {
|
||||
return blas64.General{
|
||||
Rows: r,
|
||||
Cols: c,
|
||||
Stride: a.Inc,
|
||||
Data: a.Data,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SymBandDense) checkOverlap(a blas64.General) bool {
|
||||
return checkOverlap(generalFromSymmetricBand(s.RawSymBand()), a)
|
||||
}
|
||||
|
||||
//lint:ignore U1000 This will be used when we do shadow checks for banded matrices.
|
||||
func (s *SymBandDense) checkOverlapMatrix(a Matrix) bool {
|
||||
if s == a {
|
||||
return false
|
||||
}
|
||||
var amat blas64.General
|
||||
switch ar := a.(type) {
|
||||
default:
|
||||
return false
|
||||
case RawMatrixer:
|
||||
amat = ar.RawMatrix()
|
||||
case RawSymmetricer:
|
||||
amat = generalFromSymmetric(ar.RawSymmetric())
|
||||
case RawSymBander:
|
||||
amat = generalFromSymmetricBand(ar.RawSymBand())
|
||||
case RawTriangular:
|
||||
amat = generalFromTriangular(ar.RawTriangular())
|
||||
case RawVectorer:
|
||||
r, c := a.Dims()
|
||||
amat = generalFromVector(ar.RawVector(), r, c)
|
||||
}
|
||||
return s.checkOverlap(amat)
|
||||
}
|
||||
|
||||
// generalFromSymmetricBand returns a blas64.General with the backing
|
||||
// data and dimensions of a.
|
||||
func generalFromSymmetricBand(a blas64.SymmetricBand) blas64.General {
|
||||
return blas64.General{
|
||||
Rows: a.N,
|
||||
Cols: a.K + 1,
|
||||
Data: a.Data,
|
||||
Stride: a.Stride,
|
||||
}
|
||||
}
|
||||
54
vendor/gonum.org/v1/gonum/mat/shadow_common.go
generated
vendored
Normal file
54
vendor/gonum.org/v1/gonum/mat/shadow_common.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// 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
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
72
vendor/gonum.org/v1/gonum/mat/shadow_complex.go
generated
vendored
Normal file
72
vendor/gonum.org/v1/gonum/mat/shadow_complex.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// 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.
|
||||
|
||||
// TODO(kortschak): Generate this file from shadow.go when all complex type are available.
|
||||
|
||||
package mat
|
||||
|
||||
import "gonum.org/v1/gonum/blas/cblas128"
|
||||
|
||||
// checkOverlapComplex returns false if the receiver does not overlap data elements
|
||||
// referenced by the parameter and panics otherwise.
|
||||
//
|
||||
// checkOverlapComplex methods return a boolean to allow the check call to be added to a
|
||||
// boolean expression, making use of short-circuit operators.
|
||||
func checkOverlapComplex(a, b cblas128.General) bool {
|
||||
if cap(a.Data) == 0 || cap(b.Data) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
off := offsetComplex(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 && a.Stride != 1 && b.Stride != 1 {
|
||||
// Too hard, so assume the worst; if either stride
|
||||
// is one it will be caught in rectanglesOverlap.
|
||||
panic(mismatchedStrides)
|
||||
}
|
||||
|
||||
if off < 0 {
|
||||
off = -off
|
||||
a.Cols, b.Cols = b.Cols, a.Cols
|
||||
}
|
||||
if rectanglesOverlap(off, a.Cols, b.Cols, min(a.Stride, b.Stride)) {
|
||||
panic(regionOverlap)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *CDense) checkOverlap(a cblas128.General) bool {
|
||||
return checkOverlapComplex(m.RawCMatrix(), a)
|
||||
}
|
||||
|
||||
func (m *CDense) checkOverlapMatrix(a CMatrix) bool {
|
||||
if m == a {
|
||||
return false
|
||||
}
|
||||
var amat cblas128.General
|
||||
switch ar := a.(type) {
|
||||
default:
|
||||
return false
|
||||
case RawCMatrixer:
|
||||
amat = ar.RawCMatrix()
|
||||
}
|
||||
return m.checkOverlap(amat)
|
||||
}
|
||||
124
vendor/gonum.org/v1/gonum/mat/solve.go
generated
vendored
Normal file
124
vendor/gonum.org/v1/gonum/mat/solve.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
// 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
|
||||
|
||||
// Solve solves the linear least squares problem
|
||||
//
|
||||
// minimize over x |b - A*x|_2
|
||||
//
|
||||
// where A is an m×n matrix, b is a given m element vector and x is n element
|
||||
// solution vector. Solve assumes that A has full rank, that is
|
||||
//
|
||||
// rank(A) = min(m,n)
|
||||
//
|
||||
// If m >= n, Solve finds the unique least squares solution of an overdetermined
|
||||
// system.
|
||||
//
|
||||
// If m < n, there is an infinite number of solutions that satisfy b-A*x=0. In
|
||||
// this case Solve finds the unique solution of an underdetermined system that
|
||||
// minimizes |x|_2.
|
||||
//
|
||||
// Several right-hand side vectors b and solution vectors x can be handled in a
|
||||
// single call. Vectors b are stored in the columns of the m×k matrix B. Vectors
|
||||
// x will be stored in-place into the n×k receiver.
|
||||
//
|
||||
// If the underlying matrix of a is a SolveToer, its SolveTo method is used,
|
||||
// otherwise a Dense copy of a will be used for the solution.
|
||||
//
|
||||
// If A does not have full rank, a Condition error is returned. See the
|
||||
// documentation for Condition for more information.
|
||||
func (m *Dense) Solve(a, b Matrix) error {
|
||||
aU, aTrans := untransposeExtract(a)
|
||||
if a, ok := aU.(SolveToer); ok {
|
||||
return a.SolveTo(m, aTrans, b)
|
||||
}
|
||||
|
||||
ar, ac := a.Dims()
|
||||
br, bc := b.Dims()
|
||||
if ar != br {
|
||||
panic(ErrShape)
|
||||
}
|
||||
m.reuseAsNonZeroed(ac, bc)
|
||||
|
||||
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 solves the linear least squares problem
|
||||
//
|
||||
// minimize over x |b - A*x|_2
|
||||
//
|
||||
// where A is an m×n matrix, b is a given m element vector and x is n element
|
||||
// solution vector. Solve assumes that A has full rank, that is
|
||||
//
|
||||
// rank(A) = min(m,n)
|
||||
//
|
||||
// If m >= n, Solve finds the unique least squares solution of an overdetermined
|
||||
// system.
|
||||
//
|
||||
// If m < n, there is an infinite number of solutions that satisfy b-A*x=0. In
|
||||
// this case Solve finds the unique solution of an underdetermined system that
|
||||
// minimizes |x|_2.
|
||||
//
|
||||
// The solution vector x will be stored in-place into the receiver.
|
||||
//
|
||||
// If A does not have full rank, a Condition error is returned. See the
|
||||
// documentation for Condition 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.reuseAsNonZeroed(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.reuseAsNonZeroed(c)
|
||||
m := v.asDense()
|
||||
return m.Solve(a, b)
|
||||
}
|
||||
425
vendor/gonum.org/v1/gonum/mat/svd.go
generated
vendored
Normal file
425
vendor/gonum.org/v1/gonum/mat/svd.go
generated
vendored
Normal file
@@ -0,0 +1,425 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
const badRcond = "mat: invalid rcond value"
|
||||
|
||||
// SVD is a type for creating and using the Singular Value Decomposition
|
||||
// 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
|
||||
// SVDFull 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ᵀ
|
||||
//
|
||||
// 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~ᵀ
|
||||
//
|
||||
// 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 = getFloat64s(int(work[0]), false)
|
||||
ok = lapack64.Gesvd(jobU, jobVT, aCopy.mat, svd.u, svd.vt, svd.s, work, len(work))
|
||||
putFloat64s(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
|
||||
}
|
||||
|
||||
// Rank returns the rank of A based on the count of singular values greater than
|
||||
// rcond scaled by the largest singular value.
|
||||
// Rank will panic if the receiver does not contain a successful factorization or
|
||||
// rcond is negative.
|
||||
func (svd *SVD) Rank(rcond float64) int {
|
||||
if rcond < 0 {
|
||||
panic(badRcond)
|
||||
}
|
||||
if !svd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
s0 := svd.s[0]
|
||||
for i, v := range svd.s {
|
||||
if v <= rcond*s0 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return len(svd.s)
|
||||
}
|
||||
|
||||
// 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 empty, UTo will resize dst to be m×m if the full U was computed
|
||||
// and size m×min(m,n) if the thin U was computed. When dst is non-empty, then
|
||||
// UTo will panic if dst is not the appropriate size. UTo will also panic if
|
||||
// the receiver does not contain a successful factorization, or if U was
|
||||
// not computed during factorization.
|
||||
func (svd *SVD) UTo(dst *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.IsEmpty() {
|
||||
dst.ReuseAs(r, c)
|
||||
} else {
|
||||
r2, c2 := dst.Dims()
|
||||
if r != r2 || c != c2 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
|
||||
tmp := &Dense{
|
||||
mat: svd.u,
|
||||
capRows: r,
|
||||
capCols: c,
|
||||
}
|
||||
dst.Copy(tmp)
|
||||
}
|
||||
|
||||
// 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 empty, VTo will resize dst to be n×n if the full V was computed
|
||||
// and size n×min(m,n) if the thin V was computed. When dst is non-empty, then
|
||||
// VTo will panic if dst is not the appropriate size. VTo will also panic if
|
||||
// the receiver does not contain a successful factorization, or if V was
|
||||
// not computed during factorization.
|
||||
func (svd *SVD) VTo(dst *Dense) {
|
||||
if !svd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
kind := svd.kind
|
||||
if kind&SVDThinV == 0 && kind&SVDFullV == 0 {
|
||||
panic("svd: v not computed during factorization")
|
||||
}
|
||||
r := svd.vt.Rows
|
||||
c := svd.vt.Cols
|
||||
if dst.IsEmpty() {
|
||||
dst.ReuseAs(c, r)
|
||||
} else {
|
||||
r2, c2 := dst.Dims()
|
||||
if c != r2 || r != c2 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
|
||||
tmp := &Dense{
|
||||
mat: svd.vt,
|
||||
capRows: r,
|
||||
capCols: c,
|
||||
}
|
||||
dst.Copy(tmp.T())
|
||||
}
|
||||
|
||||
// SolveTo calculates the minimum-norm solution to a linear least squares problem
|
||||
//
|
||||
// minimize over n-element vectors x: |b - A*x|_2 and |x|_2
|
||||
//
|
||||
// where b is a given m-element vector, using the SVD of m×n matrix A stored in
|
||||
// the receiver. A may be rank-deficient, that is, the given effective rank can be
|
||||
//
|
||||
// rank ≤ min(m,n)
|
||||
//
|
||||
// The rank can be computed using SVD.Rank.
|
||||
//
|
||||
// Several right-hand side vectors b and solution vectors x can be handled in a
|
||||
// single call. Vectors b are stored in the columns of the m×k matrix B and the
|
||||
// resulting vectors x will be stored in the columns of dst. dst must be either
|
||||
// empty or have the size equal to n×k.
|
||||
//
|
||||
// The decomposition must have been factorized computing both the U and V
|
||||
// singular vectors.
|
||||
//
|
||||
// SolveTo returns the residuals calculated from the complete SVD. For this
|
||||
// value to be valid the factorization must have been performed with at least
|
||||
// SVDFullU.
|
||||
func (svd *SVD) SolveTo(dst *Dense, b Matrix, rank int) []float64 {
|
||||
if !svd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if rank < 1 || len(svd.s) < rank {
|
||||
panic("svd: rank out of range")
|
||||
}
|
||||
kind := svd.kind
|
||||
if kind&SVDThinU == 0 && kind&SVDFullU == 0 {
|
||||
panic("svd: u not computed during factorization")
|
||||
}
|
||||
if kind&SVDThinV == 0 && kind&SVDFullV == 0 {
|
||||
panic("svd: v not computed during factorization")
|
||||
}
|
||||
|
||||
u := Dense{
|
||||
mat: svd.u,
|
||||
capRows: svd.u.Rows,
|
||||
capCols: svd.u.Cols,
|
||||
}
|
||||
vt := Dense{
|
||||
mat: svd.vt,
|
||||
capRows: svd.vt.Rows,
|
||||
capCols: svd.vt.Cols,
|
||||
}
|
||||
s := svd.s[:rank]
|
||||
|
||||
_, bc := b.Dims()
|
||||
c := getDenseWorkspace(svd.u.Cols, bc, false)
|
||||
defer putDenseWorkspace(c)
|
||||
c.Mul(u.T(), b)
|
||||
|
||||
y := getDenseWorkspace(rank, bc, false)
|
||||
defer putDenseWorkspace(y)
|
||||
y.DivElem(c.slice(0, rank, 0, bc), repVector{vec: s, cols: bc})
|
||||
dst.Mul(vt.slice(0, rank, 0, svd.vt.Cols).T(), y)
|
||||
|
||||
res := make([]float64, bc)
|
||||
if rank < svd.u.Cols {
|
||||
c = c.slice(len(s), svd.u.Cols, 0, bc)
|
||||
for j := range res {
|
||||
col := c.ColView(j)
|
||||
res[j] = Dot(col, col)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type repVector struct {
|
||||
vec []float64
|
||||
cols int
|
||||
}
|
||||
|
||||
func (m repVector) Dims() (r, c int) { return len(m.vec), m.cols }
|
||||
func (m repVector) At(i, j int) float64 {
|
||||
if i < 0 || len(m.vec) <= i || j < 0 || m.cols <= j {
|
||||
panic(ErrIndexOutOfRange.string) // Panic with string to prevent mat.Error recovery.
|
||||
}
|
||||
return m.vec[i]
|
||||
}
|
||||
func (m repVector) T() Matrix { return Transpose{m} }
|
||||
|
||||
// SolveVecTo calculates the minimum-norm solution to a linear least squares problem
|
||||
//
|
||||
// minimize over n-element vectors x: |b - A*x|_2 and |x|_2
|
||||
//
|
||||
// where b is a given m-element vector, using the SVD of m×n matrix A stored in
|
||||
// the receiver. A may be rank-deficient, that is, the given effective rank can be
|
||||
//
|
||||
// rank ≤ min(m,n)
|
||||
//
|
||||
// The rank can be computed using SVD.Rank.
|
||||
//
|
||||
// The resulting vector x will be stored in dst. dst must be either empty or
|
||||
// have length equal to n.
|
||||
//
|
||||
// The decomposition must have been factorized computing both the U and V
|
||||
// singular vectors.
|
||||
//
|
||||
// SolveVecTo returns the residuals calculated from the complete SVD. For this
|
||||
// value to be valid the factorization must have been performed with at least
|
||||
// SVDFullU.
|
||||
func (svd *SVD) SolveVecTo(dst *VecDense, b Vector, rank int) float64 {
|
||||
if !svd.succFact() {
|
||||
panic(badFact)
|
||||
}
|
||||
if rank < 1 || len(svd.s) < rank {
|
||||
panic("svd: rank out of range")
|
||||
}
|
||||
kind := svd.kind
|
||||
if kind&SVDThinU == 0 && kind&SVDFullU == 0 {
|
||||
panic("svd: u not computed during factorization")
|
||||
}
|
||||
if kind&SVDThinV == 0 && kind&SVDFullV == 0 {
|
||||
panic("svd: v not computed during factorization")
|
||||
}
|
||||
|
||||
u := Dense{
|
||||
mat: svd.u,
|
||||
capRows: svd.u.Rows,
|
||||
capCols: svd.u.Cols,
|
||||
}
|
||||
vt := Dense{
|
||||
mat: svd.vt,
|
||||
capRows: svd.vt.Rows,
|
||||
capCols: svd.vt.Cols,
|
||||
}
|
||||
s := svd.s[:rank]
|
||||
|
||||
c := getVecDenseWorkspace(svd.u.Cols, false)
|
||||
defer putVecDenseWorkspace(c)
|
||||
c.MulVec(u.T(), b)
|
||||
|
||||
y := getVecDenseWorkspace(rank, false)
|
||||
defer putVecDenseWorkspace(y)
|
||||
y.DivElemVec(c.sliceVec(0, rank), NewVecDense(rank, s))
|
||||
dst.MulVec(vt.slice(0, rank, 0, svd.vt.Cols).T(), y)
|
||||
|
||||
var res float64
|
||||
if rank < c.Len() {
|
||||
c = c.sliceVec(rank, c.Len())
|
||||
res = Dot(c, c)
|
||||
}
|
||||
return res
|
||||
}
|
||||
312
vendor/gonum.org/v1/gonum/mat/symband.go
generated
vendored
Normal file
312
vendor/gonum.org/v1/gonum/mat/symband.go
generated
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
// 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"
|
||||
"gonum.org/v1/gonum/lapack"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
var (
|
||||
symBandDense *SymBandDense
|
||||
_ Matrix = symBandDense
|
||||
_ allMatrix = symBandDense
|
||||
_ denseMatrix = 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
|
||||
|
||||
// SymmetricDim returns the number of rows/columns in the matrix.
|
||||
SymmetricDim() 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
|
||||
}
|
||||
|
||||
// SymmetricDim returns the size of the receiver.
|
||||
func (s *SymBandDense) SymmetricDim() 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
|
||||
}
|
||||
|
||||
// IsEmpty returns whether the receiver is empty. Empty matrices can be the
|
||||
// receiver for size-restricted operations. The receiver can be emptied using
|
||||
// Reset.
|
||||
func (s *SymBandDense) IsEmpty() bool {
|
||||
return s.mat.Stride == 0
|
||||
}
|
||||
|
||||
// Reset empties the matrix so that it can be reused as the
|
||||
// receiver of a dimensionally restricted operation.
|
||||
//
|
||||
// Reset should not be used when the matrix shares backing data.
|
||||
// See the Reseter interface for more information.
|
||||
func (s *SymBandDense) Reset() {
|
||||
s.mat.N = 0
|
||||
s.mat.K = 0
|
||||
s.mat.Stride = 0
|
||||
s.mat.Uplo = 0
|
||||
s.mat.Data = s.mat.Data[:0]
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Norm returns the specified norm of the receiver. Valid norms are:
|
||||
//
|
||||
// 1 - The maximum absolute column sum
|
||||
// 2 - The 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 is specified and with
|
||||
// ErrZeroLength if the matrix has zero size.
|
||||
func (s *SymBandDense) Norm(norm float64) float64 {
|
||||
if s.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
lnorm := normLapack(norm, false)
|
||||
if lnorm == lapack.MaxColumnSum || lnorm == lapack.MaxRowSum {
|
||||
work := getFloat64s(s.mat.N, false)
|
||||
defer putFloat64s(work)
|
||||
return lapack64.Lansb(lnorm, s.mat, work)
|
||||
}
|
||||
return lapack64.Lansb(lnorm, s.mat, nil)
|
||||
}
|
||||
|
||||
// Trace returns the trace of the matrix.
|
||||
//
|
||||
// Trace will panic with ErrZeroLength if the matrix has zero size.
|
||||
func (s *SymBandDense) Trace() float64 {
|
||||
if s.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
rb := s.RawSymBand()
|
||||
var tr float64
|
||||
for i := 0; i < rb.N; i++ {
|
||||
tr += rb.Data[i*rb.Stride]
|
||||
}
|
||||
return tr
|
||||
}
|
||||
|
||||
// MulVecTo computes S⋅x storing the result into dst.
|
||||
func (s *SymBandDense) MulVecTo(dst *VecDense, _ bool, x Vector) {
|
||||
n := s.mat.N
|
||||
if x.Len() != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
dst.reuseAsNonZeroed(n)
|
||||
|
||||
xMat, _ := untransposeExtract(x)
|
||||
if xVec, ok := xMat.(*VecDense); ok {
|
||||
if dst != xVec {
|
||||
dst.checkOverlap(xVec.mat)
|
||||
blas64.Sbmv(1, s.mat, xVec.mat, 0, dst.mat)
|
||||
} else {
|
||||
xCopy := getVecDenseWorkspace(n, false)
|
||||
xCopy.CloneFromVec(xVec)
|
||||
blas64.Sbmv(1, s.mat, xCopy.mat, 0, dst.mat)
|
||||
putVecDenseWorkspace(xCopy)
|
||||
}
|
||||
} else {
|
||||
xCopy := getVecDenseWorkspace(n, false)
|
||||
xCopy.CloneFromVec(x)
|
||||
blas64.Sbmv(1, s.mat, xCopy.mat, 0, dst.mat)
|
||||
putVecDenseWorkspace(xCopy)
|
||||
}
|
||||
}
|
||||
698
vendor/gonum.org/v1/gonum/mat/symmetric.go
generated
vendored
Normal file
698
vendor/gonum.org/v1/gonum/mat/symmetric.go
generated
vendored
Normal file
@@ -0,0 +1,698 @@
|
||||
// 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"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
var (
|
||||
symDense *SymDense
|
||||
|
||||
_ Matrix = symDense
|
||||
_ allMatrix = symDense
|
||||
_ denseMatrix = symDense
|
||||
_ Symmetric = symDense
|
||||
_ RawSymmetricer = symDense
|
||||
_ MutableSymmetric = symDense
|
||||
)
|
||||
|
||||
const badSymTriangle = "mat: blas64.Symmetric not upper"
|
||||
|
||||
// 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
|
||||
// SymmetricDim returns the number of rows/columns in the matrix.
|
||||
SymmetricDim() 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
|
||||
}
|
||||
|
||||
// SymmetricDim implements the Symmetric interface and returns the number of rows
|
||||
// and columns in the matrix.
|
||||
func (s *SymDense) SymmetricDim() 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.cap = mat.N
|
||||
s.mat = mat
|
||||
}
|
||||
|
||||
// Reset empties the matrix so that it can be reused as the
|
||||
// receiver of a dimensionally restricted operation.
|
||||
//
|
||||
// Reset should not be used when the matrix shares backing data.
|
||||
// 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]
|
||||
}
|
||||
|
||||
// ReuseAsSym changes the receiver if it IsEmpty() to be of size n×n.
|
||||
//
|
||||
// ReuseAsSym re-uses the backing data slice if it has sufficient capacity,
|
||||
// otherwise a new slice is allocated. The backing data is zero on return.
|
||||
//
|
||||
// ReuseAsSym panics if the receiver is not empty, and panics if
|
||||
// the input size is less than one. To empty the receiver for re-use,
|
||||
// Reset should be used.
|
||||
func (s *SymDense) ReuseAsSym(n int) {
|
||||
if n <= 0 {
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic(ErrNegativeDimension)
|
||||
}
|
||||
if !s.IsEmpty() {
|
||||
panic(ErrReuseNonEmpty)
|
||||
}
|
||||
s.reuseAsZeroed(n)
|
||||
}
|
||||
|
||||
// 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])
|
||||
}
|
||||
}
|
||||
|
||||
// IsEmpty returns whether the receiver is empty. Empty matrices can be the
|
||||
// receiver for size-restricted operations. The receiver can be emptied using
|
||||
// Reset.
|
||||
func (s *SymDense) IsEmpty() bool {
|
||||
// It must be the case that m.Dims() returns
|
||||
// zeros in this case. See comment in Reset().
|
||||
return s.mat.N == 0
|
||||
}
|
||||
|
||||
// reuseAsNonZeroed resizes an empty matrix to a n×n matrix,
|
||||
// or checks that a non-empty matrix is n×n.
|
||||
func (s *SymDense) reuseAsNonZeroed(n int) {
|
||||
// reuseAsNonZeroed must be kept in sync with reuseAsZeroed.
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
if s.mat.N > s.cap {
|
||||
// Panic as a string, not a mat.Error.
|
||||
panic(badCap)
|
||||
}
|
||||
if s.IsEmpty() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// reuseAsNonZeroed resizes an empty matrix to a n×n matrix,
|
||||
// or checks that a non-empty matrix is n×n. It then zeros the
|
||||
// elements of the matrix.
|
||||
func (s *SymDense) reuseAsZeroed(n int) {
|
||||
// reuseAsZeroed must be kept in sync with reuseAsNonZeroed.
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
if s.mat.N > s.cap {
|
||||
// Panic as a string, not a mat.Error.
|
||||
panic(badCap)
|
||||
}
|
||||
if s.IsEmpty() {
|
||||
s.mat = blas64.Symmetric{
|
||||
N: n,
|
||||
Stride: n,
|
||||
Data: useZeroed(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)
|
||||
}
|
||||
s.Zero()
|
||||
}
|
||||
|
||||
func (s *SymDense) isolatedWorkspace(a Symmetric) (w *SymDense, restore func()) {
|
||||
n := a.SymmetricDim()
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
w = getSymDenseWorkspace(n, false)
|
||||
return w, func() {
|
||||
s.CopySym(w)
|
||||
putSymDenseWorkspace(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.SymmetricDim()
|
||||
if n != b.SymmetricDim() {
|
||||
panic(ErrShape)
|
||||
}
|
||||
s.reuseAsNonZeroed(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.SymmetricDim()
|
||||
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 symmetric rank-one update to the matrix a with x,
|
||||
// which is treated as a column vector, and stores the result in the receiver
|
||||
//
|
||||
// s = a + alpha * x * xᵀ
|
||||
func (s *SymDense) SymRankOne(a Symmetric, alpha float64, x Vector) {
|
||||
n := x.Len()
|
||||
if a.SymmetricDim() != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
s.reuseAsNonZeroed(n)
|
||||
|
||||
if s != a {
|
||||
if rs, ok := a.(RawSymmetricer); ok {
|
||||
s.checkOverlap(generalFromSymmetric(rs.RawSymmetric()))
|
||||
}
|
||||
s.CopySym(a)
|
||||
}
|
||||
|
||||
xU, _ := untransposeExtract(x)
|
||||
if rv, ok := xU.(*VecDense); ok {
|
||||
r, c := xU.Dims()
|
||||
xmat := rv.mat
|
||||
s.checkOverlap(generalFromVector(xmat, r, c))
|
||||
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.SymmetricDim()
|
||||
r, _ := x.Dims()
|
||||
if r != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
xMat, aTrans := untransposeExtract(x)
|
||||
var g blas64.General
|
||||
if rm, ok := xMat.(*Dense); ok {
|
||||
g = rm.mat
|
||||
} else {
|
||||
g = DenseCopyOf(x).mat
|
||||
aTrans = false
|
||||
}
|
||||
if a != s {
|
||||
if rs, ok := a.(RawSymmetricer); ok {
|
||||
s.checkOverlap(generalFromSymmetric(rs.RawSymmetric()))
|
||||
}
|
||||
s.reuseAsNonZeroed(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.IsEmpty():
|
||||
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 := getSymDenseWorkspace(n, true)
|
||||
w.SymRankK(w, alpha, x)
|
||||
s.CopySym(w)
|
||||
putSymDenseWorkspace(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 symmetric rank-two update to the matrix a with the
|
||||
// vectors x and y, which are treated as column vectors, 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
|
||||
if x.Len() != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if y.Len() != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
if s != a {
|
||||
if rs, ok := a.(RawSymmetricer); ok {
|
||||
s.checkOverlap(generalFromSymmetric(rs.RawSymmetric()))
|
||||
}
|
||||
}
|
||||
|
||||
var xmat, ymat blas64.Vector
|
||||
fast := true
|
||||
xU, _ := untransposeExtract(x)
|
||||
if rv, ok := xU.(*VecDense); ok {
|
||||
r, c := xU.Dims()
|
||||
xmat = rv.mat
|
||||
s.checkOverlap(generalFromVector(xmat, r, c))
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
yU, _ := untransposeExtract(y)
|
||||
if rv, ok := yU.(*VecDense); ok {
|
||||
r, c := yU.Dims()
|
||||
ymat = rv.mat
|
||||
s.checkOverlap(generalFromVector(ymat, r, c))
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
|
||||
if s != a {
|
||||
if rs, ok := a.(RawSymmetricer); ok {
|
||||
s.checkOverlap(generalFromSymmetric(rs.RawSymmetric()))
|
||||
}
|
||||
s.reuseAsNonZeroed(n)
|
||||
s.CopySym(a)
|
||||
}
|
||||
|
||||
if fast {
|
||||
if s != a {
|
||||
s.reuseAsNonZeroed(n)
|
||||
s.CopySym(a)
|
||||
}
|
||||
blas64.Syr2(alpha, xmat, ymat, s.mat)
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
s.reuseAsNonZeroed(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.SymmetricDim()
|
||||
s.reuseAsNonZeroed(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.SymmetricDim()
|
||||
s.reuseAsNonZeroed(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 {
|
||||
return s.sliceSym(i, k)
|
||||
}
|
||||
|
||||
func (s *SymDense) sliceSym(i, k int) *SymDense {
|
||||
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
|
||||
}
|
||||
|
||||
// Norm returns the specified norm of the receiver. Valid norms are:
|
||||
//
|
||||
// 1 - The maximum absolute column sum
|
||||
// 2 - The 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 is specified and with
|
||||
// ErrZeroLength if the matrix has zero size.
|
||||
func (s *SymDense) Norm(norm float64) float64 {
|
||||
if s.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
lnorm := normLapack(norm, false)
|
||||
if lnorm == lapack.MaxColumnSum || lnorm == lapack.MaxRowSum {
|
||||
work := getFloat64s(s.mat.N, false)
|
||||
defer putFloat64s(work)
|
||||
return lapack64.Lansy(lnorm, s.mat, work)
|
||||
}
|
||||
return lapack64.Lansy(lnorm, s.mat, nil)
|
||||
}
|
||||
|
||||
// Trace returns the trace of the matrix.
|
||||
//
|
||||
// Trace will panic with ErrZeroLength if the matrix has zero size.
|
||||
func (s *SymDense) Trace() float64 {
|
||||
if s.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
// 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 s.IsEmpty() || 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 positive symmetric definite
|
||||
// or the Eigen decomposition is not successful.
|
||||
func (s *SymDense) PowPSD(a Symmetric, pow float64) error {
|
||||
dim := a.SymmetricDim()
|
||||
s.reuseAsNonZeroed(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)
|
||||
}
|
||||
var u Dense
|
||||
eigen.VectorsTo(&u)
|
||||
|
||||
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
|
||||
}
|
||||
832
vendor/gonum.org/v1/gonum/mat/triangular.go
generated
vendored
Normal file
832
vendor/gonum.org/v1/gonum/mat/triangular.go
generated
vendored
Normal file
@@ -0,0 +1,832 @@
|
||||
// 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"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
var (
|
||||
triDense *TriDense
|
||||
_ Matrix = triDense
|
||||
_ allMatrix = triDense
|
||||
_ denseMatrix = triDense
|
||||
_ Triangular = triDense
|
||||
_ RawTriangular = triDense
|
||||
_ MutableTriangular = triDense
|
||||
|
||||
_ NonZeroDoer = triDense
|
||||
_ RowNonZeroDoer = triDense
|
||||
_ ColNonZeroDoer = 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 empty.
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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.cap = mat.N
|
||||
t.mat = mat
|
||||
}
|
||||
|
||||
// Reset empties the matrix so that it can be reused as the
|
||||
// receiver of a dimensionally restricted operation.
|
||||
//
|
||||
// Reset should not be used when the matrix shares backing data.
|
||||
// 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])
|
||||
}
|
||||
}
|
||||
|
||||
// IsEmpty returns whether the receiver is empty. Empty matrices can be the
|
||||
// receiver for size-restricted operations. The receiver can be emptied using
|
||||
// Reset.
|
||||
func (t *TriDense) IsEmpty() bool {
|
||||
// It must be the case that t.Dims() returns
|
||||
// zeros in this case. See comment in Reset().
|
||||
return t.mat.Stride == 0
|
||||
}
|
||||
|
||||
// untransposeTri untransposes a matrix if applicable. If a is an UntransposeTrier, then
|
||||
// untransposeTri 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
|
||||
}
|
||||
|
||||
// ReuseAsTri changes the receiver if it IsEmpty() to be of size n×n.
|
||||
//
|
||||
// ReuseAsTri re-uses the backing data slice if it has sufficient capacity,
|
||||
// otherwise a new slice is allocated. The backing data is zero on return.
|
||||
//
|
||||
// ReuseAsTri panics if the receiver is not empty, and panics if
|
||||
// the input size is less than one. To empty the receiver for re-use,
|
||||
// Reset should be used.
|
||||
func (t *TriDense) ReuseAsTri(n int, kind TriKind) {
|
||||
if n <= 0 {
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic(ErrNegativeDimension)
|
||||
}
|
||||
if !t.IsEmpty() {
|
||||
panic(ErrReuseNonEmpty)
|
||||
}
|
||||
t.reuseAsZeroed(n, kind)
|
||||
}
|
||||
|
||||
// reuseAsNonZeroed resizes an empty receiver to an n×n triangular matrix with the given
|
||||
// orientation. If the receiver is not empty, reuseAsNonZeroed checks that the receiver
|
||||
// is the correct size and orientation.
|
||||
func (t *TriDense) reuseAsNonZeroed(n int, kind TriKind) {
|
||||
// reuseAsNonZeroed must be kept in sync with reuseAsZeroed.
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
ul := blas.Lower
|
||||
if kind == Upper {
|
||||
ul = blas.Upper
|
||||
}
|
||||
if t.mat.N > t.cap {
|
||||
// Panic as a string, not a mat.Error.
|
||||
panic(badCap)
|
||||
}
|
||||
if t.IsEmpty() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// reuseAsZeroed resizes an empty receiver to an n×n triangular matrix with the given
|
||||
// orientation. If the receiver is not empty, reuseAsZeroed checks that the receiver
|
||||
// is the correct size and orientation. It then zeros out the matrix data.
|
||||
func (t *TriDense) reuseAsZeroed(n int, kind TriKind) {
|
||||
// reuseAsZeroed must be kept in sync with reuseAsNonZeroed.
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
ul := blas.Lower
|
||||
if kind == Upper {
|
||||
ul = blas.Upper
|
||||
}
|
||||
if t.mat.N > t.cap {
|
||||
// Panic as a string, not a mat.Error.
|
||||
panic(badCap)
|
||||
}
|
||||
if t.IsEmpty() {
|
||||
t.mat = blas64.Triangular{
|
||||
N: n,
|
||||
Stride: n,
|
||||
Diag: blas.NonUnit,
|
||||
Data: useZeroed(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)
|
||||
}
|
||||
t.Zero()
|
||||
}
|
||||
|
||||
// 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 = getTriDenseWorkspace(n, kind, false)
|
||||
return w, func() {
|
||||
t.Copy(w)
|
||||
putTriWorkspace(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.reuseAsNonZeroed(a.Triangle())
|
||||
t.Copy(a)
|
||||
work := getFloat64s(3*n, false)
|
||||
iwork := getInts(n, false)
|
||||
cond := lapack64.Trcon(CondNorm, t.mat, work, iwork)
|
||||
putFloat64s(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.reuseAsNonZeroed(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()
|
||||
}
|
||||
|
||||
// Inspect types here, helps keep the loops later clean(er).
|
||||
_, aDiag := aU.(Diagonal)
|
||||
_, bDiag := bU.(Diagonal)
|
||||
// If they are both diagonal only need 1 loop.
|
||||
// All diagonal matrices are Upper.
|
||||
// TODO: Add fast paths for DiagDense.
|
||||
if aDiag && bDiag {
|
||||
t.Zero()
|
||||
for i := 0; i < n; i++ {
|
||||
t.SetTri(i, i, a.At(i, i)*b.At(i, i))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Now we know at least one matrix is non-diagonal.
|
||||
// And all diagonal matrices are all Upper.
|
||||
// The both-diagonal case is handled above.
|
||||
// TODO: Add fast paths for Dense variants.
|
||||
if kind == Upper {
|
||||
for i := 0; i < n; i++ {
|
||||
for j := i; j < n; j++ {
|
||||
switch {
|
||||
case aDiag:
|
||||
t.SetTri(i, j, a.At(i, i)*b.At(i, j))
|
||||
case bDiag:
|
||||
t.SetTri(i, j, a.At(i, j)*b.At(j, j))
|
||||
default:
|
||||
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.reuseAsNonZeroed(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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SliceTri returns a new Triangular 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.
|
||||
// SliceTri panics with ErrIndexOutOfRange if the slice is outside the capacity
|
||||
// of the receiver.
|
||||
func (t *TriDense) SliceTri(i, k int) Triangular {
|
||||
return t.sliceTri(i, k)
|
||||
}
|
||||
|
||||
func (t *TriDense) sliceTri(i, k int) *TriDense {
|
||||
if i < 0 || t.cap < i || k < i || t.cap < k {
|
||||
panic(ErrIndexOutOfRange)
|
||||
}
|
||||
v := *t
|
||||
v.mat.Data = t.mat.Data[i*t.mat.Stride+i : (k-1)*t.mat.Stride+k]
|
||||
v.mat.N = k - i
|
||||
v.cap = t.cap - i
|
||||
return &v
|
||||
}
|
||||
|
||||
// Norm returns the specified norm of the receiver. Valid norms are:
|
||||
//
|
||||
// 1 - The maximum absolute column sum
|
||||
// 2 - The 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 is specified and with
|
||||
// ErrZeroLength if the matrix has zero size.
|
||||
func (t *TriDense) Norm(norm float64) float64 {
|
||||
if t.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
lnorm := normLapack(norm, false)
|
||||
if lnorm == lapack.MaxColumnSum {
|
||||
work := getFloat64s(t.mat.N, false)
|
||||
defer putFloat64s(work)
|
||||
return lapack64.Lantr(lnorm, t.mat, work)
|
||||
}
|
||||
return lapack64.Lantr(lnorm, t.mat, nil)
|
||||
}
|
||||
|
||||
// Trace returns the trace of the matrix.
|
||||
//
|
||||
// Trace will panic with ErrZeroLength if the matrix has zero size.
|
||||
func (t *TriDense) Trace() float64 {
|
||||
if t.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
// 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.SymmetricDim()
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SolveTo solves a triangular system T * X = B or Tᵀ * X = B where T is an n×n
|
||||
// triangular matrix represented by the receiver and B is a given n×nrhs matrix.
|
||||
// If T is non-singular, the result will be stored into dst and nil will be
|
||||
// returned. If T is singular, the contents of dst will be undefined and a
|
||||
// Condition error will be returned.
|
||||
//
|
||||
// If dst is empty, SolveTo will resize it to n×nrhs. If dst is not empty,
|
||||
// SolveTo will panic if dst is not n×nrhs.
|
||||
func (t *TriDense) SolveTo(dst *Dense, trans bool, b Matrix) error {
|
||||
n, nrhs := b.Dims()
|
||||
if n != t.mat.N {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
dst.reuseAsNonZeroed(n, nrhs)
|
||||
bU, bTrans := untranspose(b)
|
||||
if dst == bU {
|
||||
if bTrans {
|
||||
work := getDenseWorkspace(n, nrhs, false)
|
||||
defer putDenseWorkspace(work)
|
||||
work.Copy(b)
|
||||
dst.Copy(work)
|
||||
}
|
||||
} else {
|
||||
if rm, ok := bU.(RawMatrixer); ok {
|
||||
dst.checkOverlap(rm.RawMatrix())
|
||||
}
|
||||
dst.Copy(b)
|
||||
}
|
||||
|
||||
transT := blas.NoTrans
|
||||
if trans {
|
||||
transT = blas.Trans
|
||||
}
|
||||
ok := lapack64.Trtrs(transT, t.mat, dst.mat)
|
||||
if !ok {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
|
||||
work := getFloat64s(3*n, false)
|
||||
iwork := getInts(n, false)
|
||||
cond := lapack64.Trcon(CondNorm, t.mat, work, iwork)
|
||||
putFloat64s(work)
|
||||
putInts(iwork)
|
||||
if cond > ConditionTolerance {
|
||||
return Condition(cond)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
694
vendor/gonum.org/v1/gonum/mat/triband.go
generated
vendored
Normal file
694
vendor/gonum.org/v1/gonum/mat/triband.go
generated
vendored
Normal file
@@ -0,0 +1,694 @@
|
||||
// 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 (
|
||||
"math"
|
||||
|
||||
"gonum.org/v1/gonum/blas"
|
||||
"gonum.org/v1/gonum/blas/blas64"
|
||||
"gonum.org/v1/gonum/lapack"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
var (
|
||||
triBand TriBanded
|
||||
_ Banded = triBand
|
||||
_ Triangular = triBand
|
||||
|
||||
triBandDense *TriBandDense
|
||||
_ Matrix = triBandDense
|
||||
_ allMatrix = triBandDense
|
||||
_ denseMatrix = 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(ErrNegativeDimension)
|
||||
}
|
||||
if k+1 > n {
|
||||
panic(ErrBandwidth)
|
||||
}
|
||||
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}
|
||||
}
|
||||
|
||||
// IsEmpty returns whether the receiver is empty. Empty matrices can be the
|
||||
// receiver for size-restricted operations. The receiver can be emptied using
|
||||
// Reset.
|
||||
func (t *TriBandDense) IsEmpty() bool {
|
||||
// It must be the case that t.Dims() returns
|
||||
// zeros in this case. See comment in Reset().
|
||||
return t.mat.Stride == 0
|
||||
}
|
||||
|
||||
// Reset empties the matrix so that it can be reused as the
|
||||
// receiver of a dimensionally restricted operation.
|
||||
//
|
||||
// Reset should not be used when the matrix shares backing data.
|
||||
// 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]
|
||||
}
|
||||
|
||||
// ReuseAsTriBand changes the receiver to be of size n×n, bandwidth k+1 and of
|
||||
// the given kind, re-using the backing data slice if it has sufficient capacity
|
||||
// and allocating a new slice otherwise. The backing data is zero on return.
|
||||
//
|
||||
// The receiver must be empty, n must be positive and k must be non-negative and
|
||||
// less than n, otherwise ReuseAsTriBand will panic. To empty the receiver for
|
||||
// re-use, Reset should be used.
|
||||
func (t *TriBandDense) ReuseAsTriBand(n, k int, kind TriKind) {
|
||||
if n <= 0 || k < 0 {
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic(ErrNegativeDimension)
|
||||
}
|
||||
if k+1 > n {
|
||||
panic(ErrBandwidth)
|
||||
}
|
||||
if !t.IsEmpty() {
|
||||
panic(ErrReuseNonEmpty)
|
||||
}
|
||||
t.reuseAsZeroed(n, k, kind)
|
||||
}
|
||||
|
||||
// reuseAsZeroed resizes an empty receiver to an n×n triangular band matrix with
|
||||
// the given bandwidth and orientation. If the receiver is not empty,
|
||||
// reuseAsZeroed checks that the receiver has the correct size, bandwidth and
|
||||
// orientation. It then zeros out the matrix data.
|
||||
func (t *TriBandDense) reuseAsZeroed(n, k int, kind TriKind) {
|
||||
// reuseAsZeroed must be kept in sync with reuseAsNonZeroed.
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
ul := blas.Lower
|
||||
if kind == Upper {
|
||||
ul = blas.Upper
|
||||
}
|
||||
if t.IsEmpty() {
|
||||
t.mat = blas64.TriangularBand{
|
||||
Uplo: ul,
|
||||
Diag: blas.NonUnit,
|
||||
N: n,
|
||||
K: k,
|
||||
Data: useZeroed(t.mat.Data, n*(k+1)),
|
||||
Stride: k + 1,
|
||||
}
|
||||
return
|
||||
}
|
||||
if t.mat.N != n || t.mat.K != k {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if t.mat.Uplo != ul {
|
||||
panic(ErrTriangle)
|
||||
}
|
||||
t.Zero()
|
||||
}
|
||||
|
||||
// reuseAsNonZeroed resizes an empty receiver to an n×n triangular band matrix
|
||||
// with the given bandwidth and orientation. If the receiver is not empty,
|
||||
// reuseAsZeroed checks that the receiver has the correct size, bandwidth and
|
||||
// orientation.
|
||||
//
|
||||
//lint:ignore U1000 This will be used later.
|
||||
func (t *TriBandDense) reuseAsNonZeroed(n, k int, kind TriKind) {
|
||||
// reuseAsNonZeroed must be kept in sync with reuseAsZeroed.
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
ul := blas.Lower
|
||||
if kind == Upper {
|
||||
ul = blas.Upper
|
||||
}
|
||||
if t.IsEmpty() {
|
||||
t.mat = blas64.TriangularBand{
|
||||
Uplo: ul,
|
||||
Diag: blas.NonUnit,
|
||||
N: n,
|
||||
K: k,
|
||||
Data: use(t.mat.Data, n*(k+1)),
|
||||
Stride: k + 1,
|
||||
}
|
||||
return
|
||||
}
|
||||
if t.mat.N != n || t.mat.K != k {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if t.mat.Uplo != ul {
|
||||
panic(ErrTriangle)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 *TriBandDense) DoNonZero(fn func(i, j int, v float64)) {
|
||||
if t.isUpper() {
|
||||
for i := 0; i < t.mat.N; i++ {
|
||||
for j := i; j < min(i+t.mat.K+1, t.mat.N); j++ {
|
||||
v := t.at(i, j)
|
||||
if v != 0 {
|
||||
fn(i, j, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < t.mat.N; i++ {
|
||||
for j := max(0, i-t.mat.K); 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 *TriBandDense) 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 < min(i+t.mat.K+1, t.mat.N); j++ {
|
||||
v := t.at(i, j)
|
||||
if v != 0 {
|
||||
fn(i, j, v)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for j := max(0, i-t.mat.K); 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 *TriBandDense) 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 < t.mat.N; i++ {
|
||||
v := t.at(i, j)
|
||||
if v != 0 {
|
||||
fn(i, j, v)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < t.mat.N; i++ {
|
||||
v := t.at(i, j)
|
||||
if v != 0 {
|
||||
fn(i, j, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.IsEmpty()) && 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],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Norm returns the specified norm of the receiver. Valid norms are:
|
||||
//
|
||||
// 1 - The maximum absolute column sum
|
||||
// 2 - The 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 is specified and with
|
||||
// ErrZeroLength if the matrix has zero size.
|
||||
func (t *TriBandDense) Norm(norm float64) float64 {
|
||||
if t.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
lnorm := normLapack(norm, false)
|
||||
if lnorm == lapack.MaxColumnSum {
|
||||
work := getFloat64s(t.mat.N, false)
|
||||
defer putFloat64s(work)
|
||||
return lapack64.Lantb(lnorm, t.mat, work)
|
||||
}
|
||||
return lapack64.Lantb(lnorm, t.mat, nil)
|
||||
}
|
||||
|
||||
// Trace returns the trace of the matrix.
|
||||
//
|
||||
// Trace will panic with ErrZeroLength if the matrix has zero size.
|
||||
func (t *TriBandDense) Trace() float64 {
|
||||
if t.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
rb := t.RawTriBand()
|
||||
var tr float64
|
||||
var offsetIndex int
|
||||
if rb.Uplo == blas.Lower {
|
||||
offsetIndex = rb.K
|
||||
}
|
||||
for i := 0; i < rb.N; i++ {
|
||||
tr += rb.Data[offsetIndex+i*rb.Stride]
|
||||
}
|
||||
return tr
|
||||
}
|
||||
|
||||
// SolveTo solves a triangular system T * X = B or Tᵀ * X = B where T is an
|
||||
// n×n triangular band matrix represented by the receiver and B is a given
|
||||
// n×nrhs matrix. If T is non-singular, the result will be stored into dst and
|
||||
// nil will be returned. If T is singular, the contents of dst will be undefined
|
||||
// and a Condition error will be returned.
|
||||
func (t *TriBandDense) SolveTo(dst *Dense, trans bool, b Matrix) error {
|
||||
n, nrhs := b.Dims()
|
||||
if n != t.mat.N {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
dst.reuseAsNonZeroed(n, nrhs)
|
||||
bU, bTrans := untranspose(b)
|
||||
if dst == bU {
|
||||
if bTrans {
|
||||
work := getDenseWorkspace(n, nrhs, false)
|
||||
defer putDenseWorkspace(work)
|
||||
work.Copy(b)
|
||||
dst.Copy(work)
|
||||
}
|
||||
} else {
|
||||
if rm, ok := bU.(RawMatrixer); ok {
|
||||
dst.checkOverlap(rm.RawMatrix())
|
||||
}
|
||||
dst.Copy(b)
|
||||
}
|
||||
|
||||
var ok bool
|
||||
if trans {
|
||||
ok = lapack64.Tbtrs(blas.Trans, t.mat, dst.mat)
|
||||
} else {
|
||||
ok = lapack64.Tbtrs(blas.NoTrans, t.mat, dst.mat)
|
||||
}
|
||||
if !ok {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SolveVecTo solves a triangular system T * x = b or Tᵀ * x = b where T is an
|
||||
// n×n triangular band matrix represented by the receiver and b is a given
|
||||
// n-vector. If T is non-singular, the result will be stored into dst and nil
|
||||
// will be returned. If T is singular, the contents of dst will be undefined and
|
||||
// a Condition error will be returned.
|
||||
func (t *TriBandDense) SolveVecTo(dst *VecDense, trans bool, b Vector) error {
|
||||
n, nrhs := b.Dims()
|
||||
if n != t.mat.N || nrhs != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if b, ok := b.(RawVectorer); ok && dst != b {
|
||||
dst.checkOverlap(b.RawVector())
|
||||
}
|
||||
dst.reuseAsNonZeroed(n)
|
||||
if dst != b {
|
||||
dst.CopyVec(b)
|
||||
}
|
||||
var ok bool
|
||||
if trans {
|
||||
ok = lapack64.Tbtrs(blas.Trans, t.mat, dst.asGeneral())
|
||||
} else {
|
||||
ok = lapack64.Tbtrs(blas.NoTrans, t.mat, dst.asGeneral())
|
||||
}
|
||||
if !ok {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func copySymBandIntoTriBand(dst *TriBandDense, s SymBanded) {
|
||||
n, k, upper := dst.TriBand()
|
||||
ns, ks := s.SymBand()
|
||||
if n != ns {
|
||||
panic("mat: triangle size mismatch")
|
||||
}
|
||||
if k != ks {
|
||||
panic("mat: triangle bandwidth mismatch")
|
||||
}
|
||||
|
||||
// TODO(vladimir-ch): implement the missing cases below as needed.
|
||||
t := dst.mat
|
||||
sU, _ := untransposeExtract(s)
|
||||
if sbd, ok := sU.(*SymBandDense); ok {
|
||||
s := sbd.RawSymBand()
|
||||
if upper {
|
||||
if s.Uplo == blas.Upper {
|
||||
// dst is upper triangular, s is stored in upper triangle.
|
||||
for i := 0; i < n; i++ {
|
||||
ilen := min(k+1, n-i)
|
||||
copy(t.Data[i*t.Stride:i*t.Stride+ilen], s.Data[i*s.Stride:i*s.Stride+ilen])
|
||||
}
|
||||
} else {
|
||||
// dst is upper triangular, s is stored in lower triangle.
|
||||
//
|
||||
// The following is a possible implementation for this case but
|
||||
// is commented out due to lack of test coverage.
|
||||
// for i := 0; i < n; i++ {
|
||||
// ilen := min(k+1, n-i)
|
||||
// for j := 0; j < ilen; j++ {
|
||||
// t.Data[i*t.Stride+j] = s.Data[(i+j)*s.Stride+k-j]
|
||||
// }
|
||||
// }
|
||||
panic("not implemented")
|
||||
}
|
||||
} else {
|
||||
if s.Uplo == blas.Upper {
|
||||
// dst is lower triangular, s is stored in upper triangle.
|
||||
panic("not implemented")
|
||||
} else {
|
||||
// dst is lower triangular, s is stored in lower triangle.
|
||||
panic("not implemented")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if upper {
|
||||
for i := 0; i < n; i++ {
|
||||
ilen := min(k+1, n-i)
|
||||
for j := 0; j < ilen; j++ {
|
||||
t.Data[i*t.Stride+j] = s.At(i, i+j)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic("not implemented")
|
||||
}
|
||||
}
|
||||
417
vendor/gonum.org/v1/gonum/mat/tridiag.go
generated
vendored
Normal file
417
vendor/gonum.org/v1/gonum/mat/tridiag.go
generated
vendored
Normal file
@@ -0,0 +1,417 @@
|
||||
// Copyright ©2020 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/internal/asm/f64"
|
||||
"gonum.org/v1/gonum/lapack/lapack64"
|
||||
)
|
||||
|
||||
var (
|
||||
tridiagDense *Tridiag
|
||||
_ Matrix = tridiagDense
|
||||
_ allMatrix = tridiagDense
|
||||
_ denseMatrix = tridiagDense
|
||||
_ Banded = tridiagDense
|
||||
_ MutableBanded = tridiagDense
|
||||
_ RawTridiagonaler = tridiagDense
|
||||
)
|
||||
|
||||
// A RawTridiagonaler can return a lapack64.Tridiagonal representation of the
|
||||
// receiver. Changes to the elements of DL, D, DU in lapack64.Tridiagonal will
|
||||
// be reflected in the original matrix, changes to the N field will not.
|
||||
type RawTridiagonaler interface {
|
||||
RawTridiagonal() lapack64.Tridiagonal
|
||||
}
|
||||
|
||||
// Tridiag represents a tridiagonal matrix by its three diagonals.
|
||||
type Tridiag struct {
|
||||
mat lapack64.Tridiagonal
|
||||
}
|
||||
|
||||
// NewTridiag creates a new n×n tridiagonal matrix with the first sub-diagonal
|
||||
// in dl, the main diagonal in d and the first super-diagonal in du. If all of
|
||||
// dl, d, and du are nil, new backing slices will be allocated for them. If dl
|
||||
// and du have length n-1 and d has length n, they will be used as backing
|
||||
// slices, and changes to the elements of the returned Tridiag will be reflected
|
||||
// in dl, d, du. If neither of these is true, NewTridiag will panic.
|
||||
func NewTridiag(n int, dl, d, du []float64) *Tridiag {
|
||||
if n <= 0 {
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic(ErrNegativeDimension)
|
||||
}
|
||||
if dl != nil || d != nil || du != nil {
|
||||
if len(dl) != n-1 || len(d) != n || len(du) != n-1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
} else {
|
||||
d = make([]float64, n)
|
||||
if n > 1 {
|
||||
dl = make([]float64, n-1)
|
||||
du = make([]float64, n-1)
|
||||
}
|
||||
}
|
||||
return &Tridiag{
|
||||
mat: lapack64.Tridiagonal{
|
||||
N: n,
|
||||
DL: dl,
|
||||
D: d,
|
||||
DU: du,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Dims returns the number of rows and columns in the matrix.
|
||||
func (a *Tridiag) Dims() (r, c int) {
|
||||
return a.mat.N, a.mat.N
|
||||
}
|
||||
|
||||
// Bandwidth returns 1, 1 - the upper and lower bandwidths of the matrix.
|
||||
func (a *Tridiag) Bandwidth() (kl, ku int) {
|
||||
return 1, 1
|
||||
}
|
||||
|
||||
// T performs an implicit transpose by returning the receiver inside a Transpose.
|
||||
func (a *Tridiag) T() Matrix {
|
||||
// An alternative would be to return the receiver with DL,DU swapped; the
|
||||
// untranspose function would then always return false. With Transpose the
|
||||
// diagonal swapping will be done in tridiagonal routines in lapack like
|
||||
// lapack64.Gtsv or gonum.Dlagtm based on the trans parameter.
|
||||
return Transpose{a}
|
||||
}
|
||||
|
||||
// TBand performs an implicit transpose by returning the receiver inside a
|
||||
// TransposeBand.
|
||||
func (a *Tridiag) TBand() Banded {
|
||||
// An alternative would be to return the receiver with DL,DU swapped; see
|
||||
// explanation in T above.
|
||||
return TransposeBand{a}
|
||||
}
|
||||
|
||||
// RawTridiagonal returns the underlying lapack64.Tridiagonal used by the
|
||||
// receiver. Changes to elements in the receiver following the call will be
|
||||
// reflected in the returned matrix.
|
||||
func (a *Tridiag) RawTridiagonal() lapack64.Tridiagonal {
|
||||
return a.mat
|
||||
}
|
||||
|
||||
// SetRawTridiagonal sets the underlying lapack64.Tridiagonal used by the
|
||||
// receiver. Changes to elements in the receiver following the call will be
|
||||
// reflected in the input.
|
||||
func (a *Tridiag) SetRawTridiagonal(mat lapack64.Tridiagonal) {
|
||||
a.mat = mat
|
||||
}
|
||||
|
||||
// IsEmpty returns whether the receiver is empty. Empty matrices can be the
|
||||
// receiver for size-restricted operations. The receiver can be zeroed using
|
||||
// Reset.
|
||||
func (a *Tridiag) IsEmpty() bool {
|
||||
return a.mat.N == 0
|
||||
}
|
||||
|
||||
// Reset empties the matrix so that it can be reused as the receiver of a
|
||||
// dimensionally restricted operation.
|
||||
//
|
||||
// Reset should not be used when the matrix shares backing data. See the Reseter
|
||||
// interface for more information.
|
||||
func (a *Tridiag) Reset() {
|
||||
a.mat.N = 0
|
||||
a.mat.DL = a.mat.DL[:0]
|
||||
a.mat.D = a.mat.D[:0]
|
||||
a.mat.DU = a.mat.DU[:0]
|
||||
}
|
||||
|
||||
// CloneFromTridiag makes a copy of the input Tridiag into the receiver,
|
||||
// overwriting the previous value of the receiver. CloneFromTridiag does not
|
||||
// place any restrictions on receiver shape.
|
||||
func (a *Tridiag) CloneFromTridiag(from *Tridiag) {
|
||||
n := from.mat.N
|
||||
switch n {
|
||||
case 0:
|
||||
panic(ErrZeroLength)
|
||||
case 1:
|
||||
a.mat = lapack64.Tridiagonal{
|
||||
N: 1,
|
||||
DL: use(a.mat.DL, 0),
|
||||
D: use(a.mat.D, 1),
|
||||
DU: use(a.mat.DU, 0),
|
||||
}
|
||||
a.mat.D[0] = from.mat.D[0]
|
||||
default:
|
||||
a.mat = lapack64.Tridiagonal{
|
||||
N: n,
|
||||
DL: use(a.mat.DL, n-1),
|
||||
D: use(a.mat.D, n),
|
||||
DU: use(a.mat.DU, n-1),
|
||||
}
|
||||
copy(a.mat.DL, from.mat.DL)
|
||||
copy(a.mat.D, from.mat.D)
|
||||
copy(a.mat.DU, from.mat.DU)
|
||||
}
|
||||
}
|
||||
|
||||
// DiagView returns the diagonal as a matrix backed by the original data.
|
||||
func (a *Tridiag) DiagView() Diagonal {
|
||||
return &DiagDense{
|
||||
mat: blas64.Vector{
|
||||
N: a.mat.N,
|
||||
Data: a.mat.D[:a.mat.N],
|
||||
Inc: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Zero sets all of the matrix elements to zero.
|
||||
func (a *Tridiag) Zero() {
|
||||
zero(a.mat.DL)
|
||||
zero(a.mat.D)
|
||||
zero(a.mat.DU)
|
||||
}
|
||||
|
||||
// Trace returns the trace of the matrix.
|
||||
//
|
||||
// Trace will panic with ErrZeroLength if the matrix has zero size.
|
||||
func (a *Tridiag) Trace() float64 {
|
||||
if a.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
return f64.Sum(a.mat.D)
|
||||
}
|
||||
|
||||
// Norm returns the specified norm of the receiver. Valid norms are:
|
||||
//
|
||||
// 1 - The maximum absolute column sum
|
||||
// 2 - The 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 is specified and with
|
||||
// ErrZeroLength if the matrix has zero size.
|
||||
func (a *Tridiag) Norm(norm float64) float64 {
|
||||
if a.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
return lapack64.Langt(normLapack(norm, false), a.mat)
|
||||
}
|
||||
|
||||
// MulVecTo computes A⋅x or Aᵀ⋅x storing the result into dst.
|
||||
func (a *Tridiag) MulVecTo(dst *VecDense, trans bool, x Vector) {
|
||||
n := a.mat.N
|
||||
if x.Len() != n {
|
||||
panic(ErrShape)
|
||||
}
|
||||
dst.reuseAsNonZeroed(n)
|
||||
t := blas.NoTrans
|
||||
if trans {
|
||||
t = blas.Trans
|
||||
}
|
||||
xMat, _ := untransposeExtract(x)
|
||||
if xVec, ok := xMat.(*VecDense); ok && dst != xVec {
|
||||
dst.checkOverlap(xVec.mat)
|
||||
lapack64.Lagtm(t, 1, a.mat, xVec.asGeneral(), 0, dst.asGeneral())
|
||||
} else {
|
||||
xCopy := getVecDenseWorkspace(n, false)
|
||||
xCopy.CloneFromVec(x)
|
||||
lapack64.Lagtm(t, 1, a.mat, xCopy.asGeneral(), 0, dst.asGeneral())
|
||||
putVecDenseWorkspace(xCopy)
|
||||
}
|
||||
}
|
||||
|
||||
// SolveTo solves a tridiagonal system A⋅X = B or Aᵀ⋅X = B where A is an
|
||||
// n×n tridiagonal matrix represented by the receiver and B is a given n×nrhs
|
||||
// matrix. If A is non-singular, the result will be stored into dst and nil will
|
||||
// be returned. If A is singular, the contents of dst will be undefined and a
|
||||
// Condition error will be returned.
|
||||
func (a *Tridiag) SolveTo(dst *Dense, trans bool, b Matrix) error {
|
||||
n, nrhs := b.Dims()
|
||||
if n != a.mat.N {
|
||||
panic(ErrShape)
|
||||
}
|
||||
|
||||
dst.reuseAsNonZeroed(n, nrhs)
|
||||
bU, bTrans := untranspose(b)
|
||||
if dst == bU {
|
||||
if bTrans {
|
||||
work := getDenseWorkspace(n, nrhs, false)
|
||||
defer putDenseWorkspace(work)
|
||||
work.Copy(b)
|
||||
dst.Copy(work)
|
||||
}
|
||||
} else {
|
||||
if rm, ok := bU.(RawMatrixer); ok {
|
||||
dst.checkOverlap(rm.RawMatrix())
|
||||
}
|
||||
dst.Copy(b)
|
||||
}
|
||||
|
||||
var aCopy Tridiag
|
||||
aCopy.CloneFromTridiag(a)
|
||||
var ok bool
|
||||
if trans {
|
||||
ok = lapack64.Gtsv(blas.Trans, aCopy.mat, dst.mat)
|
||||
} else {
|
||||
ok = lapack64.Gtsv(blas.NoTrans, aCopy.mat, dst.mat)
|
||||
}
|
||||
if !ok {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SolveVecTo solves a tridiagonal system A⋅X = B or Aᵀ⋅X = B where A is an
|
||||
// n×n tridiagonal matrix represented by the receiver and b is a given n-vector.
|
||||
// If A is non-singular, the result will be stored into dst and nil will be
|
||||
// returned. If A is singular, the contents of dst will be undefined and a
|
||||
// Condition error will be returned.
|
||||
func (a *Tridiag) SolveVecTo(dst *VecDense, trans bool, b Vector) error {
|
||||
n, nrhs := b.Dims()
|
||||
if n != a.mat.N || nrhs != 1 {
|
||||
panic(ErrShape)
|
||||
}
|
||||
if b, ok := b.(RawVectorer); ok && dst != b {
|
||||
dst.checkOverlap(b.RawVector())
|
||||
}
|
||||
dst.reuseAsNonZeroed(n)
|
||||
if dst != b {
|
||||
dst.CopyVec(b)
|
||||
}
|
||||
var aCopy Tridiag
|
||||
aCopy.CloneFromTridiag(a)
|
||||
var ok bool
|
||||
if trans {
|
||||
ok = lapack64.Gtsv(blas.Trans, aCopy.mat, dst.asGeneral())
|
||||
} else {
|
||||
ok = lapack64.Gtsv(blas.NoTrans, aCopy.mat, dst.asGeneral())
|
||||
}
|
||||
if !ok {
|
||||
return Condition(math.Inf(1))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DoNonZero calls the function fn for each of the non-zero elements of A. The
|
||||
// function fn takes a row/column index and the element value of A at (i,j).
|
||||
func (a *Tridiag) DoNonZero(fn func(i, j int, v float64)) {
|
||||
for i, aij := range a.mat.DU {
|
||||
if aij != 0 {
|
||||
fn(i, i+1, aij)
|
||||
}
|
||||
}
|
||||
for i, aii := range a.mat.D {
|
||||
if aii != 0 {
|
||||
fn(i, i, aii)
|
||||
}
|
||||
}
|
||||
for i, aij := range a.mat.DL {
|
||||
if aij != 0 {
|
||||
fn(i+1, i, aij)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DoRowNonZero calls the function fn for each of the non-zero elements of row i
|
||||
// of A. The function fn takes a row/column index and the element value of A at
|
||||
// (i,j).
|
||||
func (a *Tridiag) DoRowNonZero(i int, fn func(i, j int, v float64)) {
|
||||
n := a.mat.N
|
||||
if uint(i) >= uint(n) {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if n == 1 {
|
||||
v := a.mat.D[0]
|
||||
if v != 0 {
|
||||
fn(0, 0, v)
|
||||
}
|
||||
return
|
||||
}
|
||||
switch i {
|
||||
case 0:
|
||||
v := a.mat.D[0]
|
||||
if v != 0 {
|
||||
fn(i, 0, v)
|
||||
}
|
||||
v = a.mat.DU[0]
|
||||
if v != 0 {
|
||||
fn(i, 1, v)
|
||||
}
|
||||
case n - 1:
|
||||
v := a.mat.DL[n-2]
|
||||
if v != 0 {
|
||||
fn(n-1, n-2, v)
|
||||
}
|
||||
v = a.mat.D[n-1]
|
||||
if v != 0 {
|
||||
fn(n-1, n-1, v)
|
||||
}
|
||||
default:
|
||||
v := a.mat.DL[i-1]
|
||||
if v != 0 {
|
||||
fn(i, i-1, v)
|
||||
}
|
||||
v = a.mat.D[i]
|
||||
if v != 0 {
|
||||
fn(i, i, v)
|
||||
}
|
||||
v = a.mat.DU[i]
|
||||
if v != 0 {
|
||||
fn(i, i+1, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DoColNonZero calls the function fn for each of the non-zero elements of
|
||||
// column j of A. The function fn takes a row/column index and the element value
|
||||
// of A at (i, j).
|
||||
func (a *Tridiag) DoColNonZero(j int, fn func(i, j int, v float64)) {
|
||||
n := a.mat.N
|
||||
if uint(j) >= uint(n) {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
if n == 1 {
|
||||
v := a.mat.D[0]
|
||||
if v != 0 {
|
||||
fn(0, 0, v)
|
||||
}
|
||||
return
|
||||
}
|
||||
switch j {
|
||||
case 0:
|
||||
v := a.mat.D[0]
|
||||
if v != 0 {
|
||||
fn(0, 0, v)
|
||||
}
|
||||
v = a.mat.DL[0]
|
||||
if v != 0 {
|
||||
fn(1, 0, v)
|
||||
}
|
||||
case n - 1:
|
||||
v := a.mat.DU[n-2]
|
||||
if v != 0 {
|
||||
fn(n-2, n-1, v)
|
||||
}
|
||||
v = a.mat.D[n-1]
|
||||
if v != 0 {
|
||||
fn(n-1, n-1, v)
|
||||
}
|
||||
default:
|
||||
v := a.mat.DU[j-1]
|
||||
if v != 0 {
|
||||
fn(j-1, j, v)
|
||||
}
|
||||
v = a.mat.D[j]
|
||||
if v != 0 {
|
||||
fn(j, j, v)
|
||||
}
|
||||
v = a.mat.DL[j]
|
||||
if v != 0 {
|
||||
fn(j+1, j, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
855
vendor/gonum.org/v1/gonum/mat/vector.go
generated
vendored
Normal file
855
vendor/gonum.org/v1/gonum/mat/vector.go
generated
vendored
Normal file
@@ -0,0 +1,855 @@
|
||||
// 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/internal/asm/f64"
|
||||
)
|
||||
|
||||
var (
|
||||
vector *VecDense
|
||||
|
||||
_ Matrix = vector
|
||||
_ allMatrix = vector
|
||||
_ Vector = vector
|
||||
_ Reseter = vector
|
||||
_ MutableVector = vector
|
||||
)
|
||||
|
||||
// Vector is a vector.
|
||||
type Vector interface {
|
||||
Matrix
|
||||
AtVec(int) float64
|
||||
Len() int
|
||||
}
|
||||
|
||||
// A MutableVector can set elements of a vector.
|
||||
type MutableVector interface {
|
||||
Vector
|
||||
SetVec(i int, v float64)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return v.sliceVec(i, k)
|
||||
}
|
||||
|
||||
func (v *VecDense) sliceVec(i, k int) *VecDense {
|
||||
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.IsEmpty() {
|
||||
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.IsEmpty() {
|
||||
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.IsEmpty() {
|
||||
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 empties the matrix so that it can be reused as the
|
||||
// receiver of a dimensionally restricted operation.
|
||||
//
|
||||
// Reset should not be used when the matrix shares backing data.
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// CloneFromVec makes a copy of a into the receiver, overwriting the previous value
|
||||
// of the receiver.
|
||||
func (v *VecDense) CloneFromVec(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.CloneFromVec(a)
|
||||
return v
|
||||
}
|
||||
|
||||
// RawVector returns the underlying blas64.Vector used by the receiver.
|
||||
// Changes to elements in the receiver following the call will be reflected
|
||||
// in returned blas64.Vector.
|
||||
func (v *VecDense) RawVector() blas64.Vector {
|
||||
return v.mat
|
||||
}
|
||||
|
||||
// SetRawVector sets the underlying blas64.Vector used by the receiver.
|
||||
// Changes to elements in the receiver following the call will be reflected
|
||||
// in the input.
|
||||
func (v *VecDense) SetRawVector(a blas64.Vector) {
|
||||
v.mat = a
|
||||
}
|
||||
|
||||
// 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 {
|
||||
src := r.RawVector()
|
||||
src.N = n
|
||||
dst := v.mat
|
||||
dst.N = n
|
||||
blas64.Copy(src, dst)
|
||||
return n
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
v.setVec(i, a.AtVec(i))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Norm returns the specified norm of the receiver. Valid norms are:
|
||||
//
|
||||
// 1 - The sum of the element magnitudes
|
||||
// 2 - The Euclidean norm, the square root of the sum of the squares of the elements
|
||||
// Inf - The maximum element magnitude
|
||||
//
|
||||
// Norm will panic with ErrNormOrder if an illegal norm is specified and with
|
||||
// ErrZeroLength if the vector has zero size.
|
||||
func (v *VecDense) Norm(norm float64) float64 {
|
||||
if v.IsEmpty() {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
switch norm {
|
||||
default:
|
||||
panic(ErrNormOrder)
|
||||
case 1:
|
||||
return blas64.Asum(v.mat)
|
||||
case 2:
|
||||
return blas64.Nrm2(v.mat)
|
||||
case math.Inf(1):
|
||||
imax := blas64.Iamax(v.mat)
|
||||
return math.Abs(v.at(imax))
|
||||
}
|
||||
}
|
||||
|
||||
// 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.reuseAsNonZeroed(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, _ := untransposeExtract(a)
|
||||
if rv, ok := aU.(*VecDense); ok {
|
||||
amat = rv.mat
|
||||
if v != a {
|
||||
v.checkOverlap(amat)
|
||||
}
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
bU, _ := untransposeExtract(b)
|
||||
if rv, ok := bU.(*VecDense); ok {
|
||||
bmat = rv.mat
|
||||
if v != b {
|
||||
v.checkOverlap(bmat)
|
||||
}
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
|
||||
v.reuseAsNonZeroed(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.reuseAsNonZeroed(ar)
|
||||
|
||||
aU, _ := untransposeExtract(a)
|
||||
bU, _ := untransposeExtract(b)
|
||||
|
||||
if arv, ok := aU.(*VecDense); ok {
|
||||
if brv, ok := bU.(*VecDense); ok {
|
||||
amat := arv.mat
|
||||
bmat := brv.mat
|
||||
|
||||
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.reuseAsNonZeroed(ar)
|
||||
|
||||
aU, _ := untransposeExtract(a)
|
||||
bU, _ := untransposeExtract(b)
|
||||
|
||||
if arv, ok := aU.(*VecDense); ok {
|
||||
if brv, ok := bU.(*VecDense); ok {
|
||||
amat := arv.mat
|
||||
bmat := brv.mat
|
||||
|
||||
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.reuseAsNonZeroed(ar)
|
||||
|
||||
aU, _ := untransposeExtract(a)
|
||||
bU, _ := untransposeExtract(b)
|
||||
|
||||
if arv, ok := aU.(*VecDense); ok {
|
||||
if brv, ok := bU.(*VecDense); ok {
|
||||
amat := arv.mat
|
||||
bmat := brv.mat
|
||||
|
||||
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.reuseAsNonZeroed(ar)
|
||||
|
||||
aU, _ := untransposeExtract(a)
|
||||
bU, _ := untransposeExtract(b)
|
||||
|
||||
if arv, ok := aU.(*VecDense); ok {
|
||||
if brv, ok := bU.(*VecDense); ok {
|
||||
amat := arv.mat
|
||||
bmat := brv.mat
|
||||
|
||||
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 := untransposeExtract(a)
|
||||
var bmat blas64.Vector
|
||||
fast := true
|
||||
bU, _ := untransposeExtract(b)
|
||||
if rv, ok := bU.(*VecDense); ok {
|
||||
bmat = rv.mat
|
||||
if v != b {
|
||||
v.checkOverlap(bmat)
|
||||
}
|
||||
} else {
|
||||
fast = false
|
||||
}
|
||||
|
||||
v.reuseAsNonZeroed(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.(*VecDense); ok {
|
||||
amat := rv.mat
|
||||
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 *SymBandDense:
|
||||
if fast {
|
||||
aU.checkOverlap(v.asGeneral())
|
||||
blas64.Sbmv(1, aU.mat, bmat, 0, v.mat)
|
||||
return
|
||||
}
|
||||
case *SymDense:
|
||||
if fast {
|
||||
aU.checkOverlap(v.asGeneral())
|
||||
blas64.Symv(1, aU.mat, bmat, 0, v.mat)
|
||||
return
|
||||
}
|
||||
case *TriDense:
|
||||
if fast {
|
||||
v.CopyVec(b)
|
||||
aU.checkOverlap(v.asGeneral())
|
||||
ta := blas.NoTrans
|
||||
if trans {
|
||||
ta = blas.Trans
|
||||
}
|
||||
blas64.Trmv(ta, aU.mat, v.mat)
|
||||
return
|
||||
}
|
||||
case *Dense:
|
||||
if fast {
|
||||
aU.checkOverlap(v.asGeneral())
|
||||
t := blas.NoTrans
|
||||
if trans {
|
||||
t = blas.Trans
|
||||
}
|
||||
blas64.Gemv(t, 1, aU.mat, 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)
|
||||
}
|
||||
}
|
||||
|
||||
// ReuseAsVec changes the receiver if it IsEmpty() to be of size n×1.
|
||||
//
|
||||
// ReuseAsVec re-uses the backing data slice if it has sufficient capacity,
|
||||
// otherwise a new slice is allocated. The backing data is zero on return.
|
||||
//
|
||||
// ReuseAsVec panics if the receiver is not empty, and panics if
|
||||
// the input size is less than one. To empty the receiver for re-use,
|
||||
// Reset should be used.
|
||||
func (v *VecDense) ReuseAsVec(n int) {
|
||||
if n <= 0 {
|
||||
if n == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
panic(ErrNegativeDimension)
|
||||
}
|
||||
if !v.IsEmpty() {
|
||||
panic(ErrReuseNonEmpty)
|
||||
}
|
||||
v.reuseAsZeroed(n)
|
||||
}
|
||||
|
||||
// reuseAsNonZeroed resizes an empty vector to a r×1 vector,
|
||||
// or checks that a non-empty matrix is r×1.
|
||||
func (v *VecDense) reuseAsNonZeroed(r int) {
|
||||
// reuseAsNonZeroed must be kept in sync with reuseAsZeroed.
|
||||
if r == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
if v.IsEmpty() {
|
||||
v.mat = blas64.Vector{
|
||||
N: r,
|
||||
Inc: 1,
|
||||
Data: use(v.mat.Data, r),
|
||||
}
|
||||
return
|
||||
}
|
||||
if r != v.mat.N {
|
||||
panic(ErrShape)
|
||||
}
|
||||
}
|
||||
|
||||
// reuseAsZeroed resizes an empty vector to a r×1 vector,
|
||||
// or checks that a non-empty matrix is r×1.
|
||||
func (v *VecDense) reuseAsZeroed(r int) {
|
||||
// reuseAsZeroed must be kept in sync with reuseAsNonZeroed.
|
||||
if r == 0 {
|
||||
panic(ErrZeroLength)
|
||||
}
|
||||
if v.IsEmpty() {
|
||||
v.mat = blas64.Vector{
|
||||
N: r,
|
||||
Inc: 1,
|
||||
Data: useZeroed(v.mat.Data, r),
|
||||
}
|
||||
return
|
||||
}
|
||||
if r != v.mat.N {
|
||||
panic(ErrShape)
|
||||
}
|
||||
v.Zero()
|
||||
}
|
||||
|
||||
// IsEmpty returns whether the receiver is empty. Empty matrices can be the
|
||||
// receiver for size-restricted operations. The receiver can be emptied using
|
||||
// Reset.
|
||||
func (v *VecDense) IsEmpty() 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 = getVecDenseWorkspace(l, false)
|
||||
return n, func() {
|
||||
v.CopyVec(n)
|
||||
putVecDenseWorkspace(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 receiver must either be empty
|
||||
// have length equal to the number of rows of m.
|
||||
func (v *VecDense) ColViewOf(m RawMatrixer, j int) {
|
||||
rm := m.RawMatrix()
|
||||
|
||||
if j >= rm.Cols || j < 0 {
|
||||
panic(ErrColAccess)
|
||||
}
|
||||
if !v.IsEmpty() && 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 receiver must either be
|
||||
// empty or have length equal to the number of columns of m.
|
||||
func (v *VecDense) RowViewOf(m RawMatrixer, i int) {
|
||||
rm := m.RawMatrix()
|
||||
|
||||
if i >= rm.Rows || i < 0 {
|
||||
panic(ErrRowAccess)
|
||||
}
|
||||
if !v.IsEmpty() && 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
|
||||
}
|
||||
|
||||
// Permute rearranges the elements of the n-vector v in the receiver as
|
||||
// specified by the permutation p[0],p[1],...,p[n-1] of the integers 0,...,n-1.
|
||||
//
|
||||
// If inverse is false, the given permutation is applied:
|
||||
//
|
||||
// v[p[i]] is moved to v[i] for i=0,1,...,n-1.
|
||||
//
|
||||
// If inverse is true, the inverse permutation is applied:
|
||||
//
|
||||
// v[i] is moved to v[p[i]] for i=0,1,...,n-1.
|
||||
//
|
||||
// p must have length n, otherwise Permute will panic.
|
||||
func (v *VecDense) Permute(p []int, inverse bool) {
|
||||
v.asDense().PermuteRows(p, inverse)
|
||||
}
|
||||
Reference in New Issue
Block a user