@@ -18,6 +18,7 @@ import InfiniteArrays: OneToInf, InfUnitRange
18
18
import ContinuumArrays: basis, Weight, @simplify , AbstractBasisLayout, BasisLayout, MappedBasisLayout, grid, plotgrid, equals_layout, ExpansionLayout
19
19
import FillArrays: SquareEye
20
20
import HypergeometricFunctions: _₂F₁general2
21
+ import InfiniteLinearAlgebra: BidiagonalConjugation
21
22
22
23
export LanczosPolynomial, Legendre, Normalized, normalize, SemiclassicalJacobi, SemiclassicalJacobiWeight, WeightedSemiclassicalJacobi, OrthogonalPolynomialRatio
23
24
@@ -139,18 +140,26 @@ function semiclassical_jacobimatrix(t, a, b, c)
139
140
C = - (N). / (N.* 4 .- 2 )
140
141
B = Vcat ((α[1 ]^ 2 * 3 - α[1 ]* α[2 ]* 2 - 1 )/ 6 , - (N). / (N.* 4 .+ 2 ). * α[2 : end ]. / α)
141
142
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
154
163
end
155
164
end
156
165
@@ -162,11 +171,16 @@ function semiclassical_jacobimatrix(Q::SemiclassicalJacobi, a, b, c)
162
171
# special cases
163
172
if iszero (a) && iszero (b) && c == - one (eltype (Q. t)) # (a,b,c) = (0,0,-1) special case
164
173
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 ])
165
177
elseif iszero (c) # classical Jacobi polynomial special case
166
178
return jacobimatrix (Normalized (jacobi (b, a, UnitInterval {eltype(Q.t)} ())))
167
179
elseif iszero (Δa) && iszero (Δb) && iszero (Δc) # same basis
168
180
return Q. X
169
- end
181
+ elseif b == - one (eltype (Q. t))
182
+ return semiclassical_jacobimatrix (Q. t, a, b, c)
183
+ end
170
184
171
185
if isone (Δa/ 2 ) && iszero (Δb) && iszero (Δc) # raising by 2
172
186
qr_jacobimatrix (Q. X,Q)
@@ -408,7 +422,35 @@ function copy(L::Ldiv{SemiclassicalJacobiLayout,SemiclassicalJacobiLayout})
408
422
(inv (M_Q) * L' ) * M_P
409
423
end
410
424
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
412
454
\ (A:: LanczosPolynomial , B:: SemiclassicalJacobi ) = semijacobi_ldiv (A, B)
413
455
\ (A:: SemiclassicalJacobi , B:: LanczosPolynomial ) = semijacobi_ldiv (A, B)
414
456
function \ (w_A:: WeightedSemiclassicalJacobi{T} , w_B:: WeightedSemiclassicalJacobi{T} ) where T
457
499
458
500
\ (w_A:: Weighted{<:Any,<:SemiclassicalJacobi} , w_B:: Weighted{<:Any,<:SemiclassicalJacobi} ) = convert (WeightedBasis, w_A) \ convert (WeightedBasis, w_B)
459
501
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
+
460
519
weightedgrammatrix (P:: SemiclassicalJacobi ) = Diagonal (Fill (sum (orthogonalityweight (P)),∞))
461
520
462
521
@simplify function * (Ac:: QuasiAdjoint{<:Any,<:SemiclassicalJacobi} , wB:: WeightedBasis{<:Any,<:SemiclassicalJacobiWeight,<:SemiclassicalJacobi} )
@@ -472,9 +531,11 @@ function ldiv(Q::SemiclassicalJacobi, f::AbstractQuasiVector)
472
531
R = legendre (zero (T).. one (T))
473
532
B = neg1c_tolegendre (Q. t)
474
533
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
476
535
R̃ = Normalized (jacobi (Q. b, Q. a, UnitInterval {T} ()))
477
536
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
478
539
else # fallback to Lanzcos
479
540
R̃ = toclassical (SemiclassicalJacobi (Q. t, mod (Q. a,- 1 ), mod (Q. b,- 1 ), mod (Q. c,- 1 )))
480
541
return (Q \ R̃) * (R̃ \ f)
@@ -530,7 +591,7 @@ mutable struct SemiclassicalJacobiFamily{T, A, B, C} <: AbstractCachedVector{Sem
530
591
datasize:: Tuple{Int}
531
592
end
532
593
533
- isnormalized (:: SemiclassicalJacobi ) = true
594
+ isnormalized (J :: SemiclassicalJacobi ) = J . b ≠ - 1 # there is no normalisation for b == -1
534
595
size (P:: SemiclassicalJacobiFamily ) = (max (length (P. a), length (P. b), length (P. c)),)
535
596
536
597
_checkrangesizes () = ()
@@ -572,8 +633,12 @@ _broadcast_getindex(a::Number,k) = a
572
633
573
634
function LazyArrays. cache_filldata! (P:: SemiclassicalJacobiFamily , inds:: AbstractUnitRange )
574
635
t,a,b,c = P. t,P. a,P. b,P. c
636
+ isrange = P. b isa AbstractUnitRange
575
637
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)
577
642
end
578
643
P
579
644
end
0 commit comments