github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/mat/gsvd.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 mat 6 7 import ( 8 "github.com/jingcheng-WU/gonum/blas/blas64" 9 "github.com/jingcheng-WU/gonum/floats" 10 "github.com/jingcheng-WU/gonum/lapack" 11 "github.com/jingcheng-WU/gonum/lapack/lapack64" 12 ) 13 14 // GSVDKind specifies the treatment of singular vectors during a GSVD 15 // factorization. 16 type GSVDKind int 17 18 const ( 19 // GSVDNone specifies that no singular vectors should be computed during 20 // the decomposition. 21 GSVDNone GSVDKind = 0 22 23 // GSVDU specifies that the U singular vectors should be computed during 24 // the decomposition. 25 GSVDU GSVDKind = 1 << iota 26 // GSVDV specifies that the V singular vectors should be computed during 27 // the decomposition. 28 GSVDV 29 // GSVDQ specifies that the Q singular vectors should be computed during 30 // the decomposition. 31 GSVDQ 32 33 // GSVDAll is a convenience value for computing all of the singular vectors. 34 GSVDAll = GSVDU | GSVDV | GSVDQ 35 ) 36 37 // GSVD is a type for creating and using the Generalized Singular Value Decomposition 38 // (GSVD) of a matrix. 39 // 40 // The factorization is a linear transformation of the data sets from the given 41 // variable×sample spaces to reduced and diagonalized "eigenvariable"×"eigensample" 42 // spaces. 43 type GSVD struct { 44 kind GSVDKind 45 46 r, p, c, k, l int 47 s1, s2 []float64 48 a, b, u, v, q blas64.General 49 50 work []float64 51 iwork []int 52 } 53 54 // succFact returns whether the receiver contains a successful factorization. 55 func (gsvd *GSVD) succFact() bool { 56 return gsvd.r != 0 57 } 58 59 // Factorize computes the generalized singular value decomposition (GSVD) of the input 60 // the r×c matrix A and the p×c matrix B. The singular values of A and B are computed 61 // in all cases, while the singular vectors are optionally computed depending on the 62 // input kind. 63 // 64 // The full singular value decomposition (kind == GSVDAll) deconstructs A and B as 65 // A = U * Σ₁ * [ 0 R ] * Qᵀ 66 // 67 // B = V * Σ₂ * [ 0 R ] * Qᵀ 68 // where Σ₁ and Σ₂ are r×(k+l) and p×(k+l) diagonal matrices of singular values, and 69 // U, V and Q are r×r, p×p and c×c orthogonal matrices of singular vectors. k+l is the 70 // effective numerical rank of the matrix [ Aᵀ Bᵀ ]ᵀ. 71 // 72 // It is frequently not necessary to compute the full GSVD. Computation time and 73 // storage costs can be reduced using the appropriate kind. Either only the singular 74 // values can be computed (kind == SVDNone), or in conjunction with specific singular 75 // vectors (kind bit set according to matrix.GSVDU, matrix.GSVDV and matrix.GSVDQ). 76 // 77 // Factorize returns whether the decomposition succeeded. If the decomposition 78 // failed, routines that require a successful factorization will panic. 79 func (gsvd *GSVD) Factorize(a, b Matrix, kind GSVDKind) (ok bool) { 80 // kill the previous decomposition 81 gsvd.r = 0 82 gsvd.kind = 0 83 84 r, c := a.Dims() 85 gsvd.r, gsvd.c = r, c 86 p, c := b.Dims() 87 gsvd.p = p 88 if gsvd.c != c { 89 panic(ErrShape) 90 } 91 var jobU, jobV, jobQ lapack.GSVDJob 92 switch { 93 default: 94 panic("gsvd: bad input kind") 95 case kind == GSVDNone: 96 jobU = lapack.GSVDNone 97 jobV = lapack.GSVDNone 98 jobQ = lapack.GSVDNone 99 case GSVDAll&kind != 0: 100 if GSVDU&kind != 0 { 101 jobU = lapack.GSVDU 102 gsvd.u = blas64.General{ 103 Rows: r, 104 Cols: r, 105 Stride: r, 106 Data: use(gsvd.u.Data, r*r), 107 } 108 } 109 if GSVDV&kind != 0 { 110 jobV = lapack.GSVDV 111 gsvd.v = blas64.General{ 112 Rows: p, 113 Cols: p, 114 Stride: p, 115 Data: use(gsvd.v.Data, p*p), 116 } 117 } 118 if GSVDQ&kind != 0 { 119 jobQ = lapack.GSVDQ 120 gsvd.q = blas64.General{ 121 Rows: c, 122 Cols: c, 123 Stride: c, 124 Data: use(gsvd.q.Data, c*c), 125 } 126 } 127 } 128 129 // A and B are destroyed on call, so copy the matrices. 130 aCopy := DenseCopyOf(a) 131 bCopy := DenseCopyOf(b) 132 133 gsvd.s1 = use(gsvd.s1, c) 134 gsvd.s2 = use(gsvd.s2, c) 135 136 gsvd.iwork = useInt(gsvd.iwork, c) 137 138 gsvd.work = use(gsvd.work, 1) 139 lapack64.Ggsvd3(jobU, jobV, jobQ, aCopy.mat, bCopy.mat, gsvd.s1, gsvd.s2, gsvd.u, gsvd.v, gsvd.q, gsvd.work, -1, gsvd.iwork) 140 gsvd.work = use(gsvd.work, int(gsvd.work[0])) 141 gsvd.k, gsvd.l, ok = lapack64.Ggsvd3(jobU, jobV, jobQ, aCopy.mat, bCopy.mat, gsvd.s1, gsvd.s2, gsvd.u, gsvd.v, gsvd.q, gsvd.work, len(gsvd.work), gsvd.iwork) 142 if ok { 143 gsvd.a = aCopy.mat 144 gsvd.b = bCopy.mat 145 gsvd.kind = kind 146 } 147 return ok 148 } 149 150 // Kind returns the GSVDKind of the decomposition. If no decomposition has been 151 // computed, Kind returns -1. 152 func (gsvd *GSVD) Kind() GSVDKind { 153 if !gsvd.succFact() { 154 return -1 155 } 156 return gsvd.kind 157 } 158 159 // Rank returns the k and l terms of the rank of [ Aᵀ Bᵀ ]ᵀ. 160 func (gsvd *GSVD) Rank() (k, l int) { 161 return gsvd.k, gsvd.l 162 } 163 164 // GeneralizedValues returns the generalized singular values of the factorized matrices. 165 // If the input slice is non-nil, the values will be stored in-place into the slice. 166 // In this case, the slice must have length min(r,c)-k, and GeneralizedValues will 167 // panic with matrix.ErrSliceLengthMismatch otherwise. If the input slice is nil, 168 // a new slice of the appropriate length will be allocated and returned. 169 // 170 // GeneralizedValues will panic if the receiver does not contain a successful factorization. 171 func (gsvd *GSVD) GeneralizedValues(v []float64) []float64 { 172 if !gsvd.succFact() { 173 panic(badFact) 174 } 175 r := gsvd.r 176 c := gsvd.c 177 k := gsvd.k 178 d := min(r, c) 179 if v == nil { 180 v = make([]float64, d-k) 181 } 182 if len(v) != d-k { 183 panic(ErrSliceLengthMismatch) 184 } 185 floats.DivTo(v, gsvd.s1[k:d], gsvd.s2[k:d]) 186 return v 187 } 188 189 // ValuesA returns the singular values of the factorized A matrix. 190 // If the input slice is non-nil, the values will be stored in-place into the slice. 191 // In this case, the slice must have length min(r,c)-k, and ValuesA will panic with 192 // matrix.ErrSliceLengthMismatch otherwise. If the input slice is nil, 193 // a new slice of the appropriate length will be allocated and returned. 194 // 195 // ValuesA will panic if the receiver does not contain a successful factorization. 196 func (gsvd *GSVD) ValuesA(s []float64) []float64 { 197 if !gsvd.succFact() { 198 panic(badFact) 199 } 200 r := gsvd.r 201 c := gsvd.c 202 k := gsvd.k 203 d := min(r, c) 204 if s == nil { 205 s = make([]float64, d-k) 206 } 207 if len(s) != d-k { 208 panic(ErrSliceLengthMismatch) 209 } 210 copy(s, gsvd.s1[k:min(r, c)]) 211 return s 212 } 213 214 // ValuesB returns the singular values of the factorized B matrix. 215 // If the input slice is non-nil, the values will be stored in-place into the slice. 216 // In this case, the slice must have length min(r,c)-k, and ValuesB will panic with 217 // matrix.ErrSliceLengthMismatch otherwise. If the input slice is nil, 218 // a new slice of the appropriate length will be allocated and returned. 219 // 220 // ValuesB will panic if the receiver does not contain a successful factorization. 221 func (gsvd *GSVD) ValuesB(s []float64) []float64 { 222 if !gsvd.succFact() { 223 panic(badFact) 224 } 225 r := gsvd.r 226 c := gsvd.c 227 k := gsvd.k 228 d := min(r, c) 229 if s == nil { 230 s = make([]float64, d-k) 231 } 232 if len(s) != d-k { 233 panic(ErrSliceLengthMismatch) 234 } 235 copy(s, gsvd.s2[k:d]) 236 return s 237 } 238 239 // ZeroRTo extracts the matrix [ 0 R ] from the singular value decomposition, 240 // storing the result into dst. [ 0 R ] is of size (k+l)×c. 241 // 242 // If dst is empty, ZeroRTo will resize dst to be (k+l)×c. When dst is 243 // non-empty, ZeroRTo will panic if dst is not (k+l)×c. ZeroRTo will also panic 244 // if the receiver does not contain a successful factorization. 245 func (gsvd *GSVD) ZeroRTo(dst *Dense) { 246 if !gsvd.succFact() { 247 panic(badFact) 248 } 249 r := gsvd.r 250 c := gsvd.c 251 k := gsvd.k 252 l := gsvd.l 253 h := min(k+l, r) 254 if dst.IsEmpty() { 255 dst.ReuseAs(k+l, c) 256 } else { 257 r2, c2 := dst.Dims() 258 if r2 != k+l || c != c2 { 259 panic(ErrShape) 260 } 261 dst.Zero() 262 } 263 a := Dense{ 264 mat: gsvd.a, 265 capRows: r, 266 capCols: c, 267 } 268 dst.slice(0, h, c-k-l, c).Copy(a.Slice(0, h, c-k-l, c)) 269 if r < k+l { 270 b := Dense{ 271 mat: gsvd.b, 272 capRows: gsvd.p, 273 capCols: c, 274 } 275 dst.slice(r, k+l, c+r-k-l, c).Copy(b.Slice(r-k, l, c+r-k-l, c)) 276 } 277 } 278 279 // SigmaATo extracts the matrix Σ₁ from the singular value decomposition, storing 280 // the result into dst. Σ₁ is size r×(k+l). 281 // 282 // If dst is empty, SigmaATo will resize dst to be r×(k+l). When dst is 283 // non-empty, SigmATo will panic if dst is not r×(k+l). SigmaATo will also 284 // panic if the receiver does not contain a successful factorization. 285 func (gsvd *GSVD) SigmaATo(dst *Dense) { 286 if !gsvd.succFact() { 287 panic(badFact) 288 } 289 r := gsvd.r 290 k := gsvd.k 291 l := gsvd.l 292 if dst.IsEmpty() { 293 dst.ReuseAs(r, k+l) 294 } else { 295 r2, c := dst.Dims() 296 if r2 != r || c != k+l { 297 panic(ErrShape) 298 } 299 dst.Zero() 300 } 301 for i := 0; i < k; i++ { 302 dst.set(i, i, 1) 303 } 304 for i := k; i < min(r, k+l); i++ { 305 dst.set(i, i, gsvd.s1[i]) 306 } 307 } 308 309 // SigmaBTo extracts the matrix Σ₂ from the singular value decomposition, storing 310 // the result into dst. Σ₂ is size p×(k+l). 311 // 312 // If dst is empty, SigmaBTo will resize dst to be p×(k+l). When dst is 313 // non-empty, SigmBTo will panic if dst is not p×(k+l). SigmaBTo will also 314 // panic if the receiver does not contain a successful factorization. 315 func (gsvd *GSVD) SigmaBTo(dst *Dense) { 316 if !gsvd.succFact() { 317 panic(badFact) 318 } 319 r := gsvd.r 320 p := gsvd.p 321 k := gsvd.k 322 l := gsvd.l 323 if dst.IsEmpty() { 324 dst.ReuseAs(p, k+l) 325 } else { 326 r, c := dst.Dims() 327 if r != p || c != k+l { 328 panic(ErrShape) 329 } 330 dst.Zero() 331 } 332 for i := 0; i < min(l, r-k); i++ { 333 dst.set(i, i+k, gsvd.s2[k+i]) 334 } 335 for i := r - k; i < l; i++ { 336 dst.set(i, i+k, 1) 337 } 338 } 339 340 // UTo extracts the matrix U from the singular value decomposition, storing 341 // the result into dst. U is size r×r. 342 // 343 // If dst is empty, UTo will resize dst to be r×r. When dst is 344 // non-empty, UTo will panic if dst is not r×r. UTo will also 345 // panic if the receiver does not contain a successful factorization. 346 func (gsvd *GSVD) UTo(dst *Dense) { 347 if !gsvd.succFact() { 348 panic(badFact) 349 } 350 if gsvd.kind&GSVDU == 0 { 351 panic("mat: improper GSVD kind") 352 } 353 r := gsvd.u.Rows 354 c := gsvd.u.Cols 355 if dst.IsEmpty() { 356 dst.ReuseAs(r, c) 357 } else { 358 r2, c2 := dst.Dims() 359 if r != r2 || c != c2 { 360 panic(ErrShape) 361 } 362 } 363 364 tmp := &Dense{ 365 mat: gsvd.u, 366 capRows: r, 367 capCols: c, 368 } 369 dst.Copy(tmp) 370 } 371 372 // VTo extracts the matrix V from the singular value decomposition, storing 373 // the result into dst. V is size p×p. 374 // 375 // If dst is empty, VTo will resize dst to be p×p. When dst is 376 // non-empty, VTo will panic if dst is not p×p. VTo will also 377 // panic if the receiver does not contain a successful factorization. 378 func (gsvd *GSVD) VTo(dst *Dense) { 379 if !gsvd.succFact() { 380 panic(badFact) 381 } 382 if gsvd.kind&GSVDV == 0 { 383 panic("mat: improper GSVD kind") 384 } 385 r := gsvd.v.Rows 386 c := gsvd.v.Cols 387 if dst.IsEmpty() { 388 dst.ReuseAs(r, c) 389 } else { 390 r2, c2 := dst.Dims() 391 if r != r2 || c != c2 { 392 panic(ErrShape) 393 } 394 } 395 396 tmp := &Dense{ 397 mat: gsvd.v, 398 capRows: r, 399 capCols: c, 400 } 401 dst.Copy(tmp) 402 } 403 404 // QTo extracts the matrix Q from the singular value decomposition, storing 405 // the result into dst. Q is size c×c. 406 // 407 // If dst is empty, QTo will resize dst to be c×c. When dst is 408 // non-empty, QTo will panic if dst is not c×c. QTo will also 409 // panic if the receiver does not contain a successful factorization. 410 func (gsvd *GSVD) QTo(dst *Dense) { 411 if !gsvd.succFact() { 412 panic(badFact) 413 } 414 if gsvd.kind&GSVDQ == 0 { 415 panic("mat: improper GSVD kind") 416 } 417 r := gsvd.q.Rows 418 c := gsvd.q.Cols 419 if dst.IsEmpty() { 420 dst.ReuseAs(r, c) 421 } else { 422 r2, c2 := dst.Dims() 423 if r != r2 || c != c2 { 424 panic(ErrShape) 425 } 426 } 427 428 tmp := &Dense{ 429 mat: gsvd.q, 430 capRows: r, 431 capCols: c, 432 } 433 dst.Copy(tmp) 434 }