Skip to content

Commit bb63d69

Browse files
authored
Add roots and extend Jacobi recurrences (#114)
* Add roots and extend Jacobi recurrences * searchsortedfirst * add tests * checkpoints in Fourier should use axes type not eltype * use axestype for grid * Start Laurent * Support Laurent * Update Project.toml * new grid gramework * Update Project.toml * Create annulipdes.jl * Update annulipdes.jl * arr -> szs * tests pass * Update ClassicalOrthogonalPolynomials.jl * Update Project.toml
1 parent 547352c commit bb63d69

File tree

13 files changed

+355
-85
lines changed

13 files changed

+355
-85
lines changed

Project.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "ClassicalOrthogonalPolynomials"
22
uuid = "b30e2e7b-c4ee-47da-9d5f-2c5c27239acd"
33
authors = ["Sheehan Olver <[email protected]>"]
4-
version = "0.6.9"
4+
version = "0.7"
55

66
[deps]
77
ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
@@ -29,8 +29,8 @@ ArrayLayouts = "0.8"
2929
BandedMatrices = "0.17"
3030
BlockArrays = "0.16.9"
3131
BlockBandedMatrices = "0.11.6"
32-
ContinuumArrays = "0.11"
33-
DomainSets = "0.5.6"
32+
ContinuumArrays = "0.12.1"
33+
DomainSets = "0.5.6, 0.6"
3434
FFTW = "1.1"
3535
FastGaussQuadrature = "0.4.3, 0.5"
3636
FastTransforms = "0.14.4"
@@ -39,8 +39,8 @@ HypergeometricFunctions = "0.3.4"
3939
InfiniteArrays = "0.12.3"
4040
InfiniteLinearAlgebra = "0.6.7"
4141
IntervalSets = "0.5, 0.6, 0.7"
42-
LazyArrays = "0.22.11"
43-
LazyBandedMatrices = "0.7.14, 0.8"
42+
LazyArrays = "0.22.16"
43+
LazyBandedMatrices = "0.8.5"
4444
QuasiArrays = "0.9"
4545
SpecialFunctions = "1.0, 2"
4646
julia = "1.7"

examples/annulipdes.jl

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using ClassicalOrthogonalPolynomials, Plots
2+
3+
ρ = 0.5; T = chebyshevt..1); U = chebyshevu(T); C = ultraspherical(2, ρ..1); r = axes(T,1); D = Derivative(r);
4+
5+
L = C \ (r.^2 .* (D^2 * T)) + C \ (r .* (D * T)) # r^2 * ∂^2 + r*∂
6+
M = C\T # Identity
7+
8+
m = 2
9+
Δₘ = L - m^2*M # r^2 * Laplacian for exp(im*m*θ)*u(r), i.e. (r^2 * ∂^2 + r*∂ - m^2*I)*u
10+
11+
12+
13+
# Poisson solve
14+
c = C \ exp.(r)
15+
R = C \ (r .* C)
16+
17+
18+
d = [T[[begin,end],:];
19+
Δₘ] \ [1; 2; R^2 * c]
20+
21+
plot(T*d)
22+
23+
24+
# Helmholtz
25+
26+
27+
Q = R^2 * M # r^2, needed for Helmholtz (Δ + k^2)*u = f
28+
29+
k = 5 # f
30+
31+
d = [T[[begin,end],:];
32+
Δₘ+k^2*Q] \ [1; 2; R^2 * c]
33+
34+
35+
# transform
36+
37+
f = (r,θ) -> exp(r*cos(θ))
38+
39+
n = 10 # create a 10 x 10 transform
40+
41+
F = Fourier()
42+
𝐫,𝛉 = grid(T, n),grid(F, n)
43+
44+
transform(T, transform(F, f.(𝐫, 𝛉'); dims=2); dims=1)

src/ClassicalOrthogonalPolynomials.jl

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ using ContinuumArrays, QuasiArrays, LazyArrays, FillArrays, BandedMatrices, Bloc
1010
import Base: @_inline_meta, axes, getindex, unsafe_getindex, convert, prod, *, /, \, +, -,
1111
IndexStyle, IndexLinear, ==, OneTo, tail, similar, copyto!, copy, setindex,
1212
first, last, Slice, size, length, axes, IdentityUnitRange, sum, _sum, cumsum,
13-
to_indices, _maybetail, tail, getproperty, inv, show, isapprox, summary
13+
to_indices, _maybetail, tail, getproperty, inv, show, isapprox, summary,
14+
findall, searchsortedfirst
1415
import Base.Broadcast: materialize, BroadcastStyle, broadcasted, Broadcasted
1516
import LazyArrays: MemoryLayout, Applied, ApplyStyle, flatten, _flatten, adjointlayout,
1617
sub_materialize, arguments, sub_paddeddata, paddeddata, PaddedLayout, resizedata!, LazyVector, ApplyLayout, call,
@@ -34,10 +35,10 @@ import QuasiArrays: cardinality, checkindex, QuasiAdjoint, QuasiTranspose, Inclu
3435
import InfiniteArrays: OneToInf, InfAxes, Infinity, AbstractInfUnitRange, InfiniteCardinal, InfRanges
3536
import InfiniteLinearAlgebra: chop!, chop, choplength, compatible_resize!
3637
import ContinuumArrays: Basis, Weight, basis, @simplify, Identity, AbstractAffineQuasiVector, ProjectionFactorization,
37-
inbounds_getindex, grid, plotgrid, transform_ldiv, TransformFactorization, QInfAxes, broadcastbasis, ExpansionLayout, basismap,
38+
inbounds_getindex, grid, plotgrid, _plotgrid, _grid, transform_ldiv, TransformFactorization, QInfAxes, broadcastbasis, ExpansionLayout, basismap,
3839
AffineQuasiVector, AffineMap, WeightLayout, AbstractWeightedBasisLayout, WeightedBasisLayout, WeightedBasisLayouts, demap, AbstractBasisLayout, BasisLayout,
3940
checkpoints, weight, unweighted, MappedBasisLayouts, __sum, invmap, plan_ldiv, layout_broadcasted, MappedBasisLayout, SubBasisLayout, _broadcastbasis,
40-
plan_transform, plan_grid_transform
41+
plan_transform, plan_grid_transform, MAX_PLOT_POINTS
4142
import FastTransforms: Λ, forwardrecurrence, forwardrecurrence!, _forwardrecurrence!, clenshaw, clenshaw!,
4243
_forwardrecurrence_next, _clenshaw_next, check_clenshaw_recurrences, ChebyshevGrid, chebyshevpoints, Plan
4344

@@ -47,13 +48,14 @@ import BlockArrays: blockedrange, _BlockedUnitRange, unblock, _BlockArray, block
4748
import BandedMatrices: bandwidths
4849

4950
export OrthogonalPolynomial, Normalized, orthonormalpolynomial, LanczosPolynomial,
50-
Hermite, Jacobi, Legendre, Chebyshev, ChebyshevT, ChebyshevU, ChebyshevInterval, Ultraspherical, Fourier, Laguerre,
51+
Hermite, Jacobi, Legendre, Chebyshev, ChebyshevT, ChebyshevU, ChebyshevInterval, Ultraspherical, Fourier, Laurent, Laguerre,
5152
HermiteWeight, JacobiWeight, ChebyshevWeight, ChebyshevGrid, ChebyshevTWeight, ChebyshevUWeight, UltrasphericalWeight, LegendreWeight, LaguerreWeight,
5253
WeightedUltraspherical, WeightedChebyshev, WeightedChebyshevT, WeightedChebyshevU, WeightedJacobi,
5354
∞, Derivative, .., Inclusion,
5455
chebyshevt, chebyshevu, legendre, jacobi, ultraspherical,
5556
legendrep, jacobip, ultrasphericalc, laguerrel,hermiteh, normalizedjacobip,
56-
jacobimatrix, jacobiweight, legendreweight, chebyshevtweight, chebyshevuweight, Weighted, PiecewiseInterlace, plan_transform
57+
jacobimatrix, jacobiweight, legendreweight, chebyshevtweight, chebyshevuweight, Weighted, PiecewiseInterlace, plan_transform,
58+
expand, transform
5759

5860

5961
import Base: oneto
@@ -111,7 +113,6 @@ copy(L::Ldiv{MappedOPLayout,Lay}) where Lay<:MappedBasisLayouts = copy(Ldiv{Mapp
111113

112114
# OPs are immutable
113115
copy(a::OrthogonalPolynomial) = a
114-
copy(a::SubQuasiArray{<:Any,N,<:OrthogonalPolynomial}) where N = a
115116

116117
"""
117118
jacobimatrix(P)
@@ -245,18 +246,10 @@ _tritrunc(X, n) = _tritrunc(MemoryLayout(X), X, n)
245246
jacobimatrix(V::SubQuasiArray{<:Any,2,<:Any,<:Tuple{Inclusion,OneTo}}) =
246247
_tritrunc(jacobimatrix(parent(V)), maximum(parentindices(V)[2]))
247248

248-
grid(P::SubQuasiArray{<:Any,2,<:OrthogonalPolynomial,<:Tuple{Inclusion,OneTo}}) =
249-
eigvals(symtridiagonalize(jacobimatrix(P)))
250-
251-
function grid(Pn::SubQuasiArray{<:Any,2,<:OrthogonalPolynomial,<:Tuple{Inclusion,Any}})
252-
kr,jr = parentindices(Pn)
253-
grid(parent(Pn)[:,oneto(maximum(jr))])
254-
end
255-
256-
function plotgrid(Pn::SubQuasiArray{T,2,<:OrthogonalPolynomial,<:Tuple{Inclusion,Any}}) where T
257-
kr,jr = parentindices(Pn)
258-
grid(parent(Pn)[:,oneto(40maximum(jr))])
259-
end
249+
_grid(::AbstractOPLayout, P, n::Integer) = eigvals(symtridiagonalize(jacobimatrix(P[:,OneTo(n)])))
250+
_grid(::MappedOPLayout, P, n::Integer) = _grid(MappedBasisLayout(), P, n)
251+
_plotgrid(::AbstractOPLayout, P, n::Integer) = grid(P, min(40n, MAX_PLOT_POINTS))
252+
_plotgrid(::MappedOPLayout, P, n::Integer) = _plotgrid(MappedBasisLayout(), P, n)
260253

261254
function golubwelsch(X)
262255
D, V = eigen(symtridiagonalize(X)) # Eigenvalue decomposition
@@ -324,16 +317,16 @@ end
324317
*(A::AbstractMatrix, P::MulPlan) = MulPlan(A*P.matrix, P.dims)
325318

326319

327-
function plan_grid_transform(Q::Normalized, arr, dims=1:ndims(arr))
328-
L = Q[:,OneTo(size(arr,1))]
320+
function plan_grid_transform(Q::Normalized, szs::NTuple{N,Int}, dims=1:N) where N
321+
L = Q[:,OneTo(szs[1])]
329322
x,w = golubwelsch(L)
330323
x, MulPlan(L[x,:]'*Diagonal(w), dims)
331324
end
332325

333-
function plan_grid_transform(P::OrthogonalPolynomial, arr, dims...)
326+
function plan_grid_transform(P::OrthogonalPolynomial, szs::NTuple{N,Int}, dims=1:N) where N
334327
Q = Normalized(P)
335-
x, A = plan_grid_transform(Q, arr, dims...)
336-
n = size(arr,1)
328+
x, A = plan_grid_transform(Q, szs, dims...)
329+
n = szs[1]
337330
D = (P \ Q)[1:n, 1:n]
338331
x, D * A
339332
end
@@ -381,6 +374,6 @@ include("classical/ultraspherical.jl")
381374
include("classical/laguerre.jl")
382375
include("classical/fourier.jl")
383376
include("stieltjes.jl")
384-
377+
include("roots.jl")
385378

386379
end # module

src/classical/chebyshev.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,17 +103,17 @@ Jacobi(C::ChebyshevU{T}) where T = Jacobi(one(T)/2,one(T)/2)
103103
#######
104104

105105

106-
function plan_grid_transform(T::ChebyshevT, arr, dims...)
107-
n = size(arr,1)
108-
x = grid(T[:,oneto(n)])
106+
function plan_grid_transform(T::ChebyshevT, arr::AbstractArray, dims...)
107+
x = grid(T, size(arr,1))
109108
x, plan_chebyshevtransform(arr, dims...)
110109
end
111-
function plan_grid_transform(U::ChebyshevU{<:FastTransforms.fftwNumber}, arr, dims...)
112-
n = size(arr,1)
113-
x = grid(U[:,oneto(n)])
110+
function plan_grid_transform(U::ChebyshevU{<:FastTransforms.fftwNumber}, arr::AbstractArray, dims...)
111+
x = grid(U, size(arr,1))
114112
x, plan_chebyshevutransform(arr, dims...)
115113
end
116114

115+
plan_grid_transform(T::Chebyshev, szs::NTuple{N,Int}, dims...) where N = plan_grid_transform(T, Array{eltype(T)}(undef, szs...), dims...)
116+
117117

118118
########
119119
# Jacobi Matrix

src/classical/fourier.jl

Lines changed: 83 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,69 @@
1+
abstract type AbstractFourier{T} <: Basis{T} end
2+
3+
struct Fourier{T} <: AbstractFourier{T} end
4+
struct Laurent{T} <: AbstractFourier{T} end
15

2-
struct Fourier{T} <: Basis{T} end
36
Fourier() = Fourier{Float64}()
7+
Laurent() = Laurent{ComplexF64}()
48

59
==(::Fourier, ::Fourier) = true
10+
==(::Laurent, ::Laurent) = true
611

7-
axes(F::Fourier) = (Inclusion(ℝ), _BlockedUnitRange(1:2:∞))
12+
axes(F::AbstractFourier) = (Inclusion(ℝ), _BlockedUnitRange(1:2:∞))
813

914
function getindex(F::Fourier{T}, x::Real, j::Int)::T where T
1015
isodd(j) && return cos((j÷2)*x)
1116
sin((j÷2)*x)
1217
end
1318

19+
function getindex(F::Laurent{T}, x::Real, j::Int)::T where T
20+
s = 1-2iseven(j)
21+
exp(im*s*(j÷2)*x)
22+
end
23+
1424
### transform
15-
checkpoints(::Fourier{T}) where T = T[1.223972,3.14,5.83273484]
25+
checkpoints(F::AbstractFourier) = eltype(axes(F,1))[1.223972,3.14,5.83273484]
1626

1727
fouriergrid(T, n) = convert(T,π)*collect(0:2:2n-2)/n
28+
grid(Pn::AbstractFourier, n::Integer) = fouriergrid(eltype(axes(Pn,1)), n)
1829

19-
function grid(Pn::SubQuasiArray{T,2,<:Fourier,<:Tuple{<:Inclusion,<:AbstractUnitRange}}) where T
20-
kr,jr = parentindices(Pn)
21-
n = maximum(jr)
22-
fouriergrid(T, n)
23-
end
30+
abstract type AbstractShuffledPlan{T} <: Plan{T} end
2431

2532
"""
2633
Gives a shuffled version of the real FFT, with order
2734
1,sin(θ),cos(θ),sin(2θ)…
2835
"""
29-
struct ShuffledRFFT{T,Pl<:Plan} <: Factorization{T}
36+
struct ShuffledR2HC{T,Pl<:Plan} <: AbstractShuffledPlan{T}
37+
plan::Pl
38+
end
39+
40+
"""
41+
Gives a shuffled version of the FFT, with order
42+
1,sin(θ),cos(θ),sin(2θ)…
43+
"""
44+
struct ShuffledFFT{T,Pl<:Plan} <: AbstractShuffledPlan{T}
3045
plan::Pl
3146
end
3247

33-
size(F::ShuffledRFFT, k) = size(F.plan,k)
34-
size(F::ShuffledRFFT) = size(F.plan)
48+
size(F::AbstractShuffledPlan, k) = size(F.plan,k)
49+
size(F::AbstractShuffledPlan) = size(F.plan)
3550

36-
ShuffledRFFT{T}(p::Pl) where {T,Pl<:Plan} = ShuffledRFFT{T,Pl}(p)
37-
ShuffledRFFT{T}(n, d...) where T = ShuffledRFFT{T}(FFTW.plan_r2r(Array{T}(undef, n), FFTW.R2HC, d...))
51+
ShuffledR2HC{T}(p::Pl) where {T,Pl<:Plan} = ShuffledR2HC{T,Pl}(p)
52+
ShuffledR2HC{T}(n, d...) where T = ShuffledR2HC{T}(FFTW.plan_r2r(Array{T}(undef, n), FFTW.R2HC, d...))
3853

39-
function _shuffledrfft_postscale!(_, ret::AbstractVector{T}) where T
54+
ShuffledFFT{T}(p::Pl) where {T,Pl<:Plan} = ShuffledFFT{T,Pl}(p)
55+
ShuffledFFT{T}(n, d...) where T = ShuffledFFT{T}(FFTW.plan_fft(Array{T}(undef, n), d...))
56+
57+
58+
function _shuffledR2HC_postscale!(_, ret::AbstractVector{T}) where T
4059
n = length(ret)
4160
lmul!(convert(T,2)/n, ret)
4261
ret[1] /= 2
4362
iseven(n) && (ret[n÷2+1] /= 2)
4463
negateeven!(reverseeven!(interlace!(ret,1)))
4564
end
4665

47-
function _shuffledrfft_postscale!(d::Number, ret::AbstractMatrix{T}) where T
66+
function _shuffledR2HC_postscale!(d::Number, ret::AbstractMatrix{T}) where T
4867
if isone(d)
4968
n = size(ret,1)
5069
lmul!(convert(T,2)/n, ret)
@@ -65,20 +84,53 @@ function _shuffledrfft_postscale!(d::Number, ret::AbstractMatrix{T}) where T
6584
ret
6685
end
6786

87+
function _shuffledFFT_postscale!(_, ret::AbstractVector{T}) where T
88+
n = length(ret)
89+
cfs = lmul!(inv(convert(T,n)), ret)
90+
reverseeven!(interlace!(cfs,1))
91+
end
6892

69-
function mul!(ret::AbstractArray{T}, F::ShuffledRFFT{T}, b::AbstractArray) where T
93+
function _shuffledFFT_postscale!(d::Number, ret::AbstractMatrix{T}) where T
94+
if isone(d)
95+
n = size(ret,1)
96+
lmul!(inv(convert(T,n)), ret)
97+
for j in axes(ret,2)
98+
reverseeven!(interlace!(view(ret,:,j),1))
99+
end
100+
else
101+
n = size(ret,2)
102+
lmul!(inv(convert(T,n)), ret)
103+
for k in axes(ret,1)
104+
reverseeven!(interlace!(view(ret,k,:),1))
105+
end
106+
end
107+
ret
108+
end
109+
110+
function mul!(ret::AbstractArray{T}, F::ShuffledR2HC{T}, b::AbstractArray) where T
111+
mul!(ret, F.plan, convert(Array{T}, b))
112+
_shuffledR2HC_postscale!(F.plan.region, ret)
113+
end
114+
115+
function mul!(ret::AbstractArray{T}, F::ShuffledFFT{T}, b::AbstractArray) where T
70116
mul!(ret, F.plan, convert(Array{T}, b))
71-
_shuffledrfft_postscale!(F.plan.region, ret)
117+
_shuffledFFT_postscale!(F.plan.region, ret)
72118
end
73119

74-
*(F::ShuffledRFFT{T}, b::AbstractVecOrMat) where T = mul!(similar(b, T), F, b)
120+
*(F::AbstractShuffledPlan{T}, b::AbstractVecOrMat) where T = mul!(similar(b, T), F, b)
75121

76122
factorize(L::SubQuasiArray{T,2,<:Fourier,<:Tuple{<:Inclusion,<:OneTo}}) where T =
77-
TransformFactorization(grid(L), ShuffledRFFT{T}(size(L,2)))
123+
TransformFactorization(grid(L), ShuffledR2HC{T}(size(L,2)))
78124
factorize(L::SubQuasiArray{T,2,<:Fourier,<:Tuple{<:Inclusion,<:OneTo}}, d) where T =
79-
TransformFactorization(grid(L), ShuffledRFFT{T}((size(L,2),d),1))
125+
TransformFactorization(grid(L), ShuffledR2HC{T}((size(L,2),d),1))
126+
127+
factorize(L::SubQuasiArray{T,2,<:Laurent,<:Tuple{<:Inclusion,<:OneTo}}) where T =
128+
TransformFactorization(grid(L), ShuffledFFT{T}(size(L,2)))
129+
factorize(L::SubQuasiArray{T,2,<:Laurent,<:Tuple{<:Inclusion,<:OneTo}}, d) where T =
130+
TransformFactorization(grid(L), ShuffledFFT{T}((size(L,2),d),1))
131+
80132

81-
factorize(L::SubQuasiArray{T,2,<:Fourier,<:Tuple{<:Inclusion,<:BlockSlice}},d...) where T =
133+
factorize(L::SubQuasiArray{T,2,<:AbstractFourier,<:Tuple{<:Inclusion,<:BlockSlice}},d...) where T =
82134
ProjectionFactorization(factorize(parent(L)[:,OneTo(size(L,2))],d...),parentindices(L)[2])
83135

84136
import BlockBandedMatrices: _BlockSkylineMatrix
@@ -88,11 +140,21 @@ import BlockBandedMatrices: _BlockSkylineMatrix
88140
PseudoBlockArray(Diagonal(Vcat(2convert(TV,π),Fill(convert(TV,π),∞))), (axes(A,1),axes(B,2)))
89141
end
90142

143+
@simplify function *(A::QuasiAdjoint{<:Any,<:Laurent}, B::Laurent)
144+
TV = promote_type(eltype(A),eltype(B))
145+
Diagonal(Fill(2convert(TV,π),(axes(B,2),)))
146+
end
147+
91148
@simplify function *(D::Derivative, F::Fourier)
92149
TV = promote_type(eltype(D),eltype(F))
93150
Fourier{TV}()*_BlockArray(Diagonal(Vcat([reshape([0.0],1,1)], (1.0:∞) .* Fill([0 -one(TV); one(TV) 0], ∞))), (axes(F,2),axes(F,2)))
94151
end
95152

153+
@simplify function *(D::Derivative, F::Laurent)
154+
TV = promote_type(eltype(D),eltype(F))
155+
Laurent{TV}() * Diagonal(PseudoBlockVector((((1:∞) 2) .* (1 .- 2 .* iseven.(1:∞))) * convert(TV,im), (axes(F,2),)))
156+
end
157+
96158

97159
function broadcasted(::LazyQuasiArrayStyle{2}, ::typeof(*), c::BroadcastQuasiVector{<:Any,typeof(cos),<:Tuple{<:Inclusion{<:Any,RealNumbers}}}, F::Fourier)
98160
axes(c,1) == axes(F,1) || throw(DimensionMismatch())

0 commit comments

Comments
 (0)