Skip to content

Commit 8c22901

Browse files
authored
Lowering for Zernike polynomials (#68)
* Generalize ModalInterlace * Zernike lowering * Update test_disk.jl * Update Project.toml * add tests
1 parent cbc1211 commit 8c22901

File tree

4 files changed

+73
-32
lines changed

4 files changed

+73
-32
lines changed

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ ArrayLayouts = "0.6"
2828
BandedMatrices = "0.16"
2929
BlockArrays = "0.14.1, 0.15"
3030
BlockBandedMatrices = "0.10.1"
31-
ClassicalOrthogonalPolynomials = "0.3.1"
32-
ContinuumArrays = "0.6"
31+
ClassicalOrthogonalPolynomials = "0.3.2"
32+
ContinuumArrays = "0.6.3"
3333
DomainSets = "0.4"
3434
FastTransforms = "0.11, 0.12"
3535
FillArrays = "0.11"

src/MultivariateOrthogonalPolynomials.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import BlockBandedMatrices: _BandedBlockBandedMatrix, AbstractBandedBlockBandedM
1515
import LinearAlgebra: factorize
1616
import LazyArrays: arguments, paddeddata
1717

18-
import ClassicalOrthogonalPolynomials: jacobimatrix, Weighted, orthogonalityweight
18+
import ClassicalOrthogonalPolynomials: jacobimatrix, Weighted, orthogonalityweight, HalfWeighted
1919
import HarmonicOrthogonalPolynomials: BivariateOrthogonalPolynomial, MultivariateOrthogonalPolynomial, Plan,
2020
PartialDerivative, BlockOneTo, BlockRange1, interlace
2121

src/disk.jl

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,19 @@ struct ZernikeWeight{T} <: Weight{T}
5353
b::T
5454
end
5555

56+
5657
"""
5758
ZernikeWeight(b)
5859
5960
is a quasi-vector representing `(1-r^2)^b`
6061
"""
6162

6263
ZernikeWeight(b) = ZernikeWeight(zero(b), b)
64+
ZernikeWeight{T}(b) where T = ZernikeWeight{T}(zero(T), b)
65+
ZernikeWeight{T}() where T = ZernikeWeight{T}(zero(T))
66+
ZernikeWeight() = ZernikeWeight{Float64}()
67+
68+
copy(w::ZernikeWeight) = w
6369

6470
axes(::ZernikeWeight{T}) where T = (Inclusion(UnitDisk{T}()),)
6571

@@ -186,50 +192,55 @@ getindex(W::WeightedZernikeLaplacianDiag, k::Integer) = W[findblockindex(axes(W,
186192
WZ.P * Diagonal(WeightedZernikeLaplacianDiag{eltype(eltype(WZ))}())
187193
end
188194

189-
struct ZernikeConversion{T} <: AbstractBandedBlockBandedMatrix{T} end
190195

191-
axes(Z::ZernikeConversion) = (blockedrange(oneto(∞)), blockedrange(oneto(∞)))
196+
"""
197+
ModalInterlace
198+
"""
199+
struct ModalInterlace{T} <: AbstractBandedBlockBandedMatrix{T}
200+
ops
201+
bandwidths::NTuple{2,Int}
202+
end
203+
204+
axes(Z::ModalInterlace) = (blockedrange(oneto(∞)), blockedrange(oneto(∞)))
192205

193-
blockbandwidths(::ZernikeConversion) = (0,2)
194-
subblockbandwidths(::ZernikeConversion) = (0,0)
206+
blockbandwidths(R::ModalInterlace) = R.bandwidths
207+
subblockbandwidths(::ModalInterlace) = (0,0)
195208

196209

197-
function Base.view(W::ZernikeConversion{T}, KJ::Block{2}) where T
210+
function Base.view(R::ModalInterlace{T}, KJ::Block{2}) where T
198211
K,J = KJ.n
199212
dat = Matrix{T}(undef,1,J)
200-
if J == K
213+
l,u = blockbandwidths(R)
214+
if iseven(J-K) && -l  J - K  u
215+
sh = (J-K)÷2
201216
if isodd(K)
202-
R0 = Normalized(Jacobi(1,0)) \ Normalized(Jacobi(0,0))
203-
dat[1,1] = R0[K÷2+1,K÷2+1]
217+
k = K÷2+1
218+
dat[1,1] = R.ops[1][k,k+sh]
204219
end
205-
for m in range(2-iseven(K); step=2, length=J÷2)
206-
Rm = Normalized(Jacobi(1,m)) \ Normalized(Jacobi(0,m))
207-
j = K÷2-m÷2+isodd(K)
208-
dat[1,m] = dat[1,m+1] = Rm[j,j]
209-
end
210-
elseif J == K + 2
211-
if isodd(K)
212-
R0 = Normalized(Jacobi(1,0)) \ Normalized(Jacobi(0,0))
213-
j = K÷2+1
214-
dat[1,1] = R0[j,j+1]
215-
end
216-
for m in range(2-iseven(K); step=2, length=K÷2)
217-
Rm = Normalized(Jacobi(1,m)) \ Normalized(Jacobi(0,m))
218-
j = K÷2-m÷2+isodd(K)
219-
dat[1,m] = dat[1,m+1] = Rm[j,j+1]
220+
for m in range(2-iseven(K); step=2, length=J÷2-max(0,sh))
221+
k = K÷2-m÷2+isodd(K)
222+
dat[1,m] = dat[1,m+1] = R.ops[m+1][k,k+sh]
220223
end
221224
else
222225
fill!(dat, zero(T))
223226
end
224-
dat ./= sqrt(2one(T))
225227
_BandedMatrix(dat, K, 0, 0)
226228
end
227229

228-
getindex(R::ZernikeConversion, k::Integer, j::Integer) = R[findblockindex.(axes(R),(k,j))...]
230+
getindex(R::ModalInterlace, k::Integer, j::Integer) = R[findblockindex.(axes(R),(k,j))...]
229231

230232
function \(A::Zernike{T}, B::Zernike{V}) where {T,V}
231-
A.a == B.a && A.b == B.b && return Eye{promote_type(T,V)}(∞)
233+
TV = promote_type(T,V)
234+
A.a == B.a && A.b == B.b && return Eye{TV}(∞)
232235
@assert A.a == 0 && A.b == 1
233236
@assert B.a == 0 && B.b == 0
234-
ZernikeConversion{promote_type(T,V)}()
237+
ModalInterlace{TV}((Normalized.(Jacobi{TV}.(1,0:∞)) .\ Normalized.(Jacobi{TV}.(0,0:∞))) ./ sqrt(convert(TV, 2)), (0,2))
238+
end
239+
240+
function \(A::Zernike{T}, B::Weighted{V,Zernike{V}}) where {T,V}
241+
TV = promote_type(T,V)
242+
A.a == B.P.a == A.b == B.P.b == 0 && return Eye{TV}(∞)
243+
@assert A.a == A.b == 0
244+
@assert B.P.a == 0 && B.P.b == 1
245+
ModalInterlace{TV}((Normalized.(Jacobi{TV}.(0, 0:∞)) .\ HalfWeighted{:a}.(Normalized.(Jacobi{TV}.(1, 0:∞)))) ./ sqrt(convert(TV, 2)), (2,0))
235246
end

test/test_disk.jl

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using MultivariateOrthogonalPolynomials, ClassicalOrthogonalPolynomials, StaticArrays, BlockArrays, BandedMatrices, FastTransforms, LinearAlgebra, Test
2-
import MultivariateOrthogonalPolynomials: DiskTrav, grid, ZernikeConversion
2+
import MultivariateOrthogonalPolynomials: DiskTrav, grid
33
import ClassicalOrthogonalPolynomials: HalfWeighted
44

55

@@ -10,6 +10,13 @@ import ClassicalOrthogonalPolynomials: HalfWeighted
1010

1111
@test Zernike() == Zernike()
1212
@test Zernike(1) Zernike()
13+
@test Zernike() copy(Zernike())
14+
15+
@test ZernikeWeight() == ZernikeWeight() == ZernikeWeight(0,0) ==
16+
ZernikeWeight(0) == ZernikeWeight{Float64}() ==
17+
ZernikeWeight{Float64}(0) == ZernikeWeight{Float64}(0, 0)
18+
@test ZernikeWeight(1) ZernikeWeight()
19+
@test ZernikeWeight() copy(ZernikeWeight())
1320
end
1421

1522
@testset "Evaluation" begin
@@ -176,6 +183,29 @@ import ClassicalOrthogonalPolynomials: HalfWeighted
176183

177184

178185
R = Zernike(1) \ Zernike()
179-
@test Zernike()[xy,Block.(1:5)]' Zernike(1)[xy,Block.(1:5)]'*R[Block.(1:5),Block.(1:5)]
186+
@test Zernike()[xy,Block.(1:6)]' Zernike(1)[xy,Block.(1:6)]'*R[Block.(1:6),Block.(1:6)]
187+
end
188+
189+
@testset "Lowering" begin
190+
L0 = Normalized(Jacobi(0, 0)) \ HalfWeighted{:a}(Normalized(Jacobi(1, 0)))
191+
L1 = Normalized(Jacobi(0, 1)) \ HalfWeighted{:a}(Normalized(Jacobi(1, 1)))
192+
L2 = Normalized(Jacobi(0, 2)) \ HalfWeighted{:a}(Normalized(Jacobi(1, 2)))
193+
L3 = Normalized(Jacobi(0, 3)) \ HalfWeighted{:a}(Normalized(Jacobi(1, 3)))
194+
195+
xy = SVector(0.1,0.2)
196+
r = norm(xy)
197+
w = 1 - r^2
198+
199+
@test w*Zernike(1)[xy,Block(1)[1]] L0[1:2,1]'*Zernike()[xy,getindex.(Block.(1:2:3),1)] / sqrt(2)
200+
201+
@test w*Zernike(1)[xy,Block(2)[1]] L1[1:2,1]'*Zernike()[xy,getindex.(Block.(2:2:4),1)]/sqrt(2)
202+
@test w*Zernike(1)[xy,Block(2)[2]] L1[1:2,1]'*Zernike()[xy,getindex.(Block.(2:2:4),2)]/sqrt(2)
203+
204+
@test w*Zernike(1)[xy,Block(3)[1]] L0[2:3,2]'*Zernike()[xy,getindex.(Block.(3:2:5),1)]/sqrt(2)
205+
@test w*Zernike(1)[xy,Block(3)[2]] L2[1:2,1]'*Zernike()[xy,getindex.(Block.(3:2:5),2)]/sqrt(2)
206+
@test w*Zernike(1)[xy,Block(3)[3]] L2[1:2,1]'*Zernike()[xy,getindex.(Block.(3:2:5),3)]/sqrt(2)
207+
208+
L = Zernike() \ Weighted(Zernike(1))
209+
@test w*Zernike(1)[xy,Block.(1:5)]' Zernike()[xy,Block.(1:7)]'*L[Block.(1:7),Block.(1:5)]
180210
end
181211
end

0 commit comments

Comments
 (0)