k3s/vendor/gonum.org/v1/gonum/mat/svd.go

191 lines
5.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// 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
}