Skip to content

Commit 0af4a9e

Browse files
add two new constructors for the GramMatrix based on modified OP moments
1 parent bc0d4d4 commit 0af4a9e

File tree

3 files changed

+81
-19
lines changed

3 files changed

+81
-19
lines changed

docs/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ ApproxFun = "28f2ccd6-bb30-5033-b560-165f7b14dc2f"
33
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
44
FastTransforms = "057dd010-8810-581a-b7be-e3fc3b93f78c"
55
LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
6+
LazyArrays = "5078a376-72f3-5289-bfd5-ec5146d43c02"
67
Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
78
PlotlyJS = "f0f68f2c-4968-5e81-91da-67840de0976a"
89
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"

examples/semiclassical.jl

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
# \langle f, g \rangle = \int_{-1}^1 f(x) g(x) w(x){\rm d} x,
55
# ```
66
# where $w(x) = w^{(\alpha,\beta,\gamma,\delta,\epsilon)}(x) = (1-x)^\alpha(1+x)^\beta(2+x)^\gamma(3+x)^\delta(5-x)^\epsilon$ is a modification of the Jacobi weight.
7-
# We shall use results from [this paper](https://arxiv.org/abs/2302.08448) to consider these semi-classical orthogonal polynomials as modifications of the Jacobi polynomials $P_n^{(\alpha,\beta)}(x)$.
7+
# We shall use results from [this paper](https://arxiv.org/abs/2302.08448) to consider these semi-classical orthogonal polynomials as modifications of the orthonormalized Jacobi polynomials $\tilde{P}_n^{(\alpha,\beta)}(x)$.
88

9-
using ApproxFun, FastTransforms, LinearAlgebra, Plots, LaTeXStrings
9+
using ApproxFun, FastTransforms, LazyArrays, LinearAlgebra, Plots, LaTeXStrings
1010
const GENFIGS = joinpath(pkgdir(FastTransforms), "docs/src/generated")
1111
!isdir(GENFIGS) && mkdir(GENFIGS)
1212
plotlyjs()
@@ -30,7 +30,7 @@ P1 = plan_jac2cheb(Float64, n+1, α, β; normjac = true)
3030
q = k -> lmul!(P1, lmul!(P, [zeros(k); 1.0; zeros(n-k)]))
3131
qvals = k -> ichebyshevtransform(q(k))
3232

33-
# With the symmetric Jacobi matrix for $P_n^{(\alpha, \beta)}(x)$ and the modified plan, we may compute the modified Jacobi matrix and the corresponding roots (as eigenvalues):
33+
# With the symmetric Jacobi matrix for $\tilde{P}_n^{(\alpha, \beta)}(x)$ and the modified plan, we may compute the modified Jacobi matrix and the corresponding roots (as eigenvalues):
3434
x = Fun(x->x, NormalizedJacobi(β, α))
3535
XP = SymTridiagonal(Symmetric(Multiplication(x, space(x))[1:n+1, 1:n+1]))
3636
XQ = FastTransforms.modified_jacobi_matrix(P, XP)
@@ -61,18 +61,21 @@ function threshold!(A::AbstractArray, ϵ)
6161
A
6262
end
6363
P′ = plan_modifiedjac2jac(Float64, n+1, α+1, β+1, v.coefficients)
64-
DP = UpperTriangular(diagm(1=>[sqrt(n*(n+α+β+1)) for n in 1:n])) # The classical differentiation matrix representing 𝒟 P^{(α,β)}(y) = P^{(α+1,β+1)}(y) D_P.
65-
DQ = UpperTriangular(threshold!(P′\(DP*(P*I)), 100eps())) # The semi-classical differentiation matrix representing 𝒟 Q(y) = Q̂(y) D_Q.
66-
UpperTriangular(DQ[1:10,1:10])
64+
DP = UpperTriangular(diagm(1=>[sqrt(n*(n+α+β+1)) for n in 1:n])) # The classical differentiation matrix representing 𝒟 P^{(α,β)}(x) = P^{(α+1,β+1)}(x) D_P.
65+
DQ = UpperTriangular(threshold!(P′\(DP*(P*I)), 100eps())) # The semi-classical differentiation matrix representing 𝒟 Q(x) = Q̂(x) D_Q.
66+
UpperTriangular(DQ[1:10, 1:10])
6767

68-
# A faster method now exists via the `GramMatrix` architecture and its associated displacement equation. We compute `U`:
69-
U = Symmetric(Multiplication(u, space(u))[1:n+1, 1:n+1])
70-
71-
# Then we form a `GramMatrix` together with the Jacobi matrix:
72-
G = GramMatrix(U, XP)
68+
# A faster method now exists via the `GramMatrix` architecture and its associated displacement equation. Given the modified orthogonal polynomial moments implied by the normalized Jacobi series for $u(x)$, we pad this vector to the necessary size and construct the `GramMatrix` with these moments, the multiplication operator, and the constant $\tilde{P}_0^{(\alpha,\beta)}(x)$:
69+
μ = PaddedVector(u.coefficients, 2n+1)
70+
x = Fun(x->x, NormalizedJacobi(β, α))
71+
XP2 = SymTridiagonal(Symmetric(Multiplication(x, space(x))[1:2n+1, 1:2n+1]))
72+
p0 = Fun(NormalizedJacobi(β, α), [1])(0)
73+
G = GramMatrix(μ, XP2, p0)
74+
G[1:10, 1:10]
7375

7476
# And compute its cholesky factorization. The upper-triangular Cholesky factor represents the connection between original Jacobi and semi-classical Jacobi as ${\bf P}^{(\alpha,\beta)}(x) = {\bf Q}(x) R$.
7577
R = cholesky(G).U
78+
R[1:10, 1:10]
7679

7780
# Every else works almost as before, including evaluation on a Chebyshev grid:
7881
q = k -> lmul!(P1, ldiv!(R, [zeros(k); 1.0; zeros(n-k)]))
@@ -99,11 +102,12 @@ savefig(joinpath(GENFIGS, "semiclassical1.html"))
99102
###```
100103

101104
# And banded differentiation:
102-
V = Symmetric(Multiplication(v, space(v))[1:n+1, 1:n+1])
103-
x = Fun(x->x, NormalizedJacobi+1, α+1))
104-
XP′ = SymTridiagonal(Symmetric(Multiplication(x, space(x))[1:n+1, 1:n+1]))
105-
G′ = GramMatrix(V, XP′)
105+
μ′ = PaddedVector(v.coefficients, 2n+1)
106+
x′ = Fun(x->x, NormalizedJacobi+1, α+1))
107+
XP′ = SymTridiagonal(Symmetric(Multiplication(x′, space(x′))[1:2n+1, 1:2n+1]))
108+
p0′ = Fun(NormalizedJacobi+1, α+1), [1])(0)
109+
G′ = GramMatrix(μ′, XP′, p0′)
106110
R′ = cholesky(G′).U
107-
DP = UpperTriangular(diagm(1=>[sqrt(n*(n+α+β+1)) for n in 1:n])) # The classical differentiation matrix representing 𝒟 P^{(α,β)}(y) = P^{(α+1,β+1)}(y) D_P.
108-
DQ = UpperTriangular(threshold!(R′*(DP*(R\I)), 100eps())) # The semi-classical differentiation matrix representing 𝒟 Q(y) = Q̂(y) D_Q.
109-
UpperTriangular(DQ[1:10,1:10])
111+
DP = UpperTriangular(diagm(1=>[sqrt(n*(n+α+β+1)) for n in 1:n])) # The classical differentiation matrix representing 𝒟 P^{(α,β)}(x) = P^{(α+1,β+1)}(x) D_P.
112+
DQ = UpperTriangular(threshold!(R′*(DP*(R\I)), 100eps())) # The semi-classical differentiation matrix representing 𝒟 Q(x) = Q̂(x) D_Q.
113+
UpperTriangular(DQ[1:10, 1:10])

src/GramMatrix.jl

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,64 @@ GramMatrix(W::WT, X::XT) where {T, WT <: AbstractMatrix{T}, XT <: AbstractMatrix
5050
@inline bandwidths(G::GramMatrix) = bandwidths(G.W)
5151
@inline MemoryLayout(G::GramMatrix) = MemoryLayout(G.W)
5252

53+
"""
54+
GramMatrix(μ::AbstractVector, X::AbstractMatrix)
55+
56+
Construct a GramMatrix from modified orthogonal polynomial moments and the multiplication operator.
57+
In the standard (classical) normalization, ``p_0(x) = 1``, so that the moments
58+
``\\mu_n = \\langle p_{n-1}, 1\\rangle`` are in fact the first column of the Gram matrix.
59+
The recurrence is built from ``X^\\top W = WX``.
60+
"""
61+
GramMatrix::AbstractVector{T}, X::XT) where {T, XT <: AbstractMatrix{T}} = GramMatrix(μ, X, one(T))
62+
function GramMatrix::AbstractVector{T}, X::XT, p0::T) where {T, XT <: AbstractMatrix{T}}
63+
N = length(μ)
64+
n = (N+1)÷2
65+
@assert N == size(X, 1) == size(X, 2)
66+
@assert bandwidths(X) == (1, 1)
67+
W = Matrix{T}(undef, N, N)
68+
if n > 0
69+
@inbounds for m in 1:N
70+
W[m, 1] = p0*μ[m]
71+
end
72+
end
73+
if n > 1
74+
@inbounds for m in 2:N-1
75+
W[m, 2] = (X[m-1, m]*W[m-1, 1] + (X[m, m]-X[1, 1])*W[m, 1] + X[m+1, m]*W[m+1, 1])/X[2, 1]
76+
end
77+
end
78+
@inbounds @simd for n in 3:n
79+
for m in n:N-n+1
80+
W[m, n] = (X[m-1, m]*W[m-1, n-1] + (X[m, m]-X[n-1, n-1])*W[m, n-1] + X[m+1, m]*W[m+1, n-1] - X[n-2, n-1]*W[m, n-2])/X[n, n-1]
81+
end
82+
end
83+
return GramMatrix(Symmetric(W[1:n, 1:n], :L), eval(XT.name.name)(view(X, 1:n, 1:n)))
84+
end
85+
86+
function GramMatrix::PaddedVector{T}, X::XT, p0::T) where {T, XT <: AbstractMatrix{T}}
87+
N = length(μ)
88+
b = length.args[2])-1
89+
n = (N+1)÷2
90+
@assert N == size(X, 1) == size(X, 2)
91+
@assert bandwidths(X) == (1, 1)
92+
W = BandedMatrix{T}(undef, (N, N), (b, 0))
93+
if n > 0
94+
@inbounds for m in 1:min(N, b+1)
95+
W[m, 1] = p0*μ[m]
96+
end
97+
end
98+
if n > 1
99+
@inbounds for m in 2:min(N-1, b+2)
100+
W[m, 2] = (X[m-1, m]*W[m-1, 1] + (X[m, m]-X[1, 1])*W[m, 1] + X[m+1, m]*W[m+1, 1])/X[2, 1]
101+
end
102+
end
103+
@inbounds @simd for n in 3:n
104+
for m in n:min(N-n+1, b+n)
105+
W[m, n] = (X[m-1, m]*W[m-1, n-1] + (X[m, m]-X[n-1, n-1])*W[m, n-1] + X[m+1, m]*W[m+1, n-1] - X[n-2, n-1]*W[m, n-2])/X[n, n-1]
106+
end
107+
end
108+
return GramMatrix(Symmetric(W[1:n, 1:n], :L), eval(XT.name.name)(view(X, 1:n, 1:n)))
109+
end
110+
53111
#
54112
# X'W-W*X = G*J*G'
55113
# This returns G, where J = [0 1; -1 0], respecting the skew-symmetry of the right-hand side.
@@ -201,7 +259,6 @@ function compute_skew_generators(W::ChebyshevGramMatrix{T}) where T
201259
@inbounds @simd for j in 1:n-1
202260
G[j, 2] = -(μ[n+2-j] + μ[n+j])/2
203261
end
204-
G[n, 2] = -μ[2]/2
205262
G
206263
end
207264

0 commit comments

Comments
 (0)