Skip to content

Commit 2a3936d

Browse files
authored
Avoid recursion in Chebyshev -> Ultraspherical (#191)
* Avoid recursion in Chebyshev() -> Ultraspherical() * precompute bandwidths
1 parent 0fdd095 commit 2a3936d

File tree

4 files changed

+74
-57
lines changed

4 files changed

+74
-57
lines changed

src/Spaces/Jacobi/JacobiOperators.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ function Conversion(L::Jacobi,M::Jacobi)
178178
end
179179
end
180180

181-
error("Implement for $L$M")
181+
throw(ArgumentError("please implement $A$B"))
182182
end
183183

184184
bandwidths(::ConcreteConversion{<:Jacobi,<:Jacobi}) = (0,1)

src/Spaces/Ultraspherical/Ultraspherical.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ setdomain(S::Ultraspherical,d::Domain) = Ultraspherical(order(S),d)
3636

3737

3838
convert(::Type{Ultraspherical{T,D,R}}, S::Ultraspherical{T,D,R}) where {T,D,R} = S
39+
convert(::Type{Ultraspherical{A,D,R}}, S::Ultraspherical{B,D,R}) where {A,B,D,R} =
40+
Ultraspherical{A,D,R}(convert(A, order(S)), domain(S))
41+
42+
promote_rule(::Type{Ultraspherical{A,D,R}}, ::Type{Ultraspherical{B,D,R}}) where {A,B,D,R} =
43+
Ultraspherical{promote_type(A,B),D,R}
3944

4045

4146
canonicalspace(S::Ultraspherical) = Chebyshev(domain(S))

src/Spaces/Ultraspherical/UltrasphericalOperators.jl

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -124,28 +124,33 @@ end
124124

125125
## Conversion Operator
126126

127+
isequalhalf(x) = x == 0.5
128+
isequalhalf(::Integer) = false
127129

128-
function Conversion(A::Chebyshev,B::Ultraspherical)
129-
if order(B) 1
130-
ConcreteConversion(A,B)
131-
else
132-
d=domain(A)
133-
US=Ultraspherical(order(B)-1,d)
134-
ConversionWrapper(TimesOperator(
135-
ConcreteConversion(US,B), Conversion(A,US)))
130+
function Conversion(A::Chebyshev, B::Ultraspherical)
131+
mB = order(B)
132+
d=domain(A)
133+
dB = domain(B)
134+
d == dB || throw(ArgumentError("domains must be identical"))
135+
if isequalhalf(mB) || mB == 1
136+
return ConcreteConversion(A,B)
137+
elseif (isinteger(mB) || isapproxinteger_addhalf(mB)) && mB > 0
138+
r = mB:-1:(isinteger(mB) ? 2 : 1)
139+
v = [ConcreteConversion(Ultraspherical(i-1, d), Ultraspherical(i,d)) for i in r]
140+
U = domainspace(last(v))
141+
CAU = ConcreteConversion(A, U)
142+
v2 = Union{eltype(v), typeof(CAU)}[v; CAU]
143+
bwsum = (0, (isapproxinteger(mB) ? 2length(v2) : ℵ₀))
144+
return ConversionWrapper(TimesOperator(v2, bwsum, (ℵ₀,ℵ₀), bwsum))
136145
end
146+
throw(ArgumentError("please implement $A$B"))
137147
end
138148

139-
140-
isequalhalf(x) = x == 0.5
141-
isequalhalf(::Integer) = false
142-
143149
function Conversion(A::Ultraspherical,B::Chebyshev)
144-
if isequalhalf(order(A))
145-
ConcreteConversion(A,B)
146-
else
147-
error("Not implemented")
150+
if isequalhalf(order(A)) && domain(A) == domain(B)
151+
return ConcreteConversion(A,B)
148152
end
153+
throw(ArgumentError("please implement $A$B"))
149154
end
150155

151156

@@ -154,27 +159,26 @@ maxspace_rule(A::Ultraspherical,B::Chebyshev) = A
154159

155160
function Conversion(A::Ultraspherical,B::Ultraspherical)
156161
a=order(A); b=order(B)
162+
d=domain(A)
163+
dB = domain(B)
164+
d == dB || throw(ArgumentError("domains must be identical"))
157165
if b==a
158-
ConversionWrapper(Operator(I,A))
166+
return ConversionWrapper(Operator(I,A))
159167
elseif isapproxinteger(b-a) || isapproxinteger_addhalf(b-a)
160168
if -1 b-a 1 && (a,b) (2,1)
161-
ConcreteConversion(A,B)
169+
return ConcreteConversion(A,B)
162170
elseif b-a > 1
163-
d=domain(A)
164-
US=Ultraspherical(b-static(1),d)
165171
r = b:-1:a+1
166172
v = [ConcreteConversion(Ultraspherical(i-1,d), Ultraspherical(i,d)) for i in r]
167173
if !(last(r) a+1)
168174
vlast = ConcreteConversion(A, Ultraspherical(last(r)-1, d))
169175
v = [v; vlast]
170176
end
171-
ConversionWrapper(TimesOperator(v))
172-
else
173-
throw(ArgumentError("Cannot convert from $A to $B"))
177+
bwsum = (0, (isapproxinteger(b-a) ? 2length(v) : ℵ₀))
178+
return ConversionWrapper(TimesOperator(v, bwsum, (ℵ₀,ℵ₀), bwsum))
174179
end
175-
else
176-
throw(ArgumentError("Cannot convert from $A to $B"))
177180
end
181+
throw(ArgumentError("please implement $A$B"))
178182
end
179183

180184
maxspace_rule(A::Ultraspherical,B::Ultraspherical) = order(A) > order(B) ? A : B

test/UltrasphericalTest.jl

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ using LinearAlgebra
66
using Static
77

88
@verbose @testset "Ultraspherical" begin
9+
@testset "promotion" begin
10+
@inferred (() -> [Ultraspherical(1), Ultraspherical(2.0)])()
11+
end
912
@testset "identity fun" begin
1013
for d in (ChebyshevInterval(), 3..4, Segment(2, 5), Segment(1, 4im)), order in (1, 2, 0.5)
1114
s = Ultraspherical(order, d)
@@ -25,27 +28,27 @@ using Static
2528

2629
# Conversions from Chebyshev to Ultraspherical should lead to a small union of types
2730
Tallowed = Union{
28-
ApproxFunBase.ConcreteConversion{
29-
Chebyshev{ChebyshevInterval{Float64}, Float64},
30-
Ultraspherical{Int64, ChebyshevInterval{Float64}, Float64}, Float64},
31-
ApproxFunBase.ConversionWrapper{TimesOperator{Float64, Tuple{Int64, Int64}}, Float64}};
31+
typeof(Conversion(Chebyshev(), Ultraspherical(1))),
32+
typeof(Conversion(Chebyshev(), Ultraspherical(2)))};
3233
@inferred Tallowed Conversion(Chebyshev(), Ultraspherical(1));
3334
@inferred Tallowed Conversion(Chebyshev(), Ultraspherical(2));
35+
@inferred Conversion(Chebyshev(), Ultraspherical(static(2)));
36+
@inferred Conversion(Chebyshev(), Ultraspherical(static(0.5)));
37+
@inferred (() -> Conversion(Chebyshev(), Ultraspherical(2)))();
38+
@inferred (() -> Conversion(Chebyshev(), Ultraspherical(0.5)))();
39+
@inferred (() -> Conversion(Chebyshev(), Ultraspherical(2.5)))();
40+
3441
# Conversions between Ultraspherical should lead to a small union of types
3542
Tallowed = Union{
36-
ApproxFunBase.ConcreteConversion{
37-
Ultraspherical{Int64, ChebyshevInterval{Float64}, Float64},
38-
Ultraspherical{Int64, ChebyshevInterval{Float64}, Float64}, Float64},
39-
ApproxFunBase.ConversionWrapper{
40-
ConstantOperator{Float64,
41-
Ultraspherical{Int64, ChebyshevInterval{Float64}, Float64}}, Float64},
42-
ApproxFunBase.ConversionWrapper{TimesOperator{Float64, Tuple{Int64, Int64}}, Float64}};
43+
typeof(Conversion(Ultraspherical(1), Ultraspherical(2))),
44+
typeof(Conversion(Ultraspherical(1), Ultraspherical(3)))}
4345
@inferred Tallowed Conversion(Ultraspherical(1), Ultraspherical(2));
46+
@inferred Tallowed Conversion(Ultraspherical(1), Ultraspherical(3));
4447

4548
@inferred Conversion(Ultraspherical(static(1)), Ultraspherical(static(4)))
46-
# these cases should be handled by constant-propagation
47-
@inferred (() -> Conversion(Ultraspherical(static(1)), Ultraspherical(4)))()
48-
@inferred (() -> Conversion(Ultraspherical(1), Ultraspherical(static(4))))()
49+
@inferred (() -> Conversion(Ultraspherical(1), Ultraspherical(4)))()
50+
@inferred (() -> Conversion(Ultraspherical(1.0), Ultraspherical(4.0)))();
51+
@inferred (() -> Conversion(Ultraspherical(1.0), Ultraspherical(3.5)))();
4952

5053
for n in (2,5)
5154
C1 = Conversion(Chebyshev(), Ultraspherical(n))
@@ -62,19 +65,24 @@ using Static
6265
g = CLC * f
6366
@test g Fun(x->x^2, Chebyshev())
6467

65-
f = Fun(x->x^2, Chebyshev()) # Legendre
66-
CCL = Conversion(Chebyshev(), Ultraspherical(0.5))
67-
@test !isdiag(CCL)
68-
g = CCL * f
69-
@test g Fun(x->x^2, Ultraspherical(0.5))
70-
71-
f = Fun(x->x^2, Ultraspherical(0.5)) # Legendre
72-
for n in (2.5, 3)
73-
CLU = Conversion(Ultraspherical(0.5), Ultraspherical(n))
74-
@test !isdiag(CLU)
75-
g = CLU * f
68+
for n in (0.5, 1, 1.5, 2)
69+
f = Fun(x->x^2, Chebyshev())
70+
CCL = Conversion(Chebyshev(), Ultraspherical(n))
71+
@test !isdiag(CCL)
72+
g = CCL * f
7673
@test g Fun(x->x^2, Ultraspherical(n))
7774
end
75+
76+
ff = x->x^2 +2x^3 + 3x^4
77+
for n1 in (0.5, 1)
78+
f = Fun(ff, Ultraspherical(n1))
79+
for n2 in (2.5, 3)
80+
CLU = Conversion(Ultraspherical(n1), Ultraspherical(n2))
81+
@test !isdiag(CLU)
82+
g = CLU * f
83+
@test g Fun(ff, Ultraspherical(n1))
84+
end
85+
end
7886
end
7987

8088
@testset "Normalized space" begin
@@ -110,12 +118,12 @@ using Static
110118
@test transform(S, v) transform(J, v)
111119
end
112120
@testset for T in (Float32, Float64), ET in (T, complex(T))
113-
v = Array{ET}(undef, 10)
114-
v2 = similar(v)
115-
M = Array{ET}(undef, 10, 10)
116-
M2 = similar(M)
117-
A = Array{ET}(undef, 10, 10, 10)
118-
A2 = similar(A)
121+
v = Array{ET}(undef, 10);
122+
v2 = similar(v);
123+
M = Array{ET}(undef, 10, 10);
124+
M2 = similar(M);
125+
A = Array{ET}(undef, 10, 10, 10);
126+
A2 = similar(A);
119127
@testset for d in ((), (0..1,)), order in (0.5, 0.7, 1.5, 1, 3)
120128
U = Ultraspherical(order, d...)
121129
NU = NormalizedPolynomialSpace(U)

0 commit comments

Comments
 (0)