Skip to content

Commit e0af376

Browse files
authored
Weighted and HalfWeighted for simpler operators (#21)
* Add special support for Weighted OPs * Use HalfWeighted to give some structure to derivative operators * increase coverage
1 parent 0084985 commit e0af376

File tree

7 files changed

+181
-28
lines changed

7 files changed

+181
-28
lines changed

src/ClassicalOrthogonalPolynomials.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ recurrencecoefficients(P) = error("Override for $(typeof(P))")
149149

150150
const WeightedOrthogonalPolynomial{T, A<:AbstractQuasiVector, B<:OrthogonalPolynomial} = WeightedBasis{T, A, B}
151151

152+
function isorthogonalityweighted(wS::WeightedOrthogonalPolynomial)
153+
w,S = wS.args
154+
w == orthogonalityweight(S)
155+
end
156+
152157
"""
153158
singularities(f)
154159

src/jacobi.jl

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,46 @@ WeightedJacobi(a,b) = JacobiWeight(a,b) .* Jacobi(a,b)
142142
WeightedJacobi{T}(a,b) where T = JacobiWeight{T}(a,b) .* Jacobi{T}(a,b)
143143

144144

145+
"""
146+
HalfWeighted{lr}(Jacobi(a,b))
147+
148+
is equivalent to `JacobiWeight(a,0) .* Jacobi(a,b)` (`lr = :a`) or
149+
`JacobiWeight(0,b) .* Jacobi(a,b)` (`lr = :b`)
150+
"""
151+
struct HalfWeighted{lr, T, PP<:AbstractQuasiMatrix{T}} <: Basis{T}
152+
P::PP
153+
end
154+
155+
HalfWeighted{lr}(P) where lr = HalfWeighted{lr,eltype(P),typeof(P)}(P)
156+
157+
axes(Q::HalfWeighted) = axes(Q.P)
158+
copy(Q::HalfWeighted) = Q
159+
160+
==(A::HalfWeighted, B::HalfWeighted) = A.P == B.P
161+
162+
convert(::Type{WeightedJacobi}, Q::HalfWeighted{:a,T}) where T = JacobiWeight(Q.P.a,zero(T)) .* Q.P
163+
convert(::Type{WeightedJacobi}, Q::HalfWeighted{:b,T}) where T = JacobiWeight(zero(T),Q.P.b) .* Q.P
164+
165+
getindex(Q::HalfWeighted, x::Union{Number,AbstractVector}, jr::Union{Number,AbstractVector}) = convert(WeightedJacobi, Q)[x,jr]
166+
167+
broadcasted(::LazyQuasiArrayStyle{2}, ::typeof(*), x::Inclusion, Q::HalfWeighted) = Q * (Q.P \ (x .* Q.P))
168+
169+
\(w_A::HalfWeighted, w_B::HalfWeighted) = convert(WeightedJacobi, w_A) \ convert(WeightedJacobi, w_B)
170+
\(w_A::HalfWeighted, B::AbstractQuasiArray) = convert(WeightedJacobi, w_A) \ B
171+
\(A::AbstractQuasiArray, w_B::HalfWeighted) = A \ convert(WeightedJacobi, w_B)
172+
145173
axes(::AbstractJacobi{T}) where T = (Inclusion{T}(ChebyshevInterval{real(T)}()), oneto(∞))
146174
==(P::Jacobi, Q::Jacobi) = P.a == Q.a && P.b == Q.b
147175
==(P::Legendre, Q::Jacobi) = Jacobi(P) == Q
148176
==(P::Jacobi, Q::Legendre) = P == Jacobi(Q)
149177
==(A::WeightedJacobi, B::WeightedJacobi) = A.args == B.args
150178
==(A::WeightedJacobi, B::Jacobi{T}) where T = A == JacobiWeight(zero(T),zero(T)).*B
151179
==(A::WeightedJacobi, B::Legendre) = A == Jacobi(B)
152-
==(A::Jacobi{T}, B::WeightedJacobi) where T = JacobiWeight(zero(T),zero(T)).*A == B
153180
==(A::Legendre, B::WeightedJacobi) = Jacobi(A) == B
181+
==(A::Jacobi{T}, B::WeightedJacobi) where T = JacobiWeight(zero(T),zero(T)).*A == B
182+
==(A::Legendre, B::Weighted{<:Any,<:AbstractJacobi}) = A == B.P
183+
==(A::Weighted{<:Any,<:AbstractJacobi}, B::Legendre) = A.P == B
184+
154185

155186
summary(io::IO, P::Jacobi) = print(io, "Jacobi($(P.a), $(P.b))")
156187

@@ -290,6 +321,16 @@ function \(w_A::WeightedJacobi, B::Jacobi)
290321
w_A \ (JacobiWeight(zero(a),zero(b)) .* B)
291322
end
292323

324+
function \(A::AbstractJacobi, w_B::WeightedJacobi)
325+
= Jacobi(A)
326+
(A \ Ã) * (Ã \ w_B)
327+
end
328+
function \(w_A::WeightedJacobi, B::AbstractJacobi)
329+
= Jacobi(B)
330+
(w_A \ B̃) * (B̃ \ B)
331+
end
332+
333+
293334
function broadcastbasis(::typeof(+), w_A::WeightedJacobi, w_B::WeightedJacobi)
294335
wA,A = w_A.args
295336
wB,B = w_B.args
@@ -327,26 +368,46 @@ end
327368
##########
328369

329370
# Jacobi(a+1,b+1)\(D*Jacobi(a,b))
330-
@simplify function *(D::Derivative{<:Any,<:AbstractInterval}, S::Jacobi)
331-
A = _BandedMatrix((((1:∞) .+ (S.a + S.b))/2)', ℵ₀, -1,1)
332-
ApplyQuasiMatrix(*, Jacobi(S.a+1,S.b+1), A)
371+
@simplify *(D::Derivative{<:Any,<:AbstractInterval}, S::Jacobi) = Jacobi(S.a+1,S.b+1) * _BandedMatrix((((1:∞) .+ (S.a + S.b))/2)', ℵ₀, -1,1)
372+
373+
@simplify function *(D::Derivative{<:Any,<:AbstractInterval}, WS::Weighted{<:Any,<:Jacobi})
374+
# L_1^t
375+
S = WS.P
376+
a,b = S.a, S.b
377+
if a == b == 0
378+
D*S
379+
else
380+
Weighted(Jacobi(a-1, b-1)) * _BandedMatrix((-2*(1:∞))', ℵ₀, 1,-1)
381+
end
333382
end
334383

384+
#L_6^t
385+
@simplify function *(D::Derivative{<:Any,<:AbstractInterval}, WS::HalfWeighted{:a,<:Any,<:Jacobi})
386+
S = WS.P
387+
a,b = S.a, S.b
388+
HalfWeighted{:a}(Jacobi(a-1,b+1)) * Diagonal(-(a:∞))
389+
end
390+
391+
#L_6
392+
@simplify function *(D::Derivative{<:Any,<:AbstractInterval}, WS::HalfWeighted{:b,<:Any,<:Jacobi})
393+
S = WS.P
394+
a,b = S.a, S.b
395+
HalfWeighted{:b}(Jacobi(a+1,b-1)) * Diagonal(b:∞)
396+
end
397+
398+
335399
# Jacobi(a-1,b-1)\ (D*w*Jacobi(a,b))
336400
@simplify function *(D::Derivative{<:Any,<:AbstractInterval}, WS::WeightedJacobi)
337401
w,S = WS.args
338402
a,b = S.a, S.b
339-
if w.a == 0 && w.b == 0
403+
if isorthogonalityweighted(WS) # L_1^t
404+
D * Weighted(S)
405+
elseif w.a == w.b == 0
340406
D*S
341407
elseif iszero(w.a) && w.b == b #L_6
342-
A = Diagonal(b:∞)
343-
ApplyQuasiMatrix(*, JacobiWeight(w.a,b-1) .* Jacobi(a+1,b-1), A)
408+
D * HalfWeighted{:b}(S)
344409
elseif iszero(w.b) && w.a == a #L_6^t
345-
A = Diagonal(-(a:∞))
346-
ApplyQuasiMatrix(*, JacobiWeight(a-1,w.b) .* Jacobi(a-1,b+1), A)
347-
elseif w.a == a && w.b == b # L_1^t
348-
A = _BandedMatrix((-2*(1:∞))', ℵ₀, 1,-1)
349-
ApplyQuasiMatrix(*, JacobiWeight(a-1,b-1) .* Jacobi(a-1, b-1), A)
410+
D * HalfWeighted{:a}(S)
350411
elseif iszero(w.a)
351412
W = (JacobiWeight(w.a, b-1) .* Jacobi(a+1, b-1)) \ (D * (JacobiWeight(w.a,b) .* S))
352413
J = Jacobi(a+1,b) # range Jacobi

src/normalized.jl

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,13 @@ show(io::IO, ::MIME"text/plain", Q::Normalized) = show(io, Q)
146146

147147

148148

149+
150+
"""
151+
OrthonormalWeighted(P)
152+
153+
is the orthonormal with respect to L^2 basis given by
154+
`sqrt.(orthogonalityweight(P)) .* Normalized(P)`.
155+
"""
149156
struct OrthonormalWeighted{T, PP<:AbstractQuasiMatrix{T}} <: Basis{T}
150157
P::Normalized{T, PP}
151158
end
@@ -164,4 +171,33 @@ function getindex(Q::OrthonormalWeighted, x::Union{Number,AbstractVector}, jr::U
164171
w = orthogonalityweight(Q.P)
165172
sqrt.(w[x]) .* Q.P[x,jr]
166173
end
167-
broadcasted(::LazyQuasiArrayStyle{2}, ::typeof(*), x::Inclusion, Q::OrthonormalWeighted) = Q * (Q.P \ (x .* Q.P))
174+
broadcasted(::LazyQuasiArrayStyle{2}, ::typeof(*), x::Inclusion, Q::OrthonormalWeighted) = Q * (Q.P \ (x .* Q.P))
175+
176+
"""
177+
Weighted(P)
178+
179+
is equivalent to `orthogonalityweight(P) .* P`
180+
"""
181+
struct Weighted{T, PP<:AbstractQuasiMatrix{T}} <: Basis{T}
182+
P::PP
183+
end
184+
185+
axes(Q::Weighted) = axes(Q.P)
186+
copy(Q::Weighted) = Q
187+
188+
==(A::Weighted, B::Weighted) = A.P == B.P
189+
190+
convert(::Type{WeightedOrthogonalPolynomial}, P::Weighted) = orthogonalityweight(P.P) .* P.P
191+
192+
function getindex(Q::Weighted, x::Union{Number,AbstractVector}, jr::Union{Number,AbstractVector})
193+
w = orthogonalityweight(Q.P)
194+
w[x] .* Q.P[x,jr]
195+
end
196+
broadcasted(::LazyQuasiArrayStyle{2}, ::typeof(*), x::Inclusion, Q::Weighted) = Q * (Q.P \ (x .* Q.P))
197+
198+
\(w_A::Weighted, w_B::Weighted) = convert(WeightedOrthogonalPolynomial, w_A) \ convert(WeightedOrthogonalPolynomial, w_B)
199+
\(w_A::Weighted, B::AbstractQuasiArray) = convert(WeightedOrthogonalPolynomial, w_A) \ B
200+
\(A::AbstractQuasiArray, w_B::Weighted) = A \ convert(WeightedOrthogonalPolynomial, w_B)
201+
202+
@simplify *(Ac::QuasiAdjoint{<:Any,<:Weighted}, wB::Weighted) =
203+
convert(WeightedOrthogonalPolynomial, parent(Ac))' * convert(WeightedOrthogonalPolynomial, wB)

src/ultraspherical.jl

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const WeightedUltraspherical{T} = WeightedBasis{T,<:UltrasphericalWeight,<:Ultra
3838

3939
WeightedUltraspherical(λ) = UltrasphericalWeight(λ) .* Ultraspherical(λ)
4040
WeightedUltraspherical{T}(λ) where T = UltrasphericalWeight{T}(λ) .* Ultraspherical{T}(λ)
41-
41+
orthogonalityweight(C::Ultraspherical) = UltrasphericalWeight(C.λ)
4242

4343
ultrasphericalc(n::Integer, λ, z::Number) = Base.unsafe_getindex(Ultraspherical{promote_type(typeof(λ),typeof(z))}(λ), z, n+1)
4444

@@ -47,6 +47,11 @@ ultrasphericalc(n::Integer, λ, z::Number) = Base.unsafe_getindex(Ultraspherical
4747
==(::ChebyshevT, ::Ultraspherical) = false
4848
==(C::Ultraspherical, ::ChebyshevU) = isone(C.λ)
4949
==(::ChebyshevU, C::Ultraspherical) = isone(C.λ)
50+
==(P::Ultraspherical, Q::Jacobi) = isone(2P.λ) && Jacobi(P) == Q
51+
==(P::Jacobi, Q::Ultraspherical) = isone(2Q.λ) && P == Jacobi(Q)
52+
==(P::Ultraspherical, Q::Legendre) = isone(2P.λ)
53+
==(P::Legendre, Q::Ultraspherical) = isone(2Q.λ)
54+
5055

5156
###
5257
# interrelationships
@@ -95,20 +100,30 @@ end
95100
ApplyQuasiMatrix(*, Ultraspherical{eltype(S)}(S.λ+1), A)
96101
end
97102

98-
# Ultraspherical(λ-1)\ (D*w*Ultraspherical(λ))
99-
@simplify function *(D::Derivative{<:Any,<:AbstractInterval}, WS::WeightedUltraspherical)
100-
w,S = WS.args
103+
# Ultraspherical(λ-1)\ (D*wUltraspherical(λ))
104+
@simplify function *(D::Derivative{<:Any,<:AbstractInterval}, WS::Weighted{<:Any,<:Ultraspherical})
105+
S = WS.P
101106
λ = S.λ
102107
T = eltype(WS)
103-
if iszero(w.λ)
104-
D*S
105-
elseif w.λ == λ == 1
108+
if λ == 1
106109
A = _BandedMatrix((-(1:∞))', ℵ₀, 1,-1)
107110
ApplyQuasiMatrix(*, ChebyshevTWeight{T}() .* ChebyshevT{T}(), A)
108-
elseif w.λ == λ
111+
else
109112
n = (0:∞)
110113
A = _BandedMatrix((-one(T)/(2*-1)) * ((n.+1) .* (n .+ (2λ-1))))', ℵ₀, 1,-1)
111114
ApplyQuasiMatrix(*, WeightedUltraspherical{T}-1), A)
115+
end
116+
end
117+
118+
# Ultraspherical(λ-1)\ (D*w*Ultraspherical(λ))
119+
@simplify function *(D::Derivative{<:Any,<:AbstractInterval}, WS::WeightedUltraspherical)
120+
w,S = WS.args
121+
λ = S.λ
122+
T = eltype(WS)
123+
if iszero(w.λ)
124+
D*S
125+
elseif isorthogonalityweighted(WS) # weights match
126+
D * Weighted(S)
112127
else
113128
error("Not implemented")
114129
end
@@ -180,7 +195,7 @@ function \(w_A::WeightedUltraspherical, w_B::WeightedUltraspherical)
180195

181196
if wA == wB
182197
A \ B
183-
elseif B.λ == A.λ+1 && wB.λ == wA.λ+1
198+
elseif B.λ == A.λ+1 && wB.λ == wA.λ+1 # Lower
184199
λ = A.λ
185200
_BandedMatrix(Vcat(((2λ:∞) .* ((2λ+1):∞) ./ (4λ .*+1:∞)))',
186201
Zeros(1,∞),

test/test_jacobi.jl

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
using ClassicalOrthogonalPolynomials, FillArrays, BandedMatrices, ContinuumArrays, QuasiArrays, LazyArrays, FastGaussQuadrature, Test
2-
import ClassicalOrthogonalPolynomials: recurrencecoefficients, basis, MulQuasiMatrix
1+
using ClassicalOrthogonalPolynomials, FillArrays, BandedMatrices, ContinuumArrays, QuasiArrays, LazyArrays, LazyBandedMatrices, FastGaussQuadrature, Test
2+
import ClassicalOrthogonalPolynomials: recurrencecoefficients, basis, MulQuasiMatrix, arguments, Weighted, HalfWeighted
33

44
@testset "Jacobi" begin
55
@testset "JacobiWeight" begin
@@ -251,7 +251,7 @@ import ClassicalOrthogonalPolynomials: recurrencecoefficients, basis, MulQuasiMa
251251
U = ChebyshevU()
252252
JT = Jacobi(T)
253253
JU = Jacobi(U)
254-
254+
255255
@testset "recurrence degenerecies" begin
256256
A,B,C = recurrencecoefficients(JT)
257257
@test A[1] == 0.5
@@ -343,7 +343,7 @@ import ClassicalOrthogonalPolynomials: recurrencecoefficients, basis, MulQuasiMa
343343
@test norm((X*Min - Min*X')[1:n,1:n]) 1E-13
344344
β = X[n,n+1]*Mi[n+1,n+1]
345345
@test (x-y) * P[x,1:n]'Mi[1:n,1:n]*P[y,1:n] P[x,n:n+1]' * (X*Min - Min*X')[n:n+1,n:n+1] * P[y,n:n+1] P[x,n:n+1]' * [0 -β; β 0] * P[y,n:n+1]
346-
346+
347347
@testset "extrapolation" begin
348348
x,y = 0.1,3.4
349349
@test (x-y) * P[x,1:n]'Mi[1:n,1:n]*Base.unsafe_getindex(P,y,1:n) P[x,n:n+1]' * [0 -β; β 0] * Base.unsafe_getindex(P,y,n:n+1)
@@ -353,4 +353,24 @@ import ClassicalOrthogonalPolynomials: recurrencecoefficients, basis, MulQuasiMa
353353
@testset "special syntax" begin
354354
@test jacobip.(0:5, 0.1, 0.2, 0.3) == Jacobi(0.1, 0.2)[0.3, 1:6]
355355
end
356+
357+
@testset "Weighted/HalfWeighted" begin
358+
x = axes(Legendre(),1)
359+
D = Derivative(x)
360+
a,b = 0.1,0.2
361+
B = Jacobi(a,b)
362+
A = Jacobi(a-1,b-1)
363+
D_W = Weighted(A) \ (D * Weighted(B))
364+
@test (A * (D_W * (B \ exp.(x))))[0.1] (-a*(1+0.1) + b*(1-0.1) + (1-0.1^2)) *exp(0.1)
365+
366+
D_a = HalfWeighted{:a}(Jacobi(a-1,b+1)) \ (D * HalfWeighted{:a}(B))
367+
D_b = HalfWeighted{:b}(Jacobi(a+1,b-1)) \ (D * HalfWeighted{:b}(B))
368+
@test (Jacobi(a-1,b+1) * (D_a * (B \ exp.(x))))[0.1] (-a + 1-0.1) *exp(0.1)
369+
@test (Jacobi(a+1,b-1) * (D_b * (B \ exp.(x))))[0.1] (b + 1+0.1) *exp(0.1)
370+
371+
@test HalfWeighted{:a}(B) \ HalfWeighted{:a}(B) isa Eye
372+
@test HalfWeighted{:a}(B) \ (JacobiWeight(a,0) .* B) isa Eye
373+
374+
@test HalfWeighted{:a}(B) \ (x .* HalfWeighted{:a}(B)) isa LazyBandedMatrices.Tridiagonal
375+
end
356376
end

test/test_odes.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ using ClassicalOrthogonalPolynomials, ContinuumArrays, QuasiArrays, BandedMatric
44
import QuasiArrays: MulQuasiMatrix
55
import ClassicalOrthogonalPolynomials: oneto
66
import ContinuumArrays: MappedBasisLayout, MappedWeightedBasisLayout
7-
import LazyArrays: arguments, ApplyMatrix
7+
import LazyArrays: arguments, ApplyMatrix, colsupport, MemoryLayout
88
import SemiseparableMatrices: VcatAlmostBandedLayout
99

1010
@testset "ODEs" begin
@@ -96,7 +96,7 @@ import SemiseparableMatrices: VcatAlmostBandedLayout
9696
D = Derivative(axes(S,1))
9797
X = Diagonal(Inclusion(axes(S,1)))
9898

99-
@test_broken (Legendre() \ S)*(S\(w.*S))
99+
@test ((Legendre() \ S)*(S\(w.*S)))[1:10,1:10] (Legendre() \ (w .* S))[1:10,1:10]
100100
@test (Ultraspherical(3/2)\(D^2*(w.*S)))[1:10,1:10] diagm(0 => -(2:2:20))
101101
end
102102

test/test_ultraspherical.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,20 @@ using ClassicalOrthogonalPolynomials, BandedMatrices, LazyArrays, Test
101101
@testset "special syntax" begin
102102
@test ultrasphericalc.(0:5, 2, 0.3) == Ultraspherical(2)[0.3, 1:6]
103103
end
104+
105+
@testset "Ultraspherical vs Chebyshev and Jacobi" begin
106+
@test Ultraspherical(1) == ChebyshevU()
107+
@test ChebyshevU() == Ultraspherical(1)
108+
@test Ultraspherical(0)  ChebyshevT()
109+
@test ChebyshevT() Ultraspherical(0)
110+
@test Ultraspherical(1)  Jacobi(1/2,1/2)
111+
@test Jacobi(1/2,1/2) Ultraspherical(1)
112+
@test Ultraspherical(1/2) == Jacobi(0,0)
113+
@test Ultraspherical(1/2) == Legendre()
114+
@test Jacobi(0,0) == Ultraspherical(1/2)
115+
@test Legendre() == Ultraspherical(1/2)
116+
117+
@test Ultraspherical(1/2) \ (JacobiWeight(0,0) .* Jacobi(0,0)) isa Diagonal
118+
@test (JacobiWeight(0,0) .* Jacobi(0,0)) \ Ultraspherical(1/2) isa Diagonal
119+
end
104120
end

0 commit comments

Comments
 (0)