Skip to content

Commit beeed8c

Browse files
JilhgJilhg
andauthored
Add convolution operator ApproxFun.jl#940 (#127)
* Add Convolution operator * typo in description * overload getindex instead of defaultgetindex * overload getindex instead of ApproxFunBase.getindex --------- Co-authored-by: Jilhg <[email protected]>
1 parent e8b45c8 commit beeed8c

File tree

3 files changed

+107
-0
lines changed

3 files changed

+107
-0
lines changed

src/ApproxFunFourier.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,4 +776,6 @@ function show(io::IO, S::SpaceTypes)
776776
print(io, ")")
777777
end
778778

779+
include("Convolution.jl")
780+
779781
end #module

src/Convolution.jl

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
export Convolution
2+
3+
"""
4+
Convolution(G)
5+
6+
Represents a convolution operator with function `G`. When applied to a function `f` it computes `∫_D G(x-y) f(y) dy for x ∈ D`. For now `D` is a `PeriodigSegment(a,b)` with `a<b∈R` and `space(G)` is either `Laurent(D)` or `Fourier(D)`.
7+
"""
8+
9+
abstract type Convolution{S,T} <: Operator{T} end
10+
11+
struct ConcreteConvolution{S<:Space,T} <: Convolution{S,T}
12+
G::Fun{S,T}
13+
end
14+
15+
function Convolution(G::Fun{S,T}) where {S,T}
16+
@assert isfinite(arclength(domain(G)))
17+
ConcreteConvolution(G)
18+
end
19+
ApproxFunBase.domain(C::ConcreteConvolution) = ApproxFunBase.domain(C.G)
20+
ApproxFunBase.domainspace(C::ConcreteConvolution) = ApproxFunBase.space(C.G)
21+
ApproxFunBase.rangespace(C::ConcreteConvolution) = ApproxFunBase.space(C.G)
22+
ApproxFunBase.bandwidths(C::ConcreteConvolution) = error("Please implement convolution bandwidths on "*string(space(C.G)))
23+
getindex(C::ConcreteConvolution,k::Integer,j::Integer) = error("Please implement convolution getindex on "*string(space(C.G)))
24+
25+
ApproxFunBase.bandwidths(C::ConcreteConvolution{Laurent{PeriodicSegment{R},T}}) where {R<:Real,T} = 0,0
26+
ApproxFunBase.bandwidths(C::ConcreteConvolution{Fourier{PeriodicSegment{R},T}}) where {R<:Real,T} = 1,1
27+
28+
function getindex(C::ConcreteConvolution{Laurent{PeriodicSegment{R},T1},T2},k::Integer,j::Integer) where {R<:Real,T1,T2}
29+
fourier_index::Integer = if isodd(k) div(k-1,2) else -div(k,2) end
30+
if k == j && k ncoefficients(C.G)
31+
return (exp(-2pi*1im/arclength(domain(C.G))*fourier_index*first(domain(C.G)))*arclength(domain(C.G))*C.G.coefficients[k])::T2
32+
else
33+
return zero(T2)
34+
end
35+
end
36+
37+
function getindex(C::ConcreteConvolution{Fourier{PeriodicSegment{R},T1},T2},k::Integer,j::Integer) where {R<:Real,T1,T2}
38+
fourier_index::Integer = if isodd(k) div(k-1,2) else div(k,2) end
39+
if k<1 || j<1 || ncoefficients(C.G)==0
40+
return zero(T2)
41+
elseif k == 1
42+
if j==k
43+
return (arclength(domain(C.G))*C.G.coefficients[1])::T2
44+
else
45+
return zero(T2)
46+
end
47+
elseif 2*fourier_index ncoefficients(C.G)
48+
Gs = if 2*fourier_index ncoefficients(C.G) C.G.coefficients[2*fourier_index] else zero(T2) end # sine coefficient
49+
Gc = if 2*fourier_index+1 ncoefficients(C.G) C.G.coefficients[2*fourier_index+1] else zero(T2) end # cosine coefficient
50+
phase = 2pi/arclength(domain(C.G))*fourier_index*first(domain(C.G))
51+
if iseven(k) && j==k
52+
return (arclength(domain(C.G))*(Gc*cos(phase)-Gs*sin(phase))/2)::T2
53+
elseif iseven(k) && j==k+1
54+
return (arclength(domain(C.G))*(Gc*sin(phase)+Gs*cos(phase))/2)::T2
55+
elseif isodd(k) && j==k
56+
return (arclength(domain(C.G))*(Gc*cos(phase)-Gs*sin(phase))/2)::T2
57+
elseif isodd(k) && j==k-1
58+
return (arclength(domain(C.G))*(-Gc*sin(phase)-Gs*cos(phase))/2)::T2
59+
else
60+
return zero(T2)
61+
end
62+
else
63+
return zero(T2)
64+
end
65+
end
66+

test/runtests.jl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,3 +806,42 @@ end
806806
end
807807
end
808808
end
809+
810+
@testset "Convolution" begin
811+
@test bandwidths(Convolution(ones(Laurent())))==(0,0)
812+
@test bandwidths(Convolution(ones(Fourier())))==(1,1)
813+
814+
atol=1e-6
815+
rtol=1e-6
816+
817+
for S=[Laurent(-15..3),Fourier(-15..3)]
818+
f1=ones(S)
819+
f2=ones(S)
820+
f1f2_theory=18*ones(S)
821+
f1f2=Convolution(f1)*f2
822+
@test (f1f2,f1f2_theory,atol=atol,rtol=rtol)
823+
@test (DefiniteIntegral()*f1f2,(DefiniteIntegral()*f1)*(DefiniteIntegral()*f2),atol=atol,rtol=rtol)
824+
end
825+
826+
for S=[Laurent(-2..3),Fourier(-2..3)]
827+
f1=Fun(x->exp(-5*(x-0.5)^2),S)
828+
f2=Fun(x->exp(-5*x^2),S)
829+
f1f2_theory=Fun(x->exp(-5*(x-0.5)^2/2)*sqrt(2pi)/10*sqrt(5),S)
830+
f1f2=Convolution(f1)*f2
831+
f2f1=Convolution(f2)*f1
832+
@test (f1f2,f1f2_theory,atol=atol,rtol=rtol)
833+
@test (f2f1,f1f2_theory,atol=atol,rtol=rtol)
834+
@test (DefiniteIntegral()*f1f2,(DefiniteIntegral()*f1)*(DefiniteIntegral()*f2),atol=atol,rtol=rtol)
835+
@test (DefiniteIntegral()*f2f1,(DefiniteIntegral()*f1)*(DefiniteIntegral()*f2),atol=atol,rtol=rtol)
836+
end
837+
for S=[Laurent(0..2pi),Fourier(0..2pi)]
838+
f1=Fun(x->exp(1im*x),S)
839+
f2=Fun(x->exp(1im*x),S)
840+
f1f2_theory=Fun(x->2pi*exp(1im*x),S)
841+
f1f2=Convolution(f1)*f2
842+
@test (f1f2,f1f2_theory,atol=atol,rtol=rtol)
843+
@test (DefiniteIntegral()*f1f2,(DefiniteIntegral()*f1)*(DefiniteIntegral()*f2),atol=atol,rtol=rtol)
844+
845+
@test Convolution(Fun(S,[]))*f10
846+
end
847+
end

0 commit comments

Comments
 (0)