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