diff --git a/Project.toml b/Project.toml index 561d88f8..64826661 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "LazyArrays" uuid = "5078a376-72f3-5289-bfd5-ec5146d43c02" -version = "2.9.2" +version = "2.9.3" [deps] ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" diff --git a/ext/LazyArraysBandedMatricesExt.jl b/ext/LazyArraysBandedMatricesExt.jl index 2a68070e..da2362e1 100644 --- a/ext/LazyArraysBandedMatricesExt.jl +++ b/ext/LazyArraysBandedMatricesExt.jl @@ -11,7 +11,7 @@ import LazyArrays: sublayout, symmetriclayout, hermitianlayout, applylayout, cac PaddedColumns, CachedArray, CachedMatrix, LazyLayout, BroadcastLayout, ApplyLayout, paddeddata, resizedata!, broadcastlayout, _broadcastarray2broadcasted, _broadcast_sub_arguments, arguments, call, applybroadcaststyle, simplify, simplifiable, islazy_layout, lazymaterialize, _broadcast_mul_mul, _broadcast_mul_simplifiable, - triangularlayout, AbstractCachedMatrix, cache_layout, _mulbanded_copyto!, ApplyBandedLayout, BroadcastBandedLayout + triangularlayout, AbstractCachedMatrix, cache_layout, _mulbanded_copyto!, ApplyBandedLayout, BroadcastBandedLayout, CachedArrayStyle import Base: BroadcastStyle, similar, copy, broadcasted, getindex, OneTo, oneto, tail, sign, abs import BandedMatrices: bandedbroadcaststyle, bandwidths, isbanded, bandedcolumns, bandeddata, BandedStyle, AbstractBandedLayout, AbstractBandedMatrix, BandedColumns, BandedRows, BandedSubBandedMatrix, @@ -25,12 +25,18 @@ hermitianlayout(::Type{<:Complex}, ::AbstractLazyBandedLayout) = HermitianLayout bandedbroadcaststyle(::AbstractLazyArrayStyle) = LazyArrayStyle{2}() +bandedbroadcaststyle(::CachedArrayStyle) = CachedArrayStyle{2}() BroadcastStyle(::AbstractLazyArrayStyle{1}, ::BandedStyle) = LazyArrayStyle{2}() BroadcastStyle(::BandedStyle, ::AbstractLazyArrayStyle{1}) = LazyArrayStyle{2}() BroadcastStyle(::AbstractLazyArrayStyle{2}, ::BandedStyle) = LazyArrayStyle{2}() BroadcastStyle(::BandedStyle, ::AbstractLazyArrayStyle{2}) = LazyArrayStyle{2}() +BroadcastStyle(::CachedArrayStyle{1}, ::BandedStyle) = CachedArrayStyle{2}() +BroadcastStyle(::BandedStyle, ::CachedArrayStyle{1}) = CachedArrayStyle{2}() +BroadcastStyle(::CachedArrayStyle{2}, ::BandedStyle) = CachedArrayStyle{2}() +BroadcastStyle(::BandedStyle, ::CachedArrayStyle{2}) = CachedArrayStyle{2}() + bandedcolumns(::AbstractLazyLayout) = BandedColumns{LazyLayout}() bandedcolumns(::DualLayout{<:AbstractLazyLayout}) = BandedColumns{LazyLayout}() @@ -428,6 +434,7 @@ isbanded(::AbstractPaddedLayout, A) = true # always treat as banded const HcatBandedMatrix{T,N} = Hcat{T,NTuple{N,BandedMatrix{T,Matrix{T},OneTo{Int}}}} const VcatBandedMatrix{T,N} = Vcat{T,2,NTuple{N,BandedMatrix{T,Matrix{T},OneTo{Int}}}} +BroadcastStyle(::Type{HcatBandedMatrix{T,0}}) where {T} = LazyArrayStyle{2}() # This method is needed for e.g. Hcat(), which would otherwise get recognised as a BandedStyle, giving different behaviour before-and-after loading BandedMatrices BroadcastStyle(::Type{HcatBandedMatrix{T,N}}) where {T,N} = BandedStyle() BroadcastStyle(::Type{VcatBandedMatrix{T,N}}) where {T,N} = BandedStyle() diff --git a/src/LazyArrays.jl b/src/LazyArrays.jl index 6efb82c2..8db0b4c7 100644 --- a/src/LazyArrays.jl +++ b/src/LazyArrays.jl @@ -18,7 +18,7 @@ import Base: *, +, -, /, <, ==, >, \, ≤, ≥, (:), @_gc_preserve_begin, @_gc_p oneto, add_sum, promote_op import Base.Broadcast: AbstractArrayStyle, BroadcastStyle, Broadcasted, DefaultArrayStyle, broadcasted, combine_eltypes, - instantiate + instantiate, result_style, Unknown import LinearAlgebra: AbstractQ, AdjOrTrans, StructuredMatrixStyle, checksquare, det, diag, dot, lmul!, logabsdet, norm1, norm2, normInf, normp, pinv, rmul!, tr, tril, triu diff --git a/src/cache.jl b/src/cache.jl index 8f575dd5..98d0756c 100644 --- a/src/cache.jl +++ b/src/cache.jl @@ -62,6 +62,8 @@ cacheddata(A::Transpose) = transpose(cacheddata(parent(A))) maybe_cacheddata(A::AbstractCachedArray) = cacheddata(A) maybe_cacheddata(A::SubArray{<:Any,N,<:AbstractCachedArray}) where N = cacheddata(A) +maybe_cacheddata(A::Adjoint) = adjoint(maybe_cacheddata(parent(A))) +maybe_cacheddata(A::Transpose) = transpose(maybe_cacheddata(parent(A))) maybe_cacheddata(A) = A # no-op convert(::Type{AbstractArray{T}}, S::CachedArray{T}) where T = S @@ -228,9 +230,13 @@ resizedata!(B::CachedArray, mn...) = resizedata!(MemoryLayout(B.data), MemoryLay resizedata!(B::AbstractCachedArray, mn...) = resizedata!(MemoryLayout(B.data), UnknownLayout(), B, mn...) resizedata!(A, mn...) = A # don't do anything function resizedata!(A::AdjOrTrans, m, n) - m ≤ 0 || resizedata!(parent(A), n) + resizedata!(parent(A), n, m) A end +function resizedata!(A::AdjOrTrans{<:Any, <:AbstractVector}, m, n) + resizedata!(parent(A), n) + A +end function cache_filldata!(B, inds...) B.data[inds...] .= view(B.array,inds...) @@ -611,4 +617,85 @@ CachedArray(data::AbstractMatrix, array::AbstractQ) = CachedArray(data, array, s CachedArray(data::AbstractMatrix{T}, array::AbstractQ{T}, datasize::NTuple{2,Int}) where T = CachedMatrix{T,typeof(data),typeof(array)}(data, array, datasize) -length(A::CachedMatrix{<:T,<:AbstractMatrix{T},<:AbstractQ{T}}) where T = prod(size(A.array)) \ No newline at end of file +length(A::CachedMatrix{<:T,<:AbstractMatrix{T},<:AbstractQ{T}}) where T = prod(size(A.array)) + +## +# Tuples of potential cached arrays +# Assumes that the Tuples contain arrays of the same shape +# Some of the complication here is in making sure that we can handle things like mixing vectors and matrices that have one column +## +@inline _tuple_wrap(x::Int) = (x,) # Some CachedArrays mistakenly use Int instead of Tuple for vector sizes (e.g. https://github.com/JuliaApproximation/SemiclassicalOrthogonalPolynomials.jl/blob/a29007f2815134180b8433fdab46a23acfcdcd01/src/neg1c.jl#L19 and https://github.com/DanielVandH/InfiniteRandomArrays.jl/blob/a859d565f5bd8278d3f3499cd26b62916b4867e0/src/vector.jl#L18, to name a couple) +@inline _tuple_wrap(x::Tuple) = x +@inline _datasize(x) = size(x) +@inline _datasize(::Number) = (1,) # Numbers have size (), but we treat them as size (1,) for resizing purposes +@inline _datasize(x::AbstractCachedArray) = x.datasize +@inline _datasize(x::SubArray) = _datasize(parent(x)) +@inline _datasize(x::AdjOrTrans) = reverse(_datasize(parent(x))) +@inline _datasize(x::AdjOrTrans{<:Any, <:AbstractVector}) = (sz = only(_datasize(parent(x))); (min(1, sz), sz)) +@inline _datasizes(::Tuple{}) = () +@inline _datasizes(t::Tuple) = (_datasize(first(t)), _datasizes(Base.tail(t))...) # equivalent to _datasize.(t) + +@inline _has_vector(::Tuple{}) = false # Numbers have size () +@inline _has_vector(::Tuple{Any}) = true +@inline _has_vector(::Tuple{Any, Vararg}) = false +@inline has_vector(::Tuple{}) = false +@inline has_vector(t::Tuple) = _has_vector(first(t)) || has_vector(Base.tail(t)) +@inline to_vector_size(sz::Tuple{Any}) = sz # Already 1-tuple +@inline to_vector_size(sz::Tuple{Any, Vararg}) = (prod(sz),) # Multi-tuple -> 1-tuple + +function max_datasize(sizes::Tuple) + if has_vector(sizes) + normalized = map(to_vector_size, sizes) + return reduce(@inline((a, b) -> max.(a, b)), normalized) + else + return reduce(@inline((a, b) -> max.(a, b)), sizes) + end +end + +@inline _expand_size(sz::Tuple{Any}, ::Tuple{Any}) = sz +@inline _expand_size(sz::Tuple{Any}, ::Tuple{Any, Vararg}) = (only(sz), 1) +@inline _expand_size(sz::Tuple{Any, Vararg}, ::Tuple{Any, Vararg}) = sz + +@inline _effective_ndims(sz::Int) = 1 +@inline function _effective_ndims(sz::Tuple) + length(sz) == 1 && return 1 + length(sz) == 2 && minimum(sz) <= 1 && return 1 + return length(sz) +end + +@inline _all_same_ndims(::Tuple{}) = true +@inline _all_same_ndims(t::Tuple{Any}) = true +@inline function _all_same_ndims(t::Tuple) + first_sz = _arraysize(first(t)) + first_ndim = _effective_ndims(first_sz) + _check_same_ndims_args(first_ndim, Base.tail(t)) +end + +@inline _check_same_ndims_args(::Int, ::Tuple{}) = true +@inline function _check_same_ndims_args(expected_ndim::Int, t::Tuple) + sz = _arraysize(first(t)) + _effective_ndims(sz) == expected_ndim && _check_same_ndims_args(expected_ndim, Base.tail(t)) +end + +@inline _arraysize(x::Number) = (1,) +@inline _arraysize(x) = size(x) + +function conforming_resize!(args::Tuple) + isempty(args) && return args + if !_all_same_ndims(args) + throw(ArgumentError("Cannot conform arrays with incompatible dimensions: $(map(_arraysize, args))")) + end + sizes = _datasizes(args) + sz = max_datasize(sizes) + _resize_each!(args, sizes, sz) + return args +end + +@inline _resize_each!(::Tuple{}, ::Tuple{}, sz) = nothing +@inline function _resize_each!(args::Tuple, sizes::Tuple, sz) + arg = first(args) + orig_sz = first(sizes) + target_sz = _expand_size(sz, orig_sz) + resizedata!(arg, target_sz...) + _resize_each!(Base.tail(args), Base.tail(sizes), sz) +end \ No newline at end of file diff --git a/src/lazyapplying.jl b/src/lazyapplying.jl index dfc1e18e..67d76005 100644 --- a/src/lazyapplying.jl +++ b/src/lazyapplying.jl @@ -228,7 +228,11 @@ AbstractArray{T,N}(A::ApplyArray{<:Any,N}) where {T,N} = ApplyArray{T,N}(A.f, ma @inline applied_axes(f, args...) = map(oneto, applied_size(f, args...)) - +function cacheddata(A::ApplyArray{T, N, F}) where {T, N, F} + args = arguments(A) + conforming_resize!(args) + ApplyArray{T, N}(A.f, map(maybe_cacheddata, args)...) +end # immutable arrays don't need to copy. # Some special cases like vcat overload setindex! and therefore @@ -329,8 +333,13 @@ function show(io::IO, A::Applied) print(io, ')') end +_BroadcastStyle(x...) = BroadcastStyle(x...) +_BroadcastStyle(::Type{<:AbstractQ}) = DefaultArrayStyle{2}() + applybroadcaststyle(::Type{<:AbstractArray{<:Any,N}}, _2) where N = DefaultArrayStyle{N}() applybroadcaststyle(::Type{<:AbstractArray{<:Any,N}}, ::AbstractLazyLayout) where N = LazyArrayStyle{N}() +applybroadcaststyle(::Type{<:ApplyArray{<:Any,N,<:Any,Args}}, ::AbstractLazyLayout) where {N,Args<:Tuple} = result_style(LazyArrayStyle{N}(), tuple_type_broadcastlayout(Args)) +applybroadcaststyle(::Type{<:SubArray{<:Any,<:Any,P}}, lay::AbstractLazyLayout) where {P} = applybroadcaststyle(P, lay) BroadcastStyle(M::Type{<:ApplyArray}) = applybroadcaststyle(M, MemoryLayout(M)) BroadcastStyle(M::Type{<:SubArray{<:Any,N,<:ApplyArray}}) where N = applybroadcaststyle(M, MemoryLayout(M)) diff --git a/src/lazybroadcasting.jl b/src/lazybroadcasting.jl index 5c66ac2b..c7ffabea 100644 --- a/src/lazybroadcasting.jl +++ b/src/lazybroadcasting.jl @@ -52,6 +52,8 @@ end const BroadcastVector{T,F,Args} = BroadcastArray{T,1,F,Args} const BroadcastMatrix{T,F,Args} = BroadcastArray{T,2,F,Args} +BroadcastStyle(::Type{<:BroadcastArray{<:Any,N,<:Any,Args}}) where {N,Args<:Tuple} = result_style(LazyArrayStyle{N}(), tuple_type_broadcastlayout(Args)) + LazyArray(bc::Broadcasted) = BroadcastArray(bc) BroadcastArray{T,N,F,Args}(bc::Broadcasted) where {T,N,F,Args} = BroadcastArray{T,N,F,Args}(bc.f,bc.args) @@ -120,6 +122,12 @@ sub_materialize(::BroadcastLayout, A) = converteltype(eltype(A), sub_materialize copy(bc::Broadcasted{<:AbstractLazyArrayStyle}) = BroadcastArray(bc) +function cacheddata(A::BroadcastArray{T, N, F}) where {T, N, F} + args = arguments(A) + conforming_resize!(args) + BroadcastArray{T, N}(A.f, map(maybe_cacheddata, args)...) +end + # BroadcastArray are immutable copy(bc::BroadcastArray) = bc map(::typeof(copy), bc::BroadcastArray) = bc diff --git a/src/lazyconcat.jl b/src/lazyconcat.jl index e03382ee..1ae4b18d 100644 --- a/src/lazyconcat.jl +++ b/src/lazyconcat.jl @@ -256,7 +256,7 @@ function vcat_copyto!(arr::AbstractVector, arrays...) i = firstindex(arr) for a in arrays m = length(a) - copyto!(view(arr, range(i, length=m)), a) + m > 0 && copyto!(view(arr, range(i, length=m)), a) i += m end arr @@ -439,8 +439,16 @@ end # to take advantage of special implementations of the sub-components ###### -BroadcastStyle(::Type{<:Vcat{<:Any,N}}) where N = LazyArrayStyle{N}() -BroadcastStyle(::Type{<:Hcat{<:Any}}) = LazyArrayStyle{2}() +@inline tuple_type_broadcastlayout(::Type{I}) where {I<:Tuple} = result_style(_BroadcastStyle(Base.tuple_type_head(I)), tuple_type_broadcastlayout(Base.tuple_type_tail(I))) +@inline tuple_type_broadcastlayout(::Type{Tuple{}}) = Unknown() +@inline tuple_type_broadcastlayout(::Type{Tuple{A}}) where {A} = _BroadcastStyle(A) +@inline tuple_type_broadcastlayout(::Type{Tuple{A,B}}) where {A,B} = result_style(_BroadcastStyle(A), _BroadcastStyle(B)) +@inline tuple_type_broadcastlayout(::Type{Tuple{A,B,C}}) where {A,B,C} = result_style(_BroadcastStyle(A), tuple_type_broadcastlayout(Tuple{B,C})) +@inline tuple_type_broadcastlayout(::Type{Tuple{A,B,C,D}}) where {A,B,C,D} = result_style(_BroadcastStyle(A), tuple_type_broadcastlayout(Tuple{B,C,D})) +@inline tuple_type_broadcastlayout(::Type{Tuple{A,B,C,D,E}}) where {A,B,C,D,E} = result_style(_BroadcastStyle(A), tuple_type_broadcastlayout(Tuple{B,C,D,E})) + +BroadcastStyle(::Type{<:Vcat{<:Any,N,I}}) where {N,I<:Tuple} = result_style(LazyArrayStyle{N}(), tuple_type_broadcastlayout(I)) # the <:Tuple is to avoid ambiguity +BroadcastStyle(::Type{<:Hcat{<:Any,I}}) where {I<:Tuple} = result_style(LazyArrayStyle{2}(), tuple_type_broadcastlayout(I)) # This is if we broadcast a function on a mixed concat f.([1; [2,3]]) # such that f returns a vector, e.g., f(1) == [1,2], we don't want diff --git a/src/lazyoperations.jl b/src/lazyoperations.jl index 347615f5..2090a5f1 100644 --- a/src/lazyoperations.jl +++ b/src/lazyoperations.jl @@ -438,25 +438,35 @@ copy(Q::Accumulate) = Accumulate(Q.op, copy(Q.data), copy(Q.v), Q.dims, Q.datasi copyto!(x::AbstractArray{<:Any,N}, C::Cumsum{<:Any,N}) where N = cumsum!(x, C.v) # How we populate the data -function cache_filldata!(K::Accumulate{<:Any,1}, inds) - copyto!(view(K.data,inds), view(K.v,inds)) +function _accumulate_cache_filldata_1d!(data, v, op::F, inds) where {F} + copyto!(view(data,inds), view(v,inds)) @inbounds for k in inds - K.data[k] = K.op(K.data[k-1], K.data[k]) + data[k] = op(data[k-1], v[k]) end end -function cache_filldata!(K::Accumulate{<:Any,2}, kr, jr) - if K.dims == 1 +function cache_filldata!(K::Accumulate{<:Any,1}, inds) + _accumulate_cache_filldata_1d!(K.data, K.v, K.op, inds) +end + +function _accumulate_cache_filldata_2d!(data, v, op::F, kr, jr, dims) where {F} + if dims == 1 + copyto!(view(data, kr, jr), view(v, kr, jr)) @inbounds for j in jr, k in kr - K.data[k,j] = K.op(K.data[k-1,j], K.v[k,j]) + data[k,j] = op(data[k-1,j], v[k,j]) end - else # K.dims == 2 + else # dims == 2 + copyto!(view(data, kr, jr), view(v, kr, jr)) @inbounds for j in jr, k in kr - K.data[k,j] = K.op(K.data[k,j-1], K.v[k,j]) + data[k,j] = op(data[k,j-1], v[k,j]) end end end +function cache_filldata!(K::Accumulate{<:Any,2}, kr, jr) + _accumulate_cache_filldata_2d!(K.data, K.v, K.op, kr, jr, K.dims) +end + # keep lazy cumsum(a::LazyArray; kwds...) = Cumsum(a; kwds...) diff --git a/src/padded.jl b/src/padded.jl index 5fbf5043..7d4a95cc 100644 --- a/src/padded.jl +++ b/src/padded.jl @@ -149,8 +149,6 @@ _vcat_resizedata!(_, B, m...) = B # by default we can't resize resizedata!(B::Vcat, m...) = _vcat_resizedata!(MemoryLayout(B), B, m...) -cacheddata(B::Vcat) = Vcat(map(maybe_cacheddata, arguments(B))...) - function ==(A::CachedVector{<:Any,<:Any,<:Zeros}, B::CachedVector{<:Any,<:Any,<:Zeros}) length(A) == length(B) || return false n = max(A.datasize[1], B.datasize[1]) diff --git a/test/applytests.jl b/test/applytests.jl index 5b9bf30a..7c0085d9 100644 --- a/test/applytests.jl +++ b/test/applytests.jl @@ -1,10 +1,12 @@ module ApplyTests -using LazyArrays, FillArrays, ArrayLayouts, Test -import LazyArrays: materialize, broadcasted, DefaultApplyStyle, Applied, arguments, - ApplyArray, ApplyMatrix, ApplyVector, LazyArrayApplyStyle, ApplyLayout, call -import ArrayLayouts: StridedLayout -using LinearAlgebra + using LazyArrays, FillArrays, ArrayLayouts, Test + import LazyArrays: materialize, broadcasted, DefaultApplyStyle, Applied, arguments, + ApplyArray, ApplyMatrix, ApplyVector, LazyArrayApplyStyle, ApplyLayout, call, + CachedArrayStyle, CachedArray, LazyArrayStyle + import ArrayLayouts: StridedLayout + import Base.Broadcast: BroadcastStyle + using LinearAlgebra @testset "Applying" begin @testset "Applied" begin @@ -121,6 +123,13 @@ using LinearAlgebra end @test colsupport(M,1) == 1:5 end + + @testset "BroadcastStyle with a cached argument" begin + A = ApplyArray(*, rand(10, 2), rand(2, 2)) + @test BroadcastStyle(typeof(A)) == BroadcastStyle(typeof(A')) == BroadcastStyle(typeof(view(A, 1:2, 1:2))) == LazyArrayStyle{2}() + A = ApplyArray(*, CachedArray(rand(10, 2)), rand(2, 2)) + @test BroadcastStyle(typeof(A)) == BroadcastStyle(typeof(A')) == BroadcastStyle(typeof(view(A, 1:2, 1:2))) == CachedArrayStyle{2}() + end end # testset end # module diff --git a/test/bandedtests.jl b/test/bandedtests.jl index 7e9c075f..7b729aec 100644 --- a/test/bandedtests.jl +++ b/test/bandedtests.jl @@ -1,8 +1,9 @@ module LazyBandedTests using ArrayLayouts, LazyArrays, BandedMatrices, LinearAlgebra, Test using BandedMatrices: AbstractBandedLayout, _BandedMatrix, isbanded, BandedStyle, BandedColumns, BandedRows, resize, bandeddata -using LazyArrays: PaddedLayout, PaddedRows, PaddedColumns, arguments, call, LazyArrayStyle, ApplyLayout, simplifiable, resizedata!, MulStyle, LazyLayout, BroadcastLayout +using LazyArrays: PaddedLayout, PaddedRows, Accumulate, PaddedColumns, arguments, call, LazyArrayStyle, ApplyLayout, simplifiable, resizedata!, MulStyle, LazyLayout, BroadcastLayout, CachedArrayStyle using ArrayLayouts: OnesLayout, StridedLayout +import Base.Broadcast: BroadcastStyle LazyArraysBandedMatricesExt = Base.get_extension(LazyArrays, :LazyArraysBandedMatricesExt) BroadcastBandedLayout = LazyArraysBandedMatricesExt.BroadcastBandedLayout ApplyBandedLayout = LazyArraysBandedMatricesExt.ApplyBandedLayout @@ -967,6 +968,19 @@ LinearAlgebra.lmul!(β::Number, A::PseudoBandedMatrix) = (lmul!(β, A.data); A) @test A\D ≈ (B*B)\D @test D\A ≈ D\(B*B) end + + @testset "BroadcastStyle with cached data" begin + A = _BandedMatrix(Accumulate(*, 1:10)', 1:10, 0, 0) + @test BroadcastStyle(typeof(A)) == CachedArrayStyle{2}() + @test BroadcastStyle(typeof(A')) == CachedArrayStyle{2}() + end + + @testset "Combining BandedStyle with CachedArrayStyle" begin + @test BroadcastStyle(CachedArrayStyle{1}(), BandedStyle()) == CachedArrayStyle{2}() + @test BroadcastStyle(BandedStyle(), CachedArrayStyle{1}()) == CachedArrayStyle{2}() + @test BroadcastStyle(CachedArrayStyle{2}(), BandedStyle()) == CachedArrayStyle{2}() + @test BroadcastStyle(BandedStyle(), CachedArrayStyle{2}()) == CachedArrayStyle{2}() + end end end # module \ No newline at end of file diff --git a/test/broadcasttests.jl b/test/broadcasttests.jl index f148b06a..88d1502a 100644 --- a/test/broadcasttests.jl +++ b/test/broadcasttests.jl @@ -2,8 +2,9 @@ module BroadcastTests using LazyArrays, ArrayLayouts, LinearAlgebra, FillArrays, Base64, Test using StaticArrays, Tracker -import LazyArrays: BroadcastLayout, arguments, LazyArrayStyle, sub_materialize, simplifiable +import LazyArrays: BroadcastLayout, CachedArrayStyle, arguments, LazyArrayStyle, sub_materialize, simplifiable import Base: broadcasted +import Base.Broadcast: BroadcastStyle using ..InfiniteArrays using Infinities @@ -478,6 +479,13 @@ using Infinities @test_throws "MethodError: no method matching _vec_mul_arguments" LazyArrays._vec_mul_arguments(2, []) end end + + @testset "BroadcastStyle" begin + @test BroadcastStyle(typeof(BroadcastVector(exp, 1:10))) == LazyArrayStyle{1}() + @test BroadcastStyle(typeof(BroadcastVector(+, 1:10, cache(1:10)))) == CachedArrayStyle{1}() + @test BroadcastStyle(typeof(BroadcastMatrix(*, Accumulate(*, 1:10)', rand(10)'))) == CachedArrayStyle{2}() + @test BroadcastStyle(typeof(BroadcastMatrix(*, rand(5, 5)', LazyArrays.CachedArray(rand(5, 5))))) == CachedArrayStyle{2}() + end end end #module diff --git a/test/cachetests.jl b/test/cachetests.jl index 2131e254..380e5643 100644 --- a/test/cachetests.jl +++ b/test/cachetests.jl @@ -6,6 +6,7 @@ import LazyArrays: CachedArray, CachedMatrix, CachedVector, PaddedLayout, Cached CachedAbstractArray, CachedAbstractVector, CachedAbstractMatrix, AbstractCachedArray, AbstractCachedMatrix, PaddedColumns, cacheddata, LazyArrayStyle, maybe_cacheddata, Accumulate, CachedArrayStyle, GenericCachedLayout, AccumulateAbstractVector +import Base.Broadcast: BroadcastStyle using ..InfiniteArrays using .InfiniteArrays: OneToInf @@ -551,6 +552,11 @@ using Infinities @test maybe_cacheddata(B) === cacheddata(B) C = [1, 2, 3] @test maybe_cacheddata(C) === C + + v = cache(1:10)' + @test maybe_cacheddata(v) === cacheddata(parent(v))' + v = transpose(cache(1:10)) + @test maybe_cacheddata(v) === transpose(cacheddata(parent(v))) end @testset "Missing BroadcastStyles/MemoryLayouts/cacheddata with CachedArrayStyles" begin @@ -574,6 +580,161 @@ using Infinities @test cacheddata(F) === view(cacheddata(parent(parent(F))), 1:1)' @test cacheddata(G) === adjoint(view(cacheddata(parent(G)), 1:1, 1:1)) end -end + + @testset "BroadcastStyle for Vcat/Hcat of CachedArrayStyles" begin + @test BroadcastStyle(typeof(Vcat(cache(1:3), cache(4:6)))) == CachedArrayStyle{1}() + d = Accumulate(*, 1:10) + @test BroadcastStyle(typeof(Vcat(d, d))) == CachedArrayStyle{1}() + @test BroadcastStyle(typeof(Vcat(d', d'))) == CachedArrayStyle{2}() + @test BroadcastStyle(typeof(Hcat(d, d))) == CachedArrayStyle{2}() + @test BroadcastStyle(typeof(Vcat(d', (1:10)'))) == CachedArrayStyle{2}() + @test BroadcastStyle(typeof(Vcat((1:10)', d'))) == CachedArrayStyle{2}() + @test BroadcastStyle(typeof(Hcat(d, (1:10)))) == CachedArrayStyle{2}() + @test BroadcastStyle(typeof(Hcat((1:10), d))) == CachedArrayStyle{2}() + end + + @testset "Enforce same-size arguments for cacheddata" begin + @testset "max_datasize" begin + @test LazyArrays._datasize(1:10) == (10,) + x = cache(1:10) + @test LazyArrays._datasize(x) == (0,) + resizedata!(x, 3) + @test LazyArrays._datasize(x) == (3,) + @test LazyArrays._datasize(view(x, 3:5)) == (3,) + @test LazyArrays._datasize(transpose(x)) == (1, 3) + + arr = (1:10, cache(1:10)) + @test LazyArrays.max_datasize(LazyArrays._datasizes(arr)) == (10,) + + arr = (cache(1:10), cache(1:10)) + resizedata!(arr[1], 3) + @test LazyArrays.max_datasize(LazyArrays._datasizes(arr)) == (3,) + resizedata!(arr[2], 7) + @test LazyArrays.max_datasize(LazyArrays._datasizes(arr)) == (7,) + + arr = (rand(10, 5), LazyArrays.CachedArray(rand(10, 5))) + @test LazyArrays.max_datasize(LazyArrays._datasizes(arr)) == (10, 5) + + arr = (LazyArrays.CachedArray(rand(10, 5)), LazyArrays.CachedArray(rand(10, 5))) + @test LazyArrays.max_datasize(LazyArrays._datasizes(arr)) == (0, 0) + resizedata!(arr[1], 3, 4) + @test LazyArrays.max_datasize(LazyArrays._datasizes(arr)) == (3, 4) + resizedata!(arr[2], 2, 5); + @test LazyArrays.max_datasize(LazyArrays._datasizes(arr)) == (3, 5) + + arr = (1, [1, 2]) + @test LazyArrays._datasize(1) == (1, ) + @test LazyArrays.max_datasize(LazyArrays._datasizes(arr)) == (2,) + end + + @testset "conforming_resize!" begin + args = (cache(1:10), cache(1:10)); + LazyArrays.conforming_resize!(args); + @test LazyArrays._datasizes(args) == ((0,), (0,)); + LazyArrays.resizedata!(args[2], 4); + LazyArrays.conforming_resize!(args); + @test LazyArrays._datasizes(args) == ((4,), (4,)); + + args = (1:10, cache(1:10)); + LazyArrays.conforming_resize!(args); + @test LazyArrays._datasizes(args) == ((10,), (10,)); + + args = (cache(1:10)', cache(1:10)'); + LazyArrays.conforming_resize!(args); + @test LazyArrays._datasizes(args) == ((0, 0), (0, 0)); + LazyArrays.resizedata!(args[1], 1, 3); + LazyArrays.conforming_resize!(args); + @test LazyArrays._datasizes(args) == ((1, 3), (1, 3)); + + args = (cache(1:10)', LazyArrays.CachedArray(rand(1, 10))); + @test LazyArrays._datasizes(args) == ((0, 0), (0, 0)); + LazyArrays.conforming_resize!(args); + @test LazyArrays._datasizes(args) == ((0, 0), (0, 0)); + LazyArrays.resizedata!(args[1], 1, 4); + LazyArrays.conforming_resize!(args); + @test LazyArrays._datasizes(args) == ((1, 4), (1, 4)); + LazyArrays.resizedata!(args[2], 1, 6) + LazyArrays.conforming_resize!(args); + @test LazyArrays._datasizes(args) == ((1, 6), (1, 6)); + + args = (cache(1:10), LazyArrays.CachedArray(rand(1, 10))'); + LazyArrays.resizedata!(args[1], 4); + LazyArrays.conforming_resize!(args); + @test LazyArrays._datasizes(args) == ((4,), (4, 1)); + + args = (cache(1:10), LazyArrays.CachedArray(rand(1, 10))', 1:10); + LazyArrays.conforming_resize!(args); + @test LazyArrays._datasizes(args) == ((10,), (10, 1), (10,)); + + args = (1, cache(1:2)); + LazyArrays.conforming_resize!(args); + @test LazyArrays._datasizes(args) == ((1,), (1,)) # because scalars are treated as size (1,) + + args = (rand(10, 10), LazyArrays.CachedArray(rand(10, 10))); + LazyArrays.conforming_resize!(args); + @test LazyArrays._datasizes(args) == ((10, 10), (10, 10)); + + args = (LazyArrays.CachedArray(rand(10, 10)), LazyArrays.CachedArray(rand(10, 10))); + LazyArrays.conforming_resize!(args); + @test LazyArrays._datasizes(args) == ((0, 0), (0, 0)); + LazyArrays.resizedata!(args[1], 3, 4); + LazyArrays.conforming_resize!(args); + @test LazyArrays._datasizes(args) == ((3, 4), (3, 4)); + LazyArrays.resizedata!(args[2], 5, 6); + LazyArrays.conforming_resize!(args); + @test LazyArrays._datasizes(args) == ((5, 6), (5, 6)); + + @testset "conforming_resize! dimension mismatch" begin + args = (cache(1:10), cache(1:10)) + @test LazyArrays.conforming_resize!(args) === args + + args = (cache(1:10), LazyArrays.CachedArray(rand(10, 5))) + @test_throws ArgumentError LazyArrays.conforming_resize!(args) + + args = (LazyArrays.CachedArray(rand(3, 4)), LazyArrays.CachedArray(rand(5, 2))) + @test LazyArrays.conforming_resize!(args) === args + + args = (1, cache(1:10)) + @test LazyArrays.conforming_resize!(args) === args + + args = (cache(1:10), LazyArrays.CachedArray(rand(2, 3)), reshape(cache(1:8), 2, 2, 2)) + @test_throws ArgumentError LazyArrays.conforming_resize!(args) + + args = (reshape(cache(1:8), 2, 2, 2), reshape(cache(1:27), 3, 3, 3)) + @test LazyArrays.conforming_resize!(args) === args + + args = () + @test LazyArrays.conforming_resize!(args) === args + + args = (cache(1:10),) + @test LazyArrays.conforming_resize!(args) === args + end + end + end + + @testset "cacheddata for ApplyArray and BroadcastArray" begin + x = ApplyArray(+, 1:10, cache(11:20)); + @test cacheddata(x) == ApplyArray(+, 1:10, 11:20) + @test Base.Broadcast.BroadcastStyle(typeof(cacheddata(x))) == LazyArrays.LazyArrayStyle{1}() + + x = BroadcastVector(*, 1:10, cache(1:10)); + @test cacheddata(x) == BroadcastVector(*, 1:10, 1:10) + @test Base.Broadcast.BroadcastStyle(typeof(cacheddata(x))) == LazyArrays.LazyArrayStyle{1}() + + x = ApplyArray(+, cache(1:10), cache(11:20)); + @test cacheddata(x) == ApplyArray(+, 1:0, 1:0) + @test Base.Broadcast.BroadcastStyle(typeof(cacheddata(x))) == LazyArrays.LazyArrayStyle{1}() + LazyArrays.resizedata!(x.args[1], 3) + @test cacheddata(x) == ApplyArray(+, 1:3, 11:13) + @test Base.Broadcast.BroadcastStyle(typeof(cacheddata(x))) == LazyArrays.LazyArrayStyle{1}() + + x = BroadcastVector(*, cache(1:10), cache(11:20)); + @test cacheddata(x) == BroadcastVector(*, 1:0, 1:0) + @test Base.Broadcast.BroadcastStyle(typeof(cacheddata(x))) == LazyArrays.LazyArrayStyle{1}() + LazyArrays.resizedata!(x.args[1], 4) + @test cacheddata(x) == BroadcastVector(*, 1:4, 11:14) + @test Base.Broadcast.BroadcastStyle(typeof(cacheddata(x))) == LazyArrays.LazyArrayStyle{1}() + end +end end # module diff --git a/test/concattests.jl b/test/concattests.jl index 97a64de7..955f6802 100644 --- a/test/concattests.jl +++ b/test/concattests.jl @@ -4,7 +4,9 @@ using LazyArrays, FillArrays, LinearAlgebra, ArrayLayouts, Test, Base64 using StaticArrays import LazyArrays: MemoryLayout, DenseColumnMajor, materialize!, call, paddeddata, MulAdd, Applied, ApplyLayout, DefaultApplyStyle, sub_materialize, resizedata!, - CachedVector, ApplyLayout, arguments, BroadcastVector, LazyLayout, cacheddata + CachedVector, ApplyLayout, arguments, BroadcastVector, LazyLayout, cacheddata, + LazyArrayStyle, CachedArrayStyle, Accumulate +import Base.Broadcast: BroadcastStyle @testset "concat" begin @testset "Vcat" begin @@ -222,11 +224,14 @@ import LazyArrays: MemoryLayout, DenseColumnMajor, materialize!, call, paddeddat end @testset "cacheddata" begin - v = Vcat(1, cache(1:2)) - @test @inferred(cacheddata(v)) == [1] - resizedata!(v, 2) + v = Vcat(1, cache(1:2)); @test @inferred(cacheddata(v)) == [1, 1] + resizedata!(v, 3) + @test @inferred(cacheddata(v)) == [1, 1, 2] @test cacheddata(v) isa Vcat + + v = Vcat((1:10)', cache(11:20)'); + @test @inferred(cacheddata(v)) == Vcat((1:10)', (11:20)') end p = Vcat([1,2], Zeros(4)); @@ -710,6 +715,32 @@ import LazyArrays: MemoryLayout, DenseColumnMajor, materialize!, call, paddeddat args = LazyArrays._vcat_sub_arguments(MemoryLayout(V), V, (), 0, 1:3) @test args == () end + + @testset "BroadcastStyle" begin + args = (1:10, Accumulate(*, 1:10), BroadcastVector(exp, 1:10), BroadcastMatrix(exp, rand(10, 2)), Vcat(Accumulate(*, 1:10)', (1:10)')', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + for i in 1:6 + @test @inferred(LazyArrays.tuple_type_broadcastlayout(typeof(args[1:i]))) == Base.Broadcast.combine_styles(args[1:i]...) + end + @test @inferred(LazyArrays.tuple_type_broadcastlayout(typeof(args))) == Base.Broadcast.combine_styles(args...) == CachedArrayStyle{2}() + @test @inferred(LazyArrays.tuple_type_broadcastlayout(Tuple{})) == Base.Broadcast.Unknown() + @test BroadcastStyle(typeof(Vcat(adjoint.(args)...))) == BroadcastStyle(typeof(Vcat(transpose.(args)...))) == CachedArrayStyle{2}() + @test BroadcastStyle(typeof(Hcat(args...))) == CachedArrayStyle{2}() + + @test BroadcastStyle(typeof(Vcat((1:10)'))) == LazyArrayStyle{2}() # make sure we preserve Lazy even without lazy args + @test BroadcastStyle(typeof(Hcat((1:10)'))) == LazyArrayStyle{2}() + + @test BroadcastStyle(typeof(Vcat())) == LazyArrayStyle{1}() + @test BroadcastStyle(typeof(Hcat())) == LazyArrayStyle{2}() + end + + @testset "Previously broken case with cacheddata storing a Vcat" begin + v = Accumulate(*, Vcat(cache(1:3), 5)); + resizedata!(v, 4) + @test v.datasize == (4,) + @test v == [1, 2, 6, 30] + @test v[4] == 30 + @test Base.isassigned(v, 4) + end end end # module diff --git a/test/runtests.jl b/test/runtests.jl index 6b68ff9d..870f34fe 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -15,6 +15,13 @@ end # we include this at the top-level, so that other sub-modules may reuse the module instead of having to include the file include("infinitearrays.jl") +@testset "Avoiding unwanted behaviour from BandedMatrices extension" begin + # This test needs to be included before BandedMatrices is loaded + @test Base.Broadcast.BroadcastStyle(typeof(Hcat())) == LazyArrayStyle{2}() + using BandedMatrices + @test Base.Broadcast.BroadcastStyle(typeof(Hcat())) == LazyArrayStyle{2}() +end + @testset "Lazy MemoryLayout" begin @testset "ApplyArray" begin A = [1.0 2; 3 4]