Skip to content

Commit 93f8400

Browse files
authored
Unweighted Laplacian (#99)
* Update test_disk.jl * Fix JacobiTriangle evaluation * Add Rz * Update test_triangle.jl * redefine WeightedTriangle * Update triangle.jl * some support for general Weighted * add ==, etc., higher order L * increase coverage * fix tests * Update test_triangle.jl * add unweighted Laplacian * v0.2 * More Zernike conversions
1 parent 3721d04 commit 93f8400

File tree

7 files changed

+316
-65
lines changed

7 files changed

+316
-65
lines changed

Project.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "MultivariateOrthogonalPolynomials"
22
uuid = "4f6956fd-4f93-5457-9149-7bfc4b2ce06d"
3-
version = "0.1.4"
3+
version = "0.2"
44

55
[deps]
66
ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
@@ -44,8 +44,9 @@ StaticArrays = "1"
4444
julia = "1.6"
4545

4646
[extras]
47+
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
4748
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
4849
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
4950

5051
[targets]
51-
test = ["RecipesBase", "Test"]
52+
test = ["ForwardDiff", "RecipesBase", "Test"]

src/ModalInterlace.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ axes(Z::ModalInterlace) = blockedrange.(oneto.(Z.MN))
1414

1515
blockbandwidths(R::ModalInterlace) = R.bandwidths
1616
subblockbandwidths(::ModalInterlace) = (0,0)
17+
copy(M::ModalInterlace) = M
1718

1819

1920
function Base.view(R::ModalInterlace{T}, KJ::Block{2}) where T

src/MultivariateOrthogonalPolynomials.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
module MultivariateOrthogonalPolynomials
2+
using StaticArrays: iszero
3+
using QuasiArrays: AbstractVector
24
using ClassicalOrthogonalPolynomials, FastTransforms, BlockBandedMatrices, BlockArrays, DomainSets,
35
QuasiArrays, StaticArrays, ContinuumArrays, InfiniteArrays, InfiniteLinearAlgebra,
46
LazyArrays, SpecialFunctions, LinearAlgebra, BandedMatrices, LazyBandedMatrices, ArrayLayouts,
57
HarmonicOrthogonalPolynomials
68

7-
import Base: axes, in, ==, *, ^, \, copy, OneTo, getindex, size, oneto
9+
import Base: axes, in, ==, *, ^, \, copy, OneTo, getindex, size, oneto, all
810
import DomainSets: boundary
911

1012
import QuasiArrays: LazyQuasiMatrix, LazyQuasiArrayStyle

src/disk.jl

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ axes(P::Zernike{T}) where T = (Inclusion(UnitDisk{T}()),blockedrange(oneto(∞))
130130

131131
copy(A::Zernike) = A
132132

133+
Base.summary(io::IO, P::Zernike) = print(io, "Zernike($(P.a), $(P.b))")
134+
133135
orthogonalityweight(Z::Zernike) = ZernikeWeight(Z.a, Z.b)
134136

135137
zerniker(ℓ, m, a, b, r::T) where T = sqrt(convert(T,2)^(m+a+b+2-iszero(m))/π) * r^m * normalizedjacobip((ℓ-m) ÷ 2, b, m+a, 2r^2-1)
@@ -248,6 +250,16 @@ getindex(W::WeightedZernikeLaplacianDiag, k::Integer) = W[findblockindex(axes(W,
248250
WZ.P * Diagonal(WeightedZernikeLaplacianDiag{eltype(eltype(WZ))}())
249251
end
250252

253+
@simplify function *::Laplacian, Z::Zernike)
254+
a,b = Z.a,Z.b
255+
@assert a == 0
256+
T = promote_type(eltype(eltype(Δ)),eltype(Z)) # TODO: remove extra eltype
257+
D = Derivative(Inclusion(ChebyshevInterval{T}()))
258+
Δs = BroadcastVector{AbstractMatrix{T}}((C,B,A) -> 4(HalfWeighted{:b}(C)\(D*HalfWeighted{:b}(B)))*(B\(D*A)), Normalized.(Jacobi.(b+2,a:∞)), Normalized.(Jacobi.(b+1,(a+1):∞)), Normalized.(Jacobi.(b,a:∞)))
259+
Δ = ModalInterlace(Δs, (ℵ₀,ℵ₀), (-2,2))
260+
Zernike(a,b+2) * Δ
261+
end
262+
251263
###
252264
# Fractional Laplacian
253265
###
@@ -290,20 +302,16 @@ fractionalcfs2d(l::Integer, m::Integer, β) = fractionalcfs(l,m,β,2)
290302

291303
function \(A::Zernike{T}, B::Zernike{V}) where {T,V}
292304
TV = promote_type(T,V)
293-
A.a == B.a && A.b == B.b && return Eye{TV}(∞)
294-
@assert A.a == 0 && A.b == 1
295-
@assert B.a == 0 && B.b == 0
296-
ModalInterlace{TV}((Normalized.(Jacobi{TV}.(1,0:∞)) .\ Normalized.(Jacobi{TV}.(0,0:∞))) ./ sqrt(convert(TV, 2)), (ℵ₀,ℵ₀), (0,2))
305+
A.a == B.a && A.b == B.b && return Eye{TV}((axes(A,2),))
306+
st = Int(A.a - B.a + A.b - B.b)
307+
ModalInterlace{TV}((Normalized.(Jacobi{TV}.(A.b,A.a:∞)) .\ Normalized.(Jacobi{TV}.(B.b,B.a:∞))) .* convert(TV, 2)^(-st/2), (ℵ₀,ℵ₀), (0,2st))
297308
end
298309

299-
function \(A::Zernike{T}, B::Weighted{V,Zernike{V}}) where {T,V}
310+
function \(A::Zernike{T}, wB::Weighted{V,Zernike{V}}) where {T,V}
300311
TV = promote_type(T,V)
301-
A.a == B.P.a == A.b == B.P.b == 0 && return Eye{TV}(∞)
302-
if A.a == A.b == 0
303-
@assert B.P.a == 0 && B.P.b == 1
304-
ModalInterlace{TV}((Normalized.(Jacobi{TV}.(0, 0:∞)) .\ HalfWeighted{:a}.(Normalized.(Jacobi{TV}.(1, 0:∞)))) ./ sqrt(convert(TV, 2)), (ℵ₀,ℵ₀), (2,0))
305-
else
306-
Z = Zernike{TV}()
307-
(A \ Z) * (Z \ B)
308-
end
312+
B = wB.P
313+
A.a == B.a == A.b == B.b == 0 && return Eye{TV}((axes(A,2),))
314+
c = Int(B.a - A.a + B.b - A.b)
315+
@assert iszero(B.a)
316+
ModalInterlace{TV}((Normalized.(Jacobi{TV}.(A.b, A.a:∞)) .\ HalfWeighted{:a}.(Normalized.(Jacobi{TV}.(B.b, B.a:∞)))) .* convert(TV, 2)^(-c/2), (ℵ₀,ℵ₀), (2Int(B.b), 2Int(A.a+A.b)))
309317
end

src/triangle.jl

Lines changed: 121 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,42 @@ end
3939

4040
TriangleWeight(a::T, b::T, c::T) where T = TriangleWeight{float(T),T}(a, b, c)
4141

42-
const WeightedTriangle{T} = WeightedBasis{T,<:TriangleWeight,<:JacobiTriangle}
42+
const WeightedTriangle{T,V} = Weighted{T,JacobiTriangle{T,V}}
4343

44-
WeightedTriangle(a, b, c) = TriangleWeight(a,b,c) .* JacobiTriangle(a,b,c)
44+
WeightedTriangle(a, b, c) = Weighted(JacobiTriangle(a,b,c))
4545

4646
axes(P::TriangleWeight{T}) where T = (Inclusion(UnitTriangle{T}()),)
47+
function getindex(P::TriangleWeight, xy::StaticVector{2})
48+
x,y = xy
49+
x^P.a * y^P.b * (1-x-y)^P.c
50+
end
51+
52+
all(::typeof(isone), w::TriangleWeight) = iszero(w.a) && iszero(w.b) && iszero(w.c)
53+
==(w::TriangleWeight, v::TriangleWeight) = w.a == v.a && w.b == v.b && w.c == v.c
54+
55+
==(wA::WeightedTriangle, B::JacobiTriangle) = wA.P == B == JacobiTriangle(0,0,0)
56+
==(B::JacobiTriangle, wA::WeightedTriangle) = wA == B
57+
58+
==(w_A::WeightedBasis{<:Any,<:TriangleWeight,<:JacobiTriangle}, w_B::WeightedBasis{<:Any,<:TriangleWeight,<:JacobiTriangle}) = arguments(w_A) == arguments(w_B)
59+
60+
function ==(w_A::WeightedBasis{<:Any,<:TriangleWeight,<:JacobiTriangle}, B::JacobiTriangle)
61+
w,A = arguments(w_A)
62+
all(isone,w) && A == B
63+
end
64+
65+
==(B::JacobiTriangle, w_A::WeightedBasis{<:Any,<:TriangleWeight,<:JacobiTriangle}) = w_A == B
66+
67+
function ==(w_A::WeightedBasis{<:Any,<:TriangleWeight,<:JacobiTriangle}, wB::WeightedTriangle)
68+
w,A = arguments(w_A)
69+
w.a == A.a && w.b == A.b && w.c == A.c && A == wB.P
70+
end
71+
72+
==(wB::WeightedTriangle, w_A::WeightedBasis{<:Any,<:TriangleWeight,<:JacobiTriangle}) = w_A == wB
4773

4874
Base.summary(io::IO, P::TriangleWeight) = print(io, "x^$(P.a)*y^$(P.b)*(1-x-y)^$(P.c) on the unit triangle")
4975

76+
orthogonalityweight(P::JacobiTriangle) = TriangleWeight(P.a, P.b, P.c)
77+
5078
@simplify function *(Dx::PartialDerivative{1}, P::JacobiTriangle)
5179
a,b,c = P.a,P.b,P.c
5280
n = mortar(Fill.(oneto(∞),oneto(∞)))
@@ -117,6 +145,18 @@ function Ry(a,b,c)
117145
_BandedBlockBandedMatrix(dat, axes(k,1), (0,1), (0,1))
118146
end
119147

148+
function Rz(a,b,c)
149+
n = mortar(Fill.(oneto(∞),oneto(∞)))
150+
k = mortar(Base.OneTo.(oneto(∞)))
151+
dat = PseudoBlockArray(Vcat(
152+
(-(k .+ (b-1) ) .* (n .+ k .+ (b+c-1)) ./ ((2n .+ (a+b+c)) .* (2k .+ (b+c-1) )))',
153+
((k .- n .- a ) .* (k .+ (b+c)) ./ ((2n .+ (a+b+c)) .* (2k .+ (b+c-1) )))',
154+
(-(k .+ (b-1) ) .* (k .- n .- 1) ./ ((2n .+ (a+b+c)) .* (2k .+ (b+c-1) )))',
155+
((n .+ k .+ (a+b+c) ) .* (k .+ (b+c)) ./ ((2n .+ (a+b+c)) .* (2k .+ (b+c-1) )))'
156+
), (blockedrange(Fill(2,2)), axes(n,1)))
157+
_BandedBlockBandedMatrix(dat, axes(k,1), (0,1), (0,1))
158+
end
159+
120160
function Lx(a,b,c)
121161
n = mortar(Fill.(oneto(∞),oneto(∞)))
122162
k = mortar(Base.OneTo.(oneto(∞)))
@@ -151,35 +191,94 @@ end
151191

152192

153193
function \(w_A::WeightedTriangle, w_B::WeightedTriangle)
154-
wA,A = w_A.args
155-
wB,B = w_B.args
156-
157-
@assert wA.a == A.a && wA.b == A.b && wA.c == A.c
158-
@assert wB.a == B.a && wB.b == B.b && wB.c == B.c
194+
A,B = w_A.P,w_B.P
159195

160-
if A.a + 1 == B.a && A.b == B.b && A.c == B.c
196+
if A.a == B.a && A.b == B.b && A.c == B.c
197+
Eye{promote_type(eltype(A),eltype(B))}((axes(B,2),))
198+
elseif A.a + 1 == B.a && A.b == B.b && A.c == B.c
161199
Lx(B.a, B.b, B.c)
162200
elseif A.a == B.a && A.b + 1 == B.b && A.c == B.c
163201
Ly(B.a, B.b, B.c)
164202
elseif A.a == B.a && A.b == B.b && A.c + 1 == B.c
165203
Lz(B.a, B.b, B.c)
204+
elseif A.a < B.a
205+
w_C = WeightedTriangle(A.a+1,A.b,A.c)
206+
(w_A \ w_C) * (w_C \ w_B)
207+
elseif A.b < B.b
208+
w_C = WeightedTriangle(A.a,A.b+1,A.c)
209+
(w_A \ w_C) * (w_C \ w_B)
210+
elseif A.c < B.c
211+
w_C = WeightedTriangle(A.a,A.b,A.c+1)
212+
(w_A \ w_C) * (w_C \ w_B)
166213
else
167-
error("not implemented for $A and $wB")
214+
error("not implemented for $w_A and $w_B")
168215
end
169216
end
170217

171-
\(w_A::JacobiTriangle, w_B::WeightedTriangle) =
172-
(TriangleWeight(0,0,0) .* w_A) \ w_B
218+
function \(w_A::WeightedBasis{<:Any,<:TriangleWeight,<:JacobiTriangle}, w_B::WeightedTriangle)
219+
wA,A = w_A.args
220+
w_A == Weighted(A) && Weighted(A) \ w_B
221+
all(isone,wA) && return A \ w_B
222+
error("Not implemented")
223+
end
224+
225+
function \(w_A::WeightedTriangle, w_B::WeightedBasis{<:Any,<:TriangleWeight,<:JacobiTriangle})
226+
wB,B = w_B.args
227+
w_B == Weighted(B) && w_A \ Weighted(B)
228+
all(isone,wB) && return w_A \ B
229+
error("Not implemented")
230+
end
231+
232+
233+
function \(w_A::WeightedBasis{<:Any,<:TriangleWeight,<:JacobiTriangle}, w_B::WeightedBasis{<:Any,<:TriangleWeight,<:JacobiTriangle})
234+
wA,A = w_A.args
235+
w_A == Weighted(A) && Weighted(A) \ w_B
236+
all(isone,wA) && return A \ w_B
237+
error("Not implemented")
238+
end
239+
240+
function \(w_A::WeightedBasis{<:Any,<:TriangleWeight,<:JacobiTriangle}, B::JacobiTriangle)
241+
wA,A = w_A.args
242+
w_A == Weighted(A) && return Weighted(A) \ B
243+
all(isone,wA) && return A \ B
244+
error("Not implemented")
245+
end
246+
247+
function \(A::JacobiTriangle, w_B::WeightedBasis{<:Any,<:TriangleWeight,<:JacobiTriangle})
248+
wB,B = w_B.args
249+
w_B == Weighted(B) && return A \ Weighted(B)
250+
all(isone,wB) && return A \ B
251+
error("Not implemented")
252+
end
253+
254+
function \(A::JacobiTriangle, w_B::WeightedTriangle)
255+
w_B.P == JacobiTriangle() && return A \ w_B.P
256+
A == JacobiTriangle() && return Weighted(A) \ w_B
257+
(TriangleWeight(0,0,0) .* A) \ w_B
258+
end
259+
function \(w_A::WeightedTriangle, B::JacobiTriangle)
260+
w_A.P == JacobiTriangle() && return w_A.P \ B
261+
w_A \ (TriangleWeight(0,0,0) .* B)
262+
end
173263

174264
function \(A::JacobiTriangle, B::JacobiTriangle)
175-
if A.a == B.a && A.b == B.b && A.c == B.c
176-
Eye((axes(B,2),))
265+
if A == B
266+
Eye{promote_type(eltype(A),eltype(B))}((axes(B,2),))
177267
elseif A.a == B.a + 1 && A.b == B.b && A.c == B.c
178268
Rx(B.a, B.b, B.c)
179269
elseif A.a == B.a && A.b == B.b + 1 && A.c == B.c
180270
Ry(B.a, B.b, B.c)
181-
# elseif A.a == B.a && A.b == B.b && A.c == B.c + 1
182-
# Rz(B.a, B.b, B.c)
271+
elseif A.a == B.a && A.b == B.b && A.c == B.c + 1
272+
Rz(B.a, B.b, B.c)
273+
elseif A.a > B.a
274+
C = JacobiTriangle(B.a+1,B.b,B.c)
275+
(A \ C) * (C \ B)
276+
elseif A.b > B.b
277+
C = JacobiTriangle(B.a,B.b+1,B.c)
278+
(A \ C) * (C \ B)
279+
elseif A.c > B.c
280+
C = JacobiTriangle(B.a,B.b,B.c+1)
281+
(A \ C) * (C \ B)
183282
else
184283
error("not implemented for $A and $B")
185284
end
@@ -431,14 +530,18 @@ function tri_forwardrecurrence(N::Int, X, Y, x, y)
431530
N < 1 && return ret
432531
ret[1] = 1
433532
N < 2 && return ret
434-
ret[2] = x/X[1,1]-1
435-
ret[3] = -Y[2,1]/(Y[3,1]*X[1,1]) * (x-X[1,1]) + (y-Y[1,1])/Y[3,1]
436533

437534
X_N = X[Block.(1:N), Block.(1:N-1)]
438535
Y_N = Y[Block.(1:N), Block.(1:N-1)]
439536

537+
let n = 1
538+
A = TriangleRecurrenceA(n, X_N, Y_N)
539+
B = TriangleRecurrenceB(n, X_N, Y_N)
540+
ret[Block(2)] .= A*[x; y] + vec(B)
541+
end
542+
440543
for n = 2:N-1
441-
# P[n+1,xy] == (A[n]*[x*Eye(n); y*Eye(n)] + B[n])*P[n,xy] - C[n]*P[n-1,xy]
544+
# P[n+1,xy] == (A*[x*Eye(n); y*Eye(n)] + B)*P[n,xy] - C*P[n-1,xy]
442545
A = TriangleRecurrenceA(n, X_N, Y_N)
443546
B = TriangleRecurrenceB(n, X_N, Y_N)
444547
C = TriangleRecurrenceC(n, X_N, Y_N)

0 commit comments

Comments
 (0)