Skip to content

Commit 1fd0adf

Browse files
authored
Improve singularities behaviour (#209)
* Improve singularities behaviour * work on singularities * v0.16 * Update test_monic.jl * fix tests * increase coverage * increase cov * Update ClassicalOrthogonalPolynomials.jl * remove literal power special case * increase cov
1 parent 8462dd4 commit 1fd0adf

14 files changed

+128
-94
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
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.15.1"
4+
version = "0.15.2"
55

66

77
[deps]

src/ClassicalOrthogonalPolynomials.jl

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import InfiniteLinearAlgebra: chop!, chop, pad, choplength, compatible_resize!,
3737
import ContinuumArrays: Basis, Weight, basis_axes, @simplify, AbstractAffineQuasiVector, ProjectionFactorization,
3838
grid, plotgrid, plotgrid_layout, plotvalues_layout, grid_layout, transform_ldiv, TransformFactorization, QInfAxes, broadcastbasis, ExpansionLayout, basismap,
3939
AffineQuasiVector, AffineMap, AbstractWeightLayout, AbstractWeightedBasisLayout, WeightedBasisLayout, WeightedBasisLayouts, demap, AbstractBasisLayout, BasisLayout,
40-
checkpoints, weight, unweighted, MappedBasisLayouts, sum_layout, invmap, plan_ldiv, layout_broadcasted, MappedBasisLayout, SubBasisLayout, broadcastbasis_layout,
40+
checkpoints, weight, unweighted, MappedBasisLayouts, sum_layout, invmap, plan_ldiv, layout_broadcasted, MappedBasisLayout, MappedWeightLayout, SubBasisLayout, broadcastbasis_layout,
4141
plan_grid_transform, plan_transform, MAX_PLOT_POINTS, MulPlan, grammatrix, AdjointBasisLayout, grammatrix_layout, plan_transform_layout, _cumsum
4242
import FastTransforms: Λ, ChebyshevGrid, chebyshevpoints, Plan, ScaledPlan, th_cheb2leg, pochhammer
4343
import RecurrenceRelationships: forwardrecurrence, forwardrecurrence!, clenshaw, clenshaw!,
@@ -197,27 +197,45 @@ recurrencecoefficients(Q) = recurrencecoefficients_layout(MemoryLayout(Q), Q)
197197

198198

199199

200+
singularities_layout(lay::BroadcastLayout, a) = singularitiesbroadcast(call(lay, a), map(singularities, arguments(lay, a))...)
201+
singularities_layout(::WeightedBasisLayouts, a) = singularities_layout(BroadcastLayout{typeof(*)}(), a)
202+
singularities_layout(::MappedWeightLayout, a) = view(singularities(demap(a)), basismap(a))
203+
singularities_layout(::WeightedOPLayout, a) = singularities(weight(a))
204+
singularities_layout(::ExpansionLayout, f) = singularities(basis(f))
205+
singularities_layout(lay, a) = NoSingularities() # assume no singularities
206+
200207
"""
201208
singularities(f)
202209
203210
gives the singularity structure of an expansion, e.g.,
204211
`JacobiWeight`.
205212
"""
206-
singularities(::AbstractWeightLayout, w) = w
207-
singularities(lay::BroadcastLayout, a) = singularitiesbroadcast(call(a), map(singularities, arguments(lay, a))...)
208-
singularities(::WeightedBasisLayouts, a) = singularities(BroadcastLayout{typeof(*)}(), a)
209-
singularities(::WeightedOPLayout, a) = singularities(weight(a))
210-
singularities(w) = singularities(MemoryLayout(w), w)
211-
singularities(::ExpansionLayout, f) = singularities(basis(f))
213+
singularities(w) = singularities_layout(MemoryLayout(w), w)
214+
212215

213-
singularitiesview(w, ::Inclusion) = w # for now just assume it doesn't change
214-
singularitiesview(w, ind) = view(w, ind)
215-
singularities(S::SubQuasiArray) = singularitiesview(singularities(parent(S)), parentindices(S)[1])
216216

217217
struct NoSingularities end
218218

219-
basis_singularities(ax, ::NoSingularities) = basis(ax)
220-
basis_singularities(ax, sing) = basis_singularities(sing)
219+
## default is to just assume no singularities
220+
singularitiesbroadcast(_...) = NoSingularities()
221+
222+
for op in (:+, :*)
223+
@eval singularitiesbroadcast(::typeof($op), A, B, C, D...) = singularitiesbroadcast(*, singularitiesbroadcast(*, A, B), C, D...)
224+
@eval singularitiesbroadcast(::typeof($op), ::NoSingularities, ::NoSingularities) = NoSingularities()
225+
end
226+
227+
singularitiesbroadcast(::typeof(*), ::NoSingularities, b) = b
228+
singularitiesbroadcast(::typeof(*), a, ::NoSingularities) = a
229+
230+
231+
# for singularitiesbroadcast(literal_pow), ^, ...)
232+
# singularitiesbroadcast(F::Function, G::Function, V::SubQuasiArray, K) = singularitiesbroadcast(F, G, parent(V), K)[parentindices(V)...]
233+
# singularitiesbroadcast(F, V::SubQuasiArray...) = singularitiesbroadcast(F, map(parent,V)...)[parentindices(V...)...]
234+
# singularitiesbroadcast(F, V::NoSingularities...) = NoSingularities() # default is to assume smooth
235+
236+
237+
238+
221239
basis_axes(ax::Inclusion{<:Any,<:AbstractInterval}, v) = convert(AbstractQuasiMatrix{eltype(v)}, basis_singularities(ax, singularities(v)))
222240

223241

src/choleskyQR.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ orthogonalityweight(Q::ConvertedOrthogonalPolynomial) = Q.weight
2626
# transform to P * inv(U) if needed for differentiation, etc.
2727
arguments(::ApplyLayout{typeof(*)}, Q::ConvertedOrthogonalPolynomial) = Q.P, ApplyArray(inv, Q.U)
2828

29-
OrthogonalPolynomial(w::AbstractQuasiVector) = OrthogonalPolynomial(w, orthogonalpolynomial(singularities(w)))
29+
OrthogonalPolynomial(w::AbstractQuasiVector) = OrthogonalPolynomial(w, orthogonalpolynomial_axis(axes(w,1), singularities(w)))
3030
function OrthogonalPolynomial(w::AbstractQuasiVector, P::AbstractQuasiMatrix)
3131
Q = normalized(P)
3232
X,U = cholesky_jacobimatrix(w, Q)
@@ -39,6 +39,8 @@ orthogonalpolynomial(w::SubQuasiArray) = orthogonalpolynomial(parent(w))[parenti
3939
OrthogonalPolynomial(w::Function, P::AbstractQuasiMatrix) = OrthogonalPolynomial(w.(axes(P,1)), P)
4040
orthogonalpolynomial(w::Function, P::AbstractQuasiMatrix) = orthogonalpolynomial(w.(axes(P,1)), P)
4141

42+
orthogonalpolynomial_axis(ax, ::NoSingularities) = legendre(ax)
43+
orthogonalpolynomial_axis(ax, w) = orthogonalpolynomial(w)
4244
resizedata!(P::ConvertedOrthogonalPolynomial, ::Colon, n::Int) = resizedata!(P.X.dv, n)
4345

4446

src/classical/chebyshev.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@ broadcasted(::LazyQuasiArrayStyle{2}, ::typeof(*), ::ChebyshevWeight{kind,T}, ::
4949

5050
chebyshevt() = ChebyshevT()
5151
chebyshevt(d::AbstractInterval{T}) where T = ChebyshevT{float(T)}()[affine(d, ChebyshevInterval{T}()), :]
52+
chebyshevt(d::ChebyshevInterval{T}) where T = ChebyshevT{float(T)}()
5253
chebyshevt(d::Inclusion) = chebyshevt(d.domain)
5354
chebyshevt(S::AbstractQuasiMatrix) = chebyshevt(axes(S,1))
5455
chebyshevu() = ChebyshevU()
5556
chebyshevu(d::AbstractInterval{T}) where T = ChebyshevU{float(T)}()[affine(d, ChebyshevInterval{T}()), :]
57+
chebyshevu(d::ChebyshevInterval{T}) where T = ChebyshevU{float(T)}()
5658
chebyshevu(d::Inclusion) = chebyshevu(d.domain)
5759
chebyshevu(S::AbstractQuasiMatrix) = chebyshevu(axes(S,1))
5860

src/classical/jacobi.jl

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -92,23 +92,7 @@ hasboundedendpoints(w::AbstractJacobiWeight) = w.a ≥ 0 && w.b ≥ 0
9292
singularities(a::AbstractAffineQuasiVector) = singularities(a.x)
9393

9494

95-
## default is to just assume no singularities
96-
singularitiesbroadcast(_...) = NoSingularities()
97-
98-
for op in (:+, :*)
99-
@eval singularitiesbroadcast(::typeof($op), A, B, C, D...) = singularitiesbroadcast(*, singularitiesbroadcast(*, A, B), C, D...)
100-
end
101-
102-
singularitiesbroadcast(::typeof(*), V::Union{NoSingularities,SubQuasiArray}...) = singularitiesbroadcast(*, map(_parent,V)...)[_parentindices(V...)...]
103-
104-
105-
_parent(::NoSingularities) = NoSingularities()
106-
_parent(a) = parent(a)
107-
_parentindices(a::NoSingularities, b...) = _parentindices(b...)
108-
_parentindices(a, b...) = parentindices(a)
109-
# for singularitiesbroadcast(literal_pow), ^, ...)
110-
singularitiesbroadcast(F::Function, G::Function, V::SubQuasiArray, K) = singularitiesbroadcast(F, G, parent(V), K)[parentindices(V)...]
111-
singularitiesbroadcast(F, V::Union{NoSingularities,SubQuasiArray}...) = singularitiesbroadcast(F, map(_parent,V)...)[_parentindices(V...)...]
95+
singularities(w::AbstractJacobiWeight) = w
11296

11397

11498
abstract type AbstractJacobi{T} <: OrthogonalPolynomial{T} end
@@ -208,10 +192,11 @@ julia> J[0,:]
208192
"""
209193
jacobi(a,b) = Jacobi(a,b)
210194
jacobi(a,b, d::AbstractInterval{T}) where T = Jacobi{float(promote_type(eltype(a),eltype(b),T))}(a,b)[affine(d,ChebyshevInterval{T}()), :]
195+
jacobi(a,b, d::ChebyshevInterval{T}) where T = Jacobi{float(promote_type(eltype(a),eltype(b),T))}(a,b)
211196

212197
Jacobi(P::Legendre{T}) where T = Jacobi(zero(T), zero(T))
213198

214-
basis_singularities(w::JacobiWeight) = Weighted(Jacobi(w.a, w.b))
199+
215200

216201

217202
"""

src/classical/legendre.jl

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,9 @@ end
7373
singularitiesbroadcast(::typeof(^), L::LegendreWeight, ::NoSingularities) = L
7474
singularitiesbroadcast(::typeof(/), ::NoSingularities, L::LegendreWeight) = L # can't find roots
7575

76-
singularities(::AbstractJacobi{T}) where T = LegendreWeight{T}()
77-
singularities(::Inclusion{T,<:ChebyshevInterval}) where T = LegendreWeight{T}()
78-
singularities(d::Inclusion{T,<:AbstractInterval}) where T = LegendreWeight{T}()[affine(d,ChebyshevInterval{T}())]
79-
singularities(::AbstractFillLayout, P) = LegendreWeight{eltype(P)}()
76+
basis_singularities(ax::Inclusion, ::NoSingularities) = legendre(ax)
77+
basis_singularities(ax, sing) = basis_singularities(sing) # fallback for back compatibility
8078

81-
basis_singularities(::LegendreWeight{T}) where T = Legendre{T}()
82-
basis_singularities(v::SubQuasiArray) = view(basis_singularities(parent(v)), parentindices(v)[1], :)
8379

8480
"""
8581
Legendre{T=Float64}(a,b)

src/classical/ultraspherical.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ orthogonalityweight(C::Ultraspherical) = UltrasphericalWeight(C.λ)
5656

5757
ultrasphericalc(n::Integer, λ, z) = Base.unsafe_getindex(Ultraspherical{polynomialtype(typeof(λ),typeof(z))}(λ), z, n+1)
5858
ultraspherical(λ, d::AbstractInterval{T}) where T = Ultraspherical{float(promote_type(eltype(λ),T))}(λ)[affine(d,ChebyshevInterval{T}()), :]
59+
ultraspherical(λ, d::ChebyshevInterval{T}) where T = Ultraspherical{float(promote_type(eltype(λ),T))}(λ)
5960

6061
==(a::Ultraspherical, b::Ultraspherical) = a.λ == b.λ
6162
==(::Ultraspherical, ::ChebyshevT) = false

src/lanczos.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ function LanczosPolynomial(w_in::AbstractQuasiVector, P::AbstractQuasiMatrix)
165165
LanczosPolynomial(w, Q, LanczosData(w, Q))
166166
end
167167

168-
LanczosPolynomial(w::AbstractQuasiVector) = LanczosPolynomial(w, normalized(orthogonalpolynomial(singularities(w))))
168+
LanczosPolynomial(w::AbstractQuasiVector) = LanczosPolynomial(w, normalized(orthogonalpolynomial_axis(axes(w,1), singularities(w))))
169169

170170
==(A::LanczosPolynomial, B::LanczosPolynomial) = A.w == B.w
171171
==(::LanczosPolynomial, ::OrthogonalPolynomial) = false # TODO: fix

test/runtests.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import ClassicalOrthogonalPolynomials: jacobimatrix, ∞, ChebyshevInterval, Leg
88
import LazyArrays: ApplyStyle, colsupport, MemoryLayout, arguments
99
import SemiseparableMatrices: VcatAlmostBandedLayout
1010
import QuasiArrays: MulQuasiMatrix
11-
import ClassicalOrthogonalPolynomials: oneto
11+
import ClassicalOrthogonalPolynomials: oneto, NoSingularities
1212
import InfiniteLinearAlgebra: KronTrav, Block
1313
import FastTransforms: clenshaw!
1414

@@ -18,15 +18,15 @@ Random.seed!(0)
1818
x = Inclusion(ChebyshevInterval())
1919
@test singularities(x) == singularities(exp.(x)) == singularities(x.^2) ==
2020
singularities(x .+ 1) == singularities(1 .+ x) == singularities(x .+ x) ==
21-
LegendreWeight()
21+
NoSingularities()
2222
@test singularities(exp.(x) .* JacobiWeight(0.1,0.2)) ==
2323
singularities(JacobiWeight(0.1,0.2) .* exp.(x)) ==
2424
JacobiWeight(0.1,0.2)
2525

2626
x = Inclusion(0..1)
2727
@test singularities(x) == singularities(exp.(x)) == singularities(x.^2) ==
2828
singularities(x .+ 1) == singularities(1 .+ x) == singularities(x .+ x) ==
29-
legendreweight(0..1)
29+
NoSingularities()
3030
end
3131

3232
include("test_chebyshev.jl")

test/test_chebyshev.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,11 @@ import BandedMatrices: isbanded
582582
@testset "diff of truncation" begin
583583
@test MemoryLayout(diff(ChebyshevT()[:,1:5]) * randn(5)) isa ExpansionLayout
584584
end
585+
586+
@testset "ChebyshevInterval constructior" begin
587+
@test chebyshevt(ChebyshevInterval()) ChebyshevT()
588+
@test chebyshevu(ChebyshevInterval()) ChebyshevU()
589+
end
585590
end
586591

587592
struct QuadraticMap{T} <: Map{T} end
@@ -612,7 +617,7 @@ ContinuumArrays.invmap(::InvQuadraticMap{T}) where T = QuadraticMap{T}()
612617
g = chebyshevt(0..1) * [1:3; zeros(∞)]
613618
@test_broken (f + g)[0.1] f[0.1] + g[0.1] # ContinuumArrays needs to check maps are equal
614619

615-
@test ClassicalOrthogonalPolynomials.singularities(T) === LegendreWeight()[QuadraticMap()]
620+
@test ClassicalOrthogonalPolynomials.singularities(T) isa NoSingularities
616621
end
617622

618623
@testset "block structure" begin

0 commit comments

Comments
 (0)