-
Notifications
You must be signed in to change notification settings - Fork 37
index_labels
method and IndexLabel
type indexing
#328
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
aa8bdf9
88be1b1
8f1e165
2e4e4a2
d05d7bc
5f7434f
c27eb5a
b8d6e93
139fb33
8fde10b
d6cb218
bc7a635
7231dbe
268576f
122536f
d7c0a27
809839b
0ad5cd6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -90,7 +90,6 @@ function axes_types(::Type{A}) where {T,N,S,A<:Base.ReshapedReinterpretArray{T,N | |
end | ||
end | ||
|
||
|
||
# FUTURE NOTE: we avoid `SOneTo(1)` when `axis(A, dim::Int)``. This is inended to decreases | ||
# breaking changes for this adopting this method to situations where they clearly benefit | ||
# from the propagation of static axes. This creates the somewhat awkward situation of | ||
|
@@ -113,7 +112,7 @@ axes(A::ReshapedArray) = Base.axes(A) | |
@inline function axes(x::Union{MatAdjTrans,PermutedDimsArray}) | ||
map(GetIndex{false}(axes(parent(x))), to_parent_dims(x)) | ||
end | ||
axes(A::VecAdjTrans) = (SOneTo{1}(), axes(parent(A), 1)) | ||
axes(A::VecAdjTrans) = (SOneTo{1}(), getfield(axes(parent(A)), 1)) | ||
|
||
@inline axes(x::SubArray) = flatten_tuples(map(Base.Fix1(_sub_axes, x), sub_axes_map(typeof(x)))) | ||
@inline _sub_axes(x::SubArray, axis::SOneTo) = axis | ||
|
@@ -248,3 +247,64 @@ lazy_axes(x::AbstractRange, ::StaticInt{1}) = Base.axes1(x) | |
lazy_axes(x, ::Colon) = LazyAxis{:}(x) | ||
lazy_axes(x, ::StaticInt{dim}) where {dim} = ndims(x) < dim ? SOneTo{1}() : LazyAxis{dim}(x) | ||
@inline lazy_axes(x, dims::Tuple) = map(Base.Fix1(lazy_axes, x), dims) | ||
|
||
""" | ||
index_labels(x) | ||
index_labels(x, dim) | ||
|
||
Returns a tuple of labels assigned to each axis or a collection of labels corresponding to | ||
axis `dim` of `x`. Default is to simply return `map(keys, axes(x))`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it really a good idea to have the default return There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I should probably have changed that back to just the axis after all the discussion around naming this. My thinking was that it would be odd if we returned something that didn't "label" the indexes at all because we are assuming that is what's returned. Returning Throwing an error when this isn't defined also isn't an option because things like a vector transpose will inevitably have a dimension without labels. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we don't return There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's definitely doable and is probably a good method to have irregardless of what the default we choose. I'm not against returning something that clearly shows no labels are defined. Perhaps I'm being too strict with the rule that the return has the same indices as their corresponding axis, even for default values. I assume people will at least expect to be able to do things like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would also prefer There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it really worth defining a special type just for that? What would be the advantage over If in doubt, we could say that an error is thrown if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm just trying to figure out some generic representation of labels not being defined that can pass between methods. If we do @rafaqz how are you handling this situation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rafaqz, it looks like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, do we want to have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry I missed this earlier. If your |
||
""" | ||
index_labels(x, dim) = index_labels(x, to_dims(x, dim)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't the function live in ArrayInterfaceCore so that existing "named array" packages can overload it? BTW, it would be good to ping their authors to ensure they would all be OK with the API, otherwise it won't make a lot of sense. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Existing packages can overload it from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ArrayInterface is relatively heavy, which is why ArrayInterfaceCore was created IIUC. I guess it's up to package authors to say whether a dependency on ArrayInterface is acceptable for them or not. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That was more of an issue before we went through all the trouble to fix invalidations due to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, it's a bit of a chicken and egg issue. We use If someone has a reliable solution I'm open to it. I've been trying to actively address this and related issues for years There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @oxinabox is it still a concern depending on ArrayInterface at this point? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now that ArrayInterface doesn't like Requires.jl a billion packages, I am much more comfortable depending upon it for NamedDims.jl There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My suggestion was just to put empty function definitions in ArrayInterfaceCore (like we do with DataAPI and StatsAPI). That doesn't prevent keeping fallback method definitions in ArrayInterface as this PR does. But packages that don't want to use fallback definitions are still able to overload the functions by depending only on ArrayInterfaceCore. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We've been trying to avoid situations where the behavior of a method is different if ArrayInterface is loaded vs just ArrayInterfaceCore. |
||
index_labels(@nospecialize x::Number) = () | ||
@inline index_labels(x, d::CanonicalInt) = d > ndims(x) ? keys(axes(x, d)) : index_labels(x)[d] | ||
@inline index_labels(x) = is_forwarding_wrapper(x) ? index_labels(parent(x)) : map(keys, axes(x)) | ||
function index_labels(x::Union{MatAdjTrans,PermutedDimsArray}) | ||
map(GetIndex{false}(index_labels(parent(x))), to_parent_dims(x)) | ||
end | ||
index_labels(x::VecAdjTrans) = (keys(SOneTo{1}()), getfield(index_labels(parent(x)), 1)) | ||
index_labels(x::SubArray) = _sub_index_labels(parent(x), x.indices, IndicesInfo(x)) | ||
function _sub_index_labels(x::AbstractArray, inds::Tuple, ::IndicesInfo{N,pdims,cdims}) where {N,pdims,cdims} | ||
labels = index_labels(x) | ||
flatten_tuples(ntuple(Val{nfields(pdims)}()) do i | ||
pdim_i = getfield(pdims, i) | ||
cdim_i = getfield(cdims, i) | ||
index = getfield(inds, i) | ||
if pdim_i isa Tuple || cdim_i isa Tuple # no direct mapping to parent axes | ||
index_labels(index) | ||
elseif cdim_i === 0 # integer indexing drops axes | ||
() | ||
elseif pdim_i === 0 # trailing dimension | ||
LinearIndices((SOneTo{1}(),)) | ||
elseif index isa Base.Slice # index into labels where there is direct mapping to parent axis | ||
(getfield(labels, pdim_i),) | ||
else | ||
(@inbounds(getfield(labels, pdim_i)[index]),) | ||
end | ||
end) | ||
end | ||
index_labels(x::Union{LinearIndices,CartesianIndices}) = map(first ∘ index_labels, axes(x)) | ||
index_labels(x::Union{Symmetric,Hermitian}) = index_labels(parent(x)) | ||
index_labels(x::LazyAxis{N,P}) where {N,P} = (index_labels(getfield(x, :parent), StaticInt(N)),) | ||
@inline @inline function index_labels(x::Base.NonReshapedReinterpretArray{T,N,S}) where {T,N,S} | ||
if sizeof(T) === sizeof(S) | ||
return index_labels(parent(x)) | ||
else | ||
return flatten_tuples((keys(StaticInt(1):size(x, 1)), Base.tail(index_labels(parent(x))))) | ||
end | ||
end | ||
function index_labels(x::Base.ReshapedReinterpretArray{T,N,S}) where {T,N,S} | ||
_reinterpret_index_labels(div(StaticInt(sizeof(S)), StaticInt(sizeof(T))), x) | ||
end | ||
@inline function _reinterpreted_fieldnames(@nospecialize T::Type{<:Base.ReshapedReinterpretArray}) | ||
S = eltype(parent_type(T)) | ||
isstructtype(S) ? fieldnames(S) : () | ||
end | ||
function _reinterpret_index_labels(s::StaticInt{N}, x::Base.ReshapedReinterpretArray) where {N} | ||
__reinterpret_index_labels(s, _reinterpreted_fieldnames(typeof(x)), index_labels(parent(x))) | ||
end | ||
@inline function __reinterpret_index_labels(::StaticInt{N}, fields::NTuple{M,Symbol}, ks::Tuple) where {N,M} | ||
N === M ? (fields, ks...,) : (LinearIndices((SOneTo{N}(),)), ks...,) | ||
end | ||
_reinterpret_index_labels(::StaticInt{1}, x::Base.ReshapedReinterpretArray) = index_labels(parent(x)) | ||
_reinterpret_index_labels(::StaticInt{0}, x::Base.ReshapedReinterpretArray) = tail(index_labels(parent(x))) |
Uh oh!
There was an error while loading. Please reload this page.