Skip to content

Commit bbb032f

Browse files
authored
Introduce plan_transform (#134)
* Introduce plan_transform * v0.11 * Update bases.jl * Update bases.jl * Add transform and expand * Derivative -> Diff * Don't call using LazyArrays * Update test_splines.jl * Update bases.jl * fix FactorizationPlan * Update Project.toml * Diff -> Derivative * Diff -> Derivative * Update ci.yml * Update Project.toml * Update Project.toml * Revert "Update Project.toml" This reverts commit c1fe83e. * Revert "Update Project.toml" This reverts commit f4ebd28. * increase coverage * Add plan_transform tests * Increase coverage * Update test_chebyshev.jl * split plan_grid_transform and plan_transform
1 parent 21561bc commit bbb032f

File tree

15 files changed

+200
-56
lines changed

15 files changed

+200
-56
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ jobs:
1212
version:
1313
- '1.6'
1414
- '1'
15-
- '^1.8.0-0'
1615
os:
1716
- ubuntu-latest
1817
- macOS-latest

Project.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
name = "ContinuumArrays"
22
uuid = "7ae1f121-cc2c-504b-ac30-9b923412ae5c"
3-
version = "0.10.3"
3+
version = "0.11"
44

55
[deps]
66
ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
77
BandedMatrices = "aae01518-5342-5314-be14-df237901396f"
88
BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e"
9+
DomainSets = "5b8099bc-c8ec-5219-889f-1d9e522a28bf"
910
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
1011
InfiniteArrays = "4858937d-0d70-526a-a4dd-2d5cb5dd786c"
1112
Infinities = "e1ba4f0e-776d-440f-acd9-e1d2e9742647"
@@ -20,13 +21,14 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
2021
ArrayLayouts = "0.7.7, 0.8"
2122
BandedMatrices = "0.16, 0.17"
2223
BlockArrays = "0.16"
23-
FastTransforms = "0.13"
24+
DomainSets = "0.5"
25+
FastTransforms = "0.13, 0.14"
2426
FillArrays = "0.12, 0.13"
2527
InfiniteArrays = "0.12"
2628
Infinities = "0.1"
2729
IntervalSets = "0.5, 0.6, 0.7"
2830
LazyArrays = "0.22"
29-
QuasiArrays = "0.9"
31+
QuasiArrays = "0.9.3"
3032
RecipesBase = "1.0"
3133
StaticArrays = "1.0"
3234
julia = "1.6"

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,13 @@ julia> f[0.1]
6969

7070
Creating a finite element method is possible using standard array terminology.
7171
We always take the Lebesgue inner product associated with an axes, so in this
72-
case the mass matrix is just `L'L`. Combined with a derivative operator allows
72+
case the mass matrix is just `L'L`. Combined with a differentiation operator allows
7373
us to form the weak Laplacian.
7474
```julia
7575
julia> B = L[:,2:end-1]; # drop boundary terms to impose zero Dirichlet
7676

77+
julia> D = Derivative(L); # Differentiation operator
78+
7779
julia> Δ = (D*B)'D*B # weak Laplacian
7880
4×4 BandedMatrices.BandedMatrix{Float64,Array{Float64,2},Base.OneTo{Int64}}:
7981
10.0 -5.0

examples/heat.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ x = axes(L,1)
99
D = Derivative(x)
1010
M = L'L
1111
Δ = -((D*L)'D*L)
12-
u0 = copy(L \ exp.(x))
12+
u0 = L \ exp.(x)
1313

14-
heat(u,(M,Δ),t) = M\*u)
14+
heat(u,(M,Δ),t) = M \ *u)
1515
prob = ODEProblem(heat,u0,(0.0,1.0),(M,Δ))
1616
sol = solve(prob,Tsit5(),reltol=1e-8,abstol=1e-8)
1717

examples/poisson.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ using ContinuumArrays, Plots
55
####
66

77
L = LinearSpline(range(0,1; length=10_000))[:,2:end-1]
8-
x = axes(L,1)
9-
D = Derivative(x)
8+
D = Derivative(L)
109
Δ = -((D*L)'D*L)
1110
M = L'L
12-
f = L \ exp.(x)
11+
f = expand(L, exp)
1312
u = L *\ (M*f))
1413
plot(u)
1514

src/ContinuumArrays.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module ContinuumArrays
2-
using IntervalSets, LinearAlgebra, LazyArrays, FillArrays, BandedMatrices, QuasiArrays, Infinities, InfiniteArrays, StaticArrays, BlockArrays, RecipesBase
2+
using IntervalSets, DomainSets, LinearAlgebra, LazyArrays, FillArrays, BandedMatrices, QuasiArrays, Infinities, InfiniteArrays, StaticArrays, BlockArrays, RecipesBase
33
import Base: @_inline_meta, @_propagate_inbounds_meta, axes, size, getindex, convert, prod, *, /, \, +, -, ==, ^,
44
IndexStyle, IndexLinear, ==, OneTo, _maybetail, tail, similar, copyto!, copy, diff,
55
first, last, show, isempty, findfirst, findlast, findall, Slice, union, minimum, maximum, sum, _sum,
@@ -21,14 +21,16 @@ import QuasiArrays: cardinality, checkindex, QuasiAdjoint, QuasiTranspose, Inclu
2121
AbstractQuasiFill, UnionDomain, __sum, _cumsum, __cumsum, applylayout, _equals, layout_broadcasted, PolynomialLayout
2222
import InfiniteArrays: Infinity, InfAxes
2323

24-
export Spline, LinearSpline, HeavisideSpline, DiracDelta, Derivative, ℵ₁, Inclusion, Basis, grid, plotgrid, affine, ..
24+
export Spline, LinearSpline, HeavisideSpline, DiracDelta, Derivative, ℵ₁, Inclusion, Basis, grid, plotgrid, affine, .., transform, expand, plan_transform
2525

2626

2727

2828
const QMul2{A,B} = Mul{<:Any, <:Any, <:A,<:B}
2929
const QMul3{A,B,C} = Mul{<:AbstractQuasiArrayApplyStyle, <:Tuple{A,B,C}}
3030

3131
cardinality(::AbstractInterval) = ℵ₁
32+
cardinality(::Union{FullSpace{<:AbstractFloat},EuclideanDomain,DomainSets.RealNumbers,DomainSets.ComplexNumbers}) = ℵ₁
33+
cardinality(::Union{DomainSets.Integers,DomainSets.Rationals,DomainSets.NaturalNumbers}) = ℵ₀
3234

3335
Inclusion(d::AbstractInterval{T}) where T = Inclusion{float(T)}(d)
3436
first(S::Inclusion{<:Any,<:AbstractInterval}) = leftendpoint(S.domain)
@@ -90,7 +92,6 @@ checkpoints(A::AbstractQuasiMatrix) = checkpoints(axes(A,1))
9092

9193
include("operators.jl")
9294
include("bases/bases.jl")
93-
include("basisconcat.jl")
9495

9596
include("plotting.jl")
9697

src/bases/bases.jl

Lines changed: 105 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,12 @@ _grid(::WeightedBasisLayouts, P) = grid(unweighted(P))
156156
grid(P) = _grid(MemoryLayout(P), P)
157157

158158

159-
struct TransformFactorization{T,Grid,Plan,IPlan} <: Factorization{T}
159+
struct TransformFactorization{T,Grid,Plan} <: Factorization{T}
160160
grid::Grid
161161
plan::Plan
162-
iplan::IPlan
163162
end
164163

165-
TransformFactorization{T}(grid, plan) where T = TransformFactorization{T,typeof(grid),typeof(plan),Nothing}(grid, plan, nothing)
164+
TransformFactorization{T}(grid, plan) where T = TransformFactorization{T,typeof(grid),typeof(plan)}(grid, plan)
166165

167166
"""
168167
TransformFactorization(grid, plan)
@@ -173,44 +172,84 @@ associates a planned transform with a grid. That is, if `F` is a `TransformFacto
173172
TransformFactorization(grid, plan) = TransformFactorization{promote_type(eltype(eltype(grid)),eltype(plan))}(grid, plan)
174173

175174

176-
TransformFactorization{T}(grid, ::Nothing, iplan) where T = TransformFactorization{T,typeof(grid),Nothing,typeof(iplan)}(grid, nothing, iplan)
177-
178-
"""
179-
TransformFactorization(grid, nothing, iplan)
180-
181-
associates a planned inverse transform with a grid. That is, if `F` is a `TransformFactorization`, then
182-
`F \\ f` is equivalent to `F.iplan \\ f[F.grid]`.
183-
"""
184-
TransformFactorization(grid, ::Nothing, iplan) = TransformFactorization{promote_type(eltype(eltype(grid)),eltype(iplan))}(grid, nothing, iplan)
185175

186176
grid(T::TransformFactorization) = T.grid
187177
function size(T::TransformFactorization, k)
188178
@assert k == 2 # TODO: make consistent
189179
size(T.plan,1)
190180
end
191181

192-
function size(T::TransformFactorization{<:Any,<:Any,Nothing}, k)
193-
@assert k == 2 # TODO: make consistent
194-
size(T.iplan,2)
182+
183+
\(a::TransformFactorization, b::AbstractQuasiVector) = a.plan * convert(Array, b[a.grid])
184+
\(a::TransformFactorization, b::AbstractQuasiMatrix) = a.plan * convert(Array, b[a.grid,:])
185+
186+
"""
187+
InvPlan(factorization, dims)
188+
189+
Takes a factorization and supports it applied to different dimensions.
190+
"""
191+
struct InvPlan{T, Fact, Dims} # <: Plan{T} We don't depend on AbstractFFTs
192+
factorization::Fact
193+
dims::Dims
195194
end
196195

196+
InvPlan(fact, dims) = InvPlan{eltype(fact), typeof(fact), typeof(dims)}(fact, dims)
197197

198-
\(a::TransformFactorization{<:Any,<:Any,Nothing}, b::AbstractQuasiVector{T}) where T = a.iplan \ convert(Array{T}, b[a.grid])
199-
\(a::TransformFactorization, b::AbstractQuasiVector) = a.plan * convert(Array, b[a.grid])
200-
\(a::TransformFactorization{<:Any,<:Any,Nothing}, b::AbstractVector) = a.iplan \ b
201-
\(a::TransformFactorization, b::AbstractVector) = a.plan * b
202-
ldiv!(ret::AbstractVecOrMat, a::TransformFactorization, b::AbstractVecOrMat) = mul!(ret, a.plan, b)
203-
ldiv!(ret::AbstractVecOrMat, a::TransformFactorization{<:Any,<:Any,Nothing}, b::AbstractVecOrMat) = ldiv!(ret, a.iplan, b)
198+
size(F::InvPlan, k...) = size(F.factorization, k...)
199+
200+
201+
function *(P::InvPlan{<:Any,<:Any,Int}, x::AbstractVector)
202+
@assert P.dims == 1
203+
P.factorization \ x
204+
end
205+
206+
function *(P::InvPlan{<:Any,<:Any,Int}, X::AbstractMatrix)
207+
if P.dims == 1
208+
P.factorization \ X
209+
else
210+
@assert P.dims == 2
211+
permutedims(P.factorization \ permutedims(X))
212+
end
213+
end
204214

205-
\(a::TransformFactorization{<:Any,<:Any,Nothing}, b::AbstractQuasiMatrix{T}) where T = a \ convert(Array{T}, b[a.grid,:])
206-
\(a::TransformFactorization, b::AbstractQuasiMatrix) = a \ convert(Array, b[a.grid,:])
207-
\(a::TransformFactorization, b::AbstractMatrix) = ldiv!(Array{promote_type(eltype(a),eltype(b))}(undef,size(a,2),size(b,2)), a, b)
215+
function *(P::InvPlan{<:Any,<:Any,Int}, X::AbstractArray{<:Any,3})
216+
Y = similar(X)
217+
if P.dims == 1
218+
for j in axes(X,3)
219+
Y[:,:,j] = P.factorization \ X[:,:,j]
220+
end
221+
elseif P.dims == 2
222+
for k in axes(X,1)
223+
Y[k,:,:] = P.factorization \ X[k,:,:]
224+
end
225+
else
226+
@assert P.dims == 3
227+
for k in axes(X,1), j in axes(X,2)
228+
Y[k,j,:] = P.factorization \ X[k,j,:]
229+
end
230+
end
231+
Y
232+
end
208233

209-
function _factorize(::AbstractBasisLayout, L, dims...; kws...)
234+
function *(P::InvPlan, X::AbstractArray)
235+
for d in P.dims
236+
X = InvPlan(P.factorization, d) * X
237+
end
238+
X
239+
end
240+
241+
242+
function plan_grid_transform(L, arr, dims=1:ndims(arr))
210243
p = grid(L)
211-
TransformFactorization(p, nothing, factorize(L[p,:]))
244+
p, InvPlan(factorize(L[p,:]), dims)
212245
end
213246

247+
plan_transform(P, arr, dims...) = plan_grid_transform(P, arr, dims...)[2]
248+
249+
_factorize(::AbstractBasisLayout, L, dims...; kws...) =
250+
TransformFactorization(plan_grid_transform(L, Array{eltype(L)}(undef, size(L,2), dims...), 1)...)
251+
252+
214253

215254
"""
216255
ProjectionFactorization(F, inds)
@@ -225,9 +264,22 @@ end
225264

226265
\(a::ProjectionFactorization, b::AbstractQuasiVector) = (a.F \ b)[a.inds]
227266
\(a::ProjectionFactorization, b::AbstractQuasiMatrix) = (a.F \ b)[a.inds,:]
228-
\(a::ProjectionFactorization, b::AbstractVector) = (a.F \ b)[a.inds]
229267

230-
_factorize(::SubBasisLayout, L, dims...; kws...) = ProjectionFactorization(factorize(parent(L), dims...; kws...), parentindices(L)[2])
268+
269+
270+
# if parent is finite dimensional default to its transform and project down
271+
_sub_factorize(::Tuple{Any,Int}, (kr,jr), L, dims...; kws...) = ProjectionFactorization(factorize(parent(L), dims...; kws...), jr)
272+
_sub_factorize(::Tuple{Any,Int}, (kr,jr)::Tuple{Any,OneTo}, L, dims...; kws...) = ProjectionFactorization(factorize(parent(L), dims...; kws...), jr)
273+
274+
# ∞-dimensional parents need to use transforms. For now we assume the size of the transform is equal to the size of the truncation
275+
_sub_factorize(::Tuple{Any,Any}, (kr,jr)::Tuple{Any,OneTo}, L, dims...; kws...) =
276+
TransformFactorization(plan_grid_transform(parent(L), Array{eltype(L)}(undef, last(jr), dims...), 1)...)
277+
278+
# If jr is not OneTo we project
279+
_sub_factorize(::Tuple{Any,Any}, (kr,jr), L, dims...; kws...) =
280+
ProjectionFactorization(factorize(parent(L)[:,OneTo(maximum(jr))]), jr)
281+
282+
_factorize(::SubBasisLayout, L, dims...; kws...) = _sub_factorize(size(parent(L)), parentindices(L), L, dims...; kws...)
231283

232284

233285
"""
@@ -260,6 +312,29 @@ plan_ldiv(A, B::AbstractQuasiMatrix) = factorize(A, size(B,2))
260312
transform_ldiv(A::AbstractQuasiArray{T}, B::AbstractQuasiArray{V}, _) where {T,V} = plan_ldiv(A, B) \ B
261313
transform_ldiv(A, B) = transform_ldiv(A, B, size(A))
262314

315+
316+
"""
317+
transform(A, f)
318+
319+
finds the coefficients of a function `f` expanded in a basis defined as the columns of a quasi matrix `A`.
320+
It is equivalent to
321+
```
322+
A \\ f.(axes(A,1))
323+
```
324+
"""
325+
transform(A, f) = A \ f.(axes(A,1))
326+
327+
"""
328+
expand(A, f)
329+
330+
expands a function `f` im a basis defined as the columns of a quasi matrix `A`.
331+
It is equivalent to
332+
```
333+
A / A \\ f.(axes(A,1))
334+
```
335+
"""
336+
expand(A, f) = A * transform(A, f)
337+
263338
copy(L::Ldiv{<:AbstractBasisLayout}) = transform_ldiv(L.A, L.B)
264339
# TODO: redesign to use simplifiable(\, A, B)
265340
copy(L::Ldiv{<:AbstractBasisLayout,ApplyLayout{typeof(*)},<:Any,<:AbstractQuasiVector}) = transform_ldiv(L.A, L.B)
@@ -573,4 +648,6 @@ end
573648
__sum(::ExpansionLayout, A, dims) = __sum(ApplyLayout{typeof(*)}(), A, dims)
574649
__cumsum(::ExpansionLayout, A, dims) = __cumsum(ApplyLayout{typeof(*)}(), A, dims)
575650

651+
include("basisconcat.jl")
652+
include("basiskron.jl")
576653
include("splines.jl")
File renamed without changes.

src/bases/basiskron.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
struct KronBasisLayout <: AbstractBasisLayout end
2+
3+
QuasiArrays.kronlayout(::AbstractBasisLayout...) = KronBasisLayout()

src/bases/splines.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ end
9494
end
9595

9696

97-
## Derivative
97+
## Differentiation
9898
function copyto!(dest::MulQuasiMatrix{<:Any,<:Tuple{<:HeavisideSpline,<:Any}},
9999
M::QMul2{<:Derivative,<:LinearSpline})
100100
D, L = M.A, M.B

src/operators.jl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,24 @@ show(io::IO, δ::DiracDelta) = print(io, "δ at $(δ.x) over $(axes(δ,1))")
9292
show(io::IO, ::MIME"text/plain", δ::DiracDelta) = show(io, δ)
9393

9494
#########
95-
# Derivative
95+
# Differentiation
9696
#########
9797

98+
"""
99+
Derivative(axis)
98100
101+
represents the differentiation (or finite-differences) operator on the
102+
specified axis.
103+
"""
99104
struct Derivative{T,D} <: LazyQuasiMatrix{T}
100105
axis::Inclusion{T,D}
101106
end
102107

103108
Derivative{T}(axis::Inclusion{<:Any,D}) where {T,D} = Derivative{T,D}(axis)
104109
Derivative{T}(domain) where T = Derivative{T}(Inclusion(domain))
105110

111+
Derivative(L::AbstractQuasiMatrix) = Derivative(axes(L,1))
112+
106113
function summary(io::IO, D::Derivative)
107114
print(io, "Derivative(")
108115
summary(io,D.axis)

test/runtests.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using ContinuumArrays, QuasiArrays, LazyArrays, IntervalSets, FillArrays, LinearAlgebra, BandedMatrices, InfiniteArrays, Test, Base64, RecipesBase
1+
using ContinuumArrays, QuasiArrays, IntervalSets, DomainSets, FillArrays, LinearAlgebra, BandedMatrices, InfiniteArrays, Test, Base64, RecipesBase
22
import ContinuumArrays: ℵ₁, materialize, AffineQuasiVector, BasisLayout, AdjointBasisLayout, SubBasisLayout, ℵ₁,
33
MappedBasisLayout, AdjointMappedBasisLayout, MappedWeightedBasisLayout, TransformFactorization, Weight, WeightedBasisLayout, SubWeightedBasisLayout, WeightLayout,
44
basis, invmap, Map, checkpoints, _plotgrid, mul
@@ -12,6 +12,9 @@ import LazyArrays: MemoryLayout, ApplyStyle, Applied, colsupport, arguments, App
1212
@test eltype(x) == Float64
1313
@test x[0.1] 0.1
1414
@test x[0] x[0.0] 0.0
15+
16+
@test size(Inclusion(ℝ),1) == ℵ₁
17+
@test size(Inclusion(ℤ),1) == ℵ₀
1518
end
1619

1720
@testset "broadcast" begin

test/test_basiskron.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using ContinuumArrays, QuasiArrays, StaticArrays, Test
2+
3+
@testset "Basis Kron" begin
4+
L = LinearSpline(range(0,1; length=4))
5+
K = QuasiKron(L, L)
6+
7+
xy = axes(K,1)
8+
f = xy -> ((x,y) = xy; exp(x*cos(y)))
9+
K \ f.(xy)
10+
11+
end

test/test_chebyshev.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ ContinuumArrays.invmap(::InvQuadraticMap{T}) where T = QuadraticMap{T}()
5252
@testset "basics" begin
5353
F = factorize(T)
5454
g = grid(F)
55-
@test T \ exp.(x) == F \ exp.(x) == F \ exp.(g) == chebyshevtransform(exp.(g), Val(1))
55+
@test T \ exp.(x) == F \ exp.(x) == chebyshevtransform(exp.(g), Val(1))
5656
@test all(checkpoints(T) .∈ Ref(axes(T,1)))
5757

5858
@test T \ [exp.(x) cos.(x)] [F \ exp.(x) F \ cos.(x)]

0 commit comments

Comments
 (0)