Skip to content

Commit d1f1c63

Browse files
authored
Support finite dimensional ModalInterlace (#69)
* Support finite dimensional ModalInterlace * Start itransform * Update test_disk.jl
1 parent 9393889 commit d1f1c63

File tree

5 files changed

+105
-23
lines changed

5 files changed

+105
-23
lines changed

examples/diskheat.jl

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +0,0 @@
1-
using MultivariateOrthogonalPolynomials, DifferentialEquations
2-
3-
N = 20
4-
WZ = Weighted(Zernike(1))[:,Block.(Base.OneTo(N))]
5-
Δ = Laplacian(axes(WZ,1))
6-
*WZ).args[3].data |> typeof
7-
using LazyArrays: arguments, ApplyLayout
8-
arguments(ApplyLayout{typeof(*)}(), WZ)[2].data
9-
@ent arguments(ApplyLayout{typeof(*)}(), WZ)
10-
11-
function heat!(du, u, (R,Δ), t)
12-
13-
end

examples/diskhelmholtz.jl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using MultivariateOrthogonalPolynomials, FastTransforms, BlockBandedMatrices, Plots
2+
3+
WZ = Weighted(Zernike(1))
4+
xy = axes(WZ,1)
5+
x,y = first.(xy),last.(xy)
6+
u = Zernike(1) \ @. exp(x*cos(y))
7+
8+
N = 50
9+
KR = Block.(Base.OneTo(N))
10+
Δ = BandedBlockBandedMatrix((Zernike(1) \ (Laplacian(xy) * WZ))[KR,KR],(0,0),(0,0))
11+
C = (Zernike(1) \ WZ)[KR,KR]
12+
k = 5
13+
L = Δ - k^2 * C
14+
15+
v = (L \ u[KR])
16+
17+
F = factorize(Zernike(1)[:,KR])
18+
F.plan \ v
19+
F |> typeof |> fieldnames
20+
21+
grid(WZ[:,KR])
22+
23+
F |>typeof |> fieldnames
24+
F.F * v
25+
26+
m = DiskTrav(v).matrix
27+
28+
plan_disk2cxf(m, 0, 0) * m
29+
30+

src/MultivariateOrthogonalPolynomials.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ import DomainSets: boundary
1010
import QuasiArrays: LazyQuasiMatrix, LazyQuasiArrayStyle
1111
import ContinuumArrays: @simplify, Weight, grid, TransformFactorization, Expansion
1212

13+
import ArrayLayouts: MemoryLayout
1314
import BlockArrays: block, blockindex, BlockSlice, viewblock
1415
import BlockBandedMatrices: _BandedBlockBandedMatrix, AbstractBandedBlockBandedMatrix, _BandedMatrix, blockbandwidths, subblockbandwidths
1516
import LinearAlgebra: factorize
16-
import LazyArrays: arguments, paddeddata
17+
import LazyArrays: arguments, paddeddata, LazyArrayStyle, LazyLayout
18+
import LazyBandedMatrices: LazyBandedBlockBandedLayout
19+
import InfiniteArrays: InfiniteCardinal
1720

1821
import ClassicalOrthogonalPolynomials: jacobimatrix, Weighted, orthogonalityweight, HalfWeighted
1922
import HarmonicOrthogonalPolynomials: BivariateOrthogonalPolynomial, MultivariateOrthogonalPolynomial, Plan,

src/disk.jl

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,39 @@
88
struct DiskTrav{T, AA<:AbstractMatrix{T}} <: AbstractBlockVector{T}
99
matrix::AA
1010
function DiskTrav{T, AA}(matrix::AA) where {T,AA<:AbstractMatrix{T}}
11-
n,m = size(matrix)
12-
isodd(m) && n == m ÷ 4 + 1 || throw(ArgumentError("size must match"))
11+
m,n = size(matrix)
12+
isodd(n) && m == n ÷ 4 + 1 || throw(ArgumentError("size must match"))
1313
new{T,AA}(matrix)
1414
end
1515
end
1616

1717
DiskTrav{T}(matrix::AbstractMatrix{T}) where T = DiskTrav{T,typeof(matrix)}(matrix)
1818
DiskTrav(matrix::AbstractMatrix{T}) where T = DiskTrav{T}(matrix)
1919

20+
function DiskTrav(v::AbstractVector{T}) where T
21+
N = blocksize(v,1)
22+
m = N ÷ 2 + 1
23+
n = 4(m-1) + 1
24+
mat = zeros(T, m, n)
25+
for K in blockaxes(v,1)
26+
= Int(K)
27+
w = v[K]
28+
if isodd(K̃)
29+
mat[K̃÷2 + 1,1] = w[1]
30+
for j = 2:2:-1
31+
mat[K̃÷2-j÷2+1,2(j-1)+2] = w[j]
32+
mat[K̃÷2-j÷2+1,2(j-1)+3] = w[j+1]
33+
end
34+
else
35+
for j = 1:2:
36+
mat[K̃÷2-j÷2,2(j-1)+2] = w[j]
37+
mat[K̃÷2-j÷2,2(j-1)+3] = w[j+1]
38+
end
39+
end
40+
end
41+
DiskTrav(mat)
42+
end
43+
2044
axes(A::DiskTrav) = (blockedrange(oneto(div(size(A.matrix,2),2,RoundUp))),)
2145

2246
function getindex(A::DiskTrav, K::Block{1})
@@ -165,6 +189,7 @@ function ZernikeTransform{T}(N::Int, a::Number, b::Number) where T<:Real
165189
ZernikeTransform{T}(N, plan_disk2cxf(T, Ñ, a, b), plan_disk_analysis(T, Ñ, 4-3))
166190
end
167191
*(P::ZernikeTransform{T}, f::Matrix{T}) where T = DiskTrav(P.disk2cxf \ (P.analysis * f))[Block.(1:P.N)]
192+
\(P::ZernikeTransform, f::AbstractVector) = P.analysis \ (P.disk2cxf * DiskTrav(f).matrix)
168193

169194
factorize(S::FiniteZernike{T}) where T = TransformFactorization(grid(S), ZernikeTransform{T}(blocksize(S,2), parent(S).a, parent(S).b))
170195

@@ -174,6 +199,9 @@ struct WeightedZernikeLaplacianDiag{T} <: AbstractBlockVector{T} end
174199
axes(::WeightedZernikeLaplacianDiag) = (blockedrange(oneto(∞)),)
175200
copy(R::WeightedZernikeLaplacianDiag) = R
176201

202+
MemoryLayout(::Type{<:WeightedZernikeLaplacianDiag}) = LazyLayout()
203+
Base.BroadcastStyle(::Type{<:Diagonal{<:Any,<:WeightedZernikeLaplacianDiag}}) = LazyArrayStyle{2}()
204+
177205
function Base.view(W::WeightedZernikeLaplacianDiag{T}, K::Block{1}) where T
178206
= Int(K)
179207
d =÷2 + 1
@@ -197,12 +225,20 @@ end
197225
"""
198226
ModalInterlace
199227
"""
200-
struct ModalInterlace{T} <: AbstractBandedBlockBandedMatrix{T}
228+
struct ModalInterlace{T, MMNN<:Tuple} <: AbstractBandedBlockBandedMatrix{T}
201229
ops
230+
MN::MMNN
202231
bandwidths::NTuple{2,Int}
203232
end
204233

205-
axes(Z::ModalInterlace) = (blockedrange(oneto(∞)), blockedrange(oneto(∞)))
234+
ModalInterlace{T}(ops, MN::NTuple{2,Integer}, bandwidths::NTuple{2,Int}) where T =
235+
ModalInterlace{T,typeof(MN)}(ops, MN, bandwidths)
236+
237+
# act like lazy array
238+
MemoryLayout(::Type{<:ModalInterlace{<:Any,NTuple{2,InfiniteCardinal{0}}}}) = LazyBandedBlockBandedLayout()
239+
Base.BroadcastStyle(::Type{<:ModalInterlace{<:Any,NTuple{2,InfiniteCardinal{0}}}}) = LazyArrayStyle{2}()
240+
241+
axes(Z::ModalInterlace) = blockedrange.(oneto.(Z.MN))
206242

207243
blockbandwidths(R::ModalInterlace) = R.bandwidths
208244
subblockbandwidths(::ModalInterlace) = (0,0)
@@ -230,18 +266,27 @@ end
230266

231267
getindex(R::ModalInterlace, k::Integer, j::Integer) = R[findblockindex.(axes(R),(k,j))...]
232268

269+
function getindex(R::ModalInterlace{T}, KR::BlockOneTo, JR::BlockOneTo) where T
270+
M,N = Int(last(KR)), Int(last(JR))
271+
ModalInterlace{T}([R.ops[m][1:(M-m+2)÷2,1:(N-m+2)÷2] for m=1:min(N,M)], (M,N), R.bandwidths)
272+
end
273+
233274
function \(A::Zernike{T}, B::Zernike{V}) where {T,V}
234275
TV = promote_type(T,V)
235276
A.a == B.a && A.b == B.b && return Eye{TV}(∞)
236277
@assert A.a == 0 && A.b == 1
237278
@assert B.a == 0 && B.b == 0
238-
ModalInterlace{TV}((Normalized.(Jacobi{TV}.(1,0:∞)) .\ Normalized.(Jacobi{TV}.(0,0:∞))) ./ sqrt(convert(TV, 2)), (0,2))
279+
ModalInterlace{TV}((Normalized.(Jacobi{TV}.(1,0:∞)) .\ Normalized.(Jacobi{TV}.(0,0:∞))) ./ sqrt(convert(TV, 2)), (ℵ₀,ℵ₀), (0,2))
239280
end
240281

241282
function \(A::Zernike{T}, B::Weighted{V,Zernike{V}}) where {T,V}
242283
TV = promote_type(T,V)
243284
A.a == B.P.a == A.b == B.P.b == 0 && return Eye{TV}(∞)
244-
@assert A.a == A.b == 0
245-
@assert B.P.a == 0 && B.P.b == 1
246-
ModalInterlace{TV}((Normalized.(Jacobi{TV}.(0, 0:∞)) .\ HalfWeighted{:a}.(Normalized.(Jacobi{TV}.(1, 0:∞)))) ./ sqrt(convert(TV, 2)), (2,0))
285+
if A.a == A.b == 0
286+
@assert B.P.a == 0 && B.P.b == 1
287+
ModalInterlace{TV}((Normalized.(Jacobi{TV}.(0, 0:∞)) .\ HalfWeighted{:a}.(Normalized.(Jacobi{TV}.(1, 0:∞)))) ./ sqrt(convert(TV, 2)), (ℵ₀,ℵ₀), (2,0))
288+
else
289+
Z = Zernike{TV}()
290+
(A \ Z) * (Z \ B)
291+
end
247292
end

test/test_disk.jl

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ import ClassicalOrthogonalPolynomials: HalfWeighted
4646
@test_throws ArgumentError DiskTrav([1 2 3 4])
4747
@test_throws ArgumentError DiskTrav([1 2 3; 4 5 6])
4848
@test_throws ArgumentError DiskTrav([1 2 3 4; 5 6 7 8])
49+
50+
for N = 1:10
51+
v = PseudoBlockArray(1:sum(1:N),1:N)
52+
if iseven(N)
53+
@test DiskTrav(v) == [v; zeros(N+1)]
54+
else
55+
@test DiskTrav(v) == v
56+
end
57+
end
4958
end
5059

5160
@testset "Transform" begin
@@ -149,6 +158,7 @@ import ClassicalOrthogonalPolynomials: HalfWeighted
149158
WZ = Weighted(Zernike(1)) # Zernike(1) weighted by (1-r^2)
150159
Δ = Laplacian(axes(WZ,1))
151160
Δ_Z = Zernike(1) \* WZ)
161+
@test exp.(Δ_Z)[1:10,1:10] == exp.(Δ_Z[1:10,1:10])
152162

153163
xy = axes(WZ,1)
154164
x,y = first.(xy),last.(xy)
@@ -183,7 +193,9 @@ import ClassicalOrthogonalPolynomials: HalfWeighted
183193

184194

185195
R = Zernike(1) \ Zernike()
186-
@test Zernike()[xy,Block.(1:6)]' Zernike(1)[xy,Block.(1:6)]'*R[Block.(1:6),Block.(1:6)]
196+
197+
@test R[Block.(Base.OneTo(6)), Block.(Base.OneTo(7))] == R[Block.(1:6), Block.(1:7)]
198+
@test Zernike()[xy,Block.(1:6)]' Zernike(1)[xy,Block.(1:6)]'*R[Block.(1:6),Block.(1:6)]
187199
end
188200

189201
@testset "Lowering" begin
@@ -207,5 +219,10 @@ import ClassicalOrthogonalPolynomials: HalfWeighted
207219

208220
L = Zernike() \ Weighted(Zernike(1))
209221
@test w*Zernike(1)[xy,Block.(1:5)]' Zernike()[xy,Block.(1:7)]'*L[Block.(1:7),Block.(1:5)]
222+
223+
@test exp.(L)[1:10,1:10] == exp.(L[1:10,1:10])
224+
225+
L2 = Zernike(1) \ Weighted(Zernike(1))
226+
@test w*Zernike(1)[xy,Block.(1:5)]' Zernike(1)[xy,Block.(1:7)]'*L2[Block.(1:7),Block.(1:5)]
210227
end
211228
end

0 commit comments

Comments
 (0)