Skip to content

Commit ea05444

Browse files
synthesis and analysis of Fourier series on S^2 with grids that match spherefun
CC @Hadrien-Montanelli
1 parent 64d7c80 commit ea05444

File tree

2 files changed

+181
-2
lines changed

2 files changed

+181
-2
lines changed

src/SphericalHarmonics/synthesisanalysis.jl

Lines changed: 129 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@ function plan_synthesis(A::Matrix{T}) where T<:fftwNumber
1515
SynthesisPlan(planθ, planφ, C, zeros(T, n))
1616
end
1717

18+
function plan_synthesis2(A::Matrix{T}) where T<:fftwNumber
19+
m, n = size(A)
20+
x = FFTW.FakeArray(T, m)
21+
y = FFTW.FakeArray(T, n)
22+
planθ = FFTW.plan_r2r!(x, FFTW.REDFT00), FFTW.plan_r2r!(FFTW.FakeArray(T, m-2), FFTW.RODFT00)
23+
planφ = FFTW.plan_r2r!(y, FFTW.HC2R)
24+
C = ColumnPermutation(vcat(1:2:n, 2:2:n))
25+
SynthesisPlan(planθ, planφ, C, zeros(T, n))
26+
end
27+
1828
struct AnalysisPlan{T, P1, P2}
1929
planθ::P1
2030
planφ::P2
@@ -32,7 +42,17 @@ function plan_analysis(A::Matrix{T}) where T<:fftwNumber
3242
AnalysisPlan(planθ, planφ, C, zeros(T, n))
3343
end
3444

35-
function Base.A_mul_B!(Y::Matrix{T}, P::SynthesisPlan{T}, X::Matrix{T}) where T
45+
function plan_analysis2(A::Matrix{T}) where T<:fftwNumber
46+
m, n = size(A)
47+
x = FFTW.FakeArray(T, m)
48+
y = FFTW.FakeArray(T, n)
49+
planθ = FFTW.plan_r2r!(x, FFTW.REDFT00), FFTW.plan_r2r!(FFTW.FakeArray(T, m-2), FFTW.RODFT00)
50+
planφ = FFTW.plan_r2r!(y, FFTW.R2HC)
51+
C = ColumnPermutation(vcat(1:2:n, 2:2:n))
52+
AnalysisPlan(planθ, planφ, C, zeros(T, n))
53+
end
54+
55+
function Base.A_mul_B!(Y::Matrix{T}, P::SynthesisPlan{T, Tuple{r2rFFTWPlan{T,(REDFT01,),true,1}, r2rFFTWPlan{T,(RODFT01,),true,1}}}, X::Matrix{T}) where T
3656
M, N = size(X)
3757

3858
# Column synthesis
@@ -73,7 +93,52 @@ function Base.A_mul_B!(Y::Matrix{T}, P::SynthesisPlan{T}, X::Matrix{T}) where T
7393
Y
7494
end
7595

76-
function Base.A_mul_B!(Y::Matrix{T}, P::AnalysisPlan{T}, X::Matrix{T}) where T
96+
function Base.A_mul_B!(Y::Matrix{T}, P::SynthesisPlan{T, Tuple{r2rFFTWPlan{T,(REDFT00,),true,1}, r2rFFTWPlan{T,(RODFT00,),true,1}}}, X::Matrix{T}) where T
97+
M, N = size(X)
98+
99+
# Column synthesis
100+
PCe = P.planθ[1]
101+
PCo = P.planθ[2]
102+
103+
X[1] *= two(T)
104+
X[M,1] *= two(T)
105+
A_mul_B_col_J!(Y, PCe, X, 1)
106+
X[1] *= half(T)
107+
X[M,1] *= half(T)
108+
109+
for J = 2:4:N
110+
A_mul_B_col_J!(Y, PCo, X, J, false)
111+
J < N && A_mul_B_col_J!(Y, PCo, X, J+1, false)
112+
end
113+
for J = 4:4:N
114+
X[1,J] *= two(T)
115+
X[M,J] *= two(T)
116+
J < N && (X[1,J+1] *= two(T); X[M,J+1] *= two(T))
117+
A_mul_B_col_J!(Y, PCe, X, J)
118+
J < N && A_mul_B_col_J!(Y, PCe, X, J+1)
119+
X[1,J] *= half(T)
120+
X[M,J] *= half(T)
121+
J < N && (X[1,J+1] *= half(T); X[M,J+1] *= half(T))
122+
end
123+
scale!(half(T), Y)
124+
125+
# Row synthesis
126+
scale!(inv(sqrt(π)), Y)
127+
invsqrttwo = inv(sqrt(2))
128+
@inbounds for i = 1:M Y[i] *= invsqrttwo end
129+
130+
temp = P.temp
131+
planφ = P.planφ
132+
C = P.C
133+
for I = 1:M
134+
copy_row_I!(temp, Y, I)
135+
row_synthesis!(planφ, C, temp)
136+
copy_row_I!(Y, temp, I)
137+
end
138+
Y
139+
end
140+
141+
function Base.A_mul_B!(Y::Matrix{T}, P::AnalysisPlan{T, Tuple{r2rFFTWPlan{T,(REDFT10,),true,1}, r2rFFTWPlan{T,(RODFT10,),true,1}}}, X::Matrix{T}) where T
77142
M, N = size(X)
78143

79144
# Row analysis
@@ -109,6 +174,45 @@ function Base.A_mul_B!(Y::Matrix{T}, P::AnalysisPlan{T}, X::Matrix{T}) where T
109174
Y
110175
end
111176

177+
function Base.A_mul_B!(Y::Matrix{T}, P::AnalysisPlan{T, Tuple{r2rFFTWPlan{T,(REDFT00,),true,1}, r2rFFTWPlan{T,(RODFT00,),true,1}}}, X::Matrix{T}) where T
178+
M, N = size(X)
179+
180+
# Row analysis
181+
temp = P.temp
182+
planφ = P.planφ
183+
C = P.C
184+
for I = 1:M
185+
copy_row_I!(temp, X, I)
186+
row_analysis!(planφ, C, temp)
187+
copy_row_I!(Y, temp, I)
188+
end
189+
190+
# Column analysis
191+
PCe = P.planθ[1]
192+
PCo = P.planθ[2]
193+
194+
A_mul_B_col_J!(Y, PCe, Y, 1)
195+
Y[1] *= half(T)
196+
Y[M, 1] *= half(T)
197+
for J = 2:4:N
198+
A_mul_B_col_J!(Y, PCo, Y, J, true)
199+
J < N && A_mul_B_col_J!(Y, PCo, Y, J+1, true)
200+
Y[M-1,J] = zero(T)
201+
J < N && (Y[M-1,J+1] = zero(T))
202+
end
203+
for J = 4:4:N
204+
A_mul_B_col_J!(Y, PCe, Y, J)
205+
J < N && A_mul_B_col_J!(Y, PCe, Y, J+1)
206+
Y[1,J] *= half(T)
207+
Y[M,J] *= half(T)
208+
J < N && (Y[1,J+1] *= half(T); Y[M,J+1] *= half(T))
209+
end
210+
scale!(sqrt(π)*inv(T(M-1)), Y)
211+
sqrttwo = sqrt(2)
212+
@inbounds for i = 1:M Y[i] *= sqrttwo end
213+
214+
Y
215+
end
112216

113217

114218

@@ -185,3 +289,26 @@ function unsafe_execute_col_J!(plan::r2rFFTWPlan{T}, X::Matrix{T}, Y::Matrix{T},
185289
M = size(X, 1)
186290
ccall((:fftwf_execute_r2r, libfftwf), Void, (PlanPtr, Ptr{T}, Ptr{T}), plan, pointer(X, M*(J-1)+1), pointer(Y, M*(J-1)+1))
187291
end
292+
293+
function A_mul_B_col_J!(Y::Matrix{T}, P::r2rFFTWPlan{T}, X::Matrix{T}, J::Int, TF::Bool) where T
294+
unsafe_execute_col_J!(P, X, Y, J, TF)
295+
return Y
296+
end
297+
298+
function unsafe_execute_col_J!(plan::r2rFFTWPlan{T}, X::Matrix{T}, Y::Matrix{T}, J::Int, TF::Bool) where T<:fftwDouble
299+
M = size(X, 1)
300+
if TF
301+
ccall((:fftw_execute_r2r, libfftw), Void, (PlanPtr, Ptr{T}, Ptr{T}), plan, pointer(X, M*(J-1)+2), pointer(Y, M*(J-1)+1))
302+
else
303+
ccall((:fftw_execute_r2r, libfftw), Void, (PlanPtr, Ptr{T}, Ptr{T}), plan, pointer(X, M*(J-1)+1), pointer(Y, M*(J-1)+2))
304+
end
305+
end
306+
307+
function unsafe_execute_col_J!(plan::r2rFFTWPlan{T}, X::Matrix{T}, Y::Matrix{T}, J::Int, TF::Bool) where T<:fftwSingle
308+
M = size(X, 1)
309+
if TF
310+
ccall((:fftwf_execute_r2r, libfftwf), Void, (PlanPtr, Ptr{T}, Ptr{T}), plan, pointer(X, M*(J-1)+2), pointer(Y, M*(J-1)+1))
311+
else
312+
ccall((:fftwf_execute_r2r, libfftwf), Void, (PlanPtr, Ptr{T}, Ptr{T}), plan, pointer(X, M*(J-1)+1), pointer(Y, M*(J-1)+2))
313+
end
314+
end

test/sphericalharmonics/synthesisanalysistests.jl

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,55 @@ end
7878
@test vecnorm(UO[:,1:end-1] - UE) < n*vecnorm(UO)*eps()
7979
end
8080
end
81+
82+
@testset "Test for sampling through the poles" begin
83+
for f in ((θ,φ)->cos(50*cospi(φ)*sinpi(θ)*sinpi(φ)*sinpi(θ)),
84+
(θ,φ)->cos(50*cospi(φ)*sinpi(θ)+80*sinpi(φ)*sinpi(θ)),
85+
(θ,φ)->sqrt(5+cospi(φ)*sinpi(θ)+exp(sinpi(φ)*sinpi(θ))+sin(cospi(θ))))
86+
n = 200
87+
88+
θ = (0.5:n-0.5)/n
89+
φ = (0:2n-2)*2/(2n-1)
90+
F = [f(θ,φ) for θ in θ, φ in φ]
91+
V = zero(F)
92+
A_mul_B!(V, FastTransforms.plan_analysis(F), F)
93+
G = zero(V)
94+
A_mul_B!(G, FastTransforms.plan_synthesis(V), V)
95+
96+
θ2 = (0.0:n-1)/(n-1)
97+
F2 = [f(θ,φ) for θ in θ2, φ in φ]
98+
V2 = zero(F2)
99+
A_mul_B!(V2, FastTransforms.plan_analysis2(F2), F2)
100+
G2 = zero(V2)
101+
A_mul_B!(G2, FastTransforms.plan_synthesis2(V2), V2)
102+
103+
@test vecnorm(V-V2) < n*vecnorm(V)*eps()
104+
@test vecnorm(F-G) < n*vecnorm(F)*eps()
105+
@test vecnorm(F2-G2) < n*vecnorm(F)*eps()
106+
end
107+
end
108+
109+
# This test confirms numerically that [P_4(z⋅y) - P_4(x⋅y)]/(z⋅y - x⋅y) is actually a degree-3 polynomial on 𝕊²
110+
x = [0,0,1]
111+
y = normalize!([.123,.456,.789])
112+
113+
z = (θ,φ) -> [sinpi(θ)*cospi(φ), sinpi(θ)*sinpi(φ), cospi(θ)]
114+
115+
P4 = x -> (35*x^4-30*x^2+3)/8
116+
117+
n = 5
118+
θ = (0.5:n-0.5)/n
119+
φ = (0:2n-2)*2/(2n-1)
120+
F = [(P4(z(θ,φ)y) - P4(xy))/(z(θ,φ)y - xy) for θ in θ, φ in φ]
121+
V = zero(F)
122+
A_mul_B!(V, FastTransforms.plan_analysis(F), F)
123+
U3 = fourier2sph(V)
124+
125+
# U3 is degree-3
126+
127+
F = [P4(z(θ,φ)y) for θ in θ, φ in φ]
128+
V = zero(F)
129+
A_mul_B!(V, FastTransforms.plan_analysis(F), F)
130+
U4 = fourier2sph(V)
131+
132+
# U4 is degree-4

0 commit comments

Comments
 (0)