# Elegant way to do multi-index algebra

**URL:** <https://discourse.julialang.org/t/elegant-way-to-do-multi-index-algebra/94370>\
**Category:** General Usage\
**Tags:** question\
**Created:** [February 9, 2023, 7:11pm UTC](https://discourse.julialang.org/t/elegant-way-to-do-multi-index-algebra/94370 "2023-02-09T19:11:46Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![linwaytin](https://avatars.discourse-cdn.com/v4/letter/l/898d66/32.png) [@linwaytin](https://discourse.julialang.org/u/linwaytin)\
**Post date:** [February 9, 2023, 7:11pm UTC](https://discourse.julialang.org/t/elegant-way-to-do-multi-index-algebra/94370/1 "2023-02-09T19:11:46Z")

</div>

I want to use a number to represent a multi-index. Consider `i` in `1:3` and `j` in `1:3`. I want `3` to represent for `(0,3)`, `4` for `(1,1)`, `8` for `(3,2)` and so forth.

This can be done by `i = (num-1)÷3 + 1` and `j = (num-1)%3 + 1`. This seems not quite elegant. Is there a better way to do this job? This is the first time I think 0-based indexing is superior…

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [February 9, 2023, 7:27pm UTC](https://discourse.julialang.org/t/elegant-way-to-do-multi-index-algebra/94370/2 "2023-02-09T19:27:11Z")

</div>

> [@linwaytin](#):
>
> I want to use a number to represent a multi-index. Consider `i` in `1:3` and `j` in `1:3`. I want `3` to represent for `(0,3)`, `4` for `(1,1)`, `8` for `(3,2)` and so forth.

Julia already supports [linear indexing of multidimensional arrays](https://docs.julialang.org/en/v1/manual/arrays/#Linear-indexing).

Note that the `i = (num-1)÷3 + 1 and j = (num-1)%3 + 1` formula you gave is [row-major](https://en.wikipedia.org/wiki/Row-_and_column-major_order) but Julia is column-major. If you really care about this you can just reverse the index order or transpose the array.

```julia
julia> A = [(i,j) for i=1:3, j=1:3]
3×3 Matrix{Tuple{Int64, Int64}}:
 (1, 1) (1, 2) (1, 3)
 (2, 1) (2, 2) (2, 3)
 (3, 1) (3, 2) (3, 3)

julia> A[3], A[4], A[8]
((3, 1), (1, 2), (2, 3))

```

You can get both the `(i,j) -> n` and `n -> (i,j)` maps without allocation (as array-like objects that compute elements on the fly) using [`LinearIndices`](https://docs.julialang.org/en/v1/base/arrays/#Base.LinearIndices) and [`CartesianIndices`](https://docs.julialang.org/en/v1/base/arrays/#Base.IteratorsMD.CartesianIndices), respectively:

```julia
julia> C = CartesianIndices((3, 3)) # indices of a 3x3 array
CartesianIndices((3, 3))

julia> collect(C) # collect and display all the elements explicitly
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)

julia> C[3], C[4], C[8]
(CartesianIndex(3, 1), CartesianIndex(1, 2), CartesianIndex(2, 3))

julia> L = LinearIndices(C) # alternatively: L = LinearIndices((3, 3))
3×3 LinearIndices{2, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}}:
 1 4 7
 2 5 8
 3 6 9

julia> L[3,1], L[1,2], L[2,3]
(3, 4, 8)

```

---

<div class="post-metadata">

**Author:** ![linwaytin](https://avatars.discourse-cdn.com/v4/letter/l/898d66/32.png) [@linwaytin](https://discourse.julialang.org/u/linwaytin)\
**Post date:** [February 9, 2023, 7:35pm UTC](https://discourse.julialang.org/t/elegant-way-to-do-multi-index-algebra/94370/3 "2023-02-09T19:35:32Z")

</div>

Thanks for your answer. I know `julia` has linear indexing for multidimensional arrays, but I’m working on a custom type.

The trick using an index matrix is interesting, but the size of my type is not fixed. That means I need to generate a new matrix when the sized is changed. I will think about how to deal with this.

Thank you!

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [February 9, 2023, 7:37pm UTC](https://discourse.julialang.org/t/elegant-way-to-do-multi-index-algebra/94370/4 "2023-02-09T19:37:35Z")

</div>

> [@linwaytin](#):
>
> The trick using an index matrix is interesting, but the size of my type is not fixed. That means I need to generate a new matrix when the sized is changed. I will think about how to deal with this.

`CartesianIndices` and `LinearIndices` _aren’t “index matrices” and they don’t need to be “generated”_ — creating them is essentially free, because they really just store the size of the array:

```julia
julia> dump(L)
LinearIndices{2, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}}
  indices: Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}
    1: Base.OneTo{Int64}
      stop: Int64 3
    2: Base.OneTo{Int64}
      stop: Int64 3

julia> dump(C)
CartesianIndices{2, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}}
  indices: Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}
    1: Base.OneTo{Int64}
      stop: Int64 3
    2: Base.OneTo{Int64}
      stop: Int64 3

```

and the indices are calculated only when you do `L[i,j]` or `C[i]`. Internally, they are doing calculations much like the one you suggested.

They are array-like objects, similar to ranges `1:n`, that _act_ like arrays but don’t _store_ their elements.

---

<div class="post-metadata">

**Author:** ![linwaytin](https://avatars.discourse-cdn.com/v4/letter/l/898d66/32.png) [@linwaytin](https://discourse.julialang.org/u/linwaytin)\
**Post date:** [February 10, 2023, 2:34pm UTC](https://discourse.julialang.org/t/elegant-way-to-do-multi-index-algebra/94370/5 "2023-02-10T14:34:59Z")

</div>

Thank you for the explanation!
