gonum.org/v1/gonum@v0.14.0/lapack/gonum/dggsvp3.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" 11 "gonum.org/v1/gonum/lapack" 12 ) 13 14 // Dggsvp3 computes orthogonal matrices U, V and Q such that 15 // 16 // n-k-l k l 17 // Uᵀ*A*Q = k [ 0 A12 A13 ] if m-k-l >= 0; 18 // l [ 0 0 A23 ] 19 // m-k-l [ 0 0 0 ] 20 // 21 // n-k-l k l 22 // Uᵀ*A*Q = k [ 0 A12 A13 ] if m-k-l < 0; 23 // m-k [ 0 0 A23 ] 24 // 25 // n-k-l k l 26 // Vᵀ*B*Q = l [ 0 0 B13 ] 27 // p-l [ 0 0 0 ] 28 // 29 // where the k×k matrix A12 and l×l matrix B13 are non-singular 30 // upper triangular. A23 is l×l upper triangular if m-k-l >= 0, 31 // otherwise A23 is (m-k)×l upper trapezoidal. 32 // 33 // Dggsvp3 returns k and l, the dimensions of the sub-blocks. k+l 34 // is the effective numerical rank of the (m+p)×n matrix [ Aᵀ Bᵀ ]ᵀ. 35 // 36 // jobU, jobV and jobQ are options for computing the orthogonal matrices. The behavior 37 // is as follows 38 // 39 // jobU == lapack.GSVDU Compute orthogonal matrix U 40 // jobU == lapack.GSVDNone Do not compute orthogonal matrix. 41 // 42 // The behavior is the same for jobV and jobQ with the exception that instead of 43 // lapack.GSVDU these accept lapack.GSVDV and lapack.GSVDQ respectively. 44 // The matrices U, V and Q must be m×m, p×p and n×n respectively unless the 45 // relevant job parameter is lapack.GSVDNone. 46 // 47 // tola and tolb are the convergence criteria for the Jacobi-Kogbetliantz 48 // iteration procedure. Generally, they are the same as used in the preprocessing 49 // step, for example, 50 // 51 // tola = max(m, n)*norm(A)*eps, 52 // tolb = max(p, n)*norm(B)*eps. 53 // 54 // Where eps is the machine epsilon. 55 // 56 // iwork must have length n, work must have length at least max(1, lwork), and 57 // lwork must be -1 or greater than zero, otherwise Dggsvp3 will panic. 58 // 59 // Dggsvp3 is an internal routine. It is exported for testing purposes. 60 func (impl Implementation) Dggsvp3(jobU, jobV, jobQ lapack.GSVDJob, m, p, n int, a []float64, lda int, b []float64, ldb int, tola, tolb float64, u []float64, ldu int, v []float64, ldv int, q []float64, ldq int, iwork []int, tau, work []float64, lwork int) (k, l int) { 61 wantu := jobU == lapack.GSVDU 62 wantv := jobV == lapack.GSVDV 63 wantq := jobQ == lapack.GSVDQ 64 switch { 65 case !wantu && jobU != lapack.GSVDNone: 66 panic(badGSVDJob + "U") 67 case !wantv && jobV != lapack.GSVDNone: 68 panic(badGSVDJob + "V") 69 case !wantq && jobQ != lapack.GSVDNone: 70 panic(badGSVDJob + "Q") 71 case m < 0: 72 panic(mLT0) 73 case p < 0: 74 panic(pLT0) 75 case n < 0: 76 panic(nLT0) 77 case lda < max(1, n): 78 panic(badLdA) 79 case ldb < max(1, n): 80 panic(badLdB) 81 case ldu < 1, wantu && ldu < m: 82 panic(badLdU) 83 case ldv < 1, wantv && ldv < p: 84 panic(badLdV) 85 case ldq < 1, wantq && ldq < n: 86 panic(badLdQ) 87 case len(iwork) != n: 88 panic(shortWork) 89 case lwork < 1 && lwork != -1: 90 panic(badLWork) 91 case len(work) < max(1, lwork): 92 panic(shortWork) 93 } 94 95 var lwkopt int 96 impl.Dgeqp3(p, n, b, ldb, iwork, tau, work, -1) 97 lwkopt = int(work[0]) 98 if wantv { 99 lwkopt = max(lwkopt, p) 100 } 101 lwkopt = max(lwkopt, min(n, p)) 102 lwkopt = max(lwkopt, m) 103 if wantq { 104 lwkopt = max(lwkopt, n) 105 } 106 impl.Dgeqp3(m, n, a, lda, iwork, tau, work, -1) 107 lwkopt = max(lwkopt, int(work[0])) 108 lwkopt = max(1, lwkopt) 109 if lwork == -1 { 110 work[0] = float64(lwkopt) 111 return 0, 0 112 } 113 114 switch { 115 case len(a) < (m-1)*lda+n: 116 panic(shortA) 117 case len(b) < (p-1)*ldb+n: 118 panic(shortB) 119 case wantu && len(u) < (m-1)*ldu+m: 120 panic(shortU) 121 case wantv && len(v) < (p-1)*ldv+p: 122 panic(shortV) 123 case wantq && len(q) < (n-1)*ldq+n: 124 panic(shortQ) 125 case len(tau) < n: 126 // tau check must come after lwkopt query since 127 // the Dggsvd3 call for lwkopt query may have 128 // lwork == -1, and tau is provided by work. 129 panic(shortTau) 130 } 131 132 const forward = true 133 134 // QR with column pivoting of B: B*P = V*[ S11 S12 ]. 135 // [ 0 0 ] 136 for i := range iwork[:n] { 137 iwork[i] = 0 138 } 139 impl.Dgeqp3(p, n, b, ldb, iwork, tau, work, lwork) 140 141 // Update A := A*P. 142 impl.Dlapmt(forward, m, n, a, lda, iwork) 143 144 // Determine the effective rank of matrix B. 145 for i := 0; i < min(p, n); i++ { 146 if math.Abs(b[i*ldb+i]) > tolb { 147 l++ 148 } 149 } 150 151 if wantv { 152 // Copy the details of V, and form V. 153 impl.Dlaset(blas.All, p, p, 0, 0, v, ldv) 154 if p > 1 { 155 impl.Dlacpy(blas.Lower, p-1, min(p, n), b[ldb:], ldb, v[ldv:], ldv) 156 } 157 impl.Dorg2r(p, p, min(p, n), v, ldv, tau, work) 158 } 159 160 // Clean up B. 161 for i := 1; i < l; i++ { 162 r := b[i*ldb : i*ldb+i] 163 for j := range r { 164 r[j] = 0 165 } 166 } 167 if p > l { 168 impl.Dlaset(blas.All, p-l, n, 0, 0, b[l*ldb:], ldb) 169 } 170 171 if wantq { 172 // Set Q = I and update Q := Q*P. 173 impl.Dlaset(blas.All, n, n, 0, 1, q, ldq) 174 impl.Dlapmt(forward, n, n, q, ldq, iwork) 175 } 176 177 if p >= l && n != l { 178 // RQ factorization of [ S11 S12 ]: [ S11 S12 ] = [ 0 S12 ]*Z. 179 impl.Dgerq2(l, n, b, ldb, tau, work) 180 181 // Update A := A*Zᵀ. 182 impl.Dormr2(blas.Right, blas.Trans, m, n, l, b, ldb, tau, a, lda, work) 183 184 if wantq { 185 // Update Q := Q*Zᵀ. 186 impl.Dormr2(blas.Right, blas.Trans, n, n, l, b, ldb, tau, q, ldq, work) 187 } 188 189 // Clean up B. 190 impl.Dlaset(blas.All, l, n-l, 0, 0, b, ldb) 191 for i := 1; i < l; i++ { 192 r := b[i*ldb+n-l : i*ldb+i+n-l] 193 for j := range r { 194 r[j] = 0 195 } 196 } 197 } 198 199 // Let N-L L 200 // A = [ A11 A12 ] M, 201 // 202 // then the following does the complete QR decomposition of A11: 203 // 204 // A11 = U*[ 0 T12 ]*P1ᵀ. 205 // [ 0 0 ] 206 for i := range iwork[:n-l] { 207 iwork[i] = 0 208 } 209 impl.Dgeqp3(m, n-l, a, lda, iwork[:n-l], tau, work, lwork) 210 211 // Determine the effective rank of A11. 212 for i := 0; i < min(m, n-l); i++ { 213 if math.Abs(a[i*lda+i]) > tola { 214 k++ 215 } 216 } 217 218 // Update A12 := Uᵀ*A12, where A12 = A[0:m, n-l:n]. 219 impl.Dorm2r(blas.Left, blas.Trans, m, l, min(m, n-l), a, lda, tau, a[n-l:], lda, work) 220 221 if wantu { 222 // Copy the details of U, and form U. 223 impl.Dlaset(blas.All, m, m, 0, 0, u, ldu) 224 if m > 1 { 225 impl.Dlacpy(blas.Lower, m-1, min(m, n-l), a[lda:], lda, u[ldu:], ldu) 226 } 227 impl.Dorg2r(m, m, min(m, n-l), u, ldu, tau, work) 228 } 229 230 if wantq { 231 // Update Q[0:n, 0:n-l] := Q[0:n, 0:n-l]*P1. 232 impl.Dlapmt(forward, n, n-l, q, ldq, iwork[:n-l]) 233 } 234 235 // Clean up A: set the strictly lower triangular part of 236 // A[0:k, 0:k] = 0, and A[k:m, 0:n-l] = 0. 237 for i := 1; i < k; i++ { 238 r := a[i*lda : i*lda+i] 239 for j := range r { 240 r[j] = 0 241 } 242 } 243 if m > k { 244 impl.Dlaset(blas.All, m-k, n-l, 0, 0, a[k*lda:], lda) 245 } 246 247 if n-l > k { 248 // RQ factorization of [ T11 T12 ] = [ 0 T12 ]*Z1. 249 impl.Dgerq2(k, n-l, a, lda, tau, work) 250 251 if wantq { 252 // Update Q[0:n, 0:n-l] := Q[0:n, 0:n-l]*Z1ᵀ. 253 impl.Dorm2r(blas.Right, blas.Trans, n, n-l, k, a, lda, tau, q, ldq, work) 254 } 255 256 // Clean up A. 257 impl.Dlaset(blas.All, k, n-l-k, 0, 0, a, lda) 258 for i := 1; i < k; i++ { 259 r := a[i*lda+n-k-l : i*lda+i+n-k-l] 260 for j := range r { 261 a[j] = 0 262 } 263 } 264 } 265 266 if m > k { 267 // QR factorization of A[k:m, n-l:n]. 268 impl.Dgeqr2(m-k, l, a[k*lda+n-l:], lda, tau, work) 269 if wantu { 270 // Update U[:, k:m) := U[:, k:m]*U1. 271 impl.Dorm2r(blas.Right, blas.NoTrans, m, m-k, min(m-k, l), a[k*lda+n-l:], lda, tau, u[k:], ldu, work) 272 } 273 274 // Clean up A. 275 for i := k + 1; i < m; i++ { 276 r := a[i*lda+n-l : i*lda+min(n-l+i-k, n)] 277 for j := range r { 278 r[j] = 0 279 } 280 } 281 } 282 283 work[0] = float64(lwkopt) 284 return k, l 285 }