-
-
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
Define basic FillArrays types in Base #39184
Conversation
I believe if you export any new types it would count as “Breaking”. So perhaps better to make a user do |
I believe Base has added new exported types in the past even though they were technically breaking. I think it'd be good to export I realize now that I didn't properly attribute the code which I copied. Does anyone have guidance on how to do that properly? |
Probably I wrote most of the code and don’t care too much how you attribute. So probably a comment is fine, though double check the terms of the MIT license |
given that |
Well, a big part of the motivation for doing this was to replace We need something immutable we can export from Base that people can use instead of |
|
||
getindex(F::Fill{<:Any,0}) = getindex_value(F) | ||
|
||
@inline getindex_value(F::Fill) = F.value |
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.
Do we need this? Why not just define this in getindex
and use F[]
everywhere?
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.
That seems inconsistent to me unless you meant something else:
julia> F = fill(1,3)
3-element Vector{Int64}:
1
1
1
julia> F[]
ERROR: BoundsError: attempt to access 3-element Vector{Int64} at index []
Stacktrace:
[1] throw_boundserror(A::Vector{Int64}, I::Tuple{})
@ Base ./abstractarray.jl:651
[2] checkbounds
@ ./abstractarray.jl:616 [inlined]
[3] _getindex
@ ./abstractarray.jl:1196 [inlined]
[4] getindex(::Vector{Int64})
@ Base ./abstractarray.jl:1170
[5] top-level scope
@ REPL[20]:1
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.
By F[]
I just meant indexing in general. Seems like an unnecessary indirection/complexity.
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.
It's to allow for unified code for Fill
, Zeros
, and Ones
. Using indexing doesn't work here as one would have to add artificial 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.
whitespace
One quirk about adopting julia> ones(1,10) .+ rand.(Ref(1:99))
1×10 Matrix{Float64}:
14.0 73.0 68.0 26.0 70.0 27.0 31.0 36.0 43.0 66.0
julia> ones(1,10) .+ rand.(fill(1:99))
1×10 Matrix{Float64}:
72.0 36.0 74.0 56.0 30.0 50.0 52.0 27.0 30.0 92.0
julia> using FillArrays
julia> ones(1,10) .+ rand.(Fill(1:99)) # all constant
1×10 Matrix{Float64}:
13.0 13.0 13.0 13.0 13.0 13.0 13.0 13.0 13.0 13.0 It looks like the implementation here does this, too. Is this desirable? |
end | ||
|
||
function broadcasted(::DefaultArrayStyle{1}, ::typeof(*), a::AbstractRange, b::AbstractFill) | ||
broadcast_shape(axes(a), axes(b)) == axes(a) || throw(ArgumentError("Cannot broadcast $a and $b. Convert $b to a Vector first.")) |
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 was carried over from FillArrays.jl but I think was a temporary restriction. I'm not entirely sure this restriction should be kept.
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 I just delete that line altogether?
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 check is necessary because the function may be incorrect in the "degenerate" case where a
is length 1
and so the shape comes from b
, not a
. Perhaps it would be better to be rewritten as
if length(a) == 1
broadcasted(*, a[1], b)
else
broadcasted(*, a, _broadcast_getindex_value(b))
end
but this would not be type stable.
In any case some thought should be put into this.
Oooof, thanks for bringing this up, @mcabbott. That behavior makes sense for FillArrays, but doesn't make sense for a scalarifier :( |
Even for The one argument I saw was that sparse arrays do something similar. The justification there is that you'd like Also, what sparse arrays do is not so strong. It seems to only apply along some dimensions which they create in the broadcast, not along others: using SparseArrays
r99(ignore) = rand(1:99);
# almost like above?
ones(1,10) .+ r99.(spzeros(1)) # all random
# alone
r99.(spzeros(10)) # all constant
r99.(spzeros(10, 10)) # all constant
# broadcasting with special vectors
ones(1,10) .+ r99.(fill("zero", 10)) # all random
ones(1,10) .+ r99.(spzeros(10)) # columns are constant
ones(1,10) .+ r99.(Fill("zero", 10)) # all constant
ones(10,1) .+ r99.(spzeros(10)') # all random
# ... and when those are extended
ones(10,10) .+ r99.(fill("zero", 10)) # all random
ones(10,10) .+ r99.(spzeros(10)) # all random
ones(10,10) .+ r99.(Fill("zero", 10)) # all constant
# broadcasting with a sparse matrix
ones(1,1) .+ r99.(spzeros(10, 10)) # columns are constant
ones(1,10) .+ r99.(spzeros(10, 10)) # columns are constant
ones(10,1) .+ r99.(spzeros(10, 10)) # all constant |
It was a decision made out of practicality, since this was the only way to allow broadcasting, and yes SparseArrays provided precedence for this decision. So there are a few options:
|
It would be nice more generally if broadcasting had some knowledge of purity, as this could be exploited any time a dimension is extended:
No idea how hard that would be, though -- for some loop orders there will be a tradeoff with storage, which the |
So Ranges do some O(1) things like this, but they're explicitly enabled for a select group of operators that are known to be linear and range-preserving, like: Line 1124 in 29d5158
The trouble here is that we have a much larger group of functions (pure instead of just linear). Trying to do the same will be a never-ending task (or require that bigger purity system). That said, in many cases, it's as simple as manually hoisting the |
I don't think the |
This removes the dependence on inlining for performance, so we also remove `@inline`, since it can harm performance. make Some type a zero-dim broadcast container (e.g. a scalar) Replaces JuliaLang#35778 Replaces JuliaLang#39184 Fixes JuliaLang#39151 Refs JuliaLang#35675 Refs JuliaLang#43200
This removes the dependence on inlining for performance, so we also remove `@inline`, since it can harm performance. make Some type a zero-dim broadcast container (e.g. a scalar) Replaces JuliaLang#35778 Replaces JuliaLang#39184 Fixes JuliaLang#39151 Refs JuliaLang#35675 Refs JuliaLang#43200
This removes the dependence on inlining for performance, so we also remove `@inline`, since it can harm performance. make Some type a zero-dim broadcast container (e.g. a scalar) Replaces JuliaLang#35778 Replaces JuliaLang#39184 Fixes JuliaLang#39151 Refs JuliaLang#35675 Refs JuliaLang#43200
@MasonProtter Can you please provide the reason for closure for future reference? I'm assuming its because it was not being actively maintained which is fine. |
@dlfivefifty, I stopped working on this years ago because it seemed there was a fundamental mismatch between how this For this to be a useful immutable replacement for All the options laid out in #39184 (comment) seemed worse than just not doing this. And now it's been a few years and the PR is quite stale anyways. |
As suggested by @mbauman and @dlfivefifty in #35778 (comment) and #35778 (comment), I have copied some basic FillArrays.jl infrastructure into Base with the expectation that FillArrays.jl will pirate this infrastructure.
This way, we can suggest people use
Fill(x)
rather thanRef(x)
in broadcast code, as well as use that internally which will be more transparent to the compiler due to immutability. For example, this should fix #39151.I don't have the best understanding of FillArrays.jl (though it is pretty simple), so I'm pretty sure the subset I copied here is not an ideal subset. There's probably stuff I copied that I shouldn't have and vice versa.
Closes #18379