Skip to content

Commit b6528ea

Browse files
authored
More dimension names documentation (#201)
This provides a specific example of supporting `dimnames`. Also removed the need to specify a method per dimension access to dimnames so `dimnames(::Type{T})` is all that's necessary.
1 parent f33e147 commit b6528ea

File tree

3 files changed

+32
-30
lines changed

3 files changed

+32
-30
lines changed

docs/src/index.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ For example, all `Integers` passed to `to_dims` are converted to `Int` (unless `
8282
This is also useful for arrays that uniquely label dimensions, in which case `to_dims` serves as a safe point of hooking into existing methods with dimension arguments.
8383
`ArrayInterface` also defines native `Symbol` to `Int` and `StaticSymbol` to `StaticInt` mapping for arrays defining [`ArrayInterface.dimnames`](@ref).
8484

85-
Methods accepting dimension specific arguments should use some variation of the following pattern.
85+
Methods requiring dimension specific arguments should use some variation of the following pattern.
8686

8787
```julia
8888
f(x, dim) = f(x, ArrayInterface.to_dims(x, dim))
@@ -93,3 +93,18 @@ f(x, dim::StaticInt) = ...
9393
If `x`'s first dimension is named `:dim_1` then calling `f(x, :dim_1)` would result in `f(x, 1)`.
9494
If users knew they always wanted to call `f(x, 2)` then they could define `h(x) = f(x, static(2))`, ensuring `f` passes along that information while compiling.
9595

96+
New types defining dimension names can do something similar to:
97+
```julia
98+
using Static
99+
using ArrayInterface
100+
101+
struct NewType{dnames} end # where dnames::Tuple{Vararg{Symbol}}
102+
103+
ArrayInterface.dimnames(::Type{NewType{dnames}}) = static(dnames)
104+
```
105+
106+
Dimension names should be appropriately propagated between nested arrays using `ArrayInterface.to_parent_dims`.
107+
This allows types such as `SubArray` and `PermutedDimsArray` to work with named dimensions.
108+
Similarly, other methods that return information corresponding to dimensions (e.g., `ArrayInterfce.size`, `ArrayInterface.axes`) use `to_parent_dims` to appropriately propagate parent information.
109+
110+

src/dimensions.jl

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ end
150150
"""
151151
has_dimnames(::Type{T}) -> Bool
152152
153-
Returns `static(true)` if `x` has on or more named dimensions.
153+
Returns `static(true)` if `x` has on or more named dimensions. If all dimensions correspond
154+
to `static(:_)`, then `static(false)` is returned.
154155
"""
155156
has_dimnames(x) = has_dimnames(typeof(x))
156157
@inline has_dimnames(::Type{T}) where {T} = _has_dimnames(dimnames(T))
@@ -164,32 +165,25 @@ const SUnderscore = StaticSymbol(:_)
164165
dimnames(::Type{T}) -> Tuple{Vararg{StaticSymbol}}
165166
dimnames(::Type{T}, dim) -> StaticSymbol
166167
167-
Return the names of the dimensions for `x`.
168+
Return the names of the dimensions for `x`. `static(:_)` is used to indicate a dimension
169+
does not have a name.
168170
"""
169171
@inline dimnames(x) = dimnames(typeof(x))
170-
@inline dimnames(x, dim) = dimnames(typeof(x), dim)
171-
@inline function dimnames(::Type{T}, dim) where {T}
172-
if parent_type(T) <: T
173-
return SUnderscore
174-
else
175-
return dimnames(parent_type(T), to_parent_dims(T, dim))
176-
end
172+
@inline dimnames(::Type{T}) where {T} = _dimnames(has_parent(T), T)
173+
_dimnames(::False, ::Type{T}) where {T} = ntuple(_->static(:_), Val(ndims(T)))
174+
@inline function _dimnames(::True, ::Type{T}) where {T}
175+
eachop(_perm_dimnames, to_parent_dims(T), dimnames(parent_type(T)))
177176
end
178-
@inline function dimnames(::Type{T}) where {T}
179-
if parent_type(T) <: T
180-
return ntuple(_ -> SUnderscore, Val(ndims(T)))
177+
178+
@inline dimnames(x, dim) = dimnames(typeof(x), dim)
179+
@inline dimnames(::Type{T}, dim::Integer) where {T} = _perm_dimnames(dimnames(T), dim)
180+
function _perm_dimnames(dnames::Tuple{Vararg{StaticSymbol,N}}, dim) where {N}
181+
if dim > N
182+
return static(:_)
181183
else
182-
perm = to_parent_dims(T)
183-
if invariant_permutation(perm, perm) isa True
184-
return dimnames(parent_type(T))
185-
else
186-
return eachop(dimnames, perm, parent_type(T))
187-
end
184+
return @inbounds(dnames[dim])
188185
end
189186
end
190-
function dimnames(::Type{T}) where {T<:SubArray}
191-
return eachop(dimnames, to_parent_dims(T), parent_type(T))
192-
end
193187

194188
"""
195189
to_dims(::Type{T}, dim) -> Union{Int,StaticInt}

test/dimensions.jl

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,7 @@ struct NamedDimsWrapper{L,T,N,P<:AbstractArray{T,N}} <: ArrayInterface.AbstractA
1010
NamedDimsWrapper{L}(p) where {L} = new{L,eltype(p),ndims(p),typeof(p)}(p)
1111
end
1212
ArrayInterface.parent_type(::Type{T}) where {P,T<:NamedDimsWrapper{<:Any,<:Any,<:Any,P}} = P
13-
ArrayInterface.dimnames(::Type{T}) where {L,T<:NamedDimsWrapper{L}} = static(Val(L))
14-
function ArrayInterface.dimnames(::Type{T}, dim) where {L,T<:NamedDimsWrapper{L}}
15-
if ndims(T) < dim
16-
return static(:_)
17-
else
18-
return static(L[dim])
19-
end
20-
end
13+
ArrayInterface.dimnames(::Type{T}) where {L,T<:NamedDimsWrapper{L}} = static(L)
2114
Base.parent(x::NamedDimsWrapper) = x.parent
2215

2316
@testset "dimension permutations" begin

0 commit comments

Comments
 (0)