Skip to content

Commit f17e026

Browse files
Merge pull request #9 from MikeAClarke/master
RFC: Adds a Padua Transform
2 parents eb9f161 + ed05a6a commit f17e026

File tree

3 files changed

+308
-1
lines changed

3 files changed

+308
-1
lines changed

src/FastTransforms.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import Compat: view
99
export cjt, icjt, jjt, plan_cjt, plan_icjt
1010
export leg2cheb, cheb2leg, leg2chebu, ultra2ultra, jac2jac
1111
export gaunt
12+
export paduatransform, ipaduatransform, paduapoints, paduaeval
13+
export plan_paduatransform, plan_ipaduatransform
1214

1315
# Other module methods and constants:
1416
#export ChebyshevJacobiPlan, jac2cheb, cheb2jac
@@ -20,11 +22,11 @@ export gaunt
2022
#export RecurrencePlan, forward_recurrence!, backward_recurrence
2123

2224
include("fftBigFloat.jl")
23-
2425
include("specialfunctions.jl")
2526
include("clenshawcurtis.jl")
2627
include("fejer.jl")
2728
include("recurrence.jl")
29+
include("PaduaTransform.jl")
2830

2931
abstract FastTransformPlan{D,T}
3032

src/PaduaTransform.jl

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
"""
2+
Pre-plan an Inverse Padua Transform.
3+
"""
4+
immutable IPaduaTransformPlan{DCTPLAN}
5+
cfsmat::Matrix{Float64}
6+
tensorvals::Matrix{Float64}
7+
padvals::Vector{Float64}
8+
dctplan::DCTPLAN
9+
end
10+
11+
function plan_ipaduatransform(v::AbstractVector)
12+
N=length(v)
13+
n=Int(cld(-3+sqrt(1+8N),2))
14+
@assert N==div((n+1)*(n+2),2)
15+
IPaduaTransformPlan(zeros(Float64,n+2,n+1),Array(Float64,n+2,n+1),
16+
Array(Float64,N),FFTW.plan_r2r(Array(eltype(v),n+2,n+1),FFTW.REDFT00))
17+
end
18+
19+
"""
20+
Inverse Padua Transform maps the 2D Chebyshev coefficients to the values of the interpolation polynomial at the Padua points.
21+
"""
22+
function ipaduatransform(P::IPaduaTransformPlan,v::AbstractVector)
23+
cfsmat=trianglecfsmat(P,v)
24+
tensorvals=P.tensorvals
25+
cfsmat[:,2:end-1]=scale!(cfsmat[:,2:end-1],0.5)
26+
cfsmat[2:end-1,:]=scale!(cfsmat[2:end-1,:],0.5)
27+
tensorvals=P.dctplan*cfsmat
28+
paduavals=paduavec(P,tensorvals)
29+
return paduavals
30+
end
31+
32+
function ipaduatransform(v::AbstractVector)
33+
cfsmat=trianglecfsmat(v)
34+
n=size(cfsmat,2)-1
35+
tensorvals=Array(Float64,n+2,n+1)
36+
cfsmat[:,2:end-1]=scale!(cfsmat[:,2:end-1],0.5)
37+
cfsmat[2:end-1,:]=scale!(cfsmat[2:end-1,:],0.5)
38+
tensorvals= FFTW.r2r(cfsmat,FFTW.REDFT00)
39+
paduavals=paduavec(tensorvals)
40+
return paduavals
41+
end
42+
"""
43+
Creates (n+2)x(n+1) Chebyshev coefficient matrix from triangle coefficients.
44+
"""
45+
function trianglecfsmat(P::IPaduaTransformPlan,cfs::AbstractVector)
46+
N=length(cfs)
47+
n=Int(cld(-3+sqrt(1+8N),2))
48+
cfsmat=P.cfsmat
49+
m=1
50+
for d=1:n+1, k=1:d
51+
j=d-k+1
52+
cfsmat[k,j]=cfs[m]
53+
if m==N
54+
return cfsmat
55+
else
56+
m+=1
57+
end
58+
end
59+
return cfsmat
60+
end
61+
62+
function trianglecfsmat(cfs::AbstractVector)
63+
N=length(cfs)
64+
n=Int(cld(-3+sqrt(1+8N),2))
65+
@assert N==div((n+1)*(n+2),2)
66+
cfsmat=zeros(Float64,n+2,n+1)
67+
m=1
68+
for d=1:n+1, k=1:d
69+
j=d-k+1
70+
cfsmat[k,j]=cfs[m]
71+
if m==N
72+
return cfsmat
73+
else
74+
m+=1
75+
end
76+
end
77+
return cfsmat
78+
end
79+
80+
"""
81+
Vectorizes the function values at the Padua points.
82+
"""
83+
function paduavec(P::IPaduaTransformPlan,padmat::Matrix)
84+
n=size(padmat,2)-1
85+
padvals=P.padvals
86+
if iseven(n)>0
87+
d=div(n+2,2)
88+
m=0
89+
for i=1:n+1
90+
padvals[m+1:m+d]=padmat[1+mod(i,2):2:end-1+mod(i,2),i]
91+
m+=d
92+
end
93+
else
94+
padvals=padmat[1:2:end-1]
95+
end
96+
return padvals
97+
end
98+
99+
function paduavec(padmat::Matrix)
100+
n=size(padmat,2)-1
101+
N=div((n+1)*(n+2),2)
102+
padvals=Array(Float64,N)
103+
if iseven(n)>0
104+
d=div(n+2,2)
105+
m=0
106+
for i=1:n+1
107+
padvals[m+1:m+d]=padmat[1+mod(i,2):2:end-1+mod(i,2),i]
108+
m+=d
109+
end
110+
else
111+
padvals=padmat[1:2:end-1]
112+
end
113+
return padvals
114+
end
115+
116+
"""
117+
Pre-plan a Padua Transform.
118+
"""
119+
immutable PaduaTransformPlan{IDCTPLAN}
120+
vals::Matrix{Float64}
121+
tensorcfs::Matrix{Float64}
122+
retvec::Vector{Float64}
123+
idctplan::IDCTPLAN
124+
end
125+
126+
function plan_paduatransform(v::AbstractVector)
127+
N=length(v)
128+
n=Int(cld(-3+sqrt(1+8N),2))
129+
@assert N==div((n+1)*(n+2),2)
130+
PaduaTransformPlan(zeros(Float64,n+2,n+1),Array(Float64,n+2,n+1),
131+
zeros(Float64,N),FFTW.plan_r2r(Array(eltype(v),n+2,n+1),FFTW.REDFT00))
132+
end
133+
134+
"""
135+
Padua Transform maps from interpolant values at the Padua points to the 2D Chebyshev coefficients.
136+
"""
137+
function paduatransform(P::PaduaTransformPlan,v::AbstractVector)
138+
N=length(v)
139+
n=Int(cld(-3+sqrt(1+8N),2))
140+
tensorcfs=P.tensorcfs
141+
vals=paduavalsmat(P,v)
142+
tensorcfs=P.idctplan*vals
143+
tensorcfs=scale!(tensorcfs,2./(n*(n+1.)))
144+
tensorcfs[1,:]=scale!(tensorcfs[1,:],0.5)
145+
tensorcfs[:,1]=scale!(tensorcfs[:,1],0.5)
146+
tensorcfs[end,:]=scale!(tensorcfs[end,:],0.5)
147+
tensorcfs[:,end]=scale!(tensorcfs[:,end],0.5)
148+
cfs=trianglecfsvec(P,tensorcfs)
149+
return cfs
150+
end
151+
152+
function paduatransform(v::AbstractVector)
153+
N=length(v)
154+
n=Int(cld(-3+sqrt(1+8N),2))
155+
tensorcfs=Array(Float64,n+2,n+1)
156+
vals=paduavalsmat(v)
157+
tensorcfs=FFTW.r2r(vals,FFTW.REDFT00)
158+
tensorcfs=scale!(tensorcfs,2./(n*(n+1.)))
159+
tensorcfs[1,:]=scale!(tensorcfs[1,:],0.5)
160+
tensorcfs[:,1]=scale!(tensorcfs[:,1],0.5)
161+
tensorcfs[end,:]=scale!(tensorcfs[end,:],0.5)
162+
tensorcfs[:,end]=scale!(tensorcfs[:,end],0.5)
163+
cfsvec=trianglecfsvec(tensorcfs)
164+
return cfsvec
165+
end
166+
167+
"""
168+
Creates (n+2)x(n+1) matrix of interpolant values on the tensor grid at the (n+1)(n+2)/2 Padua points.
169+
"""
170+
function paduavalsmat(P::PaduaTransformPlan,v::AbstractVector)
171+
N=length(v)
172+
n=Int(cld(-3+sqrt(1+8N),2))
173+
vals=P.vals
174+
if iseven(n)>0
175+
d=div(n+2,2)
176+
m=0
177+
for i=1:n+1
178+
vals[1+mod(i,2):2:end-1+mod(i,2),i]=v[m+1:m+d]
179+
m+=d
180+
end
181+
else
182+
vals[1:2:end]=v
183+
end
184+
return vals
185+
end
186+
187+
function paduavalsmat(v::AbstractVector)
188+
N=length(v)
189+
n=Int(cld(-3+sqrt(1+8N),2))
190+
@assert N==div((n+1)*(n+2),2)
191+
vals=zeros(Float64,n+2,n+1)
192+
if iseven(n)>0
193+
d=div(n+2,2)
194+
m=0
195+
for i=1:n+1
196+
vals[1+mod(i,2):2:end-1+mod(i,2),i]=v[m+1:m+d]
197+
m+=d
198+
end
199+
else
200+
vals[1:2:end]=v
201+
end
202+
return vals
203+
end
204+
205+
"""
206+
Creates length (n+1)(n+2)/2 vector from matrix of triangle Chebyshev coefficients.
207+
"""
208+
function trianglecfsvec(P::PaduaTransformPlan,cfs::Matrix)
209+
m=size(cfs,2)
210+
ret=P.retvec
211+
l=1
212+
for d=1:m,k=1:d
213+
j=d-k+1
214+
ret[l]=cfs[k,j]
215+
l+=1
216+
end
217+
return ret
218+
end
219+
220+
function trianglecfsvec(cfs::Matrix)
221+
n,m=size(cfs)
222+
N=div(n*m,2)
223+
ret=Array(Float64,N)
224+
l=1
225+
for d=1:m,k=1:d
226+
j=d-k+1
227+
ret[l]=cfs[k,j]
228+
l+=1
229+
end
230+
return ret
231+
end
232+
233+
"""
234+
Returns coordinates of the (n+1)(n+2)/2 Padua points.
235+
"""
236+
function paduapoints(n::Integer)
237+
N=div((n+1)*(n+2),2)
238+
MM=Array(Float64,N,2)
239+
m=0
240+
delta=0
241+
NN=fld(n+2,2)
242+
for k=n:-1:0
243+
if isodd(n)>0
244+
delta=mod(k,2)
245+
end
246+
for j=NN+delta:-1:1
247+
m+=1
248+
MM[m,1]=sinpi(1.*k/n-0.5)
249+
if isodd(n-k)>0
250+
MM[m,2]=sinpi((2j-1.)/(n+1.)-0.5)
251+
else
252+
MM[m,2]=sinpi((2j-2.)/(n+1.)-0.5)
253+
end
254+
end
255+
end
256+
return MM
257+
end
258+
259+
"""
260+
Interpolates a 2d function at a given point using 2d Chebyshev series.
261+
"""
262+
function paduaeval(f::Function,x::Float64,y::Float64,m::Integer)
263+
M=div((m+1)*(m+2),2)
264+
pvals=Array(Float64,M)
265+
p=paduapoints(m)
266+
pvals=map(f,p[:,1],p[:,2])
267+
plan=plan_paduatransform(pvals)
268+
coeffs=paduatransform(plan,pvals)
269+
cfs_mat=trianglecfsmat(coeffs)
270+
cfs_mat=cfs_mat[1:end-1,:]
271+
f_x=sum([cfs_mat[k,j]*cos((j-1)*acos(x))*cos((k-1)*acos(y)) for k=1:m+1, j=1:m+1])
272+
return f_x
273+
end

test/runtests.jl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,35 @@ c = randn(1000)./√(1:1000);
205205
@test norm(jac2jac(c,0.,2/2,-1/4,2/2)-jjt(c,0.,2/2,-1/4,2/2),Inf) < 10length(c)*eps()
206206

207207
@test norm(ultra2ultra(ultra2ultra(c,.5,.75),.75,.5)-c,Inf) < 10length(c)*eps()
208+
209+
210+
println("Testing (I)Padua Transforms and their inverse function property")
211+
n=200
212+
N=div((n+1)*(n+2),2)
213+
v=rand(N) #Length of v is the no. of Padua points
214+
215+
@test_approx_eq paduatransform(ipaduatransform(v)) v
216+
@test_approx_eq ipaduatransform(paduatransform(v)) v
217+
218+
println("Testing runtimes for (I)Padua Transforms")
219+
@time paduatransform(v)
220+
@time ipaduatransform(v)
221+
222+
println("Runtimes for Pre-planned (I)Padua Transforms")
223+
n=300
224+
v=rand(N)
225+
Plan=plan_paduatransform(v)
226+
IPlan=plan_ipaduatransform(v)
227+
@time paduatransform(Plan,v)
228+
@time ipaduatransform(IPlan,v)
229+
230+
println("Accuracy of 2d function interpolation at a point")
231+
f_xy = (x,y) -> x^2*y+x^3
232+
g_xy = (x,y) ->cos(exp(2*x+y))*sin(y)
233+
x=0.1;y=0.2
234+
m=130
235+
l=80
236+
f_m=paduaeval(f_xy,x,y,m)
237+
g_l=paduaeval(g_xy,x,y,l)
238+
@test_approx_eq f_xy(x,y) f_m
239+
@test_approx_eq g_xy(x,y) g_l

0 commit comments

Comments
 (0)