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