gonum.org/v1/gonum@v0.14.0/lapack/gonum/dggsvd3.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 gonum
     6  
     7  import (
     8  	"math"
     9  
    10  	"gonum.org/v1/gonum/blas/blas64"
    11  	"gonum.org/v1/gonum/lapack"
    12  )
    13  
    14  // Dggsvd3 computes the generalized singular value decomposition (GSVD)
    15  // of an m×n matrix A and p×n matrix B:
    16  //
    17  //	Uᵀ*A*Q = D1*[ 0 R ]
    18  //
    19  //	Vᵀ*B*Q = D2*[ 0 R ]
    20  //
    21  // where U, V and Q are orthogonal matrices.
    22  //
    23  // Dggsvd3 returns k and l, the dimensions of the sub-blocks. k+l
    24  // is the effective numerical rank of the (m+p)×n matrix [ Aᵀ Bᵀ ]ᵀ.
    25  // R is a (k+l)×(k+l) nonsingular upper triangular matrix, D1 and
    26  // D2 are m×(k+l) and p×(k+l) diagonal matrices and of the following
    27  // structures, respectively:
    28  //
    29  // If m-k-l >= 0,
    30  //
    31  //	                  k  l
    32  //	     D1 =     k [ I  0 ]
    33  //	              l [ 0  C ]
    34  //	          m-k-l [ 0  0 ]
    35  //
    36  //	                k  l
    37  //	     D2 = l   [ 0  S ]
    38  //	          p-l [ 0  0 ]
    39  //
    40  //	             n-k-l  k    l
    41  //	[ 0 R ] = k [  0   R11  R12 ] k
    42  //	          l [  0    0   R22 ] l
    43  //
    44  // where
    45  //
    46  //	C = diag( alpha_k, ... , alpha_{k+l} ),
    47  //	S = diag( beta_k,  ... , beta_{k+l} ),
    48  //	C^2 + S^2 = I.
    49  //
    50  // R is stored in
    51  //
    52  //	A[0:k+l, n-k-l:n]
    53  //
    54  // on exit.
    55  //
    56  // If m-k-l < 0,
    57  //
    58  //	               k m-k k+l-m
    59  //	    D1 =   k [ I  0    0  ]
    60  //	         m-k [ 0  C    0  ]
    61  //
    62  //	                 k m-k k+l-m
    63  //	    D2 =   m-k [ 0  S    0  ]
    64  //	         k+l-m [ 0  0    I  ]
    65  //	           p-l [ 0  0    0  ]
    66  //
    67  //	               n-k-l  k   m-k  k+l-m
    68  //	[ 0 R ] =    k [ 0    R11  R12  R13 ]
    69  //	           m-k [ 0     0   R22  R23 ]
    70  //	         k+l-m [ 0     0    0   R33 ]
    71  //
    72  // where
    73  //
    74  //	C = diag( alpha_k, ... , alpha_m ),
    75  //	S = diag( beta_k,  ... , beta_m ),
    76  //	C^2 + S^2 = I.
    77  //
    78  //	R = [ R11 R12 R13 ] is stored in A[1:m, n-k-l+1:n]
    79  //	    [  0  R22 R23 ]
    80  //
    81  // and R33 is stored in
    82  //
    83  //	B[m-k:l, n+m-k-l:n] on exit.
    84  //
    85  // Dggsvd3 computes C, S, R, and optionally the orthogonal transformation
    86  // matrices U, V and Q.
    87  //
    88  // jobU, jobV and jobQ are options for computing the orthogonal matrices. The behavior
    89  // is as follows
    90  //
    91  //	jobU == lapack.GSVDU        Compute orthogonal matrix U
    92  //	jobU == lapack.GSVDNone     Do not compute orthogonal matrix.
    93  //
    94  // The behavior is the same for jobV and jobQ with the exception that instead of
    95  // lapack.GSVDU these accept lapack.GSVDV and lapack.GSVDQ respectively.
    96  // The matrices U, V and Q must be m×m, p×p and n×n respectively unless the
    97  // relevant job parameter is lapack.GSVDNone.
    98  //
    99  // alpha and beta must have length n or Dggsvd3 will panic. On exit, alpha and
   100  // beta contain the generalized singular value pairs of A and B
   101  //
   102  //	alpha[0:k] = 1,
   103  //	beta[0:k]  = 0,
   104  //
   105  // if m-k-l >= 0,
   106  //
   107  //	alpha[k:k+l] = diag(C),
   108  //	beta[k:k+l]  = diag(S),
   109  //
   110  // if m-k-l < 0,
   111  //
   112  //	alpha[k:m]= C, alpha[m:k+l]= 0
   113  //	beta[k:m] = S, beta[m:k+l] = 1.
   114  //
   115  // if k+l < n,
   116  //
   117  //	alpha[k+l:n] = 0 and
   118  //	beta[k+l:n]  = 0.
   119  //
   120  // On exit, iwork contains the permutation required to sort alpha descending.
   121  //
   122  // iwork must have length n, work must have length at least max(1, lwork), and
   123  // lwork must be -1 or greater than n, otherwise Dggsvd3 will panic. If
   124  // lwork is -1, work[0] holds the optimal lwork on return, but Dggsvd3 does
   125  // not perform the GSVD.
   126  func (impl Implementation) Dggsvd3(jobU, jobV, jobQ lapack.GSVDJob, m, n, p int, a []float64, lda int, b []float64, ldb int, alpha, beta, u []float64, ldu int, v []float64, ldv int, q []float64, ldq int, work []float64, lwork int, iwork []int) (k, l int, ok bool) {
   127  	wantu := jobU == lapack.GSVDU
   128  	wantv := jobV == lapack.GSVDV
   129  	wantq := jobQ == lapack.GSVDQ
   130  	switch {
   131  	case !wantu && jobU != lapack.GSVDNone:
   132  		panic(badGSVDJob + "U")
   133  	case !wantv && jobV != lapack.GSVDNone:
   134  		panic(badGSVDJob + "V")
   135  	case !wantq && jobQ != lapack.GSVDNone:
   136  		panic(badGSVDJob + "Q")
   137  	case m < 0:
   138  		panic(mLT0)
   139  	case n < 0:
   140  		panic(nLT0)
   141  	case p < 0:
   142  		panic(pLT0)
   143  	case lda < max(1, n):
   144  		panic(badLdA)
   145  	case ldb < max(1, n):
   146  		panic(badLdB)
   147  	case ldu < 1, wantu && ldu < m:
   148  		panic(badLdU)
   149  	case ldv < 1, wantv && ldv < p:
   150  		panic(badLdV)
   151  	case ldq < 1, wantq && ldq < n:
   152  		panic(badLdQ)
   153  	case len(iwork) < n:
   154  		panic(shortWork)
   155  	case lwork < 1 && lwork != -1:
   156  		panic(badLWork)
   157  	case len(work) < max(1, lwork):
   158  		panic(shortWork)
   159  	}
   160  
   161  	// Determine optimal work length.
   162  	impl.Dggsvp3(jobU, jobV, jobQ,
   163  		m, p, n,
   164  		a, lda,
   165  		b, ldb,
   166  		0, 0,
   167  		u, ldu,
   168  		v, ldv,
   169  		q, ldq,
   170  		iwork,
   171  		work, work, -1)
   172  	lwkopt := n + int(work[0])
   173  	lwkopt = max(lwkopt, 2*n)
   174  	lwkopt = max(lwkopt, 1)
   175  	work[0] = float64(lwkopt)
   176  	if lwork == -1 {
   177  		return 0, 0, true
   178  	}
   179  
   180  	switch {
   181  	case len(a) < (m-1)*lda+n:
   182  		panic(shortA)
   183  	case len(b) < (p-1)*ldb+n:
   184  		panic(shortB)
   185  	case wantu && len(u) < (m-1)*ldu+m:
   186  		panic(shortU)
   187  	case wantv && len(v) < (p-1)*ldv+p:
   188  		panic(shortV)
   189  	case wantq && len(q) < (n-1)*ldq+n:
   190  		panic(shortQ)
   191  	case len(alpha) != n:
   192  		panic(badLenAlpha)
   193  	case len(beta) != n:
   194  		panic(badLenBeta)
   195  	}
   196  
   197  	// Compute the Frobenius norm of matrices A and B.
   198  	anorm := impl.Dlange(lapack.Frobenius, m, n, a, lda, nil)
   199  	bnorm := impl.Dlange(lapack.Frobenius, p, n, b, ldb, nil)
   200  
   201  	// Get machine precision and set up threshold for determining
   202  	// the effective numerical rank of the matrices A and B.
   203  	tola := float64(max(m, n)) * math.Max(anorm, dlamchS) * dlamchP
   204  	tolb := float64(max(p, n)) * math.Max(bnorm, dlamchS) * dlamchP
   205  
   206  	// Preprocessing.
   207  	k, l = impl.Dggsvp3(jobU, jobV, jobQ,
   208  		m, p, n,
   209  		a, lda,
   210  		b, ldb,
   211  		tola, tolb,
   212  		u, ldu,
   213  		v, ldv,
   214  		q, ldq,
   215  		iwork,
   216  		work[:n], work[n:], lwork-n)
   217  
   218  	// Compute the GSVD of two upper "triangular" matrices.
   219  	_, ok = impl.Dtgsja(jobU, jobV, jobQ,
   220  		m, p, n,
   221  		k, l,
   222  		a, lda,
   223  		b, ldb,
   224  		tola, tolb,
   225  		alpha, beta,
   226  		u, ldu,
   227  		v, ldv,
   228  		q, ldq,
   229  		work)
   230  
   231  	// Sort the singular values and store the pivot indices in iwork
   232  	// Copy alpha to work, then sort alpha in work.
   233  	bi := blas64.Implementation()
   234  	bi.Dcopy(n, alpha, 1, work[:n], 1)
   235  	ibnd := min(l, m-k)
   236  	for i := 0; i < ibnd; i++ {
   237  		// Scan for largest alpha_{k+i}.
   238  		isub := i
   239  		smax := work[k+i]
   240  		for j := i + 1; j < ibnd; j++ {
   241  			if v := work[k+j]; v > smax {
   242  				isub = j
   243  				smax = v
   244  			}
   245  		}
   246  		if isub != i {
   247  			work[k+isub] = work[k+i]
   248  			work[k+i] = smax
   249  			iwork[k+i] = k + isub
   250  		} else {
   251  			iwork[k+i] = k + i
   252  		}
   253  	}
   254  
   255  	work[0] = float64(lwkopt)
   256  
   257  	return k, l, ok
   258  }