Getting original array index from its view with reduced dimensions

In this code:

a = reshape(collect(1:12), 4, 3)
v = view(a, 2, :)

for i in eachindex(v)
  # print corresponding index of array a
end

I would like to get corresponding original indices of array a, either linear or Cartesian, i.e.:

  2
  6
 10

or

(2, 1)
(2, 2)
(2, 3)

Is it possible?

Yes!

julia> parentindices(v)
(2, Base.Slice(Base.OneTo(3)))

The 2 is the first dimension (1-d slice) and the 1:3 is the second dimension.
You can do

julia> CartesianIndices(parentindices(v))
1×3 CartesianIndices{2,Tuple{UnitRange{Int64},UnitRange{Int64}}}:
 CartesianIndex(2, 1)  CartesianIndex(2, 2)  CartesianIndex(2, 3)

And of course you can convert these to linear indices if you want.

5 Likes

How do I do it in my code example?

a = reshape(collect(1:12), 4, 3)
v = view(a, 2, :)

for i in eachindex(v)
  println(i, " -> ", #= index of a =#)
end

I need both indexes: contiguous linear index of v and any type of index of a.

You can zip the two together if you want to stick to the exported api

for i_view, ci_orig in zip(eachindex(v), CartesianIndices(parentindices(v)))
  # ...
end

You can also take a more “under the hood” approach, keeping in mind that implementation details of these types aren’t guaranteed to remain the same (probably still a safe bet in this case)

for i in eachindex(v)
    i_orig = v.offset1 + v.stride1*i
    #...
end
2 Likes

@tomerarnon, sorry to revisit this but could not get the expected results.
Would it be possible to produce a MWE?

By using Iterators.product and splatting, the results requested by the OP can be obtained:

a = reshape(collect(1:12), 4, 3)

v = view(a, 2, :)   # view of 1 row
for i_view in Iterators.product(parentindices(v)...)
    println("parent index = $i_view")
end

# result:
parent index = (2, 1)
parent index = (2, 2)
parent index = (2, 3)

Seems like the behavior of CartesianIndices changed at some point.

Julia1.2:

julia> a = reshape(collect(1:12), 4, 3);

julia> v = view(a, 2, :);

julia> for (i_view, ci_orig) in zip(eachindex(v), CartesianIndices(parentindices(v)))
         @show i_view, ci_orig
       end
(i_view, ci_orig) = (1, CartesianIndex(2, 1))
(i_view, ci_orig) = (2, CartesianIndex(2, 2))
(i_view, ci_orig) = (3, CartesianIndex(2, 3))

Julia1.6:

julia> a = reshape(collect(1:12), 4, 3);

julia> v = view(a, 2, :);

julia> for (i_view, ci_orig) in zip(eachindex(v), CartesianIndices(parentindices(v)))
         @show i_view, ci_orig
       end
(i_view, ci_orig) = (1, CartesianIndex(1, 1))
(i_view, ci_orig) = (2, CartesianIndex(2, 1))
(i_view, ci_orig) = (3, CartesianIndex(1, 2))

The second method I gave still works, although ironically it was supposed to be the less stable one…

1 Like

Thanks for the feedback, the second method still works indeed (the input example is a bit unfortunate because the index numbers are equal to the matrix values)