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

Fixed the bug referenced in #43551 and added tests #43646

Closed
wants to merge 6 commits into from

Conversation

JakobSachs
Copy link
Contributor

Added a proute_rule for Bitarrays and added tests for it

@jakobnissen
Copy link
Contributor

Wait, are we sure we actually want this?

@JakobSachs
Copy link
Contributor Author

Wait, are we sure we actually want this?

I mean we already promote 'native' Bool-Arrays to Int-Arrays:

julia> promote([false,true],[1.3, 2.6])
([0.0, 1.0], [1.3, 2.6])

So why not BitArrays?

@jakobnissen
Copy link
Contributor

Because promotion, as it is now, is intended to make an object of a "lesser" type A into an object of a "larger" type B - in the sense that values of type A can always be represented in type B, but not necessarily vice versa. (this is not entirely true - most UInt can't be represented as Float64, but for practical purposes, most values that are typically in use can).

The problem here is that Array{Bool} and BitArray can represent the exact same values. It's not clear which one to promote to. The situation is more similar to String and SubString{String}.

@JeffBezanson
Copy link
Sponsor Member

Since promote([true], [1.0]) works, I have to agree it should also work if the first argument is a BitArray.

  • The # of dimensions should probably need to match, since we require that in other array promotion methods.
  • We should probably call promote_type(T,Bool) recursively just to make sure it makes sense.
    e.g.
promote_rule(a::Type{Array{T,n}}, b::Type{Array{S,n}}) where {T,n,S} = el_same(promote_type(T,S), a, b)

I'm also not sure what to do if both eltypes are Bool; we could continue to give an error in that case.

@JakobSachs
Copy link
Contributor Author

How about something like:

function promote_rule(::Type{BitArray{N}},::Type{Array{T,N}}) where {T,N} 
    typejoin(Bool,T) == Any && return promote_rule(Bool,T)
    Array{T,N}
end

Im not sure the typejoin(Bool,T) == Any is the best way to do this type of check, but it works in the cases ive tested.

It still works for our intended use-cases:

julia> promote(BitArray([false,true]),[1.3,2.6])
([0.0, 1.0], [1.3, 2.6])

julia> typeof(ans)
Tuple{Vector{Float64}, Vector{Float64}}

And correctly throws an error when used 'incorrectly':

julia> promote(BitArray([false,true]),[[1.3,2.6]])
ERROR: promotion of types BitVector and Vector{Vector{Float64}} failed to change any arguments
Stacktrace:
 [...]
julia> promote(BitArray([false,true]),["test"])
ERROR: promotion of types BitVector and Vector{String} failed to change any arguments
Stacktrace:
 [...]

Which is identical to the established behavior for normal Array{Bool}:

julia> promote([false,true],[[1.3,2.6]])
ERROR: promotion of types Vector{Bool} and Vector{Vector{Float64}} failed to change any arguments
Stacktrace:
 [...]
julia> promote([false,true],["test"])
ERROR: promotion of types Vector{Bool} and Vector{String} failed to change any arguments
Stacktrace:
 [...]

Comment on lines +1918 to +1919
typejoin(Bool,T) == Any && return promote_rule(Bool,T)
Array{T,N}
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Suggested change
typejoin(Bool,T) == Any && return promote_rule(Bool,T)
Array{T,N}
return Array{promote_type(Bool, T), N}

Comment on lines +1917 to +1920
function promote_rule(::Type{BitArray{N}},::Type{Array{T,N}}) where {T,N}
typejoin(Bool,T) == Any && return promote_rule(Bool,T)
Array{T,N}
end
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Suggested change
function promote_rule(::Type{BitArray{N}},::Type{Array{T,N}}) where {T,N}
typejoin(Bool,T) == Any && return promote_rule(Bool,T)
Array{T,N}
end
function promote_rule(a::Type{BitArray{N}}, b::Type{Array{T, N}}) where {T, N}
return el_same(promote_type(Bool, T), a, b)
end

Copy link
Sponsor Member

@vtjnash vtjnash Jan 10, 2022

Choose a reason for hiding this comment

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

But perhaps a better option is relax the existing method:

promote_rule(a::Type{Array{T,n}}, b::Type{Array{S,n}}) where {T,n,S} = el_same(promote_type(T,S), a, b)

to accepting ::Type{<:AbstractArray{S,n}} for a and b

EDIT: and if so, need to protect against:
julia> promote([false,true],BitArray(undef,2))

ERROR: StackOverflowError:
Stacktrace:
     [1] promote_result(#unused#::Type, #unused#::Type, #unused#::Type{Vector{Bool}}, #unused#::Type{BitVector})
       @ Base ./promotion.jl:308
     [2] promote_type(#unused#::Type{Vector{Bool}}, #unused#::Type{BitVector})
       @ Base ./promotion.jl:294
--- the last 2 lines are repeated 39990 more times ---
 [79983] promote_result(#unused#::Type, #unused#::Type, #unused#::Type{Vector{Bool}}, #unused#::Type{BitVector})
       @ Base ./promotion.jl:308

By fixing the definition of el_same:

$ git diff -U0 base/range.jl
diff --git a/base/range.jl b/base/range.jl
index 5c777c57fa..b4d87ec927 100644
--- a/base/range.jl
+++ b/base/range.jl
@@ -1259 +1259 @@ end
-el_same(::Type{T}, a::Type{<:AbstractArray{T,n}}, b::Type{<:AbstractArray{T,n}}) where {T,n}   = a
+el_same(::Type{T}, a::Type{<:AbstractArray{T,n}}, b::Type{<:AbstractArray{T,n}}) where {T,n}   = a === b ? a : Array{T,n}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants