1 // Copyright ©2015 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.
10 "gonum.org/v1/gonum/blas"
11 "gonum.org/v1/gonum/blas/blas64"
12 "gonum.org/v1/gonum/lapack"
15 // Dbdsqr performs a singular value decomposition of a real n×n bidiagonal matrix.
17 // The SVD of the bidiagonal matrix B is
19 // where S is a diagonal matrix of singular values, Q is an orthogonal matrix of
20 // left singular vectors, and P is an orthogonal matrix of right singular vectors.
22 // Q and P are only computed if requested. If left singular vectors are requested,
23 // this routine returns U * Q instead of Q, and if right singular vectors are
24 // requested P^T * VT is returned instead of P^T.
26 // Frequently Dbdsqr is used in conjunction with Dgebrd which reduces a general
27 // matrix A into bidiagonal form. In this case, the SVD of A is
28 // A = (U * Q) * S * (P^T * VT)
30 // This routine may also compute Q^T * C.
32 // d and e contain the elements of the bidiagonal matrix b. d must have length at
33 // least n, and e must have length at least n-1. Dbdsqr will panic if there is
34 // insufficient length. On exit, D contains the singular values of B in decreasing
37 // VT is a matrix of size n×ncvt whose elements are stored in vt. The elements
38 // of vt are modified to contain P^T * VT on exit. VT is not used if ncvt == 0.
40 // U is a matrix of size nru×n whose elements are stored in u. The elements
41 // of u are modified to contain U * Q on exit. U is not used if nru == 0.
43 // C is a matrix of size n×ncc whose elements are stored in c. The elements
44 // of c are modified to contain Q^T * C on exit. C is not used if ncc == 0.
46 // work contains temporary storage and must have length at least 4*n. Dbdsqr
47 // will panic if there is insufficient working memory.
49 // Dbdsqr returns whether the decomposition was successful.
51 // Dbdsqr is an internal routine. It is exported for testing purposes.
52 func (impl Implementation) Dbdsqr(uplo blas.Uplo, n, ncvt, nru, ncc int, d, e, vt []float64, ldvt int, u []float64, ldu int, c []float64, ldc int, work []float64) (ok bool) {
53 if uplo != blas.Upper && uplo != blas.Lower {
57 checkMatrix(n, ncvt, vt, ldvt)
60 checkMatrix(nru, n, u, ldu)
63 checkMatrix(n, ncc, c, ldc)
75 bi := blas64.Implementation()
83 // If the singular vectors do not need to be computed, use qd algorithm.
84 if !(ncvt > 0 || nru > 0 || ncc > 0) {
85 info = impl.Dlasq1(n, d, e, work)
86 // If info is 2 dqds didn't finish, and so try to.
99 lower := uplo == blas.Lower
100 var cs, sn, r float64
102 for i := 0; i < n-1; i++ {
103 cs, sn, r = impl.Dlartg(d[i], e[i])
111 impl.Dlasr(blas.Right, lapack.Variable, lapack.Forward, nru, n, work, work[n-1:], u, ldu)
114 impl.Dlasr(blas.Left, lapack.Variable, lapack.Forward, n, ncc, work, work[n-1:], c, ldc)
117 // Compute singular values to a relative accuracy of tol. If tol is negative
118 // the values will be computed to an absolute accuracy of math.Abs(tol) * norm(b)
119 tolmul := math.Max(10, math.Min(100, math.Pow(eps, -1.0/8)))
122 for i := 0; i < n; i++ {
123 smax = math.Max(smax, math.Abs(d[i]))
125 for i := 0; i < n-1; i++ {
126 smax = math.Max(smax, math.Abs(e[i]))
132 sminoa := math.Abs(d[0])
135 for i := 1; i < n; i++ {
136 mu = math.Abs(d[i]) * (mu / (mu + math.Abs(e[i-1])))
137 sminoa = math.Min(sminoa, mu)
143 sminoa = sminoa / math.Sqrt(float64(n))
144 thresh = math.Max(tol*sminoa, float64(maxIter*n*n)*unfl)
146 thresh = math.Max(math.Abs(tol)*smax, float64(maxIter*n*n)*unfl)
148 // Prepare for the main iteration loop for the singular values.
149 maxIt := maxIter * n * n
153 // m points to the last element of unconverged part of matrix.
160 for i := 0; i < n-1; i++ {
167 // Find diagonal block of matrix to work on.
168 if tol < 0 && math.Abs(d[m-1]) <= thresh {
171 smax = math.Abs(d[m-1])
175 for l3 := 0; l3 < m-1; l3++ {
177 abss := math.Abs(d[l2])
178 abse := math.Abs(e[l2])
179 if tol < 0 && abss <= thresh {
186 smin = math.Min(smin, abss)
187 smax = math.Max(math.Max(smax, abss), abse)
192 // Convergence of bottom singular value, return to top.
200 // e[ll] through e[m-2] are nonzero, e[ll-1] is zero
202 // Handle 2×2 block separately.
203 var sinr, cosr, sinl, cosl float64
204 d[m-1], d[m-2], sinr, cosr, sinl, cosl = impl.Dlasv2(d[m-2], e[m-2], d[m-1])
207 bi.Drot(ncvt, vt[(m-2)*ldvt:], 1, vt[(m-1)*ldvt:], 1, cosr, sinr)
210 bi.Drot(nru, u[m-2:], ldu, u[m-1:], ldu, cosl, sinl)
213 bi.Drot(ncc, c[(m-2)*ldc:], 1, c[(m-1)*ldc:], 1, cosl, sinl)
218 // If working on a new submatrix, choose shift direction from larger end
219 // diagonal element toward smaller.
220 if l2 > oldm-1 || m-1 < oldl2 {
221 if math.Abs(d[l2]) >= math.Abs(d[m-1]) {
227 // Apply convergence tests.
228 // TODO(btracey): There is a lot of similar looking code here. See
229 // if there is a better way to de-duplicate.
231 // Run convergence test in forward direction.
232 // First apply standard test to bottom of matrix.
233 if math.Abs(e[m-2]) <= math.Abs(tol)*math.Abs(d[m-1]) || (tol < 0 && math.Abs(e[m-2]) <= thresh) {
238 // If relative accuracy desired, apply convergence criterion forward.
239 mu := math.Abs(d[l2])
241 for l3 := l2; l3 < m-1; l3++ {
242 if math.Abs(e[l3]) <= tol*mu {
246 mu = math.Abs(d[l3+1]) * (mu / (mu + math.Abs(e[l3])))
247 sminl = math.Min(sminl, mu)
251 // Run convergence test in backward direction.
252 // First apply standard test to top of matrix.
253 if math.Abs(e[l2]) <= math.Abs(tol)*math.Abs(d[l2]) || (tol < 0 && math.Abs(e[l2]) <= thresh) {
258 // If relative accuracy desired, apply convergence criterion backward.
259 mu := math.Abs(d[m-1])
261 for l3 := m - 2; l3 >= l2; l3-- {
262 if math.Abs(e[l3]) <= tol*mu {
266 mu = math.Abs(d[l3]) * (mu / (mu + math.Abs(e[l3])))
267 sminl = math.Min(sminl, mu)
273 // Compute shift. First, test if shifting would ruin relative accuracy,
274 // and if so set the shift to zero.
276 if tol >= 0 && float64(n)*tol*(sminl/smax) <= math.Max(eps, (1.0/100)*tol) {
281 sl2 = math.Abs(d[l2])
282 shift, _ = impl.Dlas2(d[m-2], e[m-2], d[m-1])
284 sl2 = math.Abs(d[m-1])
285 shift, _ = impl.Dlas2(d[l2], e[l2], d[l2+1])
287 // Test if shift is negligible
289 if (shift/sl2)*(shift/sl2) < eps {
295 // If no shift, do simplified QR iteration.
300 var sn, r, oldsn float64
301 for i := l2; i < m-1; i++ {
302 cs, sn, r = impl.Dlartg(d[i]*cs, e[i])
306 oldcs, oldsn, d[i] = impl.Dlartg(oldcs*r, d[i+1]*sn)
309 work[i-l2+nm12] = oldcs
310 work[i-l2+nm13] = oldsn
316 impl.Dlasr(blas.Left, lapack.Variable, lapack.Forward, m-l2, ncvt, work, work[n-1:], vt[l2*ldvt:], ldvt)
319 impl.Dlasr(blas.Right, lapack.Variable, lapack.Forward, nru, m-l2, work[nm12:], work[nm13:], u[l2:], ldu)
322 impl.Dlasr(blas.Left, lapack.Variable, lapack.Forward, m-l2, ncc, work[nm12:], work[nm13:], c[l2*ldc:], ldc)
324 if math.Abs(e[m-2]) < thresh {
330 var sn, r, oldsn float64
331 for i := m - 1; i >= l2+1; i-- {
332 cs, sn, r = impl.Dlartg(d[i]*cs, e[i-1])
336 oldcs, oldsn, d[i] = impl.Dlartg(oldcs*r, d[i-1]*sn)
338 work[i-l2+nm1-1] = -sn
339 work[i-l2+nm12-1] = oldcs
340 work[i-l2+nm13-1] = -oldsn
346 impl.Dlasr(blas.Left, lapack.Variable, lapack.Backward, m-l2, ncvt, work[nm12:], work[nm13:], vt[l2*ldvt:], ldvt)
349 impl.Dlasr(blas.Right, lapack.Variable, lapack.Backward, nru, m-l2, work, work[n-1:], u[l2:], ldu)
352 impl.Dlasr(blas.Left, lapack.Variable, lapack.Backward, m-l2, ncc, work, work[n-1:], c[l2*ldc:], ldc)
354 if math.Abs(e[l2]) <= thresh {
359 // Use nonzero shift.
361 // Chase bulge from top to bottom. Save cosines and sines for
362 // later singular vector updates.
363 f := (math.Abs(d[l2]) - shift) * (math.Copysign(1, d[l2]) + shift/d[l2])
365 var cosl, sinl float64
366 for i := l2; i < m-1; i++ {
367 cosr, sinr, r := impl.Dlartg(f, g)
371 f = cosr*d[i] + sinr*e[i]
372 e[i] = cosr*e[i] - sinr*d[i]
375 cosl, sinl, r = impl.Dlartg(f, g)
377 f = cosl*e[i] + sinl*d[i+1]
378 d[i+1] = cosl*d[i+1] - sinl*e[i]
381 e[i+1] = cosl * e[i+1]
384 work[i-l2+nm1] = sinr
385 work[i-l2+nm12] = cosl
386 work[i-l2+nm13] = sinl
390 impl.Dlasr(blas.Left, lapack.Variable, lapack.Forward, m-l2, ncvt, work, work[n-1:], vt[l2*ldvt:], ldvt)
393 impl.Dlasr(blas.Right, lapack.Variable, lapack.Forward, nru, m-l2, work[nm12:], work[nm13:], u[l2:], ldu)
396 impl.Dlasr(blas.Left, lapack.Variable, lapack.Forward, m-l2, ncc, work[nm12:], work[nm13:], c[l2*ldc:], ldc)
398 if math.Abs(e[m-2]) <= thresh {
402 // Chase bulge from top to bottom. Save cosines and sines for
403 // later singular vector updates.
404 f := (math.Abs(d[m-1]) - shift) * (math.Copysign(1, d[m-1]) + shift/d[m-1])
406 for i := m - 1; i > l2; i-- {
407 cosr, sinr, r := impl.Dlartg(f, g)
411 f = cosr*d[i] + sinr*e[i-1]
412 e[i-1] = cosr*e[i-1] - sinr*d[i]
415 cosl, sinl, r := impl.Dlartg(f, g)
417 f = cosl*e[i-1] + sinl*d[i-1]
418 d[i-1] = cosl*d[i-1] - sinl*e[i-1]
424 work[i-l2+nm1-1] = -sinr
425 work[i-l2+nm12-1] = cosl
426 work[i-l2+nm13-1] = -sinl
429 if math.Abs(e[l2]) <= thresh {
433 impl.Dlasr(blas.Left, lapack.Variable, lapack.Backward, m-l2, ncvt, work[nm12:], work[nm13:], vt[l2*ldvt:], ldvt)
436 impl.Dlasr(blas.Right, lapack.Variable, lapack.Backward, nru, m-l2, work, work[n-1:], u[l2:], ldu)
439 impl.Dlasr(blas.Left, lapack.Variable, lapack.Backward, m-l2, ncc, work, work[n-1:], c[l2*ldc:], ldc)
446 // All singular values converged, make them positive.
447 for i := 0; i < n; i++ {
451 bi.Dscal(ncvt, -1, vt[i*ldvt:], 1)
456 // Sort the singular values in decreasing order.
457 for i := 0; i < n-1; i++ {
460 for j := 1; j < n-i; j++ {
467 // Swap singular values and vectors.
471 bi.Dswap(ncvt, vt[isub*ldvt:], 1, vt[(n-i-1)*ldvt:], 1)
474 bi.Dswap(nru, u[isub:], ldu, u[n-i-1:], ldu)
477 bi.Dswap(ncc, c[isub*ldc:], 1, c[(n-i-1)*ldc:], 1)
482 for i := 0; i < n-1; i++ {