Skip to content

Commit 67a6b56

Browse files
add first stab at synthesis and analysis
1 parent ec29d06 commit 67a6b56

File tree

5 files changed

+246
-1
lines changed

5 files changed

+246
-1
lines changed

src/SphericalHarmonics/SphericalHarmonics.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ include("slowplan.jl")
1313
include("Butterfly.jl")
1414
include("fastplan.jl")
1515
include("thinplan.jl")
16+
include("synthesisanalysis.jl")
1617

1718
function plan_sph2fourier(A::AbstractMatrix; opts...)
1819
M, N = size(A)

src/SphericalHarmonics/slowplan.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ end
5858
@inline length(P::Pnmp2toPlm) = length(P.rotations)
5959
@inline getindex(P::Pnmp2toPlm, i::Int) = P.rotations[i]
6060

61-
function Base.A_mul_B!(C::Pnmp2toPlm,A::AbstractVecOrMat)
61+
function Base.A_mul_B!(C::Pnmp2toPlm, A::AbstractVecOrMat)
6262
@inbounds for i = 1:length(C)
6363
A_mul_B!(C.rotations[i], A)
6464
end
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
immutable SynthesisPlan{T, P1, P2}
2+
planθ::P1
3+
planφ::P2
4+
C::ColumnPermutation
5+
temp::Vector{T}
6+
end
7+
8+
function plan_synthesis{T<:FFTW.fftwNumber}(A::Matrix{T})
9+
m, n = size(A)
10+
x = FFTW.FakeArray(T, m)
11+
y = FFTW.FakeArray(T, n)
12+
planθ = FFTW.plan_r2r!(x, FFTW.REDFT01), FFTW.plan_r2r!(x, FFTW.RODFT01)
13+
planφ = FFTW.plan_r2r!(y, FFTW.HC2R)
14+
C = ColumnPermutation(vcat(1:2:n, 2:2:n))
15+
SynthesisPlan(planθ, planφ, C, zeros(T, n))
16+
end
17+
18+
immutable AnalysisPlan{T, P1, P2}
19+
planθ::P1
20+
planφ::P2
21+
C::ColumnPermutation
22+
temp::Vector{T}
23+
end
24+
25+
function plan_analysis{T<:FFTW.fftwNumber}(A::Matrix{T})
26+
m, n = size(A)
27+
x = FFTW.FakeArray(T, m)
28+
y = FFTW.FakeArray(T, n)
29+
planθ = FFTW.plan_r2r!(x, FFTW.REDFT10), FFTW.plan_r2r!(x, FFTW.RODFT10)
30+
planφ = FFTW.plan_r2r!(y, FFTW.R2HC)
31+
C = ColumnPermutation(vcat(1:2:n, 2:2:n))
32+
AnalysisPlan(planθ, planφ, C, zeros(T, n))
33+
end
34+
35+
function Base.A_mul_B!{T}(Y::Matrix{T}, P::SynthesisPlan{T}, X::Matrix{T})
36+
M, N = size(X)
37+
38+
# Column synthesis
39+
PCe = P.planθ[1]
40+
PCo = P.planθ[2]
41+
42+
X[1] *= two(T)
43+
A_mul_B_col_J!(Y, PCe, X, 1)
44+
X[1] *= half(T)
45+
46+
for J = 2:4:N
47+
A_mul_B_col_J!(Y, PCo, X, J)
48+
A_mul_B_col_J!(Y, PCo, X, J+1)
49+
end
50+
for J = 4:4:N
51+
X[1,J] *= two(T)
52+
X[1,J+1] *= two(T)
53+
A_mul_B_col_J!(Y, PCe, X, J)
54+
A_mul_B_col_J!(Y, PCe, X, J+1)
55+
X[1,J] *= half(T)
56+
X[1,J+1] *= half(T)
57+
end
58+
scale!(half(T), Y)
59+
60+
# Row synthesis
61+
scale!(inv(sqrt(π)), Y)
62+
invsqrttwo = inv(sqrt(2))
63+
@inbounds for i = 1:M Y[i] *= invsqrttwo end
64+
65+
temp = P.temp
66+
planφ = P.planφ
67+
C = P.C
68+
for I = 1:M
69+
copy_row_I!(temp, Y, I)
70+
row_synthesis!(planφ, C, temp)
71+
copy_row_I!(Y, temp, I)
72+
end
73+
Y
74+
end
75+
76+
function Base.A_mul_B!{T}(Y::Matrix{T}, P::AnalysisPlan{T}, X::Matrix{T})
77+
M, N = size(X)
78+
79+
# Row analysis
80+
temp = P.temp
81+
planφ = P.planφ
82+
C = P.C
83+
for I = 1:M
84+
copy_row_I!(temp, X, I)
85+
row_analysis!(planφ, C, temp)
86+
copy_row_I!(Y, temp, I)
87+
end
88+
89+
# Column analysis
90+
PCe = P.planθ[1]
91+
PCo = P.planθ[2]
92+
93+
A_mul_B_col_J!(Y, PCe, Y, 1)
94+
Y[1] *= half(T)
95+
for J = 2:4:N
96+
A_mul_B_col_J!(Y, PCo, Y, J)
97+
A_mul_B_col_J!(Y, PCo, Y, J+1)
98+
end
99+
for J = 4:4:N
100+
A_mul_B_col_J!(Y, PCe, Y, J)
101+
A_mul_B_col_J!(Y, PCe, Y, J+1)
102+
Y[1,J] *= half(T)
103+
Y[1,J+1] *= half(T)
104+
end
105+
scale!(sqrt(π)*inv(T(M)), Y)
106+
sqrttwo = sqrt(2)
107+
@inbounds for i = 1:M Y[i] *= sqrttwo end
108+
109+
Y
110+
end
111+
112+
113+
114+
115+
function row_analysis!{T}(P, C, vals::Vector{T})
116+
n = length(vals)
117+
cfs = scale!(two(T)/n,P*vals)
118+
cfs[1] *= half(T)
119+
if iseven(n)
120+
cfs[n÷2+1] *= half(T)
121+
end
122+
123+
negateeven!(reverseeven!(A_mul_B!(C, cfs)))
124+
end
125+
126+
function row_synthesis!{T}(P, C, cfs::Vector{T})
127+
n = length(cfs)
128+
Ac_mul_B!(C, reverseeven!(negateeven!(cfs)))
129+
if iseven(n)
130+
cfs[n÷2+1] *= two(T)
131+
end
132+
cfs[1] *= two(T)
133+
P*scale!(half(T), cfs)
134+
end
135+
136+
function copy_row_I!(temp::Vector, Y::Matrix, I::Int)
137+
M, N = size(Y)
138+
@inbounds @simd for j = 1:N
139+
temp[j] = Y[I+M*(j-1)]
140+
end
141+
temp
142+
end
143+
144+
function copy_row_I!(Y::Matrix, temp::Vector, I::Int)
145+
M, N = size(Y)
146+
@inbounds @simd for j = 1:N
147+
Y[I+M*(j-1)] = temp[j]
148+
end
149+
Y
150+
end
151+
152+
153+
function reverseeven!(x::Vector)
154+
n = length(x)
155+
if iseven(n)
156+
@inbounds @simd for k=2:2:n÷2
157+
x[k], x[n+2-k] = x[n+2-k], x[k]
158+
end
159+
else
160+
@inbounds @simd for k=2:2:n÷2
161+
x[k], x[n+1-k] = x[n+1-k], x[k]
162+
end
163+
end
164+
x
165+
end
166+
167+
function negateeven!(x::Vector)
168+
@inbounds @simd for k = 2:2:length(x)
169+
x[k] *= -1
170+
end
171+
x
172+
end
173+
174+
import Base.FFTW: unsafe_execute!, fftwSingle, fftwDouble, fftwNumber
175+
import Base.FFTW: libfftw, libfftwf, PlanPtr, r2rFFTWPlan
176+
177+
function A_mul_B_col_J!{T}(Y::Matrix{T}, P::r2rFFTWPlan{T}, X::Matrix{T}, J::Int)
178+
unsafe_execute_col_J!(P, X, Y, J)
179+
return Y
180+
end
181+
182+
function unsafe_execute_col_J!{T<:fftwDouble}(plan::r2rFFTWPlan{T}, X::Matrix{T}, Y::Matrix{T}, J::Int)
183+
M = size(X, 1)
184+
ccall((:fftw_execute_r2r, libfftw), Void, (PlanPtr, Ptr{T}, Ptr{T}), plan, pointer(X, M*(J-1)+1), pointer(Y, M*(J-1)+1))
185+
end
186+
187+
function unsafe_execute_col_J!{T<:fftwSingle}(plan::r2rFFTWPlan{T}, X::Matrix{T}, Y::Matrix{T}, J::Int)
188+
M = size(X, 1)
189+
ccall((:fftwf_execute_r2r, libfftwf), Void, (PlanPtr, Ptr{T}, Ptr{T}), plan, pointer(X, M*(J-1)+1), pointer(Y, M*(J-1)+1))
190+
end

test/sphericalharmonictests.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ for θ in (0.123, 0.456), m in 2:2:n
5454
@test norm(S1-SA) < 1000eps()
5555
end
5656

57+
println()
58+
println("Testing synthesis and analysis")
59+
println()
60+
61+
include("synthesisanalysistests.jl")
62+
5763
println()
5864
println("Testing API")
5965
println()

test/synthesisanalysistests.jl

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using FastTransforms, Base.Test
2+
3+
import FastTransforms: normalizecolumns!, maxcolnorm
4+
5+
# Starting with normalized spherical harmonic coefficients,
6+
7+
n = 50
8+
F = sphrandn(Float64, n, n);
9+
normalizecolumns!(F);
10+
11+
# we convert to bivariate Fourier series.
12+
13+
G = sph2fourier(F);
14+
15+
# At equispaced points in angle,
16+
17+
θ = (0.5:n-0.5)*π/n
18+
φ = (0:2n-2)*2π/(2n-1)
19+
20+
# the Fourier series evaluates to:
21+
22+
SF = [sum(G[ℓ,1]*cos((ℓ-1)*θ)/sqrt(2π) forin 1:n) + sum(G[ℓ,2m]*sin(ℓ*θ)*sin(m*φ)/sqrt(π) forin 1:n, m in 1:2:n-1) + sum(G[ℓ,2m+1]*sin(ℓ*θ)*cos(m*φ)/sqrt(π) forin 1:n, m in 1:2:n-1) + sum(G[ℓ,2m]*cos((ℓ-1)*θ)*sin(m*φ)/sqrt(π) forin 1:n, m in 2:2:n-1) + sum(G[ℓ,2m+1]*cos((ℓ-1)*θ)*cos(m*φ)/sqrt(π) forin 1:n, m in 2:2:n-1) for θ in θ, φ in φ]
23+
24+
# but that was slow, so we accelerate it via in-place FFTW technology:
25+
26+
Ps = FastTransforms.plan_synthesis(G);
27+
28+
Y = zero(G);
29+
30+
A_mul_B!(Y, Ps, G)
31+
32+
@test maxcolnorm(SF - Y) < 10000eps()
33+
34+
# Retracing our steps, function values on the sphere are converted to Fourier coefficients:
35+
36+
Pa = FastTransforms.plan_analysis(Y);
37+
38+
Z = zero(Y);
39+
40+
A_mul_B!(Z, Pa, Y)
41+
42+
@test maxcolnorm(Z - G) < 10eps()
43+
44+
# And Fourier coefficients are converted back to spherical harmonic coefficients:
45+
46+
H = fourier2sph(Z)
47+
48+
@test maxcolnorm(F - H) < 100eps()

0 commit comments

Comments
 (0)