Skip to content

Commit 3729d4b

Browse files
authored
factorize -> plan_transform for Fourier (#117)
* factorize -> plan_transform for Fourier * transforms for annuli * Laplace's eqn * Add more examples, move MulPlan
1 parent bb63d69 commit 3729d4b

File tree

6 files changed

+176
-149
lines changed

6 files changed

+176
-149
lines changed

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ ArrayLayouts = "0.8"
2929
BandedMatrices = "0.17"
3030
BlockArrays = "0.16.9"
3131
BlockBandedMatrices = "0.11.6"
32-
ContinuumArrays = "0.12.1"
32+
ContinuumArrays = "0.12.3"
3333
DomainSets = "0.5.6, 0.6"
3434
FFTW = "1.1"
3535
FastGaussQuadrature = "0.4.3, 0.5"
36-
FastTransforms = "0.14.4"
36+
FastTransforms = "0.14.9"
3737
FillArrays = "0.13"
3838
HypergeometricFunctions = "0.3.4"
3939
InfiniteArrays = "0.12.3"

examples/annulipdes.jl

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,89 @@
11
using ClassicalOrthogonalPolynomials, Plots
2+
plotly()
23

3-
ρ = 0.5; T = chebyshevt..1); U = chebyshevu(T); C = ultraspherical(2, ρ..1); r = axes(T,1); D = Derivative(r);
4+
###
5+
# Laplace's equation
6+
###
7+
8+
ρ = 0.5
9+
T,C,F = chebyshevt..1),ultraspherical(2, ρ..1),Fourier()
10+
r = axes(T,1)
11+
D = Derivative(r)
412

513
L = C \ (r.^2 .* (D^2 * T)) + C \ (r .* (D * T)) # r^2 * ∂^2 + r*∂
614
M = C\T # Identity
15+
R = C \ (r .* C) # mult by r
16+
17+
uᵨ = transform(F, θ -> exp(cos(θ)+sin-1)))
18+
u₁ = transform(F, θ -> cos(100cos(θ)-sin+1)-1))
19+
20+
n = 300
21+
X = zeros(n,n)
22+
23+
for j = 1:n
24+
m = j ÷ 2
25+
Δₘ = L - m^2*M # r^2 * Laplacian for exp(im*m*θ)*u(r), i.e. (r^2 * ∂^2 + r*∂ - m^2*I)*u
26+
d = [T[[begin,end],:]; Δₘ] \ [uᵨ[j]; u₁[j]; zeros(∞)]
27+
X[:,j] .= d[1:n]
28+
end
729

8-
m = 2
9-
Δₘ = L - m^2*M # r^2 * Laplacian for exp(im*m*θ)*u(r), i.e. (r^2 * ∂^2 + r*∂ - m^2*I)*u
30+
𝐫,𝛉 = ClassicalOrthogonalPolynomials.grid(T, n),ClassicalOrthogonalPolynomials.grid(F, n)
31+
PT,PF = plan_transform(T, (n,n), 1),plan_transform(F, (n,n), 2)
32+
U = PT \ (PF \ X); U = [U U[:,1]]
1033

34+
𝐱 = 𝐫 .* cos.([𝛉; 2π]')
35+
𝐲 = 𝐫 .* sin.([𝛉; 2π]')
36+
surface(𝐱, 𝐲, U; zlims=(-2,2))
1137

1238

13-
# Poisson solve
14-
c = C \ exp.(r)
15-
R = C \ (r .* C)
39+
###
40+
# Helmholtz equation
41+
###
1642

43+
uᵨ = transform(F, θ -> exp(cos(θ)+sin-1)))
44+
u₁ = transform(F, θ -> cos(cos(θ)-sin+1)-1))
1745

18-
d = [T[[begin,end],:];
19-
Δₘ] \ [1; 2; R^2 * c]
2046

21-
plot(T*d)
47+
k = 100
2248

49+
for j = 1:n
50+
m = j ÷ 2
51+
Δₘ = L - m^2*M + k^2 * R^2*M
52+
d = [T[[begin,end],:]; Δₘ] \ [uᵨ[j]; u₁[j]; zeros(∞)]
53+
X[:,j] .= d[1:n]
54+
end
2355

24-
# Helmholtz
56+
U = PT \ (PF \ X); U = [U U[:,1]]
57+
surface(𝐱, 𝐲, U; zlims=(-10,10))
2558

2659

27-
Q = R^2 * M # r^2, needed for Helmholtz (Δ + k^2)*u = f
60+
###
61+
# Poisson
62+
###
2863

29-
k = 5 # f
64+
n = 1000
65+
𝐫,𝛉 = ClassicalOrthogonalPolynomials.grid(T, n),ClassicalOrthogonalPolynomials.grid(F, n)
66+
PT,PF = plan_transform(T, (n,n), 1),plan_transform(F, (n,n), 2)
67+
f = (x,y) -> exp(-4000((x-0.8)^2 + (y-0.1)^2))
68+
𝐱 = 𝐫 .* cos.(𝛉')
69+
𝐲 = 𝐫 .* sin.(𝛉')
3070

31-
d = [T[[begin,end],:];
32-
Δₘ+k^2*Q] \ [1; 2; R^2 * c]
71+
F = PT * (PF * f.(𝐱, 𝐲))
3372

3473

35-
# transform
3674

37-
f = (r,θ) -> exp(r*cos(θ))
75+
X = zeros(n+2, n+2)
3876

39-
n = 10 # create a 10 x 10 transform
77+
# multiply RHS by r^2 and convert to C
78+
S = (R^2*M)[1:n,1:n]
4079

41-
F = Fourier()
42-
𝐫,𝛉 = grid(T, n),grid(F, n)
80+
for j = 1:n
81+
m = j ÷ 2
82+
Δₘ = L - m^2*M
83+
X[:,j] = [T[[begin,end],:]; Δₘ][1:n+2,1:n+2] \ [0; 0; S*F[:,j]]
84+
end
4385

44-
transform(T, transform(F, f.(𝐫, 𝛉'); dims=2); dims=1)
86+
U = PT \ (PF \ X[1:n,1:n]); U = [U U[:,1]]
87+
𝐱 = 𝐫 .* cos.([𝛉; 2π]')
88+
𝐲 = 𝐫 .* sin.([𝛉; 2π]')
89+
surface(𝐱, 𝐲, U)

src/ClassicalOrthogonalPolynomials.jl

Lines changed: 6 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ import ContinuumArrays: Basis, Weight, basis, @simplify, Identity, AbstractAffin
3838
inbounds_getindex, grid, plotgrid, _plotgrid, _grid, transform_ldiv, TransformFactorization, QInfAxes, broadcastbasis, ExpansionLayout, basismap,
3939
AffineQuasiVector, AffineMap, WeightLayout, AbstractWeightedBasisLayout, WeightedBasisLayout, WeightedBasisLayouts, demap, AbstractBasisLayout, BasisLayout,
4040
checkpoints, weight, unweighted, MappedBasisLayouts, __sum, invmap, plan_ldiv, layout_broadcasted, MappedBasisLayout, SubBasisLayout, _broadcastbasis,
41-
plan_transform, plan_grid_transform, MAX_PLOT_POINTS
41+
plan_transform, plan_grid_transform, MAX_PLOT_POINTS, MulPlan
4242
import FastTransforms: Λ, forwardrecurrence, forwardrecurrence!, _forwardrecurrence!, clenshaw, clenshaw!,
43-
_forwardrecurrence_next, _clenshaw_next, check_clenshaw_recurrences, ChebyshevGrid, chebyshevpoints, Plan
43+
_forwardrecurrence_next, _clenshaw_next, check_clenshaw_recurrences, ChebyshevGrid, chebyshevpoints, Plan, ScaledPlan
4444

4545
import FastGaussQuadrature: jacobimoment
4646

@@ -262,68 +262,14 @@ function golubwelsch(V::SubQuasiArray)
262262
x,w
263263
end
264264

265-
"""
266-
MulPlan(matrix, dims)
267-
268-
Takes a matrix and supports it applied to different dimensions.
269-
"""
270-
struct MulPlan{T, Fact, Dims} # <: Plan{T} We don't depend on AbstractFFTs
271-
matrix::Fact
272-
dims::Dims
273-
end
274-
275-
MulPlan(fact, dims) = MulPlan{eltype(fact), typeof(fact), typeof(dims)}(fact, dims)
276-
277-
function *(P::MulPlan{<:Any,<:Any,Int}, x::AbstractVector)
278-
@assert P.dims == 1
279-
P.matrix * x
280-
end
281-
282-
function *(P::MulPlan{<:Any,<:Any,Int}, X::AbstractMatrix)
283-
if P.dims == 1
284-
P.matrix * X
285-
else
286-
@assert P.dims == 2
287-
permutedims(P.matrix * permutedims(X))
288-
end
289-
end
290-
291-
function *(P::MulPlan{<:Any,<:Any,Int}, X::AbstractArray{<:Any,3})
292-
Y = similar(X)
293-
if P.dims == 1
294-
for j in axes(X,3)
295-
Y[:,:,j] = P.matrix * X[:,:,j]
296-
end
297-
elseif P.dims == 2
298-
for k in axes(X,1)
299-
Y[k,:,:] = P.matrix * X[k,:,:]
300-
end
301-
else
302-
@assert P.dims == 3
303-
for k in axes(X,1), j in axes(X,2)
304-
Y[k,j,:] = P.matrix * X[k,j,:]
305-
end
306-
end
307-
Y
308-
end
309-
310-
function *(P::MulPlan, X::AbstractArray)
311-
for d in P.dims
312-
X = MulPlan(P.matrix, d) * X
313-
end
314-
X
315-
end
316-
317-
*(A::AbstractMatrix, P::MulPlan) = MulPlan(A*P.matrix, P.dims)
318-
319265

320266
function plan_grid_transform(Q::Normalized, szs::NTuple{N,Int}, dims=1:N) where N
321267
L = Q[:,OneTo(szs[1])]
322268
x,w = golubwelsch(L)
323269
x, MulPlan(L[x,:]'*Diagonal(w), dims)
324270
end
325271

326-
function plan_grid_transform(P::OrthogonalPolynomial, szs::NTuple{N,Int}, dims=1:N) where N
272+
function plan_grid_transform(::AbstractOPLayout, P, szs::NTuple{N,Int}, dims=1:N) where N
327273
Q = Normalized(P)
328274
x, A = plan_grid_transform(Q, szs, dims...)
329275
n = szs[1]
@@ -332,6 +278,9 @@ function plan_grid_transform(P::OrthogonalPolynomial, szs::NTuple{N,Int}, dims=1
332278
end
333279

334280

281+
plan_grid_transform(::MappedOPLayout, L, szs::NTuple{N,Int}, dims=1:N) where N =
282+
plan_grid_transform(MappedBasisLayout(), L, szs, dims)
283+
335284
function \(A::SubQuasiArray{<:Any,2,<:OrthogonalPolynomial}, B::SubQuasiArray{<:Any,2,<:OrthogonalPolynomial})
336285
axes(A,1) == axes(B,1) || throw(DimensionMismatch())
337286
_,jA = parentindices(A)

src/classical/fourier.jl

Lines changed: 56 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ struct ShuffledR2HC{T,Pl<:Plan} <: AbstractShuffledPlan{T}
3737
plan::Pl
3838
end
3939

40+
"""
41+
Gives a shuffled version of the real IFFT, with order
42+
1,sin(θ),cos(θ),sin(2θ)…
43+
"""
44+
struct ShuffledIR2HC{T,Pl<:Plan} <: AbstractShuffledPlan{T}
45+
plan::Pl
46+
end
47+
4048
"""
4149
Gives a shuffled version of the FFT, with order
4250
1,sin(θ),cos(θ),sin(2θ)…
@@ -45,6 +53,14 @@ struct ShuffledFFT{T,Pl<:Plan} <: AbstractShuffledPlan{T}
4553
plan::Pl
4654
end
4755

56+
"""
57+
Gives a shuffled version of the IFFT, with order
58+
1,sin(θ),cos(θ),sin(2θ)…
59+
"""
60+
struct ShuffledIFFT{T,Pl<:Plan} <: AbstractShuffledPlan{T}
61+
plan::Pl
62+
end
63+
4864
size(F::AbstractShuffledPlan, k) = size(F.plan,k)
4965
size(F::AbstractShuffledPlan) = size(F.plan)
5066

@@ -54,84 +70,70 @@ ShuffledR2HC{T}(n, d...) where T = ShuffledR2HC{T}(FFTW.plan_r2r(Array{T}(undef,
5470
ShuffledFFT{T}(p::Pl) where {T,Pl<:Plan} = ShuffledFFT{T,Pl}(p)
5571
ShuffledFFT{T}(n, d...) where T = ShuffledFFT{T}(FFTW.plan_fft(Array{T}(undef, n), d...))
5672

73+
ShuffledIFFT{T}(p::Pl) where {T,Pl<:Plan} = ShuffledIFFT{T,Pl}(p)
74+
ShuffledIR2HC{T}(p::Pl) where {T,Pl<:Plan} = ShuffledIR2HC{T,Pl}(p)
5775

58-
function _shuffledR2HC_postscale!(_, ret::AbstractVector{T}) where T
76+
inv(P::ShuffledR2HC{T}) where T = ShuffledIR2HC{T}(inv(P.plan))
77+
inv(P::ShuffledFFT{T}) where T = ShuffledIFFT{T}(inv(P.plan))
78+
79+
80+
_shuffled_prescale!(_, ret::AbstractVector) = ret
81+
_shuffled_postscale!(_, ret::AbstractVector) = ret
82+
83+
function _shuffled_postscale!(::ShuffledR2HC, ret::AbstractVector{T}) where T
5984
n = length(ret)
6085
lmul!(convert(T,2)/n, ret)
6186
ret[1] /= 2
6287
iseven(n) && (ret[n÷2+1] /= 2)
6388
negateeven!(reverseeven!(interlace!(ret,1)))
6489
end
6590

66-
function _shuffledR2HC_postscale!(d::Number, ret::AbstractMatrix{T}) where T
67-
if isone(d)
68-
n = size(ret,1)
69-
lmul!(convert(T,2)/n, ret)
70-
ldiv!(2, view(ret,1,:))
71-
iseven(n) && ldiv!(2, view(ret,n÷2+1,:))
72-
for j in axes(ret,2)
73-
negateeven!(reverseeven!(interlace!(view(ret,:,j),1)))
74-
end
75-
else
76-
n = size(ret,2)
77-
lmul!(convert(T,2)/n, ret)
78-
ldiv!(2, view(ret,:,1))
79-
iseven(n) && ldiv!(2, view(ret,:,n÷2+1))
80-
for k in axes(ret,1)
81-
negateeven!(reverseeven!(interlace!(view(ret,k,:),1)))
82-
end
83-
end
84-
ret
91+
function _shuffled_prescale!(::ShuffledIR2HC, ret::AbstractVector{T}) where T
92+
n = length(ret)
93+
reverseeven!(negateeven!(ret))
94+
ret .= [ret[1:2:end]; ret[2:2:end]] # todo: non-allocating
95+
iseven(n) && (ret[n÷2+1] *= 2)
96+
ret[1] *= 2
97+
lmul!(convert(T,n)/2, ret)
8598
end
8699

87-
function _shuffledFFT_postscale!(_, ret::AbstractVector{T}) where T
100+
function _shuffled_postscale!(::ShuffledFFT, ret::AbstractVector{T}) where T
88101
n = length(ret)
89102
cfs = lmul!(inv(convert(T,n)), ret)
90103
reverseeven!(interlace!(cfs,1))
91104
end
92105

93-
function _shuffledFFT_postscale!(d::Number, ret::AbstractMatrix{T}) where T
94-
if isone(d)
95-
n = size(ret,1)
96-
lmul!(inv(convert(T,n)), ret)
97-
for j in axes(ret,2)
98-
reverseeven!(interlace!(view(ret,:,j),1))
99-
end
100-
else
101-
n = size(ret,2)
102-
lmul!(inv(convert(T,n)), ret)
103-
for k in axes(ret,1)
104-
reverseeven!(interlace!(view(ret,k,:),1))
106+
_region(F::Plan) = F.region
107+
_region(F::ScaledPlan) = F.p.region
108+
109+
for func in (:_shuffled_postscale!, :_shuffled_prescale!)
110+
@eval function $func(F::AbstractShuffledPlan, ret::AbstractMatrix{T}) where T
111+
d = _region(F.plan)
112+
if isone(d)
113+
for j in axes(ret,2)
114+
$func(F, view(ret,:,j))
115+
end
116+
else
117+
@assert d == 2
118+
for k in axes(ret,1)
119+
$func(F, view(ret,k,:))
120+
end
105121
end
122+
ret
106123
end
107-
ret
108124
end
109125

110-
function mul!(ret::AbstractArray{T}, F::ShuffledR2HC{T}, b::AbstractArray) where T
111-
mul!(ret, F.plan, convert(Array{T}, b))
112-
_shuffledR2HC_postscale!(F.plan.region, ret)
126+
function mul!(ret::AbstractArray{T}, F::AbstractShuffledPlan{T}, bin::AbstractArray) where T
127+
b = _shuffled_prescale!(F, Array{T}(bin))
128+
mul!(ret, F.plan, b)
129+
_shuffled_postscale!(F, ret)
113130
end
114131

115-
function mul!(ret::AbstractArray{T}, F::ShuffledFFT{T}, b::AbstractArray) where T
116-
mul!(ret, F.plan, convert(Array{T}, b))
117-
_shuffledFFT_postscale!(F.plan.region, ret)
118-
end
119132

120133
*(F::AbstractShuffledPlan{T}, b::AbstractVecOrMat) where T = mul!(similar(b, T), F, b)
121134

122-
factorize(L::SubQuasiArray{T,2,<:Fourier,<:Tuple{<:Inclusion,<:OneTo}}) where T =
123-
TransformFactorization(grid(L), ShuffledR2HC{T}(size(L,2)))
124-
factorize(L::SubQuasiArray{T,2,<:Fourier,<:Tuple{<:Inclusion,<:OneTo}}, d) where T =
125-
TransformFactorization(grid(L), ShuffledR2HC{T}((size(L,2),d),1))
126-
127-
factorize(L::SubQuasiArray{T,2,<:Laurent,<:Tuple{<:Inclusion,<:OneTo}}) where T =
128-
TransformFactorization(grid(L), ShuffledFFT{T}(size(L,2)))
129-
factorize(L::SubQuasiArray{T,2,<:Laurent,<:Tuple{<:Inclusion,<:OneTo}}, d) where T =
130-
TransformFactorization(grid(L), ShuffledFFT{T}((size(L,2),d),1))
131-
132-
133-
factorize(L::SubQuasiArray{T,2,<:AbstractFourier,<:Tuple{<:Inclusion,<:BlockSlice}},d...) where T =
134-
ProjectionFactorization(factorize(parent(L)[:,OneTo(size(L,2))],d...),parentindices(L)[2])
135+
plan_grid_transform(F::Fourier{T}, szs::NTuple{N,Int}, dims...) where {T,N} = grid(F, szs[1]), ShuffledR2HC{T}(szs, dims...)
136+
plan_grid_transform(F::Laurent{T}, szs::NTuple{N,Int}, dims...) where {T,N} = grid(F, szs[1]), ShuffledFFT{T}(szs, dims...)
135137

136138
import BlockBandedMatrices: _BlockSkylineMatrix
137139

0 commit comments

Comments
 (0)