Skip to content

Commit 0bb78e2

Browse files
authored
Merge branch 'master' into master
2 parents f0f7afe + ac9efc2 commit 0bb78e2

File tree

4 files changed

+108
-25
lines changed

4 files changed

+108
-25
lines changed

src/SemiclassicalOrthogonalPolynomials.jl

+104-21
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ import Base: getindex, axes, size, \, /, *, +, -, summary, ==, copy, sum, unsafe
77

88
import ArrayLayouts: MemoryLayout, ldiv, diagonaldata, subdiagonaldata, supdiagonaldata
99
import BandedMatrices: bandwidths, AbstractBandedMatrix, BandedLayout, _BandedMatrix
10-
import LazyArrays: resizedata!, paddeddata, CachedVector, CachedMatrix, CachedAbstractVector, LazyMatrix, LazyVector, arguments, ApplyLayout, colsupport, AbstractCachedVector,
10+
import LazyArrays: resizedata!, paddeddata, CachedVector, CachedMatrix, CachedAbstractVector, LazyMatrix, LazyVector, arguments, ApplyLayout, colsupport, AbstractCachedVector, ApplyArray,
1111
AccumulateAbstractVector, LazyVector, AbstractCachedMatrix, BroadcastLayout
1212
import ClassicalOrthogonalPolynomials: OrthogonalPolynomial, recurrencecoefficients, jacobimatrix, normalize, _p0, UnitInterval, orthogonalityweight, NormalizedOPLayout,
1313
Bidiagonal, Tridiagonal, SymTridiagonal, symtridiagonalize, normalizationconstant, LanczosPolynomial,
1414
OrthogonalPolynomialRatio, Weighted, WeightLayout, UnionDomain, oneto, WeightedBasis, HalfWeighted,
1515
golubwelsch, AbstractOPLayout, weight, WeightedOPLayout
1616
import SingularIntegrals: Associated, associated, Hilbert
17+
1718
import InfiniteArrays: OneToInf, InfUnitRange
1819
import ContinuumArrays: basis, Weight, @simplify, AbstractBasisLayout, BasisLayout, MappedBasisLayout, grid, plotgrid, _equals, ExpansionLayout
1920
import FillArrays: SquareEye
@@ -94,20 +95,13 @@ RaisedOP(Q, y::Number) = RaisedOP(Q, OrthogonalPolynomialRatio(Q,y))
9495
function jacobimatrix(P::RaisedOP{T}) where T
9596
= P.
9697
X = jacobimatrix(P.Q)
97-
a,b = diagonaldata(X), supdiagonaldata(X)
98+
a,b = view(X,band(0)), view(X,band(1)) # a,b = diagonaldata(X), supdiagonaldata(X)
9899
# non-normalized lower diag of Jacobi
99100
v = Vcat(zero(T),b .* ℓ)
100101
c = BroadcastVector((ℓ,a,b,sa,v) ->*a + b - b*^2 - sa*+*v, ℓ, a, b, a[2:∞], v)
101102
Tridiagonal(c, BroadcastVector((ℓ,a,b,v) -> a - b *+ v, ℓ,a,b,v), b)
102103
end
103104

104-
105-
106-
107-
108-
109-
110-
111105
"""
112106
the bands of the Jacobi matrix
113107
"""
@@ -142,7 +136,6 @@ SemiclassicalJacobiBand{dv}(a,b,ℓ) where dv = SemiclassicalJacobiBand{dv,promo
142136
copy(r::SemiclassicalJacobiBand) = r # immutable
143137

144138

145-
146139
"""
147140
SemiclassicalJacobi(t, a, b, c)
148141
@@ -177,7 +170,7 @@ function semiclassical_jacobimatrix(t, a, b, c)
177170
if a == 0 && b == 0 && c == -1
178171
# for this special case we can generate the Jacobi operator explicitly
179172
N = (1:∞)
180-
α = T.(neg1c_αcfs(t)) # cached coefficients
173+
α = neg1c_αcfs(t) # cached coefficients
181174
A = Vcat((α[1]+1)/2 , -N./(N.*4 .- 2).*α .+ (N.+1)./(N.*4 .+ 2).*α[2:end].+1/2)
182175
C = -(N)./(N.*4 .- 2)
183176
B = Vcat((α[1]^2*3-α[1]*α[2]*2-1)/6 , -(N)./(N.*4 .+ 2).*α[2:end]./α)
@@ -187,10 +180,15 @@ function semiclassical_jacobimatrix(t, a, b, c)
187180
P = jacobi(b, a, UnitInterval{T}())
188181
iszero(c) && return symtridiagonalize(jacobimatrix(P))
189182
x = axes(P,1)
190-
jacobimatrix(LanczosPolynomial(@.(x^a * (1-x)^b * (t-x)^c), P))
183+
if iseven(c) # QR case
184+
return qr_jacobimatrix(x->(t.-x).^(c÷2), Normalized(jacobi(a,b,UnitInterval{T}())))
185+
elseif isodd(c)
186+
return semiclassical_jacobimatrix(SemiclassicalJacobi(t, a, b, c-1), a, b, c)
187+
else # if c is not an even integer, use Lanczos for now
188+
return jacobimatrix(LanczosPolynomial(@.(x^a * (1-x)^b * (t-x)^c), P))
189+
end
191190
end
192191

193-
194192
function symraised_jacobimatrix(Q, y)
195193
= OrthogonalPolynomialRatio(Q,y)
196194
X = jacobimatrix(Q)
@@ -199,9 +197,35 @@ function symraised_jacobimatrix(Q, y)
199197
end
200198

201199
function semiclassical_jacobimatrix(Q::SemiclassicalJacobi, a, b, c)
202-
if iszero(a) && iszero(b) && c == -1 # (a,b,c) = (0,0,-1) special case
200+
# special cases
201+
if iszero(a) && iszero(b) && c == -1 # (a,b,c) = (0,0,-1) special case
203202
semiclassical_jacobimatrix(Q.t, a, b, c)
204-
elseif a == Q.a+1 && b == Q.b && c == Q.c # raising by 1
203+
elseif iszero(c) # classical Jacobi polynomial special case
204+
jacobimatrix(SemiclassicalJacobi(Q.t, Q.a, Q.b, Q.c))
205+
elseif a == Q.a && b == Q.b && c == Q.c # same basis
206+
jacobimatrix(Q)
207+
end
208+
209+
# QR decomposition approach for raising
210+
Δa = a-Q.a
211+
Δb = b-Q.b
212+
Δc = c-Q.c
213+
if iseven(a-Q.a) && iseven(b-Q.b) && iseven(c-Q.c) && Δa0 && Δb0 && Δc0
214+
M = Diagonal(Ones(∞))
215+
if !iszero(Δa)
216+
M = ApplyArray(*,M,(Q.X)^((a-Q.a)÷2))
217+
end
218+
if !iszero(Δb)
219+
M = ApplyArray(*,M,(I-Q.X)^((b-Q.b)÷2))
220+
end
221+
if !iszero(Δc)
222+
M = ApplyArray(*,M,(Q.t*I-Q.X)^((c-Q.c)÷2))
223+
end
224+
return qr_jacobimatrix(M,Q,false)
225+
end
226+
227+
# Fallback from decomposition methods
228+
if a == Q.a+1 && b == Q.b && c == Q.c # raising by 1
205229
symraised_jacobimatrix(Q, 0)
206230
elseif a == Q.a && b == Q.b+1 && c == Q.c
207231
symraised_jacobimatrix(Q, 1)
@@ -213,20 +237,18 @@ function semiclassical_jacobimatrix(Q::SemiclassicalJacobi, a, b, c)
213237
symlowered_jacobimatrix(Q, :a)
214238
elseif a == Q.a && b == Q.b-1 && c == Q.c
215239
symlowered_jacobimatrix(Q, :b)
216-
elseif a > Q.a # iterative raising
240+
elseif a > Q.a # iterative raising by 1
217241
semiclassical_jacobimatrix(SemiclassicalJacobi(Q.t, Q.a+1, Q.b, Q.c, Q), a, b, c)
218242
elseif b > Q.b
219243
semiclassical_jacobimatrix(SemiclassicalJacobi(Q.t, Q.a, Q.b+1, Q.c, Q), a, b, c)
220244
elseif c > Q.c
221245
semiclassical_jacobimatrix(SemiclassicalJacobi(Q.t, Q.a, Q.b, Q.c+1, Q), a, b, c)
222-
elseif b < Q.b # iterative lowering
246+
elseif b < Q.b # iterative lowering by 1
223247
semiclassical_jacobimatrix(SemiclassicalJacobi(Q.t, Q.a, Q.b-1, Q.c, Q), a, b, c)
224248
elseif c < Q.c
225249
semiclassical_jacobimatrix(SemiclassicalJacobi(Q.t, Q.a, Q.b, Q.c-1, Q), a, b, c)
226250
elseif a < Q.a
227251
semiclassical_jacobimatrix(SemiclassicalJacobi(Q.t, Q.a-1, Q.b, Q.c, Q), a, b, c)
228-
elseif a == Q.a && b == Q.b && c == Q.c # same basis
229-
jacobimatrix(Q)
230252
else
231253
error("Not Implemented")
232254
end
@@ -292,6 +314,46 @@ function semijacobi_ldiv(P::SemiclassicalJacobi, Q)
292314
(P \ R) * _p0(R̃) * (R̃ \ Q)
293315
end
294316

317+
# returns conversion operator from SemiclassicalJacobi P to SemiclassicalJacobi Q.
318+
function semijacobi_ldiv(Q::SemiclassicalJacobi,P::SemiclassicalJacobi)
319+
@assert Q.t P.t
320+
# For polynomial modifications, use Cholesky. Use Lanzos otherwise.
321+
M = Diagonal(Ones(∞))
322+
(Q == P) && return M
323+
Δa = Q.a-P.a
324+
Δb = Q.b-P.b
325+
Δc = Q.c-P.c
326+
if iseven(Δa) && iseven(Δb) && iseven(Δc)
327+
if !iszero(Q.a-P.a)
328+
M = ApplyArray(*,M,(P.X)^((Q.a-P.a)÷2))
329+
end
330+
if !iszero(Q.b-P.b)
331+
M = ApplyArray(*,M,(I-P.X)^((Q.b-P.b)÷2))
332+
end
333+
if !iszero(Q.c-P.c)
334+
M = ApplyArray(*,M,(Q.t*I-P.X)^((Q.c-P.c)÷2))
335+
end
336+
K = qr(M).R
337+
return ApplyArray(*, Diagonal(sign.(view(K,band(0))).*Fill(abs.(1/K[1]),∞)), K) # match our normalization choice P_0(x) = 1
338+
elseif isinteger(Δa) && isinteger(Δb) && isinteger(Δc)
339+
if !iszero(Q.a-P.a)
340+
M = ApplyArray(*,M,(P.X)^(Q.a-P.a))
341+
end
342+
if !iszero(Q.b-P.b)
343+
M = ApplyArray(*,M,(I-P.X)^(Q.b-P.b))
344+
end
345+
if !iszero(Q.c-P.c)
346+
M = ApplyArray(*,M,(Q.t*I-P.X)^(Q.c-P.c))
347+
end
348+
K = cholesky(Symmetric(M)).U
349+
return ApplyArray(*, Diagonal(Fill(1/K[1],∞)), K) # match our normalization choice P_0(x) = 1
350+
else # fallback option for non-integer weight modification
351+
R = SemiclassicalJacobi(P.t, mod(P.a,-1), mod(P.b,-1), mod(P.c,-1))
352+
= toclassical(R)
353+
return (P \ R) * _p0(R̃) * (R̃ \ Q)
354+
end
355+
end
356+
295357
struct SemiclassicalJacobiLayout <: AbstractOPLayout end
296358
MemoryLayout(::Type{<:SemiclassicalJacobi}) = SemiclassicalJacobiLayout()
297359

@@ -323,6 +385,7 @@ function copy(L::Ldiv{SemiclassicalJacobiLayout,SemiclassicalJacobiLayout})
323385
(inv(M_Q) * L') * M_P
324386
end
325387

388+
\(A::SemiclassicalJacobi, B::SemiclassicalJacobi) = semijacobi_ldiv(A, B)
326389
\(A::LanczosPolynomial, B::SemiclassicalJacobi) = semijacobi_ldiv(A, B)
327390
\(A::SemiclassicalJacobi, B::LanczosPolynomial) = semijacobi_ldiv(A, B)
328391
function \(w_A::WeightedSemiclassicalJacobi, w_B::WeightedSemiclassicalJacobi)
@@ -456,7 +519,27 @@ function SemiclassicalJacobiFamily{T}(data::Vector, t, a, b, c) where T
456519
end
457520

458521
SemiclassicalJacobiFamily(t, a, b, c) = SemiclassicalJacobiFamily{float(promote_type(typeof(t),eltype(a),eltype(b),eltype(c)))}(t, a, b, c)
459-
SemiclassicalJacobiFamily{T}(t, a, b, c) where T = SemiclassicalJacobiFamily{T}([SemiclassicalJacobi{T}(t, first(a), first(b), first(c))], t, a, b, c)
522+
function SemiclassicalJacobiFamily{T}(t, a, b, c) where T
523+
ℓa = length(a)
524+
ℓb = length(b)
525+
ℓc = length(c)
526+
# We need to start with a hierarchy containing two entries
527+
if ℓa > 1 && ℓb > 1 && ℓc > 1
528+
return SemiclassicalJacobiFamily{T}([SemiclassicalJacobi{T}(t, first(a), first(b), first(c)),SemiclassicalJacobi{T}(t, a[2], b[2], c[2])], t, a, b, c)
529+
elseif ℓb > 1 && ℓc > 1
530+
return SemiclassicalJacobiFamily{T}([SemiclassicalJacobi{T}(t, first(a), first(b), first(c)),SemiclassicalJacobi{T}(t, first(a), b[2], c[2])], t, a, b, c)
531+
elseif ℓa > 1 && ℓc > 1
532+
return SemiclassicalJacobiFamily{T}([SemiclassicalJacobi{T}(t, first(a), first(b), first(c)),SemiclassicalJacobi{T}(t, a[2], first(b), c[2])], t, a, b, c)
533+
elseif ℓa > 1 && ℓb > 1
534+
return SemiclassicalJacobiFamily{T}([SemiclassicalJacobi{T}(t, first(a), first(b), first(c)),SemiclassicalJacobi{T}(t, a[2], b[2], first(c))], t, a, b, c)
535+
elseif ℓc > 1
536+
return SemiclassicalJacobiFamily{T}([SemiclassicalJacobi{T}(t, first(a), first(b), first(c)),SemiclassicalJacobi{T}(t, first(a), first(b), c[2])], t, a, b, c)
537+
elseif ℓa > 1
538+
return SemiclassicalJacobiFamily{T}([SemiclassicalJacobi{T}(t, first(a), first(b), first(c)),SemiclassicalJacobi{T}(t, a[2], first(b), first(c))], t, a, b, c)
539+
else #if ℓb > 1
540+
return SemiclassicalJacobiFamily{T}([SemiclassicalJacobi{T}(t, first(a), first(b), first(c)),SemiclassicalJacobi{T}(t, first(a), b[2], first(c))], t, a, b, c)
541+
end
542+
end
460543

461544
Base.broadcasted(::Type{SemiclassicalJacobi}, t::Number, a::Number, b::Number, c::Number) = SemiclassicalJacobi(t, a, b, c)
462545
Base.broadcasted(::Type{SemiclassicalJacobi{T}}, t::Number, a::Number, b::Number, c::Number) where T = SemiclassicalJacobi{T}(t, a, b, c)
@@ -472,7 +555,7 @@ _broadcast_getindex(a::Number,k) = a
472555
function LazyArrays.cache_filldata!(P::SemiclassicalJacobiFamily, inds::AbstractUnitRange)
473556
t,a,b,c = P.t,P.a,P.b,P.c
474557
for k in inds
475-
P.data[k] = SemiclassicalJacobi(t, _broadcast_getindex(a,k), _broadcast_getindex(b,k), _broadcast_getindex(c,k), P.data[k-1])
558+
P.data[k] = SemiclassicalJacobi(t, _broadcast_getindex(a,k), _broadcast_getindex(b,k), _broadcast_getindex(c,k), P.data[k-2])
476559
end
477560
P
478561
end

src/lowering.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
###########
2-
# Generic methods for obtaining Jacobi matrices with one of the a, b and c parameters lowered by 1.
2+
# Generic fallback methods for obtaining Jacobi matrices with one of the a, b and c parameters lowered by 1.
33
# passing symbols :a, :b or :c into lowindex determines which parameter is lowered
44
######
55
function initialα_gen(P::SemiclassicalJacobi, lowindex::Symbol)
@@ -163,8 +163,8 @@ end
163163

164164
###########
165165
# Methods for the special case of computing SemiclassicalJacobi(t,0,0,-1)
166-
######
167166
# As this can be built from SemiclassicalJacobi(t,0,0,0) which is just shifted and normalized Legendre(), we have more explicit methods at our disposal due to explicitly known coefficients for the Legendre bases.
167+
######
168168

169169
###
170170
# α coefficients implementation, cached, direct and via recurrences

src/twoband.jl

-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ function jacobimatrix(R::TwoBandJacobi{T}) where T
9898
# M = (L / L[1,1])' # equal to R.Q \ R.P
9999

100100
Tridiagonal(Interlace(L.dv/L[1,1], (ρ^2-1) * L.ev), Zeros{T}(∞), Interlace((1-ρ^2) * L.dv,L.ev/(-L[1,1])))
101-
# Tridiagonal(Interlace(L.dv/L[1,1], (1-ρ^2) * L.ev), Zeros{T}(∞), Interlace((1-ρ^2) * L.dv, L.ev/L[1,1]))
102101
end
103102

104103

test/runtests.jl

+2-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ end
117117
@testset "Mass matrix" begin
118118
@test (T'*(w_T .* T))[1:10,1:10] sum(w_T)I
119119
M = W'*(w_W .* W)
120-
@test (sum(w_T)*inv(R)'L)[1:10,1:10] M[1:10,1:10]
120+
@test (sum(w_T)*inv(R[1:10,1:10])'L[1:10,1:10]) M[1:10,1:10]
121121
@test T[0.1,1:10]' W[0.1,1:10]' * R[1:10,1:10]
122122
end
123123
end
@@ -450,3 +450,4 @@ end
450450
include("test_derivative.jl")
451451
include("test_twoband.jl")
452452
include("test_lowering.jl")
453+

0 commit comments

Comments
 (0)