Strange logical indexing behavior with 3-dimensional Array and BitMatrix

Hi,

A colleague of mine noticed something strange in the way that getindex and setindex! handle logical indexing with BitMatrix. Consider the following example:

import Random

X = ones(10,3,3) # Array{Float64,3}
mask = randn(Random.MersenneTwister(1234), 3, 3) .> 0.0 # BitMatrix
X[:,mask] .= 0 # should work...?
# ERROR: BoundsError: attempt to access 10×3×3 Array{Float64, 3} at index [1:10, 3×3 BitMatrix]
X[:,mask,:] .= 0 # works as expected... but why?

It seems intuitive that, since mask is a matrix matching the last two dimensions of X, it should automatically align with the last two axes, and thus the syntax which throws the error should be valid.

From a semantics point of view, I don’t see how the working syntax of including a : for the last axis makes any sense, since strictly speaking this operation is not being broadcasted over this axis, and the shape of the subarray returned by getindex will not necessarily match X along this axis.

Is this maybe a bug in the dispatch pattern for getindex and BitMatrix?

if possible I think you want to re-arrange the dimensions:

julia> X = rand(3,3, 10);

julia> X[mask, :] .= 0;

julia> X
3×3×10 Array{Float64, 3}:
[:, :, 1] =
 0.0       0.600695  0.0
 0.70837   0.0       0.68729
 0.209435  0.0       0.0

[:, :, 2] =
 0.0       0.660355  0.0
 0.434648  0.0       0.727985
 0.58141   0.0       0.0

I do think this is a bug in the bounds checking implementation.

julia> checkbounds(X, to_indices(X, (:, mask))...)
ERROR: BoundsError: attempt to access 10×3×3 Array{Float64, 3} at index [1:10, 3×3 BitMatrix]
Stacktrace:
 [1] throw_boundserror(A::Array{Float64, 3}, I::Tuple{Base.Slice{Base.OneTo{Int64}}, Base.LogicalIndex{Int64, BitMatrix}})
   @ Base ./abstractarray.jl:703
 [2] checkbounds(::Array{Float64, 3}, ::Base.Slice{Base.OneTo{Int64}}, ::Base.LogicalIndex{Int64, BitMatrix})
   @ Base ./abstractarray.jl:668
 [3] top-level scope
   @ REPL[7]:1

julia> checkbounds(X, to_indices(X, (:, mask, 1))...)

julia>

It looks like we’re missing a method override to handle the trailing-array-of-cartesian-index case.

2 Likes

For performance reasons? I think the indexing should behave consistently regardless of the ordering.