Tuples of CartesianIndex and broadcasting

While I can broadcast scalars, like

julia> 1 .+ (2, 3)
(3, 4)

I can’t do the same with CartesianIndexs:

julia> CartesianIndex(1,1) .+ (CartesianIndex(2,2), CartesianIndex(3,3))
ERROR: iteration is deliberately unsupported for CartesianIndex. Use `I` rather than `I...`, or use `Tuple(I)...`
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] iterate(::CartesianIndex{2}) at ./multidimensional.jl:166
 [3] copyto!(::Array{Int64,1}, ::CartesianIndex{2}) at ./abstractarray.jl:733
 [4] _collect(::UnitRange{Int64}, ::CartesianIndex{2}, ::Base.HasEltype, ::Base.HasLength) at ./array.jl:630
 [5] collect(::CartesianIndex{2}) at ./array.jl:624
 [6] broadcastable(::CartesianIndex{2}) at ./broadcast.jl:682
 [7] broadcasted(::Function, ::CartesianIndex{2}, ::Tuple{CartesianIndex{2},CartesianIndex{2}}) at ./broadcast.jl:1260
 [8] top-level scope at REPL[15]:1

I understand that 1 .+ CartesianIndex(1,1) fails, but since CartesianIndex(1,1) + CartesianIndex(1,1) is fine, I would expect the above example to work. Is the behavior intended, or a bug?

(I can of course write

julia> (CartesianIndex(1,1),) .+ (CartesianIndex(2,2), CartesianIndex(3,3))
(CartesianIndex(3, 3), CartesianIndex(4, 4))

instead. But that’s less elegant… and beside the point.)

1 Like

I think the problem there is that there is a possible ambiguity on whether the broadcast is over the indexes of the tuple or the indexes of the CartesianIndex.

1 Like

Hmm… But since broadcasting over the indexes of the CartesianIndex is not allowed (1 .+ CartesianIndex(1,1) errors), I would assume that we are left with only one possibility, no?

1 Like

You can write Ref(CartesianIndex(1,1)) .+ stuff to protect it from the broadcast. (Similar to (... ,), as I see now you said.) I think the way to specify that a type is always scalar with respect to broadcasting is

Broadcast.broadcastable(I::CartesianIndex) = Ref(I)

I wonder if it would break anything to define this?

1 Like

We’ve been hesitant to make CartesianIndexes collection-like (that is, iterable and broadcastable) because their reason for being is the they represent a single location. Generally I’ll just convert them to Tuples when I want to treat them as a list of indices.

Maybe I’m misunderstanding, but I’m arguing for the opposite, namely to treat CartesianIndexes as “single locations”/scalars “all the way” (i.e. not collection-like). If that were the case, my example above would not fail…

Oh, you’re absolutely right! 90% of the requests we get for this are the other way, and I simply didn’t read closely enough.

Yes, I think making it broadcast like a scalar would be reasonable — and nonbreaking since it errors now.

5 Likes

Well, I’m a 100% with the 10% then :smile:

Should I file an issue?

Please do!

Done

5 Likes