Skip to content

Commit 333de66

Browse files
committed
simplify jacobimatrix setup
1 parent 834017d commit 333de66

File tree

6 files changed

+264
-219
lines changed

6 files changed

+264
-219
lines changed

src/ContinuumArrays.jl

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
module ContinuumArrays
22
using IntervalSets, LinearAlgebra, LazyArrays, FillArrays, BandedMatrices, InfiniteArrays, DomainSets, InfiniteLinearAlgebra, QuasiArrays
33
import Base: @_inline_meta, axes, getindex, convert, prod, *, /, \, +, -,
4-
IndexStyle, IndexLinear, ==, OneTo, tail, similar, copyto!, copy
5-
import Base.Broadcast: materialize
4+
IndexStyle, IndexLinear, ==, OneTo, tail, similar, copyto!, copy,
5+
first, last
6+
import Base.Broadcast: materialize, BroadcastStyle
67
import LazyArrays: MemoryLayout, Applied, ApplyStyle, flatten, _flatten, colsupport, adjointlayout, LdivApplyStyle
78
import LinearAlgebra: pinv
89
import BandedMatrices: AbstractBandedLayout, _BandedMatrix
@@ -11,7 +12,7 @@ import FillArrays: AbstractFill, getindex_value
1112
import QuasiArrays: cardinality, checkindex, QuasiAdjoint, QuasiTranspose, Inclusion, SubQuasiArray,
1213
QuasiDiagonal, MulQuasiArray, MulQuasiMatrix, MulQuasiVector, QuasiMatMulMat,
1314
ApplyQuasiArray, ApplyQuasiMatrix, LazyQuasiArrayApplyStyle, AbstractQuasiArrayApplyStyle,
14-
LazyQuasiArray, LazyQuasiVector, LazyQuasiMatrix, LazyLayout
15+
LazyQuasiArray, LazyQuasiVector, LazyQuasiMatrix, LazyLayout, LazyQuasiArrayStyle
1516

1617
export Spline, LinearSpline, HeavisideSpline, DiracDelta, Derivative, JacobiWeight, Jacobi, Legendre, Chebyshev, Ultraspherical,
1718
fullmaterialize
@@ -21,6 +22,9 @@ export Spline, LinearSpline, HeavisideSpline, DiracDelta, Derivative, JacobiWeig
2122
####
2223
struct AlephInfinity{N} <: Integer end
2324

25+
==(::AlephInfinity, ::Int) = false
26+
==(::Int, ::AlephInfinity) = false
27+
2428
const ℵ₁ = AlephInfinity{1}()
2529

2630

@@ -30,6 +34,9 @@ const QMul3{A,B,C} = Mul{<:AbstractQuasiArrayApplyStyle, <:Tuple{A,B,C}}
3034
cardinality(::AbstractInterval) = ℵ₁
3135
*(ℵ::AlephInfinity) =
3236

37+
first(S::Inclusion{<:Any,<:AbstractInterval}) = leftendpoint(S.domain)
38+
last(S::Inclusion{<:Any,<:AbstractInterval}) = rightendpoint(S.domain)
39+
3340

3441
checkindex(::Type{Bool}, inds::AbstractInterval, i::Number) = (leftendpoint(inds) <= i) & (i <= rightendpoint(inds))
3542
checkindex(::Type{Bool}, inds::AbstractInterval, i::Inclusion) = i.domain inds
@@ -52,7 +59,7 @@ function materialize(V::SubQuasiArray{<:Any,2,<:Any,<:Tuple{<:Inclusion,<:Abstra
5259
A*P
5360
end
5461

55-
62+
BroadcastStyle(::Type{<:Inclusion{<:Any,<:AbstractInterval}}) = LazyQuasiArrayStyle{1}()
5663

5764
include("operators.jl")
5865
include("bases/bases.jl")

src/bases/jacobi.jl

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,17 @@ axes(::AbstractJacobi) = (Inclusion(ChebyshevInterval()), OneTo(∞))
4545
# Jacobi Matrix
4646
########
4747

48-
@simplify \(A::Legendre, *(B::Identity, C::Legendre)) =
49-
_BandedMatrix(Vcat(((0:∞)./(1:2:∞))', Zeros(1,∞), ((1:∞)./(1:2:∞))'), ∞, 1,1)
48+
jacobimatrix(::Legendre) = _BandedMatrix(Vcat(((0:∞)./(1:2:∞))', Zeros(1,∞), ((1:∞)./(1:2:∞))'), ∞, 1,1)
49+
50+
function jacobimatrix(J::Jacobi)
51+
b,a = J.b,J.a
52+
n = 0:
53+
B = @. 2*(n+1)*(n+a+b+1) / ((2n+a+b+1)*(2n+a+b+2))
54+
A = (a^2-b^2) ./ ((2n.+a.+b).*(2n.+a.+b.+2))
55+
C = @. 2*(n+a)*(n+b) / ((2n+a+b)*(2n+a+b+1))
56+
57+
_BandedMatrix(Vcat(C',A',B'), ∞, 1,1)
58+
end
5059

5160

5261
@simplify *(X::Identity, P::Legendre) = ApplyQuasiMatrix(*, P, P\(X*P))

src/bases/orthogonalpolynomials.jl

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
abstract type OrthogonalPolynomial{T} <: Basis{T} end
22

3-
@inline jacobioperator(P::OrthogonalPolynomial) =
4-
materialize(applied(\, P, applied(*, Diagonal(axes(P,1)), P)))
3+
4+
5+
@simplify function \(A::OrthogonalPolynomial, *(B::Identity, C::OrthogonalPolynomial))
6+
A === C || error("Override $(typeof(A)) \\ (x * $(typeof(C)))")
7+
jacobimatrix(A)
8+
end
9+
10+
11+
@simplify *(X::Identity, P::OrthogonalPolynomial) = ApplyQuasiMatrix(*, P, apply(\, P, applied(*, X, P)))
512

613

714
function forwardrecurrence!(v::AbstractVector{T}, b::AbstractVector, a::AbstractVector, c::AbstractVector, x) where T
@@ -60,13 +67,13 @@ _vec(a::Adjoint{<:Any,<:AbstractVector}) = a'
6067
bands(J) = _vec.(J.data.args)
6168

6269
function getindex(P::OrthogonalPolynomial{T}, x::Number, n::OneTo) where T
63-
J = jacobioperator(P)
70+
J = jacobimatrix(P)
6471
b,a,c = bands(J)
6572
forwardrecurrence!(similar(n,T),b,a,c,x)
6673
end
6774

6875
function getindex(P::OrthogonalPolynomial{T}, x::AbstractVector, n::OneTo) where T
69-
J = jacobioperator(P)
76+
J = jacobimatrix(P)
7077
b,a,c = bands(J)
7178
V = Matrix{T}(undef,length(x),length(n))
7279
for k = eachindex(x)

src/bases/ultraspherical.jl

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,9 @@ Ultraspherical(λ::Λ) where Λ = Ultraspherical{Float64,Λ}(λ)
3232
# Jacobi Matrix
3333
########
3434

35-
@simplify function \(C::Chebyshev, *(X::Identity, C2::Chebyshev))
36-
T = promote_type(eltype(C), eltype(X), eltype(C2))
37-
_BandedMatrix(Vcat(Fill(one(T)/2,1,∞), Zeros(1,∞), Hcat(one(T), Fill(one(T)/2,1,∞))), ∞, 1, 1)
38-
end
39-
40-
@simplify *(X::Identity, P::Chebyshev) = ApplyQuasiMatrix(*, P, apply(\, P, applied(*, X, P)))
35+
jacobimatrix(C::Chebyshev{T}) where T = _BandedMatrix(Vcat(Fill(one(T)/2,1,∞), Zeros(1,∞), Hcat(one(T), Fill(one(T)/2,1,∞))), ∞, 1, 1)
4136

42-
@simplify function \(P::Ultraspherical, *(X::Identity, U::Ultraspherical))
37+
function jacobimatrix(P::Ultraspherical)
4338
T = promote_type(eltype(P), eltype(X), eltype(U))
4439
λ = P.λ
4540
λ == U.λ || throw(ArgumentError())
@@ -48,8 +43,6 @@ end
4843
((one(T):∞) ./ (2 .*((zero(T):∞) .+ λ)))'), ∞, 1, 1)
4944
end
5045

51-
@simplify *(X::Identity, P::Ultraspherical) = ApplyQuasiMatrix(*, P, apply(\, P, applied(*, X, P)))
52-
5346

5447
##########
5548
# Derivatives

test/runtests.jl

Lines changed: 10 additions & 200 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
using ContinuumArrays, QuasiArrays, LazyArrays, IntervalSets, FillArrays, LinearAlgebra, BandedMatrices, Test, InfiniteArrays, ForwardDiff
22
import ContinuumArrays: ℵ₁, materialize, Chebyshev, Ultraspherical, jacobioperator, SimplifyStyle
3-
import QuasiArrays: SubQuasiArray, MulQuasiMatrix, Vec, Inclusion, QuasiDiagonal, LazyQuasiArrayApplyStyle, LmaterializeApplyStyle
3+
import QuasiArrays: SubQuasiArray, MulQuasiMatrix, Vec, Inclusion, QuasiDiagonal, LazyQuasiArrayApplyStyle, LazyQuasiArrayStyle, LmaterializeApplyStyle
44
import LazyArrays: MemoryLayout, ApplyStyle, Applied, colsupport
55
import ForwardDiff: Dual
66

77

88
@testset "Inclusion" begin
9-
@test_throws InexactError Inclusion(-1..1)[0.1]
10-
@test Inclusion(-1..1)[0.0] === 0
11-
X = QuasiDiagonal(Inclusion(-1.0..1))
9+
x = Inclusion(-1..1)
10+
@test_throws InexactError x[0.1]
11+
@test x[0.0] === 0
12+
x = Inclusion(-1.0..1)
13+
X = QuasiDiagonal(x)
1214
@test X[-1:0.1:1,-1:0.1:1] == Diagonal(-1:0.1:1)
15+
@test Base.BroadcastStyle(typeof(x)) == LazyQuasiArrayStyle{1}()
16+
@test x .* x isa BroadcastQuasiArray
17+
@test (x.*x)[0.1] == 0.1^2
1318
end
1419

1520
@testset "DiracDelta" begin
@@ -200,199 +205,4 @@ end
200205
@test u[0.1] 0.00012678835289369413
201206
end
202207

203-
@testset "Ultraspherical" begin
204-
T = Chebyshev()
205-
U = Ultraspherical(1)
206-
C = Ultraspherical(2)
207-
D = Derivative(axes(T,1))
208-
209-
@test T\T === pinv(T)*T === Eye(∞)
210-
@test U\U === pinv(U)*U === Eye(∞)
211-
@test C\C === pinv(C)*C === Eye(∞)
212-
213-
@test ApplyStyle(\,typeof(U),typeof(applied(*,D,T))) == SimplifyStyle()
214-
@test materialize(@~ U\(D*T)) isa BandedMatrix
215-
D₀ = U\(D*T)
216-
@test_broken D₀ isa BandedMatrix
217-
@test D₀[1:10,1:10] isa BandedMatrix{Float64}
218-
@test D₀[1:10,1:10] == diagm(1 => 1:9)
219-
@test colsupport(D₀,1) == 1:0
220-
221-
D₁ = C\(D*U)
222-
@test D₁ isa BandedMatrix
223-
@test apply(*,D₁,D₀.args...)[1:10,1:10] == diagm(2 => 4:2:18)
224-
@test (D₁*D₀)[1:10,1:10] == diagm(2 => 4:2:18)
225-
226-
S₀ = (U\T)[1:10,1:10]
227-
@test S₀ isa BandedMatrix{Float64}
228-
@test S₀ == diagm(0 => [1.0; fill(0.5,9)], 2=> fill(-0.5,8))
229-
230-
S₁ = (C\U)[1:10,1:10]
231-
@test S₁ isa BandedMatrix{Float64}
232-
@test S₁ == diagm(0 => 1 ./ (1:10), 2=> -(1 ./ (3:10)))
233-
end
234-
235-
@testset "Jacobi" begin
236-
S = Jacobi(true,true)
237-
W = Diagonal(JacobiWeight(true,true))
238-
D = Derivative(axes(W,1))
239-
P = Legendre()
240-
241-
@test pinv(pinv(S)) === S
242-
@test P\P === pinv(P)*P === Eye(∞)
243-
244-
Bi = pinv(Jacobi(2,2))
245-
@test Bi isa QuasiArrays.PInvQuasiMatrix
246-
247-
A = apply(\, Jacobi(2,2), applied(*, D, S))
248-
@test A isa BandedMatrix
249-
A = Jacobi(2,2) \ (D*S)
250-
@test typeof(A) == typeof(pinv(Jacobi(2,2))*(D*S))
251-
252-
@test A isa MulMatrix
253-
@test isbanded(A)
254-
@test bandwidths(A) == (-1,1)
255-
@test size(A) == (∞,∞)
256-
@test A[1:10,1:10] == diagm(1 => 1:0.5:5)
257-
258-
@test_broken @inferred(D*S)
259-
M = D*S
260-
@test M isa MulQuasiMatrix
261-
@test M.args[1] == Jacobi(2,2)
262-
@test M.args[2][1:10,1:10] == A[1:10,1:10]
263-
264-
L = Diagonal(JacobiWeight(true,false))
265-
@test apply(\, Jacobi(false,true), applied(*,L,S)) isa BandedMatrix
266-
@test_broken @inferred(Jacobi(false,true)\(L*S))
267-
A = Jacobi(false,true)\(L*S)
268-
@test A isa BandedMatrix
269-
@test size(A) == (∞,∞)
270-
271-
L = Diagonal(JacobiWeight(false,true))
272-
@test_broken @inferred(Jacobi(true,false)\(L*S))
273-
A = Jacobi(true,false)\(L*S)
274-
@test A isa BandedMatrix
275-
@test size(A) == (∞,∞)
276-
277-
A,B = (P'P),P\(W*S)
278-
279-
M = Mul(A,B)
280-
@test M[1,1] == 4/3
281-
282-
M = ApplyMatrix{Float64}(*,A,B)
283-
= M[1:10,1:10]
284-
@testisa BandedMatrix
285-
@test bandwidths(M̃) == (2,0)
286-
287-
@test A*B isa MulArray
288-
289-
A,B,C = (P\(W*S))',(P'P),P\(W*S)
290-
M = ApplyArray(*,A,B,C)
291-
@test bandwidths(M) == (2,2)
292-
@test M[1,1] 1+1/15
293-
@test typeof(M) == typeof(A*B*C)
294-
M = A*B*C
295-
@test bandwidths(M) == (2,2)
296-
@test M[1,1] 1+1/15
297-
end
298-
299-
@testset "P-FEM" begin
300-
S = Jacobi(true,true)
301-
W = Diagonal(JacobiWeight(true,true))
302-
D = Derivative(axes(W,1))
303-
P = Legendre()
304-
305-
M = P\(D*W*S)
306-
@test M isa ApplyArray
307-
@test M[1:10,1:10] == diagm(-1 => -2.0:-2:-18.0)
308-
309-
N = 10
310-
A = D*W*S[:,1:N]
311-
@test A.args[1] == P
312-
@test P\((D*W)*S[:,1:N]) isa AbstractMatrix
313-
@test P\(D*W*S[:,1:N]) isa AbstractMatrix
314-
315-
L = D*W*S
316-
Δ = L'L
317-
@test Δ isa MulMatrix
318-
@test Δ[1:3,1:3] isa BandedMatrix
319-
@test bandwidths(Δ) == (0,0)
320-
321-
L = D*W*S[:,1:N]
322-
323-
A = apply(*, (L').args..., L.args...)
324-
@test A isa MulQuasiMatrix
325-
326-
A = *((L').args..., L.args...)
327-
@test A isa MulQuasiMatrix
328-
329-
@test apply(*,L',L) isa QuasiArrays.ApplyQuasiArray
330-
331-
Δ = L'L
332-
@test Δ isa MulMatrix
333-
@test bandwidths(Δ) == (0,0)
334-
end
335-
336-
@testset "Chebyshev evaluation" begin
337-
P = Chebyshev()
338-
@test @inferred(P[0.1,Base.OneTo(0)]) == Float64[]
339-
@test @inferred(P[0.1,Base.OneTo(1)]) == [1.0]
340-
@test @inferred(P[0.1,Base.OneTo(2)]) == [1.0,0.1]
341-
for N = 1:10
342-
@test @inferred(P[0.1,Base.OneTo(N)]) @inferred(P[0.1,1:N]) [cos(n*acos(0.1)) for n = 0:N-1]
343-
@test @inferred(P[0.1,N]) cos((N-1)*acos(0.1))
344-
end
345-
@test P[0.1,[2,5,10]] [0.1,cos(4acos(0.1)),cos(9acos(0.1))]
346-
347-
P = Ultraspherical(1)
348-
@test @inferred(P[0.1,Base.OneTo(0)]) == Float64[]
349-
@test @inferred(P[0.1,Base.OneTo(1)]) == [1.0]
350-
@test @inferred(P[0.1,Base.OneTo(2)]) == [1.0,0.2]
351-
for N = 1:10
352-
@test @inferred(P[0.1,Base.OneTo(N)]) @inferred(P[0.1,1:N]) [sin((n+1)*acos(0.1))/sin(acos(0.1)) for n = 0:N-1]
353-
@test @inferred(P[0.1,N]) sin(N*acos(0.1))/sin(acos(0.1))
354-
end
355-
@test P[0.1,[2,5,10]] [0.2,sin(5acos(0.1))/sin(acos(0.1)),sin(10acos(0.1))/sin(acos(0.1))]
356-
357-
P = Ultraspherical(2)
358-
@test @inferred(P[0.1,Base.OneTo(0)]) == Float64[]
359-
@test @inferred(P[0.1,Base.OneTo(1)]) == [1.0]
360-
@test @inferred(P[0.1,Base.OneTo(2)]) == [1.0,0.4]
361-
@test @inferred(P[0.1,Base.OneTo(3)]) == [1.0,0.4,-1.88]
362-
end
363-
364-
@testset "Collocation" begin
365-
P = Chebyshev()
366-
D = Derivative(axes(P,1))
367-
n = 300
368-
x = cos.((0:n-2) .* π ./ (n-2))
369-
cfs = [P[-1,1:n]'; (D*P)[x,1:n] - P[x,1:n]] \ [exp(-1); zeros(n-1)]
370-
u = P[:,1:n]*cfs
371-
@test u[0.1] exp(0.1)
372-
373-
P = Chebyshev()
374-
D = Derivative(axes(P,1))
375-
D2 = D*(D*P) # could be D^2*P in the future
376-
n = 300
377-
x = cos.((1:n-2) .* π ./ (n-1)) # interior Chebyshev points
378-
C = [P[-1,1:n]';
379-
D2[x,1:n] + P[x,1:n];
380-
P[1,1:n]']
381-
cfs = C \ [1; zeros(n-2); 2] # Chebyshev coefficients
382-
u = P[:,1:n]*cfs # interpret in basis
383-
@test u[0.1] (3cos(0.1)sec(1) + csc(1)sin(0.1))/2
384-
end
385-
386-
@testset "Auto-diff" begin
387-
U = Ultraspherical(1)
388-
C = Ultraspherical(2)
389-
390-
f = x -> Chebyshev{eltype(x)}()[x,5]
391-
@test ForwardDiff.derivative(f,0.1) 4*U[0.1,4]
392-
f = x -> Chebyshev{eltype(x)}()[x,5][1]
393-
@test ForwardDiff.gradient(f,[0.1]) [4*U[0.1,4]]
394-
@test ForwardDiff.hessian(f,[0.1]) [8*C[0.1,3]]
395-
396-
f = x -> Chebyshev{eltype(x)}()[x,1:5]
397-
@test ForwardDiff.derivative(f,0.1) [0;(1:4).*U[0.1,1:4]]
398-
end
208+
include("test_orthogonalpolynomials.jl")

0 commit comments

Comments
 (0)