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