-
-
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
Extending in-place broadcast (broadcast!
) for user types
#24914
Comments
That's about is, as far as I know. Once you get your broadcasting in TypeSortedCollections worked out, could I ask you to contribute that sentence?
Not for me: julia> a = Foo()
Foo()
julia> broadcast!(sin, [1], a)
1-element Array{Int64,1}:
1 You just mean that it causes an ambiguity? Ambiguities are not specific to broadcasting, and in this case I don't see a solution other than the usual one (if you want to nix the ambiguity, you need to define the method that resolves it).
Again, this is a general julia issue and not specific to broadcasting. |
I can do that for this particular case, but it just seems very brittle, especially if a bunch of packages that may or may not be used together by users each extend OK on the other points. |
If you have an alternative, I'm all ears. How do you prevent ambiguities when some people are going to want to specialize on https://docs.julialang.org/en/latest/manual/methods/#man-method-design-ambiguities-1 |
One option could be to give the For example, Base could define broadcast!(f, dest::AbstractArray, As...) = broadcast_abstractarray!(f, dest, As...)
broadcast!(f, dest::Union{SparseMatrixCSC, SparseVector}, As...) = broadcast_sparse!(f, dest, As...) These appear to be the only specializations based on destination type currently in Base. Both Base and user packages could then define methods for |
Probably a more scalable option would be (with apologies for the horrible name) broadcast!(f, dest, As...) = broadcast_inputs!(f, dest, As...)
broadcast_inputs!(f, dest, As...) = # what the default broadcast! is now Then if you're specializing on Historically folks have not been happy with similar solutions (#10312). However, in practice it looks like we (unofficially) have this already: you could extend |
That's fine too. I can take a crack at it if people are happy enough with this approach. What are your thoughts on handling specializations based on the type of |
These issues are really tricky. What would be the rationale for giving the priority to It seems to me that we should try to ensure that ambiguities do not make it impossible (by throwing errors) to combine array types which have not been designed to work together. Else, the Instead, we should fall back to the |
(Apologies for not finishing reviewing #23939 yet and being relatively quiet on this front! Working on things JuliaLang/LinearAlgebra.jl#57 is consuming all my bandwidth.) Echoing Milan, one comment I planned to post in #23939 is that we should extend the wonderful mechanism therein to |
Agreed, this is an interesting design challenge, I like it 😈 Originally I didn't think we needed the broadcast!(f, dest, As...) = broadcast!(f, dest, combine_styles(As...), As...)
function broadcast!(f, dest, ::BroadcastStyle, As...)
# "real" implementation
end This seems open the door for specializing on the inputs while also limiting the potential for ambiguity by using the existing rules for combining inputs. In particular, this seems like a simple way to avoid any need to have separate variants depending on whether your This change wouldn't force the same computation to be repeated twice, since However, this is only a small portion of the fix---there's still the question of how to handle specializations of 3 different arguments, if isa(f, typeof(identity)) && N == 0
if isa(A, Number)
return fill!(C, A)
elseif isa(C, AbstractArray) && isa(A, AbstractArray) && Base.indices(C) == Base.indices(A)
return copy!(C, A)
end
end
# The rest of this would be identical to current Broadcast._broadcast! in which case Base would not have any specializations based on function type. (The With regards to how to specialize on broadcast!(f, dest, As...) = broadcast!(f, dest, combine_styles(As...), As...)
broadcast!(f, dest, ::BroadcastStyle, As...) = broadcast!(f, dest, nothing, As...)
@inline function broadcast!(f, C, ::Void, A, Bs::Vararg{Any,N}) where N
if isa(f, typeof(identity)) && N == 0
if isa(A, Number)
return fill!(C, A)
elseif isa(C, AbstractArray) && isa(A, AbstractArray) && Base.indices(C) == Base.indices(A)
return copy!(C, A)
end
end
# The rest of this would be identical to current Broadcast._broadcast!
end Then the rule is:
This explicitly gives precedence to the |
I like @timholy's suggestion, definitely the After thinking about it for a bit, I like the handling of |
Great proposal! I'm not completely sure we'll be able to get rid of all specializations on |
That all sounds fairly complicated. What do you think about doing user-controlled lazy-fusion instead? I started a branch to demonstrate that, I just stopped short of finishing updating some of the tests. If anyone wants to take it over, it's at https://github.com/JuliaLang/julia/compare/vtjnash:jn/lazydotfuse. |
Can you explain how it would help with the problem at hand? |
Fix JuliaLang#24914 (WIP). Address comments. Reimplement and generalize all-scalar optimization. Fix allocation tests for sparse broadcast!. Revert back to calling Broadcast.combine_styles twice. Fix more allocation tests in higherorderfns.jl (by @timholy).
Address comments. Reimplement and generalize all-scalar optimization. Fix allocation tests for sparse broadcast!. Revert back to calling Broadcast.combine_styles twice. Fix more allocation tests in higherorderfns.jl (by @timholy).
Reimplement and generalize all-scalar optimization. Add documentation. Explicitly return dest in various broadcast!-related methods. This is to make things easier on inference. Found by @timholy.
Reimplement and generalize all-scalar optimization. Add documentation. Explicitly return dest in various broadcast!-related methods. This is to make things easier on inference. Found by @timholy.
Reimplement and generalize all-scalar optimization. Add documentation. Explicitly return dest in various broadcast!-related methods. This is to make things easier on inference. Found by @timholy. Collapse spbroadcast_args! into broadcast! as suggested by @Sacha0. Also add nothing argument to broadcast! calls in sparse broadcast as suggested by @timholy.
#23939 introduced a new
broadcast
/broadcast!
API and documentation. Although I think the changes are great forbroadcast
, I think the story forbroadcast!
is a little weaker.First, I'd like to request a bit more documentation on extending
broadcast!
for user types. In what follows I'm assuming that the idea is to simply add methods tobroadcast!
. Even if that's all, a sentence stating that that is the case would be appreciated.Second (and maybe this can be handled by more documentation), how are users meant to extend
broadcast!
when the destination is not a user type, e.g.AbstractVector
, but one of the other arguments is a user type? For example, I'd like to define something like:but that conflicts with
(specifically, my use case is re-implementing
broadcast!
in TypeSortedCollections withAbstractVector
destination, current code here).Third, if the non-destination arguments are
Vararg
, there is the additional issue that it's not possible to express the constraint that at least one of the arguments in a list needs to be of a certain type (see #21026 (comment) and following comments).The text was updated successfully, but these errors were encountered: