Or even simpler (I think; untested):

```
ind2subv(shape, indices) = Tuple.(CartesianIndices(shape)[indices])
sub2indv(shape, indices) = LinearIndices(shape)[CartesianIndex.(indices)]
```

Or even simpler (I think; untested):

```
ind2subv(shape, indices) = Tuple.(CartesianIndices(shape)[indices])
sub2indv(shape, indices) = LinearIndices(shape)[CartesianIndex.(indices)]
```

4 Likes

Or even easier:

```
Base._ind2sub
Base._sub2ind
```

1 Like

But the point of `CartesianIndex`

etc. is that it’s *better* than the Matlab approach. Reimplementing Matlab functionality to avoid using idiomatic Julia seems counterproductive. What’s the gain?

7 Likes

I agree the Julia approach is better. My functions are not meant as a replacement, but instead as a lightweight wrapper around the Julia methods, to provide the functionality illustrated below:

ind2subv( (3,4,2), [3,5]) = [ (3, 1, 1), (1, 2, 1)]

subv2ind( (3,4,2), [(3,1,1), (1,2,1)]) = [3, 5]

This is useful for applications such as discrete graphical models. As I learn more Julia, maybe I’ll discover a more “Julianic” way to do it without using these wrappers. (BTW, I agree the function names are terrible, and are just meant to help me port my legacy matlab code, but I may change them to lin2cart and cart2lin or something like that.)

Yes, this works great!

I even wrote the “dual” of it in your style.

function cart2lin(shape, cindices)

lndx = LinearIndices(Dims(shape))

return getindex.(Ref(lndx), cindices)

end

However, it’s a pain to have to manually wrap tuples inside the CartesianIndex contructor.

I’d like to pass in a vector of tuples, as in cart2lin([3,4,2],[ (3,1,1), (2,2,1)]),

and have it convert my arguments automatically. I can do this as follows:

function tuple2lin(shape, indices)

lndx = LinearIndices(Dims(shape))

cindices = [CartesianIndex(i) for i in indices]

return getindex.(Ref(lndx), cindices)

end

but I just want to write one function, and depending on the type of the “indices” argument,

convertng from Array{Tuple} to Array{CartesianIndex} if necessary.

I tried writing 2 function signatures but it does not work (always calls the first one even if I pass

in an array of tuples). What am I missing?

function cart2lin(shape::Tuple, cindices::Array{CartesianIndex})…

function cart2lin(shape::Tuple, indices::Array{Tuple}) # == tuple2lin

Sorry, I think my last email was unclear. What I would like to do is to have Julia dispatch to either cart2lin or tuple2lin, depending on the type of the second argument. How do I do this?

function cart2lin(shape, cindices)

lndx = LinearIndices(Dims(shape))

return getindex.(Ref(lndx), cindices)

end

function tuple2lin(shape, indices)

cindices = [CartesianIndex(i) for i in indices]

return cart2lin(cindices)

end

function test()

shape = (3,4,2)

cndx = [CartesianIndex(3,1,1), CartesianIndex(2,2,1)]

lndx = cart2lin(shape, cndx)

@assert lndx == [3,5]

ndx = [(3,1,1), (2,2,1)]

lndx = tuple2lin(shape, ndx)

@assert lndx == [3,5]

shapes = [(3,4,2), (4,1,5,2)]

for shape in shapes

K = prod(shape)

subs = ind2subv(shape, 1:K)

ndx = subv2ind(shape, subs)

@assert ndx == 1:K

end

end

Do you think you can wrap your code in triple backticks and indent properly, like you did in your first post? This is really hard to read.

3 Likes

Because `CartesianIndex(cidx::CartesianIndex) = cidx`

(this is often true for wrapper types), @mbauman’s suggestion should do what you want for both tuples and `CartesianIndex`

es. I just tested this code:

```
ind2subv(shape, indices) = Tuple.(CartesianIndices(shape)[indices])
sub2indv(shape, indices) = LinearIndices(shape)[CartesianIndex.(indices)]
function test()
shape = (3, 4, 2)
cndx = [CartesianIndex(3, 1, 1), CartesianIndex(2, 2, 1)]
lndx = sub2indv(shape, cndx)
@assert lndx == [3, 5]
ndx = [(3, 1, 1), (2, 2, 1)]
lndx = sub2indv(shape, ndx)
@assert lndx == [3, 5]
shapes = [(3,4,2), (4,1,5,2)]
for shape in shapes
K = prod(shape)
subs = ind2subv(shape, 1:K)
ndx = sub2indv(shape, subs)
@assert ndx == 1:K
end
end
```

1 Like

You guys rock! Thanks a lot. I’m pasting the final solution below for posterity.

(Both versions work.)

```
"""
Transform linear indices to cartesian, cf ind2subv.
Example:
lin2cart((3,4,2), [3,5]) = [ (3, 1, 1), (2, 2, 1)]
"""
#https://discourse.julialang.org/t/psa-replacement-of-ind2sub-sub2ind-in-julia-0-7/14666/20
#return Tuple.(CartesianIndices(shape)[indices])
return [Base._ind2sub(shape, i) for i in indices]
end
function cart2lin(shape, indices)
"""
Transform vector of cartesian indices to linear indices, cf subv2ind.
Example:
cart2lin([3,4,2], [(3,1,1), (2,2,1)]) = [3, 5]
"""
#https://discourse.julialang.org/t/psa-replacement-of-ind2sub-sub2ind-in-julia-0-7/14666/20
#return LinearIndices(shape)[CartesianIndex.(indices)]
return [Base._sub2ind(shape, i...) for i in indices]
end
```

Whoops, first function got cut off. Sorry, trying one more time.

```
function lin2cart(shape, indices)
"""
Transform linear indices to cartesian, cf ind2subv.
Example:
lin2cart((3,4,2), [3,5]) = [ (3, 1, 1), (2, 2, 1)]
"""
#https://discourse.julialang.org/t/psa-replacement-of-ind2sub-sub2ind-in-julia-0-7/14666/20
#return Tuple.(CartesianIndices(shape)[indices])
return [Base._ind2sub(shape, i) for i in indices]
end
function cart2lin(shape, indices)
"""
Transform vector of cartesian indices to linear indices, cf subv2ind.
Example:
cart2lin([3,4,2], [(3,1,1), (2,2,1)]) = [3, 5]
"""
#https://discourse.julialang.org/t/psa-replacement-of-ind2sub-sub2ind-in-julia-0-7/14666/20
#return LinearIndices(shape)[CartesianIndex.(indices)]
return [Base._sub2ind(shape, i...) for i in indices]
end
```

1 Like

Glad you found a solution! Two recommendations:

I would not use the underscored functions (`_ind2sub`

and `_sub2ind`

) if I didn’t have to. They are internal functions that could have their functionality changed or be removed in a future version of Julia. They are also undocumented, so people reading your code can’t easily search for these functions to see what they’re doing.

You could also move the documentation to just above each function (not inside the function). That way they will be proper docstrings and available in Julia’s help mode. See details here.

4 Likes

It’s also possible to edit the post with the missing code.

Are you perhaps reading this through an email client? I ask because you seemed previously to reply to a remark that I had deleted, due to a misunderstanding, and referring to a post as an email. As far as I know quite a few posters do that, but it could occasionally lead to some confusion.

If you use the web-interface, you can fix typos, add code formatting (which is missing in some of your posts), and fix other mistakes without having to repost all the contents. You can also delete posts entirely.

2 Likes

I’ve read this very interesting blog post, and came away thoroughly impressed with the ease and power of `CartesianIndex`

/`ices`

. There is just one thing bothering me, and that is the behaviour of `max`

. According to the docs:

```
help?> max
[...]
Return the maximum of the arguments.
```

However,

```
julia> max(CartesianIndex(0, 1), CartesianIndex(1, 0))
CartesianIndex(1, 1)
```

doesn’t return the max of the two arguments (that is, return *either* the first *or* the second argument), but something in between. Compare this to

```
julia> max((0, 1), (1, 0))
(1, 0)
julia> max.((0, 1), (1, 0))
(1, 1)
```

Since “a CartesianIndex has to be viewed as a single (scalar) entity, rather than as a container in its own right”, I find this a bit hard to reconcile. `max`

of two index’es behaves more as if it’s been broadcasted over two containers.

Is `max`

the right name for this operation? Or should perhaps the docstring of `max`

be tweaked? Alternatively, how can I get a good intuition about how this works?

7 Likes

That’s worth opening an issue for dedicated discussion.

1 Like

Do you mean a github issue, or a separate thread on discourse?

A GitHub issue.