github.com/gonum/lapack@v0.0.0-20181123203213-e4cdc5a0bff9/native/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 native
     6  
     7  import (
     8  	"math"
     9  
    10  	"github.com/gonum/blas/blas64"
    11  	"github.com/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^T*A*Q = D1*[ 0 R ]
    17  //
    18  //  V^T*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^T B^T ]^T.
    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  	checkMatrix(m, n, a, lda)
   112  	checkMatrix(p, n, b, ldb)
   113  
   114  	wantu := jobU == lapack.GSVDU
   115  	if wantu {
   116  		checkMatrix(m, m, u, ldu)
   117  	} else if jobU != lapack.GSVDNone {
   118  		panic(badGSVDJob + "U")
   119  	}
   120  	wantv := jobV == lapack.GSVDV
   121  	if wantv {
   122  		checkMatrix(p, p, v, ldv)
   123  	} else if jobV != lapack.GSVDNone {
   124  		panic(badGSVDJob + "V")
   125  	}
   126  	wantq := jobQ == lapack.GSVDQ
   127  	if wantq {
   128  		checkMatrix(n, n, q, ldq)
   129  	} else if jobQ != lapack.GSVDNone {
   130  		panic(badGSVDJob + "Q")
   131  	}
   132  
   133  	if len(alpha) != n {
   134  		panic(badAlpha)
   135  	}
   136  	if len(beta) != n {
   137  		panic(badBeta)
   138  	}
   139  
   140  	if lwork != -1 && lwork <= n {
   141  		panic(badWork)
   142  	}
   143  	if len(work) < max(1, lwork) {
   144  		panic(shortWork)
   145  	}
   146  	if len(iwork) < n {
   147  		panic(badWork)
   148  	}
   149  
   150  	// Determine optimal work length.
   151  	impl.Dggsvp3(jobU, jobV, jobQ,
   152  		m, p, n,
   153  		a, lda,
   154  		b, ldb,
   155  		0, 0,
   156  		u, ldu,
   157  		v, ldv,
   158  		q, ldq,
   159  		iwork,
   160  		work, work, -1)
   161  	lwkopt := n + int(work[0])
   162  	lwkopt = max(lwkopt, 2*n)
   163  	lwkopt = max(lwkopt, 1)
   164  	work[0] = float64(lwkopt)
   165  	if lwork == -1 {
   166  		return 0, 0, true
   167  	}
   168  
   169  	// Compute the Frobenius norm of matrices A and B.
   170  	anorm := impl.Dlange(lapack.NormFrob, m, n, a, lda, nil)
   171  	bnorm := impl.Dlange(lapack.NormFrob, p, n, b, ldb, nil)
   172  
   173  	// Get machine precision and set up threshold for determining
   174  	// the effective numerical rank of the matrices A and B.
   175  	tola := float64(max(m, n)) * math.Max(anorm, dlamchS) * dlamchP
   176  	tolb := float64(max(p, n)) * math.Max(bnorm, dlamchS) * dlamchP
   177  
   178  	// Preprocessing.
   179  	k, l = impl.Dggsvp3(jobU, jobV, jobQ,
   180  		m, p, n,
   181  		a, lda,
   182  		b, ldb,
   183  		tola, tolb,
   184  		u, ldu,
   185  		v, ldv,
   186  		q, ldq,
   187  		iwork,
   188  		work[:n], work[n:], lwork-n)
   189  
   190  	// Compute the GSVD of two upper "triangular" matrices.
   191  	_, ok = impl.Dtgsja(jobU, jobV, jobQ,
   192  		m, p, n,
   193  		k, l,
   194  		a, lda,
   195  		b, ldb,
   196  		tola, tolb,
   197  		alpha, beta,
   198  		u, ldu,
   199  		v, ldv,
   200  		q, ldq,
   201  		work)
   202  
   203  	// Sort the singular values and store the pivot indices in iwork
   204  	// Copy alpha to work, then sort alpha in work.
   205  	bi := blas64.Implementation()
   206  	bi.Dcopy(n, alpha, 1, work[:n], 1)
   207  	ibnd := min(l, m-k)
   208  	for i := 0; i < ibnd; i++ {
   209  		// Scan for largest alpha_{k+i}.
   210  		isub := i
   211  		smax := work[k+i]
   212  		for j := i + 1; j < ibnd; j++ {
   213  			if v := work[k+j]; v > smax {
   214  				isub = j
   215  				smax = v
   216  			}
   217  		}
   218  		if isub != i {
   219  			work[k+isub] = work[k+i]
   220  			work[k+i] = smax
   221  			iwork[k+i] = k + isub
   222  		} else {
   223  			iwork[k+i] = k + i
   224  		}
   225  	}
   226  
   227  	work[0] = float64(lwkopt)
   228  
   229  	return k, l, ok
   230  }