github.com/gonum/lapack@v0.0.0-20181123203213-e4cdc5a0bff9/native/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 native 6 7 import ( 8 "math" 9 10 "github.com/gonum/blas" 11 "github.com/gonum/lapack" 12 ) 13 14 // Dggsvp3 computes orthogonal matrices U, V and Q such that 15 // 16 // n-k-l k l 17 // U^T*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^T*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^T*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^T B^T ]^T. 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 const forward = true 58 59 checkMatrix(m, n, a, lda) 60 checkMatrix(p, n, b, ldb) 61 62 wantu := jobU == lapack.GSVDU 63 if !wantu && jobU != lapack.GSVDNone { 64 panic(badGSVDJob + "U") 65 } 66 if jobU != lapack.GSVDNone { 67 checkMatrix(m, m, u, ldu) 68 } 69 70 wantv := jobV == lapack.GSVDV 71 if !wantv && jobV != lapack.GSVDNone { 72 panic(badGSVDJob + "V") 73 } 74 if jobV != lapack.GSVDNone { 75 checkMatrix(p, p, v, ldv) 76 } 77 78 wantq := jobQ == lapack.GSVDQ 79 if !wantq && jobQ != lapack.GSVDNone { 80 panic(badGSVDJob + "Q") 81 } 82 if jobQ != lapack.GSVDNone { 83 checkMatrix(n, n, q, ldq) 84 } 85 86 if len(iwork) != n { 87 panic(badWork) 88 } 89 if lwork != -1 && lwork < 1 { 90 panic(badWork) 91 } 92 if len(work) < max(1, lwork) { 93 panic(badWork) 94 } 95 96 var lwkopt int 97 impl.Dgeqp3(p, n, b, ldb, iwork, tau, work, -1) 98 lwkopt = int(work[0]) 99 if wantv { 100 lwkopt = max(lwkopt, p) 101 } 102 lwkopt = max(lwkopt, min(n, p)) 103 lwkopt = max(lwkopt, m) 104 if wantq { 105 lwkopt = max(lwkopt, n) 106 } 107 impl.Dgeqp3(m, n, a, lda, iwork, tau, work, -1) 108 lwkopt = max(lwkopt, int(work[0])) 109 lwkopt = max(1, lwkopt) 110 if lwork == -1 { 111 work[0] = float64(lwkopt) 112 return 0, 0 113 } 114 115 // tau check must come after lwkopt query since 116 // the Dggsvd3 call for lwkopt query may have 117 // lwork == -1, and tau is provided by work. 118 if len(tau) < n { 119 panic(badTau) 120 } 121 122 // QR with column pivoting of B: B*P = V*[ S11 S12 ]. 123 // [ 0 0 ] 124 for i := range iwork[:n] { 125 iwork[i] = 0 126 } 127 impl.Dgeqp3(p, n, b, ldb, iwork, tau, work, lwork) 128 129 // Update A := A*P. 130 impl.Dlapmt(forward, m, n, a, lda, iwork) 131 132 // Determine the effective rank of matrix B. 133 for i := 0; i < min(p, n); i++ { 134 if math.Abs(b[i*ldb+i]) > tolb { 135 l++ 136 } 137 } 138 139 if wantv { 140 // Copy the details of V, and form V. 141 impl.Dlaset(blas.All, p, p, 0, 0, v, ldv) 142 if p > 1 { 143 impl.Dlacpy(blas.Lower, p-1, min(p, n), b[ldb:], ldb, v[ldv:], ldv) 144 } 145 impl.Dorg2r(p, p, min(p, n), v, ldv, tau, work) 146 } 147 148 // Clean up B. 149 for i := 1; i < l; i++ { 150 r := b[i*ldb : i*ldb+i] 151 for j := range r { 152 r[j] = 0 153 } 154 } 155 if p > l { 156 impl.Dlaset(blas.All, p-l, n, 0, 0, b[l*ldb:], ldb) 157 } 158 159 if wantq { 160 // Set Q = I and update Q := Q*P. 161 impl.Dlaset(blas.All, n, n, 0, 1, q, ldq) 162 impl.Dlapmt(forward, n, n, q, ldq, iwork) 163 } 164 165 if p >= l && n != l { 166 // RQ factorization of [ S11 S12 ]: [ S11 S12 ] = [ 0 S12 ]*Z. 167 impl.Dgerq2(l, n, b, ldb, tau, work) 168 169 // Update A := A*Z^T. 170 impl.Dormr2(blas.Right, blas.Trans, m, n, l, b, ldb, tau, a, lda, work) 171 172 if wantq { 173 // Update Q := Q*Z^T. 174 impl.Dormr2(blas.Right, blas.Trans, n, n, l, b, ldb, tau, q, ldq, work) 175 } 176 177 // Clean up B. 178 impl.Dlaset(blas.All, l, n-l, 0, 0, b, ldb) 179 for i := 1; i < l; i++ { 180 r := b[i*ldb+n-l : i*ldb+i+n-l] 181 for j := range r { 182 r[j] = 0 183 } 184 } 185 } 186 187 // Let N-L L 188 // A = [ A11 A12 ] M, 189 // 190 // then the following does the complete QR decomposition of A11: 191 // 192 // A11 = U*[ 0 T12 ]*P1^T. 193 // [ 0 0 ] 194 for i := range iwork[:n-l] { 195 iwork[i] = 0 196 } 197 impl.Dgeqp3(m, n-l, a, lda, iwork[:n-l], tau, work, lwork) 198 199 // Determine the effective rank of A11. 200 for i := 0; i < min(m, n-l); i++ { 201 if math.Abs(a[i*lda+i]) > tola { 202 k++ 203 } 204 } 205 206 // Update A12 := U^T*A12, where A12 = A[0:m, n-l:n]. 207 impl.Dorm2r(blas.Left, blas.Trans, m, l, min(m, n-l), a, lda, tau, a[n-l:], lda, work) 208 209 if wantu { 210 // Copy the details of U, and form U. 211 impl.Dlaset(blas.All, m, m, 0, 0, u, ldu) 212 if m > 1 { 213 impl.Dlacpy(blas.Lower, m-1, min(m, n-l), a[lda:], lda, u[ldu:], ldu) 214 } 215 impl.Dorg2r(m, m, min(m, n-l), u, ldu, tau, work) 216 } 217 218 if wantq { 219 // Update Q[0:n, 0:n-l] := Q[0:n, 0:n-l]*P1. 220 impl.Dlapmt(forward, n, n-l, q, ldq, iwork[:n-l]) 221 } 222 223 // Clean up A: set the strictly lower triangular part of 224 // A[0:k, 0:k] = 0, and A[k:m, 0:n-l] = 0. 225 for i := 1; i < k; i++ { 226 r := a[i*lda : i*lda+i] 227 for j := range r { 228 r[j] = 0 229 } 230 } 231 if m > k { 232 impl.Dlaset(blas.All, m-k, n-l, 0, 0, a[k*lda:], lda) 233 } 234 235 if n-l > k { 236 // RQ factorization of [ T11 T12 ] = [ 0 T12 ]*Z1. 237 impl.Dgerq2(k, n-l, a, lda, tau, work) 238 239 if wantq { 240 // Update Q[0:n, 0:n-l] := Q[0:n, 0:n-l]*Z1^T. 241 impl.Dorm2r(blas.Right, blas.Trans, n, n-l, k, a, lda, tau, q, ldq, work) 242 } 243 244 // Clean up A. 245 impl.Dlaset(blas.All, k, n-l-k, 0, 0, a, lda) 246 for i := 1; i < k; i++ { 247 r := a[i*lda+n-k-l : i*lda+i+n-k-l] 248 for j := range r { 249 a[j] = 0 250 } 251 } 252 } 253 254 if m > k { 255 // QR factorization of A[k:m, n-l:n]. 256 impl.Dgeqr2(m-k, l, a[k*lda+n-l:], lda, tau, work) 257 if wantu { 258 // Update U[:, k:m) := U[:, k:m]*U1. 259 impl.Dorm2r(blas.Right, blas.NoTrans, m, m-k, min(m-k, l), a[k*lda+n-l:], lda, tau, u[k:], ldu, work) 260 } 261 262 // Clean up A. 263 for i := k + 1; i < m; i++ { 264 r := a[i*lda+n-l : i*lda+min(n-l+i-k, n)] 265 for j := range r { 266 r[j] = 0 267 } 268 } 269 } 270 271 work[0] = float64(lwkopt) 272 return k, l 273 }