Skip to content

Commit dcaf8f6

Browse files
authored
Support higher order tensors in ToeplitzHankel (#233)
* Support higher order tensors in ToeplitzHankel * Update toeplitzhankel.jl * Generalise ToeplitzPlan * generalis plan_upper for matrices * consolidate plan_uppertoeplitz * 4-tensor tests * Update toeplitzplans.jl * generalise _th_applymul! * Tesnor leg2cheb * tests pass * cheb2leg tensor support * fixes * v0.15.12 * Update toeplitzplans.jl
1 parent 198d220 commit dcaf8f6

6 files changed

+215
-280
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.15.11"
3+
version = "0.15.12"
44

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

src/FastTransforms.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ using FastGaussQuadrature, FillArrays, LinearAlgebra,
88
@reexport using GenericFFT
99

1010
import Base: convert, unsafe_convert, eltype, ndims, adjoint, transpose, show,
11-
*, \, inv, length, size, view, getindex
11+
*, \, inv, length, size, view, getindex, tail, OneTo
1212

1313
import Base.GMP: Limb
1414

src/toeplitzhankel.jl

Lines changed: 43 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -16,75 +16,42 @@ so that `L[:,k] = DL*C[:,k]` and `R[:,k] = DR*C[:,k]`.
1616
This allows a Cholesky decomposition in 𝒪(K²N) operations and 𝒪(KN) storage, K = log N log ɛ⁻¹.
1717
The tuple storage allows plans applied to each dimension.
1818
"""
19-
struct ToeplitzHankelPlan{S, N, M, N1, TP<:ToeplitzPlan{S,N1}} <: Plan{S}
20-
T::NTuple{M,TP}
21-
L::NTuple{M,Matrix{S}}
22-
R::NTuple{M,Matrix{S}}
23-
tmp::Array{S,N1}
24-
dims::NTuple{M,Int}
25-
function ToeplitzHankelPlan{S,N,M,N1,TP}(T::NTuple{M,TP}, L, R, dims) where {S,TP,N,N1,M}
19+
struct ToeplitzHankelPlan{S, N, N1, LowR, TP, Dims} <: Plan{S}
20+
T::TP # A length M Vector or Tuple of ToeplitzPlan
21+
L::LowR # A length M Vector or Tuple of Matrices storing low rank factors of L
22+
R::LowR # A length M Vector or Tuple of Matrices storing low rank factors of R
23+
tmp::Array{S,N1} # A larger dimensional array to transform each scaled array all-at-once
24+
dims::Dims # A length M Vector or Tuple of Int storing the dimensions acted on
25+
function ToeplitzHankelPlan{S,N,N1,LowR,TP,Dims}(T::TP, L::LowR, R::LowR, dims) where {S,N,N1,LowR,TP,Dims}
2626
tmp = Array{S}(undef, max.(size.(T)...)...)
27-
new{S,N,M,N1,TP}(T, L, R, tmp, dims)
27+
new{S,N,N1,LowR,TP,Dims}(T, L, R, tmp, dims)
2828
end
29-
ToeplitzHankelPlan{S,N,M,N1,TP}(T::NTuple{M,TP}, L, R, dims::Int) where {S,TP,N,N1,M} =
30-
ToeplitzHankelPlan{S,N,M,N1,TP}(T, L, R, (dims,))
3129
end
3230

33-
ToeplitzHankelPlan(T::ToeplitzPlan{S,2}, L::Matrix, R::Matrix, dims=1) where S =
34-
ToeplitzHankelPlan{S, 1, 1, 2, typeof(T)}((T,), (L,), (R,), dims)
3531

36-
ToeplitzHankelPlan(T::ToeplitzPlan{S,3}, L::Matrix, R::Matrix, dims) where S =
37-
ToeplitzHankelPlan{S, 2, 1,3, typeof(T)}((T,), (L,), (R,), dims)
32+
ToeplitzHankelPlan{S,N,M}(T::TP, L::LowR, R::LowR, dims::Dims) where {S,N,M,LowR,TP,Dims} = ToeplitzHankelPlan{S,N,M,LowR,TP,Dims}(T, L, R, dims)
33+
ToeplitzHankelPlan{S,N}(T, L, R, dims) where {S,N} = ToeplitzHankelPlan{S,N,N+1}(T, L, R, dims)
34+
ToeplitzHankelPlan(T::ToeplitzPlan{S,M}, L::Matrix, R::Matrix, dims=1) where {S,M} = ToeplitzHankelPlan{S,M-1,M}((T,), (L,), (R,), dims)
3835

39-
ToeplitzHankelPlan(T::NTuple{2,TP}, L::Tuple, R::Tuple, dims) where {S,TP<:ToeplitzPlan{S,3}} =
40-
ToeplitzHankelPlan{S, 2,2,3, TP}(T, L, R, dims)
4136

42-
43-
function *(P::ToeplitzHankelPlan{<:Any,1}, v::AbstractVector)
44-
(R,),(L,),(T,),tmp = P.R,P.L,P.T,P.tmp
45-
tmp .= R .* v
46-
T * tmp
47-
tmp .= L .* tmp
48-
sum!(v, tmp)
49-
end
50-
51-
function _th_applymul1!(v, T, L, R, tmp)
52-
N = size(R,2)
53-
m,n = size(v)
54-
tmp[1:m,1:n,1:N] .= reshape(R,size(R,1),1,N) .* v
55-
T * view(tmp,1:m,1:n,1:N)
56-
view(tmp,1:m,1:n,1:N) .*= reshape(L,size(L,1),1,N)
57-
sum!(v, view(tmp,1:m,1:n,1:N))
58-
end
59-
60-
function _th_applymul2!(v, T, L, R, tmp)
61-
N = size(R,2)
62-
m,n = size(v)
63-
tmp[1:m,1:n,1:N] .= reshape(R,1,size(R,1),N) .* v
64-
T * view(tmp,1:m,1:n,1:N)
65-
view(tmp,1:m,1:n,1:N) .*= reshape(L,1,size(L,1),N)
66-
sum!(v, view(tmp,1:m,1:n,1:N))
37+
_reshape_broadcast(d, R, ::Val{N}, M) where N = reshape(R,ntuple(k -> k == d ? size(R,1) : 1, Val(N))...,M)
38+
function _th_applymul!(d, v::AbstractArray{<:Any,N}, T, L, R, tmp) where N
39+
M = size(R,2)
40+
ax = (axes(v)..., OneTo(M))
41+
tmp[ax...] .= _reshape_broadcast(d, R, Val(N), M) .* v
42+
T * view(tmp, ax...)
43+
view(tmp,ax...) .*= _reshape_broadcast(d, L, Val(N), M)
44+
sum!(v, view(tmp,ax...))
6745
end
6846

6947

70-
function *(P::ToeplitzHankelPlan{<:Any,2,1}, v::AbstractMatrix)
71-
(R,),(L,),(T,),tmp = P.R,P.L,P.T,P.tmp
72-
if P.dims == (1,)
73-
_th_applymul1!(v, T, L, R, tmp)
74-
else
75-
_th_applymul2!(v, T, L, R, tmp)
48+
function *(P::ToeplitzHankelPlan{<:Any,N}, v::AbstractArray{<:Any,N}) where N
49+
for (R,L,T,d) in zip(P.R,P.L,P.T,P.dims)
50+
_th_applymul!(d, v, T, L, R, P.tmp)
7651
end
7752
v
7853
end
7954

80-
function *(P::ToeplitzHankelPlan{<:Any,2,2}, v::AbstractMatrix)
81-
(R1,R2),(L1,L2),(T1,T2),tmp = P.R,P.L,P.T,P.tmp
82-
83-
_th_applymul1!(v, T1, L1, R1, tmp)
84-
_th_applymul2!(v, T2, L2, R2, tmp)
85-
86-
v
87-
end
8855

8956
# partial cholesky for a Hankel matrix
9057

@@ -166,9 +133,9 @@ function *(P::ChebyshevToLegendrePlanTH, v::AbstractVector{S}) where S
166133
v
167134
end
168135

169-
function _cheb2leg_rescale1!(V::AbstractMatrix{S}) where S
170-
m,n = size(V)
171-
for j = 1:n
136+
function _cheb2leg_rescale1!(V::AbstractArray{S}) where S
137+
m = size(V,1)
138+
for j = CartesianIndices(tail(axes(V)))
172139
ret = zero(S)
173140
@inbounds for k = 1:2:m
174141
ret += -V[k,j]/(k*(k-2))
@@ -178,24 +145,15 @@ function _cheb2leg_rescale1!(V::AbstractMatrix{S}) where S
178145
V
179146
end
180147

148+
_dropfirstdim(d::Int) = ()
149+
_dropfirstdim(d::Int, m, szs...) = ((d == 1 ? 2 : 1):m, _dropfirstdim(d-1, szs...)...)
181150

182-
function *(P::ChebyshevToLegendrePlanTH, V::AbstractMatrix)
151+
function *(P::ChebyshevToLegendrePlanTH, V::AbstractArray{<:Any,N}) where N
183152
m,n = size(V)
184-
dims = P.toeplitzhankel.dims
185-
if dims == (1,)
186-
_cheb2leg_rescale1!(V)
187-
P.toeplitzhankel*view(V,2:m,:)
188-
elseif dims == (2,)
189-
_cheb2leg_rescale1!(transpose(V))
190-
P.toeplitzhankel*view(V,:,2:n)
191-
else
192-
@assert dims == (1,2)
193-
(R1,R2),(L1,L2),(T1,T2),tmp = P.toeplitzhankel.R,P.toeplitzhankel.L,P.toeplitzhankel.T,P.toeplitzhankel.tmp
194-
195-
_cheb2leg_rescale1!(V)
196-
_th_applymul1!(view(V,2:m,:), T1, L1, R1, tmp)
197-
_cheb2leg_rescale1!(transpose(V))
198-
_th_applymul2!(view(V,:,2:n), T2, L2, R2, tmp)
153+
tmp = P.toeplitzhankel.tmp
154+
for (d,R,L,T) in zip(P.toeplitzhankel.dims,P.toeplitzhankel.R,P.toeplitzhankel.L,P.toeplitzhankel.T)
155+
_cheb2leg_rescale1!(PermutedDimsArray(V, _permfirst(d, N)))
156+
_th_applymul!(d, view(V, _dropfirstdim(d, size(V)...)...), T, L, R, tmp)
199157
end
200158
V
201159
end
@@ -226,18 +184,14 @@ function _leg2chebuTH_TLC(::Type{S}, mn, d) where {S}
226184
(T, (1:n) .* C, C)
227185
end
228186

229-
230187
for f in (:leg2cheb, :leg2chebu)
231188
plan = Symbol("plan_th_", f, "!")
232189
TLC = Symbol("_", f, "TH_TLC")
233190
@eval begin
234-
$plan(::Type{S}, mn::Tuple, dims::Int) where {S} = ToeplitzHankelPlan($TLC(S, mn, dims)..., dims)
235-
236-
function $plan(::Type{S}, mn::NTuple{2,Int}, dims::NTuple{2,Int}) where {S}
237-
@assert dims == (1,2)
238-
T1,L1,C1 = $TLC(S, mn, 1)
239-
T2,L2,C2 = $TLC(S, mn, 2)
240-
ToeplitzHankelPlan((T1,T2), (L1,L2), (C1,C2), dims)
191+
$plan(::Type{S}, mn::NTuple{N,Int}, dims::Int) where {S,N} = ToeplitzHankelPlan($TLC(S, mn, dims)..., dims)
192+
function $plan(::Type{S}, mn::NTuple{N,Int}, dims) where {S,N}
193+
TLCs = $TLC.(S, Ref(mn), dims)
194+
ToeplitzHankelPlan{S,N}(map(first, TLCs), map(TLC -> TLC[2], TLCs), map(last, TLCs), dims)
241195
end
242196
end
243197
end
@@ -265,13 +219,11 @@ function _cheb2legTH_TLC(::Type{S}, mn, d) where S
265219
T, DL .* C, DR .* C
266220
end
267221

268-
plan_th_cheb2leg!(::Type{S}, mn::Tuple, dims::Int) where {S} = ChebyshevToLegendrePlanTH(ToeplitzHankelPlan(_cheb2legTH_TLC(S, mn, dims)..., dims))
222+
plan_th_cheb2leg!(::Type{S}, mn::NTuple{N,Int}, dims::Int) where {S,N} = ChebyshevToLegendrePlanTH(ToeplitzHankelPlan(_cheb2legTH_TLC(S, mn, dims)..., dims))
269223

270-
function plan_th_cheb2leg!(::Type{S}, mn::NTuple{2,Int}, dims::NTuple{2,Int}) where {S}
271-
@assert dims == (1,2)
272-
T1,L1,C1 = _cheb2legTH_TLC(S, mn, 1)
273-
T2,L2,C2 = _cheb2legTH_TLC(S, mn, 2)
274-
ChebyshevToLegendrePlanTH(ToeplitzHankelPlan((T1,T2), (L1,L2), (C1,C2), dims))
224+
function plan_th_cheb2leg!(::Type{S}, mn::NTuple{N,Int}, dims) where {S,N}
225+
TLCs = _cheb2legTH_TLC.(S, Ref(mn), dims)
226+
ChebyshevToLegendrePlanTH(ToeplitzHankelPlan{S,N}(map(first, TLCs), map(TLC -> TLC[2], TLCs), map(last, TLCs), dims))
275227
end
276228

277229

@@ -337,7 +289,7 @@ _good_plan_th_ultra2ultra!(::Type{S}, mn, λ₁, λ₂, dims::Int) where S = Toe
337289
function _good_plan_th_ultra2ultra!(::Type{S}, mn::NTuple{2,Int}, λ₁, λ₂, dims::NTuple{2,Int}) where S
338290
T1,L1,C1 = _ultra2ultraTH_TLC(S, mn, λ₁, λ₂, 1)
339291
T2,L2,C2 = _ultra2ultraTH_TLC(S, mn, λ₁, λ₂, 2)
340-
ToeplitzHankelPlan((T1,T2), (L1,L2), (C1,C2), dims)
292+
ToeplitzHankelPlan{S,2}((T1,T2), (L1,L2), (C1,C2), dims)
341293
end
342294

343295

@@ -515,7 +467,7 @@ _good_plan_th_jac2jac!(::Type{S}, mn, α, β, γ, δ, dims::Int) where S = Toepl
515467
function _good_plan_th_jac2jac!(::Type{S}, mn::NTuple{2,Int}, α, β, γ, δ, dims::NTuple{2,Int}) where S
516468
T1,L1,C1 = _jac2jacTH_TLC(S, mn, α, β, γ, δ, 1)
517469
T2,L2,C2 = _jac2jacTH_TLC(S, mn, α, β, γ, δ, 2)
518-
ToeplitzHankelPlan((T1,T2), (L1,L2), (C1,C2), dims)
470+
ToeplitzHankelPlan{S,2}((T1,T2), (L1,L2), (C1,C2), dims)
519471
end
520472

521473

@@ -685,10 +637,8 @@ end
685637
for f in (:th_leg2cheb, :th_cheb2leg, :th_leg2chebu)
686638
plan = Symbol("plan_", f, "!")
687639
@eval begin
688-
$plan(::Type{S}, mn::NTuple{N,Int}, dims::UnitRange) where {N,S} = $plan(S, mn, tuple(dims...))
689-
$plan(::Type{S}, mn::Tuple{Int}, dims::Tuple{Int}=(1,)) where {S} = $plan(S, mn, dims...)
690-
$plan(::Type{S}, (m,n)::NTuple{2,Int}) where {S} = $plan(S, (m,n), (1,2))
691640
$plan(arr::AbstractArray{T}, dims...) where T = $plan(T, size(arr), dims...)
641+
$plan(::Type{S}, mn::NTuple{N,Int}) where {S,N} = $plan(S, mn, ntuple(identity,Val(N)))
692642
$f(v, dims...) = $plan(eltype(v), size(v), dims...)*copy(v)
693643
end
694644
end

0 commit comments

Comments
 (0)