Skip to content

Commit

Permalink
treat CartesianIndex as a broadcast scalar
Browse files Browse the repository at this point in the history
  • Loading branch information
stevengj committed Jan 27, 2023
1 parent 87f8958 commit 5a301fc
Show file tree
Hide file tree
Showing 5 changed files with 18 additions and 3 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ New library features
* The `initialized=true` keyword assignment for `sortperm!` and `partialsortperm!`
is now a no-op ([#47979]). It previously exposed unsafe behavior ([#47977]).
* `binomial(x, k)` now supports non-integer `x` ([#48124]).
* A `CartesianIndex` is now treated as a "scalar" for broadcasting ([#47044]).

Standard library changes
------------------------
Expand Down
2 changes: 1 addition & 1 deletion base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,7 @@ julia> Broadcast.broadcastable("hello") # Strings break convention of matching i
Base.RefValue{String}("hello")
```
"""
broadcastable(x::Union{Symbol,AbstractString,Function,UndefInitializer,Nothing,RoundingMode,Missing,Val,Ptr,AbstractPattern,Pair,IO}) = Ref(x)
broadcastable(x::Union{Symbol,AbstractString,Function,UndefInitializer,Nothing,RoundingMode,Missing,Val,Ptr,AbstractPattern,Pair,IO,CartesianIndex}) = Ref(x)
broadcastable(::Type{T}) where {T} = Ref{Type{T}}(T)
broadcastable(x::Union{AbstractArray,Number,AbstractChar,Ref,Tuple,Broadcasted}) = x
# Default to collecting iterables — which will error for non-iterables
Expand Down
9 changes: 9 additions & 0 deletions base/multidimensional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ module IteratorsMD
A `CartesianIndex` is sometimes produced by [`eachindex`](@ref), and
always when iterating with an explicit [`CartesianIndices`](@ref).
An `I::CartesianIndex` is treated as a "scalar" (not a container)
for `broadcast`. In order to iterate over the components of a
`CartesianIndex`, convert it to a tuple with `Tuple(I)`.
# Examples
```jldoctest
julia> A = reshape(Vector(1:16), (2, 2, 2, 2))
Expand Down Expand Up @@ -61,6 +65,11 @@ module IteratorsMD
julia> A[CartesianIndex((1, 1, 2, 1))]
5
```
!!! compat "Julia 1.9"
Using a `CartesianIndex` as a "scalar" for `broadcast` requires
Julia 1.9; in previous releases, use `Ref(I)`.
"""
struct CartesianIndex{N} <: AbstractCartesianIndex{N}
I::NTuple{N,Int}
Expand Down
4 changes: 2 additions & 2 deletions test/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -993,8 +993,8 @@ end
end

i = CartesianIndex(17,-2)
@test CR .+ i === i .+ CR === CartesianIndices((19:21, -1:3))
@test CR .- i === CartesianIndices((-15:-13, 3:7))
@test CR .+ i === i .+ CR === CartesianIndices((19:21, -1:3)) == collect(CR .+ i)
@test CR .- i === CartesianIndices((-15:-13, 3:7)) == collect(CR) .- i
@test collect(i .- CR) == Ref(i) .- collect(CR)
end

Expand Down
5 changes: 5 additions & 0 deletions test/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,11 @@ end
@test count(A) == count(>=(0), pos)
end

@testset "issue #38432: make CartesianIndex a broadcast scalar" begin
@test CartesianIndex(1,2) .+ (CartesianIndex(3,4), CartesianIndex(5,6)) == (CartesianIndex(4, 6), CartesianIndex(6, 8))
@test CartesianIndex(1,2) .+ [CartesianIndex(3,4), CartesianIndex(5,6)] == [CartesianIndex(4, 6), CartesianIndex(6, 8)]
end

# test that `Broadcast` definition is defined as total and eligible for concrete evaluation
import Base.Broadcast: BroadcastStyle, DefaultArrayStyle
@test Base.infer_effects(BroadcastStyle, (DefaultArrayStyle{1},DefaultArrayStyle{2},)) |>
Expand Down

0 comments on commit 5a301fc

Please sign in to comment.