Skip to content

Commit 4e9e773

Browse files
authored
Start work on hierarchy of OPs (#28)
* Start work on hierarchy of OPs * Hide types to avoid growth for hierarchies * Hierarchical construction * fix derivative tests * Update Project.toml * Add 2 band OPs * Update Project.toml * increase coverage
1 parent f395390 commit 4e9e773

File tree

8 files changed

+372
-221
lines changed

8 files changed

+372
-221
lines changed

Project.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
2020
ArrayLayouts = "0.6"
2121
BandedMatrices = "0.16"
2222
ClassicalOrthogonalPolynomials = "0.3"
23-
ContinuumArrays = "0.5, 0.6"
23+
ContinuumArrays = "0.6.4"
2424
FillArrays = "0.11.5"
2525
HypergeometricFunctions = "0.3.4"
2626
InfiniteArrays = "0.10.2"
27-
LazyArrays = "0.21"
28-
QuasiArrays = "0.4"
27+
LazyArrays = "0.21.3"
28+
QuasiArrays = "0.4.9"
2929
SpecialFunctions = "0.10, 1.0"
3030
julia = "1.5"
3131

src/SemiclassicalOrthogonalPolynomials.jl

Lines changed: 190 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
module SemiclassicalOrthogonalPolynomials
2-
using ClassicalOrthogonalPolynomials, FillArrays, LazyArrays, ArrayLayouts, QuasiArrays, InfiniteArrays, ContinuumArrays, LinearAlgebra, BandedMatrices,
2+
using ClassicalOrthogonalPolynomials, FillArrays, LazyArrays, ArrayLayouts, QuasiArrays, InfiniteArrays, ContinuumArrays, LinearAlgebra, BandedMatrices,
33
SpecialFunctions, HypergeometricFunctions
44

55
import Base: getindex, axes, size, \, /, *, +, -, summary, ==, copy, sum, unsafe_getindex
66

77
import ArrayLayouts: MemoryLayout, ldiv, diagonaldata, subdiagonaldata, supdiagonaldata
88
import BandedMatrices: bandwidths, AbstractBandedMatrix, BandedLayout, _BandedMatrix
9-
import LazyArrays: resizedata!, paddeddata, CachedVector, CachedMatrix, CachedAbstractVector, LazyMatrix, LazyVector, arguments, ApplyLayout, colsupport, AbstractCachedVector, AccumulateAbstractVector
9+
import LazyArrays: resizedata!, paddeddata, CachedVector, CachedMatrix, CachedAbstractVector, LazyMatrix, LazyVector, arguments, ApplyLayout, colsupport, AbstractCachedVector, AccumulateAbstractVector, LazyVector
1010
import ClassicalOrthogonalPolynomials: OrthogonalPolynomial, recurrencecoefficients, jacobimatrix, normalize, _p0, UnitInterval, orthogonalityweight, NormalizedBasisLayout,
11-
Bidiagonal, Tridiagonal, SymTridiagonal, symtridiagonalize, normalizationconstant,
12-
OrthogonalPolynomialRatio, normalized_recurrencecoefficients, Weighted
11+
Bidiagonal, Tridiagonal, SymTridiagonal, symtridiagonalize, normalizationconstant, LanczosPolynomial,
12+
OrthogonalPolynomialRatio, Weighted, Expansion, UnionDomain, oneto
1313
import InfiniteArrays: OneToInf, InfUnitRange
1414
import ContinuumArrays: basis, Weight, @simplify, AbstractBasisLayout, BasisLayout, MappedBasisLayout
1515
import FillArrays: SquareEye
1616

17-
export LanczosPolynomial, Legendre, Normalized, normalize, SemiclassicalJacobi, SemiclassicalJacobiWeight, WeightedSemiclassicalJacobi, OrthogonalPolynomialRatio
17+
export LanczosPolynomial, Legendre, Normalized, normalize, SemiclassicalJacobi, SemiclassicalJacobiWeight, WeightedSemiclassicalJacobi, OrthogonalPolynomialRatio, TwoBandJacobi, TwoBandWeight
1818

1919

2020
""""
@@ -53,6 +53,91 @@ function summary(io::IO, P::SemiclassicalJacobiWeight)
5353
print(io, "x^$a * (1-x)^$b * ($t-x)^$c")
5454
end
5555

56+
function ==(A::SemiclassicalJacobiWeight, B::SemiclassicalJacobiWeight)
57+
A.a == B.a && A.b == B.b &&
58+
((iszero(A.c) && iszero(B.c)) ||
59+
(A.t == B.t && A.c == B.c))
60+
end
61+
62+
function Expansion(w::SemiclassicalJacobiWeight{T}) where T
63+
t,a,b,c = w.t,w.a,w.b,w.c
64+
P = jacobi(b, a, UnitInterval{T}())
65+
x = axes(P,1)
66+
# TODO: simplify
67+
LanczosPolynomial(@.(x^a * (1-x)^b * (t-x)^c), P).w
68+
end
69+
70+
==(A::SemiclassicalJacobiWeight, B::Expansion) = Expansion(A) == B
71+
==(A::Expansion, B::SemiclassicalJacobiWeight) = A == Expansion(B)
72+
73+
74+
"""
75+
RaisedOP(P, y)
76+
77+
gives the OPs w.r.t. (y - x) .* w based on lowering to Q.
78+
"""
79+
80+
struct RaisedOP{T, QQ, LL<:OrthogonalPolynomialRatio} <: OrthogonalPolynomial{T}
81+
Q::QQ
82+
::LL
83+
end
84+
85+
RaisedOP(Q, ℓ::OrthogonalPolynomialRatio) = RaisedOP{eltype(Q),typeof(Q),typeof(ℓ)}(Q, ℓ)
86+
RaisedOP(Q, y::Number) = RaisedOP(Q, OrthogonalPolynomialRatio(Q,y))
87+
88+
89+
function jacobimatrix(P::RaisedOP{T}) where T
90+
= P.
91+
X = jacobimatrix(P.Q)
92+
a,b = diagonaldata(X), supdiagonaldata(X)
93+
# non-normalized lower diag of Jacobi
94+
v = Vcat(zero(T),b .* ℓ)
95+
c = BroadcastVector((ℓ,a,b,sa,v) ->*a + b - b*^2 - sa*+*v, ℓ, a, b, a[2:∞], v)
96+
Tridiagonal(c, BroadcastVector((ℓ,a,b,v) -> a - b *+ v, ℓ,a,b,v), b)
97+
end
98+
99+
100+
101+
102+
103+
104+
105+
106+
"""
107+
the bands of the Jacobi matrix
108+
"""
109+
mutable struct SemiclassicalJacobiBand{dv,T} <: AbstractCachedVector{T}
110+
data::Vector{T}
111+
a::AbstractVector{T}
112+
b::AbstractVector{T}
113+
::AbstractVector{T}
114+
datasize::Tuple{Int}
115+
end
116+
117+
function LazyArrays.cache_filldata!(r::SemiclassicalJacobiBand{:dv}, inds::AbstractUnitRange)
118+
rℓ = r.ℓ[inds[1]-1:inds[end]]; ℓ,sℓ = rℓ[2:end], rℓ[1:end-1]
119+
ra = r.a[inds[1]:(inds[end]+1)]; a = ra[1:end-1]; sa = ra[2:end]
120+
rb = r.b[inds[1]-1:inds[end]]; b = rb[2:end]; sb = rb[1:end-1]
121+
r.data[inds] .= @.(a - b *+ sb*sℓ)
122+
end
123+
124+
function LazyArrays.cache_filldata!(r::SemiclassicalJacobiBand{:ev}, inds::AbstractUnitRange)
125+
rℓ = r.ℓ[inds[1]-1:inds[end]]; ℓ,sℓ = rℓ[2:end], rℓ[1:end-1]
126+
ra = r.a[inds[1]:(inds[end]+1)]; a = ra[1:end-1]; sa = ra[2:end]
127+
rb = r.b[inds[1]-1:inds[end]]; b = rb[2:end]; sb = rb[1:end-1]
128+
r.data[inds] .= @.(sqrt((ℓ*a + b - b*^2 - sa*+*sb*sℓ)*b))
129+
end
130+
131+
size(::SemiclassicalJacobiBand) = (ℵ₀,)
132+
133+
SemiclassicalJacobiBand{:dv,T}(a,b,ℓ) where T = SemiclassicalJacobiBand{:dv,T}(T[a[1] - b[1]ℓ[1]], a, b, ℓ, (1,))
134+
SemiclassicalJacobiBand{:ev,T}(a,b,ℓ) where T = SemiclassicalJacobiBand{:ev,T}(T[sqrt((ℓ[1]*a[1] + b[1] - b[1]*ℓ[1]^2 - a[2]*ℓ[1])b[1])], a, b, ℓ, (1,))
135+
SemiclassicalJacobiBand{dv}(a,b,ℓ) where dv = SemiclassicalJacobiBand{dv,promote_type(eltype(a),eltype(b),eltype(ℓ))}(a,b,ℓ)
136+
137+
copy(r::SemiclassicalJacobiBand) = r # immutable
138+
139+
140+
56141
"""
57142
SemiclassicalJacobi(t, a, b, c)
58143
@@ -63,41 +148,69 @@ struct SemiclassicalJacobi{T} <: OrthogonalPolynomial{T}
63148
a::T
64149
b::T
65150
c::T
66-
P # We need to store the basic case where ã,b̃,c̃ = mod(a,-1),mod(b,-1),mod(c,-1)
67-
# in order to compute lowering operators, etc.
68-
SemiclassicalJacobi{T}(t::T,a::T,b::T,c::T,P) where T = new{T}(t,a,b,c,P)
151+
X::AbstractMatrix{T}
152+
SemiclassicalJacobi{T}(t::T,a::T,b::T,c::T,X::AbstractMatrix{T}) where T = new{T}(t,a,b,c,X)
69153
end
70154

71155
const WeightedSemiclassicalJacobi{T} = WeightedBasis{T,<:SemiclassicalJacobiWeight,<:SemiclassicalJacobi}
72156

73-
function SemiclassicalJacobi(t, a, b, c, P::OrthogonalPolynomial)
74-
T = float(promote_type(typeof(t), typeof(a), typeof(b), typeof(c), eltype(P)))
75-
SemiclassicalJacobi{T}(T(t), T(a), T(b), T(c), P)
157+
function SemiclassicalJacobi(t, a, b, c, X::AbstractMatrix)
158+
T = float(promote_type(typeof(t), typeof(a), typeof(b), typeof(c), eltype(X)))
159+
SemiclassicalJacobi{T}(T(t), T(a), T(b), T(c), convert(AbstractMatrix{T},X))
76160
end
77161

78-
SemiclassicalJacobi(t, a, b, c, P::SemiclassicalJacobi) = SemiclassicalJacobi(t, a, b, c, P.P)
162+
SemiclassicalJacobi(t, a, b, c, P::SemiclassicalJacobi) = SemiclassicalJacobi(t, a, b, c, semiclassical_jacobimatrix(P, a, b, c))
163+
SemiclassicalJacobi(t, a, b, c) = SemiclassicalJacobi(t, a, b, c, semiclassical_jacobimatrix(t, a, b, c))
79164

80165
WeightedSemiclassicalJacobi(t, a, b, c, P...) = SemiclassicalJacobiWeight(t, a, b, c) .* SemiclassicalJacobi(t, a, b, c, P...)
81166

82167

83-
function SemiclassicalJacobi(t, a, b, c)
84-
ã,b̃,c̃ = mod(a,-1),mod(b,-1),min(c, mod(c,-1))
168+
function semiclassical_jacobimatrix(t, a, b, c)
85169
T = promote_type(typeof(t), typeof(a), typeof(b), typeof(c))
86-
P = jacobi(b̃, ã, UnitInterval{T}())
170+
P = jacobi(b, a, UnitInterval{T}())
171+
iszero(c) && return symtridiagonalize(jacobimatrix(P))
87172
x = axes(P,1)
88-
SemiclassicalJacobi(t, a, b, c, LanczosPolynomial(@.(x^ * (1-x)^ * (t-x)^), P))
173+
jacobimatrix(LanczosPolynomial(@.(x^a * (1-x)^b * (t-x)^c), P))
89174
end
90175

91176

92-
function SemiclassicalJacobi(t, a::Int, b::Int, c::Int)
93-
T = promote_type(typeof(t), typeof(a), typeof(b), typeof(c))
94-
P = Normalized(legendre(UnitInterval{T}()))
95-
x = axes(P,1)
96-
SemiclassicalJacobi(t, a, b, c, P)
177+
function symraised_jacobimatrix(Q, y)
178+
= OrthogonalPolynomialRatio(Q,y)
179+
X = jacobimatrix(Q)
180+
a,b = diagonaldata(X), supdiagonaldata(X)
181+
SymTridiagonal(SemiclassicalJacobiBand{:dv}(a,b,ℓ), SemiclassicalJacobiBand{:ev}(a,b,ℓ))
97182
end
98183

184+
function semiclassical_jacobimatrix(Q::SemiclassicalJacobi, a, b, c)
185+
if a == Q.a+1 && b == Q.b && c == Q.c
186+
symraised_jacobimatrix(Q, 0)
187+
elseif a == Q.a && b == Q.b+1 && c == Q.c
188+
symraised_jacobimatrix(Q, 1)
189+
elseif a == Q.a && b == Q.b && c == Q.c+1
190+
symraised_jacobimatrix(Q, Q.t)
191+
elseif a > Q.a
192+
semiclassical_jacobimatrix(SemiclassicalJacobi(Q.t, Q.a+1, Q.b, Q.c, Q), a, b,c)
193+
elseif b > Q.b
194+
semiclassical_jacobimatrix(SemiclassicalJacobi(Q.t, Q.a, Q.b+1, Q.c, Q), a, b,c)
195+
elseif c > Q.c
196+
semiclassical_jacobimatrix(SemiclassicalJacobi(Q.t, Q.a, Q.b, Q.c+1, Q), a, b,c)
197+
else
198+
error("Not Implement")
199+
end
200+
end
201+
202+
LanczosPolynomial(P::SemiclassicalJacobi{T}) where T =
203+
LanczosPolynomial(orthogonalityweight(P), Normalized(jacobi(P.b, P.a, UnitInterval{T}())), P.X.dv.data)
204+
205+
"""
206+
toclassical(P::SemiclassicalJacobi)
207+
208+
gives either a mapped `Jacobi` or `LanczosPolynomial` version of `P`.
209+
"""
210+
toclassical(P::SemiclassicalJacobi{T}) where T = iszero(P.c) ? Normalized(jacobi(P.b, P.a, UnitInterval{T}())) : LanczosPolynomial(P)
211+
99212
copy(P::SemiclassicalJacobi) = P
100-
axes(P::SemiclassicalJacobi) = axes(P.P)
213+
axes(P::SemiclassicalJacobi{T}) where T = (Inclusion(UnitInterval{T}()),OneToInf())
101214

102215
==(A::SemiclassicalJacobi, B::SemiclassicalJacobi) = A.t == B.t && A.a == B.a && A.b == B.b && A.c == B.c
103216
==(::AbstractQuasiMatrix, ::SemiclassicalJacobi) = false
@@ -110,57 +223,9 @@ function summary(io::IO, P::SemiclassicalJacobi)
110223
print(io, "SemiclassicalJacobi with weight x^$a * (1-x)^$b * ($t-x)^$c")
111224
end
112225

113-
"""
114-
RaisedOP(P, y)
115-
116-
gives the OPs w.r.t. (y - x) .* w based on lowering to Q.
117-
"""
118226

119-
struct RaisedOP{T, QQ, LL<:OrthogonalPolynomialRatio} <: OrthogonalPolynomial{T}
120-
Q::QQ
121-
::LL
122-
end
123227

124-
RaisedOP(Q, ℓ::OrthogonalPolynomialRatio) = RaisedOP{eltype(Q),typeof(Q),typeof(ℓ)}(Q, ℓ)
125-
RaisedOP(Q, y::Number) = RaisedOP(Q, OrthogonalPolynomialRatio(Q,y))
126-
127-
function jacobimatrix(P::RaisedOP{T}) where T
128-
= P.
129-
X = jacobimatrix(P.Q)
130-
a,b = diagonaldata(X), supdiagonaldata(X)
131-
# non-normalized lower diag of Jacobi
132-
v = Vcat(zero(T),b .* ℓ)
133-
c = BroadcastVector((ℓ,a,b,sa,v) ->*a + b - b*^2 - sa*+*v, ℓ, a, b, a[2:∞], v)
134-
Tridiagonal(c, BroadcastVector((ℓ,a,b,v) -> a - b *+ v, ℓ,a,b,v), b)
135-
end
136-
137-
"""
138-
cache_abstract(A)
139-
140-
caches a `A` without storing the type of `A` as a template.
141-
This prevents exceessive compilation.
142-
"""
143-
cache_abstract(v::AbstractVector{T}) where T = CachedAbstractVector(v)
144-
cache_abstract(S::SymTridiagonal) = SymTridiagonal(cache_abstract(S.dv), cache_abstract(S.ev))
145-
146-
function jacobimatrix(P::SemiclassicalJacobi{T}) where T
147-
if P.a 0 && P.b  0 && P.c 0
148-
jacobimatrix(P.P)
149-
elseif P.a > 0
150-
Q = SemiclassicalJacobi(P.t, P.a-1, P.b, P.c, P.P)
151-
cache_abstract(symtridiagonalize(jacobimatrix(RaisedOP(Q, 0))))
152-
elseif P.b > 0
153-
Q = SemiclassicalJacobi(P.t, P.a, P.b-1, P.c, P.P)
154-
cache_abstract(symtridiagonalize(jacobimatrix(RaisedOP(Q, 1))))
155-
elseif P.c > 0
156-
Q = SemiclassicalJacobi(P.t, P.a, P.b, P.c-1, P.P)
157-
cache_abstract(symtridiagonalize(jacobimatrix(RaisedOP(Q, P.t))))
158-
else
159-
error("Implement")
160-
end
161-
end
162-
163-
recurrencecoefficients(P::SemiclassicalJacobi) = normalized_recurrencecoefficients(P)
228+
jacobimatrix(P::SemiclassicalJacobi) = P.X
164229

165230
"""
166231
op_lowering(Q, y)
@@ -181,15 +246,17 @@ end
181246

182247
function semijacobi_ldiv(Q, P::SemiclassicalJacobi)
183248
if P.a 0 && P.b  0 && P.c 0
184-
(Q \ P.P)/_p0(P.P)
249+
= toclassical(P)
250+
(Q \ P̃)/_p0(P̃)
185251
else
186252
error("Implement")
187253
end
188254
end
189255

190256
function semijacobi_ldiv(P::SemiclassicalJacobi, Q)
191-
R = SemiclassicalJacobi(P.t, mod(P.a,-1), mod(P.b,-1), mod(P.c,-1), P)
192-
(P \ R) * _p0(P.P) * (P.P \ Q)
257+
R = SemiclassicalJacobi(P.t, mod(P.a,-1), mod(P.b,-1), mod(P.c,-1))
258+
= toclassical(R)
259+
(P \ R) * _p0(R̃) * (R̃ \ Q)
193260
end
194261

195262
struct SemiclassicalJacobiLayout <: AbstractBasisLayout end
@@ -237,16 +304,16 @@ function \(w_A::WeightedSemiclassicalJacobi, w_B::WeightedSemiclassicalJacobi)
237304
@assert A.a == B.a && A.b == B.b && A.c+1 == B.c
238305
-op_lowering(A,A.t)
239306
elseif wA.a+1  wB.a
240-
C = SemiclassicalJacobi(B.t, B.a-1, B.b, B.c, B)
241-
w_C = SemiclassicalJacobiWeight(B.t, wB.a-1, wB.b, wB.c) .* C
242-
L_2 = Base.inferencebarrier(w_C) \ Base.inferencebarrier(w_B)
243-
L_1 = Base.inferencebarrier(w_A) \ Base.inferencebarrier(w_C)
307+
C = SemiclassicalJacobi(A.t, A.a+1, A.b, A.c, A)
308+
w_C = SemiclassicalJacobiWeight(wA.t, wA.a+1, wA.b, wA.c) .* C
309+
L_2 = w_C \ w_B
310+
L_1 = w_A \ w_C
244311
L_1 * L_2
245312
elseif wA.b+1  wB.b
246-
C = SemiclassicalJacobi(B.t, B.a, B.b-1, B.c, B)
247-
w_C = SemiclassicalJacobiWeight(B.t, wB.a, wB.b-1, wB.c) .* C
248-
L_2 = Base.inferencebarrier(w_C) \ Base.inferencebarrier(w_B)
249-
L_1 = Base.inferencebarrier(w_A) \ Base.inferencebarrier(w_C)
313+
C = SemiclassicalJacobi(A.t, A.a, A.b+1, A.c, A)
314+
w_C = SemiclassicalJacobiWeight(wA.t, wA.a, wA.b+1, wA.c) .* C
315+
L_2 = w_C \ w_B
316+
L_1 = w_A \ w_C
250317
L_1 * L_2
251318
else
252319
error("Implement")
@@ -261,16 +328,22 @@ massmatrix(P::SemiclassicalJacobi) = Diagonal(Fill(sum(orthogonalityweight(P)),
261328
@simplify function *(Ac::QuasiAdjoint{<:Any,<:SemiclassicalJacobi}, wB::WeightedBasis{<:Any,<:SemiclassicalJacobiWeight,<:SemiclassicalJacobi})
262329
A = parent(Ac)
263330
w,B = arguments(wB)
264-
P = SemiclassicalJacobi(w.t, w.a, w.b, w.c, B.P)
331+
P = SemiclassicalJacobi(w.t, w.a, w.b, w.c)
265332
(P\A)' * massmatrix(P) * (P \ B)
266333
end
267334

268335

269-
ldiv(Q::SemiclassicalJacobi, f::AbstractQuasiVector) = (Q \ Q.P) * (Q.P \ f)
336+
function ldiv(Q::SemiclassicalJacobi, f::AbstractQuasiVector)
337+
R = SemiclassicalJacobi(Q.t, mod(Q.a,-1), mod(Q.b,-1), mod(Q.c,-1))
338+
= toclassical(R)
339+
(Q \ R̃) * (R̃ \ f)
340+
end
270341
function ldiv(Qn::SubQuasiArray{<:Any,2,<:SemiclassicalJacobi,<:Tuple{<:Inclusion,<:Any}}, C::AbstractQuasiArray)
271342
_,jr = parentindices(Qn)
272343
Q = parent(Qn)
273-
(Q \ Q.P)[jr,jr] * (Q.P[:,jr] \ C)
344+
R = SemiclassicalJacobi(Q.t, mod(Q.a,-1), mod(Q.b,-1), mod(Q.c,-1))
345+
= toclassical(R)
346+
(Q \ R̃)[jr,jr] * (R̃[:,jr] \ C)
274347
end
275348

276349
# sqrt(1-(1-x)^2) == sqrt(2x-x^2) == sqrt(x)*sqrt(2-x)
@@ -279,4 +352,36 @@ end
279352
include("derivatives.jl")
280353

281354

355+
###
356+
# Hierarchy
357+
###
358+
359+
function Base.broadcasted(::Type{SemiclassicalJacobi}, t::Number, ar::AbstractUnitRange, b::Number, c::Number)
360+
Ps = [SemiclassicalJacobi(t, first(ar), b, c)]
361+
for a in ar[2:end]
362+
push!(Ps, SemiclassicalJacobi(t, a, b, c, Ps[end]))
363+
end
364+
Ps
365+
end
366+
367+
function Base.broadcasted(::Type{SemiclassicalJacobi}, t::Number, a::Number, br::AbstractUnitRange, c::Number)
368+
Ps = [SemiclassicalJacobi(t, a, first(br), c)]
369+
for b in br[2:end]
370+
push!(Ps, SemiclassicalJacobi(t, a, b, c, Ps[end]))
371+
end
372+
Ps
373+
end
374+
375+
376+
function Base.broadcasted(::Type{SemiclassicalJacobi}, t::Number, a::Number, b::Number, cr::AbstractUnitRange)
377+
Ps = [SemiclassicalJacobi(t, a, b, first(cr))]
378+
for c in cr[2:end]
379+
push!(Ps, SemiclassicalJacobi(t, a, b, c, Ps[end]))
380+
end
381+
Ps
382+
end
383+
384+
385+
include("twoband.jl")
386+
282387
end

0 commit comments

Comments
 (0)