Skip to content

Commit d36576f

Browse files
authored
Simplify Zernike Jacobi matrices + bugfixes (#123)
* Add tests for addition and multiplication * Update disk.jl * Update Project.toml * Update test_disk.jl * Update test_disk.jl * add tests for copy * Overload copy() so that Z \ (x .* Z) works * fix minor inconsistency * fix typo * remove custom type for Zernike bands * adjust tests * up LazyBandedMatrices
1 parent df72310 commit d36576f

File tree

3 files changed

+61
-87
lines changed

3 files changed

+61
-87
lines changed

Project.toml

Lines changed: 4 additions & 4 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.2.5"
3+
version = "0.2.6"
44

55
[deps]
66
ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
@@ -24,7 +24,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
2424
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2525

2626
[compat]
27-
ArrayLayouts = "0.7, 0.8"
27+
ArrayLayouts = "0.8.6"
2828
BandedMatrices = "0.16, 0.17"
2929
BlockArrays = "0.16.14"
3030
BlockBandedMatrices = "0.11.5"
@@ -35,9 +35,9 @@ FastTransforms = "0.13"
3535
FillArrays = "0.12, 0.13"
3636
HarmonicOrthogonalPolynomials = "0.2.4"
3737
InfiniteArrays = "0.12"
38-
InfiniteLinearAlgebra = "0.6"
38+
InfiniteLinearAlgebra = "0.6.6"
3939
LazyArrays = "0.22.10"
40-
LazyBandedMatrices = "0.7.12"
40+
LazyBandedMatrices = "0.7.15"
4141
QuasiArrays = "0.9"
4242
SpecialFunctions = "1, 2"
4343
StaticArrays = "1"

src/disk.jl

Lines changed: 40 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -160,30 +160,13 @@ getindex(Z::Zernike, xy::StaticVector{2}, B::BlockIndex{1}) = Z[RadialCoordinate
160160
getindex(Z::Zernike, xy::StaticVector{2}, B::Block{1}) = [Z[xy, B[j]] for j=1:Int(B)]
161161
getindex(Z::Zernike, xy::StaticVector{2}, JR::BlockOneTo) = mortar([Z[xy,Block(J)] for J = 1:Int(JR[end])])
162162

163+
163164
###
164165
# Jacobi matrices
165166
###
166-
167-
# Due to excessively long lazy typing, we wrap the bands of the Zernike Jacobi matrix in its own type
168-
# The bands for X and Y are distinct
169-
struct ZernikeJacobimatrixBandsX{T} <: AbstractBlockMatrix{T}
170-
Z::Zernike{T}
171-
data::AbstractBlockMatrix{T}
172-
ZernikeJacobimatrixBandsX{T}(Z::Zernike{T}) where T = new{T}(Z, zernikejacobibandsX(Z))
173-
end
174-
struct ZernikeJacobimatrixBandsY{T} <: AbstractBlockMatrix{T}
175-
Z::Zernike{T}
176-
data::AbstractBlockMatrix{T}
177-
ZernikeJacobimatrixBandsY{T}(Z::Zernike{T}) where T = new{T}(Z, zernikejacobibandsY(Z))
178-
end
179-
180-
size(b::ZernikeJacobimatrixBandsX) = size(b.data)
181-
axes(b::ZernikeJacobimatrixBandsX) = axes(b.data)
182-
size(b::ZernikeJacobimatrixBandsY) = size(b.data)
183-
axes(b::ZernikeJacobimatrixBandsY) = axes(b.data)
184-
185-
function zernikejacobibandsX(Z::Zernike)
186-
α = Z.b # extract second basis parameter
167+
function jacobimatrix(::Val{1}, Z::Zernike{T}) where T
168+
if iszero(Z.a)
169+
α = Z.b # extract second basis parameter
187170

188171
k = mortar(Base.OneTo.(oneto(∞))) # k counts the the angular mode (+1)
189172
n = mortar(Fill.(oneto(∞),oneto(∞))) # n counts the block number which corresponds to the order (+1)
@@ -224,65 +207,48 @@ function zernikejacobibandsX(Z::Zernike)
224207
du = sqrt.( (dufirst .+ dueven .+ duodd ) ./ quotient)
225208
dl = sqrt.( (dleven .+ dlodd .+ dlspecial) ./ quotient)
226209

227-
return BlockBroadcastArray(hcat, du, Zeros((axes(n,1),)), dl)
228-
end
229-
230-
function zernikejacobibandsY(Z::Zernike)
231-
α = Z.b # extract second basis parameter
232-
233-
k = mortar(Base.OneTo.(oneto(∞))) # k counts the the angular mode (+1)
234-
n = mortar(Fill.(oneto(∞),oneto(∞))) # n counts the block number which corresponds to the order (+1)
235-
236-
# repeatedly used for sorting
237-
keven = iseven.(k)
238-
kodd = isodd.(k)
239-
neven = iseven.(n)
240-
nodd = isodd.(n)
241-
242-
# h1-h4 are helpers for our different sorting scheme
243-
244-
# first entries for all blocks
245-
h1 = (n .- nodd)
246-
l1 = (k .== 1) .* (h1 .* (h1 .+ 2*α) ./ 2)
247-
248-
# Even blocks
249-
h0 = (kodd .* ((k 2) .+ 1) .- (keven .* ((k 2) .- 1)))
250-
h2 = (k .>= 2) .* ((n 2 .- 1) .+ h0)
251-
l2 = neven .* (h2 .* (h2 .+ α))
252-
253-
# Odd blocks
254-
h3 = (n .> k .>= 2) .* (((n .+ 1) 2) .- h0)
255-
l3 = nodd .* (h3 .* (h3 .+ α))
256-
# Combine for diagonal of super diagonal block
257-
d = sqrt.((l1 .+ l2 .+ l3) ./ (4 .* (n .+-1)) .* (n .+ α)))
258-
259-
# The off-diagonals of the super diagonal block are negative, shifted versions of the diagonal with some entries skipped
260-
dl = (-1) .* (nodd .* kodd .+ neven .* keven) .* Vcat(0 , d)
261-
du = (-1) .* (nodd .* keven .+ neven .* kodd) .* d[2:end]
262-
263-
# generate and return bands
264-
return dat = BlockBroadcastArray(hcat, dl, Zeros((axes(n,1),)), d, Zeros((axes(n,1),)), du)
265-
end
266-
267-
function getindex(b::ZernikeJacobimatrixBandsX{T},i,j) where T
268-
return BlockArrays.getindex(b.data,i,j)
269-
end
270-
function getindex(b::ZernikeJacobimatrixBandsY{T},i,j) where T
271-
return BlockArrays.getindex(b.data,i,j)
272-
end
273-
274-
function jacobimatrix(::Val{1}, Z::Zernike{T}) where T
275-
if iszero(Z.a)
276-
dat = ZernikeJacobimatrixBandsX{T}(Z)
277-
return Symmetric(BlockBandedMatrices._BandedBlockBandedMatrix(dat', axes(dat,1), (-1,1), (1,1)))
210+
return Symmetric(BlockBandedMatrices._BandedBlockBandedMatrix(BlockBroadcastArray(hcat, du, Zeros((axes(n,1),)), dl)', axes(n,1), (-1,1), (1,1)))
278211
else
279212
error("Implement for non-zero first basis parameter.")
280213
end
281214
end
215+
282216
function jacobimatrix(::Val{2}, Z::Zernike{T}) where T
283217
if iszero(Z.a)
284-
dat = ZernikeJacobimatrixBandsY{T}(Z)
285-
return Symmetric(BlockBandedMatrices._BandedBlockBandedMatrix(dat', axes(dat,1), (-1,1), (2,2)))
218+
α = Z.b # extract second basis parameter
219+
220+
k = mortar(Base.OneTo.(oneto(∞))) # k counts the the angular mode (+1)
221+
n = mortar(Fill.(oneto(∞),oneto(∞))) # n counts the block number which corresponds to the order (+1)
222+
223+
# repeatedly used for sorting
224+
keven = iseven.(k)
225+
kodd = isodd.(k)
226+
neven = iseven.(n)
227+
nodd = isodd.(n)
228+
229+
# h1-h4 are helpers for our different sorting scheme
230+
231+
# first entries for all blocks
232+
h1 = (n .- nodd)
233+
l1 = (k .== 1) .* (h1 .* (h1 .+ 2*α) ./ 2)
234+
235+
# Even blocks
236+
h0 = (kodd .* ((k 2) .+ 1) .- (keven .* ((k 2) .- 1)))
237+
h2 = (k .>= 2) .* ((n 2 .- 1) .+ h0)
238+
l2 = neven .* (h2 .* (h2 .+ α))
239+
240+
# Odd blocks
241+
h3 = (n .> k .>= 2) .* (((n .+ 1) 2) .- h0)
242+
l3 = nodd .* (h3 .* (h3 .+ α))
243+
# Combine for diagonal of super diagonal block
244+
d = sqrt.((l1 .+ l2 .+ l3) ./ (4 .* (n .+-1)) .* (n .+ α)))
245+
246+
# The off-diagonals of the super diagonal block are negative, shifted versions of the diagonal with some entries skipped
247+
dl = (-1) .* (nodd .* kodd .+ neven .* keven) .* Vcat(0 , d)
248+
du = (-1) .* (nodd .* keven .+ neven .* kodd) .* view(d,2:∞)
249+
250+
# generate and return bands
251+
return Symmetric(BlockBandedMatrices._BandedBlockBandedMatrix(BlockBroadcastArray(hcat, dl, Zeros((axes(n,1),)), d, Zeros((axes(n,1),)), du)', axes(n,1), (-1,1), (2,2)))
286252
else
287253
error("Implement for non-zero first basis parameter.")
288254
end

test/test_disk.jl

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,20 @@ import ForwardDiff: hessian
6868
end
6969
end
7070

71-
@testset "Jacobi matrices" begin
71+
@testset "Jacobi matrices" begin
72+
# Setup
7273
α = 10 * rand()
7374
Z = Zernike(α)
7475
xy = axes(Z, 1)
7576
x, y = first.(xy), last.(xy)
7677
n = 150
77-
# X tests
78+
79+
# X tests
7880
JX = zeros(n,n)
7981
for j = 1:n
8082
JX[1:n,j] = (Z \ (x .* Z[:,j]))[1:n]
8183
end
82-
X = jacobimatrix(Val(1),Z)
84+
X = Z \ (x .* Z)
8385
# The Zernike Jacobi matrices are symmetric for this normalization
8486
@test issymmetric(X)
8587
# Consistency with expansion
@@ -88,23 +90,29 @@ import ForwardDiff: hessian
8890
f = Z \ (sin.(x.*y) .+ x.^2 .- y)
8991
xf = Z \ (x.*sin.(x.*y) .+ x.^3 .- x.*y)
9092
@test X[Block.(1:20),Block.(1:20)]*f[Block.(1:20)] xf[Block.(1:20)]
91-
# Y tests
93+
94+
# Y tests
9295
JY = zeros(n,n)
9396
for j = 1:n
9497
JY[1:n,j] = (Z \ (y .* Z[:,j]))[1:n]
9598
end
96-
Y = jacobimatrix(Val(2),Z)
99+
Y = Z \ (y .* Z)
97100
# The Zernike Jacobi matrices are symmetric for this normalization
98101
@test issymmetric(Y)
99102
# Consistency with expansion
100103
@test Y[1:150,1:150] JY
101-
# Multiplication by x
104+
# Multiplication by y
102105
f = Z \ (sin.(x.*y) .+ x.^2 .- y)
103106
yf = Z \ (y.*sin.(x.*y) .+ y .* x.^2 .- y.^2)
104107
@test Y[Block.(1:20),Block.(1:20)]*f[Block.(1:20)] yf[Block.(1:20)]
105-
# data size tests
106-
@test size((X.data).data) == (3, ℵ₀)
107-
@test size((Y.data).data) == (5, ℵ₀)
108+
109+
# Multiplication of Jacobi matrices
110+
@test (X*X)[Block.(1:6),Block.(1:6)] (X[Block.(1:10),Block.(1:10)]*X[Block.(1:10),Block.(1:10)])[Block.(1:6),Block.(1:6)]
111+
@test (X*Y)[Block.(1:6),Block.(1:6)] (X[Block.(1:10),Block.(1:10)]*Y[Block.(1:10),Block.(1:10)])[Block.(1:6),Block.(1:6)]
112+
113+
# Addition of Jacobi matrices
114+
@test (X+Y)[Block.(1:6),Block.(1:6)] (X[Block.(1:6),Block.(1:6)]+Y[Block.(1:6),Block.(1:6)])
115+
@test (Y+Y)[Block.(1:6),Block.(1:6)] (Y[Block.(1:6),Block.(1:6)]+Y[Block.(1:6),Block.(1:6)])
108116

109117
# for now, reject non-zero first parameter options
110118
@test_throws ErrorException("Implement for non-zero first basis parameter.") jacobimatrix(Val(1),Zernike(1,1))

0 commit comments

Comments
 (0)