Skip to content

Commit 4ac8dfd

Browse files
committed
Add convenience functions for symmetric eigen BandedMatrices
1 parent 5e9c350 commit 4ac8dfd

File tree

6 files changed

+74
-3
lines changed

6 files changed

+74
-3
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "ApproxFunOrthogonalPolynomials"
22
uuid = "b70543e2-c0d9-56b8-a290-0d4d6d4de211"
3-
version = "0.6.26"
3+
version = "0.6.27"
44

55
[deps]
66
ApproxFunBase = "fbd15aa5-315a-5a7d-a8a4-24992e37be05"

src/ApproxFunOrthogonalPolynomials.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import ApproxFunBase: Fun, SubSpace, WeightSpace, NoSpace, HeavisideSpace,
4444
ℓ⁰, recα, recβ, recγ, ℵ₀, ∞, RectDomain,
4545
assert_integer, supportsinplacetransform, ContinuousSpace, SpecialEvalPtType,
4646
isleftendpoint, isrightendpoint, evaluation_point, boundaryfn, ldiffbc, rdiffbc,
47-
LeftEndPoint, RightEndPoint
47+
LeftEndPoint, RightEndPoint, promotedomainspace
4848

4949
import DomainSets: Domain, indomain, UnionDomain, FullSpace, Point,
5050
Interval, ChebyshevInterval, boundary, rightendpoint, leftendpoint,
@@ -139,6 +139,7 @@ include("Spaces/Spaces.jl")
139139
include("roots.jl")
140140
include("specialfunctions.jl")
141141
include("fastops.jl")
142+
include("symeigen.jl")
142143
include("show.jl")
143144

144145
end

src/Spaces/Jacobi/Jacobi.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,18 @@ end
1414
Jacobi(b::T,a::T,d::Domain) where {T<:Number} =
1515
Jacobi{typeof(d),promote_type(T,real(prectype(d)))}(b, a, d)
1616
Legendre(domain = ChebyshevInterval()) = Jacobi(0,0,Domain(domain)::Domain)
17+
Legendre(s::PolynomialSpace) = Legendre(Jacobi(s))
18+
Legendre(s::Jacobi) = s.a == s.b == 0 ? s : throw(ArgumentError("can't convert $s to Legendre"))
1719
Jacobi(b::Number,a::Number,d=ChebyshevInterval()) = Jacobi(promote(b, a)..., Domain(d)::Domain)
1820
Jacobi(A::Ultraspherical) = Jacobi(order(A)-0.5,order(A)-0.5,domain(A))
1921
Jacobi(A::Chebyshev) = Jacobi(-0.5,-0.5,domain(A))
22+
Jacobi(A::Jacobi) = A
2023

2124
const NormalizedJacobi{D<:Domain,R,T} = NormalizedPolynomialSpace{Jacobi{D,R,T},D,R}
2225
NormalizedJacobi(s...) = NormalizedPolynomialSpace(Jacobi(s...))
26+
NormalizedJacobi(s::Space) = NormalizedPolynomialSpace(Jacobi(_stripnorm(s)))
2327
NormalizedLegendre(d...) = NormalizedPolynomialSpace(Legendre(d...))
28+
NormalizedLegendre(s::Space) = NormalizedPolynomialSpace(Legendre(_stripnorm(s)))
2429

2530
function normalization(::Type{T}, sp::Jacobi, k::Int) where T
2631
x = FastTransforms.Anαβ(k, sp.a, sp.b)

src/symeigen.jl

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
export symmetric_bandmatrices_eigen
2+
3+
function symmetrize_operator_eigen(L, B)
4+
L2, B2 = promotedomainspace((L, B))
5+
if isambiguous(domainspace(L))
6+
throw(ArgumentError("could not detect spaces, please specify the domain spaces for the operators"))
7+
end
8+
d = domain(L2)
9+
if !(domainspace(L2) == Legendre(d) || domainspace(L2) == Ultraspherical(0.5, d))
10+
throw(ArgumentError("domainspace of the operators must be $(Legendre(d)) or $(Ultraspherical(0.5,d)) "*
11+
"for the symmetric banded conversion, received $(domainspace(L2))"))
12+
end
13+
S = domainspace(L2)
14+
NS = NormalizedPolynomialSpace(S)
15+
D1 = Conversion(S, NS)
16+
D2 = Conversion(NS, S);
17+
QS = QuotientSpace(B2)
18+
Q = Conversion(QS, S)
19+
R = D1*Q;
20+
C = Conversion(domainspace(L2), rangespace(L2))
21+
P = cache(PartialInverseOperator(C, (0, bandwidth(L2, 1) + bandwidth(R, 1) + bandwidth(C, 2))));
22+
A = R'D1*P*L2*D2*R
23+
BB = R'R;
24+
25+
return A, BB
26+
end
27+
28+
"""
29+
symmetric_bandmatrices_eigen(L::Operator, B::Operator, n::Integer)
30+
31+
Recast the self-adjoint eigenvalue problem `L v = λ v` subject to `B v = 0` to the generalized
32+
eigenvalue problem `SA v = λ SB v`, where `SA` and `SB` are `Symmetric(::BandedMatrix)`es, and
33+
return `SA` and `SB`. The `domainspace` of the operators must be one of `Legedre(::Domain)` or
34+
`Ultraspherical(0.5, ::Domain)`.
35+
36+
!!! note
37+
No tests are performed to assert that the system is self-adjoint, and it's the user's responsibility
38+
to ensure that this holds.
39+
"""
40+
function symmetric_bandmatrices_eigen(L, B, n)
41+
AA, BB = symmetrize_operator_eigen(L, B)
42+
SA = Symmetric(AA[1:n,1:n], :L)
43+
SB = Symmetric(BB[1:n,1:n], :L)
44+
return SA, SB
45+
end

test/EigTest.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,4 +221,12 @@ using Test
221221
λ = eigvals(Matrix([B;L][1:n,1:n]), Matrix([B-B;C][1:n,1:n]))
222222
@test eltype(λ) == Complex{Float64}
223223
end
224+
225+
@testset "convenience function for symmetric problems" begin
226+
L = -Derivative(Legendre(0..1))^2
227+
B = Dirichlet()
228+
SA, SB = ApproxFunOrthogonalPolynomials.symmetric_bandmatrices_eigen(L, B, 20)
229+
λ = eigvals(SA, SB)[1:4]
230+
@test λ (1:4).^2 .* pi^2 rtol=1e-8
231+
end
224232
end

test/JacobiTest.jl

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,17 +183,29 @@ using Static
183183
end
184184

185185
@testset "conversion between spaces" begin
186-
for u in (Ultraspherical(1), Chebyshev())
186+
for u in (Ultraspherical(1), Chebyshev(), Jacobi(1,1), Legendre())
187187
@test NormalizedPolynomialSpace(Jacobi(u)) ==
188188
NormalizedJacobi(NormalizedPolynomialSpace(u))
189189
end
190190
for j in (Legendre(), Jacobi(1,1))
191191
@test NormalizedPolynomialSpace(Ultraspherical(j)) ==
192192
NormalizedUltraspherical(NormalizedPolynomialSpace(j))
193193
end
194+
@test NormalizedLegendre(NormalizedLegendre()) == NormalizedLegendre()
195+
@test NormalizedJacobi(NormalizedJacobi(1,1)) == NormalizedJacobi(1,1)
196+
@test Legendre(Legendre()) == Legendre()
197+
@test Legendre(Ultraspherical(0.5)) == Legendre()
198+
@test_throws ArgumentError Legendre(Chebyshev())
194199
end
195200

196201
@test ApproxFunOrthogonalPolynomials.normalization(ComplexF64, Jacobi(-0.5, -0.5), 0) pi
202+
203+
@testset "domainspace promotion bug" begin
204+
X = Multiplication(Fun(Legendre()), NormalizedLegendre()) Jacobi(2,2)
205+
Y = X : Legendre()
206+
@test domainspace(Y) == Legendre()
207+
@test Y * Fun(Legendre()) Fun(x->x^2, Legendre())
208+
end
197209
end
198210

199211
@testset "inplace transform" begin

0 commit comments

Comments
 (0)