What is the proper way to construct CartesianIndices from CartesianIndex in Julia 1.0?

It is recommended to use unit ranges to construct CartesianIndices, but isn’t it better to use CartesianIndex all the way down? Transforming to UnitRange make the code not quite clean.

julia> c1 = CartesianIndex((2,2))                                                                     
CartesianIndex(2, 2)                                                                                                                                                         
                                                                                                      
julia> c2 = CartesianIndex((4,5))                                                                     
CartesianIndex(4, 5)                                                                                  
                                                                                                      
julia> CartesianIndices(c1,c2)                                                                        
┌ Warning: the internal representation of CartesianIndices has changed, use `CartesianIndices((2:4, 2:
5))` (or other more appropriate AbstractUnitRange type) instead.                                      
│   caller = top-level scope at none:0                                                                
└ @ Core none:0                                                                                       
3×4 CartesianIndices{2,Tuple{UnitRange{Int64},UnitRange{Int64}}}:                                     
 CartesianIndex(2, 2)  CartesianIndex(2, 3)  CartesianIndex(2, 4)  CartesianIndex(2, 5)               
 CartesianIndex(3, 2)  CartesianIndex(3, 3)  CartesianIndex(3, 4)  CartesianIndex(3, 5)               
 CartesianIndex(4, 2)  CartesianIndex(4, 3)  CartesianIndex(4, 4)  CartesianIndex(4, 5)               

isn’t it better to use CartesianIndex all the way down? Transforming to UnitRange make the code not quite clean.

Some things turn out to be easier with the range representation, and because ranges are sometimes used to encode the array type for unconventional indexing the new representation is the only truly correct approach because it allows you to preserve the type of ranges.

However, the lack of a good default for construction from CartesianIndex is an oversight that should be corrected (hopefully in julia 1.1, see Add colon constructor for CartesianIndices by timholy · Pull Request #29440 · JuliaLang/julia · GitHub). (See also PSA: replacement of ind2sub/sub2ind in Julia 0.7+ - #6 by mauro3). In the meantime I use this:

_colon(I::CartesianIndex{N}, J::CartesianIndex{N}) where N =
    CartesianIndices(map((i,j) -> i:j, Tuple(I), Tuple(J)))
1 Like

thanks for the explanation.
The transformation to Tuple has a little bit overhead and is unnecessary?

There’s no overhead: Tuple(I) just returns I.I (which is already a tuple):

julia> I = CartesianIndex(2,3)
CartesianIndex(2, 3)

julia> @code_lowered Tuple(I)
CodeInfo(                                                                                                                                                                                                                                                                      
95 1 ─ %1 = (Base.getproperty)(index, :I)                                                                                                                                                                                                                                  │   
   └──      return %1                                                                                                                                                                                                                                                      │   
)                                                                                                                                                                                                                                                                                                                                                                                   

In general we discourage direct field access when there’s a functional form that makes perfect sense, but either is fine.

and is unnecessary

Actually, it is necessary because CartesianIndex is deliberately not iterable. The original motivation for keeping them non-iterable was a performance problem due to limitations in inference (e.g., Add `Tuple(::CartesianIndex)` by martinholters · Pull Request #23719 · JuliaLang/julia · GitHub), but even if we fix that @mbauman has correctly pointed out that expressions like getindex.((A,), CartesianIndex((1,2)), [3,4,5]) would break if we made them iterable. So I think they’re going to stay non-iterable, and hence you need to convert them into tuples for use in map.

thanks, I see you created a PR.
https://github.com/JuliaLang/julia/pull/29440