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 }