Skip to content

Commit 7f46265

Browse files
start adding ToeplitzPlusHankel
1 parent 1bd226e commit 7f46265

File tree

5 files changed

+335
-2
lines changed

5 files changed

+335
-2
lines changed

src/FastTransforms.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,9 @@ include("specialfunctions.jl")
125125
include("toeplitzplans.jl")
126126
include("toeplitzhankel.jl")
127127

128-
include("SymmetricToeplitzPlusHankel.jl")
128+
export ToeplitzPlusHankel
129+
130+
include("ToeplitzPlusHankel.jl")
129131

130132
# following use libfasttransforms by default
131133
for f in (:jac2jac,

src/GramMatrix.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ where ``J = \\begin{pmatrix} 0 & 1\\\\ -1 & 0\\end{pmatrix}`` and where:
2222
```math
2323
G_{:, 1} = e_n,\\quad{\\rm and}\\quad G_{:, 2} = W_{n-1, :}X_{n-1, n} - X^\\top W_{:, n}.
2424
```
25-
Fast (``{\\cal O}(n^2)``) Cholesky factorization of the Gram matrix returns the
25+
Fast (``O(n^2)``) Cholesky factorization of the Gram matrix returns the
2626
connection coefficients between ``{\\bf P}(x)`` and the polynomials ``{\\bf Q}(x)``
2727
orthogonal in the modified inner product, ``{\\bf P}(x) = {\\bf Q}(x) R``.
2828
"""

src/ToeplitzPlusHankel.jl

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
struct ToeplitzPlusHankel{T, S, P1 <: Plan{S}, P2 <: Plan{S}} <: AbstractMatrix{T}
2+
tc::Vector{T}
3+
tr::Vector{T}
4+
h::Vector{T}
5+
th_dft::Matrix{S}
6+
tht_dft::Matrix{S}
7+
temp::Matrix{S}
8+
plan::P1
9+
iplan::P2
10+
size::NTuple{2, Int}
11+
end
12+
13+
# enforces tr[1] == tc[1]
14+
function ToeplitzPlusHankel(tc::Vector{T}, tr::Vector{T}, h::Vector{T}) where T
15+
m = length(tc)
16+
n = length(tr)
17+
@assert length(h) == m+n-1
18+
tr[1] = tc[1]
19+
mn = m+n
20+
S = promote_type(float(T), Complex{Float32})
21+
th_dft = Matrix{S}(undef, mn, 2)
22+
copyto!(th_dft, 1, tc, 1, m)
23+
th_dft[m+1, 1] = zero(T)
24+
copyto!(th_dft, m+2, Iterators.reverse(tr), 1, n-1)
25+
copyto!(th_dft, mn+1, h, n, m)
26+
th_dft[m+1, 2] = zero(T)
27+
copyto!(th_dft, mn+m+2, h, 1, n-1)
28+
tht_dft = Matrix{S}(undef, mn, 2)
29+
copyto!(tht_dft, 1, tr, 1, n)
30+
tht_dft[n+1, 1] = zero(T)
31+
copyto!(tht_dft, n+2, Iterators.reverse(tc), 1, m-1)
32+
copyto!(tht_dft, mn+1, h, m, n)
33+
tht_dft[n+1, 2] = zero(T)
34+
copyto!(tht_dft, mn+n+2, h, 1, m-1)
35+
36+
plan = plan_fft!(th_dft, 1)
37+
plan*th_dft
38+
plan*tht_dft
39+
temp = zeros(S, mn, 2)
40+
iplan = inv(plan)
41+
42+
ToeplitzPlusHankel{T, S, typeof(plan), typeof(iplan)}(tc, tr, h, th_dft, tht_dft, temp, plan, iplan, (m, n))
43+
end
44+
45+
# A ChebyshevGramMatrix isa (symmetric positive-definite) ToeplitzPlusHankel matrix.
46+
function ToeplitzPlusHankel(G::ChebyshevGramMatrix)
47+
n = size(G, 1)
48+
ToeplitzPlusHankel(G.μ[1:n]/2, G.μ[1:n]/2, G.μ/2)
49+
end
50+
51+
size(A::ToeplitzPlusHankel) = A.size
52+
getindex(A::ToeplitzPlusHankel, i::Integer, j::Integer) = (i j ? A.tc[i-j+1] : A.tr[j-i+1]) + A.h[i+j-1]
53+
54+
# A view of a T+H is also T+H.
55+
function getindex(A::ToeplitzPlusHankel, ir::UnitRange{Int}, jr::UnitRange{Int})
56+
fir, lir = first(ir), last(ir)
57+
fjr, ljr = first(jr), last(jr)
58+
if fir fjr
59+
tc = A.tc[fir-fjr+1:lir-fjr+1]
60+
tr = [A.tc[fir-fjr+1:-1:max(1, fir-ljr+1)]; A.tr[2:ljr-fir+1]]
61+
else
62+
tc = [A.tr[fjr-fir+1:-1:max(1, fjr-lir+1)]; A.tc[2:lir-fjr+1]]
63+
tr = A.tr[fjr-fir+1:ljr-fir+1]
64+
end
65+
ToeplitzPlusHankel(tc, tr, A.h[fir+fjr-1:lir+ljr-1])
66+
end
67+
68+
69+
# y ← A x α + y β
70+
function mul!(y::StridedVector{T}, A::ToeplitzPlusHankel{T}, x::StridedVector{T}, α::S, β::S) where {T <: Real, S <: Real}
71+
m, n = size(A)
72+
@assert m == length(y)
73+
@assert n == length(x)
74+
mn = m+n
75+
th_dft = A.th_dft
76+
temp = A.temp
77+
plan = A.plan
78+
iplan = A.iplan
79+
80+
copyto!(temp, 1, x, 1, n)
81+
copyto!(temp, mn+1, Iterators.reverse(x), 1, n)
82+
@inbounds for j in n+1:mn
83+
temp[j, 1] = zero(T)
84+
temp[j, 2] = zero(T)
85+
end
86+
plan*temp
87+
temp .*= th_dft
88+
iplan*temp
89+
90+
if iszero(β)
91+
@inbounds @simd for i in 1:m
92+
y[i] = α * (real(temp[i, 1])+real(temp[i, 2]))
93+
end
94+
else
95+
@inbounds @simd for i in 1:m
96+
y[i] = α * (real(temp[i, 1])+real(temp[i, 2])) + β*y[i]
97+
end
98+
end
99+
return y
100+
end
101+
102+
# y ← A' x α + y β
103+
function mul!(y::StridedVector{T}, A::Adjoint{T, <:ToeplitzPlusHankel{T}}, x::StridedVector{T}, α::S, β::S) where {T <: Real, S <: Real}
104+
m, n = size(A)
105+
@assert m == length(y)
106+
@assert n == length(x)
107+
mn = m+n
108+
AP = A.parent
109+
tht_dft = AP.tht_dft
110+
temp = AP.temp
111+
plan = AP.plan
112+
iplan = AP.iplan
113+
114+
copyto!(temp, 1, x, 1, n)
115+
copyto!(temp, mn+1, Iterators.reverse(x), 1, n)
116+
@inbounds for j in n+1:mn
117+
temp[j, 1] = zero(T)
118+
temp[j, 2] = zero(T)
119+
end
120+
plan*temp
121+
temp .*= tht_dft
122+
iplan*temp
123+
124+
if iszero(β)
125+
@inbounds @simd for i in 1:m
126+
y[i] = α * (real(temp[i, 1])+real(temp[i, 2]))
127+
end
128+
else
129+
@inbounds @simd for i in 1:m
130+
y[i] = α * (real(temp[i, 1])+real(temp[i, 2])) + β*y[i]
131+
end
132+
end
133+
return y
134+
end
135+
136+
137+
# C ← A B α + C β
138+
function mul!(C::StridedMatrix{T}, A::ToeplitzPlusHankel{T}, B::StridedMatrix{T}, α::S, β::S) where {T <: Real, S <: Real}
139+
m, n = size(A)
140+
@assert m == size(C, 1)
141+
@assert n == size(B, 1)
142+
p = size(B, 2)
143+
if size(C, 2) != p
144+
throw(DimensionMismatch("input and output matrices must have same number of columns"))
145+
end
146+
147+
th_dft = A.th_dft
148+
TC = promote_type(float(T), Complex{Float32})
149+
temp = zeros(TC, m+n, 2p)
150+
plan = plan_fft!(temp, 1)
151+
152+
for k in 1:p
153+
copyto!(view(temp, :, 2k-1), 1, view(B, :, k), 1, n)
154+
copyto!(view(temp, :, 2k), 1, Iterators.reverse(view(B, :, k)), 1, n)
155+
end
156+
plan*temp
157+
for k in 1:p
158+
vt = view(temp, :, 2k-1:2k)
159+
vt .*= th_dft
160+
end
161+
plan\temp
162+
163+
if iszero(β)
164+
@inbounds for k in 1:p
165+
for i in 1:m
166+
C[i, k] = α * (real(temp[i, 2k-1])+real(temp[i, 2k]))
167+
end
168+
end
169+
else
170+
@inbounds for k in 1:p
171+
for i in 1:m
172+
C[i, k] = α * (real(temp[i, 2k-1])+real(temp[i, 2k])) + β*C[i, k]
173+
end
174+
end
175+
end
176+
return C
177+
end
178+
179+
# Morally equivalent to mul!(C', B', A', α, β)' with StridedMatrix replaced by AbstractMatrix below
180+
function mul!(C::StridedMatrix{T}, A::StridedMatrix{T}, B::ToeplitzPlusHankel{T}, α::S, β::S) where {T <: Real, S <: Real}
181+
n, m = size(B)
182+
@assert m == size(C, 2)
183+
@assert n == size(A, 2)
184+
p = size(A, 1)
185+
if size(C, 1) != p
186+
throw(DimensionMismatch("input and output matrices must have same number of rows"))
187+
end
188+
189+
tht_dft = B.tht_dft
190+
TC = promote_type(float(T), Complex{Float32})
191+
temp = zeros(TC, m+n, 2p)
192+
plan = plan_fft!(temp, 1)
193+
194+
for k in 1:p
195+
copyto!(view(temp, :, 2k-1), 1, view(A, k, :), 1, n)
196+
copyto!(view(temp, :, 2k), 1, Iterators.reverse(view(A, k, :)), 1, n)
197+
end
198+
plan*temp
199+
for k in 1:p
200+
vt = view(temp, :, 2k-1:2k)
201+
vt .*= tht_dft
202+
end
203+
plan\temp
204+
205+
if iszero(β)
206+
@inbounds for k in 1:p
207+
for i in 1:m
208+
C[k, i] = α * (real(temp[i, 2k-1])+real(temp[i, 2k]))
209+
end
210+
end
211+
else
212+
@inbounds for k in 1:p
213+
for i in 1:m
214+
C[k, i] = α * (real(temp[i, 2k-1])+real(temp[i, 2k])) + β*C[k, i]
215+
end
216+
end
217+
end
218+
return C
219+
end
220+
221+
# C ← A' B α + C β
222+
function mul!(C::StridedMatrix{T}, A::Adjoint{T, <:ToeplitzPlusHankel{T}}, B::StridedMatrix{T}, α::S, β::S) where {T <: Real, S <: Real}
223+
m, n = size(A)
224+
@assert m == size(C, 1)
225+
@assert n == size(B, 1)
226+
p = size(B, 2)
227+
if size(C, 2) != p
228+
throw(DimensionMismatch("input and output matrices must have same number of columns"))
229+
end
230+
231+
tht_dft = A.parent.tht_dft
232+
TC = promote_type(float(T), Complex{Float32})
233+
temp = zeros(TC, m+n, 2p)
234+
plan = plan_fft!(temp, 1)
235+
236+
for k in 1:p
237+
copyto!(view(temp, :, 2k-1), 1, view(B, :, k), 1, n)
238+
copyto!(view(temp, :, 2k), 1, Iterators.reverse(view(B, :, k)), 1, n)
239+
end
240+
plan*temp
241+
for k in 1:p
242+
vt = view(temp, :, 2k-1:2k)
243+
vt .*= tht_dft
244+
end
245+
plan\temp
246+
247+
if iszero(β)
248+
@inbounds for k in 1:p
249+
for i in 1:m
250+
C[i, k] = α * (real(temp[i, 2k-1])+real(temp[i, 2k]))
251+
end
252+
end
253+
else
254+
@inbounds for k in 1:p
255+
for i in 1:m
256+
C[i, k] = α * (real(temp[i, 2k-1])+real(temp[i, 2k])) + β*C[i, k]
257+
end
258+
end
259+
end
260+
return C
261+
end
262+
263+
# Estimate the Frobenius norm of the Toeplitz-plus-Hankel matrix by working with the symbols.
264+
function normest(A::ToeplitzPlusHankel{T}) where T
265+
m, n = size(A)
266+
tc = A.tc
267+
tr = A.tr
268+
h = A.h
269+
ret1 = zero(T)
270+
ret2 = zero(T)
271+
if m == min(m, n)
272+
for i = 1:m
273+
ret1 += (m+1-i)*abs2(tc[i])
274+
end
275+
for i = 2:n-m
276+
ret1 += m*abs2(tr[i])
277+
end
278+
for i = n-m+1:n
279+
ret1 += (n-i)*abs2(tr[i])
280+
end
281+
for i = 1:m
282+
ret2 += i*abs2(h[i])
283+
end
284+
for i = m+1:n
285+
ret2 += m*abs2(h[i])
286+
end
287+
for i = n+1:m+n-1
288+
ret2 += (m+n-i)*abs2(h[i])
289+
end
290+
else
291+
for i = 1:n
292+
ret1 += (n+1-i)*abs2(tr[i])
293+
end
294+
for i = 2:m-n
295+
ret1 += n*abs2(tc[i])
296+
end
297+
for i = m-n+1:m
298+
ret1 += (m-i)*abs2(tc[i])
299+
end
300+
for i = 1:n
301+
ret2 += i*abs2(h[i])
302+
end
303+
for i = n+1:m
304+
ret2 += n*abs2(h[i])
305+
end
306+
for i = m+1:m+n-1
307+
ret2 += (m+n-i)*abs2(h[i])
308+
end
309+
end
310+
sqrt(ret1) + sqrt(ret2)
311+
end
312+
313+
normest(A::Symmetric{T, <: ToeplitzPlusHankel{T}}) where T = normest(parent(A))
314+
normest(A::Hermitian{T, <: ToeplitzPlusHankel{T}}) where T = normest(parent(A))
315+
316+
function normest(A::ChebyshevGramMatrix{T}) where T
317+
n = size(A, 1)
318+
normest(A[1:n, 1:n])
319+
end

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ include("gaunttests.jl")
1010
include("hermitetests.jl")
1111
include("toeplitzplanstests.jl")
1212
include("toeplitzhankeltests.jl")
13+
include("toeplitzplushankeltests.jl")
1314
include("grammatrixtests.jl")
1415
include("arraystests.jl")

test/toeplitzplushankeltests.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using FastTransforms, LinearAlgebra, Test
2+
3+
@testset "ToeplitzPlusHankel" begin
4+
n = 128
5+
for T in (Float32, Float64)
6+
μ = FastTransforms.chebyshevmoments1(T, 2n-1)
7+
G = ChebyshevGramMatrix(μ)
8+
TpH = ToeplitzPlusHankel(G)
9+
@test TpH G
10+
end
11+
end

0 commit comments

Comments
 (0)