From d442b5d8fa531bca218dc3787407f4404fbdf190 Mon Sep 17 00:00:00 2001 From: Michael Abbott Date: Thu, 2 Apr 2020 18:20:50 +0200 Subject: [PATCH 1/6] add PermutedDimsArray, and FirstMajor layout --- Project.toml | 2 +- src/ArrayLayouts.jl | 4 +- src/memorylayout.jl | 94 +++++++++++++++++++++++++++++++++++++++++++- test/test_layouts.jl | 48 +++++++++++++++++++++- 4 files changed, 142 insertions(+), 6 deletions(-) diff --git a/Project.toml b/Project.toml index 7c831a1..b0458af 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ArrayLayouts" uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" authors = ["Sheehan Olver "] -version = "0.2.1" +version = "0.2.2" [deps] FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" diff --git a/src/ArrayLayouts.jl b/src/ArrayLayouts.jl index e41aba7..6732855 100644 --- a/src/ArrayLayouts.jl +++ b/src/ArrayLayouts.jl @@ -47,7 +47,7 @@ else end export materialize, materialize!, MulAdd, muladd!, Ldiv, Rdiv, Lmul, Rmul, lmul, rmul, ldiv, rdiv, mul, MemoryLayout, AbstractStridedLayout, - DenseColumnMajor, ColumnMajor, ZerosLayout, FillLayout, AbstractColumnMajor, RowMajor, AbstractRowMajor, + DenseColumnMajor, ColumnMajor, ZerosLayout, FillLayout, AbstractColumnMajor, RowMajor, AbstractRowMajor, FirstUnion, DiagonalLayout, ScalarLayout, SymTridiagonalLayout, HermitianLayout, SymmetricLayout, TriangularLayout, UnknownLayout, AbstractBandedLayout, ApplyBroadcastStyle, ConjLayout, AbstractFillLayout, colsupport, rowsupport, layout_getindex, QLayout, LayoutArray, LayoutMatrix, LayoutVector @@ -187,4 +187,4 @@ end return A end -end \ No newline at end of file +end diff --git a/src/memorylayout.jl b/src/memorylayout.jl index c838216..0ec11c0 100644 --- a/src/memorylayout.jl +++ b/src/memorylayout.jl @@ -18,6 +18,8 @@ abstract type AbstractRowMajor <: AbstractDecreasingStrides end struct DenseRowMajor <: AbstractRowMajor end struct RowMajor <: AbstractRowMajor end struct DecreasingStrides <: AbstractIncreasingStrides end +struct FirstMajor <: AbstractStridedLayout end +struct SecondMajor <: AbstractStridedLayout end struct StridedLayout <: AbstractStridedLayout end struct ScalarLayout <: MemoryLayout end @@ -42,6 +44,32 @@ dispatch to BLAS and LAPACK routines if the memory layout is BLAS compatible and the element type is a `Float32`, `Float64`, `ComplexF32`, or `ComplexF64`. In this case, one must implement the strided array interface, which requires overrides of `strides(A::MyMatrix)` and `unknown_convert(::Type{Ptr{T}}, A::MyMatrix)`. + +The complete list of more specialised types is as follows: +``` +julia> using ArrayLayouts, AbstractTrees + +julia> AbstractTrees.children(x::Type) = subtypes(x) + +julia> print_tree(AbstractStridedLayout) +AbstractStridedLayout +├─ AbstractDecreasingStrides +│ └─ AbstractRowMajor +│ ├─ DenseRowMajor +│ └─ RowMajor +├─ AbstractIncreasingStrides +│ ├─ AbstractColumnMajor +│ │ ├─ ColumnMajor +│ │ └─ DenseColumnMajor +│ ├─ DecreasingStrides +│ └─ IncreasingStrides +├─ FirstMajor +├─ SecondMajor +└─ StridedLayout + +julia> Base.show_supertypes(AbstractStridedLayout) +AbstractStridedLayout <: MemoryLayout <: Any +``` """ AbstractStridedLayout @@ -157,7 +185,7 @@ MemoryLayout(::Type{<:ReshapedArray{T,N,A,DIMS}}) where {T,N,A,DIMS} = reshapedl @inline reshapedlayout(::DenseColumnMajor, _) = DenseColumnMajor() -@inline MemoryLayout(A::Type{<:SubArray{T,N,P,I}}) where {T,N,P,I} = +@inline MemoryLayout(A::Type{<:SubArray{T,N,P,I}}) where {T,N,P,I} = sublayout(MemoryLayout(P), I) sublayout(_1, _2) = UnknownLayout() sublayout(_1, _2, _3)= UnknownLayout() @@ -257,6 +285,70 @@ transposelayout(::ConjLayout{ML}) where ML = ConjLayout{typeof(transposelayout(M adjointlayout(::Type{T}, M::MemoryLayout) where T = transposelayout(conjlayout(T, M)) +# Layouts of PermutedDimsArrays +""" + FirstMajor() + +is returned by `MemoryLayout(A)` for arrays of ndims(A) >= 3 for which `stride(A,1) == 1`, +but unlike `ColumnMajor()` in that it does not demand that the other strides are monotonic. + +Dispatch on `FirstUnion = Union{FirstMajor, AbstractColumnMajor}` to ensure this property. + +Base.show_supertypes(ArrayLayouts.FirstMajor) +""" +FirstMajor + +const FirstUnion = Union{FirstMajor, AbstractColumnMajor} + +""" + SecondMajor() + +is returned by `MemoryLayout(A)` for arrays of ndims(A) >= 3 for which `stride(A,2) == 1`. +This would be equivalent to `RowMajor()` on a matrix, but should never be produced there. +""" +SecondMajor + +MemoryLayout(::Type{PermutedDimsArray{T,N,P,Q,S}}) where {T,N,P,Q,S} = permutelayout(MemoryLayout(S), Val(P)) + +permutelayout(::Any, perm) = UnknownLayout() +permutelayout(::StridedLayout, perm) = StridedLayout() +permutelayout(::ConjLayout{ML}, perm) where ML = ConjLayout{typeof(permutelayout(ML(), perm))}() + +function permutelayout(layout::T, ::Val{perm}) where {T <: FirstUnion, perm} + issorted(perm) && return layout + issorted(reverse(perm)) && return reverse(layout) + perm[1] == 1 && return FirstMajor() + perm[2] == 1 && return SecondMajor() + return StridedLayout() +end +function permutelayout(layout::T, ::Val{perm}) where {T <: AbstractRowMajor, perm} + issorted(perm) && return layout + issorted(reverse(perm)) && return reverse(layout) + N = length(perm) # == ndims(A) + perm[1] == N && return FirstMajor() + perm[2] == N && return SecondMajor() + return StridedLayout() +end +function permutelayout(layout::SecondMajor, ::Val{perm}) where {perm} + perm[1] == 2 && return FirstMajor() + perm[2] == 2 && return SecondMajor() + return StridedLayout() +end +function permutelayout(layout::T, ::Val{perm}) where {T <: Union{IncreasingStrides,DecreasingStrides}, perm} + issorted(perm) && return layout + issorted(reverse(perm)) && return reverse(layout) + return StridedLayout() +end + +Base.reverse(::DenseRowMajor) = DenseColumnMajor() +Base.reverse(::RowMajor) = ColumnMajor() +Base.reverse(::DenseColumnMajor) = DenseRowMajor() +Base.reverse(::ColumnMajor) = RowMajor() +Base.reverse(::IncreasingStrides) = DecreasingStrides() +Base.reverse(::DecreasingStrides) = IncreasingStrides() +Base.reverse(::AbstractStridedLayout) = StridedLayout() + + # MemoryLayout of Symmetric/Hermitian """ SymmetricLayout{layout}() diff --git a/test/test_layouts.jl b/test/test_layouts.jl index f0f5dd1..db5f01a 100644 --- a/test/test_layouts.jl +++ b/test/test_layouts.jl @@ -1,9 +1,9 @@ using ArrayLayouts, LinearAlgebra, FillArrays, Test import ArrayLayouts: MemoryLayout, DenseRowMajor, DenseColumnMajor, StridedLayout, - ConjLayout, RowMajor, ColumnMajor, UnknownLayout, + ConjLayout, RowMajor, ColumnMajor, FirstMajor, SecondMajor, SymmetricLayout, HermitianLayout, UpperTriangularLayout, UnitUpperTriangularLayout, LowerTriangularLayout, - UnitLowerTriangularLayout, ScalarLayout, + UnitLowerTriangularLayout, ScalarLayout, UnknownLayout, hermitiandata, symmetricdata, FillLayout, ZerosLayout, DiagonalLayout, colsupport, rowsupport @@ -178,4 +178,48 @@ struct FooNumber <: Number end @test colsupport(LowerTriangular(A),3) ≡ 3:5 @test rowsupport(LowerTriangular(A),3) ≡ Base.OneTo(3) end + + @testset "PermutedDimsArray" begin + ArrayLayouts.MemoryLayout(A) = MemoryLayout(typeof(A)) # pending other PR... + + A = [1.0 2; 3 4] + @test MemoryLayout(PermutedDimsArray(A, (1,2))) == DenseColumnMajor() + @test MemoryLayout(PermutedDimsArray(A, (2,1))) == DenseRowMajor() + @test MemoryLayout(transpose(PermutedDimsArray(A, (2,1)))) == DenseColumnMajor() + @test MemoryLayout(adjoint(PermutedDimsArray(A, (2,1)))) == DenseColumnMajor() + B = [1.0+im 2; 3 4] + @test MemoryLayout(PermutedDimsArray(B, (2,1))) == DenseRowMajor() + @test MemoryLayout(transpose(PermutedDimsArray(B, (2,1)))) == DenseColumnMajor() + @test MemoryLayout(adjoint(PermutedDimsArray(B, (2,1)))) == ConjLayout{DenseColumnMajor}() + + C = view(ones(10,20,30), 2:9, 3:18, 4:27); + @test MemoryLayout(C) == ColumnMajor() + @test MemoryLayout(PermutedDimsArray(C, (1,2,3))) == ColumnMajor() + @test MemoryLayout(PermutedDimsArray(C, (1,3,2))) == FirstMajor() + + @test MemoryLayout(PermutedDimsArray(C, (3,1,2))) == SecondMajor() + @test MemoryLayout(PermutedDimsArray(C, (2,1,3))) == SecondMajor() + + @test MemoryLayout(PermutedDimsArray(C, (3,2,1))) == RowMajor() + @test MemoryLayout(PermutedDimsArray(C, (2,3,1))) == StridedLayout() + + D = ones(10,20,30,40); + @test MemoryLayout(D) == DenseColumnMajor() + @test MemoryLayout(PermutedDimsArray(D, (1,2,3,4))) == DenseColumnMajor() + @test MemoryLayout(PermutedDimsArray(D, (1,4,3,2))) == FirstMajor() + + @test MemoryLayout(PermutedDimsArray(D, (4,1,3,2))) == SecondMajor() + @test MemoryLayout(PermutedDimsArray(D, (2,1,4,3))) == SecondMajor() + + @test MemoryLayout(PermutedDimsArray(D, (4,3,2,1))) == DenseRowMajor() + @test MemoryLayout(PermutedDimsArray(D, (4,2,1,3))) == StridedLayout() + + issorted((1,2,3,4)) + @test_skip 0 == @allocated issorted((1,2,3,4)) + reverse((1,2,3,4)) + @test_skip 0 == @allocated reverse((1,2,3,4)) + revD = PermutedDimsArray(D, (4,3,2,1)); + MemoryLayout(revD) + @test 0 == @allocated MemoryLayout(revD) + end end From b1332cac28cbc69b9fbe1e2ee9acd16c1b989a9a Mon Sep 17 00:00:00 2001 From: Michael Abbott Date: Fri, 3 Apr 2020 12:45:06 +0200 Subject: [PATCH 2/6] change to UnitStride{D} for any dimension D --- src/ArrayLayouts.jl | 2 +- src/memorylayout.jl | 59 +++++++++++++++++++------------------------- test/test_layouts.jl | 26 +++++++++---------- 3 files changed, 39 insertions(+), 48 deletions(-) diff --git a/src/ArrayLayouts.jl b/src/ArrayLayouts.jl index 6732855..8ea0e15 100644 --- a/src/ArrayLayouts.jl +++ b/src/ArrayLayouts.jl @@ -47,7 +47,7 @@ else end export materialize, materialize!, MulAdd, muladd!, Ldiv, Rdiv, Lmul, Rmul, lmul, rmul, ldiv, rdiv, mul, MemoryLayout, AbstractStridedLayout, - DenseColumnMajor, ColumnMajor, ZerosLayout, FillLayout, AbstractColumnMajor, RowMajor, AbstractRowMajor, FirstUnion, + DenseColumnMajor, ColumnMajor, ZerosLayout, FillLayout, AbstractColumnMajor, RowMajor, AbstractRowMajor, UnitStride, DiagonalLayout, ScalarLayout, SymTridiagonalLayout, HermitianLayout, SymmetricLayout, TriangularLayout, UnknownLayout, AbstractBandedLayout, ApplyBroadcastStyle, ConjLayout, AbstractFillLayout, colsupport, rowsupport, layout_getindex, QLayout, LayoutArray, LayoutMatrix, LayoutVector diff --git a/src/memorylayout.jl b/src/memorylayout.jl index 0ec11c0..52d89a4 100644 --- a/src/memorylayout.jl +++ b/src/memorylayout.jl @@ -18,8 +18,7 @@ abstract type AbstractRowMajor <: AbstractDecreasingStrides end struct DenseRowMajor <: AbstractRowMajor end struct RowMajor <: AbstractRowMajor end struct DecreasingStrides <: AbstractIncreasingStrides end -struct FirstMajor <: AbstractStridedLayout end -struct SecondMajor <: AbstractStridedLayout end +struct UnitStride{D} <: AbstractStridedLayout end struct StridedLayout <: AbstractStridedLayout end struct ScalarLayout <: MemoryLayout end @@ -63,9 +62,8 @@ AbstractStridedLayout │ │ └─ DenseColumnMajor │ ├─ DecreasingStrides │ └─ IncreasingStrides -├─ FirstMajor -├─ SecondMajor -└─ StridedLayout +├─ StridedLayout +└─ UnitStride julia> Base.show_supertypes(AbstractStridedLayout) AbstractStridedLayout <: MemoryLayout <: Any @@ -287,26 +285,18 @@ adjointlayout(::Type{T}, M::MemoryLayout) where T = transposelayout(conjlayout(T # Layouts of PermutedDimsArrays """ - FirstMajor() + UnitStride{D}() -is returned by `MemoryLayout(A)` for arrays of ndims(A) >= 3 for which `stride(A,1) == 1`, -but unlike `ColumnMajor()` in that it does not demand that the other strides are monotonic. +is returned by `MemoryLayout(A)` for arrays of ndims(A) >= 3 for which `stride(A,D) == 1`. -Dispatch on `FirstUnion = Union{FirstMajor, AbstractColumnMajor}` to ensure this property. +`UnitStride{1}` is weaker than `ColumnMajor` in that it does not demand that the other +strides are increasing, hence is is not a subtype of `AbstractIncreasingStrides`. +To ensure that `stride(A,D) == 1`, you may dispatch on `Union{UnitStride{1}, AbstractColumnMajor}` +to allow for both options. (With complex numbers, you may also need their `ConjLayout` versions.) -Base.show_supertypes(ArrayLayouts.FirstMajor) +Likewise, both `UnitStride{ndims(A)}` and `AbstractRowMajor` have `stride(A, ndims(A)) == 1`. """ -FirstMajor - -const FirstUnion = Union{FirstMajor, AbstractColumnMajor} - -""" - SecondMajor() - -is returned by `MemoryLayout(A)` for arrays of ndims(A) >= 3 for which `stride(A,2) == 1`. -This would be equivalent to `RowMajor()` on a matrix, but should never be produced there. -""" -SecondMajor +UnitStride MemoryLayout(::Type{PermutedDimsArray{T,N,P,Q,S}}) where {T,N,P,Q,S} = permutelayout(MemoryLayout(S), Val(P)) @@ -314,25 +304,28 @@ permutelayout(::Any, perm) = UnknownLayout() permutelayout(::StridedLayout, perm) = StridedLayout() permutelayout(::ConjLayout{ML}, perm) where ML = ConjLayout{typeof(permutelayout(ML(), perm))}() -function permutelayout(layout::T, ::Val{perm}) where {T <: FirstUnion, perm} +function permutelayout(layout::AbstractColumnMajor, ::Val{perm}) where {perm} issorted(perm) && return layout issorted(reverse(perm)) && return reverse(layout) - perm[1] == 1 && return FirstMajor() - perm[2] == 1 && return SecondMajor() - return StridedLayout() + tup = ntuple(length(perm)) do D + perm[D] == 1 && return UnitStride{D}() + end + only(filter(x -> x isa MemoryLayout, tup)) end -function permutelayout(layout::T, ::Val{perm}) where {T <: AbstractRowMajor, perm} +function permutelayout(layout::AbstractRowMajor, ::Val{perm}) where {perm} issorted(perm) && return layout issorted(reverse(perm)) && return reverse(layout) N = length(perm) # == ndims(A) - perm[1] == N && return FirstMajor() - perm[2] == N && return SecondMajor() - return StridedLayout() + tup = ntuple(N) do D + perm[D] == N && return UnitStride{D}() + end + only(filter(x -> x isa MemoryLayout, tup)) end -function permutelayout(layout::SecondMajor, ::Val{perm}) where {perm} - perm[1] == 2 && return FirstMajor() - perm[2] == 2 && return SecondMajor() - return StridedLayout() +function permutelayout(layout::UnitStride{D0}, ::Val{perm}) where {D0, perm} + tup = ntuple(length(perm)) do D + perm[D] == D0 && return UnitStride{D}() + end + only(filter(x -> x isa MemoryLayout, tup)) end function permutelayout(layout::T, ::Val{perm}) where {T <: Union{IncreasingStrides,DecreasingStrides}, perm} issorted(perm) && return layout diff --git a/test/test_layouts.jl b/test/test_layouts.jl index db5f01a..90f6880 100644 --- a/test/test_layouts.jl +++ b/test/test_layouts.jl @@ -1,6 +1,6 @@ using ArrayLayouts, LinearAlgebra, FillArrays, Test import ArrayLayouts: MemoryLayout, DenseRowMajor, DenseColumnMajor, StridedLayout, - ConjLayout, RowMajor, ColumnMajor, FirstMajor, SecondMajor, + ConjLayout, RowMajor, ColumnMajor, UnitStride, SymmetricLayout, HermitianLayout, UpperTriangularLayout, UnitUpperTriangularLayout, LowerTriangularLayout, UnitLowerTriangularLayout, ScalarLayout, UnknownLayout, @@ -16,9 +16,9 @@ struct FooNumber <: Number end @test MemoryLayout(FooBar()) == MemoryLayout(FooBar) == UnknownLayout() A = randn(6) - @test MemoryLayout(typeof(A)) == MemoryLayout(typeof(Base.ReshapedArray(A,(2,3),()))) == + @test MemoryLayout(typeof(A)) == MemoryLayout(typeof(Base.ReshapedArray(A,(2,3),()))) == MemoryLayout(typeof(reinterpret(Float32,A))) == MemoryLayout(A) == DenseColumnMajor() - + @test MemoryLayout(typeof(view(A,1:3))) == DenseColumnMajor() @test MemoryLayout(typeof(view(A,Base.OneTo(3)))) == DenseColumnMajor() @test MemoryLayout(typeof(view(A,:))) == DenseColumnMajor() @@ -180,8 +180,6 @@ struct FooNumber <: Number end end @testset "PermutedDimsArray" begin - ArrayLayouts.MemoryLayout(A) = MemoryLayout(typeof(A)) # pending other PR... - A = [1.0 2; 3 4] @test MemoryLayout(PermutedDimsArray(A, (1,2))) == DenseColumnMajor() @test MemoryLayout(PermutedDimsArray(A, (2,1))) == DenseRowMajor() @@ -195,27 +193,27 @@ struct FooNumber <: Number end C = view(ones(10,20,30), 2:9, 3:18, 4:27); @test MemoryLayout(C) == ColumnMajor() @test MemoryLayout(PermutedDimsArray(C, (1,2,3))) == ColumnMajor() - @test MemoryLayout(PermutedDimsArray(C, (1,3,2))) == FirstMajor() + @test MemoryLayout(PermutedDimsArray(C, (1,3,2))) == UnitStride{1}() - @test MemoryLayout(PermutedDimsArray(C, (3,1,2))) == SecondMajor() - @test MemoryLayout(PermutedDimsArray(C, (2,1,3))) == SecondMajor() + @test MemoryLayout(PermutedDimsArray(C, (3,1,2))) == UnitStride{2}() + @test MemoryLayout(PermutedDimsArray(C, (2,1,3))) == UnitStride{2}() @test MemoryLayout(PermutedDimsArray(C, (3,2,1))) == RowMajor() - @test MemoryLayout(PermutedDimsArray(C, (2,3,1))) == StridedLayout() + @test MemoryLayout(PermutedDimsArray(C, (2,3,1))) == UnitStride{3}() D = ones(10,20,30,40); @test MemoryLayout(D) == DenseColumnMajor() @test MemoryLayout(PermutedDimsArray(D, (1,2,3,4))) == DenseColumnMajor() - @test MemoryLayout(PermutedDimsArray(D, (1,4,3,2))) == FirstMajor() + @test MemoryLayout(PermutedDimsArray(D, (1,4,3,2))) == UnitStride{1}() - @test MemoryLayout(PermutedDimsArray(D, (4,1,3,2))) == SecondMajor() - @test MemoryLayout(PermutedDimsArray(D, (2,1,4,3))) == SecondMajor() + @test MemoryLayout(PermutedDimsArray(D, (4,1,3,2))) == UnitStride{2}() + @test MemoryLayout(PermutedDimsArray(D, (2,1,4,3))) == UnitStride{2}() @test MemoryLayout(PermutedDimsArray(D, (4,3,2,1))) == DenseRowMajor() - @test MemoryLayout(PermutedDimsArray(D, (4,2,1,3))) == StridedLayout() + @test MemoryLayout(PermutedDimsArray(D, (4,2,1,3))) == UnitStride{3}() issorted((1,2,3,4)) - @test_skip 0 == @allocated issorted((1,2,3,4)) + @test_skip 0 == @allocated issorted((1,2,3,4)) # fails on Julia 1.4, in tests reverse((1,2,3,4)) @test_skip 0 == @allocated reverse((1,2,3,4)) revD = PermutedDimsArray(D, (4,3,2,1)); From 1cc572f398f861612bbb9a1794091efa0c5440c5 Mon Sep 17 00:00:00 2001 From: Michael Abbott Date: Fri, 3 Apr 2020 12:48:28 +0200 Subject: [PATCH 3/6] use Compat.jl --- Project.toml | 2 ++ src/ArrayLayouts.jl | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Project.toml b/Project.toml index b0458af..55e72a6 100644 --- a/Project.toml +++ b/Project.toml @@ -4,10 +4,12 @@ authors = ["Sheehan Olver "] version = "0.2.2" [deps] +Compat = "34da2185-b29b-5c13-b0c7-acf172513d20" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" [compat] +Compat = "3.1" FillArrays = "0.8" julia = "1" diff --git a/src/ArrayLayouts.jl b/src/ArrayLayouts.jl index 8ea0e15..efe1304 100644 --- a/src/ArrayLayouts.jl +++ b/src/ArrayLayouts.jl @@ -39,6 +39,8 @@ import LinearAlgebra.BLAS: BlasFloat, BlasReal, BlasComplex import FillArrays: AbstractFill, getindex_value +import Compat: Compat # 3.1, for Base.filter(f, ::Tuple) + if VERSION < v"1.2-" import Base: has_offset_axes require_one_based_indexing(A...) = !has_offset_axes(A...) || throw(ArgumentError("offset arrays are not supported but got an array with index other than 1")) From 018a9f35c38c3a3bfaa7037000fd67dc37dce908 Mon Sep 17 00:00:00 2001 From: Michael Abbott Date: Fri, 3 Apr 2020 13:00:27 +0200 Subject: [PATCH 4/6] tidy & fix --- src/ArrayLayouts.jl | 2 +- src/memorylayout.jl | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ArrayLayouts.jl b/src/ArrayLayouts.jl index efe1304..c0f7591 100644 --- a/src/ArrayLayouts.jl +++ b/src/ArrayLayouts.jl @@ -39,7 +39,7 @@ import LinearAlgebra.BLAS: BlasFloat, BlasReal, BlasComplex import FillArrays: AbstractFill, getindex_value -import Compat: Compat # 3.1, for Base.filter(f, ::Tuple) +import Compat: only # Compat v3.1, for Base.filter(f, ::Tuple) if VERSION < v"1.2-" import Base: has_offset_axes diff --git a/src/memorylayout.jl b/src/memorylayout.jl index 52d89a4..8a0554d 100644 --- a/src/memorylayout.jl +++ b/src/memorylayout.jl @@ -287,11 +287,11 @@ adjointlayout(::Type{T}, M::MemoryLayout) where T = transposelayout(conjlayout(T """ UnitStride{D}() -is returned by `MemoryLayout(A)` for arrays of ndims(A) >= 3 for which `stride(A,D) == 1`. +is returned by `MemoryLayout(A)` for arrays of `ndims(A) >= 3` which have `stride(A,D) == 1`. `UnitStride{1}` is weaker than `ColumnMajor` in that it does not demand that the other -strides are increasing, hence is is not a subtype of `AbstractIncreasingStrides`. -To ensure that `stride(A,D) == 1`, you may dispatch on `Union{UnitStride{1}, AbstractColumnMajor}` +strides are increasing, hence it is not a subtype of `AbstractIncreasingStrides`. +To ensure that `stride(A,1) == 1`, you may dispatch on `Union{UnitStride{1}, AbstractColumnMajor}` to allow for both options. (With complex numbers, you may also need their `ConjLayout` versions.) Likewise, both `UnitStride{ndims(A)}` and `AbstractRowMajor` have `stride(A, ndims(A)) == 1`. @@ -308,7 +308,7 @@ function permutelayout(layout::AbstractColumnMajor, ::Val{perm}) where {perm} issorted(perm) && return layout issorted(reverse(perm)) && return reverse(layout) tup = ntuple(length(perm)) do D - perm[D] == 1 && return UnitStride{D}() + perm[D] == 1 ? UnitStride{D}() : nothing end only(filter(x -> x isa MemoryLayout, tup)) end @@ -317,17 +317,17 @@ function permutelayout(layout::AbstractRowMajor, ::Val{perm}) where {perm} issorted(reverse(perm)) && return reverse(layout) N = length(perm) # == ndims(A) tup = ntuple(N) do D - perm[D] == N && return UnitStride{D}() + perm[D] == N ? UnitStride{D}() : nothing end only(filter(x -> x isa MemoryLayout, tup)) end function permutelayout(layout::UnitStride{D0}, ::Val{perm}) where {D0, perm} tup = ntuple(length(perm)) do D - perm[D] == D0 && return UnitStride{D}() + perm[D] == D0 ? UnitStride{D}() : nothing end only(filter(x -> x isa MemoryLayout, tup)) end -function permutelayout(layout::T, ::Val{perm}) where {T <: Union{IncreasingStrides,DecreasingStrides}, perm} +function permutelayout(layout::Union{IncreasingStrides,DecreasingStrides}, ::Val{perm}) where {perm} issorted(perm) && return layout issorted(reverse(perm)) && return reverse(layout) return StridedLayout() From 80c085d37ef3effb46654f23fb8739b34f95c358 Mon Sep 17 00:00:00 2001 From: Michael Abbott Date: Fri, 3 Apr 2020 13:28:47 +0200 Subject: [PATCH 5/6] smarter ntuple, rm Compat.jl --- Project.toml | 2 -- src/ArrayLayouts.jl | 2 -- src/memorylayout.jl | 18 ++++++------------ 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/Project.toml b/Project.toml index 55e72a6..b0458af 100644 --- a/Project.toml +++ b/Project.toml @@ -4,12 +4,10 @@ authors = ["Sheehan Olver "] version = "0.2.2" [deps] -Compat = "34da2185-b29b-5c13-b0c7-acf172513d20" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" [compat] -Compat = "3.1" FillArrays = "0.8" julia = "1" diff --git a/src/ArrayLayouts.jl b/src/ArrayLayouts.jl index c0f7591..8ea0e15 100644 --- a/src/ArrayLayouts.jl +++ b/src/ArrayLayouts.jl @@ -39,8 +39,6 @@ import LinearAlgebra.BLAS: BlasFloat, BlasReal, BlasComplex import FillArrays: AbstractFill, getindex_value -import Compat: only # Compat v3.1, for Base.filter(f, ::Tuple) - if VERSION < v"1.2-" import Base: has_offset_axes require_one_based_indexing(A...) = !has_offset_axes(A...) || throw(ArgumentError("offset arrays are not supported but got an array with index other than 1")) diff --git a/src/memorylayout.jl b/src/memorylayout.jl index 8a0554d..9c17860 100644 --- a/src/memorylayout.jl +++ b/src/memorylayout.jl @@ -307,25 +307,19 @@ permutelayout(::ConjLayout{ML}, perm) where ML = ConjLayout{typeof(permutelayout function permutelayout(layout::AbstractColumnMajor, ::Val{perm}) where {perm} issorted(perm) && return layout issorted(reverse(perm)) && return reverse(layout) - tup = ntuple(length(perm)) do D - perm[D] == 1 ? UnitStride{D}() : nothing - end - only(filter(x -> x isa MemoryLayout, tup)) + D = sum(ntuple(dim -> perm[dim] == 1 ? dim : 0, length(perm))) + return UnitStride{D}() end function permutelayout(layout::AbstractRowMajor, ::Val{perm}) where {perm} issorted(perm) && return layout issorted(reverse(perm)) && return reverse(layout) N = length(perm) # == ndims(A) - tup = ntuple(N) do D - perm[D] == N ? UnitStride{D}() : nothing - end - only(filter(x -> x isa MemoryLayout, tup)) + D = sum(ntuple(dim -> perm[dim] == N ? dim : 0, N)) + return UnitStride{D}() end function permutelayout(layout::UnitStride{D0}, ::Val{perm}) where {D0, perm} - tup = ntuple(length(perm)) do D - perm[D] == D0 ? UnitStride{D}() : nothing - end - only(filter(x -> x isa MemoryLayout, tup)) + D = sum(ntuple(dim -> perm[dim] == D0 ? dim : 0, length(perm))) + return UnitStride{D}() end function permutelayout(layout::Union{IncreasingStrides,DecreasingStrides}, ::Val{perm}) where {perm} issorted(perm) && return layout From ac67c352763126fa88268c70192c5399dcd7eab1 Mon Sep 17 00:00:00 2001 From: Michael Abbott Date: Fri, 3 Apr 2020 13:44:27 +0200 Subject: [PATCH 6/6] more tests --- test/test_layouts.jl | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/test/test_layouts.jl b/test/test_layouts.jl index 90f6880..861b097 100644 --- a/test/test_layouts.jl +++ b/test/test_layouts.jl @@ -201,6 +201,10 @@ struct FooNumber <: Number end @test MemoryLayout(PermutedDimsArray(C, (3,2,1))) == RowMajor() @test MemoryLayout(PermutedDimsArray(C, (2,3,1))) == UnitStride{3}() + revC = PermutedDimsArray(C, (3,2,1)); + @test MemoryLayout(PermutedDimsArray(revC, (3,2,1))) == ColumnMajor() + @test MemoryLayout(PermutedDimsArray(revC, (3,1,2))) == UnitStride{1}() + D = ones(10,20,30,40); @test MemoryLayout(D) == DenseColumnMajor() @test MemoryLayout(PermutedDimsArray(D, (1,2,3,4))) == DenseColumnMajor() @@ -212,11 +216,19 @@ struct FooNumber <: Number end @test MemoryLayout(PermutedDimsArray(D, (4,3,2,1))) == DenseRowMajor() @test MemoryLayout(PermutedDimsArray(D, (4,2,1,3))) == UnitStride{3}() + twoD = PermutedDimsArray(D, (3,1,2,4)); + MemoryLayout(PermutedDimsArray(twoD, (2,1,4,3))) == UnitStride{1}() + + revD = PermutedDimsArray(D, (4,3,2,1)); + MemoryLayout(PermutedDimsArray(revD, (4,3,2,1))) == DenseColumnMajor() + MemoryLayout(PermutedDimsArray(revD, (4,2,3,1))) == UnitStride{1}() + + issorted((1,2,3,4)) - @test_skip 0 == @allocated issorted((1,2,3,4)) # fails on Julia 1.4, in tests + # Fails on Julia 1.4, in tests. Could use BenchmarkTools.@ballocated instead. + @test_skip 0 == @allocated issorted((1,2,3,4)) reverse((1,2,3,4)) @test_skip 0 == @allocated reverse((1,2,3,4)) - revD = PermutedDimsArray(D, (4,3,2,1)); MemoryLayout(revD) @test 0 == @allocated MemoryLayout(revD) end