fixed dependencies

This commit is contained in:
nuknal
2024-10-24 15:46:01 +08:00
parent d16a5bd9c0
commit 1161e8d054
2005 changed files with 690883 additions and 0 deletions

6
vendor/gonum.org/v1/gonum/mat/README.md generated vendored Normal file
View File

@@ -0,0 +1,6 @@
# Gonum matrix
[![go.dev reference](https://pkg.go.dev/badge/gonum.org/v1/gonum/mat)](https://pkg.go.dev/gonum.org/v1/gonum/mat)
[![GoDoc](https://godocs.io/gonum.org/v1/gonum/mat?status.svg)](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
View 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
View 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

File diff suppressed because it is too large Load Diff

314
vendor/gonum.org/v1/gonum/mat/cmatrix.go generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

32
vendor/gonum.org/v1/gonum/mat/offset.go generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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)
}