Skip to content

Commit 4239507

Browse files
authored
Use polynomial transforms from FastTransforms (#226)
* use poly transforms from FastTransforms * add link to issue in test * coefficient conversions to normalized polynomials * short-circuit for same space * disable ultra transforms * reuse chebyshev-integer ultraspherical methods * ultra2ultra for non-integer cases * don't delete extra line in Chebyshev * don't delete extra lines in Ultraspherical * reduce infnorm tolerance in jacobi test
1 parent 69c583a commit 4239507

File tree

6 files changed

+140
-53
lines changed

6 files changed

+140
-53
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ BlockArrays = "0.14, 0.15, 0.16"
2828
BlockBandedMatrices = "0.10, 0.11"
2929
DomainSets = "0.5, 0.6"
3030
FastGaussQuadrature = "0.4, 0.5"
31-
FastTransforms = "0.12, 0.13, 0.14, 0.15"
31+
FastTransforms = "0.12, 0.13, 0.14"
3232
FillArrays = "0.11, 0.12, 0.13"
3333
IntervalSets = "0.5, 0.6, 0.7"
3434
LazyArrays = "0.22"

src/Spaces/Jacobi/jacobitransform.jl

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -36,28 +36,3 @@ icjt(P, cfs, ::Val{false}) = P * cfs
3636
function *(P::JacobiITransformPlan, cfs::AbstractVector)
3737
P.ichebplan * icjt(P.icjtplan, cfs, Val(inplace(P)))
3838
end
39-
40-
41-
function coefficients(f::AbstractVector{T}, a::Jacobi, b::Chebyshev) where T
42-
if domainscompatible(a, b) && !(isapproxinteger_addhalf(a.a) && isapproxinteger_addhalf(a.b))
43-
jac2cheb(f, strictconvert(T,a.a), strictconvert(T,a.b))
44-
else
45-
defaultcoefficients(f,a,b)
46-
end
47-
end
48-
function coefficients(f::AbstractVector{T}, a::Chebyshev, b::Jacobi) where T
49-
isempty(f) && return f
50-
if domainscompatible(a, b) && !(isapproxinteger_addhalf(b.a) && isapproxinteger_addhalf(b.b))
51-
cheb2jac(f, strictconvert(T,b.a), strictconvert(T,b.b))
52-
else
53-
defaultcoefficients(f,a,b)
54-
end
55-
end
56-
57-
function coefficients(f::AbstractVector,a::Jacobi,b::Jacobi)
58-
if domainscompatible(a, b) && !(isapproxinteger(a.a-b.a) && isapproxinteger(a.b-b.b))
59-
jac2jac(f,a.a,a.b,b.a,b.b)
60-
else
61-
defaultcoefficients(f,a,b)
62-
end
63-
end

src/Spaces/Spaces.jl

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,131 @@ function getindex(C::ConcreteConversion{<:HeavisideSpace,
4545
k::Integer,j::Integer)
4646
k dimension(domainspace(C)) && j==k ? one(eltype(C)) : zero(eltype(C))
4747
end
48+
49+
# Fast conversion between common bases using FastTransforms
50+
51+
# The chebyshev-ultraspherical transforms are currently very slow,
52+
# see https://github.com/JuliaApproximation/FastTransforms.jl/issues/204
53+
# We therefore go through hoops to only call these for non-integral Ultraspherical orders
54+
55+
function _changepolybasis(v::StridedVector{T},
56+
C::MaybeNormalized{<:Chebyshev{<:ChebyshevInterval}},
57+
U::MaybeNormalized{<:Ultraspherical{<:Any,<:ChebyshevInterval}},
58+
) where {T<:AbstractFloat}
59+
60+
normcheb = C isa NormalizedPolynomialSpace
61+
normultra = U isa NormalizedPolynomialSpace
62+
Uc = normultra ? _stripnorm(U) : U
63+
Cc = normcheb ? _stripnorm(C) : C
64+
if order(U) == 1
65+
v = normcheb ? ApproxFunBase.mul_coefficients(ConcreteConversion(C, Cc), v) : v
66+
vc = ultraconversion(v)
67+
normultra ? ApproxFunBase.mul_coefficients!(ConcreteConversion(Uc, U), vc) : vc
68+
elseif order(U) isa Union{Integer, StaticInt}
69+
coefficients(v, C, Ultraspherical(1,domain(U)), U)
70+
else
71+
cheb2ultra(v, strictconvert(T, order(U)); normcheb, normultra)
72+
end
73+
end
74+
function _changepolybasis(v::StridedVector{T},
75+
U::MaybeNormalized{<:Ultraspherical{<:Any,<:ChebyshevInterval}},
76+
C::MaybeNormalized{<:Chebyshev{<:ChebyshevInterval}}) where {T<:AbstractFloat}
77+
78+
normultra = U isa NormalizedPolynomialSpace
79+
normcheb = C isa NormalizedPolynomialSpace
80+
Uc = normultra ? _stripnorm(U) : U
81+
Cc = normcheb ? _stripnorm(C) : C
82+
if order(U) == 1
83+
v = normultra ? ApproxFunBase.mul_coefficients(ConcreteConversion(U, Uc), v) : v
84+
vc = ultraiconversion(v)
85+
normcheb ? ApproxFunBase.mul_coefficients!(ConcreteConversion(Cc, C), vc) : vc
86+
elseif order(U) isa Union{Integer, StaticInt}
87+
coefficients(v, U, Ultraspherical(1,domain(U)), C)
88+
else
89+
ultra2cheb(v, strictconvert(T, order(U)); normultra, normcheb)
90+
end
91+
end
92+
function _changepolybasis(v::StridedVector{T},
93+
U1::MaybeNormalized{<:Ultraspherical{<:Any,<:ChebyshevInterval}},
94+
U2::MaybeNormalized{<:Ultraspherical{<:Any,<:ChebyshevInterval}},
95+
) where {T<:AbstractFloat}
96+
97+
if isinteger(order(U1) - order(U2))
98+
defaultcoefficients(v, U1, U2)
99+
else
100+
norm1 = U1 isa NormalizedPolynomialSpace
101+
norm2 = U2 isa NormalizedPolynomialSpace
102+
ultra2ultra(v, strictconvert(T, order(U1)), strictconvert(T, order(U2)); norm1, norm2)
103+
end
104+
end
105+
106+
function _changepolybasis(v::StridedVector{T},
107+
C::MaybeNormalized{<:Chebyshev{<:ChebyshevInterval}},
108+
J::MaybeNormalized{<:Jacobi{<:ChebyshevInterval}},
109+
) where {T<:AbstractFloat}
110+
111+
normcheb = C isa NormalizedPolynomialSpace
112+
normjac = J isa NormalizedPolynomialSpace
113+
Jc = _stripnorm(J)
114+
if Jc.a == 0 && Jc.b == 0
115+
cheb2leg(v; normcheb, normleg = normjac)
116+
else
117+
cheb2jac(v, strictconvert(T,Jc.a), strictconvert(T,Jc.b); normcheb, normjac)
118+
end
119+
end
120+
function _changepolybasis(v::StridedVector{T},
121+
J::MaybeNormalized{<:Jacobi{<:ChebyshevInterval}},
122+
C::MaybeNormalized{<:Chebyshev{<:ChebyshevInterval}},
123+
) where {T<:AbstractFloat}
124+
125+
normcheb = C isa NormalizedPolynomialSpace
126+
normjac = J isa NormalizedPolynomialSpace
127+
Jc = _stripnorm(J)
128+
if Jc.a == 0 && Jc.b == 0
129+
leg2cheb(v; normcheb, normleg = normjac)
130+
else
131+
jac2cheb(v, strictconvert(T,Jc.a), strictconvert(T,Jc.b); normcheb, normjac)
132+
end
133+
end
134+
function _changepolybasis(v::StridedVector{T},
135+
U::MaybeNormalized{<:Ultraspherical{<:Any,<:ChebyshevInterval}},
136+
J::MaybeNormalized{<:Jacobi{<:ChebyshevInterval}},
137+
) where {T<:AbstractFloat}
138+
139+
normultra = U isa NormalizedPolynomialSpace
140+
normjac = J isa NormalizedPolynomialSpace
141+
Jc = _stripnorm(J)
142+
ultra2jac(v, strictconvert(T,order(U)), strictconvert(T,Jc.a), strictconvert(T,Jc.b);
143+
normultra, normjac)
144+
end
145+
function _changepolybasis(v::StridedVector{T},
146+
J::MaybeNormalized{<:Jacobi{<:ChebyshevInterval}},
147+
U::MaybeNormalized{<:Ultraspherical{<:Any,<:ChebyshevInterval}},
148+
) where {T<:AbstractFloat}
149+
150+
normjac = J isa NormalizedPolynomialSpace
151+
normultra = U isa NormalizedPolynomialSpace
152+
Jc = _stripnorm(J)
153+
jac2ultra(v, strictconvert(T,Jc.a), strictconvert(T,Jc.b), strictconvert(T,order(U));
154+
normultra, normjac)
155+
end
156+
function _changepolybasis(v::StridedVector{T},
157+
J1::MaybeNormalized{<:Jacobi{<:ChebyshevInterval}},
158+
J2::MaybeNormalized{<:Jacobi{<:ChebyshevInterval}},
159+
) where {T<:AbstractFloat}
160+
161+
norm1 = J1 isa NormalizedPolynomialSpace
162+
norm2 = J2 isa NormalizedPolynomialSpace
163+
J1c = _stripnorm(J1)
164+
J2c = _stripnorm(J2)
165+
jac2jac(v, strictconvert(T,J1c.a), strictconvert(T,J1c.b), strictconvert(T,J2c.a), strictconvert(T,J2c.b);
166+
norm1, norm2)
167+
end
168+
_changepolybasis(v, a, b) = defaultcoefficients(v, a, b)
169+
170+
function coefficients(f::AbstractVector{T},
171+
a::MaybeNormalized{<:Union{Chebyshev,Ultraspherical,Jacobi}},
172+
b::MaybeNormalized{<:Union{Chebyshev,Ultraspherical,Jacobi}}) where T
173+
spacescompatible(a,b) && return f
174+
_changepolybasis(f, a, b)
175+
end

src/Spaces/Ultraspherical/UltrasphericalOperators.jl

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,10 @@ function Conversion(A::Ultraspherical,B::Ultraspherical)
190190
throw(ArgumentError("please implement $A$B"))
191191
end
192192

193-
maxspace_rule(A::Ultraspherical,B::Ultraspherical) = order(A) > order(B) ? A : B
194-
193+
function maxspace_rule(A::Ultraspherical, B::Ultraspherical)
194+
isapproxinteger(order(A) - order(B)) || return NoSpace()
195+
order(A) > order(B) ? A : B
196+
end
195197

196198
function getindex(M::ConcreteConversion{<:Chebyshev,U,T},
197199
k::Integer,j::Integer) where {T, U<:Ultraspherical{<:Union{Integer, StaticInt}}}
@@ -292,27 +294,6 @@ function _conversion_rule(a::Ultraspherical, b::Ultraspherical)
292294
end
293295
end
294296

295-
296-
function coefficients(g::AbstractVector,sp::Ultraspherical{<:Union{Integer,StaticInt}},C::Chebyshev)
297-
domainscompatible(C,sp) || throw(ArgumentError("domains don't match: $(domain(sp)) and $(domain(C))"))
298-
if order(sp) == 1
299-
ultraiconversion(g)
300-
else
301-
# do one at a time
302-
coefficients(g,sp,Ultraspherical(1,domain(sp)),C)
303-
end
304-
end
305-
function coefficients(g::AbstractVector,C::Chebyshev,sp::Ultraspherical)
306-
domainscompatible(C,sp) || throw(ArgumentError("domains don't match: $(domain(C)) and $(domain(sp))"))
307-
if order(sp) == 1
308-
ultraconversion(g)
309-
else
310-
# do one at a time
311-
coefficients(g,C,Ultraspherical(1,domain(sp)),sp)
312-
end
313-
end
314-
315-
316297
# TODO: include in getindex to speed up
317298
function Integral(sp::Chebyshev{DD}, m::Number) where {DD<:IntervalOrSegment}
318299
assert_integer(m)

test/ChebyshevTest.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ using ApproxFunOrthogonalPolynomials: forwardrecurrence
378378
sp in Any[_sp, NormalizedPolynomialSpace(_sp)]
379379
d = domain(sp)
380380
f = Fun(sp, c)
381-
for ep in [leftendpoint, rightendpoint],
381+
@testset for ep in [leftendpoint, rightendpoint],
382382
ev in [ApproxFunBase.ConcreteEvaluation, Evaluation]
383383
E = @inferred ev(sp, ep, 0)
384384
@test E[2:4] E[1:4][2:end]

test/JacobiTest.jl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,9 +344,12 @@ using Static
344344
f = Fun(x->cospi(1000x))
345345
g = Fun(f,Legendre())
346346
h = Fun(g,Chebyshev())
347-
@test norm(f.coefficients-h.coefficients,Inf) < 1000eps()
348-
@time h = Fun(h,Legendre())
349-
@test norm(g.coefficients-h.coefficients,Inf) < 10000eps()
347+
@test norm(coefficients(f) - coefficients(h), Inf) < 1000eps()
348+
@time j = Fun(h,Legendre())
349+
# The accuracy in the following may be improved,
350+
# See https://github.com/JuliaApproximation/FastTransforms.jl/issues/201
351+
# On FastTransforms v0.15, an upper bound of 60000eps() works
352+
@test norm(coefficients(g) - coefficients(j), Inf) < 10000eps()
350353
end
351354

352355
@testset "conversion for non-compatible paramters" begin

0 commit comments

Comments
 (0)