Skip to content

Commit

Permalink
Remove the "experimental" status from non-1 indexing
Browse files Browse the repository at this point in the history
  • Loading branch information
timholy committed Jul 6, 2018
1 parent b963015 commit 7e891ee
Show file tree
Hide file tree
Showing 45 changed files with 351 additions and 16 deletions.
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ Language changes
* `try` blocks without `catch` or `finally` are no longer allowed. An explicit empty
`catch` block should be written instead ([#27554]).

* `AbstractArray` types that use unconventional (not 1-based) indexing can now support
`size`, `length`, and `@inbounds`. To optionally enforce conventional indices,
you can `@assert is_one_indexed(A)`.

Breaking changes
----------------

Expand Down
10 changes: 10 additions & 0 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ function axes(A)
map(OneTo, size(A))
end

"""
is_one_indexed(A)
is_one_indexed(A, B, ...)
Return `true` if the indices of `A` start at 1 along each axis.
If multiple arrays are passed, all must be 1-indexed.
"""
is_one_indexed(A::AbstractArray) = all(x->first(x)==1, axes(A))
is_one_indexed(A::AbstractArray, Bs::AbstractArray...) = all(is_one_indexed, (A, Bs...))

# Performance optimization: get rid of a branch on `d` in `axes(A, d)`
# for d=1. 1d arrays are heavily used, and the first dimension comes up
# in other applications.
Expand Down
4 changes: 4 additions & 0 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,7 @@ for (fname, felt) in ((:zeros, :zero), (:ones, :one))
end

function _one(unit::T, x::AbstractMatrix) where T
@assert is_one_indexed(x)
m,n = size(x)
m==n || throw(DimensionMismatch("multiplicative identity defined only for square matrices"))
# Matrix{T}(I, m, m)
Expand Down Expand Up @@ -748,6 +749,7 @@ function setindex! end
function setindex!(A::Array, X::AbstractArray, I::AbstractVector{Int})
@_propagate_inbounds_meta
@boundscheck setindex_shape_check(X, length(I))
@assert is_one_indexed(X)
X′ = unalias(A, X)
I′ = unalias(A, I)
count = 1
Expand Down Expand Up @@ -876,6 +878,7 @@ append!(a::Vector, iter) = _append!(a, IteratorSize(iter), iter)
push!(a::Vector, iter...) = append!(a, iter)

function _append!(a, ::Union{HasLength,HasShape}, iter)
@assert is_one_indexed(a)
n = length(a)
resize!(a, n+length(iter))
@inbounds for (i,item) in zip(n+1:length(a), iter)
Expand Down Expand Up @@ -923,6 +926,7 @@ prepend!(a::Vector, iter) = _prepend!(a, IteratorSize(iter), iter)
pushfirst!(a::Vector, iter...) = prepend!(a, iter)

function _prepend!(a, ::Union{HasLength,HasShape}, iter)
@assert is_one_indexed(a)
n = length(iter)
_growbeg!(a, n)
i = 0
Expand Down
2 changes: 2 additions & 0 deletions base/c.jl
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ transcode(T, src::String) = transcode(T, codeunits(src))
transcode(::Type{String}, src) = String(transcode(UInt8, src))

function transcode(::Type{UInt16}, src::AbstractVector{UInt8})
@assert is_one_indexed(src)
dst = UInt16[]
i, n = 1, length(src)
n > 0 || return dst
Expand Down Expand Up @@ -317,6 +318,7 @@ function transcode(::Type{UInt16}, src::AbstractVector{UInt8})
end

function transcode(::Type{UInt8}, src::AbstractVector{UInt16})
@assert is_one_indexed(src)
n = length(src)
n == 0 && return UInt8[]

Expand Down
3 changes: 3 additions & 0 deletions base/combinatorics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ isperm(p::Tuple{Int}) = p[1] == 1
isperm(p::Tuple{Int,Int}) = ((p[1] == 1) & (p[2] == 2)) | ((p[1] == 2) & (p[2] == 1))

function permute!!(a, p::AbstractVector{<:Integer})
@assert is_one_indexed(a, p)
count = 0
start = 0
while count < length(a)
Expand Down Expand Up @@ -114,6 +115,7 @@ julia> A
permute!(a, p::AbstractVector) = permute!!(a, copymutable(p))

function invpermute!!(a, p::AbstractVector{<:Integer})
@assert is_one_indexed(a, p)
count = 0
start = 0
while count < length(a)
Expand Down Expand Up @@ -194,6 +196,7 @@ julia> B[invperm(v)]
```
"""
function invperm(a::AbstractVector)
@assert is_one_indexed(a)
b = zero(a) # similar vector of zeros
n = length(a)
@inbounds for (i, j) in enumerate(a)
Expand Down
1 change: 1 addition & 0 deletions base/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,7 @@ The size of `b` will be increased if needed (i.e. if `nb` is greater than `lengt
and enough bytes could be read), but it will never be decreased.
"""
function readbytes!(s::IO, b::AbstractArray{UInt8}, nb=length(b))
@assert is_one_indexed(b)
olb = lb = length(b)
nr = 0
while nr < nb && !eof(s)
Expand Down
3 changes: 3 additions & 0 deletions base/iobuffer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mutable struct GenericIOBuffer{T<:AbstractVector{UInt8}} <: IO

function GenericIOBuffer{T}(data::T, readable::Bool, writable::Bool, seekable::Bool, append::Bool,
maxsize::Integer) where T<:AbstractVector{UInt8}
@assert is_one_indexed(data)
new(data,readable,writable,seekable,append,length(data),maxsize,1,-1)
end
end
Expand Down Expand Up @@ -167,6 +168,7 @@ function unsafe_read(from::GenericIOBuffer, p::Ptr{UInt8}, nb::UInt)
end

function read_sub(from::GenericIOBuffer, a::AbstractArray{T}, offs, nel) where T
@assert is_one_indexed(a)
from.readable || throw(ArgumentError("read failed, IOBuffer is not readable"))
if offs+nel-1 > length(a) || offs < 1 || nel < 0
throw(BoundsError())
Expand Down Expand Up @@ -401,6 +403,7 @@ function unsafe_write(to::GenericIOBuffer, p::Ptr{UInt8}, nb::UInt)
end

function write_sub(to::GenericIOBuffer, a::AbstractArray{UInt8}, offs, nel)
@assert is_one_indexed(a)
if offs+nel-1 > length(a) || offs < 1 || nel < 0
throw(BoundsError())
end
Expand Down
6 changes: 5 additions & 1 deletion base/multidimensional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,10 @@ _iterable(X::AbstractArray, I...) = X
end
end

diff(a::AbstractVector) = [ a[i+1] - a[i] for i=1:length(a)-1 ]
function diff(a::AbstractVector)
@assert is_one_indexed(a)
[ a[i+1] - a[i] for i=1:length(a)-1 ]
end

"""
diff(A::AbstractVector)
Expand Down Expand Up @@ -1455,6 +1458,7 @@ function _extrema_dims(A::AbstractArray, dims)
end

@noinline function extrema!(B, A)
@assert is_one_indexed(B, A)
sA = size(A)
sB = size(B)
for I in CartesianIndices(sB)
Expand Down
1 change: 1 addition & 0 deletions base/number.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ ndims(::Type{<:Number}) = 0
length(x::Number) = 1
firstindex(x::Number) = 1
lastindex(x::Number) = 1
is_one_indexed(x::Number) = true
IteratorSize(::Type{<:Number}) = HasShape{0}()
keys(::Number) = OneTo(1)

Expand Down
1 change: 1 addition & 0 deletions base/reshapedarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ _reshape(parent::Array, dims::Dims) = reshape(parent, dims)

# When reshaping Vector->Vector, don't wrap with a ReshapedArray
function _reshape(v::AbstractVector, dims::Dims{1})
@assert is_one_indexed(v)
len = dims[1]
len == length(v) || _throw_dmrs(_length(v), "length", len)
v
Expand Down
6 changes: 6 additions & 0 deletions base/sort.jl
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ function searchsorted(v::AbstractVector, x, ilo::Int, ihi::Int, o::Ordering)
end

function searchsortedlast(a::AbstractRange{<:Real}, x::Real, o::DirectOrdering)
is_one_indexed(a) || throw(ArgumentError("range must be indexed starting with 1"))
if step(a) == 0
lt(o, x, first(a)) ? 0 : length(a)
else
Expand All @@ -234,6 +235,7 @@ function searchsortedlast(a::AbstractRange{<:Real}, x::Real, o::DirectOrdering)
end

function searchsortedfirst(a::AbstractRange{<:Real}, x::Real, o::DirectOrdering)
is_one_indexed(a) || throw(ArgumentError("range must be indexed starting with 1"))
if step(a) == 0
lt(o, first(a), x) ? length(a) + 1 : 1
else
Expand All @@ -243,6 +245,7 @@ function searchsortedfirst(a::AbstractRange{<:Real}, x::Real, o::DirectOrdering)
end

function searchsortedlast(a::AbstractRange{<:Integer}, x::Real, o::DirectOrdering)
is_one_indexed(a) || throw(ArgumentError("range must be indexed starting with 1"))
if step(a) == 0
lt(o, x, first(a)) ? 0 : length(a)
else
Expand All @@ -251,6 +254,7 @@ function searchsortedlast(a::AbstractRange{<:Integer}, x::Real, o::DirectOrderin
end

function searchsortedfirst(a::AbstractRange{<:Integer}, x::Real, o::DirectOrdering)
is_one_indexed(a) || throw(ArgumentError("range must be indexed starting with 1"))
if step(a) == 0
lt(o, first(a), x) ? length(a)+1 : 1
else
Expand All @@ -259,6 +263,7 @@ function searchsortedfirst(a::AbstractRange{<:Integer}, x::Real, o::DirectOrderi
end

function searchsortedfirst(a::AbstractRange{<:Integer}, x::Unsigned, o::DirectOrdering)
is_one_indexed(a) || throw(ArgumentError("range must be indexed starting with 1"))
if lt(o, first(a), x)
if step(a) == 0
length(a) + 1
Expand All @@ -271,6 +276,7 @@ function searchsortedfirst(a::AbstractRange{<:Integer}, x::Unsigned, o::DirectOr
end

function searchsortedlast(a::AbstractRange{<:Integer}, x::Unsigned, o::DirectOrdering)
is_one_indexed(a) || throw(ArgumentError("range must be indexed starting with 1"))
if lt(o, x, first(a))
0
elseif step(a) == 0
Expand Down
1 change: 1 addition & 0 deletions base/tuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ NTuple
length(@nospecialize t::Tuple) = nfields(t)
firstindex(@nospecialize t::Tuple) = 1
lastindex(@nospecialize t::Tuple) = length(t)
is_one_indexed(@nospecialize t::Tuple) = true
size(@nospecialize(t::Tuple), d) = (d == 1) ? length(t) : throw(ArgumentError("invalid tuple dimension $d"))
@eval getindex(t::Tuple, i::Int) = getfield(t, i, $(Expr(:boundscheck)))
@eval getindex(t::Tuple, i::Real) = getfield(t, convert(Int, i), $(Expr(:boundscheck)))
Expand Down
1 change: 1 addition & 0 deletions stdlib/Base64/src/decode.jl
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ function Base.read(pipe::Base64DecodePipe, ::Type{UInt8})
end

function Base.readbytes!(pipe::Base64DecodePipe, data::AbstractVector{UInt8}, nb::Integer=length(data))
@assert is_one_indexed(data)
filled::Int = 0
while filled < nb && !eof(pipe)
if length(data) == filled
Expand Down
2 changes: 1 addition & 1 deletion stdlib/Distributed/src/pmap.jl
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ function wrap_batch(f, p, handle_errors)
remotecall_fetch(f, p, batch)
catch e
if handle_errors
return Any[BatchProcessingError(batch[i], e) for i in 1:length(batch)]
return Any[BatchProcessingError(b, e) for b in batch]
else
rethrow(e)
end
Expand Down
4 changes: 2 additions & 2 deletions stdlib/Future/src/Future.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ copy!(dst::AbstractDict, src::AbstractDict) = merge!(empty!(dst), src)
copy!(dst::AbstractVector, src::AbstractVector) = append!(empty!(dst), src)

function copy!(dst::AbstractArray, src::AbstractArray)
size(dst) == size(src) || throw(ArgumentError(
"arrays must have the same size for copy! (consider using `copyto!`)"))
axes(dst) == axes(src) || throw(ArgumentError(
"arrays must have the same axes for copy! (consider using `copyto!`)"))
copyto!(dst, src)
end

Expand Down
3 changes: 0 additions & 3 deletions stdlib/LinearAlgebra/src/LinearAlgebra.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ using Base: hvcat_fill, iszero, IndexLinear, _length, promote_op, promote_typeof
@propagate_inbounds, @pure, reduce, typed_vcat
using Base.Broadcast: Broadcasted

# We use `_length` because of non-1 indices; releases after julia 0.5
# can go back to `length`. `_length(A)` is equivalent to `length(LinearIndices(A))`.

export
# Modules
LAPACK,
Expand Down
7 changes: 5 additions & 2 deletions stdlib/LinearAlgebra/src/adjtrans.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Base: length, size, axes, IndexStyle, getindex, setindex!, parent, vec, c
struct Adjoint{T,S} <: AbstractMatrix{T}
parent::S
function Adjoint{T,S}(A::S) where {T,S}
@assert is_one_indexed(A)
checkeltype_adjoint(T, eltype(A))
new(A)
end
Expand All @@ -19,6 +20,7 @@ struct Transpose{T,S} <: AbstractMatrix{T}
parent::S
function Transpose{T,S}(A::S) where {T,S}
checkeltype_transpose(T, eltype(A))
@assert is_one_indexed(A)
new(A)
end
end
Expand Down Expand Up @@ -144,8 +146,8 @@ similar(A::AdjOrTransAbsVec) = wrapperop(A)(similar(A.parent))
similar(A::AdjOrTransAbsVec, ::Type{T}) where {T} = wrapperop(A)(similar(A.parent, Base.promote_op(wrapperop(A), T)))
# for matrices, the semantics of the wrapped and unwrapped types are generally the same
# and as you are allocating with similar anyway, you might as well get something unwrapped
similar(A::AdjOrTrans) = similar(A.parent, eltype(A), size(A))
similar(A::AdjOrTrans, ::Type{T}) where {T} = similar(A.parent, T, size(A))
similar(A::AdjOrTrans) = similar(A.parent, eltype(A), axes(A))
similar(A::AdjOrTrans, ::Type{T}) where {T} = similar(A.parent, T, axes(A))
similar(A::AdjOrTrans, ::Type{T}, dims::Dims{N}) where {T,N} = similar(A.parent, T, dims)

# sundry basic definitions
Expand Down Expand Up @@ -197,6 +199,7 @@ broadcast(f, tvs::Union{Number,TransposeAbsVec}...) = transpose(broadcast((xs...
*(u::AdjointAbsVec, v::AbstractVector) = dot(u.parent, v)
*(u::TransposeAbsVec{T}, v::AbstractVector{T}) where {T<:Real} = dot(u.parent, v)
function *(u::TransposeAbsVec, v::AbstractVector)
@assert is_one_indexed(u, v)
@boundscheck length(u) == length(v) || throw(DimensionMismatch())
return sum(@inbounds(u[k]*v[k]) for k in 1:length(u))
end
Expand Down
10 changes: 10 additions & 0 deletions stdlib/LinearAlgebra/src/bidiag.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ struct Bidiagonal{T,V<:AbstractVector{T}} <: AbstractMatrix{T}
ev::V # sub/super diagonal
uplo::Char # upper bidiagonal ('U') or lower ('L')
function Bidiagonal{T}(dv::V, ev::V, uplo::AbstractChar) where {T,V<:AbstractVector{T}}
@assert is_one_indexed(dv, ev)
if length(ev) != length(dv)-1
throw(DimensionMismatch("length of diagonal vector is $(length(dv)), length of off-diagonal vector is $(length(ev))"))
end
Expand Down Expand Up @@ -352,6 +353,9 @@ mul!(C::AbstractMatrix, A::BiTri, B::Adjoint{<:Any,<:AbstractVecOrMat}) = A_mul_
mul!(C::AbstractVector, A::BiTri, B::Transpose{<:Any,<:AbstractVecOrMat}) = throw(MethodError(mul!, (C, A, B)))

function check_A_mul_B!_sizes(C, A, B)
@assert is_one_indexed(C)
@assert is_one_indexed(A)
@assert is_one_indexed(B)
nA, mA = size(A)
nB, mB = size(B)
nC, mC = size(C)
Expand Down Expand Up @@ -432,6 +436,8 @@ function A_mul_B_td!(C::AbstractMatrix, A::BiTriSym, B::BiTriSym)
end

function A_mul_B_td!(C::AbstractVecOrMat, A::BiTriSym, B::AbstractVecOrMat)
@assert is_one_indexed(C)
@assert is_one_indexed(B)
nA = size(A,1)
nB = size(B,2)
if !(size(C,1) == size(B,1) == nA)
Expand Down Expand Up @@ -506,6 +512,7 @@ ldiv!(A::Union{Bidiagonal, AbstractTriangular}, b::AbstractVector) = naivesub!(A
ldiv!(A::Transpose{<:Any,<:Bidiagonal}, b::AbstractVector) = ldiv!(copy(A), b)
ldiv!(A::Adjoint{<:Any,<:Bidiagonal}, b::AbstractVector) = ldiv!(copy(A), b)
function ldiv!(A::Union{Bidiagonal,AbstractTriangular}, B::AbstractMatrix)
@assert is_one_indexed(A, B)
nA,mA = size(A)
tmp = similar(B,size(B,1))
n = size(B, 1)
Expand All @@ -520,6 +527,7 @@ function ldiv!(A::Union{Bidiagonal,AbstractTriangular}, B::AbstractMatrix)
B
end
function ldiv!(adjA::Adjoint{<:Any,<:Union{Bidiagonal,AbstractTriangular}}, B::AbstractMatrix)
@assert is_one_indexed(adjA, B)
A = adjA.parent
nA,mA = size(A)
tmp = similar(B,size(B,1))
Expand All @@ -535,6 +543,7 @@ function ldiv!(adjA::Adjoint{<:Any,<:Union{Bidiagonal,AbstractTriangular}}, B::A
B
end
function ldiv!(transA::Transpose{<:Any,<:Union{Bidiagonal,AbstractTriangular}}, B::AbstractMatrix)
@assert is_one_indexed(transA, B)
A = transA.parent
nA,mA = size(A)
tmp = similar(B,size(B,1))
Expand All @@ -551,6 +560,7 @@ function ldiv!(transA::Transpose{<:Any,<:Union{Bidiagonal,AbstractTriangular}},
end
#Generic solver using naive substitution
function naivesub!(A::Bidiagonal{T}, b::AbstractVector, x::AbstractVector = b) where T
@assert is_one_indexed(A, b, x)
N = size(A, 2)
if N != length(b) || N != length(x)
throw(DimensionMismatch("second dimension of A, $N, does not match one of the lengths of x, $(length(x)), or b, $(length(b))"))
Expand Down
Loading

0 comments on commit 7e891ee

Please sign in to comment.