Limit broadcast mechanism over Nullables#19787
Conversation
|
I'm ok with this limitation to scalars, as was @johnmyleswhite in the other issue, at least for now. My understanding is that the lifting would apply to |
TotalVerb
left a comment
There was a problem hiding this comment.
Needs modification of NEWS.md and the manual, in the section on Nullables.
|
My hero! Thank you. |
| as a 1-dimensional array) expanding singleton dimensions. | ||
|
|
||
| The following additional rules apply to `Nullable` arguments: | ||
| The following additional rule apply to `Nullable` arguments: |
|
This seems to simplify things so 👍. One thing that would be great to include in future design considerations for the broadcasting code would be extensibility beyond base. I.e. how user defined arrays should overload broadcast methods. I've been working on such a case in JuliaParallel/DistributedArrays.jl#120 and although it wasn't too hard with the current structure, it didn't feel like extensibility beyond base had been a priority. |
0c7bd1a to
ce7ebeb
Compare
| # broadcast should only "peel" one container level | ||
| let io = IOBuffer() | ||
| broadcast(x -> print(io, x), [Nullable(1.0)]) | ||
| String(take!(io)) == "Nullable{Float64}(1.0)" |
| _broadcast_type{S}(::Type{S}, f, T::Type, As...) = Base._return_type(f, typestuple(S, T, As...)) | ||
| _broadcast_type{T}(::Type{T}, f, A, Bs...) = Base._default_eltype(Base.Generator{ziptype(T, A, Bs...), ftype(f, A, Bs...)}) | ||
| _broadcast_type(f, T::Type, As...) = Base._return_type(f, typestuple(T, As...)) | ||
| _broadcast_type(f, A, Bs...) = Base._default_eltype(Base.Generator{ziptype(A, Bs...), ftype(f, A, Bs...)}) |
There was a problem hiding this comment.
this is used a couple of times in the new sparse code that I suspect you'll have to change done
| fpreszeros = fofzeros == zero(fofzeros) | ||
| maxnnzC = fpreszeros ? min(length(A), _sumnnzs(A, Bs...)) : length(A) | ||
| entrytypeC = Base.Broadcast._broadcast_type(Any, f, A, Bs...) | ||
| entrytypeC = Base.Broadcast._broadcast_type(f, A, Bs...) |
There was a problem hiding this comment.
(should be squashed on merge to avoid broken intermediate commits)
There was a problem hiding this comment.
Already squashed it to prevent that.
| The following additional rules apply to `Nullable` arguments: | ||
| The following additional rule applies to `Nullable` arguments: | ||
|
|
||
| - If there is at least a `Nullable`, and all the arguments are scalars or |
There was a problem hiding this comment.
Remove the list since there's only one bullet.
| # broadcast should only "peel" one container level | ||
| let io = IOBuffer() | ||
| broadcast(x -> print(io, x), [Nullable(1.0)]) | ||
| @test String(take!(io)) == "Nullable{Float64}(1.0)" |
There was a problem hiding this comment.
Sounds a bit weird to use string comparison to test the type of the result. Why not e.g. call push! to add values to a vector instead?
cb5afa5 to
e5c270a
Compare
|
I think this is ready, unless there are more comments/concerns. |
|
Thanks for doing this work. This still needs an update to doc/src/manual/types.md. |
Hoping to review shortly (beginning within the next fifteen minutes). Best! |
| @@ -291,22 +282,21 @@ ftype(T::Type, A...) = Type{T} | |||
| # if the first argument is Any, then Nullable should be treated like a | |||
| @@ -9,6 +9,8 @@ using Base: promote_eltype_op, linearindices, tail, OneTo, to_shape, | |||
| import Base: broadcast, broadcast! | |||
There was a problem hiding this comment.
I think the is_nullable_array import can be removed, and probably the function itself too (in base/nullable.jl).
|
|
||
| Broadcasts the arrays, tuples, `Ref`, nullables, and/or scalars `As` to a | ||
| container of the appropriate type and dimensions. In this context, anything | ||
| that is not a subtype of `AbstractArray`, `Ref` (except for `Ptr`s) or `Tuple`, |
There was a problem hiding this comment.
I don't think Nullable should be removed from this paragraph.
| @@ -349,7 +325,7 @@ end | |||
|
|
|||
| Broadcasts the arrays, tuples, `Ref`, nullables, and/or scalars `As` to a | |||
There was a problem hiding this comment.
Not from this pull request, but should `Ref` be plural `Ref`s for consistency?
| Broadcasts the arrays, tuples, `Ref`, nullables, and/or scalars `As` to a | ||
| container of the appropriate type and dimensions. In this context, anything | ||
| that is not a subtype of `AbstractArray`, `Ref` (except for `Ptr`s) or `Tuple`, | ||
| that is not a subtype of `AbstractArray`, `Ref` (except for `Ptr`s), `Tuple` |
There was a problem hiding this comment.
(Serial/Oxford) comma after Tuple?
| @@ -359,16 +335,9 @@ the following rules: | |||
| (and treats any `Ref` as a 0-dimensional array of its contents and any tuple | |||
| as a 1-dimensional array) expanding singleton dimensions. | |||
There was a problem hiding this comment.
Not from this pull request, but perhaps "If there is at least an array or a Ref in the arguments" -> "If there is at least one array or Ref in the arguments"? Similarly, perhaps "and treats any Ref as a 0-dimensional array of its contents and any tuple as a 1-dimensional array" -> "treating Refs as 0-dimensional arrays, and tuples as 1-dimensional arrays"?
Edit: Perhaps "If the arguments contain at least one array or Ref, it returns an array, treating Refs as 0-dimensional arrays, tuples as 1-dimensional arrays, and expanding singleton dimensions." would be better still?
| - If there is a tuple and a nullable, the result is an error, as this case is | ||
| not currently supported. | ||
| The following additional rule applies to `Nullable` arguments: If there is at | ||
| least a `Nullable`, and all the arguments are scalars or `Nullable`, it returns |
There was a problem hiding this comment.
Perhaps "at least a" should be "at least one"?
| @test isa(aa .* aa', Array19745) | ||
| end | ||
|
|
||
| # broadcast should only "peel off" one container layer |
There was a problem hiding this comment.
A simpler way to test this behavior might be nice if you can think of one?
There was a problem hiding this comment.
get.([Nullable(1), Nullable(2)]) == [1, 2] will work as a simpler test.
There was a problem hiding this comment.
Unfortunately I don't have much time for looking for better/simpler tests for today. Maybe we could revisit them another occasion?
There was a problem hiding this comment.
If @TotalVerb's suggestion does not suffice, sounds good on this end. Best!
There was a problem hiding this comment.
That should work fine. Test added.
Sacha0
left a comment
There was a problem hiding this comment.
I am in no way qualified to provide feedback on the Nullable behavior change's desirability, but this pull request's mechanics LGTM! Thanks @pabloferz!
| as a 1-dimensional array) expanding singleton dimensions. | ||
| - If the arguments contain at least one array or `Ref`, it returns an array | ||
| (expanding singleton dimensions), and treats `Ref`s as 0-dimensional arrays, | ||
| and tuples as a 1-dimensional arrays. |
There was a problem hiding this comment.
tuples as 1-dimensional arrays
(can ci skip this if you do make check-whitespace locally)
There was a problem hiding this comment.
dimensional, not element, sorry - just drop the a now that arrays is plural
|
@nanosoldier |
|
Your benchmark job has completed - no performance regressions were detected. A full report can be found here. cc @jrevels |
|
Looks like that i686 assertion failure "Cannot define a symbol twice!" in the reflection test is repeatable and has been happening since I merged this. Similar to #19792? I'll propose dropping back to LLVM 3.7 for now as a band-aid. |
|
I don't get why nanosoldier here said "no regressions" but when run on the merged version against its immediate parent commit there were some big regressions - https://github.com/JuliaCI/BaseBenchmarkReports/blob/a1fd436b6823d8e27c7bc1cf3be1b8c1f212bde7/f147aaa_vs_b2ae5a5/report.md I guess the intervening couple of commits that were merged to master between the time the PR run started and this was merged interacted badly here? |
|
That's strange, the code in this PR does not touch any code related to those regressions. |
|
I too don't see how a change to broadcasting can possibly affect comprehensions. |
This should address @JeffBezanson's concerns over #16961. That is broadcasting over
Nullables treats them as containers only when mixed with scalars. If people need to work with arrays they can use comprehensions, e.g.If someone is relying heavily on arrays of
Nullables they probably should be usingNullableArraysinstead.CC. @TotalVerb