Skip to content

Commit cefa8a1

Browse files
update disk2cxf, add rectdisk2cheb
1 parent 050aded commit cefa8a1

File tree

7 files changed

+116
-31
lines changed

7 files changed

+116
-31
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ BinaryProvider = "0.5"
2525
DSP = "0.6"
2626
FFTW = "1"
2727
FastGaussQuadrature = "0.4"
28-
FastTransforms_jll = "0.3.3"
28+
FastTransforms_jll = "0.4.0"
2929
FillArrays = "0.8, 0.9"
3030
Reexport = "0.2"
3131
SpecialFunctions = "0.8, 0.9, 0.10"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ julia> using FastTransforms, LinearAlgebra
1919

2020
## Fast orthogonal polynomial transforms
2121

22-
The 29 orthogonal polynomial transforms are listed in `FastTransforms.kind2string.(0:28)`. Univariate transforms may be planned with the standard normalization or with orthonormalization. For multivariate transforms, the standard normalization may be too severe for floating-point computations, so it is omitted. Here are two examples:
22+
The 33 orthogonal polynomial transforms are listed in `FastTransforms.kind2string.(0:32)`. Univariate transforms may be planned with the standard normalization or with orthonormalization. For multivariate transforms, the standard normalization may be too severe for floating-point computations, so it is omitted. Here are two examples:
2323

2424
### The Chebyshev--Legendre transform
2525

examples/disk.jl

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ r = [sinpi((N-n-0.5)/(2N)) for n in 0:N-1]
3737
# On the mapped tensor product grid, our function samples are:
3838
F = [f(r*cospi(θ), r*sinpi(θ)) for r in r, θ in θ]
3939

40-
# We precompute a Zernike--Chebyshev×Fourier plan:
41-
P = plan_disk2cxf(F)
40+
# We precompute a (generalized) Zernike--Chebyshev×Fourier plan:
41+
α, β = 0, 0
42+
P = plan_disk2cxf(F, α, β)
4243

4344
# And an FFTW Chebyshev×Fourier analysis plan on the disk:
4445
PA = plan_disk_analysis(F)
@@ -47,10 +48,57 @@ PA = plan_disk_analysis(F)
4748
U = P\(PA*F)
4849

4950
# The Zernike coefficients are useful for integration. The integral of $f(x,y)$
50-
# over the disk should be $\pi/2$ by harmonicity. The coefficient of $Z_0^0$
51+
# over the disk should be $\pi/2$ by harmonicity. The coefficient of $Z_{0,0}$
5152
# multiplied by `√π` is:
5253
U[1, 1]*sqrt(π)
5354

5455
# Using an orthonormal basis, the integral of $[f(x,y)]^2$ over the disk is
5556
# approximately the square of the 2-norm of the coefficients:
56-
norm(U)^2
57+
norm(U)^2 - π/(2*sqrt(2))*log1p(sqrt(2))
58+
59+
# But there's more! Next, we repeat the experiment using the Dunkl-Xu
60+
# orthonormal polynomials supported on the rectangularized disk.
61+
N = 2N
62+
M = N
63+
64+
# We analyze the function on an $N\times M$ mapped tensor product $xy$-grid defined by:
65+
# ```math
66+
# \begin{aligned}
67+
# x_n & = \cos\left(\frac{2n+1}{2N}\pi\right) = \sin\left(\frac{N-2n-1}{2N}\pi\right),\quad {\rm for} \quad 0 \le n < N,\quad{\rm and}\\
68+
# z_m & = \cos\left(\frac{2m+1}{2M}\pi\right) = \sin\left(\frac{M-2m-1}{2M}\pi\right),\quad {\rm for} \quad 0 \le m < M,\\
69+
# y_{n,m} & = \sqrt{1-x_n^2}z_m.
70+
# \end{aligned}
71+
# ```
72+
# Slightly more accuracy can be expected by using an auxiliary array:
73+
# ```math
74+
# w_n = \sin\left(\frac{2n+1}{2N}\pi\right),\quad {\rm for} \quad 0 \le n < N,
75+
# ```
76+
# so that $y_{n,m} = w_nz_m$.
77+
#
78+
# The x grid
79+
w = [sinpi((n+0.5)/N) for n in 0:N-1]
80+
x = [sinpi((N-2n-1)/(2N)) for n in 0:N-1]
81+
82+
# The z grid
83+
z = [sinpi((M-2m-1)/(2M)) for m in 0:M-1]
84+
85+
# On the mapped tensor product grid, our function samples are:
86+
F = [f(x[n], w[n]*z) for n in 1:N, z in z]
87+
88+
# We precompute a Dunkl-Xu--Chebyshev plan:
89+
P = plan_rectdisk2cheb(F, β)
90+
91+
# And an FFTW Chebyshev² analysis plan on the rectangularized disk:
92+
PA = plan_rectdisk_analysis(F)
93+
94+
# Its Dunkl-Xu coefficients are:
95+
U = P\(PA*F)
96+
97+
# The Dunkl-Xu coefficients are useful for integration. The integral of $f(x,y)$
98+
# over the disk should be $\pi/2$ by harmonicity. The coefficient of $P_{0,0}$
99+
# multiplied by `√π` is:
100+
U[1, 1]*sqrt(π)
101+
102+
# Using an orthonormal basis, the integral of $[f(x,y)]^2$ over the disk is
103+
# approximately the square of the 2-norm of the coefficients:
104+
norm(U)^2 - π/(2*sqrt(2))*log1p(sqrt(2))

src/FastTransforms.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,16 @@ import LinearAlgebra: mul!, lmul!, ldiv!
3535
export leg2cheb, cheb2leg, ultra2ultra, jac2jac,
3636
lag2lag, jac2ultra, ultra2jac, jac2cheb,
3737
cheb2jac, ultra2cheb, cheb2ultra,
38-
sph2fourier, sphv2fourier, disk2cxf, tri2cheb, tet2cheb,
39-
fourier2sph, fourier2sphv, cxf2disk, cheb2tri, cheb2tet
38+
sph2fourier, sphv2fourier, disk2cxf, rectdisk2cheb, tri2cheb, tet2cheb,
39+
fourier2sph, fourier2sphv, cxf2disk, cheb2rectdisk, cheb2tri, cheb2tet
4040

4141
export plan_leg2cheb, plan_cheb2leg, plan_ultra2ultra, plan_jac2jac,
4242
plan_lag2lag, plan_jac2ultra, plan_ultra2jac, plan_jac2cheb,
4343
plan_cheb2jac, plan_ultra2cheb, plan_cheb2ultra,
4444
plan_sph2fourier, plan_sph_synthesis, plan_sph_analysis,
4545
plan_sphv2fourier, plan_sphv_synthesis, plan_sphv_analysis,
4646
plan_disk2cxf, plan_disk_synthesis, plan_disk_analysis,
47+
plan_rectdisk2cheb, plan_rectdisk_synthesis, plan_rectdisk_analysis,
4748
plan_tri2cheb, plan_tri_synthesis, plan_tri_analysis,
4849
plan_tet2cheb, plan_tet_synthesis, plan_tet_analysis,
4950
plan_spinsph2fourier, plan_spinsph_synthesis, plan_spinsph_analysis
@@ -91,6 +92,7 @@ include("gaunt.jl")
9192
export sphones, sphzeros, sphrand, sphrandn, sphevaluate,
9293
sphvones, sphvzeros, sphvrand, sphvrandn,
9394
diskones, diskzeros, diskrand, diskrandn,
95+
rectdiskones, rectdiskzeros, rectdiskrand, rectdiskrandn,
9496
triones, trizeros, trirand, trirandn, trievaluate,
9597
tetones, tetzeros, tetrand, tetrandn,
9698
spinsphones, spinsphzeros, spinsphrand, spinsphrandn

src/libfasttransforms.jl

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -119,22 +119,25 @@ const CHEB2ULTRA = 10
119119
const SPHERE = 11
120120
const SPHEREV = 12
121121
const DISK = 13
122-
const TRIANGLE = 14
123-
const TETRAHEDRON = 15
124-
const SPINSPHERE = 16
125-
const SPHERESYNTHESIS = 17
126-
const SPHEREANALYSIS = 18
127-
const SPHEREVSYNTHESIS = 19
128-
const SPHEREVANALYSIS = 20
129-
const DISKSYNTHESIS = 21
130-
const DISKANALYSIS = 22
131-
const TRIANGLESYNTHESIS = 23
132-
const TRIANGLEANALYSIS = 24
133-
const TETRAHEDRONSYNTHESIS = 25
134-
const TETRAHEDRONANALYSIS = 26
135-
const SPINSPHERESYNTHESIS = 27
136-
const SPINSPHEREANALYSIS = 28
137-
const SPHERICALISOMETRY = 29
122+
const RECTDISK = 14
123+
const TRIANGLE = 15
124+
const TETRAHEDRON = 16
125+
const SPINSPHERE = 17
126+
const SPHERESYNTHESIS = 18
127+
const SPHEREANALYSIS = 19
128+
const SPHEREVSYNTHESIS = 20
129+
const SPHEREVANALYSIS = 21
130+
const DISKSYNTHESIS = 22
131+
const DISKANALYSIS = 23
132+
const RECTDISKSYNTHESIS = 24
133+
const RECTDISKANALYSIS = 25
134+
const TRIANGLESYNTHESIS = 26
135+
const TRIANGLEANALYSIS = 27
136+
const TETRAHEDRONSYNTHESIS = 28
137+
const TETRAHEDRONANALYSIS = 29
138+
const SPINSPHERESYNTHESIS = 30
139+
const SPINSPHEREANALYSIS = 31
140+
const SPHERICALISOMETRY = 32
138141

139142

140143
let k2s = Dict(LEG2CHEB => "Legendre--Chebyshev",
@@ -151,6 +154,7 @@ let k2s = Dict(LEG2CHEB => "Legendre--Chebyshev",
151154
SPHERE => "Spherical harmonic--Fourier",
152155
SPHEREV => "Spherical vector field--Fourier",
153156
DISK => "Zernike--Chebyshev×Fourier",
157+
RECTDISK => "Dunkl-Xu--Chebyshev²",
154158
TRIANGLE => "Proriol--Chebyshev²",
155159
TETRAHEDRON => "Proriol--Chebyshev³",
156160
SPINSPHERE => "Spin-weighted spherical harmonic--Fourier",
@@ -160,6 +164,8 @@ let k2s = Dict(LEG2CHEB => "Legendre--Chebyshev",
160164
SPHEREVANALYSIS => "FFTW Fourier analysis on the sphere (vector field)",
161165
DISKSYNTHESIS => "FFTW Chebyshev×Fourier synthesis on the disk",
162166
DISKANALYSIS => "FFTW Chebyshev×Fourier analysis on the disk",
167+
RECTDISKSYNTHESIS => "FFTW Chebyshev synthesis on the rectangularized disk",
168+
RECTDISKANALYSIS => "FFTW Chebyshev analysis on the rectangularized disk",
163169
TRIANGLESYNTHESIS => "FFTW Chebyshev synthesis on the triangle",
164170
TRIANGLEANALYSIS => "FFTW Chebyshev analysis on the triangle",
165171
TETRAHEDRONSYNTHESIS => "FFTW Chebyshev synthesis on the tetrahedron",
@@ -201,6 +207,7 @@ show(io::IO, p::FTPlan{T, 1, K}) where {T, K} = print(io, "FastTransforms ", kin
201207
show(io::IO, p::FTPlan{T, 2, SPHERE}) where T = print(io, "FastTransforms ", kind2string(SPHERE), " plan for $(p.n)×$(2p.n-1)-element array of ", T)
202208
show(io::IO, p::FTPlan{T, 2, SPHEREV}) where T = print(io, "FastTransforms ", kind2string(SPHEREV), " plan for $(p.n)×$(2p.n-1)-element array of ", T)
203209
show(io::IO, p::FTPlan{T, 2, DISK}) where T = print(io, "FastTransforms ", kind2string(DISK), " plan for $(p.n)×$(4p.n-3)-element array of ", T)
210+
show(io::IO, p::FTPlan{T, 2, RECTDISK}) where T = print(io, "FastTransforms ", kind2string(RECTDISK), " plan for $(p.n)×$(p.n)-element array of ", T)
204211
show(io::IO, p::FTPlan{T, 2, TRIANGLE}) where T = print(io, "FastTransforms ", kind2string(TRIANGLE), " plan for $(p.n)×$(p.n)-element array of ", T)
205212
show(io::IO, p::FTPlan{T, 3, TETRAHEDRON}) where T = print(io, "FastTransforms ", kind2string(TETRAHEDRON), " plan for $(p.n)×$(p.n)×$(p.n)-element array of ", T)
206213
show(io::IO, p::FTPlan{T, 2, SPINSPHERE}) where T = print(io, "FastTransforms ", kind2string(SPINSPHERE), " plan for $(p.n)×$(2p.n-1)-element array of ", T)
@@ -246,6 +253,8 @@ destroy_plan(p::FTPlan{Float64, 2, SPHEREVSYNTHESIS}) = ccall((:ft_destroy_spher
246253
destroy_plan(p::FTPlan{Float64, 2, SPHEREVANALYSIS}) = ccall((:ft_destroy_sphere_fftw_plan, libfasttransforms), Cvoid, (Ptr{ft_plan_struct}, ), p)
247254
destroy_plan(p::FTPlan{Float64, 2, DISKSYNTHESIS}) = ccall((:ft_destroy_disk_fftw_plan, libfasttransforms), Cvoid, (Ptr{ft_plan_struct}, ), p)
248255
destroy_plan(p::FTPlan{Float64, 2, DISKANALYSIS}) = ccall((:ft_destroy_disk_fftw_plan, libfasttransforms), Cvoid, (Ptr{ft_plan_struct}, ), p)
256+
destroy_plan(p::FTPlan{Float64, 2, RECTDISKSYNTHESIS}) = ccall((:ft_destroy_rectdisk_fftw_plan, libfasttransforms), Cvoid, (Ptr{ft_plan_struct}, ), p)
257+
destroy_plan(p::FTPlan{Float64, 2, RECTDISKANALYSIS}) = ccall((:ft_destroy_rectdisk_fftw_plan, libfasttransforms), Cvoid, (Ptr{ft_plan_struct}, ), p)
249258
destroy_plan(p::FTPlan{Float64, 2, TRIANGLESYNTHESIS}) = ccall((:ft_destroy_triangle_fftw_plan, libfasttransforms), Cvoid, (Ptr{ft_plan_struct}, ), p)
250259
destroy_plan(p::FTPlan{Float64, 2, TRIANGLEANALYSIS}) = ccall((:ft_destroy_triangle_fftw_plan, libfasttransforms), Cvoid, (Ptr{ft_plan_struct}, ), p)
251260
destroy_plan(p::FTPlan{Float64, 3, TETRAHEDRONSYNTHESIS}) = ccall((:ft_destroy_tetrahedron_fftw_plan, libfasttransforms), Cvoid, (Ptr{ft_plan_struct}, ), p)
@@ -299,7 +308,8 @@ unsafe_convert(::Type{Ptr{mpfr_t}}, p::TransposeFTPlan{T, FTPlan{T, N, K}}) wher
299308
for f in (:leg2cheb, :cheb2leg, :ultra2ultra, :jac2jac,
300309
:lag2lag, :jac2ultra, :ultra2jac, :jac2cheb,
301310
:cheb2jac, :ultra2cheb, :cheb2ultra,
302-
:sph2fourier, :sphv2fourier, :disk2cxf, :tri2cheb, :tet2cheb)
311+
:sph2fourier, :sphv2fourier, :disk2cxf,
312+
:rectdisk2cheb, :tri2cheb, :tet2cheb)
303313
plan_f = Symbol("plan_", f)
304314
@eval begin
305315
$plan_f(x::AbstractArray{T}, y...; z...) where T = $plan_f(T, size(x, 1), y...; z...)
@@ -309,8 +319,8 @@ for f in (:leg2cheb, :cheb2leg, :ultra2ultra, :jac2jac,
309319
end
310320

311321
for (f, plan_f) in ((:fourier2sph, :plan_sph2fourier), (:fourier2sphv, :plan_sphv2fourier),
312-
(:cxf2disk2, :plan_disk2cxf), (:cheb2tri, :plan_tri2cheb),
313-
(:cheb2tet, :plan_tet2cheb))
322+
(:cxf2disk, :plan_disk2cxf), (:cheb2rectdisk, :plan_rectdisk2cheb),
323+
(:cheb2tri, :plan_tri2cheb), (:cheb2tet, :plan_tet2cheb))
314324
@eval begin
315325
$f(x::AbstractArray, y...; z...) = $plan_f(x, y...; z...)\x
316326
end
@@ -498,11 +508,16 @@ function plan_sphv2fourier(::Type{Float64}, n::Integer)
498508
return FTPlan{Float64, 2, SPHEREV}(plan, n)
499509
end
500510

501-
function plan_disk2cxf(::Type{Float64}, n::Integer)
502-
plan = ccall((:ft_plan_disk2cxf, libfasttransforms), Ptr{ft_plan_struct}, (Cint, ), n)
511+
function plan_disk2cxf(::Type{Float64}, n::Integer, α, β)
512+
plan = ccall((:ft_plan_disk2cxf, libfasttransforms), Ptr{ft_plan_struct}, (Cint, Float64, Float64), n, α, β)
503513
return FTPlan{Float64, 2, DISK}(plan, n)
504514
end
505515

516+
function plan_rectdisk2cheb(::Type{Float64}, n::Integer, β)
517+
plan = ccall((:ft_plan_rectdisk2cheb, libfasttransforms), Ptr{ft_plan_struct}, (Cint, Float64), n, β)
518+
return FTPlan{Float64, 2, RECTDISK}(plan, n)
519+
end
520+
506521
function plan_tri2cheb(::Type{Float64}, n::Integer, α, β, γ)
507522
plan = ccall((:ft_plan_tri2cheb, libfasttransforms), Ptr{ft_plan_struct}, (Cint, Float64, Float64, Float64), n, α, β, γ)
508523
return FTPlan{Float64, 2, TRIANGLE}(plan, n)
@@ -524,6 +539,8 @@ for (fJ, fC, fE, K) in ((:plan_sph_synthesis, :ft_plan_sph_synthesis, :ft_execut
524539
(:plan_sphv_analysis, :ft_plan_sphv_analysis, :ft_execute_sphv_analysis, SPHEREVANALYSIS),
525540
(:plan_disk_synthesis, :ft_plan_disk_synthesis, :ft_execute_disk_synthesis, DISKSYNTHESIS),
526541
(:plan_disk_analysis, :ft_plan_disk_analysis, :ft_execute_disk_analysis, DISKANALYSIS),
542+
(:plan_rectdisk_synthesis, :ft_plan_rectdisk_synthesis, :ft_execute_rectdisk_synthesis, RECTDISKSYNTHESIS),
543+
(:plan_rectdisk_analysis, :ft_plan_rectdisk_analysis, :ft_execute_rectdisk_analysis, RECTDISKANALYSIS),
527544
(:plan_tri_synthesis, :ft_plan_tri_synthesis, :ft_execute_tri_synthesis, TRIANGLESYNTHESIS),
528545
(:plan_tri_analysis, :ft_plan_tri_analysis, :ft_execute_tri_analysis, TRIANGLEANALYSIS))
529546
@eval begin
@@ -718,6 +735,8 @@ for (fJ, fC, K) in ((:lmul!, :ft_execute_sph2fourier, SPHERE),
718735
(:ldiv!, :ft_execute_fourier2sphv, SPHEREV),
719736
(:lmul!, :ft_execute_disk2cxf, DISK),
720737
(:ldiv!, :ft_execute_cxf2disk, DISK),
738+
(:lmul!, :ft_execute_rectdisk2cheb, RECTDISK),
739+
(:ldiv!, :ft_execute_cheb2rectdisk, RECTDISK),
721740
(:lmul!, :ft_execute_tri2cheb, TRIANGLE),
722741
(:ldiv!, :ft_execute_cheb2tri, TRIANGLE))
723742
@eval begin

src/specialfunctions.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,11 @@ end
551551

552552
trizeros(::Type{T}, m::Int, n::Int) where T = zeros(T, m, n)
553553

554+
const rectdiskrand = trirand
555+
const rectdiskrandn = trirandn
556+
const rectdiskones = triones
557+
const rectdiskzeros = trizeros
558+
554559
"""
555560
Pointwise evaluation of triangular harmonic:
556561

test/libfasttransformstests.jl

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,27 @@ FastTransforms.set_num_threads(ceil(Int, Base.Sys.CPU_THREADS/2))
105105
test_nd_plans(p, ps, pa, A)
106106

107107
A = diskones(Float64, n, 4n-3)
108-
p = plan_disk2cxf(A)
108+
p = plan_disk2cxf(A, α, β)
109109
ps = plan_disk_synthesis(A)
110110
pa = plan_disk_analysis(A)
111111
test_nd_plans(p, ps, pa, A)
112112
A = diskones(Float64, n, 4n-3) + im*diskones(Float64, n, 4n-3)
113-
p = plan_disk2cxf(A)
113+
p = plan_disk2cxf(A, α, β)
114114
ps = plan_disk_synthesis(A)
115115
pa = plan_disk_analysis(A)
116116
test_nd_plans(p, ps, pa, A)
117117

118+
A = rectdiskones(Float64, n, n)
119+
p = plan_rectdisk2cheb(A, β)
120+
ps = plan_rectdisk_synthesis(A)
121+
pa = plan_rectdisk_analysis(A)
122+
test_nd_plans(p, ps, pa, A)
123+
A = rectdiskones(Float64, n, n) + im*rectdiskones(Float64, n, n)
124+
p = plan_rectdisk2cheb(A, β)
125+
ps = plan_rectdisk_synthesis(A)
126+
pa = plan_rectdisk_analysis(A)
127+
test_nd_plans(p, ps, pa, A)
128+
118129
A = triones(Float64, n, n)
119130
p = plan_tri2cheb(A, α, β, γ)
120131
ps = plan_tri_synthesis(A)

0 commit comments

Comments
 (0)