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

Fix length calculation of broadcast over tuples #23887

Merged
merged 1 commit into from
Nov 7, 2017

Conversation

pabloferz
Copy link
Contributor

Fixes #23647

@ararslan ararslan added collections Data structures holding multiple items, e.g. sets broadcast Applying a function over a collection labels Sep 27, 2017
@ararslan ararslan requested a review from mbauman September 27, 2017 00:41
@Sacha0
Copy link
Member

Sacha0 commented Sep 27, 2017

Was #23647 introduced by #21331? The length determination logic that existed prior to #21331 seems a bit simpler than that in this pull request?

@@ -127,6 +127,7 @@ end
end

Base.@propagate_inbounds _broadcast_getindex(A, I) = _broadcast_getindex(containertype(A), A, I)
Base.@propagate_inbounds _broadcast_getindex(::Type{Tuple}, A::Tuple{Any}, I) = A[1]
Copy link
Member

Choose a reason for hiding this comment

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

Should A[1] not be A[I]?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, you can see that there is a ::Tuple{Any} in the signature, we need this for handling cases like (1,2,3) .+ (1,). This shouldn't be a problem as we want (x,) where x is a scalar to behave the same as [x] or x.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I missed the Any last night :). To make this method's purpose clearer, perhaps add a comment or an adjacent general method

Base.@propagate_inbounds _broadcast_getindex(::Type{Tuple}, A::Tuple, I) = A[I]

though it would be redundant with

Base.@propagate_inbounds _broadcast_getindex(::Any, A, I) = A[I]

below?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have added a comment.

@pabloferz
Copy link
Contributor Author

The length determination logic that existed prior to #21331 seems a bit simpler than that in this pull request?

The problem with the previous approach is that it was hard to get a type stable result, that was the reason for #21331 but that solution wasn't good enough.

@Sacha0
Copy link
Member

Sacha0 commented Sep 27, 2017

The problem with the previous approach is that it was hard to get a type stable result, that was the reason for #21331 but that solution wasn't good enough.

Yes, rather I was wondering whether the simpler former logic could be improved while remaining simpler than that in this pull request. A few comments describing the logic in this pull request would do the trick as well :). Best!

@pabloferz
Copy link
Contributor Author

Yes, rather I was wondering whether the simpler former logic could be improved while remaining simpler than that in this pull request.

I couldn't come up with a simpler approach, but I have added comments as you suggested. The wording could probably be improved, so if you have any suggestions about it, I'm all ears.

# When the result of broadcast is a tuple it can only come from mixing n-tuples
# of the same length with scalars and 1-tuples. So, in order to have a
# type-stable broadcast we need to find a tuple of maximum length (except when
# there are only scalars empty tuples and empty tuples, in which case the
Copy link
Member

Choose a reason for hiding this comment

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

One too many "empty tuples?"

@Sacha0
Copy link
Member

Sacha0 commented Sep 29, 2017

💯 for the comments! :)

@pabloferz
Copy link
Contributor Author

pabloferz commented Sep 29, 2017

Because of a possible mess with ambiguities, I decided to use the containertype trait to disambiguate methods. I believe this also makes the implementation much clearer (in addition to the presence of some comments)

@pabloferz
Copy link
Contributor Author

Has anyone have more comments/objections? If not I'd like to merge this in a day or two.

_tuplebroadcast_maxtuple(containertype(A), containertype(B), A, B)
@inline tuplebroadcast_maxtuple(A, Bs...) =
tuplebroadcast_maxtuple(A, tuplebroadcast_maxtuple(Bs...))
tuplebroadcast_maxtuple(A::NTuple{N,Any}, ::NTuple{N,Any}...) where {N} = A
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this needs to be a _tuplebroadcast_maxtuple method, for example with the testcase broadcast(+, (1, 2), (3, 4), 5).

Copy link
Contributor Author

@pabloferz pabloferz Oct 2, 2017

Choose a reason for hiding this comment

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

That example works with this PR as is. I had it like this to reduce the compiler/inference work in cases where you have a bunch of tuples of the same length and a scalar element (which I imagined would be common), e.g. (1,2) .+ (2,3) .+ (3,4) .+ (4,5) .+ 1. But I can make it a _tuplebroadcast_maxtuple for consistency if preferred.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, I screwed up the order of my example. It should be broadcast(+, 5, (1, 2), (3, 4)) which I believe would not currently work.

Copy link
Contributor Author

@pabloferz pabloferz Nov 7, 2017

Choose a reason for hiding this comment

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

I can confirm that example (or broadcast(+, (1, 2), 5, (3, 4))) works fine.

tuplebroadcast_maxtuple will call _tuplebroadcast_maxtuple only when the number of arguments to tuplebroadcast_maxtuple is two and both are not tuples of the same length (if they are, dispatch would select tuplebroadcast_maxtuple(A::NTuple{N,Any}, ::NTuple{N,Any}...) where {N} instead).

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I understand. Thanks for the correction.

@TotalVerb
Copy link
Contributor

Wouldn't a method for two tuples of the same length be necessary anyway? This method can be kept as a specialization, right?

@timholy
Copy link
Member

timholy commented Oct 3, 2017

In case it's of interest, #23939 has a longest method.

@pabloferz
Copy link
Contributor Author

pabloferz commented Oct 3, 2017

Wouldn't a method for two tuples of the same length be necessary anyway? This method can be kept as a specialization, right?

Yes a method for tuples of the same length is necessary. And the current one could be changed into a _tuplebroadcast_maxtuple specialization, but I wrote it like this as an optimization for inference/compilation (as opposed to a runtime optimization). This could be not all that important (someone like @vtjnash could check if it's worth it), in which case I can change it.

In case it's of interest, #23939 has a longest method.

Thanks Tim, I don't think both behave the same (but that PR is really nice, I'm going to take a closer look into it).

@Sacha0
Copy link
Member

Sacha0 commented Oct 3, 2017

@nanosoldier runbenchmarks(ALL, vs = ":master")

@nanosoldier
Copy link
Collaborator

Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @ararslan

@Sacha0
Copy link
Member

Sacha0 commented Oct 4, 2017

@nanosoldier runbenchmarks("tuple" || "scalar", vs = ":master")

@nanosoldier
Copy link
Collaborator

Your benchmark job has completed - no performance regressions were detected. A full report can be found here. cc @ararslan

@ararslan
Copy link
Member

ararslan commented Nov 7, 2017

Good to go?

@TotalVerb
Copy link
Contributor

@pabloferz Please confirm the behavior of broadcast(+, 5, (1, 2), (3, 4)); while I have not tested the code I still believe the mentioned _tuplebroadcast_maxtuple method to be necessary.

@pabloferz
Copy link
Contributor Author

@TotalVerb the behaviour of broadcast for the examples you have mentioned seems to be the expected with this PR.

@ararslan ararslan merged commit 698ef27 into JuliaLang:master Nov 7, 2017
@pabloferz pabloferz deleted the pz/tuplebroadcast branch November 7, 2017 20:49
ararslan pushed a commit that referenced this pull request Nov 7, 2017
ararslan pushed a commit that referenced this pull request Nov 8, 2017
ararslan pushed a commit that referenced this pull request Nov 8, 2017
ararslan pushed a commit that referenced this pull request Nov 8, 2017
ararslan pushed a commit that referenced this pull request Nov 8, 2017
ararslan pushed a commit that referenced this pull request Nov 9, 2017
ararslan pushed a commit that referenced this pull request Nov 14, 2017
ararslan pushed a commit that referenced this pull request Nov 21, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
broadcast Applying a function over a collection collections Data structures holding multiple items, e.g. sets
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants