From 1ebb4902dc5ae11ebc0614300b3f2cad48350f88 Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Sun, 23 Jul 2023 21:27:35 +0100 Subject: [PATCH] Avoid Applied in ApplyArray (#264) * Avoid Applied in MulArray * add applied_axes etc. * Update lazyconcat.jl * axes calls size * Update inv.jl * Update inv.jl * tests pass * increase coverage * matrix function check applied axes * Update lazyapplying.jl * Update applytests.jl * empty vcat * Update concattests.jl --- Project.toml | 2 +- src/lazyapplying.jl | 96 +++++++++++++++++++++++++---------------- src/lazybroadcasting.jl | 1 + src/lazyconcat.jl | 43 +++++++++--------- src/lazyoperations.jl | 20 +++++---- src/linalg/add.jl | 7 +-- src/linalg/inv.jl | 37 ++++++++-------- src/linalg/mul.jl | 42 ++++++++++++------ src/padded.jl | 7 +-- test/applytests.jl | 7 ++- test/concattests.jl | 12 ++++-- test/runtests.jl | 1 + 12 files changed, 164 insertions(+), 111 deletions(-) diff --git a/Project.toml b/Project.toml index af9ae6a3..fae5b852 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "LazyArrays" uuid = "5078a376-72f3-5289-bfd5-ec5146d43c02" -version = "1.4.1" +version = "1.5.0" [deps] ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" diff --git a/src/lazyapplying.jl b/src/lazyapplying.jl index 4e2eedd8..9e7e2d3b 100644 --- a/src/lazyapplying.jl +++ b/src/lazyapplying.jl @@ -45,6 +45,10 @@ end @inline Applied{Style}(f::F, args::Args) where {Style,F,Args<:Tuple} = Applied{Style,F,Args}(f, args) @inline Applied{Style}(A::Applied) where Style = Applied{Style}(A.f, A.args) +ndims(a::Applied) = applied_ndims(a.f, a.args...) +eltype(a::Applied) = applied_eltype(a.f, a.args...) +axes(a::Applied) = applied_axes(a.f, a.args...) +size(a::Applied) = applied_size(a.f, a.args...) call(a) = a.f call(_, a) = a.f @@ -57,11 +61,19 @@ call(_, a) = a.f @inline arguments(::DualLayout{ML}, a) where ML = arguments(ML(), a) @inline arguments(a::AbstractArray) = arguments(MemoryLayout(a), a) -@inline check_applied_axes(A::Applied) = nothing +@inline check_applied_axes(_...) = nothing +# following repeated due to unexplained allocations @inline function instantiate(A::Applied{Style}) where Style - check_applied_axes(A) - Applied{Style}(A.f, map(instantiate, A.args)) + iargs = map(instantiate, A.args) + check_applied_axes(A.f, iargs...) + Applied{Style}(A.f, iargs) +end + +@inline function applied_instantiate(f, args...) + iargs = map(instantiate, args) + check_applied_axes(f, iargs...) + f, iargs end @inline _typesof() = () @@ -152,10 +164,8 @@ for f in (:exp, :sin, :cos, :sqrt) @eval ApplyStyle(::typeof($f), ::Type{<:AbstractMatrix}) = MatrixFunctionStyle{typeof($f)}() end -function check_applied_axes(A::Applied{<:MatrixFunctionStyle}) - length(A.args) == 1 || throw(ArgumentError("MatrixFunctions only defined with 1 arg")) - axes(A.args[1],1) == axes(A.args[1],2) || throw(DimensionMismatch("matrix is not square: dimensions are $axes(A.args[1])")) -end +@inline matrixfunction_check_applied_axes(a::AbstractMatrix) = axes(a,1) == axes(a,2) || throw(DimensionMismatch("matrix is not square: dimensions are $(axes(a))")) +@inline matrixfunction_check_applied_axes(a...) = nothing for op in (:axes, :size) @eval begin @@ -192,19 +202,27 @@ const ApplyMatrix{T, F, Args<:Tuple} = ApplyArray{T, 2, F, Args} LazyArray(A::Applied) = ApplyArray(A) -ApplyArray{T,N,F,Args}(M::Applied) where {T,N,F,Args} = ApplyArray{T,N,F,Args}(M.f, M.args) -ApplyArray{T,N}(M::Applied{Style,F,Args}) where {T,N,Style,F,Args} = ApplyArray{T,N,F,Args}(instantiate(M)) -ApplyArray{T}(M::Applied) where {T} = ApplyArray{T,ndims(M)}(M) -ApplyArray(M::Applied) = ApplyArray{eltype(M)}(M) -ApplyVector(M::Applied) = ApplyVector{eltype(M)}(M) -ApplyMatrix(M::Applied) = ApplyMatrix{eltype(M)}(M) +@inline ApplyArray{T,N,F,Args}(M::Applied) where {T,N,F,Args} = ApplyArray{T,N,F,Args}(M.f, M.args) +@inline ApplyArray{T,N}(M::Applied{Style,F,Args}) where {T,N,Style,F,Args} = ApplyArray{T,N,F,Args}(instantiate(M)) +@inline ApplyArray{T}(M::Applied) where {T} = ApplyArray{T,ndims(M)}(M) +@inline ApplyArray(M::Applied) = ApplyArray{eltype(M)}(M) -ApplyArray(f, factors...) = ApplyArray(applied(f, factors...)) -ApplyArray{T}(f, factors...) where T = ApplyArray{T}(applied(f, factors...)) -ApplyArray{T,N}(f, factors...) where {T,N} = ApplyArray{T,N}(applied(f, factors...)) -ApplyVector(f, factors...) = ApplyVector(applied(f, factors...)) -ApplyMatrix(f, factors...) = ApplyMatrix(applied(f, factors...)) +@inline ApplyArray(f, factors...) = ApplyArray{applied_eltype(f, factors...)}(f, factors...) +@inline ApplyArray{T}(f, factors...) where T = ApplyArray{T, applied_ndims(f, factors...)}(f, factors...) +@inline function ApplyArray{T,N}(f, factors...) where {T,N} + f̃, args = applied_instantiate(f, factors...) + ApplyArray{T,N,typeof(f̃),typeof(args)}(f̃, args) +end + +@inline ApplyVector(f, factors...) = ApplyVector{applied_eltype(f, factors...)}(f, factors...) +@inline ApplyMatrix(f, factors...) = ApplyMatrix{applied_eltype(f, factors...)}(f, factors...) + +ApplyArray(A::AbstractArray{T,N}) where {T,N} = ApplyArray{T,N}(call(A), arguments(A)...) +ApplyArray{T}(A::AbstractArray{V,N}) where {T,V,N} = ApplyArray{T,N}(call(A), arguments(A)...) +ApplyArray{T,N}(A::AbstractArray{V,N}) where {T,V,N} = ApplyArray{T,N}(call(A), arguments(A)...) +ApplyMatrix(A::AbstractMatrix{T}) where T = ApplyMatrix{T}(call(A), arguments(A)...) +ApplyVector(A::AbstractVector{T}) where T = ApplyVector{T}(call(A), arguments(A)...) convert(::Type{AbstractArray{T}}, A::ApplyArray{T}) where T = A convert(::Type{AbstractArray{T}}, A::ApplyArray{<:Any,N}) where {T,N} = ApplyArray{T,N}(A.f, A.args...) @@ -216,8 +234,12 @@ AbstractArray{T}(A::ApplyArray{<:Any,N}) where {T,N} = ApplyArray{T,N}(A.f, map( AbstractArray{T,N}(A::ApplyArray{T,N}) where {T,N} = copy(A) AbstractArray{T,N}(A::ApplyArray{<:Any,N}) where {T,N} = ApplyArray{T,N}(A.f, map(copy,A.args)...) -@inline axes(A::ApplyArray) = axes(Applied(A)) -@inline size(A::ApplyArray) = map(length, axes(A)) +@inline axes(A::ApplyArray) = applied_axes(A.f, A.args...) +@inline size(A::ApplyArray) = applied_size(A.f, A.args...) + +@inline applied_axes(f, args...) = map(oneto, applied_size(f, args...)) + + # immutable arrays don't need to copy. # Some special cases like vcat overload setindex! and therefore @@ -236,13 +258,16 @@ for F in (:exp, :log, :sqrt, :cos, :sin, :tan, :csc, :sec, :cot, :acosh, :asinh, :atanh, :acsch, :asech, :acoth, :acos, :asin, :atan, :acsc, :asec, :acot) @eval begin - ndims(M::Applied{LazyArrayApplyStyle,typeof($F)}) = ndims(first(M.args)) - axes(M::Applied{LazyArrayApplyStyle,typeof($F)}) = axes(first(M.args)) - size(M::Applied{LazyArrayApplyStyle,typeof($F)}) = size(first(M.args)) - eltype(M::Applied{LazyArrayApplyStyle,typeof($F)}) = eltype(first(M.args)) + @inline applied_ndims(M::typeof($F), a) = ndims(a) + @inline applied_axes(::typeof($F), a) = axes(a) + @inline applied_size(::typeof($F), a) = size(a) + @inline applied_eltype(::typeof($F), a) = float(eltype(a)) + check_applied_axes(::typeof($F), a...) = matrixfunction_check_applied_axes(a...) end end + + ### # show ### @@ -252,10 +277,12 @@ _applyarray_summary(io::IO, C) = _applyarray_summary(io::IO, C.f, arguments(C)) function _applyarray_summary(io::IO, f, args) print(io, f) print(io, "(") - summary(io, first(args)) - for a in tail(args) - print(io, ", ") - summary(io, a) + if !isempty(args) + summary(io, first(args)) + for a in tail(args) + print(io, ", ") + summary(io, a) + end end print(io, ")") end @@ -302,7 +329,6 @@ MemoryLayout(::Type{ApplyArray{T,N,F,Args}}) where {T,N,F,Args} = applylayout(F, tuple_type_memorylayouts(Args)...) @inline Applied(A::AbstractArray) = Applied(call(A), arguments(A)...) -@inline ApplyArray(A::AbstractArray) = ApplyArray(call(A), arguments(A)...) function show(io::IO, A::Applied) print(io, "Applied(", A.f) @@ -354,15 +380,11 @@ _base_copyto!(dest::AbstractArray, src::AbstractArray) = Base.invoke(copyto!, NT # triu/tril ## for tri in (:tril, :triu) - for op in (:axes, :size) - @eval begin - $op(A::Applied{<:Any,typeof($tri)}) = $op(first(A.args)) - $op(A::Applied{<:Any,typeof($tri)}, j) = $op(first(A.args), j) - end - end @eval begin - ndims(::Applied{<:Any,typeof($tri)}) = 2 - eltype(A::Applied{<:Any,typeof($tri)}) = eltype(first(A.args)) + applied_axes(::typeof($tri), a, k...) = axes(a) + applied_size(::typeof($tri), a, k...) = size(a) + applied_ndims(::typeof($tri), a, k...) = 2 + applied_eltype(::typeof($tri), a, k...) = eltype(a) $tri(A::LazyMatrix) = ApplyMatrix($tri, A) $tri(A::LazyMatrix, k::Integer) = ApplyMatrix($tri, A, k) end diff --git a/src/lazybroadcasting.jl b/src/lazybroadcasting.jl index 10cdb479..2640258f 100644 --- a/src/lazybroadcasting.jl +++ b/src/lazybroadcasting.jl @@ -9,6 +9,7 @@ is returned by `MemoryLayout(A)` if a matrix `A` is a `BroadcastArray`. """ struct BroadcastLayout{F} <: AbstractLazyLayout end +@inline tuple_type_memorylayouts(::Type{Tuple{}}) = () @inline tuple_type_memorylayouts(::Type{I}) where I<:Tuple = tuple(MemoryLayout(Base.tuple_type_head(I)), tuple_type_memorylayouts(Base.tuple_type_tail(I))...) @inline tuple_type_memorylayouts(::Type{Tuple{A}}) where {A} = (MemoryLayout(A),) @inline tuple_type_memorylayouts(::Type{Tuple{A,B}}) where {A,B} = (MemoryLayout(A),MemoryLayout(B)) diff --git a/src/lazyconcat.jl b/src/lazyconcat.jl index 2580443a..e070dca9 100644 --- a/src/lazyconcat.jl +++ b/src/lazyconcat.jl @@ -26,13 +26,16 @@ function instantiate(A::Applied{DefaultApplyStyle,typeof(vcat)}) Applied{DefaultApplyStyle}(A.f,map(instantiate,A.args)) end -@inline eltype(A::Applied{<:Any,typeof(vcat)}) = promote_type(map(eltype,A.args)...) -@inline eltype(A::Applied{<:Any,typeof(vcat),Tuple{}}) = Any -@inline ndims(A::Applied{<:Any,typeof(vcat),I}) where I = max(1,maximum(map(ndims,A.args))) -@inline ndims(A::Applied{<:Any,typeof(vcat),Tuple{}}) = 1 +@inline applied_eltype(::typeof(vcat)) = Any +@inline applied_eltype(::typeof(vcat), args...) = promote_type(map(eltype, args)...) +@inline applied_ndims(::typeof(vcat), args...) = max(1,maximum(map(ndims,args))) +@inline applied_ndims(::typeof(vcat)) = 1 @inline axes(f::Vcat{<:Any,1,Tuple{}}) = (OneTo(0),) @inline axes(f::Vcat{<:Any,1}) = tuple(oneto(+(map(length,f.args)...))) @inline axes(f::Vcat{<:Any,2}) = (oneto(+(map(a -> size(a,1), f.args)...)), axes(f.args[1],2)) +@inline size(f::Vcat) = map(length, axes(f)) + + Base.IndexStyle(::Type{<:Vcat{T,1}}) where T = Base.IndexLinear() function ==(a::Vcat{T,N}, b::Vcat{T,N}) where {N,T} @@ -134,9 +137,9 @@ function instantiate(A::Applied{DefaultApplyStyle,typeof(hcat)}) Applied{DefaultApplyStyle}(A.f,map(instantiate,A.args)) end -@inline eltype(A::Applied{<:Any,typeof(hcat)}) = promote_type(map(eltype,A.args)...) -ndims(::Applied{<:Any,typeof(hcat)}) = 2 -size(f::Applied{<:Any,typeof(hcat)}) = (size(f.args[1],1), +(map(a -> size(a,2), f.args)...)) +@inline applied_eltype(::typeof(hcat), args...) = promote_type(map(eltype,args)...) +@inline applied_ndims(::typeof(hcat), args...) = 2 +@inline applied_size(::typeof(hcat), args...) = (size(args[1],1), +(map(a -> size(a,2), args)...)) @inline hcat_getindex(f, k, j::Integer) = hcat_getindex_recursive(f, (k, j), f.args...) @@ -184,26 +187,21 @@ end # Hvcat #### -@inline eltype(A::Applied{<:Any,typeof(hvcat)}) = promote_type(map(eltype,tail(A.args))...) -ndims(::Applied{<:Any,typeof(hvcat)}) = 2 -function size(f::Applied{<:Any,typeof(hvcat),<:Tuple{Int,Vararg{Any}}}) - n = f.args[1] - sum(size.(f.args[2:n:end],1)),sum(size.(f.args[2:1+n],2)) -end - -function size(f::Applied{<:Any,typeof(hvcat),<:Tuple{NTuple{N,Int},Vararg{Any}}}) where N - n = f.args[1] +@inline applied_eltype(::typeof(hvcat), a, b...) = promote_type(map(eltype, b)...) +@inline applied_ndims(::typeof(hvcat), args...) = 2 +@inline applied_size(::typeof(hvcat), n::Int, b...) = sum(size.(b[1:n:end],1)),sum(size.(b[1:n],2)) +@inline function applied_size(::typeof(hvcat), n::NTuple{N,Int}, b...) where N as = tuple(2, (2 .+ cumsum(Base.front(n)))...) - sum(size.(getindex.(Ref(f.args), as),1)),sum(size.(f.args[2:1+n[1]],2)) + sum(size.(getindex.(Ref((n, b...)), as),1)),sum(size.(b[1:n[1]],2)) end @inline hvcat_getindex(f, k, j::Integer) = hvcat_getindex_recursive(f, (k, j), f.args...) -_hvcat_size(A) = size(A) -_hvcat_size(A::Number) = (1,1) -_hvcat_size(A::AbstractVector) = (size(A,1),1) +@inline _hvcat_size(A) = size(A) +@inline _hvcat_size(A::Number) = (1,1) +@inline _hvcat_size(A::AbstractVector) = (size(A,1),1) @inline function hvcat_getindex_recursive(f, (k,j)::Tuple{Integer,Integer}, N::Int, A, args...) T = eltype(f) @@ -966,4 +964,9 @@ end searchsorted(f::Vcat{<:Any,1}, x) = searchsortedfirst(f, x):searchsortedlast(f,x) +### +# vec +### +@inline applied_eltype(::typeof(vec), a) = eltype(a) +@inline applied_axes(::typeof(vec), a) = (oneto(length(a)),) \ No newline at end of file diff --git a/src/lazyoperations.jl b/src/lazyoperations.jl index 7f81dfdb..e0b37c6a 100644 --- a/src/lazyoperations.jl +++ b/src/lazyoperations.jl @@ -12,8 +12,8 @@ Kron{T}(A...) where T = ApplyArray{T}(kron, A...) _kron_dims() = 0 _kron_dims(A, B...) = max(ndims(A), _kron_dims(B...)) -eltype(A::Applied{<:Any,typeof(kron)}) = promote_type(map(eltype,A.args)...) -ndims(A::Applied{<:Any,typeof(kron)}) = _kron_dims(A.args...) +applied_eltype(::typeof(kron), args...) = promote_type(map(eltype,args)...) +applied_ndims(::typeof(kron), args...) = _kron_dims(args...) size(K::Kron, j::Int) = prod(size.(K.args, j)) size(a::Kron{<:Any,1}) = (size(a,1),) @@ -470,14 +470,18 @@ AccumulateAbstractVector(op, A::AbstractVector{T}) where T = AccumulateAbstractV for op in (:rot180, :rotl90, :rotr90) @eval begin - ndims(::Applied{<:Any,typeof($op)}) = 2 - eltype(A::Applied{<:Any,typeof($op)}) = eltype(A.args...) + applied_ndims(::typeof($op), a) = 2 + applied_eltype(::typeof($op), a) = eltype(a) + end +end +applied_size(::typeof(rot180), a) = size(a) +applied_axes(::typeof(rot180), a) = axes(a) +for op in (:rotl90, :rotr90) + @eval begin + applied_size(::typeof($op), a) = reverse(size(a)) + applied_axes(::typeof($op), a) = reverse(axes(a)) end end -size(A::Applied{<:Any,typeof(rot180)}) = size(A.args...) -axes(A::Applied{<:Any,typeof(rot180)}) = axes(A.args...) -size(A::Applied{<:Any,typeof(rotl90)}) = reverse(size(A.args...)) -size(A::Applied{<:Any,typeof(rotr90)}) = reverse(size(A.args...)) getindex(A::Applied{<:Any,typeof(rot180)}, k::Int, j::Int) = A.args[1][end-k+1,end-j+1] getindex(A::Applied{<:Any,typeof(rotl90)}, k::Int, j::Int) = A.args[1][j,end-k+1] diff --git a/src/linalg/add.jl b/src/linalg/add.jl index b1b39959..75719948 100644 --- a/src/linalg/add.jl +++ b/src/linalg/add.jl @@ -25,13 +25,10 @@ for op in (:+, :-) @eval begin size(M::Applied{<:Any, typeof($op)}, p::Int) = size(M)[p] axes(M::Applied{<:Any, typeof($op)}, p::Int) = axes(M)[p] - ndims(M::Applied{<:Any, typeof($op)}) = ndims(first(M.args)) length(M::Applied{<:Any, typeof($op)}) = prod(size(M)) - size(M::Applied{<:Any, typeof($op)}) = length.(axes(M)) - axes(M::Applied{<:Any, typeof($op)}) = axes(first(M.args)) - - eltype(M::Applied{<:Any, typeof($op)}) = promote_type(map(eltype,M.args)...) + applied_size(::typeof($op), args...) = size(first(args)) + applied_axes(::typeof($op), args...) = axes(first(args)) end end diff --git a/src/linalg/inv.jl b/src/linalg/inv.jl index 41b1cc43..8b24b8f5 100644 --- a/src/linalg/inv.jl +++ b/src/linalg/inv.jl @@ -22,35 +22,36 @@ ndims(A::InvOrPInv) = ndims(parent(A)) -size(A::InvOrPInv) = reverse(size(parent(A))) -axes(A::InvOrPInv) = reverse(axes(parent(A))) -size(A::InvOrPInv, k) = size(A)[k] -axes(A::InvOrPInv, k) = axes(A)[k] -eltype(A::InvOrPInv) = Base.promote_op(inv, eltype(parent(A))) +for op in (:inv, :pinv) + @eval begin + @inline applied_size(::typeof($op), a) = reverse(size(a)) + @inline applied_axes(::typeof($op), a) = reverse(axes(a)) + @inline applied_eltype(::typeof($op), a) = Base.promote_op(inv, eltype(a)) + @inline applied_ndims(::typeof($op), a) = 2 + end +end # Use ArrayLayouts.ldiv instead of \ struct LdivStyle <: ApplyStyle end struct RdivStyle <: ApplyStyle end -ApplyStyle(::typeof(\), ::Type{A}, ::Type{B}) where {A<:AbstractArray,B<:AbstractArray} = LdivStyle() -ApplyStyle(::typeof(/), ::Type{A}, ::Type{B}) where {A<:AbstractArray,B<:AbstractArray} = RdivStyle() +@inline ApplyStyle(::typeof(\), ::Type{A}, ::Type{B}) where {A<:AbstractArray,B<:AbstractArray} = LdivStyle() +@inline ApplyStyle(::typeof(/), ::Type{A}, ::Type{B}) where {A<:AbstractArray,B<:AbstractArray} = RdivStyle() -axes(M::Applied{Style,typeof(\)}) where Style = ldivaxes(M.args...) -axes(M::Applied{Style,typeof(\)}, p::Int) where Style = axes(M)[p] -size(M::Applied{Style,typeof(\)}) where Style = length.(axes(M)) -@inline eltype(M::Applied{Style,typeof(\)}) where Style = eltype(Ldiv(M.args...)) -@inline ndims(M::Applied{Style,typeof(\)}) where Style = ndims(last(M.args)) +@inline applied_axes(::typeof(\), args...) = ldivaxes(args...) +@inline applied_size(::typeof(\), args...) = length.(applied_axes(\, args...)) +@inline applied_eltype(::typeof(\), args...) = eltype(Ldiv(args...)) +@inline applied_ndims(::typeof(\), args...) = ndims(last(args)) -axes(M::Applied{Style,typeof(/)}) where Style = axes(Rdiv(M.args...)) -axes(M::Applied{Style,typeof(/)}, p::Int) where Style = axes(M)[p] -size(M::Applied{Style,typeof(/)}) where Style = length.(axes(M)) -@inline eltype(M::Applied{Style,typeof(/)}) where Style = eltype(Rdiv(M.args...)) -@inline ndims(M::Applied{Style,typeof(/)}) where Style = ndims(first(M.args)) +@inline applied_axes(::typeof(/), args...) = axes(Rdiv(args...)) +@inline applied_size(::typeof(/), args...) = length.(applied_axes(/, args...)) +@inline applied_eltype(::typeof(/), args...) = eltype(Rdiv(args...)) +@inline applied_ndims(::typeof(/), args...) = ndims(first(args)) -check_applied_axes(A::Applied{<:Any,typeof(\)}) = check_ldiv_axes(A.args...) +check_applied_axes(::typeof(\), args...) = check_ldiv_axes(args...) ###### # PInv/Inv diff --git a/src/linalg/mul.jl b/src/linalg/mul.jl index 27214214..62226f9a 100644 --- a/src/linalg/mul.jl +++ b/src/linalg/mul.jl @@ -18,17 +18,23 @@ const MulArray{T, N, Args} = ApplyArray{T, N, typeof(*), Args} const MulVector{T, Args} = MulArray{T, 1, Args} const MulMatrix{T, Args} = MulArray{T, 2, Args} +function ApplyArray{T,N}(::typeof(*), factors...) where {T,N} + _check_mul_axes(_drop_scalars(factors...)...) + ApplyArray{T,N,typeof(*),typeof(factors)}(*, factors) +end + -_drop_scalars(a::Number, b...) = _drop_scalars(b...) -_drop_scalars(a, b...) = (a, _drop_scalars(b...)...) -_drop_scalars() = () -_check_mul_axes() = nothing -_check_mul_axes(a...) = check_mul_axes(a...) -check_applied_axes(A::Applied{<:Any,typeof(*)}) = _check_mul_axes(_drop_scalars(A.args...)...) + +@inline _drop_scalars(a::Number, b...) = _drop_scalars(b...) +@inline _drop_scalars(a, b...) = (a, _drop_scalars(b...)...) +@inline _drop_scalars() = () +@inline _check_mul_axes() = nothing +@inline _check_mul_axes(a...) = check_mul_axes(a...) +@inline check_applied_axes(::typeof(*), args...) = _check_mul_axes(_drop_scalars(args...)...) size(M::Applied{<:Any,typeof(*)}, p::Int) = size(M)[p] axes(M::Applied{<:Any,typeof(*)}, p::Int) = axes(M)[p] -ndims(M::Applied{<:Any,typeof(*)}) = ndims(last(M.args)) + _mul_ndims(::Type{Tuple{A}}) where A = ndims(A) _mul_ndims(::Type{Tuple{A,B}}) where {A,B} = ndims(B) @@ -36,13 +42,19 @@ ndims(::Type{<:Applied{<:Any,typeof(*),Args}}) where Args = _mul_ndims(Args) length(M::Applied{<:Any,typeof(*)}) = prod(size(M)) -size(M::Applied{<:Any,typeof(*)}) = length.(axes(M)) +applied_size(::typeof(*), args...) = length.(applied_axes(*, args...)) @inline _eltypes() = tuple() @inline _eltypes(A, B...) = tuple(eltype(A), _eltypes(B...)...) -@inline eltype(M::Applied{<:Any,typeof(*)}) = _mul_eltype(_eltypes(M.args...)...) +for op in (:*, :+, :-) + @eval begin + @inline applied_eltype(::typeof($op), factors...) = _mul_eltype(_eltypes(factors...)...) + @inline applied_ndims(M::typeof($op), args...) = ndims(last(args)) + end +end + @inline mulaxes1(::Tuple{}) = () @inline mulaxes1(::Tuple{}, B, C...) = mulaxes1(B, C...) @@ -57,8 +69,7 @@ size(M::Applied{<:Any,typeof(*)}) = length.(axes(M)) @inline _combine_axes(a, b) = (a,b) @inline mulaxes(ax...) = _combine_axes(mulaxes1(ax...), mulaxes2(reverse(ax)...)) -@inline axes(M::Applied{<:Any,typeof(*)}) = mulaxes(map(axes,M.args)...) -@inline axes(M::Applied{<:Any, typeof(*), Tuple{}}) = () +@inline applied_axes(::typeof(*), args...) = mulaxes(map(axes, args)...) ### # show @@ -166,12 +177,15 @@ _mul_rowsupport(j, A::AbstractArray, B...) = _mul_rowsupport(rowsupport(A,j), B. rowsupport(B::Applied{<:Any,typeof(*)}, j) = _mul_rowsupport(j, B.args...) rowsupport(B::MulArray, j) = _mul_rowsupport(j, B.args...) -function getindex(M::Applied{<:Any,typeof(*)}, k...) - A,Bs = first(M.args), tail(M.args) +function _mul_getindex(args::Tuple, k...) + A,Bs = first(args), tail(args) B = _mul(Bs...) Mul(A, B)[k...] end +getindex(M::Applied{<:Any,typeof(*)}, k...) = _mul_getindex(M.args, k...) +@propagate_inbounds getindex(M::ApplyArray{T,N,typeof(*)}, kj::Vararg{Integer,N}) where {T,N} = convert(T, _mul_getindex(M.args, kj...))::T + _flatten(A::MulArray, B...) = _flatten(Applied(A), B...) flatten(A::MulArray) = ApplyArray(flatten(Applied(A))) @@ -229,7 +243,7 @@ _vec_mul_arguments(V) = _vec_mul_arguments(arguments(parent(V)), parentindices(V arguments(::ApplyLayout{typeof(*)}, V::SubArray{<:Any,2}) = _mat_mul_arguments(V) arguments(::ApplyLayout{typeof(*)}, V::SubArray{<:Any,1}) = _vec_mul_arguments(V) -@inline sub_materialize(lay::ApplyLayout{typeof(*)}, V) = apply(*, map(sub_materialize, arguments(lay, V))...) +@inline sub_materialize(lay::ApplyLayout{typeof(*)}, V) = *(map(sub_materialize, arguments(lay, V))...) ## diff --git a/src/padded.jl b/src/padded.jl index 3f313fff..4918765c 100644 --- a/src/padded.jl +++ b/src/padded.jl @@ -468,9 +468,10 @@ end # setindex ### -@inline ndims(A::Applied{<:Any,typeof(setindex)}) = ndims(A.args[1]) -@inline eltype(A::Applied{<:Any,typeof(setindex)}) = eltype(A.args[1]) -axes(A::ApplyArray{<:Any,N,typeof(setindex)}) where N = axes(A.args[1]) +@inline applied_ndims(::typeof(setindex), a, b...) = ndims(a) +@inline applied_eltype(::typeof(setindex), a, b...) = eltype(a) +@inline applied_axes(::typeof(setindex), a, b...) = axes(a) +@inline applied_size(::typeof(setindex), a, b...) = size(a) function getindex(A::ApplyVector{T,typeof(setindex)}, k::Integer) where T P,v,kr = A.args diff --git a/test/applytests.jl b/test/applytests.jl index 902ac8ba..8314aecb 100644 --- a/test/applytests.jl +++ b/test/applytests.jl @@ -40,8 +40,6 @@ import ArrayLayouts: StridedLayout end @testset "copy (#85)" begin - Base.size(A::Applied) = (length(A.args[1]),) - Base.eltype(A::Applied) = eltype(A.args[1]) v = ApplyVector(vec, ones(Int, 2, 2)) vc = copy(float.(v)) ve = convert(Vector, vc) @@ -71,6 +69,11 @@ import ArrayLayouts: StridedLayout @test MemoryLayout(typeof(v)) isa ApplyLayout{typeof(+)} @test call(v) == call(a) == + @test Array(v) == a[1:2] == Array(a)[1:2] + @test v == ApplyArray(v) == ApplyArray{Float64}(v) == ApplyVector(v) == ApplyVector{Float64}(v) + + A = ApplyArray(+,[1 2; 3 4],[3 4; 5 6]) + V = view(A, 1:2, :) + @test V == ApplyArray(V) == ApplyArray{Float64}(V) == ApplyMatrix(V) == ApplyMatrix{Float64}(V) end @testset "rot180" begin diff --git a/test/concattests.jl b/test/concattests.jl index ca7ccc41..71cb28a5 100644 --- a/test/concattests.jl +++ b/test/concattests.jl @@ -1,4 +1,5 @@ -using LazyArrays, FillArrays, LinearAlgebra, StaticArrays, ArrayLayouts, Test, Base64 +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 @@ -9,6 +10,7 @@ import LazyArrays: MemoryLayout, DenseColumnMajor, materialize!, call, paddeddat @testset "Vector" begin A = @inferred(Vcat(Vector(1:10), Vector(1:20))) @test eltype(A) == Int + @test A == ApplyArray(vcat, Vector(1:10), Vector(1:20)) @test @inferred(axes(A)) == (Base.OneTo(30),) @test @inferred(A[5]) == A[15] == 5 @test_throws BoundsError A[31] @@ -467,8 +469,6 @@ import LazyArrays: MemoryLayout, DenseColumnMajor, materialize!, call, paddeddat @test B * A ≈ Array(B) * Array(A) ≈ Vcat([1 2]', [3 4]') * A - - @test B * BroadcastArray(exp, [1 2]) ≈ B * exp.([1 2]) A = Hcat([1.0 2.0; 3 4],[3.0 4.0; 5 6]) @@ -605,4 +605,10 @@ import LazyArrays: MemoryLayout, DenseColumnMajor, materialize!, call, paddeddat @test cumsum(v) isa StepRangeLen{Float64} @test cumsum(v) == cumsum(collect(v)) end + + @testset "empty vcat" begin + v = ApplyArray(vcat) + @test v isa AbstractVector{Any} + @test stringmime("text/plain", v) == "vcat()" + end end diff --git a/test/runtests.jl b/test/runtests.jl index 8da6b813..1c8cf50a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -420,6 +420,7 @@ end @test A[2,1] == 0 @test A[1,1] == A.args[1][1,1] @test A == triu(A.args[1]) + @test size(A) == (2,2) A = ApplyArray(tril,randn(2,2)) @test A isa ApplyArray{Float64} @test A[1,2] == 0