Skip to content

Commit d8ccedd

Browse files
add FastCholesky for any OPs
X'W-WX == GJG', rank(G) = 2. This allows W to be Cholesky-factored in O(n^2).
1 parent e5cc4c4 commit d8ccedd

File tree

3 files changed

+120
-30
lines changed

3 files changed

+120
-30
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "FastTransforms"
22
uuid = "057dd010-8810-581a-b7be-e3fc3b93f78c"
3-
version = "0.16.3"
3+
version = "0.16.4"
44

55
[deps]
66
AbstractFFTs = "621f4979-c628-5d54-868e-fcf4e3e8185c"

src/SymmetricToeplitzPlusHankel.jl

Lines changed: 107 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -38,23 +38,23 @@ bandwidths(A::SymmetricBandedToeplitzPlusHankel{T}) where T = (A.b, A.b)
3838

3939
#
4040
# X'W-W*X = G*J*G'
41-
# This returns G and J, where J = [0 I; -I 0], respecting the skew-symmetry of the right-hand side.
41+
# This returns G, where J = [0 1; -1 0], respecting the skew-symmetry of the right-hand side.
4242
#
4343
function compute_skew_generators(A::SymmetricToeplitzPlusHankel{T}) where T
4444
v = A.v
4545
n = size(A, 1)
46-
J = [zero(T) one(T); -one(T) zero(T)]
4746
G = zeros(T, n, 2)
4847
G[n, 1] = one(T)
49-
u2 = reverse(v[2:n+1])
50-
u2[1:n-1] .+= v[n+1:2n-1]
51-
G[:, 2] .= -u2
52-
G, J
48+
@inbounds @simd for j in 1:n-1
49+
G[j, 2] = -(v[n+2-j] + v[n+j])
50+
end
51+
G[n, 2] = -v[2]
52+
G
5353
end
5454

5555
function cholesky(A::SymmetricToeplitzPlusHankel{T}) where T
5656
n = size(A, 1)
57-
G, J = compute_skew_generators(A)
57+
G = compute_skew_generators(A)
5858
L = zeros(T, n, n)
5959
c = A[:, 1]
6060
= zeros(T, n)
@@ -74,20 +74,29 @@ function STPHcholesky!(L::Matrix{T}, G, c, ĉ, l, v, row1, n) where T
7474
for j in 1:n-k+1
7575
v[j] = G[j, 1]*G[1, 2] - G[j, 2]*G[1, 1]
7676
end
77-
X21 = k == 1 ? T(2) : T(1)
78-
ĉ[1] = (c[2] - v[1])/X21
79-
for j in 2:n-k
80-
ĉ[j] = (c[j+1] + c[j-1] + c[1]*row1[j] - row1[1]*c[j] - v[j])/X21
77+
if k == 1
78+
for j in 2:n-k
79+
ĉ[j] = (c[j+1] + c[j-1] + c[1]*row1[j] - row1[1]*c[j] - v[j])/2
80+
end
81+
ĉ[n-k+1] = (c[n-k] + c[1]*row1[n-k+1] - row1[1]*c[n-k+1] - v[n-k+1])/2
82+
cst = 2/d
83+
for j in 1:n-k
84+
row1[j] = -cst*l[j+1]
85+
end
86+
else
87+
for j in 2:n-k
88+
ĉ[j] = c[j+1] + c[j-1] + c[1]*row1[j] - row1[1]*c[j] - v[j]
89+
end
90+
ĉ[n-k+1] = c[n-k] + c[1]*row1[n-k+1] - row1[1]*c[n-k+1] - v[n-k+1]
91+
cst = 1/d
92+
for j in 1:n-k
93+
row1[j] = -cst*l[j+1]
94+
end
8195
end
82-
ĉ[n-k+1] = (c[n-k] + c[1]*row1[n-k+1] - row1[1]*c[n-k+1] - v[n-k+1])/X21
8396
cst = c[2]/d
8497
for j in 1:n-k
8598
c[j] = ĉ[j+1] - cst*l[j+1]
8699
end
87-
cst = X21/d
88-
for j in 1:n-k
89-
row1[j] = -cst*l[j+1]
90-
end
91100
gd1 = G[1, 1]/d
92101
gd2 = G[1, 2]/d
93102
for j in 1:n-k
@@ -101,37 +110,106 @@ end
101110
function cholesky(A::SymmetricBandedToeplitzPlusHankel{T}) where T
102111
n = size(A, 1)
103112
b = A.b
104-
R = BandedMatrix{T}(undef, (n, n), (0, bandwidth(A, 2)))
113+
L = BandedMatrix{T}(undef, (n, n), (bandwidth(A, 1), 0))
105114
c = A[1:b+2, 1]
106115
= zeros(T, b+3)
107116
l = zeros(T, b+3)
108117
row1 = zeros(T, b+2)
109-
SBTPHcholesky!(R, c, ĉ, l, row1, n, b)
110-
return Cholesky(R, 'U', 0)
118+
SBTPHcholesky!(L, c, ĉ, l, row1, n, b)
119+
return Cholesky(L, 'L', 0)
111120
end
112121

113-
function SBTPHcholesky!(R::BandedMatrix{T}, c, ĉ, l, row1, n, b) where T
122+
function SBTPHcholesky!(L::BandedMatrix{T}, c, ĉ, l, row1, n, b) where T
114123
@inbounds @simd for k in 1:n
115124
d = sqrt(c[1])
116125
for j in 1:b+1
117126
l[j] = c[j]/d
118127
end
119128
for j in 1:min(n-k+1, b+1)
120-
R[k, j+k-1] = l[j]
129+
L[j+k-1, k] = l[j]
121130
end
122-
X21 = k == 1 ? T(2) : T(1)
123-
ĉ[1] = c[2]/X21
124-
for j in 2:b+1
125-
ĉ[j] = (c[j+1] + c[j-1] + c[1]*row1[j] - row1[1]*c[j])/X21
131+
if k == 1
132+
for j in 2:b+1
133+
ĉ[j] = (c[j+1] + c[j-1] + c[1]*row1[j] - row1[1]*c[j])/2
134+
end
135+
ĉ[b+2] = (c[b+1] + c[1]*row1[b+2] - row1[1]*c[b+2])/2
136+
cst = 2/d
137+
for j in 1:b+2
138+
row1[j] = -cst*l[j+1]
139+
end
140+
else
141+
for j in 2:b+1
142+
ĉ[j] = (c[j+1] + c[j-1] + c[1]*row1[j] - row1[1]*c[j])
143+
end
144+
ĉ[b+2] = (c[b+1] + c[1]*row1[b+2] - row1[1]*c[b+2])
145+
cst = 1/d
146+
for j in 1:b+2
147+
row1[j] = -cst*l[j+1]
148+
end
126149
end
127-
ĉ[b+2] = (c[b+1] + c[1]*row1[b+2] - row1[1]*c[b+2])/X21
128150
cst = c[2]/d
129151
for j in 1:b+2
130152
c[j] = ĉ[j+1] - cst*l[j+1]
131153
end
132-
cst = X21/d
133-
for j in 1:b+2
134-
row1[j] = -cst*l[j+1]
154+
end
155+
end
156+
157+
158+
159+
#
160+
# X'W-W*X = G*J*G'
161+
# This returns G, where J = [0 1; -1 0], respecting the skew-symmetry of the right-hand side.
162+
#
163+
function compute_skew_generators(W::Symmetric{T, <: AbstractMatrix{T}}, X::Tridiagonal{T, Vector{T}}) where T
164+
@assert size(W) == size(X)
165+
m, n = size(W)
166+
G = zeros(T, n, 2)
167+
G[n, 1] = one(T)
168+
G[:, 2] .= W[n-1, :]*X[n-1, n] - X'W[:, n]
169+
return G
170+
end
171+
172+
function fastcholesky(W::Symmetric{T, <: AbstractMatrix{T}}, X::Tridiagonal{T, Vector{T}}) where T
173+
n = size(W, 1)
174+
G = compute_skew_generators(W, X)
175+
L = zeros(T, n, n)
176+
c = W[:, 1]
177+
= zeros(T, n)
178+
l = zeros(T, n)
179+
v = zeros(T, n)
180+
row1 = zeros(T, n)
181+
fastcholesky!(L, X, G, c, ĉ, l, v, row1, n)
182+
return Cholesky(L, 'L', 0)
183+
end
184+
185+
186+
function fastcholesky!(L::Matrix{T}, X::Tridiagonal{T, Vector{T}}, G, c, ĉ, l, v, row1, n) where T
187+
@inbounds @simd for k in 1:n-1
188+
d = sqrt(c[k])
189+
for j in k:n
190+
L[j, k] = l[j] = c[j]/d
191+
end
192+
for j in k:n
193+
v[j] = G[j, 1]*G[k, 2] - G[j, 2]*G[k, 1]
194+
end
195+
for j in k+1:n-1
196+
ĉ[j] = (X[j-1, j]*c[j-1] + (X[j, j]-X[k, k])*c[j] + X[j+1, j]*c[j+1] + c[k]*row1[j] - row1[k]*c[j] - v[j])/X[k+1, k]
197+
end
198+
ĉ[n] = (X[n-1, n]*c[n-1] + (X[n, n]-X[k, k])*c[n] + c[k]*row1[n] - row1[k]*c[n] - v[n])/X[k+1, k]
199+
cst = X[k+1, k]/d
200+
for j in k+1:n
201+
row1[j] = -cst*l[j]
202+
end
203+
cst = c[k+1]/d
204+
for j in k:n
205+
c[j] = ĉ[j] - cst*l[j]
206+
end
207+
gd1 = G[k, 1]/d
208+
gd2 = G[k, 2]/d
209+
for j in k:n
210+
G[j, 1] -= l[j]*gd1
211+
G[j, 2] -= l[j]*gd2
135212
end
136213
end
214+
L[n, n] = sqrt(c[n])
137215
end

test/symmetrictoeplitzplushankeltests.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,15 @@ end
3737
@test R U
3838
end
3939
end
40+
41+
@testset "Fast Cholesky" begin
42+
n = 128
43+
for T in (Float32, Float64, BigFloat)
44+
R = plan_leg2cheb(T, n; normcheb=true)*I
45+
X = Tridiagonal([T(n)/(2n-1) for n in 1:n-1], zeros(T, n), [T(n)/(2n+1) for n in 1:n-1]) # Legendre X
46+
W = Symmetric(R'R)
47+
F = FastTransforms.fastcholesky(W, X)
48+
@test F.L*F.L' W
49+
@test F.U R
50+
end
51+
end

0 commit comments

Comments
 (0)