mirror of https://github.com/k3s-io/k3s
191 lines
5.3 KiB
Go
191 lines
5.3 KiB
Go
// Copyright ©2013 The Gonum Authors. All rights reserved.
|
||
// Use of this source code is governed by a BSD-style
|
||
// license that can be found in the LICENSE file.
|
||
|
||
package mat
|
||
|
||
import (
|
||
"gonum.org/v1/gonum/blas/blas64"
|
||
"gonum.org/v1/gonum/lapack"
|
||
"gonum.org/v1/gonum/lapack/lapack64"
|
||
)
|
||
|
||
// SVD is a type for creating and using the Singular Value Decomposition (SVD)
|
||
// of a matrix.
|
||
type SVD struct {
|
||
kind SVDKind
|
||
|
||
s []float64
|
||
u blas64.General
|
||
vt blas64.General
|
||
}
|
||
|
||
// 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) deconstructs A as
|
||
// A = U * Σ * V^T
|
||
// where Σ is an m×n diagonal matrix of singular vectors, U is an m×m unitary
|
||
// matrix of left singular vectors, and V is an n×n matrix of right singular vectors.
|
||
//
|
||
// It is frequently not necessary to compute the full SVD. Computation time and
|
||
// storage costs can be reduced using the appropriate kind. Only the singular
|
||
// values can be computed (kind == SVDNone), or a "thin" representation of the
|
||
// singular vectors (kind = SVDThin). The thin representation can save a significant
|
||
// amount of memory if m >> n. See the documentation for the lapack.SVDKind values
|
||
// for more information.
|
||
//
|
||
// 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) {
|
||
m, n := a.Dims()
|
||
var jobU, jobVT lapack.SVDJob
|
||
switch kind {
|
||
default:
|
||
panic("svd: bad input kind")
|
||
case SVDNone:
|
||
jobU = lapack.SVDNone
|
||
jobVT = lapack.SVDNone
|
||
case SVDFull:
|
||
// 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.
|
||
svd.u = blas64.General{
|
||
Rows: m,
|
||
Cols: m,
|
||
Stride: m,
|
||
Data: use(svd.u.Data, m*m),
|
||
}
|
||
svd.vt = blas64.General{
|
||
Rows: n,
|
||
Cols: n,
|
||
Stride: n,
|
||
Data: use(svd.vt.Data, n*n),
|
||
}
|
||
jobU = lapack.SVDAll
|
||
jobVT = lapack.SVDAll
|
||
case SVDThin:
|
||
// TODO(btracey): This code should be modified to have the larger
|
||
// matrix written in-place into aCopy when the lapack/native/dgesvd
|
||
// implementation is complete.
|
||
svd.u = blas64.General{
|
||
Rows: m,
|
||
Cols: min(m, n),
|
||
Stride: min(m, n),
|
||
Data: use(svd.u.Data, m*min(m, n)),
|
||
}
|
||
svd.vt = blas64.General{
|
||
Rows: min(m, n),
|
||
Cols: n,
|
||
Stride: n,
|
||
Data: use(svd.vt.Data, min(m, n)*n),
|
||
}
|
||
jobU = lapack.SVDInPlace
|
||
jobVT = lapack.SVDInPlace
|
||
}
|
||
|
||
// A is destroyed on call, so copy the matrix.
|
||
aCopy := DenseCopyOf(a)
|
||
svd.kind = kind
|
||
svd.s = use(svd.s, min(m, n))
|
||
|
||
work := []float64{0}
|
||
lapack64.Gesvd(jobU, jobVT, aCopy.mat, svd.u, svd.vt, svd.s, work, -1)
|
||
work = getFloats(int(work[0]), false)
|
||
ok = lapack64.Gesvd(jobU, jobVT, aCopy.mat, svd.u, svd.vt, svd.s, work, len(work))
|
||
putFloats(work)
|
||
if !ok {
|
||
svd.kind = 0
|
||
}
|
||
return ok
|
||
}
|
||
|
||
// Kind returns the matrix.SVDKind of the decomposition. If no decomposition has been
|
||
// computed, Kind returns 0.
|
||
func (svd *SVD) Kind() SVDKind {
|
||
return svd.kind
|
||
}
|
||
|
||
// Cond returns the 2-norm condition number for the factorized matrix. Cond will
|
||
// panic if the receiver does not contain a successful factorization.
|
||
func (svd *SVD) Cond() float64 {
|
||
if svd.kind == 0 {
|
||
panic("svd: no decomposition computed")
|
||
}
|
||
return svd.s[0] / svd.s[len(svd.s)-1]
|
||
}
|
||
|
||
// Values returns the singular values of the factorized matrix in decreasing 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
|
||
// matrix.ErrSliceLengthMismatch otherwise. If the input slice is nil,
|
||
// a new slice of the appropriate length will be allocated and returned.
|
||
//
|
||
// Values will panic if the receiver does not contain a successful factorization.
|
||
func (svd *SVD) Values(s []float64) []float64 {
|
||
if svd.kind == 0 {
|
||
panic("svd: no decomposition computed")
|
||
}
|
||
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, storing
|
||
// the result in-place into dst. U is size m×m if svd.Kind() == SVDFull,
|
||
// of size m×min(m,n) if svd.Kind() == SVDThin, and UTo panics otherwise.
|
||
func (svd *SVD) UTo(dst *Dense) *Dense {
|
||
kind := svd.kind
|
||
if kind != SVDFull && kind != SVDThin {
|
||
panic("mat: improper SVD kind")
|
||
}
|
||
r := svd.u.Rows
|
||
c := svd.u.Cols
|
||
if dst == nil {
|
||
dst = NewDense(r, c, nil)
|
||
} else {
|
||
dst.reuseAs(r, c)
|
||
}
|
||
|
||
tmp := &Dense{
|
||
mat: svd.u,
|
||
capRows: r,
|
||
capCols: c,
|
||
}
|
||
dst.Copy(tmp)
|
||
|
||
return dst
|
||
}
|
||
|
||
// VTo extracts the matrix V from the singular value decomposition, storing
|
||
// the result in-place into dst. V is size n×n if svd.Kind() == SVDFull,
|
||
// of size n×min(m,n) if svd.Kind() == SVDThin, and VTo panics otherwise.
|
||
func (svd *SVD) VTo(dst *Dense) *Dense {
|
||
kind := svd.kind
|
||
if kind != SVDFull && kind != SVDThin {
|
||
panic("mat: improper SVD kind")
|
||
}
|
||
r := svd.vt.Rows
|
||
c := svd.vt.Cols
|
||
if dst == nil {
|
||
dst = NewDense(c, r, nil)
|
||
} else {
|
||
dst.reuseAs(c, r)
|
||
}
|
||
|
||
tmp := &Dense{
|
||
mat: svd.vt,
|
||
capRows: r,
|
||
capCols: c,
|
||
}
|
||
dst.Copy(tmp.T())
|
||
|
||
return dst
|
||
}
|