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