Skip to content

Commit 1f84896

Browse files
authored
Support expanding columns of 2D functions (#210)
* Support expanding columns of 2D functions * use invdiagtrav * Update Project.toml * add QR tests
1 parent 0bbdf26 commit 1f84896

File tree

5 files changed

+97
-24
lines changed

5 files changed

+97
-24
lines changed

Project.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,19 @@ MultivariateOrthogonalPolynomialsStatsBaseExt = "StatsBase"
3232
[compat]
3333
ArrayLayouts = "1.11"
3434
BandedMatrices = "1"
35-
BlockArrays = "1.0"
35+
BlockArrays = "1.7.3"
3636
BlockBandedMatrices = "0.13"
3737
ClassicalOrthogonalPolynomials = "0.15.8"
3838
ContinuumArrays = "0.20"
3939
DomainSets = "0.7"
4040
FastTransforms = "0.17"
4141
FillArrays = "1.0"
42-
HarmonicOrthogonalPolynomials = "0.7"
42+
HarmonicOrthogonalPolynomials = "0.7.1"
4343
InfiniteArrays = "0.15"
44-
InfiniteLinearAlgebra = "0.9, 0.10"
44+
InfiniteLinearAlgebra = "0.10"
4545
LazyArrays = "2.3.1"
46-
LazyBandedMatrices = "0.11.3"
47-
QuasiArrays = "0.13"
46+
LazyBandedMatrices = "0.11.7"
47+
QuasiArrays = "0.13.1"
4848
Random = "1"
4949
RecurrenceRelationships = "0.2"
5050
SpecialFunctions = "1, 2"

examples/christoffelsampling.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using ClassicalOrthogonalPolynomials, MultivariateOrthogonalPolynomials, StatsBase, Plots
2+
3+
4+
function christoffel(A)
5+
Q,R = qr(A)
6+
n = size(A,2)
7+
sum(expand(Q[:,k] .^2) for k=1:n)/n
8+
end
9+
10+
x,y = coordinates(ChebyshevInterval() ^ 2)
11+
n = 3
12+
A = hcat([@.(cos(k*x)cos(j*y)) for k=0:n, j=0:n]...)
13+
K = christoffel(A)

src/MultivariateOrthogonalPolynomials.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module MultivariateOrthogonalPolynomials
22
using StaticArrays: iszero
3-
using QuasiArrays: AbstractVector
3+
using QuasiArrays: AbstractVector, _getindex
44
using ClassicalOrthogonalPolynomials, FastTransforms, BlockBandedMatrices, BlockArrays, DomainSets,
55
QuasiArrays, StaticArrays, ContinuumArrays, InfiniteArrays, InfiniteLinearAlgebra,
66
LazyArrays, SpecialFunctions, LinearAlgebra, BandedMatrices, LazyBandedMatrices, ArrayLayouts,
@@ -18,10 +18,10 @@ import BlockArrays: block, blockindex, BlockSlice, viewblock, blockcolsupport, A
1818
import BlockBandedMatrices: _BandedBlockBandedMatrix, AbstractBandedBlockBandedMatrix, _BandedMatrix, blockbandwidths, subblockbandwidths
1919
import LinearAlgebra: factorize
2020
import LazyArrays: arguments, paddeddata, LazyArrayStyle, LazyLayout, PaddedLayout, applylayout, LazyMatrix, ApplyMatrix
21-
import LazyBandedMatrices: LazyBandedBlockBandedLayout, AbstractBandedBlockBandedLayout, AbstractLazyBandedBlockBandedLayout, _krontrav_axes, DiagTravLayout, invdiagtrav, ApplyBandedBlockBandedLayout, krontrav
21+
import LazyBandedMatrices: LazyBandedBlockBandedLayout, AbstractBandedBlockBandedLayout, AbstractLazyBandedBlockBandedLayout, _krontrav_axes, DiagTravLayout, diagtrav, invdiagtrav, ApplyBandedBlockBandedLayout, krontrav
2222
import InfiniteArrays: InfiniteCardinal, OneToInf
2323

24-
import ClassicalOrthogonalPolynomials: jacobimatrix, Weighted, orthogonalityweight, HalfWeighted, WeightedBasis, pad, recurrencecoefficients, clenshaw, weightedgrammatrix, Clenshaw, OPLayout
24+
import ClassicalOrthogonalPolynomials: jacobimatrix, Weighted, orthogonalityweight, HalfWeighted, WeightedBasis, pad, recurrencecoefficients, clenshaw, weightedgrammatrix, Clenshaw, OPLayout, normalized
2525
import HarmonicOrthogonalPolynomials: BivariateOrthogonalPolynomial, MultivariateOrthogonalPolynomial, Plan,
2626
AngularMomentum, angularmomentum, BlockOneTo, BlockRange1, interlace,
2727
MultivariateOPLayout, AbstractMultivariateOPLayout, MAX_PLOT_BLOCKS
@@ -38,7 +38,8 @@ export MultivariateOrthogonalPolynomial, BivariateOrthogonalPolynomial,
3838

3939
laplacian_axis(::Inclusion{<:SVector{2}}, A; dims...) = diff(A, (2,0); dims...) + diff(A, (0, 2); dims...)
4040
abslaplacian_axis(::Inclusion{<:SVector{2}}, A; dims...) = -(diff(A, (2,0); dims...) + diff(A, (0, 2); dims...))
41-
coordinates(P) = (first.(axes(P,1)), last.(axes(P,1)))
41+
coordinates(P::AbstractQuasiArray) = (first.(axes(P,1)), last.(axes(P,1)))
42+
coordinates(P::Domain) = coordinates(Inclusion(P))
4243

4344
function diff_layout(::AbstractBasisLayout, a, (k,j)::NTuple{2,Int}; dims...)
4445
(k < 0 || j < 0) && throw(ArgumentError("order must be non-negative"))

src/rect.jl

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ const RectPolynomial{T, PP} = KronPolynomial{2, T, PP}
2929
axes(P::KronPolynomial) = (Inclusion(×(map(domain, axes.(P.args, 1))...)), _krontrav_axes(axes.(P.args, 2)...))
3030

3131

32+
normalized(P::KronPolynomial) = KronPolynomial(map(normalized, P.args))
33+
3234
function getindex(P::RectPolynomial{T}, xy::StaticVector{2}, Jj::BlockIndex{1})::T where T
3335
a,b = P.args
3436
J,j = Int(block(Jj)),blockindex(Jj)
@@ -90,6 +92,7 @@ ApplyPlan(f, P) = ApplyPlan{eltype(P), typeof(f), typeof(P)}(f, P)
9092
*(A::ApplyPlan, B::AbstractArray) = A.f(A.plan*B)
9193

9294
basis_axes(d::Inclusion{<:Any,<:ProductDomain}, v) = KronPolynomial(map(d -> basis(Inclusion(d)),components(d.domain))...)
95+
basis_axes(d::Inclusion{<:Any,<:DomainSets.FixedIntervalProduct{N,T,D}}, v) where {N,T,D} = KronPolynomial(Fill(basis(Inclusion(D())), N))
9396

9497
struct TensorPlan{T, Plans}
9598
plans::Plans
@@ -109,12 +112,20 @@ function checkpoints(P::RectPolynomial)
109112
SVector.(x, y')
110113
end
111114

112-
function plan_transform(P::KronPolynomial{d,<:Any,<:Fill}, B::Tuple{Block{1}}, dims=1:1) where d
113-
@assert dims == 1
115+
function plan_transform(P::KronPolynomial{d,<:Any,<:Fill}, (B,)::Tuple{Block{1}}, dims=1:1) where d
116+
@assert only(dims) == 1
114117

115118
T = first(P.args)
116119
@assert d == 2
117-
ApplyPlan(DiagTrav, plan_transform(T, tuple(Fill(Int(B[1]),d)...)))
120+
ApplyPlan(diagtrav, plan_transform(T, tuple(Fill(Int(B),d)...)))
121+
end
122+
123+
function plan_transform(P::KronPolynomial{d,<:Any,<:Fill}, (B,n)::Tuple{Block{1},Int}, dims=1:1) where d
124+
@assert only(dims) == 1
125+
126+
T = first(P.args)
127+
@assert d == 2
128+
ApplyPlan(A -> diagtrav(A; dims=1:d), plan_transform(T, tuple(Fill(Int(B),d)...,n), 1:d))
118129
end
119130

120131
function grid(P::RectPolynomial, B::Block{1})
@@ -133,7 +144,7 @@ function plan_transform(P::KronPolynomial{d}, B::Tuple{Block{1}}, dims=1:1) wher
133144
@assert d == 2
134145
N = Int(B[1])
135146
Fx,Fy = plan_transform(P.args[1], (N,N), 1),plan_transform(P.args[2], (N,N), 2)
136-
ApplyPlan(DiagTrav, TensorPlan(Fx,Fy))
147+
ApplyPlan(diagtrav, TensorPlan(Fx,Fy))
137148
end
138149

139150
applylayout(::Type{typeof(*)}, ::Lay, ::DiagTravLayout) where Lay <: AbstractBasisLayout = ExpansionLayout{Lay}()
@@ -143,6 +154,11 @@ pad(C::DiagTrav, ::BlockedOneTo{Int,RangeCumsum{Int,OneToInf{Int}}}) = DiagTrav(
143154

144155
QuasiArrays.mul(A::BivariateOrthogonalPolynomial, b::DiagTrav) = ApplyQuasiArray(*, A, b)
145156

157+
158+
#########
159+
# evaluation
160+
########
161+
146162
function Base.unsafe_getindex(f::Mul{KronOPLayout{2},<:DiagTravLayout{<:PaddedLayout}}, 𝐱::SVector)
147163
P,c = f.A, f.B
148164
A,B = P.args
@@ -157,9 +173,9 @@ end
157173

158174
## Special Legendre case
159175

160-
function transform_ldiv(K::KronPolynomial{d,V,<:Fill{<:Legendre}}, f::Union{AbstractQuasiVector,AbstractQuasiMatrix}) where {d,V}
176+
function transform_ldiv(K::KronPolynomial{d,V,<:Fill{<:Legendre}}, f::AbstractQuasiVector) where {d,V}
161177
T = KronPolynomial{d}(Fill(ChebyshevT{V}(), size(K.args)...))
162-
dat = (T \ f).array
178+
dat = invdiagtrav(T \ f)
163179
DiagTrav(pad(FastTransforms.th_cheb2leg(paddeddata(dat)), axes(dat)...))
164180
end
165181

test/test_rect.jl

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using MultivariateOrthogonalPolynomials, ClassicalOrthogonalPolynomials, StaticArrays, LinearAlgebra, BlockArrays, FillArrays, Base64, LazyBandedMatrices, ArrayLayouts, Random, StatsBase, Test
2-
using ClassicalOrthogonalPolynomials: expand, coefficients, recurrencecoefficients
2+
using ClassicalOrthogonalPolynomials: expand, coefficients, recurrencecoefficients, normalized
33
using MultivariateOrthogonalPolynomials: weaklaplacian, ClenshawKron
4-
using ContinuumArrays: plotgridvalues, ExpansionLayout
4+
using ContinuumArrays: plotgridvalues, ExpansionLayout, basis, grid
55
using Base: oneto
66

77
Random.seed!(3242)
@@ -32,17 +32,47 @@ Random.seed!(3242)
3232
@test T²ₙ \ one.(x) == [1; zeros(14)]
3333
@test (T² \ x)[1:5] [0;1;zeros(3)]
3434

35-
f = expand(T², splat((x,y) -> exp(x*cos(y-0.1))))
36-
@test f[SVector(0.1,0.2)] exp(0.1*cos(0.1))
35+
f = splat((x,y) -> exp(x*cos(y-0.1)))
36+
𝐟 = expand(T², f)
37+
@test 𝐟[SVector(0.1,0.2)] f(SVector(0.1,0.2))
3738

3839
= RectPolynomial(Fill(U, 2))
39-
40-
@test f[SVector(0.1,0.2)] exp(0.1cos(0.1))
40+
𝐟 = expand(U², f)
41+
@test 𝐟[SVector(0.1,0.2)] f(SVector(0.1,0.2))
4142

4243
TU = RectPolynomial(T,U)
43-
x,F = ClassicalOrthogonalPolynomials.plan_grid_transform(TU, Block(5))
44-
f = expand(TU, splat((x,y) -> exp(x*cos(y-0.1))))
45-
@test f[SVector(0.1,0.2)] exp(0.1*cos(0.1))
44+
𝐟 = expand(TU, f)
45+
@test 𝐟[SVector(0.1,0.2)] f(SVector(0.1,0.2))
46+
47+
@testset "matrix" begin
48+
N = 10
49+
𝐱 = grid(T², Block(N))
50+
51+
@test T²[𝐱,1] == ones(N,N)
52+
@test T²[𝐱,2] == first.(𝐱)
53+
@test T²[𝐱,1:3] == T²[𝐱,Block.(Base.OneTo(2))] == T²[𝐱,[Block(1),Block(2)]] == [ones(N,N) ;;; first.(𝐱) ;;; last.(𝐱)]
54+
@test T²[𝐱,Block(1)] == [ones(N,N) ;;;]
55+
@test T²[𝐱,[1 2; 3 4]] [T²[𝐱,[1,3]] ;;;; T²[𝐱,[2,4]]]
56+
57+
58+
F = plan_transform(T², Block(N))
59+
@test F * f.(𝐱) transform(T², f)[Block.(1:N)] atol=1E-6
60+
61+
x,y = coordinates(ChebyshevInterval()^2)
62+
A = [one(x) x y]
63+
F = plan_transform(T², (Block(N), 3), 1)
64+
@test F * A[𝐱,:] [I(3); zeros(52,3)]
65+
66+
@test\ A [I(3); Zeros(∞,3)]
67+
68+
= RectPolynomial(Fill(Legendre(),2))
69+
F = plan_transform(P², (Block(N),3), 1)
70+
𝐱 = grid(P², Block(N))
71+
@test F * A[𝐱,:] P²[:,Block.(Base.OneTo(N))] \ A [I(3); Zeros(52,3)]
72+
73+
F = plan_transform(normalized(P²), (Block(N),3), 1)
74+
@test F * A[𝐱,:] normalized(P²)[:,Block.(Base.OneTo(N))] \ A [Diagonal([2, 2/sqrt(3), 2/sqrt(3)]); Zeros(52,3)]
75+
end
4676
end
4777

4878
@testset "Jacobi matrices" begin
@@ -254,4 +284,17 @@ Random.seed!(3242)
254284
@test sample(f) isa SVector
255285
@test sum(sample(f, 100_000))/100_000 [sum(x .* f)/sum(f),sum(y .* f)/sum(f)] rtol=1E-1
256286
end
287+
288+
@testset "qr" begin
289+
x,y = coordinates(ChebyshevInterval()^2)
290+
A = [one(x) cos.(x) cos.(y)]
291+
292+
@test A[SVector(0.1,0.2),1] 1
293+
@test A[SVector(0.1,0.2),1:3] A[SVector(0.1,0.2),:] [1,cos(0.1),cos(0.2)]
294+
295+
Q,R = qr(A)
296+
@test Q[SVector(0.1,0.2),1] 1/2
297+
@test Q[SVector(0.1,0.2),2] (cos(0.1) - sin(1))/sqrt(2cos(2) + sin(2))
298+
@test Q[SVector(0.1,0.2),3] (cos(0.2) - sin(1))/sqrt(2cos(2) + sin(2))
299+
end
257300
end

0 commit comments

Comments
 (0)