Skip to content

Bug in function all #45562

Description

@zdlspace0528

I find a bug about all.

here is the result.

julia> all([3, 3, 3], dims = 1)
1-element Vector{Bool}:
 1

The result maybe wrong.

julia> for i in 1:10
           @show all(i * ones(Int, 3), dims = 1)
       end
all(i * ones(Int, 3), dims = 1) = Bool[1]
all(i * ones(Int, 3), dims = 1) = Bool[0]
all(i * ones(Int, 3), dims = 1) = Bool[1]
all(i * ones(Int, 3), dims = 1) = Bool[0]
all(i * ones(Int, 3), dims = 1) = Bool[1]
all(i * ones(Int, 3), dims = 1) = Bool[0]
all(i * ones(Int, 3), dims = 1) = Bool[1]
all(i * ones(Int, 3), dims = 1) = Bool[0]
all(i * ones(Int, 3), dims = 1) = Bool[1]
all(i * ones(Int, 3), dims = 1) = Bool[0]

Activity

  1. zdlspace0528 commented on Jun 2, 2022

    @zdlspace0528
    Author

    We talk about this problem here

  2. added
    bugIndicates an unexpected problem or unintended behavior
    on Jun 2, 2022
  3. LilithHafner commented on Jun 2, 2022

    @LilithHafner
    Member

    any throws an error:

    julia> any([3, 3, 3], dims = 1)
    ERROR: InexactError: Bool(3)
    Stacktrace:
      [1] Bool
        @ ./float.jl:158 [inlined]
      [2] convert
        @ ./number.jl:7 [inlined]
      [3] setindex!
        @ ./array.jl:959 [inlined]
      [4] setindex!
        @ ./multidimensional.jl:674 [inlined]
      [5] _mapreducedim!(f::typeof(identity), op::typeof(|), R::Vector{Bool}, A::Vector{Int64})
        @ Base ./reducedim.jl:311
      [6] mapreducedim!(f::Function, op::Function, R::Vector{Bool}, A::Vector{Int64})
        @ Base ./reducedim.jl:324
      [7] _mapreduce_dim(f::Function, op::Function, #unused#::Base._InitialValue, A::Vector{Int64}, dims::Int64)
        @ Base ./reducedim.jl:371
      [8] #mapreduce#758
        @ ./reducedim.jl:357 [inlined]
      [9] #_any#812
        @ ./reducedim.jl:1023 [inlined]
     [10] _any
        @ ./reducedim.jl:1023 [inlined]
     [11] #_any#811
        @ ./reducedim.jl:1022 [inlined]
     [12] _any
        @ ./reducedim.jl:1022 [inlined]
     [13] #any#785
        @ ./reducedim.jl:1003 [inlined]
     [14] top-level scope
        @ REPL[428]:1
  4. LilithHafner commented on Jun 2, 2022

    @LilithHafner
    Member

    The error we want is ERROR: TypeError: non-boolean (Int64) used in boolean context

  5. LilithHafner commented on Jun 2, 2022

    @LilithHafner
    Member

    From @pfitzseb in the linked discussion

    This boils down to

    julia> mapreduce(identity, &, [3,3,3]; dims=1)
    1-element Vector{Bool}:
    1
    
    julia> mapreduce(identity, &, [2,2,2]; dims=1)
    1-element Vector{Bool}:
    0
    

    and definitely is a bug.

    The problem is that we are using & and | for all and any. Unfortunately, || and && are not functions.

  6. LilithHafner commented on Jun 2, 2022

    @LilithHafner
    Member

    We should be using something that errors on non-booleans. We can write up custom functions and use these instead:

    boolean_and(a::Bool, b::Bool) = a & b
    boolean_and(a::Bool, b) = throw(TypeError(:boolean_and, Bool, b))
    boolean_and(a, b) = throw(TypeError(:boolean_and, Bool, a))
    boolean_or(a::Bool, b::Bool) = a | b
    boolean_or(a::Bool, b) = throw(TypeError(:boolean_or, Bool, b))
    boolean_or(a, b) = throw(TypeError(:boolean_or, Bool, a))
  7. GunnarFarneback commented on Jun 3, 2022

    @GunnarFarneback
    Contributor

    Notice that the investigation revealed two separate bugs with reductions over an axis.

    1. This should error like its non-axis version:
    julia> all([3, 3, 3], dims = 1)
    1-element Vector{Bool}:
     1
    
    julia> all([3, 3, 3])
    ERROR: TypeError: non-boolean (Int64) used in boolean context
    
    1. The downstream mapreduce also gives an incorrect result along an axis:
    julia> mapreduce(identity, &, [3,3,3], dims = 1)
    1-element Vector{Bool}:
     1
    
    julia> mapreduce(identity, &, [3,3,3])
    3
    
  8. pfitzseb commented on Jun 3, 2022

    @pfitzseb
    Member

    The reducedim part of this bug is caused by

    julia/base/reducedim.jl

    Lines 206 to 207 in bd8dbc3

    reducedim_init(f, op::typeof(&), A::AbstractArrayOrBroadcasted, region) = reducedim_initarray(A, region, true)
    reducedim_init(f, op::typeof(|), A::AbstractArrayOrBroadcasted, region) = reducedim_initarray(A, region, false)

    which causes the returned array to be unconditionally initialized as a Array{Bool}, even though &/| aren't guaranteed to return booleans.

  9. LilithHafner commented on Jun 3, 2022

    @LilithHafner
    Member

    @GunnarFarneback, the problem you labeled 2 is at the intersection of this issue and #45566.
    The linked PR fixes 1 and turns 2 from a wrong answer into an error.

  10. GunnarFarneback commented on Jun 3, 2022

    @GunnarFarneback
    Contributor

    I assume you mean the opposite. It would be good if it added tests for both problems.

  11. LilithHafner commented on Jun 3, 2022

    @LilithHafner
    Member

    The expected result in 1 is an error, so by "fixes 1" I also mean "turn a wrong answer into an error", Another way I could have said it that might be more clear is "the linked PR turns both these cases into an error. However, 2 should not error. That should be fixed elsewhere".

    I added tests for 1, the issue in the OP. Because I did't solve 2, I also didn't add tests for it because those tests would be broken.

  12. GunnarFarneback commented on Jun 3, 2022

    @GunnarFarneback
    Contributor

    Thanks, I misunderstood. Turning a wrong answer into an error sounds a bit scary, even if both are incorrect.

  13. StefanKarpinski commented on Jun 3, 2022

    @StefanKarpinski
    SponsorMember

    Unless I'm missing something, the middle method of each of these is unnecessary. You can just do:

    boolean_and(a::Bool, b::Bool) = a & b
    boolean_or(a::Bool, b::Bool) = a | b
    boolean_and(a, b) = throw(TypeError(:boolean_and, Bool, a))
    boolean_or(a, b) = throw(TypeError(:boolean_or, Bool, a))

    Could generalize this to

    function boolean_op(f::Function)
        op(a, b) = throw(TypeError(nameof(f), Bool, a))
        op(a::Bool, b::Bool) = f(a, b)
    end

    Although that does produce annoyingly ugly stack traces on account of the inner function.

  14. pfitzseb commented on Jun 3, 2022

    @pfitzseb
    Member

    This is unrelated to correctness, but shouldn't all/any short-circuit? There really is no need to check the whole array after all (although you'll of course end up with a sort of ragged iteration scheme which could be slower in some cases, I guess).

  15. LilithHafner commented on Jun 3, 2022

    @LilithHafner
    Member

    Unless I'm missing something, the middle method of each of these is unnecessary. You can just do:

    I don't like this error:

    julia> boolean_and(true, 5)
    ERROR: TypeError: non-boolean (Bool) used in boolean context
    

    Could generalize this

    Yes, though I do err on the side of cleaner error messages.

    shouldn't all/any short-circuit?

    Thanks @pfitzseb! I expect tat this would better and obviate the whole issue. I'll look into it more.

  16. raminammour commented on Jun 3, 2022

    @raminammour
    Contributor

    This is unrelated to correctness, but shouldn't all/any short-circuit? There really is no need to check the whole array after all (although you'll of course end up with a sort of ragged iteration scheme which could be slower in some cases, I guess).

    I agree with the short-circuting behavior being more efficient. But then the check on an array for example needs to be done separately, depending on how the short-circuiting is achieved. false && :hello would return false right away. So will need to check that an array is an Array{Bool} before, otherwise bugs could be hidden again...

  17. LilithHafner commented on Jun 3, 2022

    @LilithHafner
    Member

    We already have all([false, :hello]) == false. I think that this is okay because it is explicitly documented to be short circuited. It can also sometimes be somewhat challenging to determine whether an input is actually all Bools (e.g. all(function, itr) may require executing the function on every element of itr).

  18. LilithHafner commented on Jun 4, 2022

    @LilithHafner
    Member

    I think that it is appropriate for all(f, [a,b,c,d]) to be the same as f(a) && f(b) && f(c) && f(d).

  19. StefanKarpinski commented on May 5, 2023

    @StefanKarpinski
    SponsorMember

    Bump, would be good to get a fix in for this.

  20. added
    correctness bug ⚠Bugs that are likely to lead to incorrect results in user code without throwing
    on Aug 7, 2023
  21. MilesCranmer commented on Jan 4, 2026

    @MilesCranmer
    SponsorMember

    This can be closed. (I don't have triage privileges)

    julia> all([3, 3, 3]; dims=1)
    ERROR: TypeError: non-boolean (Int64) used in boolean context
    Stacktrace:
      [1] and_all
        @ ./reduce.jl:32 [inlined]
  22. adienes commented on Jan 4, 2026

    @adienes
    Member

    I think to fix

    julia> mapreduce(identity, |, [1, 1, 1]; dims=1)
    1-element Vector{Bool}:
     1
    

    we still need #58418

  23. MilesCranmer commented on Jan 4, 2026

    @MilesCranmer
    SponsorMember

    Different issue, I think? all/any no longer go through that particular mapreduce path, so it solves the original issue, even if it didn't solve it by fixing mapreduce on |

    ERROR: TypeError: non-boolean (Int64) used in boolean context
    Stacktrace:
      [1] and_all
        @ ./reduce.jl:32 [inlined]
      [2] macro expansion
        @ ./reducedim.jl:281 [inlined]
      [3] macro expansion
        @ ./simdloop.jl:77 [inlined]
      [4] _mapreducedim!(f::typeof(identity), op::typeof(Base.and_all), R::Vector{Bool}, A::Vector{Int64})
        @ Base ./reducedim.jl:280
      [5] mapreducedim!
        @ ./reducedim.jl:297 [inlined]
      [6] _mapreduce_dim
        @ ./reducedim.jl:344 [inlined]
      [7] mapreduce
        @ ./reducedim.jl:330 [inlined]
      [8] _all
        @ ./reducedim.jl:1012 [inlined]
      [9] _all
        @ ./reducedim.jl:1011 [inlined]
     [10] kwcall(::@NamedTuple{dims::Int64}, ::typeof(all), a::Vector{Int64})
        @ Base ./reducedim.jl:995
     [11] top-level scope
        @ REPL[1]:1

    i.e., I think this issue by itself could be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIndicates an unexpected problem or unintended behaviorcorrectness bug ⚠Bugs that are likely to lead to incorrect results in user code without throwing

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions