Skip to content

Commit f8b1c39

Browse files
authored
Support views of ApplyArray(*, ...) (#73)
* col/rowsupport can take ranges * views of ApplyArray(*, ...) are ApplyLayout{typeof(*)} * add call o apply array interface * only test allocated on 1.2+ * add vcat/hcat_copyto! * allocate for fast Matrix * Vcat * double cache size to avoid O(n^2) complexity * Use datasize instead of size of data, fix flatten * tests pass on 1.0 * use copyto! instead of broadcast * add tests
1 parent c7c4f96 commit f8b1c39

15 files changed

+233
-119
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "LazyArrays"
22
uuid = "5078a376-72f3-5289-bfd5-ec5146d43c02"
3-
version = "0.12.2"
3+
version = "0.12.3"
44

55
[deps]
66
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"

src/cache.jl

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,26 @@ end
9292

9393
## Array caching
9494

95+
function resizedata!(B::CachedVector{T,Vector{T}}, n::Integer) where T<:Number
96+
@boundscheck checkbounds(Bool, B, n) || throw(ArgumentError("Cannot resize beyound size of operator"))
97+
98+
# increase size of array if necessary
99+
olddata = B.data
100+
ν, = B.datasize
101+
n = max(ν,n)
102+
if n > length(B.data) # double memory to avoid O(n^2) growing
103+
B.data = Array{T}(undef, min(2n,length(B.array)))
104+
B.data[axes(olddata,1)] = olddata
105+
end
106+
107+
inds = ν+1:n
108+
B.data[inds] .= view(B.array,inds)
109+
110+
B.datasize = (n,)
111+
112+
B
113+
end
114+
95115
function resizedata!(B::CachedArray{T,N,Array{T,N}},nm::Vararg{Integer,N}) where {T<:Number,N}
96116
@boundscheck checkbounds(Bool, B, nm...) || throw(ArgumentError("Cannot resize beyound size of operator"))
97117

@@ -124,9 +144,12 @@ function convexunion(a::AbstractVector, b::AbstractVector)
124144
min(minimum(a),minimum(b)):max(maximum(a),maximum(b))
125145
end
126146

127-
colsupport(A::CachedMatrix, i) = i  size(A.data,2) ? convexunion(colsupport(A.array, i),colsupport(A.data,i)) : colsupport(A.array, i)
128-
colsupport(A::CachedVector, i) = convexunion(colsupport(A.array, i),colsupport(A.data,i))
129-
rowsupport(A::CachedMatrix, i) = i  size(A.data,1) ? convexunion(rowsupport(A.array, i),rowsupport(A.data,i)) : rowsupport(A.array, i)
147+
colsupport(A::CachedMatrix, i) =
148+
minimum(i)  A.datasize[2] ? convexunion(colsupport(A.array, i),colsupport(A.data,i) Base.OneTo(A.datasize[1])) : colsupport(A.array, i)
149+
colsupport(A::CachedVector, i) =
150+
convexunion(colsupport(A.array, i),colsupport(A.data,i) Base.OneTo(A.datasize[1]))
151+
rowsupport(A::CachedMatrix, i) =
152+
minimum(i)  A.datasize[1] ? convexunion(rowsupport(A.array, i),rowsupport(A.data,i) Base.OneTo(A.datasize[2])) : rowsupport(A.array, i)
130153

131154
Base.replace_in_print_matrix(A::CachedMatrix, i::Integer, j::Integer, s::AbstractString) =
132155
i in colsupport(A,j) ? s : Base.replace_with_centered_mark(s)

src/lazyapplying.jl

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ end
1616
@inline Applied{Style}(f::F, args::Args) where {Style,F,Args<:Tuple} = Applied{Style,F,Args}(f, args)
1717
@inline Applied{Style}(A::Applied) where Style = Applied{Style}(A.f, A.args)
1818

19+
20+
call(a) = a.f
21+
call(_, a) = a.f
22+
call(a::AbstractArray) = call(MemoryLayout(typeof(a)), a)
1923
arguments(a) = a.args
2024
arguments(_, a) = a.args
2125
arguments(a::AbstractArray) = arguments(MemoryLayout(typeof(a)), a)
@@ -182,8 +186,6 @@ AbstractArray{T}(A::ApplyArray{<:Any,N}) where {T,N} = ApplyArray{T,N}(A.f, map(
182186
AbstractArray{T,N}(A::ApplyArray{T,N}) where {T,N} = copy(A)
183187
AbstractArray{T,N}(A::ApplyArray{<:Any,N}) where {T,N} = ApplyArray{T,N}(A.f, map(copy,A.args)...)
184188

185-
186-
@inline Applied(A::ApplyArray) = applied(A.f, A.args...)
187189
@inline axes(A::ApplyArray) = axes(Applied(A))
188190
@inline size(A::ApplyArray) = map(length, axes(A))
189191
@inline copy(A::ApplyArray) = ApplyArray(A.f, map(copy,A.args)...)
@@ -230,6 +232,9 @@ MemoryLayout(::Type{Applied{Style,F,Args}}) where {Style,F,Args} =
230232
MemoryLayout(::Type{ApplyArray{T,N,F,Args}}) where {T,N,F,Args} =
231233
applylayout(F, tuple_type_memorylayouts(Args)...)
232234

235+
@inline Applied(A::AbstractArray) = Applied(call(A), arguments(A)...)
236+
@inline ApplyArray(A::AbstractArray) = ApplyArray(call(A), arguments(A)...)
237+
233238
function show(io::IO, A::Applied)
234239
print(io, "Applied(", A.f)
235240
for a in A.args
@@ -273,7 +278,10 @@ end
273278
@inline getindex(A::LazyMatrix, kr::AbstractUnitRange, jr::Colon) = lazy_getindex(A, kr, jr)
274279
@inline getindex(A::LazyMatrix, kr::AbstractUnitRange, jr::AbstractUnitRange) = lazy_getindex(A, kr, jr)
275280

281+
@inline copyto!(dest::AbstractArray{T,N}, src::ApplyArray{T,N}) where {T,N} = copyto!(dest, Applied(src))
282+
@inline copyto!(dest::AbstractArray, src::ApplyArray) = copyto!(dest, Applied(src))
276283

277-
diagonallayout(::LazyLayout) = DiagonalLayout{LazyLayout}()
278-
diagonallayout(::ApplyLayout) = DiagonalLayout{LazyLayout}()
279-
284+
# avoid infinite-loop
285+
_base_copyto!(dest::AbstractArray{T,N}, src::AbstractArray{T,N}) where {T,N} = Base.invoke(copyto!, NTuple{2,AbstractArray{T,N}}, dest, src)
286+
_base_copyto!(dest::AbstractArray, src::AbstractArray) = Base.invoke(copyto!, NTuple{2,AbstractArray}, dest, src)
287+
@inline copyto!(dest::AbstractArray, M::Applied{LazyArrayApplyStyle}) = _base_copyto!(dest, materialize(M))

src/lazybroadcasting.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,6 @@ broadcasted(::LazyArrayStyle{N}, ::typeof(*), a::AbstractArray{T,N}, b::Zeros{V,
152152
broadcasted(::LazyArrayStyle{N}, ::typeof(*), a::Zeros{T,N}, b::AbstractArray{V,N}) where {T,V,N} =
153153
broadcast(DefaultArrayStyle{N}(), *, a, b)
154154

155-
diagonallayout(::BroadcastLayout) = DiagonalLayout{LazyLayout}()
156-
157155

158156
###
159157
# support

src/lazyconcat.jl

Lines changed: 43 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,8 @@ end
116116

117117
## copyto!
118118
# based on Base/array.jl, Base/abstractarray.jl
119-
120-
function copyto!(dest::AbstractMatrix, V::Vcat{<:Any,2})
121-
arrays = V.args
119+
copyto!(dest::AbstractArray, V::Vcat) = vcat_copyto!(dest, arguments(V)...)
120+
function vcat_copyto!(dest::AbstractMatrix, arrays...)
122121
nargs = length(arrays)
123122
nrows = size(dest,1)
124123
nrows == sum(a->size(a, 1), arrays) || throw(DimensionMismatch("sum of rows each matrix must equal $nrows"))
@@ -131,35 +130,13 @@ function copyto!(dest::AbstractMatrix, V::Vcat{<:Any,2})
131130
pos = 1
132131
for a in arrays
133132
p1 = pos+size(a,1)-1
134-
dest[pos:p1, :] .= a
133+
copyto!(view(dest,pos:p1, :), a)
135134
pos = p1+1
136135
end
137136
return dest
138137
end
139138

140-
# this is repeated to avoid allocation in .=
141-
function copyto!(dest::AbstractMatrix, V::Vcat{<:Any,2,<:Tuple{Vararg{<:AbstractMatrix}}})
142-
arrays = V.args
143-
nargs = length(arrays)
144-
nrows = size(dest,1)
145-
nrows == sum(a->size(a, 1), arrays) || throw(DimensionMismatch("sum of rows each matrix must equal $nrows"))
146-
ncols = size(dest, 2)
147-
for a in arrays
148-
if size(a, 2) != ncols
149-
throw(DimensionMismatch("number of columns of each array must match (got $(map(x->size(x,2), A)))"))
150-
end
151-
end
152-
pos = 1
153-
for a in arrays
154-
p1 = pos+size(a,1)-1
155-
dest[pos:p1, :] = a
156-
pos = p1+1
157-
end
158-
return dest
159-
end
160-
161-
function copyto!(arr::AbstractVector, A::Vcat{<:Any,1,<:Tuple{Vararg{<:AbstractVector}}})
162-
arrays = A.args
139+
function vcat_copyto!(arr::AbstractVector, arrays...)
163140
n = 0
164141
for a in arrays
165142
n += length(a)
@@ -168,16 +145,14 @@ function copyto!(arr::AbstractVector, A::Vcat{<:Any,1,<:Tuple{Vararg{<:AbstractV
168145

169146
i = 0
170147
@inbounds for a in arrays
171-
for ai in a
172-
i += 1
173-
arr[i] = ai
174-
end
148+
m = length(a)
149+
copyto!(view(arr,i+1:i+m), a)
150+
i += m
175151
end
176152
arr
177153
end
178154

179-
function copyto!(arr::Vector{T}, A::Vcat{T,1,<:Tuple{Vararg{<:Vector{T}}}}) where T
180-
arrays = A.args
155+
function vcat_copyto!(arr::Vector{T}, arrays::Vector{T}...) where T
181156
n = 0
182157
for a in arrays
183158
n += length(a)
@@ -217,8 +192,8 @@ function copyto!(arr::Vector{T}, A::Vcat{T,1,<:Tuple{Vararg{<:Vector{T}}}}) wher
217192
return arr
218193
end
219194

220-
function copyto!(dest::AbstractMatrix, H::Hcat)
221-
arrays = H.args
195+
copyto!(dest::AbstractMatrix, H::Hcat) = hcat_copyto!(dest, arguments(H)...)
196+
function hcat_copyto!(dest::AbstractMatrix, arrays...)
222197
nargs = length(arrays)
223198
nrows = size(dest, 1)
224199
ncols = 0
@@ -229,7 +204,7 @@ function copyto!(dest::AbstractMatrix, H::Hcat)
229204
ncols += (nd==2 ? size(a,2) : 1)
230205
end
231206

232-
nrows == size(H,1) || throw(DimensionMismatch("Destination rows must match"))
207+
nrows == size(first(arrays),1) || throw(DimensionMismatch("Destination rows must match"))
233208
ncols == size(dest,2) || throw(DimensionMismatch("Destination columns must match"))
234209

235210
pos = 1
@@ -242,22 +217,22 @@ function copyto!(dest::AbstractMatrix, H::Hcat)
242217
else
243218
for a in arrays
244219
p1 = pos+(isa(a,AbstractMatrix) ? size(a, 2) : 1)-1
245-
dest[:, pos:p1] .= a
220+
copyto!(view(dest,:, pos:p1), a)
246221
pos = p1+1
247222
end
248223
end
249224
return dest
250225
end
251226

252-
function copyto!(dest::AbstractMatrix, H::Hcat{<:Any,Tuple{Vararg{<:AbstractVector}}})
227+
function hcat_copyto!(dest::AbstractMatrix, arrays::AbstractVector...)
253228
height = size(dest, 1)
254-
for j = 1:length(H)
255-
if length(H[j]) != height
229+
for j = 1:length(arrays)
230+
if length(arrays[j]) != height
256231
throw(DimensionMismatch("vectors must have same lengths"))
257232
end
258233
end
259-
for j=1:length(H)
260-
dest[i,:] .= H[j]
234+
for j=1:length(arrays)
235+
copyto!(view(dest,:,j), arrays[j])
261236
end
262237

263238
dest
@@ -502,27 +477,36 @@ applylayout(::Type{typeof(vcat)}, ::A, ::ZerosLayout) where A = PaddedLayout{A}(
502477
cachedlayout(::A, ::ZerosLayout) where A = PaddedLayout{A}()
503478

504479

505-
paddeddata(A::CachedArray) = A.data
480+
paddeddata(A::CachedArray) = view(A.data,OneTo.(A.datasize)...)
506481
paddeddata(A::Vcat) = A.args[1]
507482

508483
function ==(A::CachedVector{<:Any,<:Any,<:Zeros}, B::CachedVector{<:Any,<:Any,<:Zeros})
509484
length(A) == length(B) || return false
510-
n = max(length(A.data), length(B.data))
485+
n = max(A.datasize[1], B.datasize[1])
511486
resizedata!(A,n); resizedata!(B,n)
512-
A.data == B.data
487+
view(A.data,OneTo(n)) == view(B.data,OneTo(n))
513488
end
514489

515490
# special copyto! since `similar` of a padded returns a cached
516491
for Typ in (:Number, :AbstractVector)
517492
@eval function copyto!(dest::CachedVector{T,Vector{T},<:Zeros{T,1}}, src::Vcat{<:Any,1,<:Tuple{<:$Typ,<:Zeros}}) where T
518493
length(src) length(dest) || throw(BoundsError())
519494
a,_ = src.args
520-
resizedata!(dest, length(a)) # make sure we are padded enough
521-
copyto!(dest.data, a)
495+
n = length(a)
496+
resizedata!(dest, n) # make sure we are padded enough
497+
copyto!(view(dest.data,OneTo(n)), a)
522498
dest
523499
end
524500
end
525501

502+
function copyto!(dest::CachedVector{T,Vector{T},<:Zeros{T,1}}, src::CachedVector{V,Vector{V},<:Zeros{V,1}}) where {T,V}
503+
length(src) length(dest) || throw(BoundsError())
504+
n = src.datasize[1]
505+
resizedata!(dest, n)
506+
copyto!(view(dest.data,OneTo(n)), view(src.data,OneTo(n)))
507+
dest
508+
end
509+
526510
struct Dot{StyleA,StyleB,ATyp,BTyp}
527511
A::ATyp
528512
B::BTyp
@@ -556,16 +540,17 @@ end
556540
# subarrays
557541
###
558542

559-
subarraylayout(::ApplyLayout{typeof(vcat)}, _) =
560-
ApplyLayout{typeof(vcat)}()
561-
subarraylayout(::ApplyLayout{typeof(hcat)}, _) =
562-
ApplyLayout{typeof(hcat)}()
543+
subarraylayout(::ApplyLayout{typeof(vcat)}, _) = ApplyLayout{typeof(vcat)}()
544+
subarraylayout(::ApplyLayout{typeof(hcat)}, _) = ApplyLayout{typeof(hcat)}()
563545

564546
arguments(::ApplyLayout{typeof(vcat)}, V::SubArray{<:Any,2,<:Any,<:Tuple{<:Slice,<:Any}}) =
565547
view.(arguments(parent(V)), Ref(:), Ref(parentindices(V)[2]))
566548
arguments(::ApplyLayout{typeof(hcat)}, V::SubArray{<:Any,2,<:Any,<:Tuple{<:Any,<:Slice}}) =
567549
view.(arguments(parent(V)), Ref(parentindices(V)[1]), Ref(:))
568550

551+
copyto!(dest::AbstractArray{T,N}, src::SubArray{T,N,<:Vcat{T,N}}) where {T,N} = vcat_copyto!(dest, arguments(src)...)
552+
copyto!(dest::AbstractMatrix{T}, src::SubArray{T,2,<:Hcat{T}}) where T = hcat_copyto!(dest, arguments(src)...)
553+
569554

570555
_vcat_lastinds(sz) = _vcat_cumsum(sz...)
571556
_vcat_firstinds(sz) = (1, (1 .+ most(_vcat_lastinds(sz)))...)
@@ -612,7 +597,7 @@ function sub_materialize(::ApplyLayout{typeof(vcat)}, V)
612597
_,jr = parentindices(V)
613598
for a in arguments(V)
614599
m = size(a,1)
615-
view(ret,n+1:n+m,:) .= a
600+
copyto!(view(ret,n+1:n+m,:), a)
616601
n += m
617602
end
618603
ret
@@ -624,9 +609,13 @@ function sub_materialize(::ApplyLayout{typeof(hcat)}, V)
624609
kr,_ = parentindices(V)
625610
for a in arguments(V)
626611
m = size(a,2)
627-
view(ret,:,n+1:n+m) .= a
612+
copyto!(view(ret,:,n+1:n+m), a)
628613
n += m
629614
end
630615
ret
631616
end
632-
617+
# temporarily allocate. In the future, we add a loop over arguments
618+
materialize!(M::MatMulMatAdd{<:AbstractColumnMajor,<:ApplyLayout{typeof(vcat)}}) =
619+
materialize!(MulAdd(M.α,M.A,Array(M.B),M.β,M.C))
620+
materialize!(M::MatMulVecAdd{<:AbstractColumnMajor,<:ApplyLayout{typeof(vcat)}}) =
621+
materialize!(MulAdd(M.α,M.A,Array(M.B),M.β,M.C))

src/lazyoperations.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ copyto!(R::AbstractMatrix, K::Kron{<:Any,2,<:Tuple{<:AbstractMatrix,<:AbstractMa
6262
_kron2!(R, K)
6363
copyto!(R::AbstractVector, K::Kron{<:Any,1,<:Tuple{<:AbstractVector,<:AbstractVector}}) =
6464
_kron2!(R, K)
65+
copyto!(R::AbstractMatrix{T}, K::Kron{T,2,<:Tuple{<:AbstractMatrix,<:AbstractMatrix}}) where T =
66+
_kron2!(R, K)
67+
copyto!(R::AbstractVector{T}, K::Kron{T,1,<:Tuple{<:AbstractVector,<:AbstractVector}}) where T =
68+
_kron2!(R, K)
6569

6670

6771
struct Diff{T, N, Arr} <: LazyArray{T, N}

src/linalg/add.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ _view_tuple(a, b::Tuple) = view(a, b...)
9090
for op in (:+, :-)
9191
@eval begin
9292
subarraylayout(a::ApplyLayout{typeof($op)}, _) = a
93-
arguments(a::SubArray{<:Any,N,<:ApplyArray{<:Any,N,typeof($op)}}) where N =
93+
arguments(::ApplyLayout{typeof($op)}, a::SubArray) =
9494
_view_tuple.(arguments(parent(a)), Ref(parentindices(a)))
95+
call(::ApplyLayout{typeof($op)}, a::SubArray) = $op
9596
end
9697
end

src/linalg/diagonal.jl

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
####
2-
# Diagonal
3-
####
4-
5-
rowsupport(::Diagonal, k) = k:k
6-
colsupport(::Diagonal, j) = j:j
1+
diagonallayout(::LazyLayout) = DiagonalLayout{LazyLayout}()
2+
diagonallayout(::ApplyLayout) = DiagonalLayout{LazyLayout}()
3+
diagonallayout(::BroadcastLayout) = DiagonalLayout{LazyLayout}()
74

8-
rowsupport(::DiagonalLayout, _, k) = k:k
9-
colsupport(::DiagonalLayout, _, j) = j:j
5+
rowsupport(::DiagonalLayout, _, k) = minimum(k):maximum(k)
6+
colsupport(::DiagonalLayout, _, j) = minimum(j):maximum(j)
107

118
###
129
# Lmul

0 commit comments

Comments
 (0)