@@ -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