@@ -18,6 +18,7 @@ import InfiniteArrays: OneToInf, InfUnitRange
1818import ContinuumArrays: basis, Weight, @simplify , AbstractBasisLayout, BasisLayout, MappedBasisLayout, grid, plotgrid, equals_layout, ExpansionLayout
1919import FillArrays: SquareEye
2020import HypergeometricFunctions: _₂F₁general2
21+ import InfiniteLinearAlgebra: BidiagonalConjugation
2122
2223export LanczosPolynomial, Legendre, Normalized, normalize, SemiclassicalJacobi, SemiclassicalJacobiWeight, WeightedSemiclassicalJacobi, OrthogonalPolynomialRatio
2324
@@ -139,18 +140,26 @@ function semiclassical_jacobimatrix(t, a, b, c)
139140 C = - (N). / (N.* 4 .- 2 )
140141 B = Vcat ((α[1 ]^ 2 * 3 - α[1 ]* α[2 ]* 2 - 1 )/ 6 , - (N). / (N.* 4 .+ 2 ). * α[2 : end ]. / α)
141142 return SymTridiagonal (A, sqrt .(B.* C)) # if J is Tridiagonal(c,a,b) then for norm. OPs it becomes SymTridiagonal(a, sqrt.( b.* c))
142- end
143- P = Normalized (jacobi (b, a, UnitInterval {T} ()))
144- iszero (c) && return jacobimatrix (P)
145- if isone (c)
146- return cholesky_jacobimatrix (Symmetric (P \ ((t.- axes (P,1 )). * P)), P)
147- elseif c == 2
148- return qr_jacobimatrix (Symmetric (P \ ((t.- axes (P,1 )). * P)), P)
149- elseif isinteger (c) && c ≥ 0 # reduce other integer c cases to hierarchy
150- return SemiclassicalJacobi .(t, a, b, 0 : Int (c))[end ]. X
151- else # if c is not an integer, use Lanczos
152- x = axes (P,1 )
153- return jacobimatrix (LanczosPolynomial (@. (x^ a * (1 - x)^ b * (t- x)^ c), jacobi (b, a, UnitInterval {T} ())))
143+ elseif b == - one (T)
144+ J′ = semiclassical_jacobimatrix (t, a, one (b), c)
145+ J′a, J′b = diagonaldata (J′), supdiagonaldata (J′)
146+ A = Vcat (one (T), J′a[1 : end ])
147+ B = Vcat (- one (T), J′b[1 : end ])
148+ C = Vcat (zero (T), J′b[1 : end ])
149+ return Tridiagonal (B, A, C)
150+ else
151+ P = Normalized (jacobi (b, a, UnitInterval {T} ()))
152+ iszero (c) && return jacobimatrix (P)
153+ if isone (c)
154+ return cholesky_jacobimatrix (Symmetric (P \ ((t.- axes (P,1 )). * P)), P)
155+ elseif isone (c/ 2 )
156+ return qr_jacobimatrix (Symmetric (P \ ((t.- axes (P,1 )). * P)), P)
157+ elseif isinteger (c) && c ≥ 0 # reduce other integer c cases to hierarchy
158+ return SemiclassicalJacobi .(t, a, b, 0 : Int (c))[end ]. X
159+ else # if c is not an integer, use Lanczos
160+ x = axes (P,1 )
161+ return jacobimatrix (LanczosPolynomial (@. (x^ a * (1 - x)^ b * (t- x)^ c), jacobi (b, a, UnitInterval {T} ())))
162+ end
154163 end
155164end
156165
@@ -162,11 +171,16 @@ function semiclassical_jacobimatrix(Q::SemiclassicalJacobi, a, b, c)
162171 # special cases
163172 if iszero (a) && iszero (b) && c == - one (eltype (Q. t)) # (a,b,c) = (0,0,-1) special case
164173 return semiclassical_jacobimatrix (Q. t, zero (Q. t), zero (Q. t), c)
174+ elseif iszero (Δa) && iszero (Δc) && Δb == 2 && b == 1
175+ # When going from P[t, a, -1, c] to P[t, a, 1, c], you can just take
176+ return SymTridiagonal (Q. X. d[2 : end ], Q. X. du[2 : end ])
165177 elseif iszero (c) # classical Jacobi polynomial special case
166178 return jacobimatrix (Normalized (jacobi (b, a, UnitInterval {eltype(Q.t)} ())))
167179 elseif iszero (Δa) && iszero (Δb) && iszero (Δc) # same basis
168180 return Q. X
169- end
181+ elseif b == - one (eltype (Q. t))
182+ return semiclassical_jacobimatrix (Q. t, a, b, c)
183+ end
170184
171185 if isone (Δa/ 2 ) && iszero (Δb) && iszero (Δc) # raising by 2
172186 qr_jacobimatrix (Q. X,Q)
@@ -408,7 +422,35 @@ function copy(L::Ldiv{SemiclassicalJacobiLayout,SemiclassicalJacobiLayout})
408422 (inv (M_Q) * L' ) * M_P
409423end
410424
411- \ (A:: SemiclassicalJacobi , B:: SemiclassicalJacobi ) = semijacobi_ldiv (A, B)
425+ function \ (A:: SemiclassicalJacobi , B:: SemiclassicalJacobi{T} ) where {T}
426+ if A. b == - 1 && B. b ≠ - 1
427+ return UpperTriangular (ApplyArray (inv, B \ A))
428+ elseif B. b == - 1 && A. b ≠ - 1
429+ # First convert Bᵗᵃ⁻¹ᶜ into Bᵗᵃ⁰ᶜ
430+ Bᵗᵃ⁰ᶜ = SemiclassicalJacobi (B. t, B. a, zero (B. b), B. c, A)
431+ Bᵗᵃ¹ᶜ = SemiclassicalJacobi (B. t, B. a, one (B. a), B. c, A)
432+ Rᵦₐ₁ᵪᵗᵃ⁰ᶜ = Weighted (Bᵗᵃ⁰ᶜ) \ Weighted (Bᵗᵃ¹ᶜ)
433+ b1 = Rᵦₐ₁ᵪᵗᵃ⁰ᶜ[band (0 )]
434+ b0 = Vcat (one (T), Rᵦₐ₁ᵪᵗᵃ⁰ᶜ[band (- 1 )])
435+ Rᵦₐ₋₁ᵪᵗᵃ⁰ᶜ = Bidiagonal (b0, b1, :U )
436+ # Then convert Bᵗᵃ⁰ᶜ into A and complete
437+ Rₐ₀ᵪᴬ = UpperTriangular (A \ Bᵗᵃ⁰ᶜ)
438+ return ApplyArray (* , Rₐ₀ᵪᴬ, Rᵦₐ₋₁ᵪᵗᵃ⁰ᶜ)
439+ elseif A. b == B. b == - 1
440+ Bᵗᵃ¹ᶜ = SemiclassicalJacobi (B. t, B. a, one (B. b), B. c, B)
441+ Aᵗᵃ¹ᶜ = SemiclassicalJacobi (A. t, A. a, one (A. b), A. c, A)
442+ Rₐ₁ᵪᵗᵘ¹ᵛ = Aᵗᵃ¹ᶜ \ Bᵗᵃ¹ᶜ
443+ # Make 1 ⊕ Rₐ₁ᵪᵗᵘ¹ᵛ
444+ V = eltype (Rₐ₁ᵪᵗᵘ¹ᵛ)
445+ Rₐ₋₁ᵪᵗᵘ⁻¹ᵛ = Vcat (
446+ Hcat (one (V), Zeros {V} (1 , ∞)),
447+ Hcat (Zeros {V} (∞), Rₐ₁ᵪᵗᵘ¹ᵛ)
448+ )
449+ return Rₐ₋₁ᵪᵗᵘ⁻¹ᵛ
450+ else
451+ return semijacobi_ldiv (A, B)
452+ end
453+ end
412454\ (A:: LanczosPolynomial , B:: SemiclassicalJacobi ) = semijacobi_ldiv (A, B)
413455\ (A:: SemiclassicalJacobi , B:: LanczosPolynomial ) = semijacobi_ldiv (A, B)
414456function \ (w_A:: WeightedSemiclassicalJacobi{T} , w_B:: WeightedSemiclassicalJacobi{T} ) where T
457499
458500\ (w_A:: Weighted{<:Any,<:SemiclassicalJacobi} , w_B:: Weighted{<:Any,<:SemiclassicalJacobi} ) = convert (WeightedBasis, w_A) \ convert (WeightedBasis, w_B)
459501
502+ function \ (w_A:: HalfWeighted{lr, T, <:SemiclassicalJacobi} , B:: AbstractQuasiArray{V} ) where {lr, T, V}
503+ WP = convert (WeightedBasis, w_A)
504+ w_A. P. b ≠ - 1 && return WP \ B # no need to special case here
505+ ! iszero (WP. args[1 ]. b) && throw (ArgumentError (" Cannot expand in a weighted basis including 1/(1-x)." ))
506+ # To expand f(x) = w(x)P(x)𝐟, note that P = [1 (1-x)Q] so
507+ # f(x) = w(x)[1 (1-x)Q(x)][f₀; 𝐟₁] = w(x)f₀ + w(x)(1-x)Q(x)𝐟₁. Thus,
508+ # f(1) = w(1)f₀ ⟹ f₀ = f(1) / w(1)
509+ # Then, f(x) - w(x)f₀ = w(x)(1-x)Q(x)𝐟₁, so that 𝐟₁ is just the expansion of
510+ # f(x) - w(x)f₀ in the w(x)(1-x)Q(x) basis.
511+ w, P = WP. args
512+ f₀ = B[end ] / w[end ]
513+ C = B - w * f₀
514+ Q = SemiclassicalJacobiWeight (w. t, w. a, one (w. b), w. c) .* SemiclassicalJacobi (P. t, P. a, one (P. b), P. c, P)
515+ f = Q \ C
516+ return Vcat (f₀, f)
517+ end
518+
460519weightedgrammatrix (P:: SemiclassicalJacobi ) = Diagonal (Fill (sum (orthogonalityweight (P)),∞))
461520
462521@simplify function * (Ac:: QuasiAdjoint{<:Any,<:SemiclassicalJacobi} , wB:: WeightedBasis{<:Any,<:SemiclassicalJacobiWeight,<:SemiclassicalJacobi} )
@@ -472,9 +531,11 @@ function ldiv(Q::SemiclassicalJacobi, f::AbstractQuasiVector)
472531 R = legendre (zero (T).. one (T))
473532 B = neg1c_tolegendre (Q. t)
474533 return (B \ (R \ f))
475- elseif isinteger (Q. a) && isinteger (Q. b) && isinteger (Q. c) # (a,b,c) are integers -> use QR/Cholesky
534+ elseif isinteger (Q. a) && ( isinteger (Q. b) && Q . b ≥ 0 ) && isinteger (Q. c) # (a,b,c) are integers -> use QR/Cholesky
476535 R̃ = Normalized (jacobi (Q. b, Q. a, UnitInterval {T} ()))
477536 return (Q \ SemiclassicalJacobi (Q. t, Q. a, Q. b, 0 )) * _p0 (R̃) * (R̃ \ f)
537+ elseif isinteger (Q. a) && isone (- Q. b) && isinteger (Q. c)
538+ return semijacobi_ldiv (Q, f) # jacobi(< 0, Q.a) fails in the method above. jacobi(-1, 0) also leads to NaNs in coefficients
478539 else # fallback to Lanzcos
479540 R̃ = toclassical (SemiclassicalJacobi (Q. t, mod (Q. a,- 1 ), mod (Q. b,- 1 ), mod (Q. c,- 1 )))
480541 return (Q \ R̃) * (R̃ \ f)
@@ -530,7 +591,7 @@ mutable struct SemiclassicalJacobiFamily{T, A, B, C} <: AbstractCachedVector{Sem
530591 datasize:: Tuple{Int}
531592end
532593
533- isnormalized (:: SemiclassicalJacobi ) = true
594+ isnormalized (J :: SemiclassicalJacobi ) = J . b ≠ - 1 # there is no normalisation for b == -1
534595size (P:: SemiclassicalJacobiFamily ) = (max (length (P. a), length (P. b), length (P. c)),)
535596
536597_checkrangesizes () = ()
@@ -572,8 +633,12 @@ _broadcast_getindex(a::Number,k) = a
572633
573634function LazyArrays. cache_filldata! (P:: SemiclassicalJacobiFamily , inds:: AbstractUnitRange )
574635 t,a,b,c = P. t,P. a,P. b,P. c
636+ isrange = P. b isa AbstractUnitRange
575637 for k in inds
576- P. data[k] = SemiclassicalJacobi (t, _broadcast_getindex (a,k), _broadcast_getindex (b,k), _broadcast_getindex (c,k), P. data[k- 2 ])
638+ # If P.data[k-2] is not normalised (aka b = -1), cholesky fails. With the current design, this is only a problem if P.b
639+ # is a range since we can translate between polynomials that both have b = -1.
640+ Pprev = (isrange && P. b[k- 2 ] == - 1 ) ? P. data[k- 1 ] : P. data[k- 2 ] # isrange && P.b[k-2] == -1 could also be !isnormalized(P.data[k-2])
641+ P. data[k] = SemiclassicalJacobi (t, _broadcast_getindex (a,k), _broadcast_getindex (b,k), _broadcast_getindex (c,k), Pprev)
577642 end
578643 P
579644end
0 commit comments