1+ """
2+ Store a diagonally-scaled Toeplitz∘Hankel matrix:
3+ DL(T∘H)DR
4+ where the Hankel matrix `H` is non-negative definite. This allows a Cholesky decomposition in 𝒪(K²N) operations and 𝒪(KN) storage, K = log N log ɛ⁻¹.
5+ """
6+ struct ToeplitzHankelPlan{S, N, M, N1, TP<: ToeplitzPlan{S,N1} } <: Plan{S}
7+ T:: NTuple{M,TP}
8+ L:: NTuple{M,Matrix{S}}
9+ R:: NTuple{M,Matrix{S}}
10+ tmp:: Array{S,N1}
11+ dims:: NTuple{M,Int}
12+ function ToeplitzHankelPlan {S,N,M,N1,TP} (T:: NTuple{M,TP} , L, R, dims) where {S,TP,N,N1,M}
13+ tmp = Array {S} (undef, max .(size .(T)... )... )
14+ new {S,N,M,N1,TP} (T, L, R, tmp, dims)
15+ end
16+ ToeplitzHankelPlan {S,N,M,N1,TP} (T:: NTuple{M,TP} , L, R, dims:: Int ) where {S,TP,N,N1,M} =
17+ ToeplitzHankelPlan {S,N,M,N1,TP} (T, L, R, (dims,))
18+ end
19+
20+ ToeplitzHankelPlan (T:: ToeplitzPlan{S,2} , L:: Matrix , R:: Matrix , dims= 1 ) where S =
21+ ToeplitzHankelPlan {S, 1, 1, 2, typeof(T)} ((T,), (L,), (R,), dims)
22+
23+ ToeplitzHankelPlan (T:: ToeplitzPlan{S,3} , L:: Matrix , R:: Matrix , dims) where S =
24+ ToeplitzHankelPlan {S, 2, 1,3, typeof(T)} ((T,), (L,), (R,), dims)
25+
26+ ToeplitzHankelPlan (T:: NTuple{2,TP} , L:: Tuple , R:: Tuple , dims) where {S,TP<: ToeplitzPlan{S,3} } =
27+ ToeplitzHankelPlan {S, 2,2,3, TP} (T, L, R, dims)
28+
29+
30+ function * (P:: ToeplitzHankelPlan{<:Any,1} , v:: AbstractVector )
31+ (R,),(L,),(T,),tmp = P. R,P. L,P. T,P. tmp
32+ tmp .= R .* v
33+ T * tmp
34+ tmp .= L .* tmp
35+ sum! (v, tmp)
36+ end
37+
38+ function _th_applymul1! (v, T, L, R, tmp)
39+ N = size (R,2 )
40+ m,n = size (v)
41+ tmp[1 : m,1 : n,1 : N] .= reshape (R,size (R,1 ),1 ,N) .* v
42+ T * view (tmp,1 : m,1 : n,1 : N)
43+ view (tmp,1 : m,1 : n,1 : N) .*= reshape (L,size (L,1 ),1 ,N)
44+ sum! (v, view (tmp,1 : m,1 : n,1 : N))
45+ end
46+
47+ function _th_applymul2! (v, T, L, R, tmp)
48+ N = size (R,2 )
49+ m,n = size (v)
50+ tmp[1 : m,1 : n,1 : N] .= reshape (R,1 ,size (R,1 ),N) .* v
51+ T * view (tmp,1 : m,1 : n,1 : N)
52+ view (tmp,1 : m,1 : n,1 : N) .*= reshape (L,1 ,size (L,1 ),N)
53+ sum! (v, view (tmp,1 : m,1 : n,1 : N))
54+ end
55+
56+
57+ function * (P:: ToeplitzHankelPlan{<:Any,2,1} , v:: AbstractMatrix )
58+ (R,),(L,),(T,),tmp = P. R,P. L,P. T,P. tmp
59+ if P. dims == (1 ,)
60+ _th_applymul1! (v, T, L, R, tmp)
61+ else
62+ _th_applymul2! (v, T, L, R, tmp)
63+ end
64+ v
65+ end
66+
67+ function * (P:: ToeplitzHankelPlan{<:Any,2,2} , v:: AbstractMatrix )
68+ (R1,R2),(L1,L2),(T1,T2),tmp = P. R,P. L,P. T,P. tmp
69+
70+ _th_applymul1! (v, T1, L1, R1, tmp)
71+ _th_applymul2! (v, T2, L2, R2, tmp)
72+
73+ v
74+ end
75+
76+ # partial cholesky for a Hankel matrix
77+
78+ function hankel_partialchol (v:: Vector{T} ) where T
79+ # Assumes positive definite
80+ σ = T[]
81+ n = isempty (v) ? 0 : (length (v)+ 2 ) ÷ 2
82+ C = Matrix {T} (undef, n, n)
83+ d = v[1 : 2 : end ] # diag of H
84+ @assert length (v) ≥ 2 n- 1
85+ reltol = maximum (abs,d)* eps (T)* log (n)
86+ r = 0
87+ for k = 1 : n
88+ mx,idx = findmax (d)
89+ if mx ≤ reltol break end
90+ push! (σ, inv (mx))
91+ C[:,k] .= view (v,idx: n+ idx- 1 )
92+ for j = 1 : k- 1
93+ nCjidxσj = - C[idx,j]* σ[j]
94+ LinearAlgebra. axpy! (nCjidxσj, view (C,:,j), view (C,:,k))
95+ end
96+ @inbounds for p= 1 : n
97+ d[p] -= C[p,k]^ 2 / mx
98+ end
99+ r += 1
100+ end
101+ for k= 1 : length (σ) rmul! (view (C,:,k), sqrt (σ[k])) end
102+ C[:,1 : r]
103+ end
104+
105+
106+ # Diagonally-scaled Toeplitz∘Hankel polynomial transforms
107+
108+
109+
110+ struct ChebyshevToLegendrePlanTH{TH}
111+ toeplitzhankel:: TH
112+ end
113+
114+ function * (P:: ChebyshevToLegendrePlanTH , v:: AbstractVector{S} ) where S
115+ n = length (v)
116+ ret = zero (S)
117+ @inbounds for k = 1 : 2 : n
118+ ret += - v[k]/ (k* (k- 2 ))
119+ end
120+ v[1 ] = ret
121+ P. toeplitzhankel* view (v,2 : n)
122+ v
123+ end
124+
125+ function _cheb2leg_rescale1! (V:: AbstractMatrix{S} ) where S
126+ m,n = size (V)
127+ for j = 1 : n
128+ ret = zero (S)
129+ @inbounds for k = 1 : 2 : m
130+ ret += - V[k,j]/ (k* (k- 2 ))
131+ end
132+ V[1 ,j] = ret
133+ end
134+ V
135+ end
136+
137+
138+ function * (P:: ChebyshevToLegendrePlanTH , V:: AbstractMatrix )
139+ m,n = size (V)
140+ dims = P. toeplitzhankel. dims
141+ if dims == (1 ,)
142+ _cheb2leg_rescale1! (V)
143+ P. toeplitzhankel* view (V,2 : m,:)
144+ elseif dims == (2 ,)
145+ _cheb2leg_rescale1! (transpose (V))
146+ P. toeplitzhankel* view (V,:,2 : n)
147+ else
148+ @assert dims == (1 ,2 )
149+ (R1,R2),(L1,L2),(T1,T2),tmp = P. toeplitzhankel. R,P. toeplitzhankel. L,P. toeplitzhankel. T,P. toeplitzhankel. tmp
150+
151+ _cheb2leg_rescale1! (V)
152+ _th_applymul1! (view (V,2 : m,:), T1, L1, R1, tmp)
153+ _cheb2leg_rescale1! (transpose (V))
154+ _th_applymul2! (view (V,:,2 : n), T2, L2, R2, tmp)
155+ end
156+ V
157+ end
158+
159+
160+
161+ function _leg2chebTH_TLC (:: Type{S} , mn, d) where S
162+ n = mn[d]
163+ λ = Λ .(0 : half (real (S)): n- 1 )
164+ t = zeros (S,n)
165+ t[1 : 2 : end ] .= 2 .* view (λ, 1 : 2 : n) ./ π
166+ C = hankel_partialchol (λ)
167+ T = plan_uppertoeplitz! (t, (mn... , size (C,2 )), d)
168+ L = copy (C)
169+ L[1 ,:] ./= 2
170+ T,L,C
171+ end
172+
173+ function _leg2chebuTH_TLC (:: Type{S} , mn, d) where {S}
174+ n = mn[d]
175+ S̃ = real (S)
176+ λ = Λ .(0 : half (S̃): n- 1 )
177+ t = zeros (S,n)
178+ t[1 : 2 : end ] = λ[1 : 2 : n]. / (((1 : 2 : n). - 2 ))
179+ h = λ./ ((1 : 2 n- 1 ). + 1 )
180+ C = hankel_partialchol (h)
181+ T = plan_uppertoeplitz! (- 2 t/ π, (length (t), size (C,2 )), 1 )
182+ (T, (1 : n) .* C, C)
183+ end
184+
185+
186+ for f in (:leg2cheb , :leg2chebu )
187+ plan = Symbol (" plan_th_" , f, " !" )
188+ TLC = Symbol (" _" , f, " TH_TLC" )
189+ @eval begin
190+ $ plan (:: Type{S} , mn:: Tuple , dims:: Int ) where {S} = ToeplitzHankelPlan ($ TLC (S, mn, dims)... , dims)
191+
192+ function $plan (:: Type{S} , mn:: NTuple{2,Int} , dims:: NTuple{2,Int} ) where {S}
193+ @assert dims == (1 ,2 )
194+ T1,L1,C1 = $ TLC (S, mn, 1 )
195+ T2,L2,C2 = $ TLC (S, mn, 2 )
196+ ToeplitzHankelPlan ((T1,T2), (L1,L2), (C1,C2), dims)
197+ end
198+ end
199+ end
200+
201+ _sub_dim_by_one (d) = ()
202+ _sub_dim_by_one (d, m, n... ) = (isone (d) ? m- 1 : m, _sub_dim_by_one (d- 1 , n... )... )
203+
204+ function _cheb2legTH_TLC (:: Type{S} , mn, d) where S
205+ n = mn[d]
206+ t = zeros (S,n- 1 )
207+ S̃ = real (S)
208+ if n > 1
209+ t[1 : 2 : end ] = Λ .(0 : one (S̃): div (n- 2 ,2 ), - half (S̃), one (S̃))
210+ end
211+ h = Λ .(1 : half (S̃): n- 1 , zero (S̃), 3 half (S̃))
212+ DL = (3 half (S̃): n- half (S̃))
213+ DR = - (one (S̃): n- one (S̃)). / 4
214+ C = hankel_partialchol (h)
215+ T = plan_uppertoeplitz! (t, (_sub_dim_by_one (d, mn... )... , size (C,2 )), d)
216+ T, DL .* C, DR .* C
217+ end
218+
219+ plan_th_cheb2leg! (:: Type{S} , mn:: Tuple , dims:: Int ) where {S} = ChebyshevToLegendrePlanTH (ToeplitzHankelPlan (_cheb2legTH_TLC (S, mn, dims)... , dims))
220+
221+ function plan_th_cheb2leg! (:: Type{S} , mn:: NTuple{2,Int} , dims:: NTuple{2,Int} ) where {S}
222+ @assert dims == (1 ,2 )
223+ T1,L1,C1 = _cheb2legTH_TLC (S, mn, 1 )
224+ T2,L2,C2 = _cheb2legTH_TLC (S, mn, 2 )
225+ ChebyshevToLegendrePlanTH (ToeplitzHankelPlan ((T1,T2), (L1,L2), (C1,C2), dims))
226+ end
227+
228+ function plan_th_ultra2ultra! (:: Type{S} , (n,):: Tuple{Int} , λ₁, λ₂) where {S}
229+ @assert abs (λ₁- λ₂) < 1
230+ S̃ = real (S)
231+ DL = (zero (S̃): n- one (S̃)) .+ λ₂
232+ jk = 0 : half (S̃): n- 1
233+ t = zeros (S,n)
234+ t[1 : 2 : n] = Λ .(jk,λ₁- λ₂,one (S̃))[1 : 2 : n]
235+ h = Λ .(jk,λ₁,λ₂+ one (S̃))
236+ lmul! (gamma (λ₂)/ gamma (λ₁),h)
237+ C = hankel_partialchol (h)
238+ T = plan_uppertoeplitz! (lmul! (inv (gamma (λ₁- λ₂)),t), (length (t), size (C,2 )), 1 )
239+ ToeplitzHankelPlan (T, DL .* C, C)
240+ end
241+
242+ function alternatesign! (v)
243+ @inbounds for k = 2 : 2 : length (v)
244+ v[k] = - v[k]
245+ end
246+ v
247+ end
248+
249+ function plan_th_jac2jac! (:: Type{S} , (n,), α, β, γ, δ) where {S}
250+ if β == δ
251+ @assert abs (α- γ) < 1
252+ @assert α+ β > - 1
253+ jk = 0 : n- 1
254+ DL = (2 jk .+ γ .+ β .+ 1 ). * Λ .(jk,γ+ β+ 1 ,β+ 1 )
255+ t = convert (AbstractVector{S}, Λ .(jk, α- γ,1 ))
256+ h = Λ .(0 : 2 n- 2 ,α+ β+ 1 ,γ+ β+ 2 )
257+ DR = Λ .(jk,β+ 1 ,α+ β+ 1 )./ gamma (α- γ)
258+ C = hankel_partialchol (h)
259+ T = plan_uppertoeplitz! (t, (length (t), size (C,2 )), 1 )
260+ elseif α == γ
261+ jk = 0 : n- 1
262+ DL = (2 jk .+ δ .+ α .+ 1 ). * Λ .(jk,δ+ α+ 1 ,α+ 1 )
263+ h = Λ .(0 : 2 n- 2 ,α+ β+ 1 ,δ+ α+ 2 )
264+ DR = Λ .(jk,α+ 1 ,α+ β+ 1 )./ gamma (β- δ)
265+ t = alternatesign! (convert (AbstractVector{S}, Λ .(jk,β- δ,1 )))
266+ C = hankel_partialchol (h)
267+ T = plan_uppertoeplitz! (t, (length (t), size (C,2 )), 1 )
268+ else
269+ throw (ArgumentError (" Cannot create Toeplitz dot Hankel, use a sequence of plans." ))
270+ end
271+
272+ ToeplitzHankelPlan (T, DL .* C, DR .* C)
273+ end
274+
275+ for f in (:th_leg2cheb , :th_cheb2leg , :th_leg2chebu )
276+ plan = Symbol (" plan_" , f, " !" )
277+ @eval begin
278+ $ plan (:: Type{S} , mn:: NTuple{N,Int} , dims:: UnitRange ) where {N,S} = $ plan (S, mn, tuple (dims... ))
279+ $ plan (:: Type{S} , mn:: Tuple{Int} , dims:: Tuple{Int} = (1 ,)) where {S} = $ plan (S, mn, dims... )
280+ $ plan (:: Type{S} , (m,n):: NTuple{2,Int} ) where {S} = $ plan (S, (m,n), (1 ,2 ))
281+ $ plan (arr:: AbstractArray{T} , dims... ) where T = $ plan (T, size (arr), dims... )
282+ $ f (v, dims... ) = $ plan (eltype (v), size (v), dims... )* copy (v)
283+ end
284+ end
285+
286+ th_ultra2ultra (v, λ₁, λ₂, dims... ) = plan_th_ultra2ultra! (eltype (v),size (v),λ₁,λ₂, dims... )* copy (v)
287+ th_jac2jac (v, α, β, γ, δ, dims... ) = plan_th_jac2jac! (eltype (v),size (v),α,β,γ,δ, dims... )* copy (v)
0 commit comments