github.com/gopherd/gonum@v0.0.4/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/gopherd/gonum/blas/blas64" 11 "github.com/gopherd/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 }