Skip to content

Commit 4a7cc32

Browse files
update nufft
reverse type I and II to match the paper add `lambertw` as a temporary measure add docstrings and section in readme
1 parent 3ed3fe0 commit 4a7cc32

File tree

9 files changed

+305
-135
lines changed

9 files changed

+305
-135
lines changed

README.md

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,34 @@ is valid for the half-open square `(α,β) ∈ (-1/2,1/2]^2`. Therefore, the fas
7575
when the parameters are inside. If the parameters `(α,β)` are not exceptionally beyond the square,
7676
then increment/decrement operators are used with linear complexity (and linear conditioning) in the degree.
7777

78+
## Nonuniform fast Fourier transforms
79+
80+
The NUFFTs are implemented thanks to [Alex Townsend](https://github.com/ajt60gaibb). `nufft1` assumes uniform samples and noninteger frequencies, while `nufft2` assumes nonuniform samples and integer frequencies.
81+
```julia
82+
julia> n = 10^4;
83+
84+
julia> c = complex(rand(n));
85+
86+
julia> ω = collect(0:n-1) + rand(n);
87+
88+
julia> nufft1(c, ω, eps());
89+
90+
julia> p1 = plan_nufft1(ω, eps());
91+
92+
julia> @time p1*c;
93+
0.002383 seconds (6 allocations: 156.484 KiB)
94+
95+
julia> x = (collect(0:n-1) + 3rand(n))/n;
96+
97+
julia> nufft2(c, x, eps());
98+
99+
julia> p2 = plan_nufft2(x, eps());
100+
101+
julia> @time p2*c;
102+
0.001478 seconds (6 allocations: 156.484 KiB)
103+
104+
```
105+
78106
## The Padua Transform
79107

80108
The Padua transform and its inverse are implemented thanks to [Michael Clarke](https://github.com/MikeAClarke). These are optimized methods designed for computing the bivariate Chebyshev coefficients by interpolating a bivariate function at the Padua points on `[-1,1]^2`.
@@ -87,7 +115,7 @@ julia> N = div((n+1)*(n+2),2);
87115
julia> v = rand(N); # The length of v is the number of Padua points
88116

89117
julia> @time norm(ipaduatransform(paduatransform(v))-v)
90-
0.006571 seconds (846 allocations: 1.746 MiB)
118+
0.006571 seconds (846 allocations: 1.746 MiB)
91119
3.123637691861415e-14
92120

93121
```
@@ -126,10 +154,12 @@ As with other fast transforms, `plan_sph2fourier` saves effort by caching the pr
126154

127155
[2] N. Hale and A. Townsend. <a href="http://dx.doi.org/10.1137/130932223">A fast, simple, and stable Chebyshev—Legendre transform using and asymptotic formula</a>, *SIAM J. Sci. Comput.*, **36**:A148—A167, 2014.
128156

129-
[3] J. Keiner. <a href="http://dx.doi.org/10.1137/070703065">Computing with expansions in Gegenbauer polynomials</a>, *SIAM J. Sci. Comput.*, **31**:2151—2171, 2009.
157+
[3] J. Keiner. <a href="http://dx.doi.org/10.1137/070703065">Computing with expansions in Gegenbauer polynomials</a>, *SIAM J. Sci. Comput.*, **31**:2151—2171, 2009.
158+
159+
[4] D. Ruiz—Antolín and A. Townsend. <a href="https://arxiv.org/abs/1701.04492">A nonuniform fast Fourier transform based on low rank approximation</a>, arXiv:1701.04492, 2017.
130160

131-
[4] R. M. Slevinsky. <a href="https://doi.org/10.1093/imanum/drw070">On the use of Hahn's asymptotic formula and stabilized recurrence for a fast, simple, and stable Chebyshev—Jacobi transform</a>, in press at *IMA J. Numer. Anal.*, 2017.
161+
[5] R. M. Slevinsky. <a href="https://doi.org/10.1093/imanum/drw070">On the use of Hahn's asymptotic formula and stabilized recurrence for a fast, simple, and stable Chebyshev—Jacobi transform</a>, in press at *IMA J. Numer. Anal.*, 2017.
132162

133-
[5] R. M. Slevinsky. <a href="https://arxiv.org/abs/1705.05448">Fast and backward stable transforms between spherical harmonic expansions and bivariate Fourier series</a>, arXiv:1705.05448, 2017.
163+
[6] R. M. Slevinsky. <a href="https://arxiv.org/abs/1705.05448">Fast and backward stable transforms between spherical harmonic expansions and bivariate Fourier series</a>, arXiv:1705.05448, 2017.
134164

135-
[6] A. Townsend, M. Webb, and S. Olver. <a href="https://doi.org/10.1090/mcom/3277">Fast polynomial transforms based on Toeplitz and Hankel matrices</a>, in press at *Math. Comp.*, 2017.
165+
[7] A. Townsend, M. Webb, and S. Olver. <a href="https://doi.org/10.1090/mcom/3277">Fast polynomial transforms based on Toeplitz and Hankel matrices</a>, in press at *Math. Comp.*, 2017.

docs/src/index.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,22 @@ plan_cjt
5252
plan_icjt
5353
```
5454

55+
```@docs
56+
nufft1
57+
```
58+
59+
```@docs
60+
nufft2
61+
```
62+
63+
```@docs
64+
plan_nufft1
65+
```
66+
67+
```@docs
68+
plan_nufft2
69+
```
70+
5571
```@docs
5672
paduatransform
5773
```
@@ -112,6 +128,10 @@ FastTransforms.δ
112128
FastTransforms.Λ
113129
```
114130

131+
```@docs
132+
FastTransforms.lambertw
133+
```
134+
115135
```@docs
116136
FastTransforms.pochhammer
117137
```

src/FastTransforms.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@ export normleg2cheb, cheb2normleg, normleg12cheb2, cheb22normleg1
1616
export plan_leg2cheb, plan_cheb2leg
1717
export plan_normleg2cheb, plan_cheb2normleg
1818
export plan_normleg12cheb2, plan_cheb22normleg1
19+
1920
export gaunt
21+
2022
export paduatransform, ipaduatransform, paduatransform!, ipaduatransform!, paduapoints
2123
export plan_paduatransform!, plan_ipaduatransform!
24+
2225
export nufft, nufft1, nufft2
23-
export nufft_plan, nufft1_plan, nufft2_plan
26+
export plan_nufft, plan_nufft1, plan_nufft2
2427

2528
export SlowSphericalHarmonicPlan, FastSphericalHarmonicPlan, ThinSphericalHarmonicPlan
2629
export sph2fourier, fourier2sph, plan_sph2fourier

src/nufft.jl

Lines changed: 163 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,185 @@
1-
function nufft1_plan{T<:AbstractFloat}( x::AbstractVector{T}, ϵ::T )
2-
3-
t_idx = AssignClosestEquispacedFFTpoint( x )
4-
γ = PerturbationParameter( x, AssignClosestEquispacedGridpoint( x ) )
5-
K = FindK(γ, ϵ)
6-
u = constructU(x, K)
7-
v = constructV(x, K)
8-
p( c ) = (u.*(fft(Diagonal(c)*v,1)[t_idx,:]))*ones(K)
1+
doc"""
2+
Pre-compute a nonuniform fast Fourier transform of type `N`.
3+
4+
For best performance, choose the right number of threads by `FFTW.set_num_threads(4)`, for example.
5+
"""
6+
immutable NUFFTPlan{N,T,FFT} <: Base.DFT.Plan{T}
7+
U::Matrix{T}
8+
V::Matrix{T}
9+
p::FFT
10+
t::Vector{Int}
11+
temp::Matrix{T}
12+
temp2::Matrix{T}
13+
Ones::Vector{T}
914
end
1015

11-
function nufft2_plan{T<:AbstractFloat}( ω::AbstractVector{T}, ϵ::T )
16+
doc"""
17+
Computes a nonuniform fast Fourier transform of type I:
18+
19+
```math
20+
f_j = \sum_{k=1}^N c_k e^{-2\pi{\rm i} (j-1)/N \omega_k},\quad{\rm for}\quad 1 \le j \le N.
21+
```
22+
"""
23+
function plan_nufft1{T<:AbstractFloat}::AbstractVector{T}, ϵ::T)
24+
N = length(ω)
25+
ωdN = ω/N
26+
t = AssignClosestEquispacedFFTpoint(ωdN)
27+
γ = PerturbationParameter(ωdN, AssignClosestEquispacedGridpoint(ωdN))
28+
K = FindK(γ, ϵ)
29+
U = constructU( ωdN, K)
30+
V = constructV( ωdN, K)
31+
p = plan_ifft!(V, 1)
32+
temp = zeros(Complex{T}, N, K)
33+
temp2 = zeros(Complex{T}, N, K)
34+
Ones = ones(Complex{T}, K)
1235

13-
N = size(ω, 1)
14-
t_idx = AssignClosestEquispacedFFTpoint( ω/N )
15-
γ = PerturbationParameter( ω/N, AssignClosestEquispacedGridpoint( ω/N ) )
16-
K = FindK(γ, ϵ)
17-
u = constructU( ω/N, K)
18-
v = constructV( ω/N, K)
19-
In = speye(Complex{T}, N, N)
20-
p( c ) = (v.*(N*conj(ifft(In[:,t_idx]*conj(Diagonal(c)*u),1))))*ones(K)
36+
NUFFTPlan{1, eltype(U), typeof(p)}(U, V, p, t, temp, temp2, Ones)
2137
end
2238

23-
nufft_plan{T<:AbstractFloat}( x::AbstractVector{T}, ϵ::T ) = nufft1_plan( x, ϵ )
24-
nufft{T<:AbstractFloat}( c::AbstractVector, x::AbstractVector{T}, ϵ::T ) = nufft_plan(x, ϵ)(c)
25-
nufft1{T<:AbstractFloat}( c::AbstractVector, x::AbstractVector{T}, ϵ::T ) = nufft1_plan(x, ϵ)(c)
26-
nufft2{T<:AbstractFloat}( c::AbstractVector, ω::AbstractVector{T}, ϵ::T ) = nufft2_plan(ω, ϵ)(c)
39+
doc"""
40+
Computes a nonuniform fast Fourier transform of type II:
2741
28-
FindK{T<:AbstractFloat}::T, ϵ::T) = Int( ceil(5.0*γ.*exp(lambertw(log(10.0/ϵ)./γ/7.0))) )
29-
AssignClosestEquispacedGridpoint{T<:AbstractFloat}( x::AbstractVector{T} )::AbstractVector{T} = round(size(x,1)*x)
30-
AssignClosestEquispacedFFTpoint{T<:AbstractFloat}( x::AbstractVector{T} )::Array{Int64,1} = mod(round(Int64, size(x,1)*x), size(x,1)) + 1
31-
PerturbationParameter{T<:AbstractFloat}( x::AbstractVector{T}, s_vec::AbstractVector{T} )::AbstractFloat = norm( size(x,1)*x - s_vec, Inf)
42+
```math
43+
f_j = \sum_{k=1}^N c_k e^{-2\pi{\rm i} x_j (k-1)},\quad{\rm for}\quad 1 \le j \le N.
44+
```
45+
"""
46+
function plan_nufft2{T<:AbstractFloat}(x::AbstractVector{T}, ϵ::T)
47+
N = length(x)
48+
t = AssignClosestEquispacedFFTpoint(x)
49+
γ = PerturbationParameter(x, AssignClosestEquispacedGridpoint(x))
50+
K = FindK(γ, ϵ)
51+
U = constructU(x, K)
52+
V = constructV(x, K)
53+
p = plan_fft!(U, 1)
54+
temp = zeros(Complex{T}, N, K)
55+
temp2 = zeros(Complex{T}, N, K)
56+
Ones = ones(Complex{T}, K)
57+
58+
NUFFTPlan{2, eltype(U), typeof(p)}(U, V, p, t, temp, temp2, Ones)
59+
end
3260

33-
function constructU{T<:AbstractFloat}(x::AbstractVector{T}, K::Int64)
34-
# Construct a low rank approximation, using Chebyshev expansions
35-
# for AK = exp(-2*pi*1im*(x[j]-j/N)*k):
61+
function (*){N,T,V}(p::NUFFTPlan{N,T}, x::AbstractVector{V})
62+
A_mul_B!(zeros(promote_type(T,V), length(x)), p, x)
63+
end
3664

37-
N = size(x, 1)
38-
#(s_vec, t_idx, γ) = FindAlgorithmicParameters( x )
39-
s_vec = AssignClosestEquispacedGridpoint( x )
40-
er = N*x - s_vec
41-
γ = norm( er, Inf )
65+
function Base.A_mul_B!{T}(y::AbstractVector{T}, P::NUFFTPlan{1,T}, c::AbstractVector{T})
66+
U, V, p, t, temp, temp2, Ones = P.U, P.V, P.p, P.t, P.temp, P.temp2, P.Ones
4267

43-
# colspace vectors:
44-
u = Diagonal(exp(-1im*pi*er))*ChebyshevP(K-1, er/γ)*Bessel_coeffs(K, γ)
45-
end
68+
# (V.*(N*conj(ifft(In[:,t]*conj(Diagonal(c)*U),1))))*ones(K)
4669

47-
function constructV{T<:AbstractFloat}(x::AbstractVector{T}, K::Int64)
70+
broadcast!(*, temp, c, U)
71+
conj!(temp)
72+
fill!(temp2, zero(T))
73+
recombine_rows!(temp, t, temp2)
74+
p*temp2
75+
conj!(temp2)
76+
broadcast!(*, temp, V, temp2)
77+
A_mul_B!(y, temp, Ones)
78+
scale!(length(c), y)
4879

49-
N = size(x, 1)
50-
v = complex(ChebyshevP(K-1, 2.0*collect(0:N-1)/N - ones(N) ))
80+
y
5181
end
5282

53-
function Bessel_coeffs{T<:AbstractFloat}(K::Int64, γ::T)::Array{Complex{T},2}
54-
# Calculate the Chebyshev coefficients of exp(-2*pi*1im*x*y) on [-gam,gam]x[0,1]
83+
function Base.A_mul_B!{T}(y::AbstractVector{T}, P::NUFFTPlan{2,T}, c::AbstractVector{T})
84+
U, V, p, t, temp, temp2, Ones = P.U, P.V, P.p, P.t, P.temp, P.temp2, P.Ones
85+
86+
# (U.*(fft(Diagonal(c)*V,1)[t+1,:]))*ones(K)
87+
88+
broadcast!(*, temp, c, V)
89+
p*temp
90+
reindex_temp!(temp, t, temp2)
91+
broadcast!(*, temp, U, temp2)
92+
A_mul_B!(y, temp, Ones)
93+
94+
y
95+
end
5596

56-
cfs = complex(zeros( K, K ))
57-
arg = -γ*pi/2.0
58-
for p = 0:K-1
59-
for q = mod(p,2):2:K-1
60-
cfs[p+1,q+1] = 4.0*(1im)^q*besselj((p+q)/2,arg).*besselj((q-p)/2,arg)
61-
end
97+
function reindex_temp!{T}(temp::Matrix{T}, t::Vector{Int}, temp2::Matrix{T})
98+
@inbounds for j = 1:size(temp, 2)
99+
for i = 1:size(temp, 1)
100+
temp2[i, j] = temp[t[i], j]
101+
end
102+
end
103+
temp2
62104
end
63-
cfs[1,:] = cfs[1,:]/2.0
64-
cfs[:,1] = cfs[:,1]/2.0
65-
return cfs
105+
106+
function recombine_rows!{T}(temp::Matrix{T}, t::Vector{Int}, temp2::Matrix{T})
107+
@inbounds for j = 1:size(temp, 2)
108+
for i = 1:size(temp, 1)
109+
temp2[t[i], j] += temp[i, j]
110+
end
111+
end
112+
temp2
66113
end
67114

68-
function ChebyshevP{T<:AbstractFloat}(n::Int64, x::AbstractVector{T})::AbstractArray{T}
69-
# Evaluate Chebyshev polynomials of degree 0,...,n at x:
115+
doc"""
116+
Pre-compute a nonuniform fast Fourier transform of type I.
117+
"""
118+
nufft1{T<:AbstractFloat}(c::AbstractVector, ω::AbstractVector{T}, ϵ::T) = plan_nufft1(ω, ϵ)*c
119+
120+
doc"""
121+
Pre-compute a nonuniform fast Fourier transform of type II.
122+
"""
123+
nufft2{T<:AbstractFloat}(c::AbstractVector, x::AbstractVector{T}, ϵ::T) = plan_nufft2(x, ϵ)*c
124+
125+
FindK{T<:AbstractFloat}::T, ϵ::T) = Int(ceil(5*γ*exp(lambertw(log(10/ϵ)/γ/7))))
126+
AssignClosestEquispacedGridpoint{T<:AbstractFloat}(x::AbstractVector{T})::AbstractVector{T} = round.([Int], size(x, 1)*x)
127+
AssignClosestEquispacedFFTpoint{T<:AbstractFloat}(x::AbstractVector{T})::Array{Int,1} = mod.(round.([Int], size(x, 1)*x), size(x, 1)) + 1
128+
PerturbationParameter{T<:AbstractFloat}(x::AbstractVector{T}, s_vec::AbstractVector{T})::AbstractFloat = norm(size(x, 1)*x - s_vec, Inf)
70129

71-
N = size(x, 1)
72-
Tcheb = Array{T}(N, n+1)
130+
function constructU{T<:AbstractFloat}(x::AbstractVector{T}, K::Int)
131+
# Construct a low rank approximation, using Chebyshev expansions
132+
# for AK = exp(-2*pi*1im*(x[j]-j/N)*k):
133+
N = size(x, 1)
134+
#(s_vec, t, γ) = FindAlgorithmicParameters( x )
135+
s_vec = AssignClosestEquispacedGridpoint(x)
136+
er = N*x - s_vec
137+
γ = norm(er, Inf)
138+
# colspace vectors:
139+
Diagonal(exp.(-im*(pi*er)))*ChebyshevP(K-1, er/γ)*Bessel_coeffs(K, γ)
140+
end
73141

74-
# T_0(x) = 1.0
75-
One = convert(eltype(x),1.0)
76-
@inbounds for j = 1:N
77-
Tcheb[j, 1] = One
142+
function constructV{T<:AbstractFloat}(x::AbstractVector{T}, K::Int)
143+
N = size(x, 1)
144+
complex(ChebyshevP(K-1, two(T)*collect(0:N-1)/N - ones(N) ))
78145
end
79-
# T_1(x) = x
80-
if ( n > 0 )
81-
@inbounds for j = 1:N
82-
Tcheb[j, 2] = x[j]
83-
end
146+
147+
function Bessel_coeffs{T<:AbstractFloat}(K::Int, γ::T)
148+
# Calculate the Chebyshev coefficients of exp(-2*pi*1im*x*y) on [-gam,gam]x[0,1]
149+
cfs = zeros(Complex{T}, K, K)
150+
arg = -γ*π/two(T)
151+
for p = 0:K-1
152+
for q = mod(p,2):2:K-1
153+
cfs[p+1,q+1] = 4*(1im)^q*besselj((p+q)/2,arg).*besselj((q-p)/2,arg)
154+
end
155+
end
156+
cfs[1,:] = cfs[1,:]/two(T)
157+
cfs[:,1] = cfs[:,1]/two(T)
158+
return cfs
84159
end
85-
# 3-term recurrence relation:
86-
twoX = 2x
87-
@inbounds for k = 2:n
88-
@inbounds for j = 1:N
89-
Tcheb[j, k+1] = twoX[j]*Tcheb[j, k] - Tcheb[j, k-1]
90-
end
160+
161+
function ChebyshevP{T<:AbstractFloat}(n::Int, x::AbstractVector{T})
162+
# Evaluate Chebyshev polynomials of degree 0,...,n at x:
163+
N = size(x, 1)
164+
Tcheb = Matrix{T}(N, n+1)
165+
166+
# T_0(x) = 1.0
167+
One = convert(eltype(x),1.0)
168+
@inbounds for j = 1:N
169+
Tcheb[j, 1] = One
170+
end
171+
# T_1(x) = x
172+
if ( n > 0 )
173+
@inbounds for j = 1:N
174+
Tcheb[j, 2] = x[j]
175+
end
176+
end
177+
# 3-term recurrence relation:
178+
twoX = 2x
179+
@inbounds for k = 2:n
180+
@simd for j = 1:N
181+
Tcheb[j, k+1] = twoX[j]*Tcheb[j, k] - Tcheb[j, k-1]
182+
end
183+
end
184+
return Tcheb
91185
end
92-
return Tcheb
93-
end

0 commit comments

Comments
 (0)