@@ -102,9 +102,28 @@ function svdDemmelKahan!(B::Bidiagonal{T}, n1, n2, U = nothing, Vᴴ = nothing)
102102 return B
103103end
104104
105+ # Recurrence to estimate smallest singular value from LAWN3 Lemma 1
106+ function estimate_σ⁻ (dv:: AbstractVector , ev:: AbstractVector , n1:: Integer , n2:: Integer )
107+ λ = abs (dv[n2])
108+ B∞ = λ
109+ for j in (n2 - 1 ): - 1 : n1
110+ λ = abs (dv[j])* (λ/ (λ + abs (ev[j])))
111+ B∞ = min (B∞, λ)
112+ end
113+
114+ μ = abs (dv[n1])
115+ B1 = μ
116+ for j in n1: (n2 - 1 )
117+ μ = abs (dv[j + 1 ])* (μ* (μ + abs (ev[j])))
118+ B1 = min (B1, μ)
119+ end
120+
121+ return min (B∞, B1)
122+ end
123+
105124# The actual SVD solver routine
106125# Notice that this routine doesn't adjust the sign and sorts the values
107- function __svd! (B:: Bidiagonal{T} , U = nothing , Vᴴ = nothing ; tol = eps (T), debug = false ) where T<: Real
126+ function __svd! (B:: Bidiagonal{T} , U = nothing , Vᴴ = nothing ; tol = 100 eps (T), debug = false ) where T<: Real
108127
109128 n = size (B, 1 )
110129 n1 = 1
@@ -113,22 +132,21 @@ function __svd!(B::Bidiagonal{T}, U = nothing, Vᴴ = nothing; tol = eps(T), deb
113132 e = B. ev
114133 count = 0
115134
135+ # See LAWN3 page 6 and 22
136+ σ⁻ = estimate_σ⁻ (d, e, n1, n2)
137+ fudge = n
138+ thresh = tol* σ⁻
139+
116140 if istriu (B)
117141 while true
118142
119143 # Search for biggest index for non-zero off diagonal value in e
120144 for n2i = n2: - 1 : 1
121145 if n2i == 1
122146 # We are done!
123-
124147 return nothing
125148 else
126- tolcritTop = tol * abs (d[n2i - 1 ] * d[n2i])
127-
128- # debug && println("n2i=", n2i, ", d[n2i-1]=", d[n2i-1], ", d[n2i]=", d[n2i],
129- # ", e[n2i-1]=", e[n2i-1], ", tolcritTop=", tolcritTop)
130-
131- if abs (e[n2i - 1 ]) > tolcritTop
149+ if abs (e[n2i - 1 ]) > thresh
132150 n2 = n2i
133151 break
134152 end
@@ -137,40 +155,45 @@ function __svd!(B::Bidiagonal{T}, U = nothing, Vᴴ = nothing; tol = eps(T), deb
137155
138156 # Search for largest sub-bidiagonal matrix ending at n2
139157 for _n1 = (n2 - 1 ): - 1 : 1
140-
141- if _n1 == 1
158+ if abs (e[_n1]) < thresh
159+ n1 = _n1
160+ break
161+ elseif _n1 == 1
142162 n1 = _n1
143163 break
144- else
145- tolcritBottom = tol * abs (d[_n1 - 1 ] * d[_n1])
146-
147- # debug && println("n1=", n1, ", d[n1]=", d[n1], ", d[n1-1]=", d[n1-1], ", e[n1-1]", e[n1-1],
148- # ", tolcritBottom=", tolcritBottom)
149-
150- if abs (e[_n1 - 1 ]) < tolcritBottom
151- n1 = _n1
152- break
153- end
154164 end
155165 end
156166
157- debug && println (" n1=" , n1, " , n2=" , n2, " , d[n1]=" , d[n1], " , d[n2]=" , d[n2], " , e[n1]=" , e[n1])
167+ debug && println (" n1=" , n1, " , n2=" , n2, " , d[n1]=" , d[n1], " , d[n2]=" , d[n2], " , e[n1]=" , e[n1], " e[n2]= " , e[n2 - 1 ], " thresh= " , thresh )
158168
159- # FixMe! Calling the zero shifted version only the first time is adhoc but seems
160- # to work well. Reexamine analysis in LAWN 3 to improve this later.
161- if count == 0
169+
170+ # See LAWN 3 p 18 and
171+ # We approximate the smallest and largest singular values of the
172+ # current block to determine if it's safe to use shift or if
173+ # the zero shift algorithm is required to maintain high relative
174+ # accuracy
175+ σ⁻ = estimate_σ⁻ (d, e, n1, n2)
176+ σ⁺ = max (maximum (view (d, n1: n2)), maximum (view (e, n1: (n2 - 1 ))))
177+
178+ if fudge* tol* σ⁻ <= eps (σ⁺)
162179 svdDemmelKahan! (B, n1, n2, U, Vᴴ)
163180 else
164181 shift = svdvals2x2 (d[n2 - 1 ], d[n2], e[n2 - 1 ])[1 ]
165- svdIter! (B, n1, n2, shift, U, Vᴴ)
182+ if shift^ 2 < eps (B. dv[n1]^ 2 )
183+ # Shift is too small to affect the iteration so just use
184+ # zero-shift algorithm anyway
185+ svdDemmelKahan! (B, n1, n2, U, Vᴴ)
186+ else
187+ svdIter! (B, n1, n2, shift, U, Vᴴ)
188+ end
166189 end
190+
167191 count += 1
168192 debug && println (" count=" , count)
169193 end
170194 else
171195 # Just transpose the matrix
172- error (" " )
173- # return _svd!(Bidiagonal(d, e, :U), Vᴴ, U; tol = tol, debug = debug)
196+ error (" SVD for lower triangular bidiagonal matrices isn't implemented yet." )
174197 end
175198end
176199
0 commit comments