gonum.org/v1/gonum@v0.14.0/lapack/gonum/dtgsja.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/blas/blas64" 12 "gonum.org/v1/gonum/lapack" 13 ) 14 15 // Dtgsja computes the generalized singular value decomposition (GSVD) 16 // of two real upper triangular or trapezoidal matrices A and B. 17 // 18 // A and B have the following forms, which may be obtained by the 19 // preprocessing subroutine Dggsvp from a general m×n matrix A and p×n 20 // matrix B: 21 // 22 // n-k-l k l 23 // A = k [ 0 A12 A13 ] if m-k-l >= 0; 24 // l [ 0 0 A23 ] 25 // m-k-l [ 0 0 0 ] 26 // 27 // n-k-l k l 28 // A = k [ 0 A12 A13 ] if m-k-l < 0; 29 // m-k [ 0 0 A23 ] 30 // 31 // n-k-l k l 32 // B = l [ 0 0 B13 ] 33 // p-l [ 0 0 0 ] 34 // 35 // where the k×k matrix A12 and l×l matrix B13 are non-singular 36 // upper triangular. A23 is l×l upper triangular if m-k-l >= 0, 37 // otherwise A23 is (m-k)×l upper trapezoidal. 38 // 39 // On exit, 40 // 41 // Uᵀ*A*Q = D1*[ 0 R ], Vᵀ*B*Q = D2*[ 0 R ], 42 // 43 // where U, V and Q are orthogonal matrices. 44 // R is a non-singular upper triangular matrix, and D1 and D2 are 45 // diagonal matrices, which are of the following structures: 46 // 47 // If m-k-l >= 0, 48 // 49 // k l 50 // D1 = k [ I 0 ] 51 // l [ 0 C ] 52 // m-k-l [ 0 0 ] 53 // 54 // k l 55 // D2 = l [ 0 S ] 56 // p-l [ 0 0 ] 57 // 58 // n-k-l k l 59 // [ 0 R ] = k [ 0 R11 R12 ] k 60 // l [ 0 0 R22 ] l 61 // 62 // where 63 // 64 // C = diag( alpha_k, ... , alpha_{k+l} ), 65 // S = diag( beta_k, ... , beta_{k+l} ), 66 // C^2 + S^2 = I. 67 // 68 // R is stored in 69 // 70 // A[0:k+l, n-k-l:n] 71 // 72 // on exit. 73 // 74 // If m-k-l < 0, 75 // 76 // k m-k k+l-m 77 // D1 = k [ I 0 0 ] 78 // m-k [ 0 C 0 ] 79 // 80 // k m-k k+l-m 81 // D2 = m-k [ 0 S 0 ] 82 // k+l-m [ 0 0 I ] 83 // p-l [ 0 0 0 ] 84 // 85 // n-k-l k m-k k+l-m 86 // [ 0 R ] = k [ 0 R11 R12 R13 ] 87 // m-k [ 0 0 R22 R23 ] 88 // k+l-m [ 0 0 0 R33 ] 89 // 90 // where 91 // 92 // C = diag( alpha_k, ... , alpha_m ), 93 // S = diag( beta_k, ... , beta_m ), 94 // C^2 + S^2 = I. 95 // 96 // R = [ R11 R12 R13 ] is stored in A[0:m, n-k-l:n] 97 // [ 0 R22 R23 ] 98 // 99 // and R33 is stored in 100 // 101 // B[m-k:l, n+m-k-l:n] on exit. 102 // 103 // The computation of the orthogonal transformation matrices U, V or Q 104 // is optional. These matrices may either be formed explicitly, or they 105 // may be post-multiplied into input matrices U1, V1, or Q1. 106 // 107 // Dtgsja essentially uses a variant of Kogbetliantz algorithm to reduce 108 // min(l,m-k)×l triangular or trapezoidal matrix A23 and l×l 109 // matrix B13 to the form: 110 // 111 // U1ᵀ*A13*Q1 = C1*R1; V1ᵀ*B13*Q1 = S1*R1, 112 // 113 // where U1, V1 and Q1 are orthogonal matrices. C1 and S1 are diagonal 114 // matrices satisfying 115 // 116 // C1^2 + S1^2 = I, 117 // 118 // and R1 is an l×l non-singular upper triangular matrix. 119 // 120 // jobU, jobV and jobQ are options for computing the orthogonal matrices. The behavior 121 // is as follows 122 // 123 // jobU == lapack.GSVDU Compute orthogonal matrix U 124 // jobU == lapack.GSVDUnit Use unit-initialized matrix 125 // jobU == lapack.GSVDNone Do not compute orthogonal matrix. 126 // 127 // The behavior is the same for jobV and jobQ with the exception that instead of 128 // lapack.GSVDU these accept lapack.GSVDV and lapack.GSVDQ respectively. 129 // The matrices U, V and Q must be m×m, p×p and n×n respectively unless the 130 // relevant job parameter is lapack.GSVDNone. 131 // 132 // k and l specify the sub-blocks in the input matrices A and B: 133 // 134 // A23 = A[k:min(k+l,m), n-l:n) and B13 = B[0:l, n-l:n] 135 // 136 // of A and B, whose GSVD is going to be computed by Dtgsja. 137 // 138 // tola and tolb are the convergence criteria for the Jacobi-Kogbetliantz 139 // iteration procedure. Generally, they are the same as used in the preprocessing 140 // step, for example, 141 // 142 // tola = max(m, n)*norm(A)*eps, 143 // tolb = max(p, n)*norm(B)*eps, 144 // 145 // where eps is the machine epsilon. 146 // 147 // work must have length at least 2*n, otherwise Dtgsja will panic. 148 // 149 // alpha and beta must have length n or Dtgsja will panic. On exit, alpha and 150 // beta contain the generalized singular value pairs of A and B 151 // 152 // alpha[0:k] = 1, 153 // beta[0:k] = 0, 154 // 155 // if m-k-l >= 0, 156 // 157 // alpha[k:k+l] = diag(C), 158 // beta[k:k+l] = diag(S), 159 // 160 // if m-k-l < 0, 161 // 162 // alpha[k:m]= C, alpha[m:k+l]= 0 163 // beta[k:m] = S, beta[m:k+l] = 1. 164 // 165 // if k+l < n, 166 // 167 // alpha[k+l:n] = 0 and 168 // beta[k+l:n] = 0. 169 // 170 // On exit, A[n-k:n, 0:min(k+l,m)] contains the triangular matrix R or part of R 171 // and if necessary, B[m-k:l, n+m-k-l:n] contains a part of R. 172 // 173 // Dtgsja returns whether the routine converged and the number of iteration cycles 174 // that were run. 175 // 176 // Dtgsja is an internal routine. It is exported for testing purposes. 177 func (impl Implementation) Dtgsja(jobU, jobV, jobQ lapack.GSVDJob, m, p, n, k, l int, a []float64, lda int, b []float64, ldb int, tola, tolb float64, alpha, beta, u []float64, ldu int, v []float64, ldv int, q []float64, ldq int, work []float64) (cycles int, ok bool) { 178 const maxit = 40 179 180 initu := jobU == lapack.GSVDUnit 181 wantu := initu || jobU == lapack.GSVDU 182 183 initv := jobV == lapack.GSVDUnit 184 wantv := initv || jobV == lapack.GSVDV 185 186 initq := jobQ == lapack.GSVDUnit 187 wantq := initq || jobQ == lapack.GSVDQ 188 189 switch { 190 case !initu && !wantu && jobU != lapack.GSVDNone: 191 panic(badGSVDJob + "U") 192 case !initv && !wantv && jobV != lapack.GSVDNone: 193 panic(badGSVDJob + "V") 194 case !initq && !wantq && jobQ != lapack.GSVDNone: 195 panic(badGSVDJob + "Q") 196 case m < 0: 197 panic(mLT0) 198 case p < 0: 199 panic(pLT0) 200 case n < 0: 201 panic(nLT0) 202 203 case lda < max(1, n): 204 panic(badLdA) 205 case len(a) < (m-1)*lda+n: 206 panic(shortA) 207 208 case ldb < max(1, n): 209 panic(badLdB) 210 case len(b) < (p-1)*ldb+n: 211 panic(shortB) 212 213 case len(alpha) != n: 214 panic(badLenAlpha) 215 case len(beta) != n: 216 panic(badLenBeta) 217 218 case ldu < 1, wantu && ldu < m: 219 panic(badLdU) 220 case wantu && len(u) < (m-1)*ldu+m: 221 panic(shortU) 222 223 case ldv < 1, wantv && ldv < p: 224 panic(badLdV) 225 case wantv && len(v) < (p-1)*ldv+p: 226 panic(shortV) 227 228 case ldq < 1, wantq && ldq < n: 229 panic(badLdQ) 230 case wantq && len(q) < (n-1)*ldq+n: 231 panic(shortQ) 232 233 case len(work) < 2*n: 234 panic(shortWork) 235 } 236 237 // Initialize U, V and Q, if necessary 238 if initu { 239 impl.Dlaset(blas.All, m, m, 0, 1, u, ldu) 240 } 241 if initv { 242 impl.Dlaset(blas.All, p, p, 0, 1, v, ldv) 243 } 244 if initq { 245 impl.Dlaset(blas.All, n, n, 0, 1, q, ldq) 246 } 247 248 bi := blas64.Implementation() 249 minTol := math.Min(tola, tolb) 250 251 // Loop until convergence. 252 upper := false 253 for cycles = 1; cycles <= maxit; cycles++ { 254 upper = !upper 255 256 for i := 0; i < l-1; i++ { 257 for j := i + 1; j < l; j++ { 258 var a1, a2, a3 float64 259 if k+i < m { 260 a1 = a[(k+i)*lda+n-l+i] 261 } 262 if k+j < m { 263 a3 = a[(k+j)*lda+n-l+j] 264 } 265 266 b1 := b[i*ldb+n-l+i] 267 b3 := b[j*ldb+n-l+j] 268 269 var b2 float64 270 if upper { 271 if k+i < m { 272 a2 = a[(k+i)*lda+n-l+j] 273 } 274 b2 = b[i*ldb+n-l+j] 275 } else { 276 if k+j < m { 277 a2 = a[(k+j)*lda+n-l+i] 278 } 279 b2 = b[j*ldb+n-l+i] 280 } 281 282 csu, snu, csv, snv, csq, snq := impl.Dlags2(upper, a1, a2, a3, b1, b2, b3) 283 284 // Update (k+i)-th and (k+j)-th rows of matrix A: Uᵀ*A. 285 if k+j < m { 286 bi.Drot(l, a[(k+j)*lda+n-l:], 1, a[(k+i)*lda+n-l:], 1, csu, snu) 287 } 288 289 // Update i-th and j-th rows of matrix B: Vᵀ*B. 290 bi.Drot(l, b[j*ldb+n-l:], 1, b[i*ldb+n-l:], 1, csv, snv) 291 292 // Update (n-l+i)-th and (n-l+j)-th columns of matrices 293 // A and B: A*Q and B*Q. 294 bi.Drot(min(k+l, m), a[n-l+j:], lda, a[n-l+i:], lda, csq, snq) 295 bi.Drot(l, b[n-l+j:], ldb, b[n-l+i:], ldb, csq, snq) 296 297 if upper { 298 if k+i < m { 299 a[(k+i)*lda+n-l+j] = 0 300 } 301 b[i*ldb+n-l+j] = 0 302 } else { 303 if k+j < m { 304 a[(k+j)*lda+n-l+i] = 0 305 } 306 b[j*ldb+n-l+i] = 0 307 } 308 309 // Update orthogonal matrices U, V, Q, if desired. 310 if wantu && k+j < m { 311 bi.Drot(m, u[k+j:], ldu, u[k+i:], ldu, csu, snu) 312 } 313 if wantv { 314 bi.Drot(p, v[j:], ldv, v[i:], ldv, csv, snv) 315 } 316 if wantq { 317 bi.Drot(n, q[n-l+j:], ldq, q[n-l+i:], ldq, csq, snq) 318 } 319 } 320 } 321 322 if !upper { 323 // The matrices A13 and B13 were lower triangular at the start 324 // of the cycle, and are now upper triangular. 325 // 326 // Convergence test: test the parallelism of the corresponding 327 // rows of A and B. 328 var error float64 329 for i := 0; i < min(l, m-k); i++ { 330 bi.Dcopy(l-i, a[(k+i)*lda+n-l+i:], 1, work, 1) 331 bi.Dcopy(l-i, b[i*ldb+n-l+i:], 1, work[l:], 1) 332 ssmin := impl.Dlapll(l-i, work, 1, work[l:], 1) 333 error = math.Max(error, ssmin) 334 } 335 if math.Abs(error) <= minTol { 336 // The algorithm has converged. 337 // Compute the generalized singular value pairs (alpha, beta) 338 // and set the triangular matrix R to array A. 339 for i := 0; i < k; i++ { 340 alpha[i] = 1 341 beta[i] = 0 342 } 343 344 for i := 0; i < min(l, m-k); i++ { 345 a1 := a[(k+i)*lda+n-l+i] 346 b1 := b[i*ldb+n-l+i] 347 gamma := b1 / a1 348 if !math.IsInf(gamma, 0) { 349 // Change sign if necessary. 350 if gamma < 0 { 351 bi.Dscal(l-i, -1, b[i*ldb+n-l+i:], 1) 352 if wantv { 353 bi.Dscal(p, -1, v[i:], ldv) 354 } 355 } 356 beta[k+i], alpha[k+i], _ = impl.Dlartg(math.Abs(gamma), 1) 357 358 if alpha[k+i] >= beta[k+i] { 359 bi.Dscal(l-i, 1/alpha[k+i], a[(k+i)*lda+n-l+i:], 1) 360 } else { 361 bi.Dscal(l-i, 1/beta[k+i], b[i*ldb+n-l+i:], 1) 362 bi.Dcopy(l-i, b[i*ldb+n-l+i:], 1, a[(k+i)*lda+n-l+i:], 1) 363 } 364 } else { 365 alpha[k+i] = 0 366 beta[k+i] = 1 367 bi.Dcopy(l-i, b[i*ldb+n-l+i:], 1, a[(k+i)*lda+n-l+i:], 1) 368 } 369 } 370 371 for i := m; i < k+l; i++ { 372 alpha[i] = 0 373 beta[i] = 1 374 } 375 if k+l < n { 376 for i := k + l; i < n; i++ { 377 alpha[i] = 0 378 beta[i] = 0 379 } 380 } 381 382 return cycles, true 383 } 384 } 385 } 386 387 // The algorithm has not converged after maxit cycles. 388 return cycles, false 389 }