Skip to content
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

Detangle Slice and fix mixed-dimension in-place reductions #28941

Merged
merged 4 commits into from
Dec 1, 2018

Conversation

mbauman
Copy link
Member

@mbauman mbauman commented Aug 29, 2018

Well this was a fun adventure. Here I thought fixing #28928 would just be a simple conversion to ntuple… it turned into a big rabbit hole that glaringly exposed the need to split our two uses of Base.Slice between flagging "whole dimensions" for SubArray and offset axes. This is something @timholy and I had talked about for quite some time and I thought would be more challenging and not a terribly pressing issue, but it turns out this conflation was producing incorrect results in our reductions.

@mbauman mbauman added the arrays [a, r, r, a, y, s] label Aug 29, 2018
@mbauman mbauman requested a review from timholy August 29, 2018 00:34
test/offsetarray.jl Outdated Show resolved Hide resolved
Copy link
Member

@timholy timholy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beautiful as always. I am really pleased to have the concepts split, 👍 . Most important question is regarding backwards compatibility, how much of an advantage is there in keeping both behaviors under a single "roof" by adding that type parameter? If that's not important then I think this could be made completely non-breaking?

@@ -218,17 +218,20 @@ Extract first entry of slices of array A into existing array R.
"""
copyfirst!(R::AbstractArray, A::AbstractArray) = mapfirst!(identity, R, A)

function mapfirst!(f, R::AbstractArray, A::AbstractArray)
function mapfirst!(f, R::AbstractArray, A::AbstractArray{<:Any,N}) where {N}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's up with the type signature here? Forced specialization?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had been having trouble getting ntuple to specialize with just constant propagation, so I switched to Val(N) instead… but you're probably right that Val(ndims(A)) would do just as well.

base/indices.jl Outdated
Slice(r::AbstractUnitRange) = Slice{typeof(r), false}(r)
Slice(S::Slice) = Slice{typeof(S.indices), false}(S.indices)
Slice(S::Slice{<:Any, false}) = S
const WholeSlice{T} = Slice{T, true}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

map!(f, R, view(A, t...))
end
# We know that the axes of R and A are compatible, but R might have a different number of
# dimensions than A, which is trickier than it seems due to offset arrays and type stability
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to have this inferrable/nonallocating.

stdlib/SparseArrays/src/sparsematrix.jl Outdated Show resolved Hide resolved
test/offsetarray.jl Show resolved Hide resolved
We use axes in many downstream computations that may not re-index directly into the original array, so we add a second type parameter to `Slice` that is turned on when converting `:` in indexing expressions -- and really only `SubArray` cares about.
…ty edge-cases

Alleviates #28928 but does not completely remove allocations due to the allocation of the view that gets passed to `map!`.
that we will encourage offset array implementations to use instead of Base.Slice
@mbauman
Copy link
Member Author

mbauman commented Sep 12, 2018

I tried out Tim's suggestion of a completely different type and I like it. On the downside, it's basically an exact copy of Slice's implementation, but on the upside, it means that the changes to Slice are essentially nonbreaking.

We just introduce a new type (I have named it IdentityUnitRange) that's effectively a copy of Slice but without the special "entire-dimension" handling. We can then encourage OffsetArrays.jl to use it instead of Base.Slice. Perhaps we should even export it as a new feature in 1.1. How do folks feel about the name? Here's its docstring:

   IdentityUnitRange(range::AbstractUnitRange)

 Represent an AbstractUnitRange `range` as an offset vector such that `range[i] == i`.

 `IdentityUnitRange`s are frequently used as axes for offset arrays.

@timholy
Copy link
Member

timholy commented Nov 30, 2018

Here's another fun one:

julia> copyto!([1:3;], OffsetArray(1:5, -2:2))
3-element Array{Int64,1}:
 4
 5
 3

The fact that it doesn't throw an error is due to this method. The core problem being that it's a Slice of a different array. Once these are disentangled and OffsetArrays don't use Slice anymore for their axes, this might still be an important test-case for arrays that do use them.

@mbauman
Copy link
Member Author

mbauman commented Nov 30, 2018

Let's make this PR happen. It fixes many such issues. I'm surprised it hasn't picked up any conflicts since I last touched it… let's see if CI is still happy.

@mbauman mbauman closed this Nov 30, 2018
@mbauman mbauman reopened this Nov 30, 2018
@timholy
Copy link
Member

timholy commented Nov 30, 2018

I say merge at will.

@mbauman mbauman merged commit 413fc47 into master Dec 1, 2018
@fredrikekre fredrikekre deleted the mb/sliceupslices branch December 1, 2018 19:18
Keno added a commit that referenced this pull request Jul 6, 2023
The change in #50429 moves around some dispatch boundaries and pushes
the allocations in the offsetarrays `maximum!` test over the limit.
The implementation of that code is massively type unstable. Somewhat,
ironically, the whole original point of that test was to test that
the implementation was not type-unstable (#28941), so actually opt our
OffsetArrays implementation into the interface that's supposed to
guarantee that.

If this PR is fine here, I'll submit the same upstream to avoid
diverging the implementations too much.
DilumAluthge pushed a commit that referenced this pull request Sep 1, 2023
The change in #50429 moves around some dispatch boundaries and pushes
the allocations in the offsetarrays `maximum!` test over the limit. The
implementation of that code is massively type unstable. Somewhat,
ironically, the whole original point of that test was to test that the
implementation was not type-unstable (#28941), so actually opt our
OffsetArrays implementation into the interface that's supposed to
guarantee that.

If this PR is fine here, I'll submit the same upstream to avoid
diverging the implementations too much.

Co-authored-by: Jameson Nash <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
arrays [a, r, r, a, y, s]
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants