github.com/gonum/matrix@v0.0.0-20181209220409-c518dec07be9/mat64/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 mat64
     6  
     7  import (
     8  	"github.com/gonum/blas/blas64"
     9  	"github.com/gonum/floats"
    10  	"github.com/gonum/lapack"
    11  	"github.com/gonum/lapack/lapack64"
    12  	"github.com/gonum/matrix"
    13  )
    14  
    15  // GSVD is a type for creating and using the Generalized Singular Value Decomposition
    16  // (GSVD) of a matrix.
    17  //
    18  // The factorization is a linear transformation of the data sets from the given
    19  // variable×sample spaces to reduced and diagonalized "eigenvariable"×"eigensample"
    20  // spaces.
    21  type GSVD struct {
    22  	kind matrix.GSVDKind
    23  
    24  	r, p, c, k, l int
    25  	s1, s2        []float64
    26  	a, b, u, v, q blas64.General
    27  
    28  	work  []float64
    29  	iwork []int
    30  }
    31  
    32  // Factorize computes the generalized singular value decomposition (GSVD) of the input
    33  // the r×c matrix A and the p×c matrix B. The singular values of A and B are computed
    34  // in all cases, while the singular vectors are optionally computed depending on the
    35  // input kind.
    36  //
    37  // The full singular value decomposition (kind == GSVDU|GSVDV|GSVDQ) deconstructs A and B as
    38  //  A = U * Σ₁ * [ 0 R ] * Q^T
    39  //
    40  //  B = V * Σ₂ * [ 0 R ] * Q^T
    41  // where Σ₁ and Σ₂ are r×(k+l) and p×(k+l) diagonal matrices of singular values, and
    42  // U, V and Q are r×r, p×p and c×c orthogonal matrices of singular vectors. k+l is the
    43  // effective numerical rank of the matrix [ A^T B^T ]^T.
    44  //
    45  // It is frequently not necessary to compute the full GSVD. Computation time and
    46  // storage costs can be reduced using the appropriate kind. Either only the singular
    47  // values can be computed (kind == SVDNone), or in conjunction with specific singular
    48  // vectors (kind bit set according to matrix.GSVDU, matrix.GSVDV and matrix.GSVDQ).
    49  //
    50  // Factorize returns whether the decomposition succeeded. If the decomposition
    51  // failed, routines that require a successful factorization will panic.
    52  func (gsvd *GSVD) Factorize(a, b Matrix, kind matrix.GSVDKind) (ok bool) {
    53  	r, c := a.Dims()
    54  	gsvd.r, gsvd.c = r, c
    55  	p, c := b.Dims()
    56  	gsvd.p = p
    57  	if gsvd.c != c {
    58  		panic(matrix.ErrShape)
    59  	}
    60  	var jobU, jobV, jobQ lapack.GSVDJob
    61  	switch {
    62  	default:
    63  		panic("gsvd: bad input kind")
    64  	case kind == matrix.GSVDNone:
    65  		jobU = lapack.GSVDNone
    66  		jobV = lapack.GSVDNone
    67  		jobQ = lapack.GSVDNone
    68  	case (matrix.GSVDU|matrix.GSVDV|matrix.GSVDQ)&kind != 0:
    69  		if matrix.GSVDU&kind != 0 {
    70  			jobU = lapack.GSVDU
    71  			gsvd.u = blas64.General{
    72  				Rows:   r,
    73  				Cols:   r,
    74  				Stride: r,
    75  				Data:   use(gsvd.u.Data, r*r),
    76  			}
    77  		}
    78  		if matrix.GSVDV&kind != 0 {
    79  			jobV = lapack.GSVDV
    80  			gsvd.v = blas64.General{
    81  				Rows:   p,
    82  				Cols:   p,
    83  				Stride: p,
    84  				Data:   use(gsvd.v.Data, p*p),
    85  			}
    86  		}
    87  		if matrix.GSVDQ&kind != 0 {
    88  			jobQ = lapack.GSVDQ
    89  			gsvd.q = blas64.General{
    90  				Rows:   c,
    91  				Cols:   c,
    92  				Stride: c,
    93  				Data:   use(gsvd.q.Data, c*c),
    94  			}
    95  		}
    96  	}
    97  
    98  	// A and B are destroyed on call, so copy the matrices.
    99  	aCopy := DenseCopyOf(a)
   100  	bCopy := DenseCopyOf(b)
   101  
   102  	gsvd.s1 = use(gsvd.s1, c)
   103  	gsvd.s2 = use(gsvd.s2, c)
   104  
   105  	gsvd.iwork = useInt(gsvd.iwork, c)
   106  
   107  	gsvd.work = use(gsvd.work, 1)
   108  	lapack64.Ggsvd3(jobU, jobV, jobQ, aCopy.mat, bCopy.mat, gsvd.s1, gsvd.s2, gsvd.u, gsvd.v, gsvd.q, gsvd.work, -1, gsvd.iwork)
   109  	gsvd.work = use(gsvd.work, int(gsvd.work[0]))
   110  	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)
   111  	if ok {
   112  		gsvd.a = aCopy.mat
   113  		gsvd.b = bCopy.mat
   114  		gsvd.kind = kind
   115  	}
   116  	return ok
   117  }
   118  
   119  // Kind returns the matrix.GSVDKind of the decomposition. If no decomposition has been
   120  // computed, Kind returns 0.
   121  func (gsvd *GSVD) Kind() matrix.GSVDKind {
   122  	return gsvd.kind
   123  }
   124  
   125  // Rank returns the k and l terms of the rank of [ A^T B^T ]^T.
   126  func (gsvd *GSVD) Rank() (k, l int) {
   127  	return gsvd.k, gsvd.l
   128  }
   129  
   130  // GeneralizedValues returns the generalized singular values of the factorized matrices.
   131  // If the input slice is non-nil, the values will be stored in-place into the slice.
   132  // In this case, the slice must have length min(r,c)-k, and GeneralizedValues will
   133  // panic with matrix.ErrSliceLengthMismatch otherwise. If the input slice is nil,
   134  // a new slice of the appropriate length will be allocated and returned.
   135  //
   136  // GeneralizedValues will panic if the receiver does not contain a successful factorization.
   137  func (gsvd *GSVD) GeneralizedValues(v []float64) []float64 {
   138  	if gsvd.kind == 0 {
   139  		panic("gsvd: no decomposition computed")
   140  	}
   141  	r := gsvd.r
   142  	c := gsvd.c
   143  	k := gsvd.k
   144  	d := min(r, c)
   145  	if v == nil {
   146  		v = make([]float64, d-k)
   147  	}
   148  	if len(v) != d-k {
   149  		panic(matrix.ErrSliceLengthMismatch)
   150  	}
   151  	floats.DivTo(v, gsvd.s1[k:d], gsvd.s2[k:d])
   152  	return v
   153  }
   154  
   155  // ValuesA returns the singular values of the factorized A matrix.
   156  // If the input slice is non-nil, the values will be stored in-place into the slice.
   157  // In this case, the slice must have length min(r,c)-k, and ValuesA will panic with
   158  // matrix.ErrSliceLengthMismatch otherwise. If the input slice is nil,
   159  // a new slice of the appropriate length will be allocated and returned.
   160  //
   161  // ValuesA will panic if the receiver does not contain a successful factorization.
   162  func (gsvd *GSVD) ValuesA(s []float64) []float64 {
   163  	if gsvd.kind == 0 {
   164  		panic("gsvd: no decomposition computed")
   165  	}
   166  	r := gsvd.r
   167  	c := gsvd.c
   168  	k := gsvd.k
   169  	d := min(r, c)
   170  	if s == nil {
   171  		s = make([]float64, d-k)
   172  	}
   173  	if len(s) != d-k {
   174  		panic(matrix.ErrSliceLengthMismatch)
   175  	}
   176  	copy(s, gsvd.s1[k:min(r, c)])
   177  	return s
   178  }
   179  
   180  // ValuesB returns the singular values of the factorized B matrix.
   181  // If the input slice is non-nil, the values will be stored in-place into the slice.
   182  // In this case, the slice must have length min(r,c)-k, and ValuesB will panic with
   183  // matrix.ErrSliceLengthMismatch otherwise. If the input slice is nil,
   184  // a new slice of the appropriate length will be allocated and returned.
   185  //
   186  // ValuesB will panic if the receiver does not contain a successful factorization.
   187  func (gsvd *GSVD) ValuesB(s []float64) []float64 {
   188  	if gsvd.kind == 0 {
   189  		panic("gsvd: no decomposition computed")
   190  	}
   191  	r := gsvd.r
   192  	c := gsvd.c
   193  	k := gsvd.k
   194  	d := min(r, c)
   195  	if s == nil {
   196  		s = make([]float64, d-k)
   197  	}
   198  	if len(s) != d-k {
   199  		panic(matrix.ErrSliceLengthMismatch)
   200  	}
   201  	copy(s, gsvd.s2[k:d])
   202  	return s
   203  }
   204  
   205  // ZeroRFromGSVD extracts the matrix [ 0 R ] from the singular value decomposition, storing
   206  // the result in-place into the receiver. [ 0 R ] is size (k+l)×c.
   207  func (m *Dense) ZeroRFromGSVD(gsvd *GSVD) {
   208  	if gsvd.kind == 0 {
   209  		panic("gsvd: no decomposition computed")
   210  	}
   211  	r := gsvd.r
   212  	c := gsvd.c
   213  	k := gsvd.k
   214  	l := gsvd.l
   215  	h := min(k+l, r)
   216  	m.reuseAsZeroed(k+l, c)
   217  	a := Dense{
   218  		mat:     gsvd.a,
   219  		capRows: r,
   220  		capCols: c,
   221  	}
   222  	m.Slice(0, h, c-k-l, c).(*Dense).
   223  		Copy(a.Slice(0, h, c-k-l, c))
   224  	if r < k+l {
   225  		b := Dense{
   226  			mat:     gsvd.b,
   227  			capRows: gsvd.p,
   228  			capCols: c,
   229  		}
   230  		m.Slice(r, k+l, c+r-k-l, c).(*Dense).
   231  			Copy(b.Slice(r-k, l, c+r-k-l, c))
   232  	}
   233  }
   234  
   235  // SigmaAFromGSVD extracts the matrix Σ₁ from the singular value decomposition, storing
   236  // the result in-place into the receiver. Σ₁ is size r×(k+l).
   237  func (m *Dense) SigmaAFromGSVD(gsvd *GSVD) {
   238  	if gsvd.kind == 0 {
   239  		panic("gsvd: no decomposition computed")
   240  	}
   241  	r := gsvd.r
   242  	k := gsvd.k
   243  	l := gsvd.l
   244  	m.reuseAsZeroed(r, k+l)
   245  	for i := 0; i < k; i++ {
   246  		m.set(i, i, 1)
   247  	}
   248  	for i := k; i < min(r, k+l); i++ {
   249  		m.set(i, i, gsvd.s1[i])
   250  	}
   251  }
   252  
   253  // SigmaBFromGSVD extracts the matrix Σ₂ from the singular value decomposition, storing
   254  // the result in-place into the receiver. Σ₂ is size p×(k+l).
   255  func (m *Dense) SigmaBFromGSVD(gsvd *GSVD) {
   256  	if gsvd.kind == 0 {
   257  		panic("gsvd: no decomposition computed")
   258  	}
   259  	r := gsvd.r
   260  	p := gsvd.p
   261  	k := gsvd.k
   262  	l := gsvd.l
   263  	m.reuseAsZeroed(p, k+l)
   264  	for i := 0; i < min(l, r-k); i++ {
   265  		m.set(i, i+k, gsvd.s2[k+i])
   266  	}
   267  	for i := r - k; i < l; i++ {
   268  		m.set(i, i+k, 1)
   269  	}
   270  }
   271  
   272  // UFromGSVD extracts the matrix U from the singular value decomposition, storing
   273  // the result in-place into the receiver. U is size r×r.
   274  func (m *Dense) UFromGSVD(gsvd *GSVD) {
   275  	if gsvd.kind&matrix.GSVDU == 0 {
   276  		panic("mat64: improper GSVD kind")
   277  	}
   278  	r := gsvd.u.Rows
   279  	c := gsvd.u.Cols
   280  	m.reuseAs(r, c)
   281  
   282  	tmp := &Dense{
   283  		mat:     gsvd.u,
   284  		capRows: r,
   285  		capCols: c,
   286  	}
   287  	m.Copy(tmp)
   288  }
   289  
   290  // VFromGSVD extracts the matrix V from the singular value decomposition, storing
   291  // the result in-place into the receiver. V is size p×p.
   292  func (m *Dense) VFromGSVD(gsvd *GSVD) {
   293  	if gsvd.kind&matrix.GSVDV == 0 {
   294  		panic("mat64: improper GSVD kind")
   295  	}
   296  	r := gsvd.v.Rows
   297  	c := gsvd.v.Cols
   298  	m.reuseAs(r, c)
   299  
   300  	tmp := &Dense{
   301  		mat:     gsvd.v,
   302  		capRows: r,
   303  		capCols: c,
   304  	}
   305  	m.Copy(tmp)
   306  }
   307  
   308  // QFromGSVD extracts the matrix Q from the singular value decomposition, storing
   309  // the result in-place into the receiver. Q is size c×c.
   310  func (m *Dense) QFromGSVD(gsvd *GSVD) {
   311  	if gsvd.kind&matrix.GSVDQ == 0 {
   312  		panic("mat64: improper GSVD kind")
   313  	}
   314  	r := gsvd.q.Rows
   315  	c := gsvd.q.Cols
   316  	m.reuseAs(r, c)
   317  
   318  	tmp := &Dense{
   319  		mat:     gsvd.q,
   320  		capRows: r,
   321  		capCols: c,
   322  	}
   323  	m.Copy(tmp)
   324  }