github.com/gopherd/gonum@v0.0.4/mat/gsvd.go (about)

     1  // Copyright ©2017 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package mat
     6  
     7  import (
     8  	"github.com/gopherd/gonum/blas/blas64"
     9  	"github.com/gopherd/gonum/floats"
    10  	"github.com/gopherd/gonum/lapack"
    11  	"github.com/gopherd/gonum/lapack/lapack64"
    12  )
    13  
    14  // GSVDKind specifies the treatment of singular vectors during a GSVD
    15  // factorization.
    16  type GSVDKind int
    17  
    18  const (
    19  	// GSVDNone specifies that no singular vectors should be computed during
    20  	// the decomposition.
    21  	GSVDNone GSVDKind = 0
    22  
    23  	// GSVDU specifies that the U singular vectors should be computed during
    24  	// the decomposition.
    25  	GSVDU GSVDKind = 1 << iota
    26  	// GSVDV specifies that the V singular vectors should be computed during
    27  	// the decomposition.
    28  	GSVDV
    29  	// GSVDQ specifies that the Q singular vectors should be computed during
    30  	// the decomposition.
    31  	GSVDQ
    32  
    33  	// GSVDAll is a convenience value for computing all of the singular vectors.
    34  	GSVDAll = GSVDU | GSVDV | GSVDQ
    35  )
    36  
    37  // GSVD is a type for creating and using the Generalized Singular Value Decomposition
    38  // (GSVD) of a matrix.
    39  //
    40  // The factorization is a linear transformation of the data sets from the given
    41  // variable×sample spaces to reduced and diagonalized "eigenvariable"×"eigensample"
    42  // spaces.
    43  type GSVD struct {
    44  	kind GSVDKind
    45  
    46  	r, p, c, k, l int
    47  	s1, s2        []float64
    48  	a, b, u, v, q blas64.General
    49  
    50  	work  []float64
    51  	iwork []int
    52  }
    53  
    54  // succFact returns whether the receiver contains a successful factorization.
    55  func (gsvd *GSVD) succFact() bool {
    56  	return gsvd.r != 0
    57  }
    58  
    59  // Factorize computes the generalized singular value decomposition (GSVD) of the input
    60  // the r×c matrix A and the p×c matrix B. The singular values of A and B are computed
    61  // in all cases, while the singular vectors are optionally computed depending on the
    62  // input kind.
    63  //
    64  // The full singular value decomposition (kind == GSVDAll) deconstructs A and B as
    65  //  A = U * Σ₁ * [ 0 R ] * Qᵀ
    66  //
    67  //  B = V * Σ₂ * [ 0 R ] * Qᵀ
    68  // where Σ₁ and Σ₂ are r×(k+l) and p×(k+l) diagonal matrices of singular values, and
    69  // U, V and Q are r×r, p×p and c×c orthogonal matrices of singular vectors. k+l is the
    70  // effective numerical rank of the matrix [ Aᵀ Bᵀ ]ᵀ.
    71  //
    72  // It is frequently not necessary to compute the full GSVD. Computation time and
    73  // storage costs can be reduced using the appropriate kind. Either only the singular
    74  // values can be computed (kind == SVDNone), or in conjunction with specific singular
    75  // vectors (kind bit set according to GSVDU, GSVDV and GSVDQ).
    76  //
    77  // Factorize returns whether the decomposition succeeded. If the decomposition
    78  // failed, routines that require a successful factorization will panic.
    79  func (gsvd *GSVD) Factorize(a, b Matrix, kind GSVDKind) (ok bool) {
    80  	// kill the previous decomposition
    81  	gsvd.r = 0
    82  	gsvd.kind = 0
    83  
    84  	r, c := a.Dims()
    85  	gsvd.r, gsvd.c = r, c
    86  	p, c := b.Dims()
    87  	gsvd.p = p
    88  	if gsvd.c != c {
    89  		panic(ErrShape)
    90  	}
    91  	var jobU, jobV, jobQ lapack.GSVDJob
    92  	switch {
    93  	default:
    94  		panic("gsvd: bad input kind")
    95  	case kind == GSVDNone:
    96  		jobU = lapack.GSVDNone
    97  		jobV = lapack.GSVDNone
    98  		jobQ = lapack.GSVDNone
    99  	case GSVDAll&kind != 0:
   100  		if GSVDU&kind != 0 {
   101  			jobU = lapack.GSVDU
   102  			gsvd.u = blas64.General{
   103  				Rows:   r,
   104  				Cols:   r,
   105  				Stride: r,
   106  				Data:   use(gsvd.u.Data, r*r),
   107  			}
   108  		}
   109  		if GSVDV&kind != 0 {
   110  			jobV = lapack.GSVDV
   111  			gsvd.v = blas64.General{
   112  				Rows:   p,
   113  				Cols:   p,
   114  				Stride: p,
   115  				Data:   use(gsvd.v.Data, p*p),
   116  			}
   117  		}
   118  		if GSVDQ&kind != 0 {
   119  			jobQ = lapack.GSVDQ
   120  			gsvd.q = blas64.General{
   121  				Rows:   c,
   122  				Cols:   c,
   123  				Stride: c,
   124  				Data:   use(gsvd.q.Data, c*c),
   125  			}
   126  		}
   127  	}
   128  
   129  	// A and B are destroyed on call, so copy the matrices.
   130  	aCopy := DenseCopyOf(a)
   131  	bCopy := DenseCopyOf(b)
   132  
   133  	gsvd.s1 = use(gsvd.s1, c)
   134  	gsvd.s2 = use(gsvd.s2, c)
   135  
   136  	gsvd.iwork = useInt(gsvd.iwork, c)
   137  
   138  	gsvd.work = use(gsvd.work, 1)
   139  	lapack64.Ggsvd3(jobU, jobV, jobQ, aCopy.mat, bCopy.mat, gsvd.s1, gsvd.s2, gsvd.u, gsvd.v, gsvd.q, gsvd.work, -1, gsvd.iwork)
   140  	gsvd.work = use(gsvd.work, int(gsvd.work[0]))
   141  	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)
   142  	if ok {
   143  		gsvd.a = aCopy.mat
   144  		gsvd.b = bCopy.mat
   145  		gsvd.kind = kind
   146  	}
   147  	return ok
   148  }
   149  
   150  // Kind returns the GSVDKind of the decomposition. If no decomposition has been
   151  // computed, Kind returns -1.
   152  func (gsvd *GSVD) Kind() GSVDKind {
   153  	if !gsvd.succFact() {
   154  		return -1
   155  	}
   156  	return gsvd.kind
   157  }
   158  
   159  // Rank returns the k and l terms of the rank of [ Aᵀ Bᵀ ]ᵀ.
   160  func (gsvd *GSVD) Rank() (k, l int) {
   161  	return gsvd.k, gsvd.l
   162  }
   163  
   164  // GeneralizedValues returns the generalized singular values of the factorized matrices.
   165  // If the input slice is non-nil, the values will be stored in-place into the slice.
   166  // In this case, the slice must have length min(r,c)-k, and GeneralizedValues will
   167  // panic with ErrSliceLengthMismatch otherwise. If the input slice is nil,
   168  // a new slice of the appropriate length will be allocated and returned.
   169  //
   170  // GeneralizedValues will panic if the receiver does not contain a successful factorization.
   171  func (gsvd *GSVD) GeneralizedValues(v []float64) []float64 {
   172  	if !gsvd.succFact() {
   173  		panic(badFact)
   174  	}
   175  	r := gsvd.r
   176  	c := gsvd.c
   177  	k := gsvd.k
   178  	d := min(r, c)
   179  	if v == nil {
   180  		v = make([]float64, d-k)
   181  	}
   182  	if len(v) != d-k {
   183  		panic(ErrSliceLengthMismatch)
   184  	}
   185  	floats.DivTo(v, gsvd.s1[k:d], gsvd.s2[k:d])
   186  	return v
   187  }
   188  
   189  // ValuesA returns the singular values of the factorized A matrix.
   190  // If the input slice is non-nil, the values will be stored in-place into the slice.
   191  // In this case, the slice must have length min(r,c)-k, and ValuesA will panic with
   192  // ErrSliceLengthMismatch otherwise. If the input slice is nil,
   193  // a new slice of the appropriate length will be allocated and returned.
   194  //
   195  // ValuesA will panic if the receiver does not contain a successful factorization.
   196  func (gsvd *GSVD) ValuesA(s []float64) []float64 {
   197  	if !gsvd.succFact() {
   198  		panic(badFact)
   199  	}
   200  	r := gsvd.r
   201  	c := gsvd.c
   202  	k := gsvd.k
   203  	d := min(r, c)
   204  	if s == nil {
   205  		s = make([]float64, d-k)
   206  	}
   207  	if len(s) != d-k {
   208  		panic(ErrSliceLengthMismatch)
   209  	}
   210  	copy(s, gsvd.s1[k:min(r, c)])
   211  	return s
   212  }
   213  
   214  // ValuesB returns the singular values of the factorized B matrix.
   215  // If the input slice is non-nil, the values will be stored in-place into the slice.
   216  // In this case, the slice must have length min(r,c)-k, and ValuesB will panic with
   217  // ErrSliceLengthMismatch otherwise. If the input slice is nil,
   218  // a new slice of the appropriate length will be allocated and returned.
   219  //
   220  // ValuesB will panic if the receiver does not contain a successful factorization.
   221  func (gsvd *GSVD) ValuesB(s []float64) []float64 {
   222  	if !gsvd.succFact() {
   223  		panic(badFact)
   224  	}
   225  	r := gsvd.r
   226  	c := gsvd.c
   227  	k := gsvd.k
   228  	d := min(r, c)
   229  	if s == nil {
   230  		s = make([]float64, d-k)
   231  	}
   232  	if len(s) != d-k {
   233  		panic(ErrSliceLengthMismatch)
   234  	}
   235  	copy(s, gsvd.s2[k:d])
   236  	return s
   237  }
   238  
   239  // ZeroRTo extracts the matrix [ 0 R ] from the singular value decomposition,
   240  // storing the result into dst. [ 0 R ] is of size (k+l)×c.
   241  //
   242  // If dst is empty, ZeroRTo will resize dst to be (k+l)×c. When dst is
   243  // non-empty, ZeroRTo will panic if dst is not (k+l)×c. ZeroRTo will also panic
   244  // if the receiver does not contain a successful factorization.
   245  func (gsvd *GSVD) ZeroRTo(dst *Dense) {
   246  	if !gsvd.succFact() {
   247  		panic(badFact)
   248  	}
   249  	r := gsvd.r
   250  	c := gsvd.c
   251  	k := gsvd.k
   252  	l := gsvd.l
   253  	h := min(k+l, r)
   254  	if dst.IsEmpty() {
   255  		dst.ReuseAs(k+l, c)
   256  	} else {
   257  		r2, c2 := dst.Dims()
   258  		if r2 != k+l || c != c2 {
   259  			panic(ErrShape)
   260  		}
   261  		dst.Zero()
   262  	}
   263  	a := Dense{
   264  		mat:     gsvd.a,
   265  		capRows: r,
   266  		capCols: c,
   267  	}
   268  	dst.slice(0, h, c-k-l, c).Copy(a.Slice(0, h, c-k-l, c))
   269  	if r < k+l {
   270  		b := Dense{
   271  			mat:     gsvd.b,
   272  			capRows: gsvd.p,
   273  			capCols: c,
   274  		}
   275  		dst.slice(r, k+l, c+r-k-l, c).Copy(b.Slice(r-k, l, c+r-k-l, c))
   276  	}
   277  }
   278  
   279  // SigmaATo extracts the matrix Σ₁ from the singular value decomposition, storing
   280  // the result into dst. Σ₁ is size r×(k+l).
   281  //
   282  // If dst is empty, SigmaATo will resize dst to be r×(k+l). When dst is
   283  // non-empty, SigmATo will panic if dst is not r×(k+l). SigmaATo will also
   284  // panic if the receiver does not contain a successful factorization.
   285  func (gsvd *GSVD) SigmaATo(dst *Dense) {
   286  	if !gsvd.succFact() {
   287  		panic(badFact)
   288  	}
   289  	r := gsvd.r
   290  	k := gsvd.k
   291  	l := gsvd.l
   292  	if dst.IsEmpty() {
   293  		dst.ReuseAs(r, k+l)
   294  	} else {
   295  		r2, c := dst.Dims()
   296  		if r2 != r || c != k+l {
   297  			panic(ErrShape)
   298  		}
   299  		dst.Zero()
   300  	}
   301  	for i := 0; i < k; i++ {
   302  		dst.set(i, i, 1)
   303  	}
   304  	for i := k; i < min(r, k+l); i++ {
   305  		dst.set(i, i, gsvd.s1[i])
   306  	}
   307  }
   308  
   309  // SigmaBTo extracts the matrix Σ₂ from the singular value decomposition, storing
   310  // the result into dst. Σ₂ is size p×(k+l).
   311  //
   312  // If dst is empty, SigmaBTo will resize dst to be p×(k+l). When dst is
   313  // non-empty, SigmBTo will panic if dst is not p×(k+l). SigmaBTo will also
   314  // panic if the receiver does not contain a successful factorization.
   315  func (gsvd *GSVD) SigmaBTo(dst *Dense) {
   316  	if !gsvd.succFact() {
   317  		panic(badFact)
   318  	}
   319  	r := gsvd.r
   320  	p := gsvd.p
   321  	k := gsvd.k
   322  	l := gsvd.l
   323  	if dst.IsEmpty() {
   324  		dst.ReuseAs(p, k+l)
   325  	} else {
   326  		r, c := dst.Dims()
   327  		if r != p || c != k+l {
   328  			panic(ErrShape)
   329  		}
   330  		dst.Zero()
   331  	}
   332  	for i := 0; i < min(l, r-k); i++ {
   333  		dst.set(i, i+k, gsvd.s2[k+i])
   334  	}
   335  	for i := r - k; i < l; i++ {
   336  		dst.set(i, i+k, 1)
   337  	}
   338  }
   339  
   340  // UTo extracts the matrix U from the singular value decomposition, storing
   341  // the result into dst. U is size r×r.
   342  //
   343  // If dst is empty, UTo will resize dst to be r×r. When dst is
   344  // non-empty, UTo will panic if dst is not r×r. UTo will also
   345  // panic if the receiver does not contain a successful factorization.
   346  func (gsvd *GSVD) UTo(dst *Dense) {
   347  	if !gsvd.succFact() {
   348  		panic(badFact)
   349  	}
   350  	if gsvd.kind&GSVDU == 0 {
   351  		panic("mat: improper GSVD kind")
   352  	}
   353  	r := gsvd.u.Rows
   354  	c := gsvd.u.Cols
   355  	if dst.IsEmpty() {
   356  		dst.ReuseAs(r, c)
   357  	} else {
   358  		r2, c2 := dst.Dims()
   359  		if r != r2 || c != c2 {
   360  			panic(ErrShape)
   361  		}
   362  	}
   363  
   364  	tmp := &Dense{
   365  		mat:     gsvd.u,
   366  		capRows: r,
   367  		capCols: c,
   368  	}
   369  	dst.Copy(tmp)
   370  }
   371  
   372  // VTo extracts the matrix V from the singular value decomposition, storing
   373  // the result into dst. V is size p×p.
   374  //
   375  // If dst is empty, VTo will resize dst to be p×p. When dst is
   376  // non-empty, VTo will panic if dst is not p×p. VTo will also
   377  // panic if the receiver does not contain a successful factorization.
   378  func (gsvd *GSVD) VTo(dst *Dense) {
   379  	if !gsvd.succFact() {
   380  		panic(badFact)
   381  	}
   382  	if gsvd.kind&GSVDV == 0 {
   383  		panic("mat: improper GSVD kind")
   384  	}
   385  	r := gsvd.v.Rows
   386  	c := gsvd.v.Cols
   387  	if dst.IsEmpty() {
   388  		dst.ReuseAs(r, c)
   389  	} else {
   390  		r2, c2 := dst.Dims()
   391  		if r != r2 || c != c2 {
   392  			panic(ErrShape)
   393  		}
   394  	}
   395  
   396  	tmp := &Dense{
   397  		mat:     gsvd.v,
   398  		capRows: r,
   399  		capCols: c,
   400  	}
   401  	dst.Copy(tmp)
   402  }
   403  
   404  // QTo extracts the matrix Q from the singular value decomposition, storing
   405  // the result into dst. Q is size c×c.
   406  //
   407  // If dst is empty, QTo will resize dst to be c×c. When dst is
   408  // non-empty, QTo will panic if dst is not c×c. QTo will also
   409  // panic if the receiver does not contain a successful factorization.
   410  func (gsvd *GSVD) QTo(dst *Dense) {
   411  	if !gsvd.succFact() {
   412  		panic(badFact)
   413  	}
   414  	if gsvd.kind&GSVDQ == 0 {
   415  		panic("mat: improper GSVD kind")
   416  	}
   417  	r := gsvd.q.Rows
   418  	c := gsvd.q.Cols
   419  	if dst.IsEmpty() {
   420  		dst.ReuseAs(r, c)
   421  	} else {
   422  		r2, c2 := dst.Dims()
   423  		if r != r2 || c != c2 {
   424  			panic(ErrShape)
   425  		}
   426  	}
   427  
   428  	tmp := &Dense{
   429  		mat:     gsvd.q,
   430  		capRows: r,
   431  		capCols: c,
   432  	}
   433  	dst.Copy(tmp)
   434  }