Skip to content

Commit

Permalink
Deprecate linearindices in favor of LinearIndices
Browse files Browse the repository at this point in the history
LinearIndices is strictly more powerful than linearindices: these two functions
return arrays holding the same elements, but the former also preserves the shape
and indices of the original array.

Also improve docstrings.
  • Loading branch information
nalimilan committed Apr 12, 2018
1 parent 18ac6e6 commit 786e282
Show file tree
Hide file tree
Showing 27 changed files with 194 additions and 180 deletions.
9 changes: 6 additions & 3 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -624,8 +624,10 @@ Library improvements
other containers, by taking the multiplicity of the arguments into account.
Use `unique` to get the old behavior.

* The type `LinearIndices` has been added, providing conversion from
cartesian indices to linear indices using the normal indexing operation. ([#24715])
* The `linearindices` function has been deprecated in favor of the new
`LinearIndices` type, which additionnally provides conversion from
cartesian indices to linear indices using the normal indexing operation.
([#24715], [#26775]).

* `IdDict{K,V}` replaces `ObjectIdDict`. It has type parameters
like other `AbstractDict` subtypes and its constructors mirror the
Expand Down Expand Up @@ -1446,4 +1448,5 @@ Command-line option changes
[#26436]: https://github.com/JuliaLang/julia/issues/26436
[#26442]: https://github.com/JuliaLang/julia/issues/26442
[#26600]: https://github.com/JuliaLang/julia/issues/26600
[#26670]: https://github.com/JuliaLang/julia/issues/26670
[#26670]: https://github.com/JuliaLang/julia/issues/26670
[#26775]: https://github.com/JuliaLang/julia/issues/26775
101 changes: 18 additions & 83 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -96,33 +96,8 @@ indices1(iter) = OneTo(length(iter))
unsafe_indices(A) = axes(A)
unsafe_indices(r::AbstractRange) = (OneTo(unsafe_length(r)),) # Ranges use checked_sub for size

"""
linearindices(A)
Return a `UnitRange` specifying the valid range of indices for `A[i]`
where `i` is an `Int`. For arrays with conventional indexing (indices
start at 1), or any multidimensional array, this is `1:length(A)`;
however, for one-dimensional arrays with unconventional indices, this
is `axes(A, 1)`.
Calling this function is the "safe" way to write algorithms that
exploit linear indexing.
# Examples
```jldoctest
julia> A = fill(1, (5,6,7));
julia> b = linearindices(A);
julia> extrema(b)
(1, 210)
```
"""
linearindices(A::AbstractArray) = (@_inline_meta; OneTo(_length(A)))
linearindices(A::AbstractVector) = (@_inline_meta; indices1(A))

keys(a::AbstractArray) = CartesianIndices(axes(a))
keys(a::AbstractVector) = linearindices(a)
keys(a::AbstractVector) = LinearIndices(a)

prevind(::AbstractArray, i::Integer) = Int(i)-1
nextind(::AbstractArray, i::Integer) = Int(i)+1
Expand Down Expand Up @@ -187,7 +162,7 @@ julia> lastindex(rand(3,4,5), 2)
4
```
"""
lastindex(a::AbstractArray) = (@_inline_meta; last(linearindices(a)))
lastindex(a::AbstractArray) = (@_inline_meta; last(LinearIndices(a)))
lastindex(a::AbstractArray, d) = (@_inline_meta; last(axes(a, d)))

"""
Expand All @@ -205,7 +180,7 @@ julia> firstindex(rand(3,4,5), 2)
1
```
"""
firstindex(a::AbstractArray) = (@_inline_meta; first(linearindices(a)))
firstindex(a::AbstractArray) = (@_inline_meta; first(LinearIndices(a)))
firstindex(a::AbstractArray, d) = (@_inline_meta; first(axes(a, d)))

first(a::AbstractArray) = a[first(eachindex(a))]
Expand Down Expand Up @@ -321,49 +296,6 @@ function trailingsize(inds::Indices)
prod(map(unsafe_length, inds))
end

## Traits for array types ##

abstract type IndexStyle end
struct IndexLinear <: IndexStyle end
struct IndexCartesian <: IndexStyle end

"""
IndexStyle(A)
IndexStyle(typeof(A))
`IndexStyle` specifies the "native indexing style" for array `A`. When
you define a new `AbstractArray` type, you can choose to implement
either linear indexing or cartesian indexing. If you decide to
implement linear indexing, then you must set this trait for your array
type:
Base.IndexStyle(::Type{<:MyArray}) = IndexLinear()
The default is `IndexCartesian()`.
Julia's internal indexing machinery will automatically (and invisibly)
convert all indexing operations into the preferred style. This allows users
to access elements of your array using any indexing style, even when explicit
methods have not been provided.
If you define both styles of indexing for your `AbstractArray`, this
trait can be used to select the most performant indexing style. Some
methods check this trait on their inputs, and dispatch to different
algorithms depending on the most efficient access pattern. In
particular, [`eachindex`](@ref) creates an iterator whose type depends
on the setting of this trait.
"""
IndexStyle(A::AbstractArray) = IndexStyle(typeof(A))
IndexStyle(::Type{Union{}}) = IndexLinear()
IndexStyle(::Type{<:AbstractArray}) = IndexCartesian()
IndexStyle(::Type{<:Array}) = IndexLinear()
IndexStyle(::Type{<:AbstractRange}) = IndexLinear()

IndexStyle(A::AbstractArray, B::AbstractArray) = IndexStyle(IndexStyle(A), IndexStyle(B))
IndexStyle(A::AbstractArray, B::AbstractArray...) = IndexStyle(IndexStyle(A), IndexStyle(B...))
IndexStyle(::IndexLinear, ::IndexLinear) = IndexLinear()
IndexStyle(::IndexStyle, ::IndexStyle) = IndexCartesian()

## Bounds checking ##

# The overall hierarchy is
Expand Down Expand Up @@ -411,10 +343,11 @@ function checkbounds(::Type{Bool}, A::AbstractArray, I...)
@_inline_meta
checkbounds_indices(Bool, axes(A), I)
end

# Linear indexing is explicitly allowed when there is only one (non-cartesian) index
function checkbounds(::Type{Bool}, A::AbstractArray, i)
@_inline_meta
checkindex(Bool, linearindices(A), i)
checkindex(Bool, eachindex(IndexLinear(), A), i)
end
# As a special extension, allow using logical arrays that match the source array exactly
function checkbounds(::Type{Bool}, A::AbstractArray{<:Any,N}, I::AbstractArray{Bool,N}) where N
Expand Down Expand Up @@ -674,7 +607,7 @@ function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer, n::
n < 0 && throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative")))
n == 0 && return dest
dmax = dstart + n - 1
inds = linearindices(dest)
inds = LinearIndices(dest)
if (dstart inds || dmax inds) | (sstart < 1)
sstart < 1 && throw(ArgumentError(string("source start offset (",sstart,") is < 1")))
throw(BoundsError(dest, dstart:dmax))
Expand Down Expand Up @@ -704,7 +637,7 @@ copyto!(dest::AbstractArray, src::AbstractArray) =
copyto!(IndexStyle(dest), dest, IndexStyle(src), src)

function copyto!(::IndexStyle, dest::AbstractArray, ::IndexStyle, src::AbstractArray)
destinds, srcinds = linearindices(dest), linearindices(src)
destinds, srcinds = LinearIndices(dest), LinearIndices(src)
isempty(srcinds) || (first(srcinds) destinds && last(srcinds) destinds) ||
throw(BoundsError(dest, srcinds))
@inbounds for i in srcinds
Expand All @@ -714,7 +647,7 @@ function copyto!(::IndexStyle, dest::AbstractArray, ::IndexStyle, src::AbstractA
end

function copyto!(::IndexStyle, dest::AbstractArray, ::IndexCartesian, src::AbstractArray)
destinds, srcinds = linearindices(dest), linearindices(src)
destinds, srcinds = LinearIndices(dest), LinearIndices(src)
isempty(srcinds) || (first(srcinds) destinds && last(srcinds) destinds) ||
throw(BoundsError(dest, srcinds))
i = 0
Expand All @@ -725,11 +658,11 @@ function copyto!(::IndexStyle, dest::AbstractArray, ::IndexCartesian, src::Abstr
end

function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray)
copyto!(dest, dstart, src, first(linearindices(src)), _length(src))
copyto!(dest, dstart, src, first(LinearIndices(src)), _length(src))
end

function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray, sstart::Integer)
srcinds = linearindices(src)
srcinds = LinearIndices(src)
sstart srcinds || throw(BoundsError(src, sstart))
copyto!(dest, dstart, src, sstart, last(srcinds)-sstart+1)
end
Expand All @@ -739,7 +672,7 @@ function copyto!(dest::AbstractArray, dstart::Integer,
n::Integer)
n == 0 && return dest
n < 0 && throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative")))
destinds, srcinds = linearindices(dest), linearindices(src)
destinds, srcinds = LinearIndices(dest), LinearIndices(src)
(dstart destinds && dstart+n-1 destinds) || throw(BoundsError(dest, dstart:dstart+n-1))
(sstart srcinds && sstart+n-1 srcinds) || throw(BoundsError(src, sstart:sstart+n-1))
@inbounds for i = 0:(n-1)
Expand Down Expand Up @@ -868,11 +801,13 @@ function eachindex(A::AbstractArray, B::AbstractArray...)
@_inline_meta
eachindex(IndexStyle(A,B...), A, B...)
end
eachindex(::IndexLinear, A::AbstractArray) = linearindices(A)
eachindex(::IndexLinear, A::AbstractArray) = (@_inline_meta; OneTo(_length(A)))
eachindex(::IndexLinear, A::AbstractVector) = (@_inline_meta; indices1(A))
function eachindex(::IndexLinear, A::AbstractArray, B::AbstractArray...)
@_inline_meta
indsA = linearindices(A)
_all_match_first(linearindices, indsA, B...) || throw_eachindex_mismatch(IndexLinear(), A, B...)
indsA = eachindex(IndexLinear(), A)
_all_match_first(X->eachindex(IndexLinear(), X), indsA, B...) ||
throw_eachindex_mismatch(IndexLinear(), A, B...)
indsA
end
function _all_match_first(f::F, inds, A, B...) where F<:Function
Expand Down Expand Up @@ -911,7 +846,7 @@ end
pointer(x::AbstractArray{T}) where {T} = unsafe_convert(Ptr{T}, x)
function pointer(x::AbstractArray{T}, i::Integer) where T
@_inline_meta
unsafe_convert(Ptr{T}, x) + (i - first(linearindices(x)))*elsize(x)
unsafe_convert(Ptr{T}, x) + (i - first(LinearIndices(x)))*elsize(x)
end

## Approach:
Expand Down Expand Up @@ -2025,7 +1960,7 @@ end
@inline ith_all(i, as) = (as[1][i], ith_all(i, tail(as))...)

function map_n!(f::F, dest::AbstractArray, As) where F
for i = linearindices(As[1])
for i = LinearIndices(As[1])
dest[i] = f(ith_all(i, As)...)
end
return dest
Expand Down
14 changes: 7 additions & 7 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ function _collect(c, itr, ::EltypeUnknown, isz::Union{HasLength,HasShape})
end

function collect_to_with_first!(dest::AbstractArray, v1, itr, st)
i1 = first(linearindices(dest))
i1 = first(LinearIndices(dest))
dest[i1] = v1
return collect_to!(dest, itr, i1+1, st)
end
Expand Down Expand Up @@ -1385,15 +1385,15 @@ julia> reverse(A, 3, 5)
3
```
"""
function reverse(A::AbstractVector, s=first(linearindices(A)), n=last(linearindices(A)))
function reverse(A::AbstractVector, s=first(LinearIndices(A)), n=last(LinearIndices(A)))
B = similar(A)
for i = first(linearindices(A)):s-1
for i = first(LinearIndices(A)):s-1
B[i] = A[i]
end
for i = s:n
B[i] = A[n+s-i]
end
for i = n+1:last(linearindices(A))
for i = n+1:last(LinearIndices(A))
B[i] = A[i]
end
return B
Expand All @@ -1403,7 +1403,7 @@ end
reverse(A::Vector) = invoke(reverse, Tuple{AbstractVector}, A)

function reverseind(a::AbstractVector, i::Integer)
li = linearindices(a)
li = LinearIndices(a)
first(li) + last(li) - i
end

Expand Down Expand Up @@ -1433,8 +1433,8 @@ julia> A
1
```
"""
function reverse!(v::AbstractVector, s=first(linearindices(v)), n=last(linearindices(v)))
liv = linearindices(v)
function reverse!(v::AbstractVector, s=first(LinearIndices(v)), n=last(LinearIndices(v)))
liv = LinearIndices(v)
if n <= s # empty case; ok
elseif !(first(liv) s last(liv))
throw(BoundsError(v, s))
Expand Down
2 changes: 1 addition & 1 deletion base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Broadcast

using .Base.Cartesian
using .Base: Indices, OneTo, linearindices, tail, to_shape,
using .Base: Indices, OneTo, tail, to_shape,
_msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache,
isoperator, promote_typejoin, unalias
import .Base: broadcast, broadcast!
Expand Down
2 changes: 2 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1580,6 +1580,8 @@ function search(buf::IOBuffer, delim::UInt8)
return Int(q-p+1)
end

@deprecate linearindices(x::AbstractArray) LinearIndices(x)

# PR #26647
# The `keep` argument in `split` and `rpslit` has been renamed to `keepempty`.
# To remove this deprecation, remove the `keep` argument from the function signatures as well as
Expand Down
2 changes: 1 addition & 1 deletion base/errorshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ function is_default_method(m::Method)
end

@noinline function throw_eachindex_mismatch(::IndexLinear, A...)
throw(DimensionMismatch("all inputs to eachindex must have the same indices, got $(join(linearindices.(A), ", ", " and "))"))
throw(DimensionMismatch("all inputs to eachindex must have the same indices, got $(join(LinearIndices.(A), ", ", " and "))"))
end
@noinline function throw_eachindex_mismatch(::IndexCartesian, A...)
throw(DimensionMismatch("all inputs to eachindex must have the same axes, got $(join(axes.(A), ", ", " and "))"))
Expand Down
1 change: 0 additions & 1 deletion base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,6 @@ next(v::SimpleVector,i) = (v[i],i+1)
done(v::SimpleVector,i) = (length(v) < i)
isempty(v::SimpleVector) = (length(v) == 0)
axes(v::SimpleVector) = (OneTo(length(v)),)
linearindices(v::SimpleVector) = axes(v, 1)
axes(v::SimpleVector, d) = d <= 1 ? axes(v)[d] : OneTo(1)

function ==(v1::SimpleVector, v2::SimpleVector)
Expand Down
1 change: 0 additions & 1 deletion base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,6 @@ export
isperm,
issorted,
last,
linearindices,
mapslices,
max,
maximum!,
Expand Down
Loading

0 comments on commit 786e282

Please sign in to comment.