-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Fixes, tests, & the future of Base.Slice #21052
Conversation
This is in preparation for a later reparametrization and renaming
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.
Hrm, I just realized that if Slice
no longer always represents all indices within a dimension then the index style computation for SubArray will need to change. That will significantly slow down many views with :
since they'll be indexed cartesian much more often. I'm no longer as certain that using this type more generally is a good idea.
Just as a side-note: As it stands, Base should only create offset Slice
s when you're using OffsetArrays in the first place.
@@ -440,13 +440,15 @@ false | |||
checkindex(::Type{Bool}, inds::AbstractUnitRange, i) = throw(ArgumentError("unable to check bounds for indices of type $(typeof(i))")) | |||
checkindex(::Type{Bool}, inds::AbstractUnitRange, i::Real) = (first(inds) <= i) & (i <= last(inds)) | |||
checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Colon) = true | |||
checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Slice) = true | |||
checkindex(::Type{Bool}, inds::AbstractUnitRange, s::Slice) = | |||
(first(s) >= first(inds)) & (last(s) <= last(inds)) |
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.
This is a little unfortunate. I suppose it's not a deal-breaker, but it stinks if we need to check bounds for indexing with colons.
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.
Yes, that was my main concern too. Colon
is effectively an unlimited Slice
, which is why we never needed to check bounds before; now that it's finite, we have to check.
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.
Could we resolve this by making Colon
a subtype of AbstractUnitRange
?
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 think that'd put us right back to where we are now, only s/Slice/Colon/
.
In order for Colon to be a range, it must know where to start and how long it is. That can only be resolved upon indexing (within to_indices
). Thus, there must be some way of constructing a colon with a given start and length. But if a user ends up using that same construction then it might not accurately describe an entire dimension. And now we're right back to where we started, except this time it's a more visible, exported object that users regularly construct.
Agreed, unless people create them manually: |
Let's get some data on how bad this would be for performance: @nanosoldier |
I think the largest regressions would come from deleting these methods: Lines 44 to 47 in 9f90b9a
|
Slice(convert(I, max(first(r), first(s)):min(last(r), last(s)))) | ||
intersect(r::Slice, s::Slice) = | ||
Slice(max(first(r), first(s)):min(last(r), last(s))) | ||
reverse(r::Slice) = error("reverse is not supported for Base.Slice") |
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.
is this #7512 or avoiding a fallback?
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.
Currently:
julia> reverse(Base.Slice(-10:10))
10:-1:-10
julia> indices(reverse(Base.Slice(-10:10)))
(Base.OneTo(21),)
Those indices should be the same as the indices of the original Slice
object (-10:10).
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.
is that Slice
s problem, or indices
' ?
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.
The general AbstractArray
reverse would throw an error for an offset array, but OrdinalRange
just always creates a 1-indexed array step range via colon()
.
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.
… and the correct result isn't representable without general offset array support. Slice itself cannot represent reverse(Base.Slice(-10:10))
— which should be an offset array with values 10:-1:-10
and indices -10:10
. It could be implemented like I did for length
and friends above, where it succeeds for 1-based slices but errors otherwise.
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.
In Base, we can't currently create an array with indices -10:10
unless it also holds values -10:10
(courtesy of Base.Slice
). It's a byproduct of being able to create some but not all types of OffsetArrays.
Perhaps we could add a |
Yeah, I'd be on board with that. We could make it easy to construct Maybe call it |
Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @jrevels |
Superseded by #27038 |
@mbauman added
Base.Slice
in #19730 as part of a major simplification of indexing rules. I confess I didn't really spend long enough thinking about it, and just mentally said to myself, "Colon got renamed." What I missed is that it's really quite simple and deserving of independent existence. Ifisa(r, Base.Slice)
, thenr
is anAbstractUnitRange
that should satisfy the following identities (which nearly imply one another):r[i] == i
for any in-boundsi
;r[r] === r
collect(r) == collect(indices(r, 1))
Now, thinking of this as "Colon got renamed" isn't strictly wrong, because in 0.5
Colon()
satisfies the first two of these (andindices(:)
is not defined). But the nameSlice
doesn't convey this; consequently, for Julia 1.0 I think we should rename it, as well as reparametrize it more along the lines of otherAbstractUnitRange
objects. In my mind the leading contenders areIdentityRange
(because of the first property) andIdempotentRange
(because of the second).More immediately, this type turns out to have useful properties that we're not currently exploiting or testing; in particular, it's a simple mechanism for creating "indices-preserving views", i.e.
v = view(a, Base.Slice(ind1), Base.Slice(ind2), ...)
satisfiesv[i, j, ...] == a[i, j, ...]
but might nevertheless represent a sub-region ofa
(and does not necessarily start indexing at 1). Wanting to exploit this, I noticed that the test suite forBase.Slice
was not very extensive. I decided to add quite a few tests, and unsurprisingly I discovered (and then fixed) a number of bugs.There's also a looming issue to address: without anyone really noticing,
Base.Slice
turns out to have introduced a non-1-indices AbstractArray type to Base. (I and others have put a lot of work into supporting non-1 arrays in Base, but Base does not actually allow you to create such an object without defining new types or loading packages.) The problem, then, is that unlike all otherAbstractArrays
s in Base, we can't support arithmetic operations onBase.Slice
. For example, we can't return a conceptually-correct value forr::Base.Slice + 1
unlessr
happens to start indexing at 1. I see two options:Given where we are, going back to
Colon
seems like a step backwards, so I don't really consider that to be a viable option.