Skip to content

Commit b4c637f

Browse files
authored
Add Quasildiv (#31)
* start sub * tests pass * Increase coverage * MappedBasisLayout * Move over to quasildivapplystyle * Update bases.jl * Update Project.toml * Support transform by axes * sub-of-sub grid * Update ContinuumArrays.jl * add diff, other fixes * Support for sum * Update operators.jl * Update runtests.jl * Improve type stability * sub_materialize for continuous * Update Project.toml * Update Project.toml * Increase coverage * Increase coverage
1 parent 1142329 commit b4c637f

File tree

6 files changed

+173
-51
lines changed

6 files changed

+173
-51
lines changed

Project.toml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,15 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1111
QuasiArrays = "c4ea9172-b204-11e9-377d-29865faadc5c"
1212

1313
[compat]
14-
BandedMatrices = "0.14"
15-
FillArrays = "0.8"
16-
IntervalSets = "0.3.1"
17-
LazyArrays = "0.14"
18-
QuasiArrays = "0.0.4, 0.0.5"
14+
BandedMatrices = "0.14.1"
15+
FillArrays = "0.8.2"
16+
IntervalSets = "0.3.2"
17+
LazyArrays = "0.14.7"
18+
QuasiArrays = "0.0.6"
1919
julia = "1.1"
2020

2121
[extras]
22-
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
2322
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2423

2524
[targets]
26-
test = ["Test", "ForwardDiff"]
25+
test = ["Test"]

src/ContinuumArrays.jl

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
module ContinuumArrays
22
using IntervalSets, LinearAlgebra, LazyArrays, FillArrays, BandedMatrices, QuasiArrays
33
import Base: @_inline_meta, @_propagate_inbounds_meta, axes, getindex, convert, prod, *, /, \, +, -, ==,
4-
IndexStyle, IndexLinear, ==, OneTo, tail, similar, copyto!, copy,
5-
first, last, show, isempty, findfirst, findlast, findall, Slice, union, minimum, maximum
4+
IndexStyle, IndexLinear, ==, OneTo, tail, similar, copyto!, copy, diff,
5+
first, last, show, isempty, findfirst, findlast, findall, Slice, union, minimum, maximum, sum, _sum
66
import Base.Broadcast: materialize, BroadcastStyle, broadcasted
77
import LazyArrays: MemoryLayout, Applied, ApplyStyle, flatten, _flatten, colsupport,
88
adjointlayout, LdivApplyStyle, arguments, _arguments, call, broadcastlayout, lazy_getindex,
9-
sublayout, ApplyLayout, BroadcastLayout, combine_mul_styles
9+
sublayout, sub_materialize, ApplyLayout, BroadcastLayout, combine_mul_styles
1010
import LinearAlgebra: pinv
1111
import BandedMatrices: AbstractBandedLayout, _BandedMatrix
12-
import FillArrays: AbstractFill, getindex_value
12+
import FillArrays: AbstractFill, getindex_value, SquareEye
1313

1414
import QuasiArrays: cardinality, checkindex, QuasiAdjoint, QuasiTranspose, Inclusion, SubQuasiArray,
1515
QuasiDiagonal, MulQuasiArray, MulQuasiMatrix, MulQuasiVector, QuasiMatMulMat, quasimulapplystyle,
1616
ApplyQuasiArray, ApplyQuasiMatrix, LazyQuasiArrayApplyStyle, AbstractQuasiArrayApplyStyle,
17-
LazyQuasiArray, LazyQuasiVector, LazyQuasiMatrix, LazyLayout, LazyQuasiArrayStyle
17+
LazyQuasiArray, LazyQuasiVector, LazyQuasiMatrix, LazyLayout, LazyQuasiArrayStyle, quasildivapplystyle
1818

1919
export Spline, LinearSpline, HeavisideSpline, DiracDelta, Derivative, fullmaterialize, ℵ₁, Inclusion, Basis, WeightedBasis, grid, transform
2020

@@ -86,6 +86,15 @@ AffineQuasiVector(A, x::AffineQuasiVector, b) = AffineQuasiVector(A*x.A, x.x, A*
8686

8787
axes(A::AffineQuasiVector) = axes(A.x)
8888
getindex(A::AffineQuasiVector, k::Number) = A.A*A.x[k] .+ A.b
89+
function getindex(A::AffineQuasiVector, k::Inclusion)
90+
@boundscheck A.x[k] # throws bounds error if k ≠ x
91+
A
92+
end
93+
94+
getindex(A::AffineQuasiVector, ::Colon) = copy(A)
95+
96+
copy(A::AffineQuasiVector) = A
97+
8998
inbounds_getindex(A::AffineQuasiVector{<:Any,<:Any,<:Inclusion}, k::Number) = A.A*k .+ A.b
9099
isempty(A::AffineQuasiVector) = isempty(A.x)
91100
==(a::AffineQuasiVector, b::AffineQuasiVector) = a.A == b.A && a.x == b.x && a.b == b.b
@@ -107,21 +116,28 @@ broadcasted(::LazyQuasiArrayStyle{1}, ::typeof(+), x::AffineQuasiVector, b::Numb
107116
broadcasted(::LazyQuasiArrayStyle{1}, ::typeof(-), a::Number, x::AffineQuasiVector) = AffineQuasiVector(-one(eltype(x)), x, a)
108117
broadcasted(::LazyQuasiArrayStyle{1}, ::typeof(-), x::AffineQuasiVector, b::Number) = AffineQuasiVector(one(eltype(x)), x, -b)
109118

110-
function checkindex(::Type{Bool}, inds::Inclusion{<:Any,<:AbstractInterval}, r::AffineQuasiVector{<:Real,<:Real,<:Inclusion{<:Real,<:AbstractInterval}})
119+
function checkindex(::Type{Bool}, inds::Inclusion{<:Any,<:AbstractInterval}, r::AffineQuasiVector{<:Any,<:Any,<:Inclusion{<:Any,<:AbstractInterval}})
111120
@_propagate_inbounds_meta
112121
isempty(r) | (checkindex(Bool, inds, first(r)) & checkindex(Bool, inds, last(r)))
113122
end
114123

124+
igetindex(d::AffineQuasiVector, x) = d.A \ (x .- d.b)
125+
115126
for find in (:findfirst, :findlast, :findall)
116-
@eval $find(f::Base.Fix2{typeof(isequal)}, d::AffineQuasiVector) = $find(isequal(d.A \ (f.x .- d.b)), d.x)
127+
@eval $find(f::Base.Fix2{typeof(isequal)}, d::AffineQuasiVector) = $find(isequal(igetindex(d, f.x)), d.x)
117128
end
118129

119130
minimum(d::AffineQuasiVector{<:Real,<:Real,<:Inclusion}) = signbit(d.A) ? last(d) : first(d)
120131
maximum(d::AffineQuasiVector{<:Real,<:Real,<:Inclusion}) = signbit(d.A) ? first(d) : last(d)
121132

122133
union(d::AffineQuasiVector{<:Real,<:Real,<:Inclusion}) = Inclusion(minimum(d)..maximum(d))
123134

135+
const QInfAxes = Union{Inclusion,AffineQuasiVector}
124136

137+
sub_materialize(_, V::AbstractQuasiArray, ::Tuple{QInfAxes}) = V
138+
sub_materialize(_, V::AbstractQuasiArray, ::Tuple{QInfAxes,QInfAxes}) = V
139+
sub_materialize(_, V::AbstractQuasiArray, ::Tuple{<:Any,QInfAxes}) = V
140+
sub_materialize(_, V::AbstractQuasiArray, ::Tuple{QInfAxes,Any}) = V
125141

126142

127143
include("operators.jl")

src/bases/bases.jl

Lines changed: 68 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -30,56 +30,53 @@ combine_mul_styles(::AbstractAdjointBasisLayout) = LazyQuasiArrayApplyStyle()
3030
ApplyStyle(::typeof(pinv), ::Type{<:Basis}) = LazyQuasiArrayApplyStyle()
3131
pinv(J::Basis) = apply(pinv,J)
3232

33-
_multup(a::Tuple) = Mul(a...)
34-
_multup(a) = a
35-
3633

3734
function ==(A::Basis, B::Basis)
3835
axes(A) == axes(B) && throw(ArgumentError("Override == to compare bases of type $(typeof(A)) and $(typeof(B))"))
3936
false
4037
end
4138

39+
@inline quasildivapplystyle(::AbstractBasisLayout, ::AbstractBasisLayout) = LdivApplyStyle()
40+
@inline quasildivapplystyle(::AbstractBasisLayout, _) = LdivApplyStyle()
41+
@inline quasildivapplystyle(_, ::AbstractBasisLayout) = LdivApplyStyle()
4242

43-
ApplyStyle(::typeof(\), ::Type{<:Basis}, ::Type{<:AbstractQuasiMatrix}) = LdivApplyStyle()
44-
ApplyStyle(::typeof(\), ::Type{<:Basis}, ::Type{<:AbstractQuasiVector}) = LdivApplyStyle()
45-
ApplyStyle(::typeof(\), ::Type{<:SubQuasiArray{<:Any,2,<:Basis}}, ::Type{<:AbstractQuasiMatrix}) = LdivApplyStyle()
46-
ApplyStyle(::typeof(\), ::Type{<:SubQuasiArray{<:Any,2,<:Basis}}, ::Type{<:AbstractQuasiVector}) = LdivApplyStyle()
4743

48-
copy(L::Ldiv{<:AbstractBasisLayout,BroadcastLayout{typeof(+)}}) = +(broadcast(\,Ref(L.A),arguments(L.B))...)
49-
copy(L::Ldiv{<:AbstractBasisLayout,BroadcastLayout{typeof(+)},<:Any,<:AbstractQuasiVector}) =
50-
+(broadcast(\,Ref(L.A),arguments(L.B))...)
44+
@inline copy(L::Ldiv{<:AbstractBasisLayout,BroadcastLayout{typeof(+)}}) = +(broadcast(\,Ref(L.A),arguments(L.B))...)
45+
@inline copy(L::Ldiv{<:AbstractBasisLayout,BroadcastLayout{typeof(+)},<:Any,<:AbstractQuasiVector}) =
46+
transform_ldiv(L.A, L.B)
5147

52-
function copy(L::Ldiv{<:AbstractBasisLayout,BroadcastLayout{typeof(-)}})
48+
@inline function copy(L::Ldiv{<:AbstractBasisLayout,BroadcastLayout{typeof(-)}})
5349
a,b = arguments(L.B)
5450
(L.A\a)-(L.A\b)
5551
end
5652

57-
function copy(L::Ldiv{<:AbstractBasisLayout,BroadcastLayout{typeof(-)},<:Any,<:AbstractQuasiVector})
58-
a,b = arguments(L.B)
59-
(L.A\a)-(L.A\b)
60-
end
53+
@inline copy(L::Ldiv{<:AbstractBasisLayout,BroadcastLayout{typeof(-)},<:Any,<:AbstractQuasiVector}) =
54+
transform_ldiv(L.A, L.B)
6155

6256
function copy(P::Ldiv{BasisLayout,BasisLayout})
6357
A, B = P.A, P.B
64-
A == B || throw(ArgumentError("Override materialize for $(typeof(A)) \\ $(typeof(B))"))
65-
Eye(size(A,2))
58+
A == B || throw(ArgumentError("Override copy for $(typeof(A)) \\ $(typeof(B))"))
59+
SquareEye{eltype(P)}(size(A,2))
6660
end
6761
function copy(P::Ldiv{SubBasisLayout,SubBasisLayout})
6862
A, B = P.A, P.B
6963
(parent(A) == parent(B) && parentindices(A) == parentindices(B)) ||
70-
throw(ArgumentError("Override materialize for $(typeof(A)) \\ $(typeof(B))"))
71-
Eye(size(A,2))
64+
throw(ArgumentError("Override copy for $(typeof(A)) \\ $(typeof(B))"))
65+
SquareEye{eltype(P)}(size(A,2))
7266
end
7367

74-
function copy(P::Ldiv{MappedBasisLayout,MappedBasisLayout})
68+
@inline function copy(P::Ldiv{MappedBasisLayout,MappedBasisLayout})
7569
A, B = P.A, P.B
7670
demap(A)\demap(B)
7771
end
78-
# function copy(P::Ldiv{MappedBasisLayout,SubBasisLayout})
79-
# A, B = P.A, P.B
80-
# # use lazy_getindex to avoid sparse arrays
81-
# lazy_getindex(parent(A)\parent(B),:,parentindices(B)[2])
82-
# end
72+
73+
@inline copy(L::Ldiv{BasisLayout,SubBasisLayout}) = apply(\, L.A, ApplyQuasiArray(L.B))
74+
@inline function copy(L::Ldiv{SubBasisLayout,BasisLayout})
75+
P = parent(L.A)
76+
kr, jr = parentindices(L.A)
77+
lazy_getindex(apply(\, P, L.B), jr, :) # avoid sparse arrays
78+
end
79+
8380

8481
for Bas1 in (:Basis, :WeightedBasis), Bas2 in (:Basis, :WeightedBasis)
8582
@eval ==(A::SubQuasiArray{<:Any,2,<:$Bas1}, B::SubQuasiArray{<:Any,2,<:$Bas2}) =
@@ -88,23 +85,39 @@ end
8885

8986

9087
# expansion
91-
grid(P) = error("Overload Grid")
88+
_grid(_, P) = error("Overload Grid")
89+
_grid(::MappedBasisLayout, P) = igetindex.(Ref(parentindices(P)[1]), grid(demap(P)))
90+
_grid(::SubBasisLayout, P) = grid(parent(P))
91+
grid(P) = _grid(MemoryLayout(typeof(P)), P)
92+
9293
function transform(L)
9394
p = grid(L)
9495
p,L[p,:]
9596
end
9697

97-
function copy(L::Ldiv{<:AbstractBasisLayout,<:Any,<:Any,<:AbstractQuasiVector})
98-
p,T = transform(L.A)
99-
T \ L.B[p]
98+
function transform_ldiv(A, B, _)
99+
p,T = transform(A)
100+
T \ convert(Array, B[p])
100101
end
101102

103+
transform_ldiv(A, B) = transform_ldiv(A, B, axes(A))
104+
105+
copy(L::Ldiv{<:AbstractBasisLayout,<:Any,<:Any,<:AbstractQuasiVector}) =
106+
transform_ldiv(L.A, L.B)
107+
102108
copy(L::Ldiv{<:AbstractBasisLayout,ApplyLayout{typeof(*)},<:Any,<:AbstractQuasiVector}) =
103-
copy(Ldiv{LazyLayout,ApplyLayout{typeof(*)}}(L.A, L.B))
109+
transform_ldiv(L.A, L.B)
110+
111+
function copy(L::Ldiv{ApplyLayout{typeof(*)},<:AbstractBasisLayout})
112+
args = arguments(L.A)
113+
@assert length(args) == 2 # temporary
114+
apply(\, last(args), apply(\, first(args), L.B))
115+
end
116+
104117

105118
function copy(L::Ldiv{<:AbstractBasisLayout,BroadcastLayout{typeof(*)},<:AbstractQuasiMatrix,<:AbstractQuasiVector})
106119
p,T = transform(L.A)
107-
T \ L.B[p]
120+
T \ L.B[p]
108121
end
109122

110123
## materialize views
@@ -144,6 +157,9 @@ end
144157
sublayout(::AbstractBasisLayout, ::Type{<:Tuple{<:Inclusion,<:AbstractUnitRange}}) = SubBasisLayout()
145158
sublayout(::BasisLayout, ::Type{<:Tuple{<:AffineQuasiVector,<:AbstractUnitRange}}) = MappedBasisLayout()
146159

160+
@inline sub_materialize(::AbstractBasisLayout, V::AbstractQuasiArray) = V
161+
@inline sub_materialize(::AbstractBasisLayout, V::AbstractArray) = V
162+
147163
demap(x) = x
148164
demap(V::SubQuasiArray{<:Any,2,<:Any,<:Tuple{<:Any,<:Slice}}) = parent(V)
149165
function demap(V::SubQuasiArray{<:Any,2})
@@ -172,8 +188,28 @@ function arguments(V::SubQuasiArray{<:Any,2,<:Any,<:Tuple{<:Inclusion,<:Abstract
172188
A,P
173189
end
174190

175-
copy(L::Ldiv{BasisLayout,SubBasisLayout}) = apply(\, L.A, ApplyQuasiArray(L.B))
191+
####
192+
# sum
193+
####
176194

195+
_sum(V::AbstractQuasiArray, dims) = __sum(MemoryLayout(typeof(V)), V, dims)
196+
_sum(V::AbstractQuasiArray, ::Colon) = __sum(MemoryLayout(typeof(V)), V, :)
197+
sum(V::AbstractQuasiArray; dims=:) = _sum(V, dims)
177198

199+
__sum(L, Vm, _) = error("Override for $L")
200+
function __sum(::SubBasisLayout, Vm, dims)
201+
@assert dims == 1
202+
sum(parent(Vm); dims=dims)[:,parentindices(Vm)[2]]
203+
end
204+
function __sum(::ApplyLayout{typeof(*)}, V::AbstractQuasiVector, ::Colon)
205+
a = arguments(V)
206+
first(apply(*, sum(a[1]; dims=1), tail(a)...))
207+
end
208+
209+
function __sum(::MappedBasisLayout, V::AbstractQuasiArray, dims)
210+
kr, jr = parentindices(V)
211+
@assert kr isa AffineQuasiVector
212+
sum(demap(V); dims=dims)/kr.A
213+
end
178214

179215
include("splines.jl")

src/bases/splines.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ end
3939

4040
grid(L::LinearSpline) = L.points
4141
transform(L::LinearSpline{T}) where T = grid(L),Eye{T}(size(L,2))
42+
function transform(V::SubQuasiArray{<:Any,2,<:LinearSpline,<:Tuple{<:Inclusion,<:Any}})
43+
g, T = transform(parent(V))
44+
g, qr(lazy_getindex(T,:,parentindices(V)[2])) # avoid sparse matrices of sub-diagonal
45+
end
4246

4347
## Sub-bases
4448

@@ -124,4 +128,14 @@ ApplyStyle(::typeof(*), ::Type{<:QuasiAdjoint{<:Any,<:LinearSpline}}, ::Type{<:Q
124128
function copy(M::QMul2{<:QuasiAdjoint{<:Any,<:LinearSpline},<:QuasiAdjoint{<:Any,<:Derivative}})
125129
Bc,Ac = M.args
126130
apply(*,Ac',Bc')'
131+
end
132+
133+
134+
##
135+
# sum
136+
##
137+
138+
function _sum(A::HeavisideSpline, dims)
139+
@assert dims == 1
140+
permutedims(diff(A.points))
127141
end

src/operators.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ axes(D::Derivative) = (D.axis, D.axis)
153153
==(a::Derivative, b::Derivative) = a.axis == b.axis
154154
copy(D::Derivative) = Derivative(copy(D.axis))
155155

156+
function diff(d::AbstractQuasiVector)
157+
x = axes(d,1)
158+
Derivative(x)*d
159+
end
156160

157161

158162
# struct Multiplication{T,F,A} <: AbstractQuasiMatrix{T}

0 commit comments

Comments
 (0)