Assignment of values to a subset of a subset of indices of an array

Why does the following give different results?

a = zeros(Float32, 10)
b = rand(Float32,10)
ind1 = [3,4,6,9]
ind2 = [2,3,4]
julia> a[ind1[ind2]] .= mean(b[ind1[ind2]])
3-element view(::Vector{Float32}, [4, 6, 9]) with eltype Float32:
 -0.45284092
 -0.45284092
 -0.45284092

julia> a'
1×10 adjoint(::Vector{Float32}) with eltype Float32:
 0.0  0.0  0.0  -0.452841  0.0  -0.452841  0.0  0.0  -0.452841  0.0

versus:

julia> a[ind1][ind2] .= mean(b[ind1][ind2])
3-element view(::Vector{Float32}, [2, 3, 4]) with eltype Float32:
 -0.45284092
 -0.45284092
 -0.45284092

julia> a'
1×10 adjoint(::Vector{Float32}) with eltype Float32:
 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0

In other words, why can we index like b[ind1][ind2] to get a correct series of values (using Int64 indices, not BitVectors) but a value cannot be written to a[ind1][ind2]? Should it not result in a warning if such assignment is done incorrectly?

The problem is that:

  • a[ind1[ind2]] first allocates a temporary array _temp1 = ind1[ind2], and then an array a[_temp1], so you’re mutating a, whereas
  • a[ind1][ind2] first creates a temporary array _temp2 = a[ind1] and then another array _temp2[ind2], so you’re not mutating a, you’re mutating _temp2.
Meta.@lower MWE
julia> begin
           a = 1:10
           b = 1:10
           ind1 = [3,4,6,9]
           ind2 = [2,3,4]
       end;

julia> Meta.@lower a[ind1[ind2]]
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1 ─ %1 = a
│   %2 = Base.getindex(ind1, ind2)
│   %3 = Base.getindex(%1, %2)
└──      return %3
))))

julia> Meta.@lower b[ind1][ind2]
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1 ─ %1 = Base.getindex(b, ind1)
│   %2 = Base.getindex(%1, ind2)
└──      return %2
))))

Side note: When trying to debug these things it’s useful to simplify your working example. e.g. not using rand and mean, just presenting the indexing problem.

Thanks for the clear explanation and the Meta.@lower example. I can see now that _temp2 has no notion of the location in a anymore, just the values. It all makes sense.

You may want to mark @savq’s answer as the solution.