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

Limit broadcast mechanism over Nullables #19787

Merged
merged 5 commits into from
Jan 1, 2017

Conversation

pabloferz
Copy link
Contributor

@pabloferz pabloferz commented Dec 31, 2016

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.

[x .+ 1 for x in [Nullable(1), Nullable(2)]]

If someone is relying heavily on arrays of Nullables they probably should be using NullableArrays instead.

CC. @TotalVerb

@TotalVerb
Copy link
Contributor

TotalVerb commented Dec 31, 2016

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 NullableArrays, and be implemented there, but not Array{T <: Nullable}.

cc @nalimilan @Sacha0 @ararslan

Copy link
Contributor

@TotalVerb TotalVerb left a comment

Choose a reason for hiding this comment

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

Needs modification of NEWS.md and the manual, in the section on Nullables.

@JeffBezanson
Copy link
Member

My hero! Thank you.

@@ -360,16 +343,10 @@ 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.

The following additional rules apply to `Nullable` arguments:
The following additional rule apply to `Nullable` arguments:
Copy link
Contributor

Choose a reason for hiding this comment

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

rule applies

@andreasnoack
Copy link
Member

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.

# 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)"
Copy link
Contributor

Choose a reason for hiding this comment

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

missing @test

_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...)})
Copy link
Contributor

@tkelman tkelman Dec 31, 2016

Choose a reason for hiding this comment

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

this is used a couple of times in the new sparse code that I suspect you'll have to change done

@@ -73,7 +73,7 @@ function _noshapecheck_map{Tf,N}(f::Tf, A::SparseVecOrMat, Bs::Vararg{SparseVecO
fofzeros = f(_zeros_eltypes(A, Bs...)...)
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...)
Copy link
Contributor

Choose a reason for hiding this comment

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

(should be squashed on merge to avoid broken intermediate commits)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Already squashed it to prevent that.

@@ -360,16 +343,10 @@ 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.

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
Copy link
Member

Choose a reason for hiding this comment

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

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)"
Copy link
Member

Choose a reason for hiding this comment

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

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?

@pabloferz
Copy link
Contributor Author

I think this is ready, unless there are more comments/concerns.

@TotalVerb
Copy link
Contributor

Thanks for doing this work. This still needs an update to doc/src/manual/types.md.

@Sacha0
Copy link
Member

Sacha0 commented Dec 31, 2016

I think this is ready, unless there are more comments/concerns.

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
Copy link
Contributor

Choose a reason for hiding this comment

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

Delete this comment.

@@ -9,6 +9,8 @@ using Base: promote_eltype_op, linearindices, tail, OneTo, to_shape,
import Base: broadcast, broadcast!
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 the is_nullable_array import can be removed, and probably the function itself too (in base/nullable.jl).

@@ -349,8 +332,8 @@ end

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`,
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think Nullable should be removed from this paragraph.

Copy link
Contributor

@TotalVerb TotalVerb left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks again!

@@ -349,7 +325,7 @@ end

Broadcasts the arrays, tuples, `Ref`, nullables, and/or scalars `As` to a
Copy link
Member

Choose a reason for hiding this comment

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

Not from this pull request, but should `Ref` be plural `Ref`s for consistency?

@@ -349,7 +325,7 @@ end

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`
Copy link
Member

Choose a reason for hiding this comment

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

(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.
Copy link
Member

@Sacha0 Sacha0 Dec 31, 2016

Choose a reason for hiding this comment

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

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
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps "at least a" should be "at least one"?

@@ -409,3 +409,12 @@ Base.Broadcast.broadcast_c(f, ::Type{Array19745}, A, Bs...) =
@test isa(aa .+ 1, Array19745)
@test isa(aa .* aa', Array19745)
end

# broadcast should only "peel off" one container layer
Copy link
Member

Choose a reason for hiding this comment

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

A simpler way to test this behavior might be nice if you can think of one?

Copy link
Contributor

Choose a reason for hiding this comment

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

get.([Nullable(1), Nullable(2)]) == [1, 2] will work as a simpler test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately I don't have much time for looking for better/simpler tests for today. Maybe we could revisit them another occasion?

Copy link
Member

Choose a reason for hiding this comment

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

If @TotalVerb's suggestion does not suffice, sounds good on this end. Best!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That should work fine. Test added.

Copy link
Member

@Sacha0 Sacha0 left a comment

Choose a reason for hiding this comment

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

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.
Copy link
Contributor

@tkelman tkelman Jan 1, 2017

Choose a reason for hiding this comment

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

tuples as 1-dimensional arrays

(can ci skip this if you do make check-whitespace locally)

Copy link
Contributor

Choose a reason for hiding this comment

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

dimensional, not element, sorry - just drop the a now that arrays is plural

@tkelman
Copy link
Contributor

tkelman commented Jan 1, 2017

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

@nanosoldier
Copy link
Collaborator

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

@tkelman tkelman merged commit f147aaa into JuliaLang:master Jan 1, 2017
@tkelman
Copy link
Contributor

tkelman commented Jan 1, 2017

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.

@tkelman
Copy link
Contributor

tkelman commented Jan 2, 2017

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?

@pabloferz
Copy link
Contributor Author

That's strange, the code in this PR does not touch any code related to those regressions.

@TotalVerb
Copy link
Contributor

I too don't see how a change to broadcasting can possibly affect comprehensions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
missing data Base.missing and related functionality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants