+++ /dev/null
-// 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
-}