-
-
Notifications
You must be signed in to change notification settings - Fork 24
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
Allow zero-arrays #58
Conversation
Should this allow One way to do so would be make it a scalar as far as the rules are concerned, by unwrapping, something like this: isnumeric(x::Ref{<:Number}) = true # these aren't leaf according to functors
isnumeric(x::Ref{<:Integer}) = false
iswriteable(::Ref) = true # needed not for subtract!, but for update's fmap(copy)
init(o, x::Ref) = init(o, x.x) # treat like a scalar for what state to store
update!(ℓ::Leaf, x::Ref, ::Zero, ::Zero...) = ℓ, x
function update!(ℓ::Leaf, x::Ref, x̄s...)
s′, x̄′ = apply!(ℓ.rule, ℓ.state, x.x, map(x̄ -> base(x̄).x, x̄s)...)
x[] = x[] .- x̄′
Leaf(ℓ.rule, s′), x
end Tests here have a few examples, but the are regarded as non-trainable -- it's just testing that they don't give errors really. |
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 can see how one might accidentally get a 0-dim array, but a Ref
is a conscious choice. What's the value over using a 0-dim array or single element array? I thinking ignoring them is fine.
if iswriteable(x) | ||
x .= x .- x̄ | ||
else | ||
broadcast_preserving_zero_d(eltype(x), broadcasted(-, x, x̄)) |
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 a public function? Meaning we can expect it to be stable?
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'm not entirely sure. It has a docstring but isn't in the manual. It's used quite a bit, e.g. to implement conj
at https://github.com/JuliaLang/julia/blob/4c8c5153a566b25ef8c7b7091b5126328812d287/base/abstractarraymath.jl#L145 .
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 doubt this is the only use of it in the wild either: https://juliahub.com/ui/Search?q=broadcast_preserving_zero_d&type=code
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.
Good enough for me!
For zero-arrays, someone will try, and I think it should either work or be an error. Seems bad if Maybe |
👍🏾
Certainly not contentious enough on my end that it can't be resolved now, but Flux's advice for scalar params has been a 1-element vector for quite some time. I don't see the practical reason someone would want |
While looking for JuliaLang/julia#35778 (comment) as background reading for this discussion, I found https://juliaarrays.github.io/StaticArrays.jl/latest/pages/api/#Scalar. Perhaps it's fine to ask users to use a type like this if they want 0-d arrays? |
That plays badly with Functors right now: julia> fmap(println, (x = Fill(1.0), y = Fill(1.0)))
Fill(1.0)
(x = nothing, y = nothing) And if we fix it, we can fix scalars too. Really you should use a 1-element vector, This PR votes for "work" now. |
I thought the consternation with using |
Yes I don't know what to do there. These don't follow Base: julia> using FillArrays
julia> Fill(1) .+ Fill(2)
0-dimensional Fill{Int64}, with entry equal to 3
julia> using StaticArrays
julia> s0 = SArray{Tuple{}}(fill(1.0))
Scalar{Float64}((1.0,))
julia> s0 isa AbstractArray{Float64, 0}
true
julia> s0 .+ s0
Scalar{Float64}((2.0,)) which results in weird behaviour or errors:
xref JuliaArrays/FillArrays.jl#145 and nothing https://github.com/JuliaArrays/StaticArrays.jl/search?q=broadcast_preserving_zero_d They also don't really work with Functors.jl, so perhaps they aren't usable anyway. Maybe this PR is a bit random then, since Array actually works (by mutation) and ImmutableArrays haven't landed yet. And wrappers like this aren't preserved by broadcasting: julia> using ReadOnlyArrays
julia> ReadOnlyArray(fill(0))
0-dimensional ReadOnlyArray{Int64, 0, Array{Int64, 0}}:
0
julia> ans .+ ans
0
julia> Broadcast.broadcast_preserving_zero_d(-, ReadOnlyArray(fill(0)), ReadOnlyArray(fill(0)))
0-dimensional Array{Int64, 0}:
0
julia> ReadOnlyArray([1,2]) .+ 3
2-element Vector{Int64}:
4
5 |
This wants to check that zero-dimensional arrays are allowed, and preserved.
But they aren't right now, because
subtract!
will return a number. Unless it's writing in-place. Unless we usebroadcast_preserving_zero_d
, which, edit, this PR now does:This doesn't work for StaticArrays, maybe that's their problem not ours:
Alternatively, we could ban zero-arrays as being too weird.