Skip to content

Commit e12006c

Browse files
committed
radau/lobatto
1 parent cc668ce commit e12006c

File tree

5 files changed

+165
-3
lines changed

5 files changed

+165
-3
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.13.4"
4+
version = "0.13.5"
55

66
[deps]
77
ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"

src/ClassicalOrthogonalPolynomials.jl

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,76 @@ end
318318

319319
golubwelsch(V::SubQuasiArray) = golubwelsch(parent(V), maximum(parentindices(V)[2]))
320320

321+
function gaussradau(P, n::Integer, endpt)
322+
## See Thm 3.2 in Gautschi (2004)'s book
323+
# n is the number of interior points
324+
J = symtridiagonalize(jacobimatrix(P, n + 1))
325+
α = diagonaldata(J)
326+
β = supdiagonaldata(J)
327+
T = eltype(P)
328+
endpt = T(endpt)
329+
p0 = zero(T)
330+
p1 = one(T)
331+
for i in 1:n
332+
# Evaluate the monic polynomials πₙ₋₁(endpt), πₙ(endpt)
333+
_p1 = p0
334+
p0 = p1
335+
p1 = (endpt - α[i]) * p0
336+
i > 1 && (p1 -= β[i - 1]^2 * _p1)
337+
end
338+
a = endpt - β[end]^2 * p0 / p1
339+
α′ = vcat(@view(α[begin:end-1]), a)
340+
J′ = SymTridiagonal(α′, β)
341+
x, w = golubwelsch(J′)
342+
w .*= sum(orthogonalityweight(P))
343+
if endpt axes(P, 1)[begin]
344+
x[1] = endpt # avoid rounding errors
345+
else
346+
x[end] = endpt
347+
end
348+
return x, w
349+
end
350+
351+
function gausslobatto(P, n::Integer)
352+
## See Thm 3.6 of Gautschi (2004)'s book
353+
# n is the number of interior points
354+
a, b = axes(P, 1)[begin], axes(P, 1)[end]
355+
J = symtridiagonalize(jacobimatrix(P, n + 2))
356+
α = diagonaldata(J)
357+
β = supdiagonaldata(J)
358+
T = eltype(P)
359+
a, b = T(a), T(b)
360+
p0a = zero(T)
361+
p1a = one(T)
362+
p0b = zero(T)
363+
p1b = one(T)
364+
for i in 1:(n+1)
365+
# Evaluate the monic polynomials πₙ₋₁(a), πₙ₋₁(b), πₙ(a), πₙ(b)
366+
_p1a = p0a
367+
_p1b = p0b
368+
p0a = p1a
369+
p0b = p1b
370+
p1a = (a - α[i]) * p0a
371+
p1b = (b - α[i]) * p0b
372+
if i > 1
373+
p1a -= β[i - 1]^2 * _p1a
374+
p1b -= β[i - 1]^2 * _p1b
375+
end
376+
end
377+
# Solve Eq. 3.1.2.8
378+
Δ = p1a * p0b - p1b * p0a # This could underflow/overflow for large n
379+
aext = (a * p1a * p0b - b * p1b * p0a) / Δ
380+
bext = (b - a) * p1a * p1b / Δ
381+
α′ = vcat(@view(α[begin:end-1]), aext)
382+
β′ = vcat(@view(β[begin:end-1]), sqrt(bext)) # LazyBandedMatrices doesn't like when we use Vcat(array, scalar) apparently
383+
J′ = LazyBandedMatrices.SymTridiagonal(α′, β′)
384+
x, w = golubwelsch(J′)
385+
w .*= sum(orthogonalityweight(P))
386+
x[1] = a
387+
x[end] = b # avoid rounding errors. Doesn't affect the actual result but avoids e.g. domain errors
388+
return x, w
389+
end
390+
321391
# Default is Golub–Welsch expansion
322392
# note this computes the grid an extra time.
323393
function plan_transform_layout(::AbstractOPLayout, P, szs::NTuple{N,Int}, dims=ntuple(identity,Val(N))) where N

test/runtests.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ include("test_lanczos.jl")
4343
include("test_interlace.jl")
4444
include("test_choleskyQR.jl")
4545
include("test_roots.jl")
46+
include("test_lobattoradau.jl")
4647

4748
@testset "Auto-diff" begin
4849
U = Ultraspherical(1)
@@ -83,4 +84,4 @@ end
8384
@testset "Issue #179" begin
8485
@test startswith(sprint(show, MIME"text/plain"(), Chebyshev()[0.3, :]; context=(:compact=>true, :limit=>true)), "ℵ₀-element view(::ChebyshevT{Float64}, 0.3, :)")
8586
@test startswith(sprint(show, MIME"text/plain"(), Jacobi(0.2, 0.5)[-0.7, :]; context=(:compact=>true, :limit=>true)), "ℵ₀-element view(::Jacobi{Float64}, -0.7, :)")
86-
end
87+
end

test/test_jacobi.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ import ClassicalOrthogonalPolynomials: recurrencecoefficients, basis, MulQuasiMa
8181

8282
M = P[x,1:3]'Diagonal(w)*P[x,1:3]
8383
@test M Diagonal(M)
84-
x,w = gaussradau(3,a,b)
84+
x,w = FastGaussQuadrature.gaussradau(3,a,b)
8585
M = P[x,1:3]'Diagonal(w)*P[x,1:3]
8686
@test M Diagonal(M)
8787

test/test_lobattoradau.jl

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using ClassicalOrthogonalPolynomials, FastGaussQuadrature
2+
const COP = ClassicalOrthogonalPolynomials
3+
const FGQ = FastGaussQuadrature
4+
using Test
5+
using ClassicalOrthogonalPolynomials: symtridiagonalize
6+
using LinearAlgebra
7+
8+
@testset "gaussradau" begin
9+
@testset "Compare with FastGaussQuadrature" begin
10+
x1, w1 = COP.gaussradau(Legendre(), 5, -1.0)
11+
x2, w2 = FGQ.gaussradau(6)
12+
@test x1 x2 && w1 w2
13+
@test x1[1] == -1
14+
15+
x1, w1 = COP.gaussradau(Jacobi(1.0, 3.5), 25, -1.0)
16+
x2, w2 = FGQ.gaussradau(26, 1.0, 3.5)
17+
@test x1 x2 && w1 w2
18+
@test x1[1] == -1
19+
20+
I0, I1 = COP.ChebyshevInterval(), COP.UnitInterval()
21+
P = Jacobi(2.0, 0.0)[COP.affine(I1, I0), :]
22+
x1, w1 = COP.gaussradau(P, 18, 0.0)
23+
x2, w2 = FGQ.gaussradau(19, 2.0, 0.0)
24+
@test 2x1 .- 1 x2 && 2w1 w2
25+
26+
x1, w1 = COP.gaussradau(Jacobi(1 / 2, 1 / 2), 4, 1.0)
27+
x2, w2 = FGQ.gaussradau(5, 1 / 2, 1 / 2)
28+
@test sort(-x1) x2
29+
@test_broken w1 w2 # What happens to the weights when inverting the interval?
30+
end
31+
32+
@testset "Example 3.5 in Gautschi (2004)'s book" begin
33+
P = Laguerre(3.0)
34+
n = 5
35+
J = symtridiagonalize(jacobimatrix(P))[1:(n-1), 1:(n-1)]
36+
_J = zeros(n, n)
37+
_J[1:n-1, 1:n-1] .= J
38+
_J[n-1, n] = sqrt((n - 1) * (n - 1 + P.α))
39+
_J[n, n-1] = _J[n-1, n]
40+
_J[n, n] = n - 1
41+
x, V = eigen(_J)
42+
w = 6V[1, :] .^ 2
43+
xx, ww = COP.gaussradau(P, n - 1, 0.0)
44+
@test xx x && ww w
45+
end
46+
47+
@testset "Some numerical integration" begin
48+
f = x -> 2x + 7x^2 + 10x^3 + exp(-x)
49+
x, w = COP.gaussradau(Chebyshev(), 10, -1.0)
50+
@test dot(f.(x), w) 14.97303754807069897 # integral of (2x + 7x^2 + 10x^3 + exp(-x))/sqrt(1-x62)
51+
@test x[1] == -1
52+
53+
f = x -> -1.0 + 5x^6
54+
x, w = COP.gaussradau(Jacobi(-1/2, -1/2), 2, 1.0)
55+
@test dot(f.(x), w) 9π/16
56+
@test x[end] == 1
57+
@test length(x) == 3
58+
end
59+
end
60+
61+
@testset "gausslobatto" begin
62+
@testset "Compare with FastGaussQuadrature" begin
63+
x1, w1 = COP.gausslobatto(Legendre(), 5)
64+
x2, w2 = FGQ.gausslobatto(7)
65+
@test x1 x2 && w1 w2
66+
@test x1[1] == -1
67+
@test x1[end] == 1
68+
69+
I0, I1 = COP.ChebyshevInterval(), COP.UnitInterval()
70+
P = Legendre()[COP.affine(I1, I0), :]
71+
x1, w1 = COP.gausslobatto(P, 18)
72+
x2, w2 = FGQ.gausslobatto(20)
73+
@test 2x1 .- 1 x2 && 2w1 w2
74+
end
75+
76+
@testset "Some numerical integration" begin
77+
f = x -> 2x + 7x^2 + 10x^3 + exp(-x)
78+
x, w = COP.gausslobatto(Chebyshev(), 10)
79+
@test dot(f.(x), w) 14.97303754807069897
80+
@test x[1] == -1
81+
@test x[end] == 1
82+
@test length(x) == 12
83+
84+
f = x -> -1.0 + 5x^6
85+
x, w = COP.gausslobatto(Jacobi(-1/2, -1/2), 4)
86+
@test dot(f.(x), w) 9π/16
87+
@test x[1]==-1
88+
@test x[end] == 1
89+
@test length(x) == 6
90+
end
91+
end

0 commit comments

Comments
 (0)