Mapping Linear Indices to Cartesian Indices

Hello,

I’m working with matrices in such a way that I want to be able to access values by both the Cartesian and linear Indices. I want to be able to map any given linear index to its Cartesian counterpart and vice versa.

This is the best I have so far:

> a = rand(3,3)

3×3 Matrix{Float64}:
 0.806  0.512  0.657
 0.278  0.313  0.266
 0.793  0.707  0.720

> for (index, value) in enumerate(CartesianIndices(a))
       println(index, ", ", Tuple(value), ", ", a[index])
  end

1, (1, 1), 0.806
2, (2, 1), 0.278
3, (3, 1), 0.793
4, (1, 2), 0.512
5, (2, 2), 0.313
6, (3, 2), 0.707
7, (1, 3), 0.657
8, (2, 3), 0.266
9, (3, 3), 0.720

The loop iterates over the matrix using its Cartesian indices, and enumerate applies a label to each element which happens to correspond to the linear index. I can save this map as an array of tuples, which would work, but it seems really clumsy. And I’m sure there is a built-in function that does what I need, I just haven’t been able to google exactly the right phrase yet.

Thanks in advance

Both LinearIndices(a) and CartesianIndices(a) are array-like objects, which can be indexed in either way. Is this what you want?

julia> CartesianIndices(rand(3,3))[6]
CartesianIndex(3, 2)

julia> LinearIndices(rand(3,3))[CartesianIndex(3,2)]
6

You can also map or broadcast over these things, e.g. broadcast(println, LinearIndices(rand(3,3)), " -> ", CartesianIndices(rand(3,3)), " -> ", rand(3,3));

1 Like

Thanks for your reply.

I’m looking for a function that will take in one type of index and output the other for a given matrix. Something like Cart2Linear(a, (2,2)), which would output 5 for a 3x3 matrix a There would also be an inverse function Linear2Cart() which does the opposite.

Looking at your note, LinearIndices() does (part of) what I need:

> a = rand(3,3)

3×3 Matrix{Float64}:
 0.806  0.512  0.657
 0.278  0.313  0.266
 0.793  0.707  0.720

> a_linear = LinearIndices(a)

3×3 LinearIndices{2, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}}:
 1  4  7
 2  5  8
 3  6  9

simply calling a_linear[i,j] gives me the linear index corresponding to Cartesian index (i, j), which is exactly what want. The inverse doesn’t seem to work exactly the same way, though:

> CartesianIndices(a)
CartesianIndices((3, 3))

I was expecting a 3x3 matrix of tuples, where each tuple is the the Cartesian index of that element.

If necessary, it wouldn’t be difficult to write a function which computes the Cartesian index for any linear index given the dimensions of the matrix. But I expected that this functionality would already exist.

We should probably make LinearIndices and CartesianIndices print more similarly.

Yeah, that’s what it is — well, it’s a matrix of CartesianIndexes that hold the tuples — but it just doesn’t print like it.

julia> collect(CartesianIndices((3, 3)))
3×3 Matrix{CartesianIndex{2}}:
 CartesianIndex(1, 1)  CartesianIndex(1, 2)  CartesianIndex(1, 3)
 CartesianIndex(2, 1)  CartesianIndex(2, 2)  CartesianIndex(2, 3)
 CartesianIndex(3, 1)  CartesianIndex(3, 2)  CartesianIndex(3, 3)

Oh, okay. That makes sense.

So for a given matrix a, we can convert a linear index n to a Cartesian index using:
CartesianIndices(a)[n]
Inversely, we can convert a Cartesian index (i,j) to a linear index using:
LinearIndices(a)[2,2]

This is exactly what I needed. One remaining question - do the above methods allocate a new matrix every time? That would be problematic.

Nope, just like the range 1:9, they are lazy computational objects that just behave like an Array. You can index into them and even do maths on them, but they won’t take up any space unless you explicitly collect them like I did above. They’re often completely removed from the actual compiled code.

2 Likes