Skip to content

Commit 6b23e0d

Browse files
authored
inverse transforms (#59)
* inverse transforms * 2D inverse plans for PiecewisePolynomial * ContinuousPolynomial{1} * 2D ContinuousPolynomial{1} transform * C^(1) inverse transform * differentiation works * Dirichlet plan_transform * Update Project.toml * tests pass
1 parent 19d859d commit 6b23e0d

9 files changed

+427
-25
lines changed

Project.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "PiecewiseOrthogonalPolynomials"
22
uuid = "4461d12d-4663-4550-8580-cb764c85e20f"
33
authors = ["Sheehan Olver <[email protected]>"]
4-
version = "0.2.5"
4+
version = "0.3"
55

66
[deps]
77
ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
@@ -21,11 +21,11 @@ QuasiArrays = "c4ea9172-b204-11e9-377d-29865faadc5c"
2121

2222
[compat]
2323
ArrayLayouts = "1.0.12"
24-
BandedMatrices = "1"
24+
BandedMatrices = "1.3"
2525
BlockArrays = "0.16.25"
26-
BlockBandedMatrices = "0.12.2"
27-
ClassicalOrthogonalPolynomials = "0.12"
28-
ContinuumArrays = "0.17"
26+
BlockBandedMatrices = "0.12.9"
27+
ClassicalOrthogonalPolynomials = "0.12.1"
28+
ContinuumArrays = "0.17.1"
2929
FillArrays = "1.0"
3030
InfiniteArrays = "0.13"
3131
InfiniteLinearAlgebra = "0.7"

examples/2d.jl

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
###
2+
# This example shows how we can expand functions on a 2d rectangle using plan_transform.
3+
# This is a "manual mode" interface that might in the future be cleaned up to a user-facing
4+
# interface.
5+
###
6+
7+
8+
using PiecewiseOrthogonalPolynomials, Plots, Test
9+
using PiecewiseOrthogonalPolynomials: plan_grid_transform, plan_transform
10+
11+
r = range(-1,1; length=3)
12+
P = ContinuousPolynomial{0}(r)
13+
14+
# degrees in each dimension
15+
M,N = Block(80),Block(81)
16+
((x,y),pl) = plan_grid_transform(P, (M,N))
17+
18+
19+
20+
# x and y are matrices of points in each dimension, with the columns corresponding
21+
# to different elements . This is needed to leverage
22+
# fast 1D transforms acting on arrays. Thus to transform we make a tensor grid as
23+
# a 4-tensor. The way to do this is as follows:
24+
25+
f = (x,y) -> exp(x*cos(10y))
26+
F = f.(x, reshape(y, 1, 1, size(y)...))
27+
28+
# F contains our function samples. to plot we need to reduce back to vector/matrices.
29+
# But an issue is that the sample points are in the reverse order. Thus we need to do
30+
# some reordering:
31+
surface(vec(x[end:-1:1,:]), vec(y[end:-1:1,:]), reshape(F[end:-1:1,:,end:-1:1,:], length(x), length(y))')
32+
33+
34+
# We can now transform the samples to coefficient space:
35+
X = pl * F
36+
37+
# We approximate the original function to high-order
38+
@test P[0.1,Block(1):M]' * X * P[0.2,Block(1):N] f(0.1,0.2)
39+
40+
# Further we can transform back:
41+
@test pl \ X F
42+
43+
44+
# If we use ContinuousPolynomial{1} we can differentiate. This can be done as follows:
45+
46+
C = ContinuousPolynomial{1}(r)
47+
48+
# Compute coefficients of f in tensor product of C^(1)
49+
# This has one less block so we decrease M and N by one:
50+
51+
pl_C = plan_transform(C, (M-1,N-1))
52+
F_C = f.(x, reshape(y, 1, 1, size(y,1),:))
53+
X_C = pl_C * F_C
54+
55+
@test C[0.1,Block(1):M-1]' * X_C * C[0.2,Block(1):N-1] f(0.1,0.2)
56+
57+
# Make the 1D differentiation matrices from C to P:
58+
59+
D_x = (P \ diff(C))[Block(1):M, Block(1):M-1]
60+
D_y = (P \ diff(C))[Block(1):N, Block(1):N-1]
61+
62+
63+
# We now compare this to an analytical derivative at a poiunt
64+
65+
f_xy = (x,y) -> -10sin(10y)*exp(x*cos(10y)) - 10cos(10y)*x*sin(10y)*exp(x*cos(10y))
66+
@test P[0.1,Block(1):M]'*(D_x*X_C*D_y')*P[0.2,Block(1):N] f_xy(0.1,0.2)
67+
68+
# We can transform back to get the values on a large grid:
69+
70+
F_xy = pl \ (D_x*X_C*D_y')
71+
72+
@test F_xy f_xy.(x, reshape(y,1,1,size(y)...))
73+
74+
surface(vec(x[end:-1:1,:]), vec(y[end:-1:1,:]), reshape(F_xy[end:-1:1,:,end:-1:1,:], length(x), length(y))')
75+

src/PiecewiseOrthogonalPolynomials.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import BandedMatrices: _BandedMatrix
66
import BlockArrays: BlockSlice, block, blockindex, blockvec
77
import BlockBandedMatrices: _BandedBlockBandedMatrix, AbstractBandedBlockBandedMatrix, subblockbandwidths, blockbandwidths, AbstractBandedBlockBandedLayout, layout_replace_in_print_matrix
88
import ClassicalOrthogonalPolynomials: grid, plotgrid, ldiv, pad, adaptivetransform_ldiv, Plan, singularities, basis_singularities, singularitiesbroadcast
9-
import ContinuumArrays: @simplify, factorize, TransformFactorization, AbstractBasisLayout, MemoryLayout, layout_broadcasted, ExpansionLayout, basis, plan_transform, plan_grid_transform, grammatrix, weaklaplacian
9+
import ContinuumArrays: @simplify, factorize, TransformFactorization, AbstractBasisLayout, MemoryLayout, layout_broadcasted, ExpansionLayout, basis, plan_transform, plan_grid_transform, grammatrix, weaklaplacian, MulPlan, InvPlan
1010
import LazyArrays: paddeddata
1111
import LazyBandedMatrices: BlockBroadcastMatrix, BlockVec, BandedLazyLayouts, AbstractLazyBandedBlockBandedLayout, UpperOrLowerTriangular
12-
import Base: axes, getindex, +, -, *, /, ==, \, OneTo, oneto, replace_in_print_matrix, copy, diff, getproperty, adjoint, transpose, tail, _sum
12+
import Base: size, axes, getindex, +, -, *, /, ==, \, OneTo, oneto, replace_in_print_matrix, copy, diff, getproperty, adjoint, transpose, tail, _sum, inv
1313
import LinearAlgebra: BlasInt
1414
import MatrixFactorizations: reversecholcopy
1515

src/continuouspolynomial.jl

Lines changed: 105 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,113 @@ plan_transform(P::ContinuousPolynomial{0}, szs::NTuple{N,Union{Int,Block{1}}}, d
5151
for grd in (:grid, :plotgrid)
5252
@eval begin
5353
$grd(C::ContinuousPolynomial, n::Block{1}) = $grd(PiecewisePolynomial(C), n)
54-
$grd(C::ContinuousPolynomial, n::Int) = $grd(PiecewisePolynomial(C), n)
54+
$grd(C::ContinuousPolynomial, n::Int) = $grd(C, findblock(axes(C,2), n))
5555
end
5656
end
5757

58+
struct ContinuousPolynomialTransform{T, Pl<:Plan{T}, RRs, Dims} <: Plan{T}
59+
legendretransform::Pl
60+
R::RRs
61+
dims::Dims
62+
end
63+
64+
65+
66+
67+
function plan_transform(C::ContinuousPolynomial{1,T}, Ns::NTuple{N,Block{1}}, dims=ntuple(identity,Val(N))) where {N,T}
68+
P = Legendre{T}()
69+
W = Weighted(Jacobi{T}(1,1))
70+
# TODO: this is unnecessarily not triangular which makes it much slower than necessary and prevents in-place.
71+
# However, the speed of the Legendre transform will far exceed this so its not high priority.
72+
# Probably using Ultraspherical(-1/2) would be better.
73+
= [[T[1 1; -1 1]/2; Zeros{T}(∞,2)] (P \ W)]
74+
ContinuousPolynomialTransform(plan_transform(ContinuousPolynomial{0}(C), Ns .+ 1, dims).F,
75+
InvPlan(map(N -> R̃[1:Int(N),1:Int(N)], Ns .+ 1), _doubledims(dims...)),
76+
dims)
77+
end
78+
79+
_tensorsize2contblocks() = ()
80+
_tensorsize2contblocks(M,N, Ns...) = (Vcat(N+1, Fill(N, M-2)), _tensorsize2contblocks(Ns...)...)
81+
82+
83+
function *(Pl::ContinuousPolynomialTransform{T,<:Any,<:Any,Int}, X::AbstractMatrix{T}) where T
84+
dat = Pl.R * (Pl.legendretransform*X)
85+
cfs = PseudoBlockArray{T}(undef, _tensorsize2contblocks(size(X)...)...)
86+
dims = Pl.dims
87+
@assert dims == 1
88+
89+
M,N = size(X,1), size(X,2)
90+
if size(dat,1) 1
91+
cfs[Block(1)[1]] = dat[1,1]
92+
for j = 1:N-1
93+
isapprox(dat[2,j], dat[1,j+1]; atol=1000*M*eps()) || throw(ArgumentError("Discontinuity in data on order of $(abs(dat[2,j]- dat[1,j+1]))."))
94+
end
95+
for j = 1:N
96+
cfs[Block(1)[j+1]] = dat[2,j]
97+
end
98+
end
99+
cfs[Block.(2:M-1)] .= vec(dat[3:end,:]')
100+
cfs
101+
end
102+
103+
function \(Pl::ContinuousPolynomialTransform{T,<:Any,<:Any,Int}, cfs::AbstractVector{T}) where T
104+
dims = Pl.dims
105+
@assert dims == 1
106+
107+
M,N = blocksize(cfs,1)+1, size(axes(cfs,1)[Block(1)],1)-1
108+
dat = Matrix{T}(undef, M, N)
109+
110+
if M 1
111+
dat[1,1] = cfs[Block(1)[1]]
112+
for j = 1:N-1
113+
dat[2,j] = dat[1,j+1] = cfs[Block(1)[j+1]]
114+
end
115+
dat[2,end] = cfs[Block(1)[N+1]]
116+
end
117+
118+
for j = 1:N, k = 3:M
119+
dat[k,j] = cfs[Block(k-1)[j]]
120+
end
121+
122+
Pl.legendretransform \ (Pl.R \ dat)
123+
end
124+
125+
126+
127+
function _contpolyinds2blocks(k, j)
128+
k == 1 && return Block(1)[j]
129+
k == 2 && return Block(1)[j+1]
130+
Block(k-1)[j]
131+
end
132+
function *(Pl::ContinuousPolynomialTransform{T}, X::AbstractArray{T,4}) where T
133+
dat = Pl.R * (Pl.legendretransform*X)
134+
cfs = PseudoBlockArray{T}(undef, _tensorsize2contblocks(size(X)...)...)
135+
dims = Pl.dims
136+
@assert dims == (1,2)
137+
138+
M,N,O,P = size(X)
139+
for k = 1:M, j = 1:N, l = 1:O, m = 1:P
140+
cfs[_contpolyinds2blocks(k,j), _contpolyinds2blocks(l,m)] = dat[k,j,l,m]
141+
end
142+
cfs
143+
end
144+
145+
function \(Pl::ContinuousPolynomialTransform{T}, cfs::AbstractMatrix{T}) where T
146+
M,N = blocksize(cfs,1)+1, size(axes(cfs,1)[Block(1)],1)-1
147+
O,P = blocksize(cfs,2)+1, size(axes(cfs,2)[Block(1)],1)-1
148+
149+
dat = Array{T}(undef, M, N, O, P)
150+
dims = Pl.dims
151+
@assert dims == (1,2)
152+
153+
for k = 1:M, j = 1:N, l = 1:O, m = 1:P
154+
dat[k,j,l,m] = cfs[_contpolyinds2blocks(k,j), _contpolyinds2blocks(l,m)]
155+
end
156+
157+
Pl.legendretransform \ (Pl.R \ dat)
158+
end
159+
160+
58161
function adaptivetransform_ldiv(Q::ContinuousPolynomial{1,V}, f::AbstractQuasiVector) where V
59162
T = promote_type(V, eltype(f))
60163
C₀ = ContinuousPolynomial{0,V}(Q)
@@ -88,12 +191,7 @@ end
88191
adaptivetransform_ldiv(Q::ContinuousPolynomial{1,V}, f::AbstractQuasiMatrix) where V =
89192
BlockBroadcastArray(hcat, (Q \ f[:,j] for j = axes(f,2))...)
90193

91-
function grid(V::SubQuasiArray{T,2,<:ContinuousPolynomial{1},<:Tuple{Inclusion,BlockSlice}}) where {T}
92-
P = parent(V)
93-
_, JR = parentindices(V)
94-
pts = P.points
95-
grid(view(PiecewisePolynomial(Weighted(Jacobi{T}(1, 1)), pts), :, JR))
96-
end
194+
grid(P::ContinuousPolynomial{1}, M::Block{1}) = grid(ContinuousPolynomial{0}(P), M + 1)
97195

98196
#######
99197
# Conversion

src/dirichlet.jl

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,120 @@ function \(P::ContinuousPolynomial, Q::DirichletPolynomial)
4848
(P \ C) * (C \ Q)
4949
end
5050

51+
###
52+
# transforms
53+
###
54+
5155

5256
function adaptivetransform_ldiv(Q::DirichletPolynomial{V}, f::AbstractQuasiVector) where V
5357
C = ContinuousPolynomial{1}(Q)
5458
(Q \ C) * (C \ f)
5559
end
5660

61+
struct DirichletPolynomialTransform{T, Pl<:Plan{T}, RRs, Dims} <: Plan{T}
62+
legendretransform::Pl
63+
R::RRs
64+
dims::Dims
65+
end
66+
67+
DirichletPolynomialTransform(pl::ContinuousPolynomialTransform) =
68+
DirichletPolynomialTransform(pl.legendretransform, pl.R, pl.dims)
69+
70+
plan_transform(C::DirichletPolynomial{T}, Ns::NTuple{N,Block{1}}, dims=ntuple(identity,Val(N))) where {N,T} =
71+
DirichletPolynomialTransform(plan_transform(ContinuousPolynomial{1}(C), Ns, dims))
72+
73+
_tensorsize2dirichletblocks() = ()
74+
_tensorsize2dirichletblocks(M,N, Ns...) = (Vcat(N-1, Fill(N, M-2)), _tensorsize2dirichletblocks(Ns...)...)
75+
76+
77+
function *(Pl::DirichletPolynomialTransform{T,<:Any,<:Any,Int}, X::AbstractMatrix{T}) where T
78+
dat = Pl.R * (Pl.legendretransform*X)
79+
cfs = PseudoBlockArray{T}(undef, _tensorsize2dirichletblocks(size(X)...)...)
80+
dims = Pl.dims
81+
@assert dims == 1
82+
83+
M,N = size(X,1), size(X,2)
84+
if size(dat,1) 1
85+
for j = 1:N-1
86+
cfs[Block(1)[j]] = dat[2,j]
87+
end
88+
end
89+
cfs[Block.(2:M-1)] .= vec(dat[3:end,:]')
90+
cfs
91+
end
92+
93+
function \(Pl::DirichletPolynomialTransform{T,<:Any,<:Any,Int}, cfs::AbstractVector{T}) where T
94+
dims = Pl.dims
95+
@assert dims == 1
96+
97+
M,N = blocksize(cfs,1)+1, size(axes(cfs,1)[Block(1)],1)+1
98+
dat = Matrix{T}(undef, M, N)
99+
100+
if M 1
101+
dat[1,1] = zero(T)
102+
for j = 1:N-1
103+
dat[2,j] = dat[1,j+1] = cfs[Block(1)[j]]
104+
end
105+
dat[2,end] = zero(T)
106+
end
107+
108+
for j = 1:N, k = 3:M
109+
dat[k,j] = cfs[Block(k-1)[j]]
110+
end
111+
112+
Pl.legendretransform \ (Pl.R \ dat)
113+
end
114+
115+
116+
117+
function _dirichletpolyinds2blocks(k, j)
118+
k == 1 && return Block(1)[j-1]
119+
k == 2 && return Block(1)[j]
120+
Block(k-1)[j]
121+
end
122+
function *(Pl::DirichletPolynomialTransform{T}, X::AbstractArray{T,4}) where T
123+
dat = Pl.R * (Pl.legendretransform*X)
124+
cfs = PseudoBlockArray{T}(undef, _tensorsize2dirichletblocks(size(X)...)...)
125+
dims = Pl.dims
126+
@assert dims == (1,2)
127+
128+
M,N,O,P = size(X)
129+
for k = 1:M, j = 1:N, l = 1:O, m = 1:P
130+
k == j == 1 && continue
131+
k == 2 && j == N && continue
132+
l == m == 1 && continue
133+
l == 2 && m == P && continue
134+
cfs[_dirichletpolyinds2blocks(k,j), _dirichletpolyinds2blocks(l,m)] = dat[k,j,l,m]
135+
end
136+
cfs
137+
end
138+
139+
function \(Pl::DirichletPolynomialTransform{T}, cfs::AbstractMatrix{T}) where T
140+
M,N = blocksize(cfs,1)+1, size(axes(cfs,1)[Block(1)],1)+1
141+
O,P = blocksize(cfs,2)+1, size(axes(cfs,2)[Block(1)],1)+1
142+
143+
dat = Array{T}(undef, M, N, O, P)
144+
dims = Pl.dims
145+
@assert dims == (1,2)
146+
147+
for k = 1:M, j = 1:N, l = 1:O, m = 1:P
148+
if k == j == 1 ||
149+
(k == 2 && j == N) ||
150+
l == m == 1 ||
151+
(l == 2 && m == P)
152+
dat[k,j,l,m] = zero(T)
153+
else
154+
dat[k,j,l,m] = cfs[_dirichletpolyinds2blocks(k,j), _dirichletpolyinds2blocks(l,m)]
155+
end
156+
end
157+
158+
Pl.legendretransform \ (Pl.R \ dat)
159+
end
160+
161+
###
162+
# weaklaplacian
163+
###
164+
57165
function weaklaplacian(C::DirichletPolynomial{T,<:AbstractRange}) where T
58166
r = C.points
59167
N = length(r)
@@ -102,8 +210,8 @@ end
102210

103211
for grd in (:grid, :plotgrid)
104212
@eval begin
105-
$grd(C::DirichletPolynomial, n::Integer) = $grd(PiecewisePolynomial(C), n)
106-
$grd(C::DirichletPolynomial, n::Block{1}) = $grd(PiecewisePolynomial(C), n)
213+
$grd(C::DirichletPolynomial, n::Integer) = $grd(ContinuousPolynomial{1}(C.points), n)
214+
$grd(C::DirichletPolynomial, n::Block{1}) = $grd(ContinuousPolynomial{1}(C.points), n)
107215
end
108216
end
109217

0 commit comments

Comments
 (0)