Skip to content

Commit b8253bb

Browse files
authored
Simplify Jacobi conversion branches (#310)
* simplify Jacobi conversion branches * Fix order of operators * Tests, and improved inference in conversion to/from Chebyshev * Add inference tests * comment out unused branch
1 parent e9d177a commit b8253bb

File tree

5 files changed

+119
-55
lines changed

5 files changed

+119
-55
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "ApproxFunOrthogonalPolynomials"
22
uuid = "b70543e2-c0d9-56b8-a290-0d4d6d4de211"
3-
version = "0.6.46"
3+
version = "0.6.47"
44

55
[deps]
66
ApproxFunBase = "fbd15aa5-315a-5a7d-a8a4-24992e37be05"

src/ApproxFunOrthogonalPolynomials.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ import ApproxFunBase: Fun, SubSpace, WeightSpace, NoSpace, HeavisideSpace,
5757
LeftEndPoint, RightEndPoint, normalizedspace, promotedomainspace,
5858
bandmatrices_eigen, SymmetricEigensystem, SkewSymmetricEigensystem,
5959
mean, # differs from Statistics.mean after https://github.com/JuliaApproximation/ApproxFunBase.jl/pull/506
60-
israggedbelow
60+
israggedbelow, bandwidthssum
6161

6262
import DomainSets: Domain, indomain, UnionDomain, FullSpace, Point,
6363
Interval, ChebyshevInterval, boundary, rightendpoint, leftendpoint,

src/Spaces/Jacobi/JacobiOperators.jl

Lines changed: 69 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,15 @@ function _conversion_shiftordersbyone(L::Jacobi, M::Jacobi)
144144
# Conversion(J, M) = Conversion(Jacobi(M.b, L.a, dm), Jacobi(M.b, M.a, dm))
145145
CLJ = [ConcreteConversion(Jacobi(b-1,L.a,dm), Jacobi(b, L.a, dm)) for b in M.b:-1:L.b+1]
146146
CJM = [ConcreteConversion(Jacobi(M.b,a-1,dm), Jacobi(M.b, a, dm)) for a in M.a:-1:L.a+1]
147-
C = [CJM; CLJ]
148-
return ConversionWrapper(TimesOperator(C), L, M)
147+
bw = bandwidthssum(bandwidths, CLJ) .+ bandwidthssum(bandwidths, CJM)
148+
bbw = bandwidthssum(blockbandwidths, CLJ) .+ bandwidthssum(blockbandwidths, CJM)
149+
sbbw = bandwidthssum(subblockbandwidths, CLJ) .+ bandwidthssum(subblockbandwidths, CJM)
150+
op1 = isempty(CJM) ? CLJ[1] : CJM[1]
151+
opend = isempty(CLJ) ? CJM[end] : CLJ[end]
152+
ts = (size(op1, 1), size(opend, 2))
153+
# we deliberately use Operator{T} to improve type-stability in Conversion
154+
C = Operator{eltype(eltype(CLJ))}[CJM; CLJ]
155+
return ConversionWrapper(TimesOperator(C, bw, ts, bbw, sbbw), L, M)
149156
end
150157

151158
## Conversion
@@ -162,28 +169,60 @@ function Conversion(L::Jacobi,M::Jacobi)
162169
dm=domain(M)
163170
dl=domain(L)
164171

165-
if isapproxinteger(L.a-M.a) && isapproxinteger(L.b-M.b) && M.b >= L.b && M.a >= L.a
166-
if isapprox(L.a,M.a) && isapprox(L.b,M.b)
167-
return ConversionWrapper(Operator(I,L))
168-
elseif (isapprox(L.b+1,M.b) && isapprox(L.a,M.a)) ||
169-
(isapprox(L.b,M.b) && isapprox(L.a+1,M.a))
172+
La, Lb = L.a, L.b
173+
Ma, Mb = M.a, M.b
174+
175+
if isapproxinteger(La-Ma) && isapproxinteger(Lb-Mb) && Mb >= Lb && Ma >= La
176+
if isapprox(La,Ma) && isapprox(Lb,Mb)
177+
return ConversionWrapper(Operator(I,L), L, M)
178+
elseif (isapprox(Lb+1,Mb) && isapprox(La,Ma)) ||
179+
(isapprox(Lb,Mb) && isapprox(La+1,Ma))
170180
return ConcreteConversion(L,M)
171-
elseif L.a L.b && isapproxminhalf(L.a) && M.a M.b
172-
return Conversion(L,Chebyshev(dl),Ultraspherical(M),M)
173-
elseif L.a L.b && M.a M.b && isapproxminhalf(M.a)
174-
return Conversion(L,Ultraspherical(L),Chebyshev(dm),M)
175-
elseif L.a L.b && M.a M.b
176-
return Conversion(L,Ultraspherical(L),Ultraspherical(M),M)
181+
elseif La Lb && isapproxminhalf(La) && Ma Mb
182+
C = Chebyshev(dl)
183+
Conv_LC = ConcreteConversion(L, C)
184+
U = Ultraspherical(M)
185+
Conv_UM = ConcreteConversion(U, M)
186+
return ConversionWrapper(Conv_UM * Conversion(C, U) * Conv_LC, L, M)
187+
# return Conversion(L,Chebyshev(dl),Ultraspherical(M),M)
188+
# elseif La ≈ Lb && Ma ≈ Mb && isapproxminhalf(Ma)
189+
# C = Chebyshev(dm)
190+
# Conv_CM = ConcreteConversion(C, M)
191+
# U = Ultraspherical(L)
192+
# Conv_LU = ConcreteConversion(L, U)
193+
# return ConversionWrapper(Conv_CM * Conversion(U, C) * Conv_LU, L, M)
194+
# # return Conversion(L,Ultraspherical(L),Chebyshev(dm),M)
195+
elseif La Lb && Ma Mb && !isapproxminhalf(Ma)
196+
UL = Ultraspherical(L)
197+
Conv_LU = ConcreteConversion(L, UL)
198+
UM = Ultraspherical(M)
199+
Conv_UM = ConcreteConversion(UM, M)
200+
return ConversionWrapper(Conv_UM * ultraconv_nonequal(UL, UM) * Conv_LU, L, M)
201+
# return Conversion(L,Ultraspherical(L),Ultraspherical(M),M)
177202
else
178203
return _conversion_shiftordersbyone(L, M)
179204
end
180-
elseif isapproxhalfoddinteger(L.a - M.a) && isapproxhalfoddinteger(L.b - M.b)
181-
if L.a L.b && M.a M.b && isapproxminhalf(M.a)
182-
return Conversion(L,Ultraspherical(L),Chebyshev(dm),M)
183-
elseif L.a L.b && isapproxminhalf(L.a) && M.a M.b && M.a >= L.a
184-
return Conversion(L,Chebyshev(dl),Ultraspherical(M),M)
185-
elseif L.a L.b && M.a M.b && M.a >= L.a
186-
return Conversion(L,Ultraspherical(L),Ultraspherical(M),M)
205+
elseif isapproxhalfoddinteger(La - Ma) && isapproxhalfoddinteger(Lb - Mb)
206+
if La Lb && Ma Mb && isapproxminhalf(Ma)
207+
C = Chebyshev(dm)
208+
Conv_CM = ConcreteConversion(C, M)
209+
U = Ultraspherical(L)
210+
Conv_LU = ConcreteConversion(L, U)
211+
return ConversionWrapper(Conv_CM * Conversion(U, C) * Conv_LU, L, M)
212+
# return Conversion(L,Ultraspherical(L),Chebyshev(dm),M)
213+
elseif La Lb && isapproxminhalf(La) && Ma Mb && Ma >= La
214+
C = Chebyshev(dl)
215+
Conv_LC = ConcreteConversion(L, C)
216+
U = Ultraspherical(M)
217+
Conv_UM = ConcreteConversion(U, M)
218+
return ConversionWrapper(Conv_UM * Conversion(C, U) * Conv_LC, L, M)
219+
# return Conversion(L,Chebyshev(dl),Ultraspherical(M),M)
220+
elseif La Lb && Ma Mb && Ma >= La
221+
UL = Ultraspherical(L)
222+
Conv_LU = ConcreteConversion(L, UL)
223+
UM = Ultraspherical(M)
224+
Conv_UM = ConcreteConversion(UM, M)
225+
return ConversionWrapper(Conv_UM * Conversion(UL, UM) * Conv_LU, L, M)
187226
end
188227
end
189228

@@ -264,7 +303,7 @@ function Conversion(A::Jacobi, B::Chebyshev)
264303
ConversionWrapper(SpaceOperator(ConcreteConversion(Ultraspherical(A), B), A, B))
265304
elseif A.a == A.b
266305
US = Ultraspherical(A)
267-
ConversionWrapper(SpaceOperator(TimesOperator(Conversion(US,B), ConcreteConversion(A,US)), A, B))
306+
ConversionWrapper(TimesOperator(Conversion(US,B), ConcreteConversion(A,US)), A, B)
268307
else
269308
J = Jacobi(B)
270309
ConcreteConversion(J,B)*Conversion(A,J)
@@ -279,7 +318,7 @@ function Conversion(A::Chebyshev, B::Jacobi)
279318
ConversionWrapper(SpaceOperator(ConcreteConversion(A, Ultraspherical(B)), A, B))
280319
elseif B.a == B.b
281320
US = Ultraspherical(B)
282-
ConversionWrapper(SpaceOperator(TimesOperator(ConcreteConversion(US,B), Conversion(A,US)), A, B))
321+
ConversionWrapper(TimesOperator(ConcreteConversion(US,B), Conversion(A,US)), A, B)
283322
else
284323
J = Jacobi(A)
285324
Conversion(J,B)*ConcreteConversion(A,J)
@@ -291,16 +330,16 @@ end
291330
@assert domain(A) == domain(B)
292331
if isequalminhalf(A.a) && isequalminhalf(A.b)
293332
C = Chebyshev(domain(A))
294-
ConversionWrapper(SpaceOperator(
295-
TimesOperator(Conversion(C,B), ConcreteConversion(A,C)), A, B))
333+
ConversionWrapper(
334+
TimesOperator(Conversion(C,B), ConcreteConversion(A,C)), A, B)
296335
elseif isequalminhalf(A.a - order(B)) && isequalminhalf(A.b - order(B))
297336
ConcreteConversion(A,B)
298337
elseif A.a == A.b == 0
299338
ConversionWrapper(SpaceOperator(Conversion(Ultraspherical(A), B), A, B))
300339
elseif A.a == A.b
301340
US = Ultraspherical(A)
302-
ConversionWrapper(SpaceOperator(
303-
TimesOperator(Conversion(US,B), ConcreteConversion(A,US)), A, B))
341+
ConversionWrapper(
342+
TimesOperator(Conversion(US,B), ConcreteConversion(A,US)), A, B)
304343
else
305344
J = Jacobi(B)
306345
ConcreteConversion(J,B)*Conversion(A,J)
@@ -311,16 +350,16 @@ end
311350
@assert domain(A) == domain(B)
312351
if isequalminhalf(B.a) && isequalminhalf(B.b)
313352
C = Chebyshev(domain(B))
314-
ConversionWrapper(SpaceOperator(
315-
TimesOperator(ConcreteConversion(C, B), Conversion(A, C)), A, B))
353+
ConversionWrapper(
354+
TimesOperator(ConcreteConversion(C, B), Conversion(A, C)), A, B)
316355
elseif isequalminhalf(B.a - order(A)) && isequalminhalf(B.b - order(A))
317356
ConcreteConversion(A,B)
318357
elseif B.a == B.b == 0
319358
ConversionWrapper(SpaceOperator(Conversion(A, Ultraspherical(B)), A, B))
320359
elseif B.a == B.b
321360
US = Ultraspherical(B)
322-
ConversionWrapper(SpaceOperator(
323-
TimesOperator(ConcreteConversion(US,B), Conversion(A,US)), A, B))
361+
ConversionWrapper(
362+
TimesOperator(ConcreteConversion(US,B), Conversion(A,US)), A, B)
324363
else
325364
J = Jacobi(A)
326365
Conversion(J,B)*ConcreteConversion(A,J)

src/Spaces/Ultraspherical/UltrasphericalOperators.jl

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,26 @@ end
166166

167167
maxspace_rule(A::Ultraspherical,B::Chebyshev) = A
168168

169+
function ultraconv_nonequal(A, B)
170+
a=order(A); b=order(B)
171+
d=domain(A)
172+
if -0.5 b-a 1 # -0.5, 0.5 or 1
173+
return ConcreteConversion(A,B)
174+
elseif b-a > 1
175+
r = b:-1:a+1
176+
v = [ConcreteConversion(Ultraspherical(i-1,d), Ultraspherical(i,d)) for i in r]
177+
if !(last(r) a+1)
178+
vlast = ConcreteConversion(A, Ultraspherical(last(r)-1, d))
179+
v2 = Union{eltype(v), typeof(vlast)}[v; vlast]
180+
else
181+
v2 = v
182+
end
183+
bwsum = isapproxinteger(b-a) ? (0, 2length(v)) : (0,ℵ₀)
184+
return ConversionWrapper(TimesOperator(v2, bwsum, (ℵ₀,ℵ₀), bwsum), A, B)
185+
else
186+
throw(ArgumentError("please implement $A$B"))
187+
end
188+
end
169189

170190
function Conversion(A::Ultraspherical,B::Ultraspherical)
171191
@assert domain(A) == domain(B)
@@ -174,20 +194,7 @@ function Conversion(A::Ultraspherical,B::Ultraspherical)
174194
if b==a
175195
return ConversionWrapper(Operator(I,A))
176196
elseif isapproxinteger(b-a) || isapproxhalfoddinteger(b-a)
177-
if -0.5 b-a 1 # -0.5, 0.5 or 1
178-
return ConcreteConversion(A,B)
179-
elseif b-a > 1
180-
r = b:-1:a+1
181-
v = [ConcreteConversion(Ultraspherical(i-1,d), Ultraspherical(i,d)) for i in r]
182-
if !(last(r) a+1)
183-
vlast = ConcreteConversion(A, Ultraspherical(last(r)-1, d))
184-
v2 = Union{eltype(v), typeof(vlast)}[v; vlast]
185-
else
186-
v2 = v
187-
end
188-
bwsum = isapproxinteger(b-a) ? (0, 2length(v)) : (0,ℵ₀)
189-
return ConversionWrapper(TimesOperator(v2, bwsum, (ℵ₀,ℵ₀), bwsum), A, B)
190-
end
197+
return ultraconv_nonequal(A, B)
191198
end
192199
throw(ArgumentError("please implement $A$B"))
193200
end

test/JacobiTest.jl

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -75,25 +75,25 @@ include("testutils.jl")
7575
@testset for J in (J1, NormalizedPolynomialSpace(J1))
7676
g = Fun(f, J)
7777
if !any(isnan, coefficients(g))
78-
@test Conversion(C, J) * f g
78+
@test @inferred(Conversion(C, J) * f) g
7979
end
8080
end
8181
end
8282
# Jacobi(-0.5, -0.5) to NormalizedJacobi(-0.5, -0.5) can have a NaN in the (1,1) index
8383
# if the normalization is not carefully implemented,
8484
# so this test checks that this is not the case
85-
g = Conversion(Jacobi(-0.5,-0.5), NormalizedJacobi(-0.5,-0.5)) * Fun(x->x^3, Jacobi(-0.5, -0.5))
85+
g = @inferred Conversion(Jacobi(-0.5,-0.5), NormalizedJacobi(-0.5,-0.5)) * Fun(x->x^3, Jacobi(-0.5, -0.5))
8686
@test coefficients(g) coefficients(Fun(x->x^3, NormalizedChebyshev()))
8787
end
88-
@testset "legendre" begin
88+
@testset "Legendre" begin
8989
f = Fun(x->x^2, Legendre(d))
9090
C = space(f)
9191
for J1 in (Jacobi(-0.5, -0.5, d), Legendre(d),
9292
Jacobi(0.5, 0.5, d), Jacobi(1, 1, d))
9393
@testset for J in (J1, NormalizedPolynomialSpace(J1))
9494
g = Fun(f, J)
9595
if !any(isnan, coefficients(g))
96-
@test Conversion(C, J) * f g
96+
@test @inferred(Conversion(C, J) * f) g
9797
end
9898
end
9999
end
@@ -106,7 +106,7 @@ include("testutils.jl")
106106
@testset for J in (J1, NormalizedPolynomialSpace(J1))
107107
g = Fun(f, J)
108108
if !any(isnan, coefficients(g))
109-
@test Conversion(U, J) * f g
109+
@test @inferred(Conversion(U, J) * f) g
110110
end
111111
end
112112
end
@@ -119,7 +119,7 @@ include("testutils.jl")
119119
@testset for J in (J1,)
120120
g = Fun(f, J)
121121
if !any(isnan, coefficients(g))
122-
@test Conversion(U, J) * f g
122+
@test @inferred(Conversion(U, J) * f) g
123123
end
124124
end
125125
end
@@ -129,14 +129,32 @@ include("testutils.jl")
129129
@testset for (S1, S2) in ((Legendre(), Jacobi(-0.5, -0.5)), (Jacobi(-0.5, -0.5), Legendre()),
130130
(Legendre(), Jacobi(1.5, 1.5)), (Legendre(), Ultraspherical(0.5)),
131131
(Ultraspherical(0.5), Legendre()))
132-
@test Conversion(S1, S2) * Fun(x->x^4, S1) Fun(x->x^4, S2)
132+
@test @inferred(Conversion(S1, S2) * Fun(x->x^4, S1)) Fun(x->x^4, S2)
133133
end
134134

135135
C = Conversion(Jacobi(Chebyshev()), Ultraspherical(1))
136-
@test C * Fun(x->x^2, Jacobi(-0.5, -0.5)) Fun(x->x^2, Ultraspherical(1))
136+
@test @inferred(C * Fun(x->x^2, Jacobi(-0.5, -0.5))) Fun(x->x^2, Ultraspherical(1))
137137

138138
C = Conversion(Ultraspherical(0.5), Jacobi(Chebyshev()))
139-
@test C * Fun(x->x^2, Ultraspherical(0.5)) Fun(x->x^2, Jacobi(Chebyshev()))
139+
@test @inferred(C * Fun(x->x^2, Ultraspherical(0.5))) Fun(x->x^2, Jacobi(Chebyshev()))
140+
141+
for Lb in -0.5:0.5:2, La in -0.5:0.5:2,
142+
Mb in Lb:1:4, Ma in La:1:4
143+
f = Fun(x -> x^4, Jacobi(Lb, La))
144+
g = Fun(x -> x^4, Jacobi(Mb, Ma))
145+
h = @inferred Conversion(Jacobi(Lb, La), Jacobi(Mb, Ma)) * f
146+
@test space(h) == space(g)
147+
@test h g
148+
end
149+
150+
for Lb in -0.5:0.5:2, Mb in Lb:0.5:4
151+
La = Lb; Ma = Mb
152+
f = Fun(x -> x^4, Jacobi(Lb, La))
153+
g = Fun(x -> x^4, Jacobi(Mb, Ma))
154+
h = @inferred Conversion(Jacobi(Lb, La), Jacobi(Mb, Ma)) * f
155+
@test space(h) == space(g)
156+
@test h g
157+
end
140158

141159
@testset "inference tests" begin
142160
#= Note all cases are inferred as of now,

0 commit comments

Comments
 (0)