-
Notifications
You must be signed in to change notification settings - Fork 37
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
More fall backs to parent working and AbstractArray2
#120
Merged
Merged
Changes from 2 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
ba58c63
Create separate axes/size/dimensions methods
Tokazama 44a6f5e
Throw Wrapper type in tests and catch more bugs
Tokazama ab82709
Get ReinterpretArray working
Tokazama 41ea958
Strides working with AbstractArray2
Tokazama cbcfdac
fix fxn(A, dim) methods
Tokazama 26d5bff
Test known_stride(x, dim) and known_offsets(x, dim)
Tokazama e7119c3
Test the nothings
Tokazama 452301d
Repuprpose `can_preserve_indices` + size tests
Tokazama b360be7
Test from_parent_dims(::ReinterpretArray)
Tokazama 93dd634
`Int` sizes + axes tests
Tokazama 5387002
Fixed dimnames and from_parent_dims tests
Tokazama File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
|
||
""" | ||
axes_types(::Type{T}, dim) | ||
|
||
Returns the axis type along dimension `dim`. | ||
""" | ||
axes_types(x, dim) = axes_types(typeof(x), dim) | ||
@inline axes_types(::Type{T}, dim) where {T} = axes_types(T, to_dims(T, dim)) | ||
@inline function axes_types(::Type{T}, dim::StaticInt) where {T} | ||
if dim > ndims(T) | ||
return OptionallyStaticUnitRange{One,One} | ||
else | ||
return _get_tuple(axes_types(T), dim) | ||
end | ||
end | ||
@inline function axes_types(::Type{T}, dim::Integer) where {T} | ||
if dim > ndims(T) | ||
return OptionallyStaticUnitRange{One,One} | ||
else | ||
return axes_types(T).parameters[Int(dim)] | ||
end | ||
end | ||
|
||
""" | ||
axes_types(::Type{T}) -> Type | ||
|
||
Returns the type of the axes for `T` | ||
""" | ||
axes_types(x) = axes_types(typeof(x)) | ||
function axes_types(::Type{T}) where {T} | ||
if parent_type(T) <: T | ||
return Tuple{Vararg{OptionallyStaticUnitRange{One,Int},ndims(T)}} | ||
else | ||
return eachop_tuple(axes_types, parent_type(T), to_parent_dims(T)) | ||
end | ||
end | ||
function axes_types(::Type{T}) where {T<:MatAdjTrans} | ||
return eachop_tuple(_get_tuple, axes_types(parent_type(T)), to_parent_dims(T)) | ||
end | ||
function axes_types(::Type{T}) where {T<:PermutedDimsArray} | ||
return eachop_tuple(_get_tuple, axes_types(parent_type(T)), to_parent_dims(T)) | ||
end | ||
function axes_types(::Type{T}) where {T<:AbstractRange} | ||
if known_length(T) === nothing | ||
return Tuple{OptionallyStaticUnitRange{One,Int}} | ||
else | ||
return Tuple{OptionallyStaticUnitRange{One,StaticInt{known_length(T)}}} | ||
end | ||
end | ||
|
||
@inline function axes_types(::Type{T}) where {P,I,T<:SubArray{<:Any,<:Any,P,I}} | ||
return _sub_axes_types(Val(ArrayStyle(T)), I, axes_types(P)) | ||
end | ||
@inline function axes_types(::Type{T}) where {T<:Base.ReinterpretArray} | ||
return _reinterpret_axes_types( | ||
axes_types(parent_type(T)), | ||
eltype(T), | ||
eltype(parent_type(T)), | ||
) | ||
end | ||
function axes_types(::Type{T}) where {N,T<:Base.ReshapedArray{<:Any,N}} | ||
return Tuple{Vararg{OptionallyStaticUnitRange{One,Int},N}} | ||
end | ||
|
||
# These methods help handle identifying axes that don't directly propagate from the | ||
# parent array axes. They may be worth making a formal part of the API, as they provide | ||
# a low traffic spot to change what axes_types produces. | ||
@inline function sub_axis_type(::Type{A}, ::Type{I}) where {A,I} | ||
if known_length(I) === nothing | ||
return OptionallyStaticUnitRange{One,Int} | ||
else | ||
return OptionallyStaticUnitRange{One,StaticInt{known_length(I)}} | ||
end | ||
end | ||
@generated function _sub_axes_types( | ||
::Val{S}, | ||
::Type{I}, | ||
::Type{PI}, | ||
) where {S,I<:Tuple,PI<:Tuple} | ||
out = Expr(:curly, :Tuple) | ||
d = 1 | ||
for i in I.parameters | ||
ad = argdims(S, i) | ||
if ad > 0 | ||
push!(out.args, :(sub_axis_type($(PI.parameters[d]), $i))) | ||
d += ad | ||
else | ||
d += 1 | ||
end | ||
end | ||
Expr(:block, Expr(:meta, :inline), out) | ||
end | ||
@inline function reinterpret_axis_type(::Type{A}, ::Type{T}, ::Type{S}) where {A,T,S} | ||
if known_length(A) === nothing | ||
return OptionallyStaticUnitRange{One,Int} | ||
else | ||
return OptionallyStaticUnitRange{ | ||
One, | ||
StaticInt{Int(known_length(A) / (sizeof(T) / sizeof(S)))}, | ||
} | ||
end | ||
end | ||
@generated function _reinterpret_axes_types( | ||
::Type{I}, | ||
::Type{T}, | ||
::Type{S}, | ||
) where {I<:Tuple,T,S} | ||
out = Expr(:curly, :Tuple) | ||
for i = 1:length(I.parameters) | ||
if i === 1 | ||
push!(out.args, reinterpret_axis_type(I.parameters[1], T, S)) | ||
else | ||
push!(out.args, I.parameters[i]) | ||
end | ||
end | ||
Expr(:block, Expr(:meta, :inline), out) | ||
end | ||
|
||
""" | ||
axes(A, d) | ||
|
||
Return a valid range that maps to each index along dimension `d` of `A`. | ||
""" | ||
axes(a, dim) = axes(a, to_dims(a, dim)) | ||
function axes(a::A, dim::Integer) where {A} | ||
if parent_type(A) <: A | ||
return Base.axes(a, Int(dim)) | ||
else | ||
return axes(parent(a), to_parent_dims(A, dim)) | ||
end | ||
end | ||
axes(A::SubArray, dim::Integer) = Base.axes(A, dim) # TODO implement ArrayInterface version | ||
axes(A::ReinterpretArray, dim::Integer) = Base.axes(A, dim) # TODO implement ArrayInterface version | ||
axes(A::Base.ReshapedArray, dim::Integer) = Base.axes(A, dim) # TODO implement ArrayInterface version | ||
|
||
""" | ||
axes(A) | ||
|
||
Return a tuple of ranges where each range maps to each element along a dimension of `A`. | ||
""" | ||
@inline function axes(a::A) where {A} | ||
if parent_type(A) <: A | ||
return Base.axes(a) | ||
else | ||
return axes(parent(a)) | ||
end | ||
end | ||
axes(A::PermutedDimsArray) = permute(axes(parent(A)), to_parent_dims(A)) | ||
axes(A::Union{Transpose,Adjoint}) = Base.axes(A) # TODO implement ArrayInterface version | ||
axes(A::SubArray) = Base.axes(A) # TODO implement ArrayInterface version | ||
axes(A::ReinterpretArray) = Base.axes(A) # TODO implement ArrayInterface version | ||
axes(A::Base.ReshapedArray) = Base.axes(A) # TODO implement ArrayInterface version | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the size and stride methods convert to
Int
to avoid potential type instabilities/problems in generic code unaware of ArrayInterface?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These only exist to pass things forward to the ArrayInterface implementation. But if you look at the actually implementation...
...
StaticInt
is never passed to a method in Base but persists internally, preventing premature conversion ofStaticInt
to anInt
and relying on constant propagation for inference.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I define a statically sized
AbstractArray2
using the ArtayInterface, thenBase.size
will suddenly returnStaticInt
s as well.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought it made sense because
StaticInt
is part of the public interface and we want to propagate static information, avoiding a dependence on constant propagation when dealing with deeply nested types. I'm sure we will run into some situations where methods explicitly requireInt
, but I also assumed that the end goal was forStaticInt
to become standard.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thinking was that users might assume that
size
returns anNTuple{N,Int}
, and end up inadvertently writing code likemyprod
that doesn't handle heterogenous tuples well:I don't want to accidentally cause bad performance in other libraries. I have no idea how prevalent code like this is though, and would hope authors would fix it.
I also figure that if code is set up to take advantage of static size information, it could probably also take the step to use
ArrayInterface.size
& co instead. E.g., many of my libraries haveusing ArrayInterface: size, strides
.But if code is written in ignorance of these, is it more likely that they'd benefit from the forced constant prop, or that they'd be hurt by the need for constant prop to ensure type stability?
As is, I have definitions like here:
Meant to try and avoid placing too many performance foot-guns about.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the examples Chris. I think it nicely illustrates the problems with returning
StaticInt
in the wild.It will undoubtedly cause problems introducing
StaticInt
into the public space, although StaticArrays already does this and returnsSOneTo
for its axes. Perhaps another way of approaching this is asking ourselves if we really do plan for something likeStaticInt
to be passed around in the future. If we do then at what point do we start pushing people to make that adjustment? Having to move back and forth between static and dynamic space so that users don't have to learn about using things likeNTuple{N}
vsTuple{Vararg{Any,N}}
seems like a mistake to me, but so does country music and I'm not going to take that away from people either.@timholy, you had some valuable input when we made
StaticInt
. Any opinions?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made it so that
strides
andsize
will returnInt
types. Returning statically sized axes seems to work for StaticArrays so I left that in. I think this also makes it so that new subtypes ofAbstractArray2
can just defineaxes
andaxes_types
andArrayInterface.size
andArrayInterface.strides
can catch any static info.