| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437 | // 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 matimport (	"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 intconst (	// 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 matrix.GSVDU, matrix.GSVDV and matrix.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 matrix.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// matrix.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// matrix.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).(*Dense).		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).(*Dense).			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)}
 |