Skip to content

Commit 935a5fa

Browse files
authored
Associated OPs (#29)
* reorg * Start associated * start testing weights * Legendre Hilbert transform * Update test_jacobi.jl * Associated OPs transform * Update jacobi.jl * UltrasphericalWeight sum * Increase coverage * increase coverage
1 parent 58e2f79 commit 935a5fa

14 files changed

+320
-113
lines changed

Project.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
1414
FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838"
1515
FastTransforms = "057dd010-8810-581a-b7be-e3fc3b93f78c"
1616
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
17+
HypergeometricFunctions = "34004b35-14d8-5ef3-9330-4cdb6864b03a"
1718
InfiniteArrays = "4858937d-0d70-526a-a4dd-2d5cb5dd786c"
1819
InfiniteLinearAlgebra = "cde9dba0-b1de-11e9-2c62-0bab9446c55c"
1920
IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953"
@@ -26,20 +27,21 @@ SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
2627
[compat]
2728
ArrayLayouts = "0.6.2"
2829
BandedMatrices = "0.16.5"
29-
BlockArrays = "0.14.1, 0.15"
30+
BlockArrays = "0.15"
3031
BlockBandedMatrices = "0.10"
31-
ContinuumArrays = "0.6.4"
32+
ContinuumArrays = "0.7"
3233
DomainSets = "0.4, 0.5"
3334
FFTW = "1.1"
3435
FastGaussQuadrature = "0.4.3"
3536
FastTransforms = "0.11, 0.12"
3637
FillArrays = "0.11"
38+
HypergeometricFunctions = "0.3.4"
3739
InfiniteArrays = "0.10.3"
3840
InfiniteLinearAlgebra = "0.5.3"
3941
IntervalSets = "0.5"
4042
LazyArrays = "0.21"
41-
LazyBandedMatrices = "0.5.1"
42-
QuasiArrays = "0.4.6"
43+
LazyBandedMatrices = "0.5.5"
44+
QuasiArrays = "0.5"
4345
SpecialFunctions = "0.10, 1"
4446
julia = "1.5"
4547

src/ClassicalOrthogonalPolynomials.jl

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module ClassicalOrthogonalPolynomials
22
using ContinuumArrays, QuasiArrays, LazyArrays, FillArrays, BandedMatrices, BlockArrays,
33
IntervalSets, DomainSets, ArrayLayouts, SpecialFunctions,
44
InfiniteLinearAlgebra, InfiniteArrays, LinearAlgebra, FastGaussQuadrature, FastTransforms, FFTW,
5-
LazyBandedMatrices
5+
LazyBandedMatrices, HypergeometricFunctions
66

77
import Base: @_inline_meta, axes, getindex, convert, prod, *, /, \, +, -,
88
IndexStyle, IndexLinear, ==, OneTo, tail, similar, copyto!, copy,
@@ -126,6 +126,7 @@ Note that `X` is the transpose of the usual definition of the Jacobi matrix.
126126
"""
127127
jacobimatrix(P) = error("Override for $(typeof(P))")
128128

129+
129130
"""
130131
recurrencecoefficients(P)
131132
@@ -261,10 +262,52 @@ _vec(a::InfiniteArrays.ReshapedArray) = _vec(parent(a))
261262
_vec(a::Adjoint{<:Any,<:AbstractVector}) = a'
262263

263264
include("clenshaw.jl")
265+
include("ratios.jl")
266+
include("normalized.jl")
267+
include("lanczos.jl")
268+
269+
function _tritrunc(_, X, n)
270+
c,a,b = subdiagonaldata(X),diagonaldata(X),supdiagonaldata(X)
271+
Tridiagonal(c[OneTo(n-1)],a[OneTo(n)],b[OneTo(n-1)])
272+
end
273+
274+
function _tritrunc(::SymTridiagonalLayout, X, n)
275+
a,b = diagonaldata(X),supdiagonaldata(X)
276+
SymTridiagonal(a[OneTo(n)],b[OneTo(n-1)])
277+
end
278+
279+
_tritrunc(X, n) = _tritrunc(MemoryLayout(X), X, n)
280+
281+
jacobimatrix(V::SubQuasiArray{<:Any,2,<:Any,<:Tuple{Inclusion,OneTo}}) =
282+
_tritrunc(jacobimatrix(parent(V)), maximum(parentindices(V)[2]))
283+
284+
grid(P::SubQuasiArray{<:Any,2,<:OrthogonalPolynomial,<:Tuple{Inclusion,AbstractUnitRange}}) =
285+
eigvals(symtridiagonalize(jacobimatrix(P)))
264286

287+
function golubwelsch(X)
288+
D, V = eigen(symtridiagonalize(X)) # Eigenvalue decomposition
289+
D, V[1,:].^2
290+
end
291+
292+
function golubwelsch(V::SubQuasiArray)
293+
x,w = golubwelsch(jacobimatrix(V))
294+
w .*= sum(orthogonalityweight(parent(V)))
295+
x,w
296+
end
297+
298+
function factorize(L::SubQuasiArray{T,2,<:Normalized,<:Tuple{Inclusion,OneTo}}) where T
299+
x,w = golubwelsch(L)
300+
TransformFactorization(x, L[x,:]'*Diagonal(w))
301+
end
265302

266-
factorize(L::SubQuasiArray{T,2,<:OrthogonalPolynomial,<:Tuple{<:Inclusion,<:OneTo}}) where T =
267-
TransformFactorization(grid(L), nothing, qr(L[grid(L),:])) # Use QR so type-stable
303+
304+
function factorize(L::SubQuasiArray{T,2,<:OrthogonalPolynomial,<:Tuple{Inclusion,OneTo}}) where T
305+
x,w = golubwelsch(L)
306+
Q = Normalized(parent(L))[parentindices(L)...]
307+
D = L \ Q
308+
F = factorize(Q)
309+
TransformFactorization(F.grid, D*F.plan)
310+
end
268311

269312
function factorize(L::SubQuasiArray{T,2,<:OrthogonalPolynomial,<:Tuple{<:Inclusion,<:AbstractUnitRange}}) where T
270313
_,jr = parentindices(L)
@@ -290,15 +333,13 @@ function \(wA::WeightedOrthogonalPolynomial, wB::WeightedOrthogonalPolynomial)
290333
A\B
291334
end
292335

293-
include("ratios.jl")
294-
include("normalized.jl")
295-
include("lanczos.jl")
296-
include("hermite.jl")
297-
include("jacobi.jl")
298-
include("chebyshev.jl")
299-
include("ultraspherical.jl")
300-
include("laguerre.jl")
301-
include("fourier.jl")
336+
337+
include("classical/hermite.jl")
338+
include("classical/jacobi.jl")
339+
include("classical/chebyshev.jl")
340+
include("classical/ultraspherical.jl")
341+
include("classical/laguerre.jl")
342+
include("classical/fourier.jl")
302343
include("stieltjes.jl")
303344

304345

File renamed without changes.
File renamed without changes.
File renamed without changes.

src/jacobi.jl renamed to src/classical/jacobi.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,10 @@ copy(Q::HalfWeighted) = Q
163163

164164
convert(::Type{WeightedOrthogonalPolynomial}, Q::HalfWeighted{:a,T,<:Jacobi}) where T = JacobiWeight(Q.P.a,zero(T)) .* Q.P
165165
convert(::Type{WeightedOrthogonalPolynomial}, Q::HalfWeighted{:b,T,<:Jacobi}) where T = JacobiWeight(zero(T),Q.P.b) .* Q.P
166-
convert(::Type{WeightedOrthogonalPolynomial}, Q::HalfWeighted{:a,T,<:Normalized}) where T = JacobiWeight(Q.P.P.a,zero(T)) .* Q.P
167-
convert(::Type{WeightedOrthogonalPolynomial}, Q::HalfWeighted{:b,T,<:Normalized}) where T = JacobiWeight(zero(T),Q.P.P.b) .* Q.P
166+
function convert(::Type{WeightedOrthogonalPolynomial}, Q::HalfWeighted{lr,T,<:Normalized}) where {T,lr}
167+
w,_ = arguments(convert(WeightedOrthogonalPolynomial, HalfWeighted{lr}(Q.P.P)))
168+
w .* Q.P
169+
end
168170

169171
getindex(Q::HalfWeighted, x::Union{Number,AbstractVector}, jr::Union{Number,AbstractVector}) = convert(WeightedOrthogonalPolynomial, Q)[x,jr]
170172

@@ -199,7 +201,7 @@ summary(io::IO, P::Jacobi) = print(io, "Jacobi($(P.a), $(P.b))")
199201
# transforms
200202
###
201203

202-
function grid(Pn::SubQuasiArray{T,2,<:AbstractJacobi,<:Tuple{<:Inclusion,<:AbstractUnitRange}}) where T
204+
function grid(Pn::SubQuasiArray{T,2,<:AbstractJacobi,<:Tuple{Inclusion,AbstractUnitRange}}) where T
203205
kr,jr = parentindices(Pn)
204206
ChebyshevGrid{1,T}(maximum(jr))
205207
end
File renamed without changes.

src/ultraspherical.jl renamed to src/classical/ultraspherical.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ struct UltrasphericalWeight{T,Λ} <: AbstractJacobiWeight{T}
1010
end
1111

1212
UltrasphericalWeight{T}(λ) where T = UltrasphericalWeight{T,typeof(λ)}(λ)
13-
UltrasphericalWeight(λ) = UltrasphericalWeight{typeof(λ),typeof(λ)}(λ)
13+
UltrasphericalWeight(λ) = UltrasphericalWeight{float(typeof(λ)),typeof(λ)}(λ)
1414

1515
==(a::UltrasphericalWeight, b::UltrasphericalWeight) = a.λ == b.λ
1616

@@ -19,6 +19,7 @@ function getindex(w::UltrasphericalWeight, x::Number)
1919
(1-x^2)^(w.λ-one(w.λ)/2)
2020
end
2121

22+
sum(w::UltrasphericalWeight{T}) where T = sqrt(convert(T,π))*exp(loggamma(one(T)/2 + w.λ)-loggamma(1+w.λ))
2223

2324

2425
struct Ultraspherical{T,Λ} <: AbstractJacobi{T}

src/lanczos.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ struct LanczosConversion{T} <: LayoutMatrix{T}
6868
end
6969

7070
size(::LanczosConversion) = (∞,∞)
71+
copy(R::LanczosConversion) = R
72+
7173
bandwidths(::LanczosConversion) = (0,∞)
7274
colsupport(L::LanczosConversion, j) = 1:maximum(j)
7375
rowsupport(L::LanczosConversion, j) = minimum(j):

src/stieltjes.jl

Lines changed: 131 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,187 @@
1+
####
2+
# Associated
3+
####
4+
5+
6+
"""
7+
AssociatedWeighted(P)
8+
9+
We normalise so that `orthogonalityweight(::Associated)` is a probability measure.
10+
"""
11+
struct AssociatedWeight{T,OPs<:AbstractQuasiMatrix{T}} <: Weight{T}
12+
P::OPs
13+
end
14+
axes(w::AssociatedWeight) = (axes(w.P,1),)
15+
16+
sum(::AssociatedWeight{T}) where T = one(T)
17+
18+
"""
19+
Associated(P)
20+
21+
constructs the associated orthogonal polynomials for P, which have the Jacobi matrix
22+
23+
jacobimatrix(P)[2:end,2:end]
24+
25+
and constant first term. Or alternatively
26+
27+
w = orthogonalityweight(P)
28+
A = recurrencecoefficients(P)[1]
29+
Associated(P) == (w/(sum(w)*A[1]))'*((P[:,2:end]' - P[:,2:end]) ./ (x' - x))
30+
31+
where `x = axes(P,1)`.
32+
"""
33+
34+
struct Associated{T, OPs<:AbstractQuasiMatrix{T}} <: OrthogonalPolynomial{T}
35+
P::OPs
36+
end
37+
38+
associated(P) = Associated(P)
39+
40+
axes(Q::Associated) = axes(Q.P)
41+
==(A::Associated, B::Associated) = A.P == B.P
42+
43+
orthogonalityweight(Q::Associated) = AssociatedWeight(Q.P)
44+
45+
function associated_jacobimatrix(X::Tridiagonal)
46+
c,a,b = subdiagonaldata(X),diagonaldata(X),supdiagonaldata(X)
47+
Tridiagonal(c[2:end], a[2:end], b[2:end])
48+
end
49+
50+
function associated_jacobimatrix(X::SymTridiagonal)
51+
a,b = diagonaldata(X),supdiagonaldata(X)
52+
SymTridiagonal(a[2:end], b[2:end])
53+
end
54+
jacobimatrix(a::Associated) = associated_jacobimatrix(jacobimatrix(a.P))
55+
56+
associated(::ChebyshevT{T}) where T = ChebyshevU{T}()
57+
associated(::ChebyshevU{T}) where T = ChebyshevU{T}()
58+
59+
160
const StieltjesPoint{T,V,D} = BroadcastQuasiMatrix{T,typeof(inv),Tuple{BroadcastQuasiMatrix{T,typeof(-),Tuple{T,QuasiAdjoint{V,Inclusion{V,D}}}}}}
261
const ConvKernel{T,D} = BroadcastQuasiMatrix{T,typeof(-),Tuple{D,QuasiAdjoint{T,D}}}
362
const Hilbert{T,D} = BroadcastQuasiMatrix{T,typeof(inv),Tuple{ConvKernel{T,Inclusion{T,D}}}}
463
const LogKernel{T,D} = BroadcastQuasiMatrix{T,typeof(log),Tuple{BroadcastQuasiMatrix{T,typeof(abs),Tuple{ConvKernel{T,Inclusion{T,D}}}}}}
564
const PowKernel{T,D,F<:Real} = BroadcastQuasiMatrix{T,typeof(^),Tuple{BroadcastQuasiMatrix{T,typeof(abs),Tuple{ConvKernel{T,Inclusion{T,D}}}},F}}
665

766

8-
@simplify function *(S::StieltjesPoint{<:Any,<:Any,<:ChebyshevInterval}, wT::WeightedBasis{<:Any,<:ChebyshevTWeight,<:ChebyshevT})
9-
w,T = wT.args
10-
J = jacobimatrix(T)
11-
z, x = parent(S).args[1].args
12-
transpose((J'-z*I) \ [-π; zeros(∞)])
67+
@simplify function *(H::Hilbert, w::ChebyshevTWeight)
68+
T = promote_type(eltype(H), eltype(w))
69+
zeros(T, axes(w,1))
70+
end
71+
72+
@simplify function *(H::Hilbert, w::ChebyshevUWeight)
73+
T = promote_type(eltype(H), eltype(w))
74+
fill(convert(T,π), axes(w,1))
1375
end
1476

15-
@simplify function *(H::Hilbert{<:Any,<:ChebyshevInterval}, wT::WeightedBasis{<:Any,<:ChebyshevTWeight,<:ChebyshevT})
77+
@simplify function *(H::Hilbert, w::LegendreWeight)
78+
T = promote_type(eltype(H), eltype(w))
79+
x = axes(w,1)
80+
log.(x .+ 1) .- log.(1 .- x)
81+
end
82+
83+
@simplify function *(H::Hilbert, wT::Weighted{<:Any,<:ChebyshevT})
1684
T = promote_type(eltype(H), eltype(wT))
17-
ApplyQuasiArray(*, ChebyshevU{T}(), _BandedMatrix(Fill(-convert(T,π),1,∞), ℵ₀, -1, 1))
85+
ChebyshevU{T}() * _BandedMatrix(Fill(-convert(T,π),1,∞), ℵ₀, -1, 1)
1886
end
1987

20-
@simplify function *(H::Hilbert{<:Any,<:ChebyshevInterval}, wU::WeightedBasis{<:Any,<:ChebyshevUWeight,<:ChebyshevU})
88+
@simplify function *(H::Hilbert, wU::Weighted{<:Any,<:ChebyshevU})
2189
T = promote_type(eltype(H), eltype(wU))
22-
ApplyQuasiArray(*, ChebyshevT{T}(), _BandedMatrix(Fill(convert(T,π),1,∞), ℵ₀, 1, -1))
90+
ChebyshevT{T}() * _BandedMatrix(Fill(convert(T,π),1,∞), ℵ₀, 1, -1)
91+
end
92+
93+
94+
@simplify function *(H::Hilbert, wP::Weighted{<:Any,<:OrthogonalPolynomial})
95+
P = wP.P
96+
w = orthogonalityweight(P)
97+
A = recurrencecoefficients(P)[1]
98+
(-A[1]*sum(w))*[zero(axes(P,1)) associated(P)] + (H*w) .* P
2399
end
24100

101+
@simplify *(H::Hilbert, P::Legendre) = H * Weighted(P)
102+
25103
###
26104
# LogKernel
27105
###
28106

29-
@simplify function *(L::LogKernel{<:Any,<:ChebyshevInterval}, wT::WeightedBasis{<:Any,<:ChebyshevTWeight,<:ChebyshevT})
107+
@simplify function *(L::LogKernel, wT::Weighted{<:Any,<:ChebyshevT})
30108
T = promote_type(eltype(L), eltype(wT))
31-
ApplyQuasiArray(*, ChebyshevT{T}(), Diagonal(Vcat(-π*log(2*one(T)),-convert(T,π)./(1:))))
109+
ChebyshevT{T}() * Diagonal(Vcat(-π*log(2*one(T)),-convert(T,π)./(1:∞)))
32110
end
33111

34112

113+
35114
###
36115
# PowKernel
37116
###
38117

39-
@simplify function *(K::PowKernel{<:Any,<:ChebyshevInterval}, wT::WeightedBasis{<:Any,<:JacobiWeight,<:Jacobi})
118+
@simplify function *(K::PowKernel, wT::Weighted{<:Any,<:Jacobi})
40119
T = promote_type(eltype(K), eltype(wT))
41120
cnv,α = K.args
42121
x,y = K.args[1].args[1].args
43122
@assert x' == y
44123
β = (-α-1)/2
45-
ApplyQuasiArray(*, ChebyshevT{T}(), Diagonal(1:∞))
124+
error("Not implemented")
125+
# ChebyshevT{T}() * Diagonal(1:∞)
46126
end
47127

48128

49129
####
50130
# StieltjesPoint
51131
####
52132

53-
@simplify function *(S::StieltjesPoint, wT::SubQuasiArray{<:Any,2,<:WeightedBasis,<:Tuple{<:AbstractAffineQuasiVector,<:Any}})
133+
stieltjesmoment_jacobi_normalization(n::Int::Real::Real) = 2^+β)*gamma(n+α+1)*gamma(n+β+1)/gamma(2n+α+β+2)
134+
135+
@simplify function *(S::StieltjesPoint, w::AbstractJacobiWeight)
136+
α,β = w.a,w.b
137+
z,_ = parent(S).args[1].args
138+
(x = 2/(1-z);stieltjesmoment_jacobi_normalization(0,α,β)*HypergeometricFunctions.mxa_₂F₁(1+1+β+2,x))
139+
end
140+
141+
@simplify function *(S::StieltjesPoint, wP::Weighted)
142+
P = wP.P
143+
w = orthogonalityweight(P)
144+
X = jacobimatrix(P)
145+
z, x = parent(S).args[1].args
146+
transpose((X'-z*I) \ [-sum(w)*_p0(P); zeros(∞)])
147+
end
148+
149+
150+
@simplify function *(S::StieltjesPoint, wT::SubQuasiArray{<:Any,2,<:Any,<:Tuple{<:AbstractAffineQuasiVector,<:Any}})
54151
P = parent(wT)
55152
z, x = parent(S).args[1].args
56153
= inbounds_getindex(parentindices(wT)[1], z)
57154
= axes(P,1)
58155
(inv.(z̃ .-') * P)[:,parentindices(wT)[2]]
59156
end
60157

61-
@simplify function *(H::Hilbert, wT::SubQuasiArray{<:Any,2,<:WeightedBasis,<:Tuple{<:AbstractAffineQuasiVector,<:Any}})
158+
@simplify function *(H::Hilbert, wT::SubQuasiArray{<:Any,2,<:Any,<:Tuple{<:AbstractAffineQuasiVector,<:Any}})
62159
P = parent(wT)
63160
x = axes(P,1)
64161
apply(*, inv.(x .- x'), P)[parentindices(wT)...]
65162
end
66163

67164

68-
@simplify function *(L::LogKernel, wT::SubQuasiArray{<:Any,2,<:WeightedBasis,<:Tuple{<:AbstractAffineQuasiVector,<:Slice}})
165+
@simplify function *(L::LogKernel, wT::SubQuasiArray{<:Any,2,<:Any,<:Tuple{<:AbstractAffineQuasiVector,<:Slice}})
69166
V = promote_type(eltype(L), eltype(wT))
70-
P = parent(wT)
167+
wP = parent(wT)
71168
kr, jr = parentindices(wT)
72-
@assert P isa WeightedBasis{<:Any,<:ChebyshevWeight,<:Chebyshev}
73-
x = axes(P,1)
74-
w,T = P.args
75-
D = T \ apply(*, log.(abs.(x .- x')), P)
169+
x = axes(wP,1)
170+
w,T = arguments(ApplyLayout{typeof(*)}(), wP)
171+
@assert w isa ChebyshevTWeight
172+
@assert T isa ChebyshevT
173+
D = T \ (log.(abs.(x .- x')) * wP)
76174
c = inv(2*kr.A)
77175
T[kr,:] * Diagonal(Vcat(2*convert(V,π)*c*log(c), 2c*D.diag.args[2]))
78-
end
176+
end
177+
178+
179+
180+
### generic fallback
181+
for Op in (:Hilbert, :StieltjesPoint, :LogKernel, :PowKernel)
182+
@eval @simplify function *(H::$Op, wP::WeightedBasis{<:Any,<:Weight,<:Any})
183+
w,P = wP.args
184+
Q = OrthogonalPolynomial(w)
185+
(H * Weighted(Q)) * (Q \ P)
186+
end
187+
end

test/test_jacobi.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ import ClassicalOrthogonalPolynomials: recurrencecoefficients, basis, MulQuasiMa
242242
w = JacobiWeight(1.0,1.0)
243243
wS = w .* S
244244

245-
W = Diagonal(w)
245+
W = QuasiDiagonal(w)
246246
@test W[0.1,0.2] 0.0
247247
end
248248

0 commit comments

Comments
 (0)