@@ -5,11 +5,14 @@ import Base: SVD, svdvals!, svdfact!
5
5
include (" utils.jl" )
6
6
include (" bidiagonalize.jl" )
7
7
8
-
9
-
10
8
function svdfact! (X; sorted= true , thin= true )
11
9
m,n = size (X)
12
- m >= n || error (" Generic SVD requires more rows than columns." )
10
+ t = false
11
+ if m < n
12
+ m,n = n,m
13
+ X = X'
14
+ t = true
15
+ end
13
16
B,P = bidiagonalize_tall! (X)
14
17
U,Vt = full (P,thin= thin)
15
18
U,S,Vt = svd! (B,U,Vt)
@@ -27,11 +30,15 @@ function svdfact!(X; sorted=true, thin=true)
27
30
U = U[:,I]
28
31
Vt = Vt[I,:]
29
32
end
30
- SVD (U,S,Vt)
33
+ t ? SVD (Vt ' ,S,U ' ) : SVD (U,S,Vt)
31
34
end
32
35
33
36
function svdvals! (X; sorted= true )
34
- B,P = bidiagonalize_tall! (copy (X))
37
+ m,n = size (X)
38
+ if m < n
39
+ X = X'
40
+ end
41
+ B,P = bidiagonalize_tall! (X)
35
42
S = svd! (B)[2 ]
36
43
for i = eachindex (S)
37
44
if signbit (S[i])
45
52
"""
46
53
Tests if the B[i-1,i] element is approximately zero, using the criteria
47
54
```math
48
- |B_{i-1,i}| < ɛ*(|B_{i-1,i-1}| + |B_{i,i}|)
55
+ |B_{i-1,i}| ≤ ɛ*(|B_{i-1,i-1}| + |B_{i,i}|)
49
56
```
50
57
"""
51
- offdiag_approx_zero (B:: Bidiagonal ,i,ɛ) =
52
- abs (B. ev[i- 1 ]) < ɛ* (abs (B. dv[i- 1 ]) + abs (B. dv[i]))
58
+ function offdiag_approx_zero (B:: Bidiagonal ,i,ɛ)
59
+ iszero = abs (B. ev[i- 1 ]) ≤ ɛ* (abs (B. dv[i- 1 ]) + abs (B. dv[i]))
60
+ if iszero
61
+ B. ev[i- 1 ] = 0
62
+ end
63
+ iszero
64
+ end
53
65
54
66
55
67
"""
@@ -70,34 +82,112 @@ function svd!{T<:Real}(B::Bidiagonal{T}, U=nothing, Vt=nothing, ɛ::T = eps(T))
70
82
n = size (B, 1 )
71
83
n₂ = n
72
84
85
+ maxB = max (maxabs (B. dv),maxabs (B. ev))
86
+
73
87
if istriu (B)
74
88
while true
89
+ @label mainloop
90
+
75
91
while offdiag_approx_zero (B,n₂,ɛ)
76
92
n₂ -= 1
77
93
if n₂ == 1
78
- return U,B . dv,Vt
94
+ @goto done
79
95
end
80
96
end
97
+
98
+
99
+
81
100
n₁ = n₂ - 1
101
+ # check for diagonal zeros
102
+ if abs (B. dv[n₁]) ≤ ɛ* maxB
103
+ svd_zerodiag_row! (U,B,n₁,n₂)
104
+ @goto mainloop
105
+ end
82
106
while n₁ > 1 && ! offdiag_approx_zero (B,n₁,ɛ)
83
107
n₁ -= 1
108
+ # check for diagonal zeros
109
+ if abs (B. dv[n₁]) ≤ ɛ* maxB
110
+ svd_zerodiag_row! (U,B,n₁,n₂)
111
+ @goto mainloop
112
+ end
113
+ end
114
+
115
+ if abs (B. dv[n₂]) ≤ ɛ* maxB
116
+ svd_zerodiag_col! (B,Vt,n₁,n₂)
117
+ @goto mainloop
84
118
end
85
119
86
- # TODO : check for diagonal zeros
87
120
88
121
d₁ = B. dv[n₂- 1 ]
89
122
d₂ = B. dv[n₂]
90
123
e = B. ev[n₂- 1 ]
91
-
124
+
92
125
s₁, s₂ = svdvals2x2 (d₁, d₂, e)
93
- # use singular value closest to
126
+ # use singular value closest to sqrt of final element of B'*B
94
127
h = hypot (d₂,e)
95
- shift = abs (s₁- h) < abs (s₂- h) ? s₁ : s₂
128
+ shift = abs (s₁- h) < abs (s₂- h) ? s₁ : s₂
96
129
svd_gk! (B, U, Vt, n₁, n₂, shift)
97
130
end
98
131
else
99
132
throw (ArgumentError (" lower bidiagonal version not implemented yet" ))
100
133
end
134
+ @label done
135
+ U, B. dv, Vt
136
+ end
137
+
138
+
139
+ """
140
+ Sets B[n₁,n₁] to zero, then zeros out row n₁ by applying sequential row (left) Givens rotations up to n₂.
141
+ """
142
+ function svd_zerodiag_row! (U,B,n₁,n₂)
143
+ e = B. ev[n₁]
144
+ B. dv[n₁] = 0 # set to zero
145
+ B. ev[n₁] = 0
146
+
147
+ for i = n₁+ 1 : n₂
148
+ # n₁ [0 ,e ] = G * [e ,0 ]
149
+ # [ ... ] [ ... ]
150
+ # i [dᵢ,eᵢ] [dᵢ,eᵢ]
151
+ dᵢ = B. dv[i]
152
+
153
+ G,r = givens (dᵢ,e,i,n₁)
154
+ A_mul_Bc! (U,G)
155
+ B. dv[i] = r # -G.s*e + G.c*dᵢ
156
+
157
+ if i < n₂
158
+ eᵢ = B. ev[i]
159
+ e = G. s* eᵢ
160
+ B. ev[i] = G. c* eᵢ
161
+ end
162
+ end
163
+ end
164
+
165
+
166
+ """
167
+ Sets B[n₂,n₂] to zero, then zeros out column n₂ by applying sequential column (right) Givens rotations up to n₁.
168
+ """
169
+ function svd_zerodiag_col! (B,Vt,n₁,n₂)
170
+ e = B. ev[n₂- 1 ]
171
+ B. dv[n₂] = 0 # set to zero
172
+ B. ev[n₂- 1 ] = 0
173
+
174
+ for i = n₂- 1 : - 1 : n₁
175
+ # i n₂ i n₂
176
+ # [eᵢ,...,e ] = [eᵢ,...,0 ] * G'
177
+ # [dᵢ,...,0 ] [dᵢ,...,e ]
178
+ dᵢ = B. dv[i]
179
+
180
+ G,r = givens (dᵢ,e,i,n₂)
181
+ A_mul_B! (G,Vt)
182
+
183
+ B. dv[i] = r # G.c*dᵢ + G.s*e
184
+
185
+ if n₁ < i
186
+ eᵢ = B. ev[i- 1 ]
187
+ e = - G. s* eᵢ
188
+ B. ev[i- 1 ] = G. c* eᵢ
189
+ end
190
+ end
101
191
end
102
192
103
193
@@ -110,15 +200,15 @@ A Givens rotation is applied to the top 2x2 matrix, and the resulting "bulge" is
110
200
function svd_gk! {T<:Real} (B:: Bidiagonal{T} ,U,Vt,n₁,n₂,shift)
111
201
112
202
if istriu (B)
113
-
203
+
114
204
d₁′ = B. dv[n₁]
115
205
e₁′ = B. ev[n₁]
116
206
d₂′ = B. dv[n₁+ 1 ]
117
207
118
208
G, r = givens (d₁′ - abs2 (shift)/ d₁′, e₁′, n₁, n₁+ 1 )
119
209
A_mul_B! (G, Vt)
120
210
121
- # [d₁,e₁] = [d₁′,e₁′] * G
211
+ # [d₁,e₁] = [d₁′,e₁′] * G'
122
212
# [b ,d₂] [0 ,d₂′]
123
213
124
214
@@ -129,48 +219,48 @@ function svd_gk!{T<:Real}(B::Bidiagonal{T},U,Vt,n₁,n₂,shift)
129
219
130
220
for i = n₁: n₂- 2
131
221
132
- # [. ,e₁′,b′ ] = G * [d₁,e₁,0 ]
222
+ # [. ,e₁′,b′ ] = G * [d₁,e₁,0 ]
133
223
# [0 ,d₂′,e₂′] [b ,d₂,e₂]
134
224
135
225
e₂ = B. ev[i+ 1 ]
136
226
137
227
G, r = givens (d₁, b, i, i+ 1 )
138
228
A_mul_Bc! (U, G)
139
229
140
- B. dv[i] = G. c* d₁ + G. s* b
141
-
230
+ B. dv[i] = r # G.c*d₁ + G.s*b
231
+
142
232
e₁′ = G. c* e₁ + G. s* d₂
143
233
d₂′ = - G. s* e₁ + G. c* d₂
144
-
234
+
145
235
b′ = G. s* e₂
146
236
e₂′ = G. c* e₂
147
237
148
- # [. ,0 ] = [e₁′,b′ ] * G
238
+ # [. ,0 ] = [e₁′,b′ ] * G'
149
239
# [d₁,e₁] [d₂′,e₂′]
150
240
# [b ,d₂] [0 ,d₃′]
151
241
152
242
d₃′ = B. dv[i+ 2 ]
153
243
154
244
G, r = givens (e₁′, b′, i+ 1 , i+ 2 )
155
245
A_mul_B! (G, Vt)
156
-
157
- B. ev[i] = e₁′* G. c + b′* G. s
158
-
246
+
247
+ B. ev[i] = r # e₁′*G.c + b′*G.s
248
+
159
249
d₁ = d₂′* G. c + e₂′* G. s
160
250
e₁ = - d₂′* G. s + e₂′* G. c
161
251
162
252
b = d₃′* G. s
163
253
d₂ = d₃′* G. c
164
254
end
165
255
166
- # [. ,.] = G * [d₁,e₁]
256
+ # [. ,.] = G * [d₁,e₁]
167
257
# [0 ,.] [b ,d₂]
168
258
169
259
G, r = givens (d₁,b,n₂- 1 ,n₂)
170
260
A_mul_Bc! (U, G)
171
-
172
- B. dv[n₂- 1 ] = G. c* d₁ + G. s* b
173
-
261
+
262
+ B. dv[n₂- 1 ] = r # G.c*d₁ + G.s*b
263
+
174
264
B. ev[n₂- 1 ] = G. c* e₁ + G. s* d₂
175
265
B. dv[n₂] = - G. s* e₁ + G. c* d₂
176
266
else
@@ -198,7 +288,7 @@ function svdvals2x2(f, h, g)
198
288
199
289
fhmin = min (fa,ha)
200
290
fhmax = max (fa,ha)
201
-
291
+
202
292
if fhmin == 0
203
293
ssmin = zero (f)
204
294
if fhmax == 0
0 commit comments