Skip to content

Commit

Permalink
Store indices in CartesianRange
Browse files Browse the repository at this point in the history
The main advantage of this change is that you no longer lose type information from `CartesianRange(indices(A))`. The types of the specific AbstractUnitRanges are important for `similar`, etc.
  • Loading branch information
timholy committed Mar 10, 2017
1 parent e91065c commit 5cad699
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 63 deletions.
6 changes: 6 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1283,4 +1283,10 @@ end
# END 0.6 deprecations

# BEGIN 1.0 deprecations
function CartesianRange{N}(start::CartesianIndex{N}, stop::CartesianIndex{N})
inds = map((f,l)->f:l, start.I, stop.I)
depwarn("the internal representation of CartesianRange has changed, use CartesianRange($inds) (or other more approriate AbstractUnitRange type) instead.", :CartesianRange)
CartesianRange(inds)
end

# END 1.0 deprecations
119 changes: 70 additions & 49 deletions base/multidimensional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

### Multidimensional iterators
module IteratorsMD
import Base: eltype, length, size, start, done, next, last, in, getindex,
import Base: eltype, length, size, start, done, next, first, last, in, getindex,
setindex!, IndexStyle, min, max, zero, one, isless, eachindex,
ndims, iteratorsize, convert

Expand Down Expand Up @@ -94,7 +94,6 @@ module IteratorsMD

# Iteration
"""
CartesianRange(Istart::CartesianIndex, Istop::CartesianIndex) -> R
CartesianRange(sz::Dims) -> R
CartesianRange(istart:istop, jstart:jstop, ...) -> R
Expand All @@ -112,28 +111,48 @@ module IteratorsMD
Consequently these can be useful for writing algorithms that
work in arbitrary dimensions.
"""
struct CartesianRange{I<:CartesianIndex}
start::I
stop::I
struct CartesianRange{N,R<:NTuple{N,AbstractUnitRange{Int}}}
indices::R
end

CartesianRange(index::CartesianIndex) = CartesianRange(one(index), index)
CartesianRange(::Tuple{}) = CartesianRange{CartesianIndex{0}}(CartesianIndex{0}(()),CartesianIndex{0}(()))
CartesianRange{N}(sz::NTuple{N,Int}) = CartesianRange(CartesianIndex(sz))
CartesianRange{N}(rngs::NTuple{N,Union{Integer,AbstractUnitRange}}) =
CartesianRange(CartesianIndex(map(first, rngs)), CartesianIndex(map(last, rngs)))

convert{N}(::Type{NTuple{N,UnitRange{Int}}}, R::CartesianRange{CartesianIndex{N}}) =
map((f,l)->f:l, first(R).I, last(R).I)
CartesianRange(::Tuple{}) = CartesianRange{0,typeof(())}(())
CartesianRange{N}(inds::NTuple{N,AbstractUnitRange{Int}}) =
CartesianRange{N,typeof(inds)}(inds)
CartesianRange{N}(inds::Vararg{AbstractUnitRange{Int},N}) =
CartesianRange(inds)
CartesianRange{N}(inds::NTuple{N,AbstractUnitRange{<:Integer}}) =
CartesianRange(ur_int.(inds))
CartesianRange{N}(inds::Vararg{AbstractUnitRange{<:Integer},N}) =
CartesianRange(ur_int.(inds))
ur_int(r::AbstractUnitRange{Int}) = r
# This should be specialized for other AbstractUnitRanges
ur_int(r::UnitRange{<:Integer}) = convert(UnitRange{Int}, r)
ur_int(r::Base.OneTo{<:Integer}) = convert(Base.OneTo{Int}, r)

CartesianRange(index::CartesianIndex) = CartesianRange(index.I)
CartesianRange{N}(sz::NTuple{N,<:Integer}) = CartesianRange(map(Base.OneTo, sz))
CartesianRange{N}(inds::NTuple{N,Union{<:Integer,AbstractUnitRange{<:Integer}}}) =
CartesianRange(map(i->first(i):last(i), inds))

convert{N}(::Type{NTuple{N,AbstractUnitRange{Int}}}, R::CartesianRange{N}) =
R.indices
convert{N}(::Type{NTuple{N,AbstractUnitRange}}, R::CartesianRange) =
convert(NTuple{N,AbstractUnitRange{Int}}, R)
convert{N}(::Type{NTuple{N,UnitRange{Int}}}, R::CartesianRange) =
UnitRange{Int}.(convert(NTuple{N,AbstractUnitRange}, R))
convert{N}(::Type{NTuple{N,UnitRange}}, R::CartesianRange) =
convert(NTuple{N,UnitRange{Int}}, R)
convert{N}(::Type{Tuple{Vararg{UnitRange{Int}}}}, R::CartesianRange{CartesianIndex{N}}) =
UnitRange.(convert(NTuple{N,AbstractUnitRange}, R))
convert{N}(::Type{Tuple{Vararg{AbstractUnitRange{Int}}}}, R::CartesianRange{N}) =
convert(NTuple{N,AbstractUnitRange{Int}}, R)
convert(::Type{Tuple{Vararg{AbstractUnitRange}}}, R::CartesianRange) =
convert(Tuple{Vararg{AbstractUnitRange{Int}}}, R)
convert{N}(::Type{Tuple{Vararg{UnitRange{Int}}}}, R::CartesianRange{N}) =
convert(NTuple{N,UnitRange{Int}}, R)
convert(::Type{Tuple{Vararg{UnitRange}}}, R::CartesianRange) =
convert(Tuple{Vararg{UnitRange{Int}}}, R)

ndims(R::CartesianRange) = length(R.start)
ndims{I<:CartesianIndex}(::Type{CartesianRange{I}}) = length(I)
ndims(R::CartesianRange) = ndims(typeof(R))
ndims{N}(::Type{CartesianRange{N}}) = N
ndims{N,TT}(::Type{CartesianRange{N,TT}}) = N

eachindex(::IndexCartesian, A::AbstractArray) = CartesianRange(indices(A))

Expand All @@ -146,17 +165,20 @@ module IteratorsMD
@inline maxt(a::Tuple, b::Tuple{}) = a
@inline maxt(a::Tuple, b::Tuple) = (max(a[1], b[1]), maxt(tail(a), tail(b))...)

eltype{I}(::Type{CartesianRange{I}}) = I
eltype(R::CartesianRange) = eltype(typeof(R))
eltype{N}(::Type{CartesianRange{N}}) = CartesianIndex{N}
eltype{N,TT}(::Type{CartesianRange{N,TT}}) = CartesianIndex{N}
iteratorsize(::Type{<:CartesianRange}) = Base.HasShape()

@inline function start(iter::CartesianRange{<:CartesianIndex})
if any(map(>, iter.start.I, iter.stop.I))
return iter.stop+1
@inline function start(iter::CartesianRange)
iterfirst, iterlast = first(iter), last(iter)
if any(map(>, iterfirst.I, iterlast.I))
return iterlast+1
end
iter.start
iterfirst
end
@inline function next{I<:CartesianIndex}(iter::CartesianRange{I}, state)
state, I(inc(state.I, iter.start.I, iter.stop.I))
@inline function next(iter::CartesianRange, state)
state, CartesianIndex(inc(state.I, first(iter).I, last(iter).I))
end
# increment & carry
@inline inc(::Tuple{}, ::Tuple{}, ::Tuple{}) = ()
Expand All @@ -168,39 +190,38 @@ module IteratorsMD
newtail = inc(tail(state), tail(start), tail(stop))
(start[1], newtail...)
end
@inline done(iter::CartesianRange{<:CartesianIndex}, state) = state.I[end] > iter.stop.I[end]
@inline done(iter::CartesianRange, state) = state.I[end] > last(iter.indices[end])

# 0-d cartesian ranges are special-cased to iterate once and only once
start(iter::CartesianRange{<:CartesianIndex{0}}) = false
next(iter::CartesianRange{<:CartesianIndex{0}}, state) = iter.start, true
done(iter::CartesianRange{<:CartesianIndex{0}}, state) = state
start(iter::CartesianRange{0}) = false
next(iter::CartesianRange{0}, state) = CartesianIndex(), true
done(iter::CartesianRange{0}, state) = state

size(iter::CartesianRange{<:CartesianIndex}) = map(dimlength, iter.start.I, iter.stop.I)
size(iter::CartesianRange) = map(dimlength, first(iter).I, last(iter).I)
dimlength(start, stop) = stop-start+1

length(iter::CartesianRange) = prod(size(iter))

last(iter::CartesianRange) = iter.stop
first(iter::CartesianRange) = CartesianIndex(map(first, iter.indices))
last(iter::CartesianRange) = CartesianIndex(map(last, iter.indices))

@inline function in{I<:CartesianIndex}(i::I, r::CartesianRange{I})
_in(true, i.I, r.start.I, r.stop.I)
@inline function in{N}(i::CartesianIndex{N}, r::CartesianRange{N})
_in(true, i.I, first(r).I, last(r).I)
end
_in(b, ::Tuple{}, ::Tuple{}, ::Tuple{}) = b
@inline _in(b, i, start, stop) = _in(b & (start[1] <= i[1] <= stop[1]), tail(i), tail(start), tail(stop))

simd_outer_range(iter::CartesianRange{CartesianIndex{0}}) = iter
simd_outer_range(iter::CartesianRange{0}) = iter
function simd_outer_range(iter::CartesianRange)
start = CartesianIndex(tail(iter.start.I))
stop = CartesianIndex(tail(iter.stop.I))
CartesianRange(start, stop)
CartesianRange(tail(iter.indices))
end

simd_inner_length(iter::CartesianRange{<:CartesianIndex{0}}, ::CartesianIndex) = 1
simd_inner_length(iter::CartesianRange, I::CartesianIndex) = iter.stop[1]-iter.start[1]+1
simd_inner_length(iter::CartesianRange{0}, ::CartesianIndex) = 1
simd_inner_length(iter::CartesianRange, I::CartesianIndex) = length(iter.indices[1])

simd_index(iter::CartesianRange{<:CartesianIndex{0}}, ::CartesianIndex, I1::Int) = iter.start
simd_index(iter::CartesianRange{0}, ::CartesianIndex, I1::Int) = first(iter)
@inline function simd_index(iter::CartesianRange, Ilast::CartesianIndex, I1::Int)
CartesianIndex((I1+iter.start[1], Ilast.I...))
CartesianIndex((I1+first(iter.indices[1]), Ilast.I...))
end

# Split out the first N elements of a tuple
Expand Down Expand Up @@ -808,23 +829,23 @@ function copy!{T,N}(dest::AbstractArray{T,N}, src::AbstractArray{T,N})
end

@generated function copy!{T1,T2,N}(dest::AbstractArray{T1,N},
Rdest::CartesianRange{CartesianIndex{N}},
Rdest::CartesianRange{N},
src::AbstractArray{T2,N},
Rsrc::CartesianRange{CartesianIndex{N}})
Rsrc::CartesianRange{N})
quote
isempty(Rdest) && return dest
if size(Rdest) != size(Rsrc)
throw(ArgumentError("source and destination must have same size (got $(size(Rsrc)) and $(size(Rdest)))"))
end
@boundscheck checkbounds(dest, Rdest.start)
@boundscheck checkbounds(dest, Rdest.stop)
@boundscheck checkbounds(src, Rsrc.start)
@boundscheck checkbounds(src, Rsrc.stop)
ΔI = Rdest.start - Rsrc.start
@boundscheck checkbounds(dest, first(Rdest))
@boundscheck checkbounds(dest, last(Rdest))
@boundscheck checkbounds(src, first(Rsrc))
@boundscheck checkbounds(src, last(Rsrc))
ΔI = first(Rdest) - first(Rsrc)
# TODO: restore when #9080 is fixed
# for I in Rsrc
# @inbounds dest[I+ΔI] = src[I]
@nloops $N i (n->Rsrc.start[n]:Rsrc.stop[n]) begin
@nloops $N i (n->Rsrc.indices[n]) begin
@inbounds @nref($N,dest,n->i_n+ΔI[n]) = @nref($N,src,i)
end
dest
Expand Down
2 changes: 1 addition & 1 deletion base/permuteddimsarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ function _copy!{T,N,perm}(P::PermutedDimsArray{T,N,perm}, src)
return P
end

@noinline function _permutedims!(P::PermutedDimsArray, src, R1::CartesianRange{CartesianIndex{0}}, R2, R3, ds, dp)
@noinline function _permutedims!(P::PermutedDimsArray, src, R1::CartesianRange{0}, R2, R3, ds, dp)
ip, is = indices(src, dp), indices(src, ds)
for jo in first(ip):8:last(ip), io in first(is):8:last(is)
for I3 in R3, I2 in R2
Expand Down
7 changes: 4 additions & 3 deletions test/arrayops.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1489,9 +1489,10 @@ end
@test a[1,2] == 7
@test 2*CartesianIndex{3}(1,2,3) == CartesianIndex{3}(2,4,6)

R = CartesianRange(CartesianIndex{2}(2,3),CartesianIndex{2}(5,5))
R = CartesianRange(2:5, 3:5)
@test eltype(R) <: CartesianIndex{2}
@test eltype(typeof(R)) <: CartesianIndex{2}
@test eltype(CartesianRange{2}) <: CartesianIndex{2}
indexes = collect(R)
@test indexes[1] == CartesianIndex{2}(2,3)
@test indexes[2] == CartesianIndex{2}(3,3)
Expand All @@ -1513,8 +1514,8 @@ end
@test @inferred(convert(NTuple{2,UnitRange}, R)) === (2:5, 3:5)
@test @inferred(convert(Tuple{Vararg{UnitRange}}, R)) === (2:5, 3:5)

@test CartesianRange((3:5,-7:7)) == CartesianRange(CartesianIndex{2}(3,-7),CartesianIndex{2}(5,7))
@test CartesianRange((3,-7:7)) == CartesianRange(CartesianIndex{2}(3,-7),CartesianIndex{2}(3,7))
@test CartesianRange((3:5,-7:7)) == CartesianRange(3:5,-7:7)
@test CartesianRange((3,-7:7)) == CartesianRange(3:3,-7:7)
end

@testset "itr, start, done, next" begin
Expand Down
15 changes: 5 additions & 10 deletions test/simdloop.jl
Original file line number Diff line number Diff line change
Expand Up @@ -101,28 +101,23 @@ function simd_cartesian_range!(indexes, crng)
indexes
end

crng = CartesianRange(CartesianIndex{4}(2,0,1,3),
CartesianIndex{4}(4,1,1,5))
crng = CartesianRange(2:4, 0:1, 1:1, 3:5)
indexes = simd_cartesian_range!(Array{eltype(crng)}(0), crng)
@test indexes == vec(collect(crng))

crng = CartesianRange(CartesianIndex{2}(-1,1),
CartesianIndex{2}(1,3))
crng = CartesianRange(-1:1, 1:3)
indexes = simd_cartesian_range!(Array{eltype(crng)}(0), crng)
@test indexes == vec(collect(crng))

crng = CartesianRange(CartesianIndex{2}(-1,1),
CartesianIndex{2}(-1,3))
crng = CartesianRange(-1:-1, 1:3)
indexes = simd_cartesian_range!(Array{eltype(crng)}(0), crng)
@test indexes == vec(collect(crng))

crng = CartesianRange(CartesianIndex{1}(2),
CartesianIndex{1}(4))
crng = CartesianRange(2:4)
indexes = simd_cartesian_range!(Array{eltype(crng)}(0), crng)
@test indexes == collect(crng)

crng = CartesianRange(CartesianIndex{0}(),
CartesianIndex{0}())
crng = CartesianRange()
indexes = simd_cartesian_range!(Array{eltype(crng)}(0), crng)
@test indexes == vec(collect(crng))

Expand Down

0 comments on commit 5cad699

Please sign in to comment.