Skip to content

Commit 449a39d

Browse files
authored
ExpansionLayout-Lazy ambiguity (#150)
* ExpansionLayout-Lazy ambiguity * Use expansion for Derivative * Update operators.jl * Update bases.jl * Support _sum * __sum -> sum_layout * Update Project.toml * Drop old Julia versions * test sum and dot and basis * increase coverage
1 parent 1152f4c commit 449a39d

File tree

9 files changed

+111
-34
lines changed

9 files changed

+111
-34
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
version:
13-
- '1.6'
14-
- '1'
15-
- '^1.9.0-0'
13+
- '1.9'
1614
os:
1715
- ubuntu-latest
1816
- macOS-latest

Project.toml

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

55
[deps]
66
AbstractFFTs = "621f4979-c628-5d54-868e-fcf4e3e8185c"
@@ -19,21 +19,21 @@ RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
1919
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
2020

2121
[compat]
22-
AbstractFFTs = "1"
23-
ArrayLayouts = "0.7.7, 0.8, 1"
24-
BandedMatrices = "0.16, 0.17"
22+
AbstractFFTs = "1.0"
23+
ArrayLayouts = "1.0"
24+
BandedMatrices = "0.17"
2525
BlockArrays = "0.16"
26-
DomainSets = "0.5, 0.6"
27-
FastTransforms = "0.13, 0.14"
28-
FillArrays = "0.12, 0.13, 1"
26+
DomainSets = "0.6"
27+
FastTransforms = "0.15"
28+
FillArrays = "1.0"
2929
InfiniteArrays = "0.12"
3030
Infinities = "0.1"
31-
IntervalSets = "0.5, 0.6, 0.7"
32-
LazyArrays = "0.22, 1"
33-
QuasiArrays = "0.9.6"
31+
IntervalSets = "0.7"
32+
LazyArrays = "1.0"
33+
QuasiArrays = "0.10"
3434
RecipesBase = "1.0"
3535
StaticArrays = "1.0"
36-
julia = "1.6"
36+
julia = "1.9"
3737

3838
[extras]
3939
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"

src/ContinuumArrays.jl

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Base.Broadcast: materialize, BroadcastStyle, broadcasted, Broadcasted
88
import LazyArrays: MemoryLayout, Applied, ApplyStyle, flatten, _flatten, colsupport, combine_mul_styles, AbstractArrayApplyStyle,
99
adjointlayout, arguments, _mul_arguments, call, broadcastlayout, layout_getindex, UnknownLayout,
1010
sublayout, sub_materialize, ApplyLayout, BroadcastLayout, combine_mul_styles, applylayout,
11-
simplifiable, _simplify, AbstractLazyLayout, PaddedLayout
11+
simplifiable, _simplify, AbstractLazyLayout, PaddedLayout, simplify, Dot
1212
import LinearAlgebra: pinv, inv, dot, norm2, ldiv!, mul!
1313
import BandedMatrices: AbstractBandedLayout, _BandedMatrix
1414
import BlockArrays: block, blockindex, unblock, blockedrange, _BlockedUnitRange, _BlockArray
@@ -18,7 +18,7 @@ import QuasiArrays: cardinality, checkindex, QuasiAdjoint, QuasiTranspose, Inclu
1818
QuasiDiagonal, MulQuasiArray, MulQuasiMatrix, MulQuasiVector, QuasiMatMulMat, QuasiArrayLayout,
1919
ApplyQuasiArray, ApplyQuasiMatrix, LazyQuasiArrayApplyStyle, AbstractQuasiArrayApplyStyle, AbstractQuasiLazyLayout,
2020
LazyQuasiArray, LazyQuasiVector, LazyQuasiMatrix, LazyLayout, LazyQuasiArrayStyle, _factorize, _cutdim,
21-
AbstractQuasiFill, UnionDomain, __sum, _cumsum, __cumsum, applylayout, _equals, layout_broadcasted, PolynomialLayout
21+
AbstractQuasiFill, UnionDomain, sum_size, sum_layout, _cumsum, cumsum_layout, applylayout, _equals, layout_broadcasted, PolynomialLayout, _dot
2222
import InfiniteArrays: Infinity, InfAxes
2323
import AbstractFFTs: Plan
2424

@@ -47,6 +47,8 @@ function dot(x::Inclusion{T,<:AbstractInterval}, y::Inclusion{V,<:AbstractInterv
4747
convert(TV, b^3 - a^3)/3
4848
end
4949

50+
sum(x::Inclusion{T,<:AbstractInterval}) where T = convert(T, width(x.domain))
51+
5052

5153
include("maps.jl")
5254

@@ -97,4 +99,17 @@ include("bases/bases.jl")
9799

98100
include("plotting.jl")
99101

102+
###
103+
# sum/dot
104+
###
105+
106+
sum_size(::Tuple{InfiniteCardinal{1}}, a, dims) = _sum(expand(a), dims)
107+
_dot(::InfiniteCardinal{1}, a, b) = dot(expand(a), expand(b))
108+
function copy(d::Dot{<:ExpansionLayout,<:ExpansionLayout,<:AbstractQuasiArray,<:AbstractQuasiArray})
109+
a,b = d.A,d.B
110+
P,c = basis(a),coefficients(a)
111+
Q,d = basis(b),coefficients(b)
112+
c' * (P'Q) * d
113+
end
114+
100115
end

src/bases/bases.jl

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,19 @@ A / A \\ f.(axes(A,1))
280280
"""
281281
expand(A, f) = A * transform(A, f)
282282

283+
"""
284+
expand(v)
285+
286+
finds a natural basis for a quasi-vector and expands
287+
in that basis.
288+
"""
289+
function expand(v)
290+
P = basis(v)
291+
ApplyQuasiArray(*, P, P \ v)
292+
end
293+
294+
295+
283296
copy(L::Ldiv{<:AbstractBasisLayout}) = transform_ldiv(L.A, L.B)
284297
# TODO: redesign to use simplifiable(\, A, B)
285298
copy(L::Ldiv{<:AbstractBasisLayout,ApplyLayout{typeof(*)},<:Any,<:AbstractQuasiVector}) = transform_ldiv(L.A, L.B)
@@ -318,9 +331,19 @@ _factorize(::WeightedBasisLayouts, wS, dims...; kws...) = WeightedFactorization(
318331
##
319332

320333
struct ExpansionLayout{Lay} <: AbstractLazyLayout end
321-
applylayout(::Type{typeof(*)}, ::Lay, ::Union{PaddedLayout,AbstractStridedLayout}) where Lay <: AbstractBasisLayout = ExpansionLayout{Lay}()
334+
applylayout(::Type{typeof(*)}, ::Lay, ::Union{PaddedLayout,AbstractStridedLayout,ZerosLayout}) where Lay <: AbstractBasisLayout = ExpansionLayout{Lay}()
335+
336+
"""
337+
basis(v)
338+
339+
gives a basis for expanding given quasi-vector.
340+
"""
341+
basis(v) = basis_layout(MemoryLayout(v), v)
342+
343+
basis_layout(::ExpansionLayout, v::ApplyQuasiArray{<:Any,N,typeof(*)}) where N = v.args[1]
344+
basis_layout(lay, v) = basis_axes(axes(v,1), v) # allow choosing a basis based on axes
345+
basis_axes(ax, v) = error("Overload for $ax")
322346

323-
basis(v::ApplyQuasiArray{<:Any,N,typeof(*)}) where N = v.args[1]
324347
coefficients(v::ApplyQuasiArray{<:Any,N,typeof(*),<:Tuple{Any,Any}}) where N = v.args[2]
325348
coefficients(v::ApplyQuasiArray{<:Any,N,typeof(*),<:Tuple{Any,Any,Vararg{Any}}}) where N = ApplyArray(*, tail(v.args)...)
326349

@@ -333,6 +356,7 @@ end
333356
LazyArrays._mul_arguments(::ExpansionLayout, A) = LazyArrays._mul_arguments(ApplyLayout{typeof(*)}(), A)
334357
copy(L::Ldiv{Bas,<:ExpansionLayout}) where Bas<:AbstractBasisLayout = copy(Ldiv{Bas,ApplyLayout{typeof(*)}}(L.A, L.B))
335358
copy(L::Mul{<:ExpansionLayout,Lay}) where Lay = copy(Mul{ApplyLayout{typeof(*)},Lay}(L.A, L.B))
359+
copy(L::Mul{<:ExpansionLayout,Lay}) where Lay<:AbstractLazyLayout = copy(Mul{ApplyLayout{typeof(*)},Lay}(L.A, L.B))
336360

337361
function _broadcastbasis(::typeof(+), _, _, a, b)
338362
try
@@ -374,8 +398,8 @@ function layout_broadcasted(::Tuple{Vararg{ExpansionLayout}}, ::typeof(+), fs...
374398
P * +(_plus_P_ldiv_Ps_cs(P, Ps, cs)...) # +((Ref(P) .\ Ps .* cs)...)
375399
end
376400

377-
function layout_broadcasted(::NTuple{2,ExpansionLayout}, ::typeof(*), a, f)
378-
axes(a,1) == axes(f,1) || throw(DimensionMismatch())
401+
function layout_broadcasted(::Tuple{Any,ExpansionLayout}, ::typeof(*), a, f)
402+
axes(a)[1] == axes(f)[1] || throw(DimensionMismatch())
379403
P,c = arguments(f)
380404
(a .* P) * c
381405
end
@@ -575,24 +599,22 @@ end
575599
####
576600
# sum
577601
####
578-
579-
580-
function __sum(::SubBasisLayout, Vm, dims)
602+
function sum_layout(::SubBasisLayout, Vm, dims)
581603
@assert dims == 1
582604
sum(parent(Vm); dims=dims)[:,parentindices(Vm)[2]]
583605
end
584606

585-
__sum(::AdjointBasisLayout, Vm::AbstractQuasiMatrix, dims) = permutedims(sum(Vm'; dims=(isone(dims) ? 2 : 1)))
607+
sum_layout(::AdjointBasisLayout, Vm::AbstractQuasiMatrix, dims) = permutedims(sum(Vm'; dims=(isone(dims) ? 2 : 1)))
586608

587609

588-
function __sum(::MappedBasisLayouts, V, dims)
610+
function sum_layout(::MappedBasisLayouts, V, dims)
589611
kr = basismap(V)
590612
@assert kr isa AbstractAffineQuasiVector
591613
sum(demap(V); dims=dims)/kr.A
592614
end
593615

594-
__sum(::ExpansionLayout, A, dims) = __sum(ApplyLayout{typeof(*)}(), A, dims)
595-
__cumsum(::ExpansionLayout, A, dims) = __cumsum(ApplyLayout{typeof(*)}(), A, dims)
616+
sum_layout(::ExpansionLayout, A, dims) = sum_layout(ApplyLayout{typeof(*)}(), A, dims)
617+
cumsum_layout(::ExpansionLayout, A, dims) = cumsum_layout(ApplyLayout{typeof(*)}(), A, dims)
596618

597619
include("basisconcat.jl")
598620
include("basiskron.jl")

src/bases/splines.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Spline{o}(S::Spline) where {o} = Spline{o}(S.points)
1111

1212
for Typ in (:LinearSpline, :HeavisideSpline)
1313
STyp = string(Typ)
14-
@eval function summary(io::IO, L::$Typ)
14+
@eval function show(io::IO, L::$Typ)
1515
print(io, "$($STyp)(")
1616
print(IOContext(io, :limit=>true), L.points)
1717
print(io,")")

src/maps.jl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ A subtype of `Map` is used as a one-to-one map between two domains
77
via `view`. The domain of the map `m` is `axes(m,1)` and the range
88
is `union(m)`.
99
10-
Maps must also overload `invmap` to give the inverse of the map, which
10+
Maps must also overload `invmap` to give the inverse of the map, which
1111
is equivalent to `invmap(m)[x] == findfirst(isequal(x), m)`.
1212
"""
1313

@@ -40,6 +40,7 @@ end
4040
# Affine map represents A*x .+ b
4141
abstract type AbstractAffineQuasiVector{T,AA,X,B} <: Map{T} end
4242

43+
show(io::IO, a::AbstractAffineQuasiVector) = summary(io, a)
4344
summary(io::IO, a::AbstractAffineQuasiVector) = print(io, "$(a.A) * $(a.x) .+ ($(a.b))")
4445

4546
MemoryLayout(::Type{<:AbstractAffineQuasiVector}) = PolynomialLayout()
@@ -152,5 +153,11 @@ const AffineMappedQuasiMatrix = SubQuasiArray{<:Any, 2, <:Any, <:Tuple{AbstractA
152153
_sum(V::AffineMappedQuasiVector, ::Colon) = parentindices(V)[1].A \ sum(parent(V))
153154

154155
# pretty print for bases
155-
summary(io::IO, P::AffineMappedQuasiMatrix) = print(io, "$(parent(P)) affine mapped to $(parentindices(P)[1].x.domain)")
156-
summary(io::IO, P::AffineMappedQuasiVector) = print(io, "$(parent(P)) affine mapped to $(parentindices(P)[1].x.domain)")
156+
show(io::IO, a::AffineMap) = summary(io, a)
157+
summary(io::IO, a::AffineMap) = print(io, "Affine map from $(a.domain) to $(a.range)")
158+
159+
show(io::IO, P::Union{AffineMappedQuasiVector,AffineMappedQuasiMatrix}) = print(io, "$(parent(P)) affine mapped to $(parentindices(P)[1].x.domain)")
160+
function summary(io::IO, P::Union{AffineMappedQuasiVector,AffineMappedQuasiMatrix})
161+
summary(io, parent(P))
162+
print(io, " affine mapped to $(parentindices(P)[1].x.domain)")
163+
end

src/operators.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ Derivative{T}(domain) where T = Derivative{T}(Inclusion(domain))
111111

112112
Derivative(L::AbstractQuasiMatrix) = Derivative(axes(L,1))
113113

114+
show(io::IO, a::Derivative) = summary(io, a)
114115
function summary(io::IO, D::Derivative)
115116
print(io, "Derivative(")
116117
summary(io,D.axis)
@@ -154,3 +155,9 @@ Identity(d::Inclusion) = QuasiDiagonal(d)
154155

155156
@simplify *(D::Derivative, x::Inclusion) = ones(promote_type(eltype(D),eltype(x)), x)
156157
@simplify *(D::Derivative, c::AbstractQuasiFill) = zeros(promote_type(eltype(D),eltype(c)), axes(c,1))
158+
# @simplify *(D::Derivative, x::AbstractQuasiVector) = D * expand(x)
159+
160+
struct OperatorLayout <: AbstractLazyLayout end
161+
MemoryLayout(::Type{<:Derivative}) = OperatorLayout()
162+
# copy(M::Mul{OperatorLayout, <:ExpansionLayout}) = simplify(M)
163+
# copy(M::Mul{OperatorLayout, <:AbstractLazyLayout}) = M.A * expand(M.B)

test/test_chebyshev.jl

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
using ContinuumArrays, LinearAlgebra, FastTransforms, QuasiArrays, ArrayLayouts, Test
1+
using ContinuumArrays, LinearAlgebra, FastTransforms, QuasiArrays, ArrayLayouts, Base64, LazyArrays, Test
22
import ContinuumArrays: Basis, Weight, Map, LazyQuasiArrayStyle, TransformFactorization,
33
ExpansionLayout, checkpoints, MappedBasisLayout, MappedWeightedBasisLayout,
4-
SubWeightedBasisLayout, WeightedBasisLayout, WeightLayout
4+
SubWeightedBasisLayout, WeightedBasisLayout, WeightLayout, basis
55

6+
using IntervalSets: AbstractInterval
67
"""
78
This is a simple implementation of Chebyshev for testing. Use ClassicalOrthogonalPolynomials
89
for the real implementation.
@@ -24,10 +25,23 @@ Base.getindex(::ChebyshevWeight, x::Float64) = 1/sqrt(1-x^2)
2425
Base.getindex(w::ChebyshevWeight, ::Inclusion) = w # TODO: make automatic
2526

2627
ContinuumArrays.plan_grid_transform(L::Chebyshev, szs::NTuple{N,Int}, dims=1:N) where N = grid(L), plan_chebyshevtransform(Array{eltype(L)}(undef, szs...), dims)
28+
ContinuumArrays.basis_axes(::Inclusion{<:Any,<:AbstractInterval}, v) = Chebyshev(100)
29+
function ContinuumArrays._sum(T::Chebyshev, dims)
30+
n = 2:size(T,2)-1
31+
[2; 0; @. ((1/(n+1) - 1/(n-1)) - ((-1)^(n+1)/(n+1) - (-1)^(n-1)/(n-1)))/2]'
32+
end
2733

2834
# This is wrong but just for tests
2935
QuasiArrays.layout_broadcasted(::Tuple{ExpansionLayout,Any}, ::typeof(*), a::ApplyQuasiVector{<:Any,typeof(*),<:Tuple{Chebyshev,Any}}, b::Chebyshev) = b * Matrix(I, 5, 5)
3036

37+
ContinuumArrays.@simplify function *(A::QuasiAdjoint{<:Any,<:Chebyshev}, B::Chebyshev)
38+
m,n = size(A,1),size(B,2)
39+
T = promote_type(eltype(A), eltype(B))
40+
f = (k,j) -> isodd(j-k) ? zero(T) : -(((1 + (-1)^(j + k))*(-1 + j^2 + k^2))/(j^4 + (-1 + k^2)^2 - 2j^2*(1 + k^2)))
41+
f.(0:m-1, (0:n-1)')
42+
end
43+
44+
3145
struct QuadraticMap{T} <: Map{T} end
3246
struct InvQuadraticMap{T} <: Map{T} end
3347

@@ -89,6 +103,7 @@ ContinuumArrays.invmap(::InvQuadraticMap{T}) where T = QuadraticMap{T}()
89103
y = affine(0..1, x)
90104

91105
@test summary(T[y,:]) == "Chebyshev affine mapped to 0..1"
106+
@test stringmime("text/plain", T[y,:]) == "Chebyshev(5) affine mapped to 0..1"
92107
@test MemoryLayout(wT[y,:]) isa MappedWeightedBasisLayout
93108
@test MemoryLayout(w[y] .* T[y,:]) isa MappedWeightedBasisLayout
94109
@test wT[y,:][[0.1,0.2],1:5] == (w[y] .* T[y,:])[[0.1,0.2],1:5] == (w .* T[:,1:5])[y,:][[0.1,0.2],:]
@@ -135,4 +150,17 @@ ContinuumArrays.invmap(::InvQuadraticMap{T}) where T = QuadraticMap{T}()
135150
= T * (T \ a)
136151
@test T \ (ã .* ã) [1.5,1,0.5,0,0]
137152
end
153+
154+
@testset "sum/dot" begin
155+
@test sum(x) 2.0
156+
@test sum(exp.(x)) - 1/
157+
@test dot(x, x) 2/3
158+
@test dot(exp.(x), x) 2/
159+
end
160+
161+
@testset "Expansion * Lazy" begin
162+
f = T * collect(1.0:5)
163+
@test (f * ones(1,4))[0.1,:] == fill(f[0.1],4)
164+
@test (f * BroadcastArray(exp, (1:4)'))[0.1,:] f[0.1] * exp.(1:4)
165+
end
138166
end

test/test_maps.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,6 @@ import ContinuumArrays: AffineQuasiVector
7474

7575
@testset "show" begin
7676
@test stringmime("text/plain", y) == "2.0 * Inclusion(0..1) .+ (-1.0)"
77-
@test stringmime("text/plain", a) == "0.4 * Inclusion(-2..3) .+ (-0.2)"
77+
@test stringmime("text/plain", a) == "Affine map from Inclusion(-2..3) to Inclusion(-1..1)"
7878
end
7979
end

0 commit comments

Comments
 (0)