Skip to content

Conversation

@p-zubieta
Copy link
Contributor

@p-zubieta p-zubieta 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.

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

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.

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?

@p-zubieta
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!

# nullables need to be treated like scalars sometimes and like containers
# other times, so there are two variants of typestuple.

# 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.

using Base: promote_eltype_op, linearindices, tail, OneTo, to_shape,
_msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache,
nullable_returntype, null_safe_eltype_op, hasvalue, is_nullable_array
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).

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!

"""
broadcast(f, As...)
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?

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?

- If the arguments are tuples and zero or more scalars, it returns a tuple.
- If there is at least an array or a `Ref` in the arguments, it returns an array
(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"?

@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?

@p-zubieta
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