github.com/gonum/matrix@v0.0.0-20181209220409-c518dec07be9/mat64/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 mat64 6 7 import ( 8 "github.com/gonum/blas/blas64" 9 "github.com/gonum/floats" 10 "github.com/gonum/lapack" 11 "github.com/gonum/lapack/lapack64" 12 "github.com/gonum/matrix" 13 ) 14 15 // GSVD is a type for creating and using the Generalized Singular Value Decomposition 16 // (GSVD) of a matrix. 17 // 18 // The factorization is a linear transformation of the data sets from the given 19 // variable×sample spaces to reduced and diagonalized "eigenvariable"×"eigensample" 20 // spaces. 21 type GSVD struct { 22 kind matrix.GSVDKind 23 24 r, p, c, k, l int 25 s1, s2 []float64 26 a, b, u, v, q blas64.General 27 28 work []float64 29 iwork []int 30 } 31 32 // Factorize computes the generalized singular value decomposition (GSVD) of the input 33 // the r×c matrix A and the p×c matrix B. The singular values of A and B are computed 34 // in all cases, while the singular vectors are optionally computed depending on the 35 // input kind. 36 // 37 // The full singular value decomposition (kind == GSVDU|GSVDV|GSVDQ) deconstructs A and B as 38 // A = U * Σ₁ * [ 0 R ] * Q^T 39 // 40 // B = V * Σ₂ * [ 0 R ] * Q^T 41 // where Σ₁ and Σ₂ are r×(k+l) and p×(k+l) diagonal matrices of singular values, and 42 // U, V and Q are r×r, p×p and c×c orthogonal matrices of singular vectors. k+l is the 43 // effective numerical rank of the matrix [ A^T B^T ]^T. 44 // 45 // It is frequently not necessary to compute the full GSVD. Computation time and 46 // storage costs can be reduced using the appropriate kind. Either only the singular 47 // values can be computed (kind == SVDNone), or in conjunction with specific singular 48 // vectors (kind bit set according to matrix.GSVDU, matrix.GSVDV and matrix.GSVDQ). 49 // 50 // Factorize returns whether the decomposition succeeded. If the decomposition 51 // failed, routines that require a successful factorization will panic. 52 func (gsvd *GSVD) Factorize(a, b Matrix, kind matrix.GSVDKind) (ok bool) { 53 r, c := a.Dims() 54 gsvd.r, gsvd.c = r, c 55 p, c := b.Dims() 56 gsvd.p = p 57 if gsvd.c != c { 58 panic(matrix.ErrShape) 59 } 60 var jobU, jobV, jobQ lapack.GSVDJob 61 switch { 62 default: 63 panic("gsvd: bad input kind") 64 case kind == matrix.GSVDNone: 65 jobU = lapack.GSVDNone 66 jobV = lapack.GSVDNone 67 jobQ = lapack.GSVDNone 68 case (matrix.GSVDU|matrix.GSVDV|matrix.GSVDQ)&kind != 0: 69 if matrix.GSVDU&kind != 0 { 70 jobU = lapack.GSVDU 71 gsvd.u = blas64.General{ 72 Rows: r, 73 Cols: r, 74 Stride: r, 75 Data: use(gsvd.u.Data, r*r), 76 } 77 } 78 if matrix.GSVDV&kind != 0 { 79 jobV = lapack.GSVDV 80 gsvd.v = blas64.General{ 81 Rows: p, 82 Cols: p, 83 Stride: p, 84 Data: use(gsvd.v.Data, p*p), 85 } 86 } 87 if matrix.GSVDQ&kind != 0 { 88 jobQ = lapack.GSVDQ 89 gsvd.q = blas64.General{ 90 Rows: c, 91 Cols: c, 92 Stride: c, 93 Data: use(gsvd.q.Data, c*c), 94 } 95 } 96 } 97 98 // A and B are destroyed on call, so copy the matrices. 99 aCopy := DenseCopyOf(a) 100 bCopy := DenseCopyOf(b) 101 102 gsvd.s1 = use(gsvd.s1, c) 103 gsvd.s2 = use(gsvd.s2, c) 104 105 gsvd.iwork = useInt(gsvd.iwork, c) 106 107 gsvd.work = use(gsvd.work, 1) 108 lapack64.Ggsvd3(jobU, jobV, jobQ, aCopy.mat, bCopy.mat, gsvd.s1, gsvd.s2, gsvd.u, gsvd.v, gsvd.q, gsvd.work, -1, gsvd.iwork) 109 gsvd.work = use(gsvd.work, int(gsvd.work[0])) 110 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) 111 if ok { 112 gsvd.a = aCopy.mat 113 gsvd.b = bCopy.mat 114 gsvd.kind = kind 115 } 116 return ok 117 } 118 119 // Kind returns the matrix.GSVDKind of the decomposition. If no decomposition has been 120 // computed, Kind returns 0. 121 func (gsvd *GSVD) Kind() matrix.GSVDKind { 122 return gsvd.kind 123 } 124 125 // Rank returns the k and l terms of the rank of [ A^T B^T ]^T. 126 func (gsvd *GSVD) Rank() (k, l int) { 127 return gsvd.k, gsvd.l 128 } 129 130 // GeneralizedValues returns the generalized singular values of the factorized matrices. 131 // If the input slice is non-nil, the values will be stored in-place into the slice. 132 // In this case, the slice must have length min(r,c)-k, and GeneralizedValues will 133 // panic with matrix.ErrSliceLengthMismatch otherwise. If the input slice is nil, 134 // a new slice of the appropriate length will be allocated and returned. 135 // 136 // GeneralizedValues will panic if the receiver does not contain a successful factorization. 137 func (gsvd *GSVD) GeneralizedValues(v []float64) []float64 { 138 if gsvd.kind == 0 { 139 panic("gsvd: no decomposition computed") 140 } 141 r := gsvd.r 142 c := gsvd.c 143 k := gsvd.k 144 d := min(r, c) 145 if v == nil { 146 v = make([]float64, d-k) 147 } 148 if len(v) != d-k { 149 panic(matrix.ErrSliceLengthMismatch) 150 } 151 floats.DivTo(v, gsvd.s1[k:d], gsvd.s2[k:d]) 152 return v 153 } 154 155 // ValuesA returns the singular values of the factorized A matrix. 156 // If the input slice is non-nil, the values will be stored in-place into the slice. 157 // In this case, the slice must have length min(r,c)-k, and ValuesA will panic with 158 // matrix.ErrSliceLengthMismatch otherwise. If the input slice is nil, 159 // a new slice of the appropriate length will be allocated and returned. 160 // 161 // ValuesA will panic if the receiver does not contain a successful factorization. 162 func (gsvd *GSVD) ValuesA(s []float64) []float64 { 163 if gsvd.kind == 0 { 164 panic("gsvd: no decomposition computed") 165 } 166 r := gsvd.r 167 c := gsvd.c 168 k := gsvd.k 169 d := min(r, c) 170 if s == nil { 171 s = make([]float64, d-k) 172 } 173 if len(s) != d-k { 174 panic(matrix.ErrSliceLengthMismatch) 175 } 176 copy(s, gsvd.s1[k:min(r, c)]) 177 return s 178 } 179 180 // ValuesB returns the singular values of the factorized B matrix. 181 // If the input slice is non-nil, the values will be stored in-place into the slice. 182 // In this case, the slice must have length min(r,c)-k, and ValuesB will panic with 183 // matrix.ErrSliceLengthMismatch otherwise. If the input slice is nil, 184 // a new slice of the appropriate length will be allocated and returned. 185 // 186 // ValuesB will panic if the receiver does not contain a successful factorization. 187 func (gsvd *GSVD) ValuesB(s []float64) []float64 { 188 if gsvd.kind == 0 { 189 panic("gsvd: no decomposition computed") 190 } 191 r := gsvd.r 192 c := gsvd.c 193 k := gsvd.k 194 d := min(r, c) 195 if s == nil { 196 s = make([]float64, d-k) 197 } 198 if len(s) != d-k { 199 panic(matrix.ErrSliceLengthMismatch) 200 } 201 copy(s, gsvd.s2[k:d]) 202 return s 203 } 204 205 // ZeroRFromGSVD extracts the matrix [ 0 R ] from the singular value decomposition, storing 206 // the result in-place into the receiver. [ 0 R ] is size (k+l)×c. 207 func (m *Dense) ZeroRFromGSVD(gsvd *GSVD) { 208 if gsvd.kind == 0 { 209 panic("gsvd: no decomposition computed") 210 } 211 r := gsvd.r 212 c := gsvd.c 213 k := gsvd.k 214 l := gsvd.l 215 h := min(k+l, r) 216 m.reuseAsZeroed(k+l, c) 217 a := Dense{ 218 mat: gsvd.a, 219 capRows: r, 220 capCols: c, 221 } 222 m.Slice(0, h, c-k-l, c).(*Dense). 223 Copy(a.Slice(0, h, c-k-l, c)) 224 if r < k+l { 225 b := Dense{ 226 mat: gsvd.b, 227 capRows: gsvd.p, 228 capCols: c, 229 } 230 m.Slice(r, k+l, c+r-k-l, c).(*Dense). 231 Copy(b.Slice(r-k, l, c+r-k-l, c)) 232 } 233 } 234 235 // SigmaAFromGSVD extracts the matrix Σ₁ from the singular value decomposition, storing 236 // the result in-place into the receiver. Σ₁ is size r×(k+l). 237 func (m *Dense) SigmaAFromGSVD(gsvd *GSVD) { 238 if gsvd.kind == 0 { 239 panic("gsvd: no decomposition computed") 240 } 241 r := gsvd.r 242 k := gsvd.k 243 l := gsvd.l 244 m.reuseAsZeroed(r, k+l) 245 for i := 0; i < k; i++ { 246 m.set(i, i, 1) 247 } 248 for i := k; i < min(r, k+l); i++ { 249 m.set(i, i, gsvd.s1[i]) 250 } 251 } 252 253 // SigmaBFromGSVD extracts the matrix Σ₂ from the singular value decomposition, storing 254 // the result in-place into the receiver. Σ₂ is size p×(k+l). 255 func (m *Dense) SigmaBFromGSVD(gsvd *GSVD) { 256 if gsvd.kind == 0 { 257 panic("gsvd: no decomposition computed") 258 } 259 r := gsvd.r 260 p := gsvd.p 261 k := gsvd.k 262 l := gsvd.l 263 m.reuseAsZeroed(p, k+l) 264 for i := 0; i < min(l, r-k); i++ { 265 m.set(i, i+k, gsvd.s2[k+i]) 266 } 267 for i := r - k; i < l; i++ { 268 m.set(i, i+k, 1) 269 } 270 } 271 272 // UFromGSVD extracts the matrix U from the singular value decomposition, storing 273 // the result in-place into the receiver. U is size r×r. 274 func (m *Dense) UFromGSVD(gsvd *GSVD) { 275 if gsvd.kind&matrix.GSVDU == 0 { 276 panic("mat64: improper GSVD kind") 277 } 278 r := gsvd.u.Rows 279 c := gsvd.u.Cols 280 m.reuseAs(r, c) 281 282 tmp := &Dense{ 283 mat: gsvd.u, 284 capRows: r, 285 capCols: c, 286 } 287 m.Copy(tmp) 288 } 289 290 // VFromGSVD extracts the matrix V from the singular value decomposition, storing 291 // the result in-place into the receiver. V is size p×p. 292 func (m *Dense) VFromGSVD(gsvd *GSVD) { 293 if gsvd.kind&matrix.GSVDV == 0 { 294 panic("mat64: improper GSVD kind") 295 } 296 r := gsvd.v.Rows 297 c := gsvd.v.Cols 298 m.reuseAs(r, c) 299 300 tmp := &Dense{ 301 mat: gsvd.v, 302 capRows: r, 303 capCols: c, 304 } 305 m.Copy(tmp) 306 } 307 308 // QFromGSVD extracts the matrix Q from the singular value decomposition, storing 309 // the result in-place into the receiver. Q is size c×c. 310 func (m *Dense) QFromGSVD(gsvd *GSVD) { 311 if gsvd.kind&matrix.GSVDQ == 0 { 312 panic("mat64: improper GSVD kind") 313 } 314 r := gsvd.q.Rows 315 c := gsvd.q.Cols 316 m.reuseAs(r, c) 317 318 tmp := &Dense{ 319 mat: gsvd.q, 320 capRows: r, 321 capCols: c, 322 } 323 m.Copy(tmp) 324 }