From 2b991c4404955e24f0ff4285c57e055e1193fd44 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sun, 26 Jun 2016 10:39:43 -0500 Subject: [PATCH 01/15] Add specialized Range type, OneTo, for use with indices --- base/abstractarray.jl | 48 ++++++++--------- base/multidimensional.jl | 2 +- base/range.jl | 112 ++++++++++++++++++++++++++------------- test/ranges.jl | 30 +++++++++++ 4 files changed, 129 insertions(+), 63 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index fec3620e2a2bb..490adf632428f 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -5,9 +5,11 @@ typealias AbstractVector{T} AbstractArray{T,1} typealias AbstractMatrix{T} AbstractArray{T,2} typealias AbstractVecOrMat{T} Union{AbstractVector{T}, AbstractMatrix{T}} -typealias RangeIndex Union{Int, Range{Int}, UnitRange{Int}, Colon} -typealias UnitRangeInteger{T<:Integer} UnitRange{T} -typealias Indices{N} NTuple{N,UnitRangeInteger} +typealias RangeIndex Union{Int, Range{Int}, AbstractUnitRange{Int}, Colon} +typealias Indices{N} NTuple{N,AbstractUnitRange} +typealias IndicesOne{N} NTuple{N,OneTo} +typealias DimOrInd Union{Integer, AbstractUnitRange} +typealias DimsOrInds{N} NTuple{N,DimOrInd} ## Basic functions ## @@ -30,20 +32,20 @@ Returns the valid range of indices for array `A` along dimension `d`. """ function indices(A::AbstractArray, d) @_inline_meta - 1:size(A,d) + OneTo(size(A,d)) end """ indices(A) Returns the tuple of valid indices for array `A`. """ -indices{T,N}(A::AbstractArray{T,N}) = _indices((), A) +indices{T,N}(A::AbstractArray{T,N}) = _indices((), A) # faster than ntuple(d->indices(A,d), Val{N}) _indices{T,N}(out::NTuple{N}, A::AbstractArray{T,N}) = out function _indices(out, A::AbstractArray) @_inline_meta _indices((out..., indices(A, length(out)+1)), A) end -# This simpler implementation suffers from #16327 +# Note: a simpler implementation suffers from #16327 # function indices{T,N}(A::AbstractArray{T,N}) # @_inline_meta # ntuple(d->indices(A, d), Val{N}) @@ -203,15 +205,15 @@ Return `true` if the given `index` is within the bounds of arrays can extend this method in order to provide a specialized bounds checking implementation. """ -checkindex(::Type{Bool}, inds::UnitRange, i) = throw(ArgumentError("unable to check bounds for indices of type $(typeof(i))")) -checkindex(::Type{Bool}, inds::UnitRange, i::Real) = (first(inds) <= i) & (i <= last(inds)) -checkindex(::Type{Bool}, inds::UnitRange, ::Colon) = true -function checkindex(::Type{Bool}, inds::UnitRange, r::Range) +checkindex(::Type{Bool}, inds::AbstractUnitRange, i) = throw(ArgumentError("unable to check bounds for indices of type $(typeof(i))")) +checkindex(::Type{Bool}, inds::AbstractUnitRange, i::Real) = (first(inds) <= i) & (i <= last(inds)) +checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Colon) = true +function checkindex(::Type{Bool}, inds::AbstractUnitRange, r::Range) @_propagate_inbounds_meta isempty(r) | (checkindex(Bool, inds, first(r)) & checkindex(Bool, inds, last(r))) end -checkindex{N}(::Type{Bool}, indx::UnitRange, I::AbstractArray{Bool,N}) = N == 1 && indx == indices1(I) -function checkindex(::Type{Bool}, inds::UnitRange, I::AbstractArray) +checkindex{N}(::Type{Bool}, indx::AbstractUnitRange, I::AbstractArray{Bool,N}) = N == 1 && indx == indices1(I) +function checkindex(::Type{Bool}, inds::AbstractUnitRange, I::AbstractArray) @_inline_meta b = true for i in I @@ -312,7 +314,6 @@ checkbounds(A::AbstractArray) = checkbounds(A, 1) # 0-d case ## Constructors ## # default arguments to similar() -typealias SimIdx Union{Integer,UnitRangeInteger} """ similar(array, [element_type=eltype(array)], [dims=size(array)]) @@ -351,17 +352,15 @@ different element type it will create a regular `Array` instead: See also `allocate_for`. """ similar{T}(a::AbstractArray{T}) = similar(a, T) -similar( a::AbstractArray, T::Type) = _similar(indicesbehavior(a), a, T) +similar( a::AbstractArray, T::Type) = similar(a, T, indices(a)) similar{T}(a::AbstractArray{T}, dims::Tuple) = similar(a, T, dims) -similar{T}(a::AbstractArray{T}, dims::SimIdx...) = similar(a, T, dims) -similar( a::AbstractArray, T::Type, dims::SimIdx...) = similar(a, T, dims) -similar( a::AbstractArray, T::Type, dims::DimsInteger) = similar(a, T, convert(Dims, dims)) +similar{T}(a::AbstractArray{T}, dims::DimOrInd...) = similar(a, T, dims) +similar( a::AbstractArray, T::Type, dims::DimOrInd...) = similar(a, T, dims) +similar( a::AbstractArray, T::Type, dims::DimsInteger) = similar(a, T, Dims(dims)) # similar creates an Array by default +similar( a::AbstractArray, T::Type, inds::IndicesOne) = similar(a, T, map(last, inds)) similar( a::AbstractArray, T::Type, dims::Dims) = Array(T, dims) -_similar(::IndicesStartAt1, a::AbstractArray, T::Type) = similar(a, T, size(a)) -_similar(::IndicesBehavior, a::AbstractArray, T::Type) = similar(a, T, indices(a)) - """ allocate_for(storagetype, referencearray, [shape]) @@ -1300,10 +1299,11 @@ function _sub2ind(inds, L, ind, i::Integer, I::Integer...) end nextL(L, l::Integer) = L*l -nextL(L, r::UnitRange) = L*unsafe_length(r) +nextL(L, r::AbstractUnitRange) = L*unsafe_length(r) offsetin(i, l::Integer) = i-1 -offsetin(i, r::UnitRange) = i-first(r) +offsetin(i, r::AbstractUnitRange) = i-first(r) unsafe_length(r::UnitRange) = r.stop-r.start+1 +unsafe_length(r::OneTo) = length(r) ind2sub(::Tuple{}, ind::Integer) = (@_inline_meta; ind == 1 ? () : throw(BoundsError())) ind2sub(dims::DimsInteger, ind::Integer) = (@_inline_meta; _ind2sub((), dims, ind-1)) @@ -1323,9 +1323,9 @@ function _ind2sub(out, inds, ind) end _lookup(ind, d::Integer) = ind+1 -_lookup(ind, r::UnitRange) = ind+first(r) +_lookup(ind, r::AbstractUnitRange) = ind+first(r) _div(ind, d::Integer) = div(ind, d), 1, d -_div(ind, r::UnitRange) = (d = unsafe_length(r); (div(ind, d), first(r), d)) +_div(ind, r::AbstractUnitRange) = (d = unsafe_length(r); (div(ind, d), first(r), d)) smart_ind2sub(shape::NTuple{1}, ind) = (ind,) smart_ind2sub(shape, ind) = ind2sub(shape, ind) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 838101efd8328..197a585ec5439 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -78,7 +78,7 @@ end CartesianRange{N}(index::CartesianIndex{N}) = 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{Int,UnitRange{Int}}}) = CartesianRange(CartesianIndex(map(r->first(r), rngs)), CartesianIndex(map(r->last(r), rngs))) +CartesianRange{N}(rngs::NTuple{N,Union{Integer,AbstractUnitRange}}) = CartesianRange(CartesianIndex(map(r->first(r), rngs)), CartesianIndex(map(r->last(r), rngs))) ndims(R::CartesianRange) = length(R.start) ndims{I<:CartesianIndex}(::Type{CartesianRange{I}}) = length(I) diff --git a/base/range.jl b/base/range.jl index 58ec756d1b078..a3badcf109535 100644 --- a/base/range.jl +++ b/base/range.jl @@ -10,6 +10,7 @@ abstract Range{T} <: AbstractArray{T,1} ## ordinal ranges abstract OrdinalRange{T,S} <: Range{T} +abstract AbstractUnitRange{T} <: OrdinalRange{T,Int} immutable StepRange{T,S} <: OrdinalRange{T,S} start::T @@ -64,7 +65,7 @@ steprem(start,stop,step) = (stop-start) % step StepRange{T,S}(start::T, step::S, stop::T) = StepRange{T,S}(start, step, stop) -immutable UnitRange{T<:Real} <: OrdinalRange{T,Int} +immutable UnitRange{T<:Real} <: AbstractUnitRange{T} start::T stop::T UnitRange(start, stop) = new(start, unitrange_last(start,stop)) @@ -78,6 +79,12 @@ unitrange_last{T}(start::T, stop::T) = ifelse(stop >= start, convert(T,start+floor(stop-start)), convert(T,start-one(stop-start))) +immutable OneTo{T<:Integer} <: AbstractUnitRange{T} + stop::T + OneTo(stop) = new(max(zero(T), stop)) +end +OneTo{T<:Integer}(stop::T) = OneTo{T}(stop) + colon(a::Real, b::Real) = colon(promote(a,b)...) colon{T<:Real}(start::T, stop::T) = UnitRange{T}(start, stop) @@ -310,12 +317,12 @@ size(r::Range) = (length(r),) isempty(r::StepRange) = (r.start != r.stop) & ((r.step > zero(r.step)) != (r.stop > r.start)) -isempty(r::UnitRange) = r.start > r.stop +isempty(r::AbstractUnitRange) = first(r) > last(r) isempty(r::FloatRange) = length(r) == 0 isempty(r::LinSpace) = length(r) == 0 step(r::StepRange) = r.step -step(r::UnitRange) = 1 +step(r::AbstractUnitRange) = 1 step(r::FloatRange) = r.step/r.divisor step{T}(r::LinSpace{T}) = ifelse(r.len <= 0, convert(T,NaN), (r.stop-r.start)/r.divisor) @@ -323,7 +330,8 @@ function length(r::StepRange) n = Integer(div(r.stop+r.step - r.start, r.step)) isempty(r) ? zero(n) : n end -length(r::UnitRange) = Integer(r.stop - r.start + 1) +length(r::AbstractUnitRange) = Integer(last(r) - first(r) + 1) +length(r::OneTo) = r.stop length(r::FloatRange) = Integer(r.len) length(r::LinSpace) = Integer(r.len + signbit(r.len - 1)) @@ -338,11 +346,12 @@ function length{T<:Union{Int,UInt,Int64,UInt64}}(r::StepRange{T}) end end -length{T<:Union{Int,Int64}}(r::UnitRange{T}) = - checked_add(checked_sub(r.stop, r.start), one(T)) +length{T<:Union{Int,Int64}}(r::AbstractUnitRange{T}) = + checked_add(checked_sub(last(r), first(r)), one(T)) +length{T<:Union{Int,Int64}}(r::OneTo{T}) = T(r.stop) -length{T<:Union{UInt,UInt64}}(r::UnitRange{T}) = - r.stop < r.start ? zero(T) : checked_add(r.stop - r.start, one(T)) +length{T<:Union{UInt,UInt64}}(r::AbstractUnitRange{T}) = + r.stop < r.start ? zero(T) : checked_add(last(r) - first(r), one(T)) # some special cases to favor default Int type let smallint = (Int === Int64 ? @@ -355,20 +364,21 @@ let smallint = (Int === Int64 ? div(Int(r.stop)+Int(r.step) - Int(r.start), Int(r.step)) end - length{T <: smallint}(r::UnitRange{T}) = Int(r.stop) - Int(r.start) + 1 + length{T <: smallint}(r::AbstractUnitRange{T}) = Int(last(r)) - Int(first(r)) + 1 + length{T <: smallint}(r::OneTo{T}) = Int(r.stop) end first{T}(r::OrdinalRange{T}) = convert(T, r.start) +first{T}(r::OneTo{T}) = one(T) first{T}(r::FloatRange{T}) = convert(T, r.start/r.divisor) first{T}(r::LinSpace{T}) = convert(T, (r.len-1)*r.start/r.divisor) -last{T}(r::StepRange{T}) = r.stop -last(r::UnitRange) = r.stop +last{T}(r::OrdinalRange{T}) = convert(T, r.stop) last{T}(r::FloatRange{T}) = convert(T, (r.start + (r.len-1)*r.step)/r.divisor) last{T}(r::LinSpace{T}) = convert(T, (r.len-1)*r.stop/r.divisor) -minimum(r::UnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : first(r) -maximum(r::UnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : last(r) +minimum(r::AbstractUnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : first(r) +maximum(r::AbstractUnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : last(r) minimum(r::Range) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : min(first(r), last(r)) maximum(r::Range) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : max(first(r), last(r)) @@ -398,9 +408,10 @@ done{T,S}(r::StepRange{T,S}, i::Integer) = isempty(r) | (i == oftype(i, r.stop) + r.step) start{T}(r::UnitRange{T}) = oftype(r.start + one(T), r.start) -next{T}(r::UnitRange{T}, i) = (convert(T, i), i + one(T)) -done{T}(r::UnitRange{T}, i) = i == oftype(i, r.stop) + one(T) +next{T}(r::AbstractUnitRange{T}, i) = (convert(T, i), i + one(T)) +done{T}(r::AbstractUnitRange{T}, i) = i == oftype(i, r.stop) + one(T) +start{T}(r::OneTo{T}) = one(T) # some special cases to favor default Int type to avoid overflow let smallint = (Int === Int64 ? @@ -411,7 +422,8 @@ let smallint = (Int === Int64 ? start{T<:smallint}(r::StepRange{T}) = convert(Int, r.start) next{T<:smallint}(r::StepRange{T}, i) = (i % T, i + r.step) start{T<:smallint}(r::UnitRange{T}) = convert(Int, r.start) - next{T<:smallint}(r::UnitRange{T}, i) = (i % T, i + 1) + next{T<:smallint}(r::AbstractUnitRange{T}, i) = (i % T, i + 1) + start{T<:smallint}(r::OneTo{T}) = 1 end ## indexing @@ -423,6 +435,12 @@ function getindex{T}(v::UnitRange{T}, i::Integer) ret end +function getindex{T}(v::OneTo{T}, i::Integer) + @_inline_meta + @boundscheck ((i > 0) & (i <= v.stop)) || throw_boundserror(v, i) + convert(T, i) +end + function getindex{T}(v::Range{T}, i::Integer) @_inline_meta ret = convert(T, first(v) + (i - 1)*step(v)) @@ -447,17 +465,23 @@ end getindex(r::Range, ::Colon) = copy(r) -function getindex{T<:Integer}(r::UnitRange, s::UnitRange{T}) +function getindex{T<:Integer}(r::UnitRange, s::AbstractUnitRange{T}) @_inline_meta @boundscheck checkbounds(r, s) - st = oftype(r.start, r.start + s.start-1) + st = oftype(r.start, r.start + first(s)-1) range(st, length(s)) end -function getindex{T<:Integer}(r::UnitRange, s::StepRange{T}) +function getindex{T}(r::OneTo{T}, s::OneTo) @_inline_meta @boundscheck checkbounds(r, s) - st = oftype(r.start, r.start + s.start-1) + OneTo(T(s.stop)) +end + +function getindex{T<:Integer}(r::AbstractUnitRange, s::StepRange{T}) + @_inline_meta + @boundscheck checkbounds(r, s) + st = oftype(first(r), first(r) + s.start-1) range(st, step(s), length(s)) end @@ -487,6 +511,7 @@ end show(io::IO, r::Range) = print(io, repr(first(r)), ':', repr(step(r)), ':', repr(last(r))) show(io::IO, r::UnitRange) = print(io, repr(first(r)), ':', repr(last(r))) +show(io::IO, r::OneTo) = print(io, "Base.OneTo(", r.stop, ")") =={T<:Range}(r::T, s::T) = (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) ==(r::OrdinalRange, s::OrdinalRange) = (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) @@ -508,15 +533,17 @@ function ==(r::Range, s::Range) return true end -intersect{T1<:Integer, T2<:Integer}(r::UnitRange{T1}, s::UnitRange{T2}) = max(r.start,s.start):min(last(r),last(s)) +intersect(r::OneTo, s::OneTo) = OneTo(min(r.stop,s.stop)) -intersect{T<:Integer}(i::Integer, r::UnitRange{T}) = +intersect{T1<:Integer, T2<:Integer}(r::AbstractUnitRange{T1}, s::AbstractUnitRange{T2}) = max(first(r),first(s)):min(last(r),last(s)) + +intersect{T<:Integer}(i::Integer, r::AbstractUnitRange{T}) = i < first(r) ? (first(r):i) : i > last(r) ? (i:last(r)) : (i:i) intersect{T<:Integer}(r::UnitRange{T}, i::Integer) = intersect(i, r) -function intersect{T1<:Integer, T2<:Integer}(r::UnitRange{T1}, s::StepRange{T2}) +function intersect{T1<:Integer, T2<:Integer}(r::AbstractUnitRange{T1}, s::StepRange{T2}) if isempty(s) range(first(r), 0) elseif step(s) == 0 @@ -535,7 +562,7 @@ function intersect{T1<:Integer, T2<:Integer}(r::UnitRange{T1}, s::StepRange{T2}) end end -function intersect{T1<:Integer, T2<:Integer}(r::StepRange{T1}, s::UnitRange{T2}) +function intersect{T1<:Integer, T2<:Integer}(r::StepRange{T1}, s::AbstractUnitRange{T2}) if step(r) < 0 reverse(intersect(s, reverse(r))) else @@ -597,7 +624,7 @@ function intersect(r1::Range, r2::Range, r3::Range, r::Range...) end # findin (the index of intersection) -function _findin{T1<:Integer, T2<:Integer}(r::Range{T1}, span::UnitRange{T2}) +function _findin{T1<:Integer, T2<:Integer}(r::Range{T1}, span::AbstractUnitRange{T2}) local ifirst local ilast fspan = first(span) @@ -630,11 +657,11 @@ end ## linear operations on ranges ## --(r::OrdinalRange) = range(-r.start, -step(r), length(r)) +-(r::OrdinalRange) = range(-first(r), -step(r), length(r)) -(r::FloatRange) = FloatRange(-r.start, -r.step, r.len, r.divisor) -(r::LinSpace) = LinSpace(-r.start, -r.stop, r.len, r.divisor) -.+(x::Real, r::UnitRange) = range(x + r.start, length(r)) +.+(x::Real, r::AbstractUnitRange) = range(x + first(r), length(r)) .+(x::Real, r::Range) = (x+first(r)):step(r):(x+last(r)) #.+(x::Real, r::StepRange) = range(x + r.start, r.step, length(r)) .+(x::Real, r::FloatRange) = FloatRange(r.divisor*x + r.start, r.step, r.len, r.divisor) @@ -651,7 +678,7 @@ function .-(x::Real, r::LinSpace) x2 = x * r.divisor / (r.len - 1) LinSpace(x2 - r.start, x2 - r.stop, r.len, r.divisor) end -.-(r::UnitRange, x::Real) = range(r.start-x, length(r)) +.-(r::AbstractUnitRange, x::Real) = range(first(r)-x, length(r)) .-(r::StepRange , x::Real) = range(r.start-x, r.step, length(r)) .-(r::FloatRange, x::Real) = FloatRange(r.start - r.divisor*x, r.step, r.len, r.divisor) function .-(r::LinSpace, x::Real) @@ -659,14 +686,14 @@ function .-(r::LinSpace, x::Real) LinSpace(r.start - x2, r.stop - x2, r.len, r.divisor) end -.*(x::Real, r::OrdinalRange) = range(x*r.start, x*step(r), length(r)) +.*(x::Real, r::OrdinalRange) = range(x*first(r), x*step(r), length(r)) .*(x::Real, r::FloatRange) = FloatRange(x*r.start, x*r.step, r.len, r.divisor) .*(x::Real, r::LinSpace) = LinSpace(x * r.start, x * r.stop, r.len, r.divisor) .*(r::Range, x::Real) = x .* r .*(r::FloatRange, x::Real) = x .* r .*(r::LinSpace, x::Real) = x .* r -./(r::OrdinalRange, x::Real) = range(r.start/x, step(r)/x, length(r)) +./(r::OrdinalRange, x::Real) = range(first(r)/x, step(r)/x, length(r)) ./(r::FloatRange, x::Real) = FloatRange(r.start/x, r.step/x, r.len, r.divisor) ./(r::LinSpace, x::Real) = LinSpace(r.start / x, r.stop / x, r.len, r.divisor) @@ -675,15 +702,24 @@ promote_rule{T1,T2}(::Type{UnitRange{T1}},::Type{UnitRange{T2}}) = convert{T<:Real}(::Type{UnitRange{T}}, r::UnitRange{T}) = r convert{T<:Real}(::Type{UnitRange{T}}, r::UnitRange) = UnitRange{T}(r.start, r.stop) +promote_rule{T1,T2}(::Type{OneTo{T1}},::Type{OneTo{T2}}) = + OneTo{promote_type(T1,T2)} +convert{T<:Real}(::Type{OneTo{T}}, r::OneTo{T}) = r +convert{T<:Real}(::Type{OneTo{T}}, r::OneTo) = OneTo{T}(r.stop) + +promote_rule{T1,UR<:AbstractUnitRange}(::Type{UnitRange{T1}}, ::Type{UR}) = + UnitRange{promote_type(T1,eltype(UR))} +convert{T<:Real}(::Type{UnitRange{T}}, r::AbstractUnitRange) = UnitRange{T}(first(r), last(r)) + promote_rule{T1a,T1b,T2a,T2b}(::Type{StepRange{T1a,T1b}},::Type{StepRange{T2a,T2b}}) = StepRange{promote_type(T1a,T2a),promote_type(T1b,T2b)} convert{T1,T2}(::Type{StepRange{T1,T2}}, r::StepRange{T1,T2}) = r -promote_rule{T1a,T1b,T2}(::Type{StepRange{T1a,T1b}},::Type{UnitRange{T2}}) = - StepRange{promote_type(T1a,T2),promote_type(T1b,T2)} +promote_rule{T1a,T1b,UR<:AbstractUnitRange}(::Type{StepRange{T1a,T1b}},::Type{UR}) = + StepRange{promote_type(T1a,eltype(UR)),promote_type(T1b,eltype(UR))} convert{T1,T2}(::Type{StepRange{T1,T2}}, r::Range) = StepRange{T1,T2}(convert(T1, first(r)), convert(T2, step(r)), convert(T1, last(r))) -convert{T}(::Type{StepRange}, r::UnitRange{T}) = +convert{T}(::Type{StepRange}, r::AbstractUnitRange{T}) = StepRange{T,T}(first(r), step(r), last(r)) promote_rule{T1,T2}(::Type{FloatRange{T1}},::Type{FloatRange{T2}}) = @@ -765,15 +801,15 @@ reverse(r::LinSpace) = LinSpace(r.stop, r.start, r.len, r.divisor) ## sorting ## -issorted(r::UnitRange) = true +issorted(r::AbstractUnitRange) = true issorted(r::Range) = step(r) >= zero(step(r)) -sort(r::UnitRange) = r -sort!(r::UnitRange) = r +sort(r::AbstractUnitRange) = r +sort!(r::AbstractUnitRange) = r sort(r::Range) = issorted(r) ? r : reverse(r) -sortperm(r::UnitRange) = 1:length(r) +sortperm(r::AbstractUnitRange) = 1:length(r) sortperm(r::Range) = issorted(r) ? (1:1:length(r)) : (length(r):-1:1) function sum{T<:Real}(r::Range{T}) @@ -806,6 +842,6 @@ function in(x, r::Range) n >= 1 && n <= length(r) && r[n] == x end -in{T<:Integer}(x::Integer, r::UnitRange{T}) = (first(r) <= x) & (x <= last(r)) +in{T<:Integer}(x::Integer, r::AbstractUnitRange{T}) = (first(r) <= x) & (x <= last(r)) in{T<:Integer}(x, r::Range{T}) = isinteger(x) && !isempty(r) && x>=minimum(r) && x<=maximum(r) && (mod(convert(T,x),step(r))-mod(first(r),step(r)) == 0) in(x::Char, r::Range{Char}) = !isempty(r) && x >= minimum(r) && x <= maximum(r) && (mod(Int(x) - Int(first(r)), step(r)) == 0) diff --git a/test/ranges.jl b/test/ranges.jl index bbe8a4ad931f4..d6c5a0787a95b 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -736,3 +736,33 @@ let r = 1:3, a = [1,2,3] @test convert(Array{Int,1}, r) == a @test convert(Array{Float64,1}, r) == a end + +# OneTo +r = Base.OneTo(-5) +@test isempty(r) +@test length(r) == 0 +@test size(r) == (0,) +r = Base.OneTo(3) +@test !isempty(r) +@test length(r) == 3 +@test size(r) == (3,) +@test step(r) == 1 +@test first(r) == 1 +@test last(r) == 3 +@test minimum(r) == 1 +@test maximum(r) == 3 +@test r[2] == 2 +@test_throws BoundsError r[4] +@test_throws BoundsError r[0] +@test r+1 === 2:4 +@test 2*r === 2:2:6 +k = 0 +for i in r + @test i == (k+=1) +end +@test intersect(r, Base.OneTo(2)) == Base.OneTo(2) +@test intersect(r, 0:5) == 1:3 +io = IOBuffer() +show(io, r) +str = takebuf_string(io) +@test str == "Base.OneTo(3)" From ec078129cfd71952dd0d744bf1ab207bfe824182 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 27 Jun 2016 04:22:49 -0500 Subject: [PATCH 02/15] Iteration improvements for arrays with non-1 indices Better to have this logic in the main functions rather than in specific packages --- base/abstractarray.jl | 1 + base/multidimensional.jl | 2 +- test/offsetarray.jl | 2 -- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 490adf632428f..8919380ad7d86 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -628,6 +628,7 @@ next(A::AbstractArray,i) = (@_propagate_inbounds_meta; (idx, s) = next(i[1], i[2 done(A::AbstractArray,i) = (@_propagate_inbounds_meta; done(i[1], i[2])) # eachindex iterates over all indices. LinearSlow definitions are later. +eachindex(A::AbstractVector) = (@_inline_meta(); indices1(A)) eachindex(A::AbstractArray) = (@_inline_meta(); eachindex(linearindexing(A), A)) function eachindex(A::AbstractArray, B::AbstractArray) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 197a585ec5439..63fb01a5c9f74 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -83,7 +83,7 @@ CartesianRange{N}(rngs::NTuple{N,Union{Integer,AbstractUnitRange}}) = CartesianR ndims(R::CartesianRange) = length(R.start) ndims{I<:CartesianIndex}(::Type{CartesianRange{I}}) = length(I) -eachindex(::LinearSlow, A::AbstractArray) = CartesianRange(size(A)) +eachindex(::LinearSlow, A::AbstractArray) = CartesianRange(indices(A)) @inline eachindex(::LinearSlow, A::AbstractArray, B::AbstractArray...) = CartesianRange(maxsize((), A, B...)) maxsize(sz) = sz diff --git a/test/offsetarray.jl b/test/offsetarray.jl index 790edde60d350..853186ba46730 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -30,8 +30,6 @@ parenttype(A::OffsetArray) = parenttype(typeof(A)) Base.parent(A::OffsetArray) = A.parent Base.size(A::OffsetArray) = size(parent(A)) -Base.eachindex(::LinearSlow, A::OffsetArray) = CartesianRange(indices(A)) -Base.eachindex(::LinearFast, A::OffsetVector) = indices(A, 1) Base.summary(A::OffsetArray) = string(typeof(A))*" with indices "*string(indices(A)) # Implementations of indices and indices1. Since bounds-checking is From c318a9f437b9a0c1cf3c12d9c3caac2cd954cbf9 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 27 Jun 2016 04:33:53 -0500 Subject: [PATCH 03/15] Replace shape with indices, and redesign similar --- base/abstractarray.jl | 57 +++++++++++++++------------------------- base/bitarray.jl | 2 ++ base/broadcast.jl | 12 ++++----- base/essentials.jl | 2 +- base/exports.jl | 2 +- base/multidimensional.jl | 23 +++++++--------- base/number.jl | 2 +- base/operators.jl | 2 +- base/reshapedarray.jl | 2 -- base/sort.jl | 10 +++---- base/subarray.jl | 19 ++++++++------ test/offsetarray.jl | 8 +++--- 12 files changed, 62 insertions(+), 79 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 8919380ad7d86..e690be6d0733c 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -162,29 +162,6 @@ immutable IndicesSlow1D <: IndicesPerformance end # indices(A) is better than i indicesperformance(A::AbstractArray) = indicesperformance(typeof(A)) indicesperformance{T<:AbstractArray}(::Type{T}) = IndicesFast1D() -""" - shape(A) - -Returns a tuple specifying the "shape" of array `A`. For arrays with -conventional indexing (indices start at 1), this is equivalent to -`size(A)`; otherwise it is equivalent to `indices(A)`. -""" -shape(a) = shape(indicesbehavior(a), a) -""" - shape(A, d) - -Specifies the "shape" of the array `A` along dimension `d`. For arrays -with conventional indexing (starting at 1), this is equivalent to -`size(A, d)`; for arrays with unconventional indexing (indexing may -start at something different from 1), it is equivalent to `indices(A, -d)`. -""" -shape(a, d) = shape(indicesbehavior(a), a, d) -shape(::IndicesStartAt1, a) = size(a) -shape(::IndicesStartAt1, a, d) = size(a, d) -shape(::IndicesBehavior, a) = indices(a) -shape(::IndicesBehavior, a, d) = indices(a, d) - ## Bounds checking ## @generated function trailingsize{T,N,n}(A::AbstractArray{T,N}, ::Type{Val{n}}) (isa(n, Int) && isa(N, Int)) || error("Must have concrete type") @@ -352,14 +329,22 @@ different element type it will create a regular `Array` instead: See also `allocate_for`. """ similar{T}(a::AbstractArray{T}) = similar(a, T) -similar( a::AbstractArray, T::Type) = similar(a, T, indices(a)) -similar{T}(a::AbstractArray{T}, dims::Tuple) = similar(a, T, dims) -similar{T}(a::AbstractArray{T}, dims::DimOrInd...) = similar(a, T, dims) -similar( a::AbstractArray, T::Type, dims::DimOrInd...) = similar(a, T, dims) -similar( a::AbstractArray, T::Type, dims::DimsInteger) = similar(a, T, Dims(dims)) +similar( a::AbstractArray, T::Type) = similar(a, T, to_shape(indices(a))) +similar{T}(a::AbstractArray{T}, dims::Tuple) = similar(a, T, to_shape(dims)) +similar{T}(a::AbstractArray{T}, dims::DimOrInd...) = similar(a, T, to_shape(dims)) +similar( a::AbstractArray, T::Type, dims::DimOrInd...) = similar(a, T, to_shape(dims)) +similar( a::AbstractArray, T::Type, dims) = similar(a, T, to_shape(dims)) # similar creates an Array by default -similar( a::AbstractArray, T::Type, inds::IndicesOne) = similar(a, T, map(last, inds)) -similar( a::AbstractArray, T::Type, dims::Dims) = Array(T, dims) +similar( a::AbstractArray, T::Type, dims::Dims) = Array{T}(dims) + +to_shape(::Tuple{}) = () +to_shape(dims::Dims) = dims +to_shape(dims::DimsOrInds) = map(to_shape, dims) +# each dimension +to_shape(i::Int) = i +to_shape(i::Integer) = Int(i) +to_shape(r::OneTo) = Int(last(r)) +to_shape(r::UnitRange) = convert(UnitRange{Int}, r) """ allocate_for(storagetype, referencearray, [shape]) @@ -381,7 +366,7 @@ conventional indexing, this will likely just call `Array{Int}(size(A))`, but if `A` has unconventional indexing then the indices of the result will match `A`. - allocate_for(BitArray, A, (shape(A, 2),)) + allocate_for(BitArray, A, (indices(A, 2),)) would create a 1-dimensional logical array whose indices match those of the columns of `A`. @@ -392,10 +377,10 @@ possible that several different ones will be simultaneously in use). See also `similar`. """ -allocate_for(f, a, shape::Union{SimIdx,Tuple{Vararg{SimIdx}}}) = f(shape) -allocate_for(f, a) = allocate_for(f, a, shape(a)) +allocate_for(f, a, shape::Union{DimOrInd,DimsOrInds}) = f(to_shape(shape)) +allocate_for(f, a) = allocate_for(f, a, indices(a)) # allocate_for when passed multiple arrays. Necessary for broadcast, etc. -function allocate_for(f, as::Tuple, shape::Union{SimIdx,Tuple{Vararg{SimIdx}}}) +function allocate_for(f, as::Tuple, shape::Union{DimOrInd,DimsOrInds}) @_inline_meta a = promote_indices(as...) allocate_for(f, a, shape) @@ -1406,7 +1391,7 @@ function mapslices(f, A::AbstractArray, dims::AbstractVector) return map(f,A) end - dimsA = [shape(A)...] + dimsA = [indices(A)...] ndimsA = ndims(A) alldims = [1:ndimsA;] @@ -1431,7 +1416,7 @@ function mapslices(f, A::AbstractArray, dims::AbstractVector) if eltype(Rsize) == Int Rsize[dims] = [size(r1)..., ntuple(d->1, nextra)...] else - Rsize[dims] = [indices(r1)..., ntuple(d->1:1, nextra)...] + Rsize[dims] = [indices(r1)..., ntuple(d->OneTo(1), nextra)...] end R = similar(r1, tuple(Rsize...,)) diff --git a/base/bitarray.jl b/base/bitarray.jl index 946deb1b4fa3c..e0754de709666 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -47,6 +47,8 @@ end isassigned{N}(B::BitArray{N}, i::Int) = 1 <= i <= length(B) +linearindexing{A<:BitArray}(::Type{A}) = LinearFast() + ## aux functions ## const _msk64 = ~UInt64(0) diff --git a/base/broadcast.jl b/base/broadcast.jl index ab8fee3c97753..074a6dac2c22e 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -3,7 +3,7 @@ module Broadcast using Base.Cartesian -using Base: promote_op, promote_eltype, promote_eltype_op, @get!, _msk_end, unsafe_bitgetindex, shape, linearindices, allocate_for, tail, dimlength +using Base: promote_op, promote_eltype, promote_eltype_op, @get!, _msk_end, unsafe_bitgetindex, linearindices, allocate_for, tail, dimlength import Base: .+, .-, .*, ./, .\, .//, .==, .<, .!=, .<=, .÷, .%, .<<, .>>, .^ export broadcast, broadcast!, bitbroadcast export broadcast_getindex, broadcast_setindex! @@ -13,8 +13,8 @@ export broadcast_getindex, broadcast_setindex! ## Calculate the broadcast shape of the arguments, or error if incompatible # array inputs broadcast_shape() = () -broadcast_shape(A) = shape(A) -@inline broadcast_shape(A, B...) = broadcast_shape((), shape(A), map(shape, B)...) +broadcast_shape(A) = indices(A) +@inline broadcast_shape(A, B...) = broadcast_shape((), indices(A), map(indices, B)...) # shape inputs broadcast_shape(shape::Tuple) = shape @inline broadcast_shape(shape::Tuple, shape1::Tuple, shapes::Tuple...) = broadcast_shape(_bcs((), shape, shape1), shapes...) @@ -40,7 +40,7 @@ _bcsm(a::Number, b::Number) = a == b || b == 1 ## Check that all arguments are broadcast compatible with shape # comparing one input against a shape check_broadcast_shape(shp) = nothing -check_broadcast_shape(shp, A) = check_broadcast_shape(shp, shape(A)) +check_broadcast_shape(shp, A) = check_broadcast_shape(shp, indices(A)) check_broadcast_shape(::Tuple{}, ::Tuple{}) = nothing check_broadcast_shape(shp, ::Tuple{}) = nothing check_broadcast_shape(::Tuple{}, Ashp::Tuple) = throw(DimensionMismatch("cannot broadcast array to have fewer dimensions")) @@ -133,7 +133,7 @@ end end @inline function broadcast!{nargs}(f, B::AbstractArray, As::Vararg{Any,nargs}) - check_broadcast_shape(shape(B), As...) + check_broadcast_shape(indices(B), As...) sz = size(B) mapindex = map(x->newindexer(sz, x), As) _broadcast!(f, B, mapindex, As, Val{nargs}) @@ -363,7 +363,7 @@ for (f, scalarf) in ((:.==, :(==)), :((A,ind)->A), :((B,ind)->B[ind])), (:AbstractArray, :Any, :A, :((A,ind)->A[ind]), :((B,ind)->B))) - shape = :(shape($active)) + shape = :(indices($active)) @eval begin function ($f)(A::$sigA, B::$sigB) P = allocate_for(BitArray, $active, $shape) diff --git a/base/essentials.jl b/base/essentials.jl index bdc95b133b7f4..6635d99670c0b 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -171,7 +171,7 @@ start(v::SimpleVector) = 1 next(v::SimpleVector,i) = (v[i],i+1) done(v::SimpleVector,i) = (i > v.length) isempty(v::SimpleVector) = (v.length == 0) -indices(v::SimpleVector, d) = d == 1 ? (1:length(v)) : (1:1) +indices(v::SimpleVector, d) = d == 1 ? OneTo(length(v)) : OneTo(1) linearindices(v::SimpleVector) = indices(v, 1) function ==(v1::SimpleVector, v2::SimpleVector) diff --git a/base/exports.jl b/base/exports.jl index 000d2b4a96d2f..acc1597849c98 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -28,6 +28,7 @@ export # Types AbstractChannel, AbstractMatrix, + AbstractUnitRange, AbstractVector, AbstractVecOrMat, Array, @@ -583,7 +584,6 @@ export searchsortedlast, select!, select, - shape, shuffle, shuffle!, size, diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 63fb01a5c9f74..3346d5983dd07 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -10,9 +10,6 @@ using Base: LinearFast, LinearSlow, AbstractCartesianIndex, fill_to_length, tail export CartesianIndex, CartesianRange -# Traits for linear indexing -linearindexing{A<:BitArray}(::Type{A}) = LinearFast() - # CartesianIndex immutable CartesianIndex{N} <: AbstractCartesianIndex{N} I::NTuple{N,Int} @@ -178,18 +175,18 @@ index_lengths_dim(A, dim, ::Colon) = (trailingsize(A, dim),) # whose length is equal to the dimension we're to process next. This # allows us to dispatch, which is important for the type-stability of # the lines involving Colon as the final index. -index_shape(A::AbstractVector, I::Colon) = shape(A) +index_shape(A::AbstractVector, I::Colon) = indices(A) index_shape(A::AbstractArray, I::Colon) = (length(A),) @inline index_shape(A::AbstractArray, I...) = index_shape_dim(A, (true,), I...) @inline index_shape_dim(A, dim, ::Colon) = (trailingsize(A, length(dim)),) -@inline index_shape_dim{T,N}(A::AbstractArray{T,N}, dim::NTuple{N}, ::Colon) = (shape(A, N),) +@inline index_shape_dim{T,N}(A::AbstractArray{T,N}, dim::NTuple{N}, ::Colon) = (indices(A, N),) @inline index_shape_dim(A, dim, I::Real...) = () -@inline index_shape_dim(A, dim, ::Colon, i, I...) = (shape(A, length(dim)), index_shape_dim(A, (dim...,true), i, I...)...) +@inline index_shape_dim(A, dim, ::Colon, i, I...) = (indices(A, length(dim)), index_shape_dim(A, (dim...,true), i, I...)...) @inline index_shape_dim(A, dim, ::Real, I...) = (index_shape_dim(A, (dim...,true), I...)...) @inline index_shape_dim{N}(A, dim, ::CartesianIndex{N}, I...) = (index_shape_dim(A, (dim...,ntuple(d->true,Val{N})...), I...)...) -@inline index_shape_dim(A, dim, i::AbstractArray, I...) = (shape(i)..., index_shape_dim(A, (dim...,true), I...)...) +@inline index_shape_dim(A, dim, i::AbstractArray, I...) = (indices(i)..., index_shape_dim(A, (dim...,true), I...)...) @inline index_shape_dim(A, dim, i::AbstractArray{Bool}, I...) = (sum(i), index_shape_dim(A, (dim...,true), I...)...) -@inline index_shape_dim{N}(A, dim, i::AbstractArray{CartesianIndex{N}}, I...) = (shape(i)..., index_shape_dim(A, (dim...,ntuple(d->true,Val{N})...), I...)...) +@inline index_shape_dim{N}(A, dim, i::AbstractArray{CartesianIndex{N}}, I...) = (indices(i)..., index_shape_dim(A, (dim...,ntuple(d->true,Val{N})...), I...)...) @inline decolon(A::AbstractVector, ::Colon) = (indices(A,1),) @inline decolon(A::AbstractArray, ::Colon) = (1:length(A),) @@ -291,8 +288,6 @@ end end end -dimlength(r::Range) = length(r) -dimlength(i::Integer) = i @noinline throw_checksize_error(A, sz) = throw(DimensionMismatch("output array is the wrong size; expected $sz, got $(size(A))")) ## setindex! ## @@ -787,7 +782,7 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`. @generated function unique{T,N}(A::AbstractArray{T,N}, dim::Int) quote 1 <= dim <= $N || return copy(A) - hashes = allocate_for(inds->zeros(UInt, inds), A, shape(A, dim)) + hashes = allocate_for(inds->zeros(UInt, inds), A, indices(A, dim)) # Compute hash for each row k = 0 @@ -796,7 +791,7 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`. end # Collect index of first row for each hash - uniquerow = allocate_for(Array{Int}, A, shape(A, dim)) + uniquerow = allocate_for(Array{Int}, A, indices(A, dim)) firstrow = Dict{Prehashed,Int}() for k = indices(A, dim) uniquerow[k] = get!(firstrow, Prehashed(hashes[k]), k) @@ -804,7 +799,7 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`. uniquerows = collect(values(firstrow)) # Check for collisions - collided = allocate_for(falses, A, shape(A, dim)) + collided = allocate_for(falses, A, indices(A, dim)) @inbounds begin @nloops $N i A d->(if d == dim k = i_d @@ -819,7 +814,7 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`. end if any(collided) - nowcollided = allocate_for(BitArray, A, shape(A, dim)) + nowcollided = allocate_for(BitArray, A, indices(A, dim)) while any(collided) # Collect index of first row for each collided hash empty!(firstrow) diff --git a/base/number.jl b/base/number.jl index 3f8cbfc37c610..d2d3270f1cfc4 100644 --- a/base/number.jl +++ b/base/number.jl @@ -7,7 +7,7 @@ isinteger(x::Integer) = true size(x::Number) = () size(x::Number,d) = convert(Int,d)<1 ? throw(BoundsError()) : 1 indices(x::Number) = () -indices(x::Number,d) = convert(Int,d)<1 ? throw(BoundsError()) : (1:1) +indices(x::Number,d) = convert(Int,d)<1 ? throw(BoundsError()) : OneTo(1) eltype{T<:Number}(::Type{T}) = T ndims(x::Number) = 0 ndims{T<:Number}(::Type{T}) = 0 diff --git a/base/operators.jl b/base/operators.jl index 6570a14e3e612..dab3177c82d77 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -387,7 +387,7 @@ function promote_shape(a::AbstractArray, b::AbstractArray) throw(DimensionMismatch("dimensions must match")) end end - return shape(a) + return indices(a) end function throw_setindex_mismatch(X, I) diff --git a/base/reshapedarray.jl b/base/reshapedarray.jl index a28a7fb2d4721..a8577b1d4bc84 100644 --- a/base/reshapedarray.jl +++ b/base/reshapedarray.jl @@ -87,8 +87,6 @@ size_strides(out::Tuple) = out @inline size_strides(out, s, sz...) = size_strides((out..., out[end]*s), sz...) size(A::ReshapedArray) = A.dims -size(A::ReshapedArray, d) = d <= ndims(A) ? A.dims[d] : 1 -similar(A::ReshapedArray, eltype::Type) = similar(parent(A), eltype, size(A)) similar(A::ReshapedArray, eltype::Type, dims::Dims) = similar(parent(A), eltype, dims) linearindexing{R<:ReshapedArrayLF}(::Type{R}) = LinearFast() parent(A::ReshapedArray) = A.parent diff --git a/base/sort.jl b/base/sort.jl index 9b2bd7ea4e724..e5aa1522374b1 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -2,7 +2,7 @@ module Sort -using Base: Order, copymutable, linearindices, allocate_for, shape, linearindexing, viewindexing, LinearFast +using Base: Order, copymutable, linearindices, allocate_for, linearindexing, viewindexing, LinearFast import Base.sort, @@ -447,7 +447,7 @@ function sortperm(v::AbstractVector; by=identity, rev::Bool=false, order::Ordering=Forward) - p = Base.allocate_for(Vector{Int}, v, shape(v, 1)) + p = Base.allocate_for(Vector{Int}, v, indices(v, 1)) for (i,ind) in zip(eachindex(p), indices(v, 1)) p[i] = ind end @@ -507,7 +507,7 @@ end function sortrows(A::AbstractMatrix; kws...) inds = indices(A,1) T = slicetypeof(A, inds, :) - rows = allocate_for(Vector{T}, A, shape(A, 1)) + rows = allocate_for(Vector{T}, A, indices(A, 1)) for i in inds rows[i] = view(A, i, :) end @@ -518,7 +518,7 @@ end function sortcols(A::AbstractMatrix; kws...) inds = indices(A,2) T = slicetypeof(A, :, inds) - cols = allocate_for(Vector{T}, A, shape(A, 2)) + cols = allocate_for(Vector{T}, A, indices(A, 2)) for i in inds cols[i] = view(A, :, i) end @@ -532,7 +532,7 @@ function slicetypeof{T,N}(A::AbstractArray{T,N}, i1, i2) SubArray{T,1,typeof(A),typeof(I),fast} end slice_dummy(::Colon) = Colon() -slice_dummy{T}(::UnitRange{T}) = one(T) +slice_dummy{T}(::AbstractUnitRange{T}) = one(T) ## fast clever sorting for floats ## diff --git a/base/subarray.jl b/base/subarray.jl index adf7807b321ec..81ffd7a9d0a05 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -18,7 +18,7 @@ immutable SubArray{T,N,P,I,L} <: AbstractArray{T,N} end # Compute the linear indexability of the indices, and combine it with the linear indexing of the parent function SubArray(parent::AbstractArray, indexes::Tuple, dims::Tuple) - SubArray(linearindexing(viewindexing(indexes), linearindexing(parent)), parent, indexes, convert(Dims, dims)) + SubArray(linearindexing(viewindexing(indexes), linearindexing(parent)), parent, indexes, map(dimlength, dims)) end function SubArray{P, I, N}(::LinearSlow, parent::P, indexes::I, dims::NTuple{N, Int}) SubArray{eltype(P), N, P, I, false}(parent, indexes, dims, 0, 0) @@ -47,6 +47,9 @@ viewindexing(I::Tuple{Vararg{Any}}) = LinearSlow() # Of course, all other array types are slow viewindexing(I::Tuple{AbstractArray, Vararg{Any}}) = LinearSlow() +dimlength(r::Range) = length(r) +dimlength(i::Integer) = i + # Simple utilities size(V::SubArray) = V.dims length(V::SubArray) = prod(V.dims) @@ -57,7 +60,7 @@ parent(V::SubArray) = V.parent parentindexes(V::SubArray) = V.indexes parent(a::AbstractArray) = a -parentindexes(a::AbstractArray) = ntuple(i->1:size(a,i), ndims(a)) +parentindexes(a::AbstractArray) = ntuple(i->OneTo(size(a,i)), ndims(a)) ## SubArray creation # Drops singleton dimensions (those indexed with a scalar) @@ -273,19 +276,19 @@ end # they are taken from the range/vector # Since bounds-checking is performance-critical and uses # indices, it's worth optimizing these implementations thoroughly -indices(S::SubArray, d::Integer) = 1 <= d <= ndims(S) ? indices(S)[d] : (d > ndims(S) ? (1:1) : error("dimension $d out of range")) +indices(S::SubArray, d::Integer) = 1 <= d <= ndims(S) ? indices(S)[d] : (d > ndims(S) ? OneTo(1) : error("dimension $d out of range")) indices(S::SubArray) = (@_inline_meta; _indices(indicesbehavior(parent(S)), S)) -_indices(::IndicesStartAt1, S::SubArray) = (@_inline_meta; map(s->1:s, size(S))) +_indices(::IndicesStartAt1, S::SubArray) = (@_inline_meta; map(s->OneTo(s), size(S))) _indices(::IndicesBehavior, S::SubArray) = (@_inline_meta; _indices((), 1, S, S.indexes...)) _indices(out::Tuple, dim, S::SubArray) = out -_indices(out::Tuple, dim, S::SubArray, i1, I...) = (@_inline_meta; _indices((out..., 1:length(i1)), dim+1, S, I...)) +_indices(out::Tuple, dim, S::SubArray, i1, I...) = (@_inline_meta; _indices((out..., OneTo(length(i1))), dim+1, S, I...)) _indices(out::Tuple, dim, S::SubArray, ::Real, I...) = (@_inline_meta; _indices(out, dim+1, S, I...)) _indices(out::Tuple, dim, S::SubArray, ::Colon, I...) = (@_inline_meta; _indices((out..., indices(parent(S), dim)), dim+1, S, I...)) -indices1{T}(S::SubArray{T,0}) = 1:1 +indices1{T}(S::SubArray{T,0}) = OneTo(1) indices1(S::SubArray) = (@_inline_meta; _indices1(indicesbehavior(parent(S)), S)) -_indices1(::IndicesStartAt1, S::SubArray) = 1:S.dims[1] +_indices1(::IndicesStartAt1, S::SubArray) = OneTo(S.dims[1]) _indices1(::IndicesBehavior, S::SubArray) = (@_inline_meta; _indices1(S, 1, S.indexes...)) -_indices1(S::SubArray, dim, i1, I...) = (@_inline_meta; 1:length(i1)) +_indices1(S::SubArray, dim, i1, I...) = (@_inline_meta; OneTo(length(i1))) _indices1(S::SubArray, dim, i1::Real, I...) = (@_inline_meta; _indices1(S, dim+1, I...)) _indices1(S::SubArray, dim, i1::Colon, I...) = (@_inline_meta; indices(parent(S), dim)) diff --git a/test/offsetarray.jl b/test/offsetarray.jl index 853186ba46730..4d1c1cdb7ee4f 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -7,7 +7,7 @@ module OAs -using Base: SimIdx, Indices, LinearSlow, LinearFast +using Base: DimOrInd, Indices, LinearSlow, LinearFast export OffsetArray @@ -46,13 +46,13 @@ Base.indices1{T}(A::OffsetArray{T,0}) = 1:1 function Base.similar(A::OffsetArray, T::Type, dims::Dims) B = similar(parent(A), T, dims) end -function Base.similar(A::AbstractArray, T::Type, inds::Tuple{Vararg{SimIdx}}) +function Base.similar(A::AbstractArray, T::Type, inds::Tuple{Vararg{DimOrInd}}) B = similar(A, T, map(Base.dimlength, inds)) OffsetArray(B, map(indsoffset, inds)) end -Base.allocate_for(f, A::OffsetArray, shape::SimIdx) = OffsetArray(f(Base.dimlength(shape)), (indsoffset(shape),)) -Base.allocate_for(f, A::OffsetArray, shape::Tuple{Vararg{SimIdx}}) = OffsetArray(f(map(Base.dimlength, shape)), map(indsoffset, shape)) +Base.allocate_for(f, A::OffsetArray, shape::DimOrInd) = OffsetArray(f(Base.dimlength(shape)), (indsoffset(shape),)) +Base.allocate_for(f, A::OffsetArray, shape::Tuple{Vararg{DimOrInd}}) = OffsetArray(f(map(Base.dimlength, shape)), map(indsoffset, shape)) Base.promote_indices(a::OffsetArray, b::OffsetArray) = a Base.reshape(A::AbstractArray, inds::Indices) = OffsetArray(reshape(A, map(Base.dimlength, inds)), map(indsoffset, inds)) From 15a522d7b8bdb32f5ee048878f086f1d597f67e3 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sun, 26 Jun 2016 17:41:06 -0500 Subject: [PATCH 04/15] Update broadcast and its callers to the new indices behavior --- base/broadcast.jl | 44 +++++++++++++++++++------------------ base/deprecated.jl | 6 +++++ base/operators.jl | 10 ++++----- base/sparse/sparse.jl | 2 +- base/sparse/sparsematrix.jl | 26 +++++++++++----------- test/broadcast.jl | 33 ++++++++++++++-------------- 6 files changed, 64 insertions(+), 57 deletions(-) diff --git a/base/broadcast.jl b/base/broadcast.jl index 074a6dac2c22e..4bab04952d452 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -3,7 +3,7 @@ module Broadcast using Base.Cartesian -using Base: promote_op, promote_eltype, promote_eltype_op, @get!, _msk_end, unsafe_bitgetindex, linearindices, allocate_for, tail, dimlength +using Base: promote_op, promote_eltype, promote_eltype_op, @get!, _msk_end, unsafe_bitgetindex, linearindices, to_shape, allocate_for, tail, dimlength, OneTo import Base: .+, .-, .*, ./, .\, .//, .==, .<, .!=, .<=, .÷, .%, .<<, .>>, .^ export broadcast, broadcast!, bitbroadcast export broadcast_getindex, broadcast_setindex! @@ -39,6 +39,8 @@ _bcsm(a::Number, b::Number) = a == b || b == 1 ## Check that all arguments are broadcast compatible with shape ## Check that all arguments are broadcast compatible with shape # comparing one input against a shape +check_broadcast_shape(::Tuple{}) = nothing +check_broadcast_shape(::Tuple{}, A::Union{AbstractArray,Number}) = check_broadcast_shape((), indices(A)) check_broadcast_shape(shp) = nothing check_broadcast_shape(shp, A) = check_broadcast_shape(shp, indices(A)) check_broadcast_shape(::Tuple{}, ::Tuple{}) = nothing @@ -217,13 +219,13 @@ end @inline bitbroadcast(f, As...) = broadcast!(f, allocate_for(BitArray, As, broadcast_shape(As...)), As...) -broadcast_getindex(src::AbstractArray, I::AbstractArray...) = broadcast_getindex!(Array{eltype(src)}(broadcast_shape(I...)), src, I...) +broadcast_getindex(src::AbstractArray, I::AbstractArray...) = broadcast_getindex!(Array{eltype(src)}(to_shape(broadcast_shape(I...))), src, I...) @generated function broadcast_getindex!(dest::AbstractArray, src::AbstractArray, I::AbstractArray...) N = length(I) Isplat = Expr[:(I[$d]) for d = 1:N] quote @nexprs $N d->(I_d = I[d]) - check_broadcast_shape(size(dest), $(Isplat...)) # unnecessary if this function is never called directly + check_broadcast_shape(indices(dest), $(Isplat...)) # unnecessary if this function is never called directly checkbounds(src, $(Isplat...)) @nloops $N i dest d->(@nexprs $N k->(j_d_k = size(I_k, d) == 1 ? 1 : i_d)) begin @nexprs $N k->(@inbounds J_k = @nref $N I_k d->j_d_k) @@ -240,22 +242,22 @@ end @nexprs $N d->(I_d = I[d]) checkbounds(A, $(Isplat...)) shape = broadcast_shape($(Isplat...)) - @nextract $N shape d->(length(shape) < d ? 1 : shape[d]) + @nextract $N shape d->(length(shape) < d ? OneTo(1) : shape[d]) if !isa(x, AbstractArray) - @nloops $N i d->(1:shape_d) d->(@nexprs $N k->(j_d_k = size(I_k, d) == 1 ? 1 : i_d)) begin + xA = convert(eltype(A), x) + @nloops $N i d->shape_d d->(@nexprs $N k->(j_d_k = size(I_k, d) == 1 ? 1 : i_d)) begin @nexprs $N k->(@inbounds J_k = @nref $N I_k d->j_d_k) - @inbounds (@nref $N A J) = x + @inbounds (@nref $N A J) = xA end else X = x - # To call setindex_shape_check, we need to create fake 1-d indexes of the proper size - @nexprs $N d->(fakeI_d = 1:shape_d) - @ncall $N Base.setindex_shape_check X shape - k = 1 - @nloops $N i d->(1:shape_d) d->(@nexprs $N k->(j_d_k = size(I_k, d) == 1 ? 1 : i_d)) begin - @nexprs $N k->(@inbounds J_k = @nref $N I_k d->j_d_k) - @inbounds (@nref $N A J) = X[k] - k += 1 + @nexprs $N d->(shapelen_d = dimlength(shape_d)) + @ncall $N Base.setindex_shape_check X shapelen + Xstate = start(X) + @inbounds @nloops $N i d->shape_d d->(@nexprs $N k->(j_d_k = size(I_k, d) == 1 ? 1 : i_d)) begin + @nexprs $N k->(J_k = @nref $N I_k d->j_d_k) + x_el, Xstate = next(X, Xstate) + (@nref $N A J) = x_el end end A @@ -271,22 +273,22 @@ end eltype_plus(As::AbstractArray...) = promote_eltype_op(+, As...) -.+(As::AbstractArray...) = broadcast!(+, Array{eltype_plus(As...)}(broadcast_shape(As...)), As...) +.+(As::AbstractArray...) = broadcast!(+, Array{eltype_plus(As...)}(to_shape(broadcast_shape(As...))), As...) function .-(A::AbstractArray, B::AbstractArray) - broadcast!(-, Array{promote_op(-, eltype(A), eltype(B))}(broadcast_shape(A,B)), A, B) + broadcast!(-, Array{promote_op(-, eltype(A), eltype(B))}(to_shape(broadcast_shape(A,B))), A, B) end eltype_mul(As::AbstractArray...) = promote_eltype_op(*, As...) -.*(As::AbstractArray...) = broadcast!(*, Array{eltype_mul(As...)}(broadcast_shape(As...)), As...) +.*(As::AbstractArray...) = broadcast!(*, Array{eltype_mul(As...)}(to_shape(broadcast_shape(As...))), As...) function ./(A::AbstractArray, B::AbstractArray) - broadcast!(/, Array{promote_op(/, eltype(A), eltype(B))}(broadcast_shape(A, B)), A, B) + broadcast!(/, Array{promote_op(/, eltype(A), eltype(B))}(to_shape(broadcast_shape(A, B))), A, B) end function .\(A::AbstractArray, B::AbstractArray) - broadcast!(\, Array{promote_op(\, eltype(A), eltype(B))}(broadcast_shape(A, B)), A, B) + broadcast!(\, Array{promote_op(\, eltype(A), eltype(B))}(to_shape(broadcast_shape(A, B))), A, B) end typealias RatIntT{T<:Integer} Union{Type{Rational{T}},Type{T}} @@ -296,11 +298,11 @@ type_rdiv{T<:Integer,S<:Integer}(::RatIntT{T}, ::RatIntT{S}) = type_rdiv{T<:Integer,S<:Integer}(::CRatIntT{T}, ::CRatIntT{S}) = Complex{Rational{promote_type(T,S)}} function .//(A::AbstractArray, B::AbstractArray) - broadcast!(//, Array{type_rdiv(eltype(A), eltype(B))}(broadcast_shape(A, B)), A, B) + broadcast!(//, Array{type_rdiv(eltype(A), eltype(B))}(to_shape(broadcast_shape(A, B))), A, B) end function .^(A::AbstractArray, B::AbstractArray) - broadcast!(^, Array{promote_op(^, eltype(A), eltype(B))}(broadcast_shape(A, B)), A, B) + broadcast!(^, Array{promote_op(^, eltype(A), eltype(B))}(to_shape(broadcast_shape(A, B))), A, B) end # ## element-wise comparison operators returning BitArray ## diff --git a/base/deprecated.jl b/base/deprecated.jl index b22c1a3cc6523..f1051985b1f30 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -766,6 +766,12 @@ function first(::Colon) 1 end +# Not exported, but may be useful just in case +function Broadcast.check_broadcast_shape(sz::Dims, As::Union{AbstractArray,Number}...) + depwarn("check_broadcast_shape(size(A), B...) should be replaced with check_broadcast_shape(indices(A), B...)", :check_broadcast_shape) + Broadcast.check_broadcast_shape(map(OneTo, sz), As...) +end + @deprecate slice view @deprecate sub view diff --git a/base/operators.jl b/base/operators.jl index dab3177c82d77..ae9ccd639d525 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -403,7 +403,7 @@ end # for permutations that leave array elements in the same linear order. # those are the permutations that preserve the order of the non-singleton # dimensions. -function setindex_shape_check(X::AbstractArray, I...) +function setindex_shape_check(X::AbstractArray, I::Integer...) li = ndims(X) lj = length(I) i = j = 1 @@ -440,16 +440,16 @@ end setindex_shape_check(X::AbstractArray) = (length(X)==1 || throw_setindex_mismatch(X,())) -setindex_shape_check(X::AbstractArray, i) = +setindex_shape_check(X::AbstractArray, i::Integer) = (length(X)==i || throw_setindex_mismatch(X, (i,))) -setindex_shape_check{T}(X::AbstractArray{T,1}, i) = +setindex_shape_check{T}(X::AbstractArray{T,1}, i::Integer) = (length(X)==i || throw_setindex_mismatch(X, (i,))) -setindex_shape_check{T}(X::AbstractArray{T,1}, i, j) = +setindex_shape_check{T}(X::AbstractArray{T,1}, i::Integer, j::Integer) = (length(X)==i*j || throw_setindex_mismatch(X, (i,j))) -function setindex_shape_check{T}(X::AbstractArray{T,2}, i, j) +function setindex_shape_check{T}(X::AbstractArray{T,2}, i::Integer, j::Integer) if length(X) != i*j throw_setindex_mismatch(X, (i,j)) end diff --git a/base/sparse/sparse.jl b/base/sparse/sparse.jl index b09d036e6d8b1..7a7a5a82fc905 100644 --- a/base/sparse/sparse.jl +++ b/base/sparse/sparse.jl @@ -2,7 +2,7 @@ module SparseArrays -using Base: ReshapedArray, setindex_shape_check +using Base: ReshapedArray, setindex_shape_check, to_shape using Base.Sort: Forward using Base.LinAlg: AbstractTriangular, PosDefException diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index a2f2391a2d6e5..9be5e09d73950 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -1413,8 +1413,8 @@ end function gen_broadcast_body_sparse(f::Function, is_first_sparse::Bool) F = Expr(:quote, f) quote - Base.Broadcast.check_broadcast_shape(size(B), A_1) - Base.Broadcast.check_broadcast_shape(size(B), A_2) + Base.Broadcast.check_broadcast_shape(indices(B), A_1) + Base.Broadcast.check_broadcast_shape(indices(B), A_2) colptrB = B.colptr; rowvalB = B.rowval; nzvalB = B.nzval colptr1 = A_1.colptr; rowval1 = A_1.rowval; nzval1 = A_1.nzval @@ -1577,8 +1577,8 @@ function gen_broadcast_body_zpreserving(f::Function, is_first_sparse::Bool) op2 = :(val1) end quote - Base.Broadcast.check_broadcast_shape(size(B), $A1) - Base.Broadcast.check_broadcast_shape(size(B), $A2) + Base.Broadcast.check_broadcast_shape(indices(B), $A1) + Base.Broadcast.check_broadcast_shape(indices(B), $A2) nnzB = isempty(B) ? 0 : nnz($A1) * div(B.n, ($A1).n) * div(B.m, ($A1).m) @@ -1647,16 +1647,16 @@ end broadcast{Tv1,Ti1,Tv2,Ti2}(f::Function, A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = - broadcast!(f, spzeros(promote_type(Tv1, Tv2), promote_type(Ti1, Ti2), broadcast_shape(A_1, A_2)...), A_1, A_2) + broadcast!(f, spzeros(promote_type(Tv1, Tv2), promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))...), A_1, A_2) broadcast_zpreserving!(args...) = broadcast!(args...) broadcast_zpreserving(args...) = broadcast(args...) broadcast_zpreserving{Tv1,Ti1,Tv2,Ti2}(f::Function, A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = - broadcast_zpreserving!(f, spzeros(promote_type(Tv1, Tv2), promote_type(Ti1, Ti2), broadcast_shape(A_1, A_2)...), A_1, A_2) + broadcast_zpreserving!(f, spzeros(promote_type(Tv1, Tv2), promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))...), A_1, A_2) broadcast_zpreserving{Tv,Ti}(f::Function, A_1::SparseMatrixCSC{Tv,Ti}, A_2::Union{Array,BitArray,Number}) = - broadcast_zpreserving!(f, spzeros(promote_eltype(A_1, A_2), Ti, broadcast_shape(A_1, A_2)...), A_1, A_2) + broadcast_zpreserving!(f, spzeros(promote_eltype(A_1, A_2), Ti, to_shape(broadcast_shape(A_1, A_2))...), A_1, A_2) broadcast_zpreserving{Tv,Ti}(f::Function, A_1::Union{Array,BitArray,Number}, A_2::SparseMatrixCSC{Tv,Ti}) = - broadcast_zpreserving!(f, spzeros(promote_eltype(A_1, A_2), Ti, broadcast_shape(A_1, A_2)...), A_1, A_2) + broadcast_zpreserving!(f, spzeros(promote_eltype(A_1, A_2), Ti, to_shape(broadcast_shape(A_1, A_2))...), A_1, A_2) ## Binary arithmetic and boolean operators @@ -1676,7 +1676,7 @@ for (op, pro) in ((+, :eltype_plus), throw(DimensionMismatch("")) end Tv = ($pro)(A_1, A_2) - B = spzeros(Tv, promote_type(Ti1, Ti2), broadcast_shape(A_1, A_2)...) + B = spzeros(Tv, promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))...) $body B end @@ -1718,15 +1718,15 @@ end # macro (.^)(A::Array, B::SparseMatrixCSC) = (.^)(A, full(B)) .+{Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = - broadcast!(+, spzeros(eltype_plus(A_1, A_2), promote_type(Ti1, Ti2), broadcast_shape(A_1, A_2)...), A_1, A_2) + broadcast!(+, spzeros(eltype_plus(A_1, A_2), promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))...), A_1, A_2) function .-{Tva,Tia,Tvb,Tib}(A::SparseMatrixCSC{Tva,Tia}, B::SparseMatrixCSC{Tvb,Tib}) - broadcast!(-, spzeros(eltype_plus(A, B), promote_type(Tia, Tib), broadcast_shape(A, B)...), A, B) + broadcast!(-, spzeros(eltype_plus(A, B), promote_type(Tia, Tib), to_shape(broadcast_shape(A, B))...), A, B) end ## element-wise comparison operators returning SparseMatrixCSC ## -.<{Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = broadcast!(<, spzeros( Bool, promote_type(Ti1, Ti2), broadcast_shape(A_1, A_2)...), A_1, A_2) -.!={Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = broadcast!(!=, spzeros( Bool, promote_type(Ti1, Ti2), broadcast_shape(A_1, A_2)...), A_1, A_2) +.<{Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = broadcast!(<, spzeros( Bool, promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))...), A_1, A_2) +.!={Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = broadcast!(!=, spzeros( Bool, promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))...), A_1, A_2) ## full equality function ==(A1::SparseMatrixCSC, A2::SparseMatrixCSC) diff --git a/test/broadcast.jl b/test/broadcast.jl index 0b43510974ae7..c17610c5914fa 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -3,7 +3,7 @@ module TestBroadcastInternals using Base.Broadcast: broadcast_shape, check_broadcast_shape, newindex, _bcs, _bcsm -using Base.Test +using Base: Test, OneTo @test @inferred(_bcs((), (3,5), (3,5))) == (3,5) @test @inferred(_bcs((), (3,1), (3,5))) == (3,5) @@ -18,21 +18,21 @@ using Base.Test @test_throws DimensionMismatch _bcs((), (-1:1, 2:6), (-1:1, 2:5)) @test_throws DimensionMismatch _bcs((), (-1:1, 2:5), (2, 2:5)) -@test @inferred(broadcast_shape(zeros(3,4), zeros(3,4))) == (3,4) -@test @inferred(broadcast_shape(zeros(3,4), zeros(3))) == (3,4) -@test @inferred(broadcast_shape(zeros(3), zeros(3,4))) == (3,4) -@test @inferred(broadcast_shape(zeros(3), zeros(1,4), zeros(1))) == (3,4) - -check_broadcast_shape((3,5), zeros(3,5)) -check_broadcast_shape((3,5), zeros(3,1)) -check_broadcast_shape((3,5), zeros(3)) -check_broadcast_shape((3,5), zeros(3,5), zeros(3)) -check_broadcast_shape((3,5), zeros(3,5), 1) -check_broadcast_shape((3,5), 5, 2) -@test_throws DimensionMismatch check_broadcast_shape((3,5), zeros(2,5)) -@test_throws DimensionMismatch check_broadcast_shape((3,5), zeros(3,4)) -@test_throws DimensionMismatch check_broadcast_shape((3,5), zeros(3,4,2)) -@test_throws DimensionMismatch check_broadcast_shape((3,5), zeros(3,5), zeros(2)) +@test @inferred(broadcast_shape(zeros(3,4), zeros(3,4))) == (OneTo(3),OneTo(4)) +@test @inferred(broadcast_shape(zeros(3,4), zeros(3))) == (OneTo(3),OneTo(4)) +@test @inferred(broadcast_shape(zeros(3), zeros(3,4))) == (OneTo(3),OneTo(4)) +@test @inferred(broadcast_shape(zeros(3), zeros(1,4), zeros(1))) == (OneTo(3),OneTo(4)) + +check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,5)) +check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,1)) +check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3)) +check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,5), zeros(3)) +check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,5), 1) +check_broadcast_shape((OneTo(3),OneTo(5)), 5, 2) +@test_throws DimensionMismatch check_broadcast_shape((OneTo(3),OneTo(5)), zeros(2,5)) +@test_throws DimensionMismatch check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,4)) +@test_throws DimensionMismatch check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,4,2)) +@test_throws DimensionMismatch check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,5), zeros(2)) check_broadcast_shape((-1:1, 6:9), (-1:1, 6:9)) check_broadcast_shape((-1:1, 6:9), (-1:1, 1)) @@ -40,7 +40,6 @@ check_broadcast_shape((-1:1, 6:9), (1, 6:9)) @test_throws DimensionMismatch check_broadcast_shape((-1:1, 6:9), (-1, 6:9)) @test_throws DimensionMismatch check_broadcast_shape((-1:1, 6:9), (-1:1, 6)) check_broadcast_shape((-1:1, 6:9), 1) -check_broadcast_shape((-1:1, 6:9), zeros(1,1)) ci(x) = CartesianIndex(x) @test @inferred(newindex(ci((2,2)), (true, true))) == ci((2,2)) From cac487d5dc2ca6d3293695dd134b3798bd876a0a Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sun, 26 Jun 2016 17:41:51 -0500 Subject: [PATCH 05/15] Updates for subarray for new indices --- base/abstractarray.jl | 5 ----- base/serialize.jl | 2 +- base/subarray.jl | 18 ++++++++++-------- test/subarray.jl | 15 +++++++++++---- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index e690be6d0733c..7ab8b0d271ac4 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1313,11 +1313,6 @@ _lookup(ind, r::AbstractUnitRange) = ind+first(r) _div(ind, d::Integer) = div(ind, d), 1, d _div(ind, r::AbstractUnitRange) = (d = unsafe_length(r); (div(ind, d), first(r), d)) -smart_ind2sub(shape::NTuple{1}, ind) = (ind,) -smart_ind2sub(shape, ind) = ind2sub(shape, ind) -smart_sub2ind(shape::NTuple{1}, i) = (i,) -smart_sub2ind(shape, I...) = (@_inline_meta; sub2ind(shape, I...)) - # Vectorized forms function sub2ind{N,T<:Integer}(inds::Union{Dims{N},Indices{N}}, I::AbstractVector{T}...) I1 = I[1] diff --git a/base/serialize.jl b/base/serialize.jl index adb0048310034..057dec4ed4590 100644 --- a/base/serialize.jl +++ b/base/serialize.jl @@ -235,7 +235,7 @@ _trimmedsubarray(A, V, newindexes, index::ViewIndex, indexes...) = _trimmedsubar trimmedindex(P, d, i::Real) = oftype(i, 1) trimmedindex(P, d, i::Colon) = i -trimmedindex(P, d, i::AbstractVector) = oftype(i, 1:length(i)) +trimmedindex(P, d, i::AbstractArray) = oftype(i, reshape(linearindices(i), indices(i))) function serialize{T<:AbstractString}(s::AbstractSerializer, ss::SubString{T}) # avoid saving a copy of the parent string, keeping the type of ss diff --git a/base/subarray.jl b/base/subarray.jl index 81ffd7a9d0a05..aa527607da513 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -48,7 +48,7 @@ viewindexing(I::Tuple{Vararg{Any}}) = LinearSlow() viewindexing(I::Tuple{AbstractArray, Vararg{Any}}) = LinearSlow() dimlength(r::Range) = length(r) -dimlength(i::Integer) = i +dimlength(i::Integer) = Int(i) # Simple utilities size(V::SubArray) = V.dims @@ -79,11 +79,17 @@ function view{N}(A::AbstractArray, I::Vararg{ViewIndex,N}) # TODO: DEPRECATE FOR @boundscheck checkbounds(A, I...) unsafe_view(reshape(A, Val{N}), I...) end + function unsafe_view{T,N}(A::AbstractArray{T,N}, I::Vararg{ViewIndex,N}) @_inline_meta J = to_indexes(I...) SubArray(A, J, map(dimlength, index_shape(A, J...))) end +function unsafe_view{T,N}(V::SubArray{T,N}, I::Vararg{ViewIndex,N}) + @_inline_meta + idxs = reindex(V, V.indexes, to_indexes(I...)) + SubArray(V.parent, idxs, map(dimlength, (index_shape(V.parent, idxs...)))) +end # Re-indexing is the heart of a view, transforming A[i, j][x, y] to A[i[x], j[y]] # @@ -169,12 +175,6 @@ function setindex!(V::FastContiguousSubArray, x, i::Real) V end -function unsafe_view{T,N}(V::SubArray{T,N}, I::Vararg{ViewIndex,N}) - @_inline_meta - idxs = reindex(V, V.indexes, to_indexes(I...)) - SubArray(V.parent, idxs, index_shape(V.parent, idxs...)) -end - linearindexing{T<:FastSubArray}(::Type{T}) = LinearFast() linearindexing{T<:SubArray}(::Type{T}) = LinearSlow() @@ -261,7 +261,9 @@ unsafe_convert{T,N,P,I<:Tuple{Vararg{RangeIndex}}}(::Type{Ptr{T}}, V::SubArray{T pointer(V::FastSubArray, i::Int) = pointer(V.parent, V.offset1 + V.stride1*i) pointer(V::FastContiguousSubArray, i::Int) = pointer(V.parent, V.offset1 + i) -pointer(V::SubArray, i::Int) = pointer(V, smart_ind2sub(shape(V), i)) +pointer(V::SubArray, i::Int) = _pointer(V, i) +_pointer{T}(V::SubArray{T,1}, i::Int) = pointer(V, (i,)) +_pointer(V::SubArray, i::Int) = pointer(V, ind2sub(indices(V), i)) function pointer{T,N,P<:Array,I<:Tuple{Vararg{RangeIndex}}}(V::SubArray{T,N,P,I}, is::Tuple{Vararg{Int}}) index = first_index(V) diff --git a/test/subarray.jl b/test/subarray.jl index 771e0c963f1e0..5181bf4f6671b 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -1,10 +1,6 @@ # This file is a part of Julia. License is MIT: http://julialang.org/license using Base.Test -# import Base: ViewIndex, dimsizeexpr, rangetype, merge_indexes, first_index, stride1expr, tailsize -using Base.Cartesian - -print_underestimates = false ######## Utilities ########### @@ -336,6 +332,7 @@ sA = view(A, 2:2, 1:5, :) @test parentindexes(sA) == (2:2, 1:5, :) @test Base.parentdims(sA) == [1:3;] @test size(sA) == (1, 5, 8) +@test indices(sA) === (Base.OneTo(1), Base.OneTo(5), Base.OneTo(8)) @test sA[1, 2, 1:8][:] == [5:15:120;] sA[2:5:end] = -1 @test all(sA[2:5:end] .== -1) @@ -355,6 +352,7 @@ test_bounds(sA) sA = view(A, 1:3, 3:3, 2:5) @test Base.parentdims(sA) == [1:3;] @test size(sA) == (3,1,4) +@test indices(sA) === (Base.OneTo(3), Base.OneTo(1), Base.OneTo(4)) @test sA == A[1:3,3:3,2:5] @test sA[:] == A[1:3,3,2:5][:] test_bounds(sA) @@ -367,6 +365,12 @@ sA = view(A, 1:2:3, 1:3:5, 1:2:8) # Test with mixed types @test sA[:, Int16[1,2], big(2)] == [31 40; 33 42] test_bounds(sA) +sA = view(A, 1:1, 1:5, [1 3; 4 2]) +@test ndims(sA) == 4 +@test indices(sA) === (Base.OneTo(1), Base.OneTo(5), Base.OneTo(2), Base.OneTo(2)) +sA = view(A, 1:2, 3, [1 3; 4 2]) +@test ndims(sA) == 3 +@test indices(sA) === (Base.OneTo(2), Base.OneTo(2), Base.OneTo(2)) # sub logical indexing #4763 A = view([1:10;], 5:8) @@ -384,6 +388,7 @@ sA = view(A, 2, :, 1:8) @test parentindexes(sA) == (2, :, 1:8) @test Base.parentdims(sA) == [2:3;] @test size(sA) == (5, 8) +@test indices(sA) === (Base.OneTo(5), Base.OneTo(8)) @test strides(sA) == (3,15) @test sA[2, 1:8][:] == [5:15:120;] @test sA[:,1] == [2:3:14;] @@ -395,11 +400,13 @@ test_bounds(sA) sA = view(A, 1:3, 1:5, 5) @test Base.parentdims(sA) == [1:2;] @test size(sA) == (3,5) +@test indices(sA) === (Base.OneTo(3),Base.OneTo(5)) @test strides(sA) == (1,3) test_bounds(sA) sA = view(A, 1:2:3, 3, 1:2:8) @test Base.parentdims(sA) == [1,3] @test size(sA) == (2,4) +@test indices(sA) === (Base.OneTo(2), Base.OneTo(4)) @test strides(sA) == (2,30) @test sA[:] == A[sA.indexes...][:] test_bounds(sA) From 1741ced1abb3a755248dfbfe07efd821392572e5 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 27 Jun 2016 04:12:33 -0500 Subject: [PATCH 06/15] Rename allocate_for -> similar. Fixes #17124. --- base/abstractarray.jl | 33 +++++++++++---------------------- base/broadcast.jl | 12 ++++++------ base/exports.jl | 1 - base/multidimensional.jl | 8 ++++---- base/sort.jl | 8 ++++---- test/offsetarray.jl | 13 ++++++------- 6 files changed, 31 insertions(+), 44 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 7ab8b0d271ac4..079d1995e1156 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -326,7 +326,6 @@ different element type it will create a regular `Array` instead: 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 -See also `allocate_for`. """ similar{T}(a::AbstractArray{T}) = similar(a, T) similar( a::AbstractArray, T::Type) = similar(a, T, to_shape(indices(a))) @@ -347,44 +346,34 @@ to_shape(r::OneTo) = Int(last(r)) to_shape(r::UnitRange) = convert(UnitRange{Int}, r) """ - allocate_for(storagetype, referencearray, [shape]) + similar(storagetype, indices) Create an uninitialized mutable array analogous to that specified by -`storagetype`, but with type and shape specified by the final two -arguments. The main purpose of this function is to support allocation -of arrays that may have unconventional indexing (starting at other -than 1), as determined by `referencearray` and the optional `shape` -information. +`storagetype`, but with `indices` specified by the last +argument. `storagetype` might be a type or a function. **Examples**: - allocate_for(Array{Int}, A) + similar(Array{Int}, indices(A)) creates an array that "acts like" an `Array{Int}` (and might indeed be backed by one), but which is indexed identically to `A`. If `A` has -conventional indexing, this will likely just call +conventional indexing, this will be identical to `Array{Int}(size(A))`, but if `A` has unconventional indexing then the indices of the result will match `A`. - allocate_for(BitArray, A, (indices(A, 2),)) + similar(BitArray, (indices(A, 2),)) would create a 1-dimensional logical array whose indices match those of the columns of `A`. -The main purpose of the `referencearray` argument is to select a -particular array type supporting unconventional indexing (as it is -possible that several different ones will be simultaneously in use). + similar(dims->zeros(Int, dims), indices(A)) -See also `similar`. +would create an array of `Int`, initialized to zero, matching the +indices of `A`. """ -allocate_for(f, a, shape::Union{DimOrInd,DimsOrInds}) = f(to_shape(shape)) -allocate_for(f, a) = allocate_for(f, a, indices(a)) -# allocate_for when passed multiple arrays. Necessary for broadcast, etc. -function allocate_for(f, as::Tuple, shape::Union{DimOrInd,DimsOrInds}) - @_inline_meta - a = promote_indices(as...) - allocate_for(f, a, shape) -end +similar(f, shape::Tuple) = f(to_shape(shape)) +similar(f, dims::DimOrInd...) = similar(f, dims) promote_indices(a) = a function promote_indices(a, b, c...) diff --git a/base/broadcast.jl b/base/broadcast.jl index 4bab04952d452..e671e414c326f 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -3,7 +3,7 @@ module Broadcast using Base.Cartesian -using Base: promote_op, promote_eltype, promote_eltype_op, @get!, _msk_end, unsafe_bitgetindex, linearindices, to_shape, allocate_for, tail, dimlength, OneTo +using Base: promote_op, promote_eltype, promote_eltype_op, @get!, _msk_end, unsafe_bitgetindex, linearindices, to_shape, tail, dimlength, OneTo import Base: .+, .-, .*, ./, .\, .//, .==, .<, .!=, .<=, .÷, .%, .<<, .>>, .^ export broadcast, broadcast!, bitbroadcast export broadcast_getindex, broadcast_setindex! @@ -181,7 +181,7 @@ function broadcast_t(f, ::Type{Any}, As...) shp = broadcast_shape(As...) iter = CartesianRange(shp) if isempty(iter) - return allocate_for(Array{Union{}}, As, shp) + return similar(Array{Union{}}, shp) end nargs = length(As) sz = size(iter) @@ -189,12 +189,12 @@ function broadcast_t(f, ::Type{Any}, As...) st = start(iter) I, st = next(iter, st) val = f([ As[i][newindex(I, indexmaps[i])] for i=1:nargs ]...) - B = allocate_for(Array{typeof(val)}, As, shp) + B = similar(Array{typeof(val)}, shp) B[I] = val return _broadcast!(f, B, indexmaps, As, Val{nargs}, iter, st, 1) end -@inline broadcast_t(f, T, As...) = broadcast!(f, allocate_for(Array{T}, As, broadcast_shape(As...)), As...) +@inline broadcast_t(f, T, As...) = broadcast!(f, similar(Array{T}, broadcast_shape(As...)), As...) @inline broadcast(f, As...) = broadcast_t(f, promote_eltype_op(f, As...), As...) @@ -217,7 +217,7 @@ function broadcast(f, As...) end =# -@inline bitbroadcast(f, As...) = broadcast!(f, allocate_for(BitArray, As, broadcast_shape(As...)), As...) +@inline bitbroadcast(f, As...) = broadcast!(f, similar(BitArray, broadcast_shape(As...)), As...) broadcast_getindex(src::AbstractArray, I::AbstractArray...) = broadcast_getindex!(Array{eltype(src)}(to_shape(broadcast_shape(I...))), src, I...) @generated function broadcast_getindex!(dest::AbstractArray, src::AbstractArray, I::AbstractArray...) @@ -368,7 +368,7 @@ for (f, scalarf) in ((:.==, :(==)), shape = :(indices($active)) @eval begin function ($f)(A::$sigA, B::$sigB) - P = allocate_for(BitArray, $active, $shape) + P = similar(BitArray, $shape) F = parent(P) l = length(F) l == 0 && return F diff --git a/base/exports.jl b/base/exports.jl index acc1597849c98..e97f804792eb6 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -484,7 +484,6 @@ export zeta, # arrays - allocate_for, bitbroadcast, broadcast!, broadcast, diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 3346d5983dd07..3c4e3b3bef930 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -782,7 +782,7 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`. @generated function unique{T,N}(A::AbstractArray{T,N}, dim::Int) quote 1 <= dim <= $N || return copy(A) - hashes = allocate_for(inds->zeros(UInt, inds), A, indices(A, dim)) + hashes = similar(inds->zeros(UInt, inds), indices(A, dim)) # Compute hash for each row k = 0 @@ -791,7 +791,7 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`. end # Collect index of first row for each hash - uniquerow = allocate_for(Array{Int}, A, indices(A, dim)) + uniquerow = similar(Array{Int}, indices(A, dim)) firstrow = Dict{Prehashed,Int}() for k = indices(A, dim) uniquerow[k] = get!(firstrow, Prehashed(hashes[k]), k) @@ -799,7 +799,7 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`. uniquerows = collect(values(firstrow)) # Check for collisions - collided = allocate_for(falses, A, indices(A, dim)) + collided = similar(falses, indices(A, dim)) @inbounds begin @nloops $N i A d->(if d == dim k = i_d @@ -814,7 +814,7 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`. end if any(collided) - nowcollided = allocate_for(BitArray, A, indices(A, dim)) + nowcollided = similar(BitArray, indices(A, dim)) while any(collided) # Collect index of first row for each collided hash empty!(firstrow) diff --git a/base/sort.jl b/base/sort.jl index e5aa1522374b1..9cac327aa92c5 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -2,7 +2,7 @@ module Sort -using Base: Order, copymutable, linearindices, allocate_for, linearindexing, viewindexing, LinearFast +using Base: Order, copymutable, linearindices, linearindexing, viewindexing, LinearFast import Base.sort, @@ -447,7 +447,7 @@ function sortperm(v::AbstractVector; by=identity, rev::Bool=false, order::Ordering=Forward) - p = Base.allocate_for(Vector{Int}, v, indices(v, 1)) + p = similar(Vector{Int}, indices(v, 1)) for (i,ind) in zip(eachindex(p), indices(v, 1)) p[i] = ind end @@ -507,7 +507,7 @@ end function sortrows(A::AbstractMatrix; kws...) inds = indices(A,1) T = slicetypeof(A, inds, :) - rows = allocate_for(Vector{T}, A, indices(A, 1)) + rows = similar(Vector{T}, indices(A, 1)) for i in inds rows[i] = view(A, i, :) end @@ -518,7 +518,7 @@ end function sortcols(A::AbstractMatrix; kws...) inds = indices(A,2) T = slicetypeof(A, :, inds) - cols = allocate_for(Vector{T}, A, indices(A, 2)) + cols = similar(Vector{T}, indices(A, 2)) for i in inds cols[i] = view(A, :, i) end diff --git a/test/offsetarray.jl b/test/offsetarray.jl index 4d1c1cdb7ee4f..ce38c5721a8e5 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -7,7 +7,7 @@ module OAs -using Base: DimOrInd, Indices, LinearSlow, LinearFast +using Base: DimOrInd, DimsOrInds, Indices, LinearSlow, LinearFast export OffsetArray @@ -46,16 +46,15 @@ Base.indices1{T}(A::OffsetArray{T,0}) = 1:1 function Base.similar(A::OffsetArray, T::Type, dims::Dims) B = similar(parent(A), T, dims) end -function Base.similar(A::AbstractArray, T::Type, inds::Tuple{Vararg{DimOrInd}}) +function Base.similar(A::AbstractArray, T::Type, inds::Tuple{UnitRange,Vararg{UnitRange}}) B = similar(A, T, map(Base.dimlength, inds)) OffsetArray(B, map(indsoffset, inds)) end -Base.allocate_for(f, A::OffsetArray, shape::DimOrInd) = OffsetArray(f(Base.dimlength(shape)), (indsoffset(shape),)) -Base.allocate_for(f, A::OffsetArray, shape::Tuple{Vararg{DimOrInd}}) = OffsetArray(f(map(Base.dimlength, shape)), map(indsoffset, shape)) +Base.similar(f::Union{Function,Type}, shape::Tuple{UnitRange,Vararg{UnitRange}}) = OffsetArray(f(map(Base.dimlength, shape)), map(indsoffset, shape)) Base.promote_indices(a::OffsetArray, b::OffsetArray) = a -Base.reshape(A::AbstractArray, inds::Indices) = OffsetArray(reshape(A, map(Base.dimlength, inds)), map(indsoffset, inds)) +Base.reshape(A::AbstractArray, inds::Tuple{UnitRange,Vararg{UnitRange}}) = OffsetArray(reshape(A, map(Base.dimlength, inds)), map(indsoffset, inds)) @inline function Base.getindex{T,N}(A::OffsetArray{T,N}, I::Vararg{Int,N}) @boundscheck checkbounds(A, I...) @@ -207,10 +206,10 @@ B = similar(A, (3,4)) @test isa(B, Array{Int,2}) @test size(B) == (3,4) @test indices(B) == (1:3, 1:4) -B = similar(A, (-3:3,4)) +B = similar(A, (-3:3,1:4)) @test isa(B, OffsetArray{Int,2}) @test indices(B) == (-3:3, 1:4) -B = similar(parent(A), (-3:3,4)) +B = similar(parent(A), (-3:3,1:4)) @test isa(B, OffsetArray{Int,2}) @test indices(B) == (-3:3, 1:4) From 24f3534e4cd667c8f9887e912939aec8d924d281 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 29 Jun 2016 11:16:47 -0500 Subject: [PATCH 07/15] Eliminate the indicesbehavior trait --- base/abstractarray.jl | 52 +++++---------------------------------- base/arraymath.jl | 18 ++++++-------- base/permuteddimsarray.jl | 8 +----- base/reshapedarray.jl | 26 +++++++++----------- base/sort.jl | 2 +- base/subarray.jl | 17 ++----------- doc/manual/interfaces.rst | 17 ++++++------- 7 files changed, 37 insertions(+), 103 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 079d1995e1156..1eb8cbab57f61 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -146,15 +146,6 @@ linearindexing(A::AbstractArray, B::AbstractArray...) = linearindexing(linearind linearindexing(::LinearFast, ::LinearFast) = LinearFast() linearindexing(::LinearIndexing, ::LinearIndexing) = LinearSlow() -abstract IndicesBehavior -immutable IndicesStartAt1 <: IndicesBehavior end # indices 1:size(A,d) -immutable IndicesUnitRange <: IndicesBehavior end # arb UnitRange indices -immutable IndicesList <: IndicesBehavior end # indices like (:cat, :dog, :mouse) - -indicesbehavior(A::AbstractArray) = indicesbehavior(typeof(A)) -indicesbehavior{T<:AbstractArray}(::Type{T}) = IndicesStartAt1() -indicesbehavior(::Number) = IndicesStartAt1() - abstract IndicesPerformance immutable IndicesFast1D <: IndicesPerformance end # indices(A, d) is fast immutable IndicesSlow1D <: IndicesPerformance end # indices(A) is better than indices(A,d) @@ -375,26 +366,6 @@ indices of `A`. similar(f, shape::Tuple) = f(to_shape(shape)) similar(f, dims::DimOrInd...) = similar(f, dims) -promote_indices(a) = a -function promote_indices(a, b, c...) - @_inline_meta - promote_indices(promote_indices(a, b), c...) -end -# overload this to return true for your type, e.g., -# promote_indices(a::OffsetArray, b::OffsetArray) = a -promote_indices(a::AbstractArray, b::AbstractArray) = _promote_indices(indicesbehavior(a), indicesbehavior(b), a, b) -_promote_indices(::IndicesStartAt1, ::IndicesStartAt1, a, b) = a -_promote_indices(::IndicesBehavior, ::IndicesBehavior, a, b) = throw(ArgumentError("types $(typeof(a)) and $(typeof(b)) do not have promote_indices defined")) -promote_indices(a::Number, b::AbstractArray) = b -promote_indices(a::AbstractArray, b::Number) = a - -# Strip off the index-changing container---this assumes that `parent` -# performs such an operation. TODO: since few things in Base need this, it -# would be great to find a way to eliminate this function. -normalize_indices(A) = normalize_indices(indicesbehavior(A), A) -normalize_indices(::IndicesStartAt1, A) = A -normalize_indices(::IndicesBehavior, A) = parent(A) - ## from general iterable to any array function copy!(dest::AbstractArray, src) @@ -1229,38 +1200,26 @@ end # sub2ind and ind2sub # fallbacks function sub2ind(A::AbstractArray, I...) - @_inline_meta - sub2ind(indicesbehavior(A), A, I...) -end -function sub2ind(::IndicesStartAt1, A::AbstractArray, I...) - @_inline_meta - sub2ind(size(A), I...) -end -function sub2ind(::IndicesBehavior, A::AbstractArray, I...) @_inline_meta sub2ind(indices(A), I...) end function ind2sub(A::AbstractArray, ind) - @_inline_meta - ind2sub(indicesbehavior(A), A, ind) -end -function ind2sub(::IndicesStartAt1, A::AbstractArray, ind) - @_inline_meta - ind2sub(size(A), ind) -end -function ind2sub(::IndicesBehavior, A::AbstractArray, ind) @_inline_meta ind2sub(indices(A), ind) end +# 0-dimensional arrays and indexing with [] sub2ind(::Tuple{}) = 1 sub2ind(::DimsInteger) = 1 sub2ind(::Indices) = 1 sub2ind(::Tuple{}, I::Integer...) = (@_inline_meta; _sub2ind((), 1, 1, I...)) +# Generic cases sub2ind(dims::DimsInteger, I::Integer...) = (@_inline_meta; _sub2ind(dims, 1, 1, I...)) sub2ind(inds::Indices, I::Integer...) = (@_inline_meta; _sub2ind(inds, 1, 1, I...)) -# In 1d, there's a question of whether we're doing cartesian indexing or linear indexing. Support only the former. +# In 1d, there's a question of whether we're doing cartesian indexing +# or linear indexing. Support only the former. sub2ind(inds::Indices{1}, I::Integer...) = throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) +sub2ind(inds::Tuple{OneTo}, I::Integer...) = (@_inline_meta; _sub2ind(inds, 1, 1, I...)) # only OneTo is safe _sub2ind(::Any, L, ind) = ind function _sub2ind(::Tuple{}, L, ind, i::Integer, I::Integer...) @@ -1284,6 +1243,7 @@ ind2sub(::Tuple{}, ind::Integer) = (@_inline_meta; ind == 1 ? () : throw(BoundsE ind2sub(dims::DimsInteger, ind::Integer) = (@_inline_meta; _ind2sub((), dims, ind-1)) ind2sub(inds::Indices, ind::Integer) = (@_inline_meta; _ind2sub((), inds, ind-1)) ind2sub(inds::Indices{1}, ind::Integer) = throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) +ind2sub(inds::Tuple{OneTo}, ind::Integer) = (@_inline_meta; _ind2sub((), inds, ind-1)) _ind2sub(::Tuple{}, ::Tuple{}, ind) = (ind+1,) function _ind2sub(out, indslast::NTuple{1}, ind) diff --git a/base/arraymath.jl b/base/arraymath.jl index cb3cd4625e26e..c89ac3c2d6a30 100644 --- a/base/arraymath.jl +++ b/base/arraymath.jl @@ -211,8 +211,8 @@ function flipdim{T}(A::Array{T}, d::Integer) end function rotl90(A::AbstractMatrix) - B = similar_transpose(A) - ind2 = indices(A,2) + ind1, ind2 = indices(A) + B = similar(A, (ind2,ind1)) n = first(ind2)+last(ind2) for i=indices(A,1), j=ind2 B[n-j,i] = A[i,j] @@ -220,8 +220,8 @@ function rotl90(A::AbstractMatrix) return B end function rotr90(A::AbstractMatrix) - B = similar_transpose(A) - ind1 = indices(A,1) + ind1, ind2 = indices(A) + B = similar(A, (ind2,ind1)) m = first(ind1)+last(ind1) for i=ind1, j=indices(A,2) B[j,m-i] = A[i,j] @@ -246,10 +246,6 @@ end rotr90(A::AbstractMatrix, k::Integer) = rotl90(A,-k) rot180(A::AbstractMatrix, k::Integer) = mod(k, 2) == 1 ? rot180(A) : copy(A) -similar_transpose(A::AbstractMatrix) = similar_transpose(indicesbehavior(A), A) -similar_transpose(::IndicesStartAt1, A::AbstractMatrix) = similar(A, (size(A,2), size(A,1))) -similar_transpose(::IndicesBehavior, A::AbstractMatrix) = similar(A, (indices(A,2), indices(A,1))) - ## Transpose ## transpose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(transpose, B, A) ctranspose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(ctranspose, B, A) @@ -315,11 +311,13 @@ function ccopy!(B, A) end function transpose(A::AbstractMatrix) - B = similar_transpose(A) + ind1, ind2 = indices(A) + B = similar(A, (ind2, ind1)) transpose!(B, A) end function ctranspose(A::AbstractMatrix) - B = similar_transpose(A) + ind1, ind2 = indices(A) + B = similar(A, (ind2, ind1)) ctranspose!(B, A) end ctranspose{T<:Real}(A::AbstractVecOrMat{T}) = transpose(A) diff --git a/base/permuteddimsarray.jl b/base/permuteddimsarray.jl index c5a461509d494..dd64416fae86b 100644 --- a/base/permuteddimsarray.jl +++ b/base/permuteddimsarray.jl @@ -2,8 +2,6 @@ module PermutedDimsArrays -using Base: IndicesStartAt1, IndicesBehavior, indicesbehavior - export permutedims # Some day we will want storage-order-aware iteration, so put perm in the parameters @@ -47,14 +45,10 @@ _genperm(out, I) = out @inline genperm(I, perm::AbstractVector{Int}) = genperm(I, (perm...,)) function Base.permutedims{T,N}(A::AbstractArray{T,N}, perm) - dest = similar_permute(A, perm) + dest = similar(A, genperm(indices(A), perm)) permutedims!(dest, A, perm) end -similar_permute(A::AbstractArray, perm) = similar_permute(indicesbehavior(A), A, perm) -similar_permute{T,N}(::IndicesStartAt1, A::AbstractArray{T,N}, perm) = similar(A, genperm(size(A), perm)) -similar_permute{T,N}(::IndicesBehavior, A::AbstractArray{T,N}, perm) = similar(A, genperm(indices(A), perm)) - function Base.permutedims!(dest, src::AbstractArray, perm) Base.checkdims_perm(dest, src, perm) P = PermutedDimsArray(dest, invperm(perm)) diff --git a/base/reshapedarray.jl b/base/reshapedarray.jl index a8577b1d4bc84..053a554eb821a 100644 --- a/base/reshapedarray.jl +++ b/base/reshapedarray.jl @@ -36,24 +36,20 @@ start(R::ReshapedArrayIterator) = start(R.iter) end length(R::ReshapedArrayIterator) = length(R.iter) -reshape(parent::AbstractArray, ref::AbstractArray) = reshape(indicesbehavior(ref), parent, ref) -reshape(::IndicesStartAt1, parent::AbstractArray, ref::AbstractArray) = reshape(parent, size(ref)) -reshape(::IndicesBehavior, parent::AbstractArray, ref::AbstractArray) = reshape(parent, indices(ref)) - -reshape(parent::AbstractArray, dims::Dims) = _reshape(parent, dims) -reshape(parent::AbstractArray, len::Integer) = reshape(parent, (Int(len),)) -reshape(parent::AbstractArray, dims::Int...) = reshape(parent, dims) +reshape(parent::AbstractArray, shp::Tuple) = _reshape(parent, to_shape(shp)) +reshape(parent::AbstractArray, dims::DimOrInd...) = reshape(parent, dims) reshape{T,N}(parent::AbstractArray{T,N}, ndims::Type{Val{N}}) = parent function reshape{T,AN,N}(parent::AbstractArray{T,AN}, ndims::Type{Val{N}}) - reshape(parent, rdims((), size(parent), Val{N})) -end -# Move elements from sz to out until out reaches the desired dimensionality N, -# either filling with 1 or collapsing the product of trailing dims into the last element -@pure rdims{N}(out::NTuple{N}, sz::Tuple{}, ::Type{Val{N}}) = out -@pure rdims{N}(out::NTuple{N}, sz::Tuple{Any, Vararg{Any}}, ::Type{Val{N}}) = (front(out)..., last(out) * prod(sz)) -@pure rdims{N}(out::Tuple, sz::Tuple{}, ::Type{Val{N}}) = rdims((out..., 1), (), Val{N}) -@pure rdims{N}(out::Tuple, sz::Tuple{Any, Vararg{Any}}, ::Type{Val{N}}) = rdims((out..., first(sz)), tail(sz), Val{N}) + reshape(parent, rdims((), indices(parent), Val{N})) +end +# Move elements from inds to out until out reaches the desired +# dimensionality N, either filling with OneTo(1) or collapsing the +# product of trailing dims into the last element +@pure rdims{N}(out::NTuple{N}, inds::Tuple{}, ::Type{Val{N}}) = out +@pure rdims{N}(out::NTuple{N}, inds::Tuple{Any, Vararg{Any}}, ::Type{Val{N}}) = (front(out)..., length(last(out)) * prod(map(length, inds))) +@pure rdims{N}(out::Tuple, inds::Tuple{}, ::Type{Val{N}}) = rdims((out..., OneTo(1)), (), Val{N}) +@pure rdims{N}(out::Tuple, inds::Tuple{Any, Vararg{Any}}, ::Type{Val{N}}) = rdims((out..., first(inds)), tail(inds), Val{N}) function _reshape(parent::AbstractArray, dims::Dims) prod(dims) == length(parent) || throw(DimensionMismatch("parent has $(length(parent)) elements, which is incompatible with size $dims")) diff --git a/base/sort.jl b/base/sort.jl index 9cac327aa92c5..99900be146ef4 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -492,7 +492,7 @@ function sort(A::AbstractArray, dim::Integer; else Av = A[:] sort_chunks!(Av, size(A,1), alg, order) - reshape(Av, A) + reshape(Av, indices(A)) end end diff --git a/base/subarray.jl b/base/subarray.jl index aa527607da513..02b4cb5b3d95a 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -279,17 +279,13 @@ end # Since bounds-checking is performance-critical and uses # indices, it's worth optimizing these implementations thoroughly indices(S::SubArray, d::Integer) = 1 <= d <= ndims(S) ? indices(S)[d] : (d > ndims(S) ? OneTo(1) : error("dimension $d out of range")) -indices(S::SubArray) = (@_inline_meta; _indices(indicesbehavior(parent(S)), S)) -_indices(::IndicesStartAt1, S::SubArray) = (@_inline_meta; map(s->OneTo(s), size(S))) -_indices(::IndicesBehavior, S::SubArray) = (@_inline_meta; _indices((), 1, S, S.indexes...)) +indices(S::SubArray) = (@_inline_meta; _indices((), 1, S, S.indexes...)) _indices(out::Tuple, dim, S::SubArray) = out _indices(out::Tuple, dim, S::SubArray, i1, I...) = (@_inline_meta; _indices((out..., OneTo(length(i1))), dim+1, S, I...)) _indices(out::Tuple, dim, S::SubArray, ::Real, I...) = (@_inline_meta; _indices(out, dim+1, S, I...)) _indices(out::Tuple, dim, S::SubArray, ::Colon, I...) = (@_inline_meta; _indices((out..., indices(parent(S), dim)), dim+1, S, I...)) indices1{T}(S::SubArray{T,0}) = OneTo(1) -indices1(S::SubArray) = (@_inline_meta; _indices1(indicesbehavior(parent(S)), S)) -_indices1(::IndicesStartAt1, S::SubArray) = OneTo(S.dims[1]) -_indices1(::IndicesBehavior, S::SubArray) = (@_inline_meta; _indices1(S, 1, S.indexes...)) +indices1(S::SubArray) = (@_inline_meta; _indices1(S, 1, S.indexes...)) _indices1(S::SubArray, dim, i1, I...) = (@_inline_meta; OneTo(length(i1))) _indices1(S::SubArray, dim, i1::Real, I...) = (@_inline_meta; _indices1(S, dim+1, I...)) _indices1(S::SubArray, dim, i1::Colon, I...) = (@_inline_meta; indices(parent(S), dim)) @@ -297,15 +293,6 @@ _indices1(S::SubArray, dim, i1::Colon, I...) = (@_inline_meta; indices(parent(S) # Moreover, incides(S) is fast but indices(S, d) is slower indicesperformance{T<:SubArray}(::Type{T}) = IndicesSlow1D() -indicesbehavior(S::SubArray) = _indicesbehavior(indicesbehavior(parent(S)), S) -_indicesbehavior(::IndicesStartAt1, S::SubArray) = IndicesStartAt1() -_indicesbehavior(::IndicesBehavior, S::SubArray) = (@_inline_meta; _indicesbehavior(keepcolon((), S.indexes...), S)) -keepcolon(out) = out -keepcolon(out, ::Colon, I...) = (@_inline_meta; (Colon(),)) -keepcolon(out, i1, I...) = (@_inline_meta; keepcolon(out, I...)) -_indicesbehavior(::Tuple{}, S::SubArray) = IndicesStartAt1() -_indicesbehavior(::Tuple{Colon}, S::SubArray) = indicesbehavior(parent(S)) - ## Compatability # deprecate? function parentdims(s::SubArray) diff --git a/doc/manual/interfaces.rst b/doc/manual/interfaces.rst index 5ede56eb24f8c..b0f0886da6c22 100644 --- a/doc/manual/interfaces.rst +++ b/doc/manual/interfaces.rst @@ -166,9 +166,8 @@ Methods to implement :func:`similar(A, dims::NTuple{Int}) ` ``similar(A, eltype(A), dims)`` Return a mutable array with the same element type and size `dims` :func:`similar(A, ::Type{S}, dims::NTuple{Int}) ` ``Array{S}(dims)`` Return a mutable array with the specified element type and size **Non-traditional indices** **Default definition** **Brief description** -:func:`Base.indicesbehavior(::Type) ` ``Base.IndicesStartAt1()`` Trait with values ``IndicesStartAt1()``, ``IndicesUnitRange()``, ``IndicesList()`` -:func:`indices(A, d) ` ``1:size(A, d)`` Return the range of valid indices along dimension ``d`` -:func:`Base.similar(A, ::Type{S}, inds::NTuple{Ind}) ` ``similar(A, S, map(Base.dimlength, inds))`` Return a mutable array with the specified indices ``inds`` (see below for discussion of ``Ind``) +:func:`indices(A, d) ` ``OneTo(size(A, d))`` Return the ``AbstractUnitRange`` of valid indices along dimension ``d`` +:func:`Base.similar(A, ::Type{S}, inds::NTuple{Ind}) ` ``similar(A, S, map(Base.dimlength, inds))`` Return a mutable array with the specified indices ``inds`` (see below) ===================================================================== ============================================ ======================================================================================= If a type is defined as a subtype of ``AbstractArray``, it inherits a very large set of rich behaviors including iteration and multidimensional indexing built on top of single-element access. See the :ref:`arrays manual page ` and :ref:`standard library section ` for more supported methods. @@ -290,9 +289,9 @@ In addition to all the iterable and indexable methods from above, these types ca If you are defining an array type that allows non-traditional indexing (indices that start at something other than 1), you should specialize -``indices`` and ``indicesbehavior``. You should also specialize -``similar`` so that the ``dims`` argument (ordinarily a ``Dims`` -size-tuple) can be a mixture of ``Integer`` and ``UnitRange`` objects; -the ``Integer`` entries imply that the indexing starts from 1, whereas -the dimensions encoded with ``UnitRange`` may have arbitrary starting -index. +``indices``. You should also specialize ``similar`` so that the +``dims`` argument (ordinarily a ``Dims`` size-tuple) can accept +``AbstractUnitRange`` objects, perhaps range-types ``Ind`` of your own +design. For example, if indexing always starts with 0 for your +arrays, you likely want to define a ``ZeroTo`` range type. Otherwise, +you can use standard ``UnitRange``. From 513af18f4c077396b1ce6426208012c3d62c2209 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 6 Jul 2016 10:03:50 -0500 Subject: [PATCH 08/15] Simpler sub2ind/ind2sub for Tuple{OneTo} --- base/abstractarray.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 1eb8cbab57f61..6f2176d511dee 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1220,6 +1220,7 @@ sub2ind(inds::Indices, I::Integer...) = (@_inline_meta; _sub2ind(inds, 1, 1, I.. # or linear indexing. Support only the former. sub2ind(inds::Indices{1}, I::Integer...) = throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) sub2ind(inds::Tuple{OneTo}, I::Integer...) = (@_inline_meta; _sub2ind(inds, 1, 1, I...)) # only OneTo is safe +sub2ind(inds::Tuple{OneTo}, i::Integer) = i _sub2ind(::Any, L, ind) = ind function _sub2ind(::Tuple{}, L, ind, i::Integer, I::Integer...) @@ -1243,7 +1244,7 @@ ind2sub(::Tuple{}, ind::Integer) = (@_inline_meta; ind == 1 ? () : throw(BoundsE ind2sub(dims::DimsInteger, ind::Integer) = (@_inline_meta; _ind2sub((), dims, ind-1)) ind2sub(inds::Indices, ind::Integer) = (@_inline_meta; _ind2sub((), inds, ind-1)) ind2sub(inds::Indices{1}, ind::Integer) = throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) -ind2sub(inds::Tuple{OneTo}, ind::Integer) = (@_inline_meta; _ind2sub((), inds, ind-1)) +ind2sub(inds::Tuple{OneTo}, ind::Integer) = (ind,) _ind2sub(::Tuple{}, ::Tuple{}, ind) = (ind+1,) function _ind2sub(out, indslast::NTuple{1}, ind) From 66b6a619584832e5889bc4fd85ee48e33d733b2a Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 6 Jul 2016 09:49:28 -0500 Subject: [PATCH 09/15] Fix offsetarray tests and move them earlier The reason to move earlier is to test whether the methods corrupt other operations. --- test/choosetests.jl | 4 ++-- test/offsetarray.jl | 27 ++++++++++++++++++--------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/test/choosetests.jl b/test/choosetests.jl index 9818d9acbeb12..9484d0e394f7c 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -17,7 +17,7 @@ function choosetests(choices = []) testnames = [ "linalg", "subarray", "core", "inference", "keywordargs", "numbers", "printf", "char", "string", "triplequote", "unicode", - "dates", "dict", "hashing", "iobuffer", "staged", + "dates", "dict", "hashing", "iobuffer", "staged", "offsetarray", "arrayops", "tuple", "reduce", "reducedim", "random", "abstractarray", "intfuncs", "simdloop", "vecelement", "blas", "sparse", "bitarray", "copy", "math", "fastmath", "functional", @@ -33,7 +33,7 @@ function choosetests(choices = []) "markdown", "base64", "serialize", "misc", "threads", "enums", "cmdlineargs", "i18n", "workspace", "libdl", "int", "checked", "intset", "floatfuncs", "compile", "parallel", "inline", - "boundscheck", "error", "ambiguous", "offsetarray", "cartesian" + "boundscheck", "error", "ambiguous", "cartesian" ] if Base.USE_GPL_LIBS diff --git a/test/offsetarray.jl b/test/offsetarray.jl index ce38c5721a8e5..4a91bf6a29f26 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -24,7 +24,6 @@ OffsetArray{T,N}(A::AbstractArray{T,N}, offsets::Vararg{Int,N}) = OffsetArray(A, (::Type{OffsetArray{T}}){T,N}(inds::Indices{N}) = OffsetArray{T,N}(inds) Base.linearindexing{T<:OffsetArray}(::Type{T}) = Base.linearindexing(parenttype(T)) -Base.indicesbehavior{T<:OffsetArray}(::Type{T}) = Base.IndicesUnitRange() parenttype{T,N,AA}(::Type{OffsetArray{T,N,AA}}) = AA parenttype(A::OffsetArray) = parenttype(typeof(A)) @@ -52,7 +51,6 @@ function Base.similar(A::AbstractArray, T::Type, inds::Tuple{UnitRange,Vararg{Un end Base.similar(f::Union{Function,Type}, shape::Tuple{UnitRange,Vararg{UnitRange}}) = OffsetArray(f(map(Base.dimlength, shape)), map(indsoffset, shape)) -Base.promote_indices(a::OffsetArray, b::OffsetArray) = a Base.reshape(A::AbstractArray, inds::Tuple{UnitRange,Vararg{UnitRange}}) = OffsetArray(reshape(A, map(Base.dimlength, inds)), map(indsoffset, inds)) @@ -99,6 +97,7 @@ end using OAs +let # Basics A0 = [1 3; 2 4] A = OffsetArray(A0, (-1,2)) # LinearFast @@ -133,26 +132,30 @@ S = OffsetArray(view(A0, 1:2, 1:2), (-1,2)) # LinearSlow @test eachindex(A) == 1:4 @test eachindex(S) == CartesianRange((0:1,3:4)) -# slice +# view S = view(A, :, 3) @test S == OffsetArray([1,2], (A.offsets[1],)) @test S[0] == 1 @test S[1] == 2 @test_throws BoundsError S[2] +@test indices(S) === (0:1,) S = view(A, 0, :) @test S == OffsetArray([1,3], (A.offsets[2],)) @test S[3] == 1 @test S[4] == 3 @test_throws BoundsError S[1] +@test indices(S) === (3:4,) S = view(A, 0:0, 4) @test S == [3] @test S[1] == 3 @test_throws BoundsError S[0] +@test indices(S) === (Base.OneTo(1),) S = view(A, 1, 3:4) @test S == [2,4] @test S[1] == 2 @test S[2] == 4 @test_throws BoundsError S[3] +@test indices(S) === (Base.OneTo(2),) S = view(A, :, :) @test S == A @test S[0,3] == S[1] == 1 @@ -160,6 +163,7 @@ S = view(A, :, :) @test S[0,4] == S[3] == 3 @test S[1,4] == S[4] == 4 @test_throws BoundsError S[1,1] +@test indices(S) === (0:1, 3:4) # iteration for (a,d) in zip(A, A0) @@ -201,29 +205,33 @@ cmp_showf(Base.print_matrix, io, OffsetArray(rand(10^3,10^3), (10,-9))) # neithe B = similar(A, Float32) @test isa(B, OffsetArray{Float32,2}) @test size(B) == size(A) -@test indices(B) == indices(A) +@test indices(B) === indices(A) B = similar(A, (3,4)) @test isa(B, Array{Int,2}) @test size(B) == (3,4) -@test indices(B) == (1:3, 1:4) +@test indices(B) === (Base.OneTo(3), Base.OneTo(4)) B = similar(A, (-3:3,1:4)) @test isa(B, OffsetArray{Int,2}) -@test indices(B) == (-3:3, 1:4) +@test indices(B) === (-3:3, 1:4) B = similar(parent(A), (-3:3,1:4)) @test isa(B, OffsetArray{Int,2}) -@test indices(B) == (-3:3, 1:4) +@test indices(B) === (-3:3, 1:4) # Indexing with OffsetArray indices i1 = OffsetArray([2,1], (-5,)) i1 = OffsetArray([2,1], -5) b = A0[i1, 1] -@test indices(b) == (-4:-3,) +@test indices(b) === (-4:-3,) @test b[-4] == 2 @test b[-3] == 1 b = A0[1,i1] -@test indices(b) == (-4:-3,) +@test indices(b) === (-4:-3,) @test b[-4] == 3 @test b[-3] == 1 +v = view(A0, i1, 1) +@test indices(v) === (-4:-3,) +v = view(A0, 1:1, i1) +@test indices(v) === (Base.OneTo(1), -4:-3) # logical indexing @test A[A .> 2] == [3,4] @@ -354,3 +362,4 @@ v = OffsetArray(rand(8), (-2,)) @test 2*A == OffsetArray(2*parent(A), A.offsets) @test A+A == OffsetArray(parent(A)+parent(A), A.offsets) @test A.*A == OffsetArray(parent(A).*parent(A), A.offsets) +end From 5ebb70243d1237d0018d95ac842c4f206be9bbd4 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 6 Jul 2016 06:14:22 -0500 Subject: [PATCH 10/15] subarray indices: work around an inference hang (#17278) --- base/subarray.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/base/subarray.jl b/base/subarray.jl index 02b4cb5b3d95a..e41e1faf3b639 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -279,16 +279,16 @@ end # Since bounds-checking is performance-critical and uses # indices, it's worth optimizing these implementations thoroughly indices(S::SubArray, d::Integer) = 1 <= d <= ndims(S) ? indices(S)[d] : (d > ndims(S) ? OneTo(1) : error("dimension $d out of range")) -indices(S::SubArray) = (@_inline_meta; _indices((), 1, S, S.indexes...)) -_indices(out::Tuple, dim, S::SubArray) = out -_indices(out::Tuple, dim, S::SubArray, i1, I...) = (@_inline_meta; _indices((out..., OneTo(length(i1))), dim+1, S, I...)) -_indices(out::Tuple, dim, S::SubArray, ::Real, I...) = (@_inline_meta; _indices(out, dim+1, S, I...)) -_indices(out::Tuple, dim, S::SubArray, ::Colon, I...) = (@_inline_meta; _indices((out..., indices(parent(S), dim)), dim+1, S, I...)) +indices(S::SubArray) = (@_inline_meta; _indices_sub(S, 1, S.indexes...)) +_indices_sub(S::SubArray, dim::Int) = () +_indices_sub(S::SubArray, dim::Int, ::Real, I...) = (@_inline_meta; _indices_sub(S, dim+1, I...)) +_indices_sub(S::SubArray, dim::Int, ::Colon, I...) = (@_inline_meta; (indices(parent(S), dim), _indices_sub(S, dim+1, I...)...)) +_indices_sub(S::SubArray, dim::Int, i1::AbstractArray, I...) = (@_inline_meta; (indices(i1)..., _indices_sub(S, dim+1, I...)...)) indices1{T}(S::SubArray{T,0}) = OneTo(1) indices1(S::SubArray) = (@_inline_meta; _indices1(S, 1, S.indexes...)) -_indices1(S::SubArray, dim, i1, I...) = (@_inline_meta; OneTo(length(i1))) _indices1(S::SubArray, dim, i1::Real, I...) = (@_inline_meta; _indices1(S, dim+1, I...)) _indices1(S::SubArray, dim, i1::Colon, I...) = (@_inline_meta; indices(parent(S), dim)) +_indices1(S::SubArray, dim, i1::AbstractArray, I...) = (@_inline_meta; indices1(i1)) # Moreover, incides(S) is fast but indices(S, d) is slower indicesperformance{T<:SubArray}(::Type{T}) = IndicesSlow1D() From 90fc0b6898d6aa255a38428f0d89b7ba852f443f Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 29 Jun 2016 13:46:36 -0500 Subject: [PATCH 11/15] Performance improvements for indices --- base/abstractarray.jl | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 6f2176d511dee..cef551de86049 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -11,6 +11,10 @@ typealias IndicesOne{N} NTuple{N,OneTo} typealias DimOrInd Union{Integer, AbstractUnitRange} typealias DimsOrInds{N} NTuple{N,DimOrInd} +macro _inline_pure_meta() + Expr(:meta, :inline, :pure) +end + ## Basic functions ## vect() = Array{Any}(0) @@ -30,27 +34,19 @@ size{N}(x, d1::Integer, d2::Integer, dx::Vararg{Integer, N}) = (size(x, d1), siz Returns the valid range of indices for array `A` along dimension `d`. """ -function indices(A::AbstractArray, d) - @_inline_meta - OneTo(size(A,d)) -end +indices{T,N}(A::AbstractArray{T,N}, d) = d <= N ? indices(A)[d] : OneTo(1) """ indices(A) Returns the tuple of valid indices for array `A`. """ -indices{T,N}(A::AbstractArray{T,N}) = _indices((), A) # faster than ntuple(d->indices(A,d), Val{N}) -_indices{T,N}(out::NTuple{N}, A::AbstractArray{T,N}) = out -function _indices(out, A::AbstractArray) - @_inline_meta - _indices((out..., indices(A, length(out)+1)), A) +function indices{T,N}(A::AbstractArray{T,N}) + @_inline_pure_meta + map(s->OneTo(s), size(A)) end -# Note: a simpler implementation suffers from #16327 -# function indices{T,N}(A::AbstractArray{T,N}) -# @_inline_meta -# ntuple(d->indices(A, d), Val{N}) -# end + indices1(A) = (@_inline_meta; indices(A, 1)) + """ linearindices(A) @@ -1241,21 +1237,21 @@ unsafe_length(r::UnitRange) = r.stop-r.start+1 unsafe_length(r::OneTo) = length(r) ind2sub(::Tuple{}, ind::Integer) = (@_inline_meta; ind == 1 ? () : throw(BoundsError())) -ind2sub(dims::DimsInteger, ind::Integer) = (@_inline_meta; _ind2sub((), dims, ind-1)) -ind2sub(inds::Indices, ind::Integer) = (@_inline_meta; _ind2sub((), inds, ind-1)) +ind2sub(dims::DimsInteger, ind::Integer) = (@_inline_meta; _ind2sub(dims, ind-1)) +ind2sub(inds::Indices, ind::Integer) = (@_inline_meta; _ind2sub(inds, ind-1)) ind2sub(inds::Indices{1}, ind::Integer) = throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) ind2sub(inds::Tuple{OneTo}, ind::Integer) = (ind,) -_ind2sub(::Tuple{}, ::Tuple{}, ind) = (ind+1,) -function _ind2sub(out, indslast::NTuple{1}, ind) +_ind2sub(::Tuple{}, ind) = (ind+1,) +function _ind2sub(indslast::NTuple{1}, ind) @_inline_meta - (out..., _lookup(ind, indslast[1])) + (_lookup(ind, indslast[1]),) end -function _ind2sub(out, inds, ind) +function _ind2sub(inds, ind) @_inline_meta r1 = inds[1] indnext, f, l = _div(ind, r1) - _ind2sub((out..., ind-l*indnext+f), tail(inds), indnext) + (ind-l*indnext+f, _ind2sub(tail(inds), indnext)...) end _lookup(ind, d::Integer) = ind+1 From cf9dd6ba608b120794711e9965cb711d5147eec2 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 7 Jul 2016 06:16:12 -0500 Subject: [PATCH 12/15] Elim. `indicesperformance` and use only a single path for bounds-checking --- base/abstractarray.jl | 70 ++++++--------------------------------- base/essentials.jl | 2 +- base/permuteddimsarray.jl | 2 +- base/subarray.jl | 4 --- 4 files changed, 12 insertions(+), 66 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index cef551de86049..5bbc4d3a58d2a 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -142,13 +142,6 @@ linearindexing(A::AbstractArray, B::AbstractArray...) = linearindexing(linearind linearindexing(::LinearFast, ::LinearFast) = LinearFast() linearindexing(::LinearIndexing, ::LinearIndexing) = LinearSlow() -abstract IndicesPerformance -immutable IndicesFast1D <: IndicesPerformance end # indices(A, d) is fast -immutable IndicesSlow1D <: IndicesPerformance end # indices(A) is better than indices(A,d) - -indicesperformance(A::AbstractArray) = indicesperformance(typeof(A)) -indicesperformance{T<:AbstractArray}(::Type{T}) = IndicesFast1D() - ## Bounds checking ## @generated function trailingsize{T,N,n}(A::AbstractArray{T,N}, ::Type{Val{n}}) (isa(n, Int) && isa(N, Int)) || error("Must have concrete type") @@ -187,7 +180,8 @@ function checkindex(::Type{Bool}, inds::AbstractUnitRange, I::AbstractArray) end # check all indices/dimensions -# To make extension easier, avoid specializing checkbounds on index types +# To make extension easier, avoid specializations of checkbounds on index types +# (That said, typically one does not need to specialize this function.) """ checkbounds(Bool, array, indexes...) @@ -197,15 +191,12 @@ behaviors. """ function checkbounds(::Type{Bool}, A::AbstractArray, I...) @_inline_meta - _checkbounds(Bool, indicesperformance(A), A, I...) -end -function _checkbounds(::Type{Bool}, ::IndicesSlow1D, A::AbstractArray, I...) - @_inline_meta - checkbounds_indices(indices(A), I) + _chkbounds(A, I...) end -_checkbounds(::Type{Bool}, ::IndicesSlow1D, A::AbstractArray, I::AbstractArray{Bool}) = _chkbnds(A, (true,), I) +_chkbounds(A::AbstractArray, i::Integer) = (@_inline_meta; checkindex(Bool, linearindices(A), i)) +_chkbounds(A::AbstractArray, I::AbstractArray{Bool}) = (@_inline_meta; checkbounds_logical(A, I)) +_chkbounds(A::AbstractArray, I...) = (@_inline_meta; checkbounds_indices(indices(A), I)) -# Bounds-checking for arrays for which indices(A) is faster than indices(A, d) checkbounds_indices(::Tuple{}, ::Tuple{}) = true checkbounds_indices(::Tuple{}, I::Tuple{Any}) = (@_inline_meta; checkindex(Bool, 1:1, I[1])) checkbounds_indices(::Tuple{}, I::Tuple) = (@_inline_meta; checkindex(Bool, 1:1, I[1]) & checkbounds_indices((), tail(I))) @@ -213,52 +204,11 @@ checkbounds_indices(inds::Tuple{Any}, I::Tuple{Any}) = (@_inline_meta; checkinde checkbounds_indices(inds::Tuple, I::Tuple{Any}) = (@_inline_meta; checkindex(Bool, 1:prod(map(dimlength, inds)), I[1])) checkbounds_indices(inds::Tuple, I::Tuple) = (@_inline_meta; checkindex(Bool, inds[1], I[1]) & checkbounds_indices(tail(inds), tail(I))) -# Bounds-checking for arrays for which indices(A, d) is fast -function _checkbounds(::Type{Bool}, ::IndicesFast1D, A::AbstractArray, I...) - @_inline_meta - # checked::NTuple{M} means we have checked dimensions 1:M-1, now - # need to check dimension M. checked[M] indicates whether all the - # previous ones are in-bounds. - # By growing checked, it allows us to test whether we've processed - # the same number of dimensions as the array, even while - # supporting CartesianIndex - _chkbnds(A, (true,), I...) -end -checkbounds(::Type{Bool}, A::AbstractArray) = checkbounds(Bool, A, 1) # 0-d case # Single logical array indexing: -_chkbnds(A::AbstractArray, ::NTuple{1,Bool}, I::AbstractArray{Bool}) = indices(A) == indices(I) -_chkbnds(A::AbstractArray, ::NTuple{1,Bool}, I::AbstractVector{Bool}) = length(A) == length(I) -_chkbnds(A::AbstractVector, ::NTuple{1,Bool}, I::AbstractArray{Bool}) = length(A) == length(I) -_chkbnds(A::AbstractVector, ::NTuple{1,Bool}, I::AbstractVector{Bool}) = indices(A) == indices(I) -# Linear indexing: -function _chkbnds(A::AbstractVector, ::NTuple{1,Bool}, I) - @_inline_meta - checkindex(Bool, indices1(A), I) -end -function _chkbnds(A::AbstractArray, ::NTuple{1,Bool}, I) - @_inline_meta - checkindex(Bool, 1:length(A), I) -end -# When all indices have been checked: -_chkbnds{M}(A, checked::NTuple{M,Bool}) = checked[M] -# When the number of indices matches the array dimensionality: -function _chkbnds{T,N}(A::AbstractArray{T,N}, checked::NTuple{N,Bool}, I1) - @_inline_meta - checked[N] & checkindex(Bool, indices(A, N), I1) -end -# When the last checked dimension is not equal to the array dimensionality: -# TODO: for #14770 (deprecating partial linear indexing), change to 1:trailingsize(...) to 1:1 -function _chkbnds{T,N,M}(A::AbstractArray{T,N}, checked::NTuple{M,Bool}, I1) - @_inline_meta - checked[M] & checkindex(Bool, 1:trailingsize(A, Val{M}), I1) -end -# Checking an interior dimension: -function _chkbnds{T,N,M}(A::AbstractArray{T,N}, checked::NTuple{M,Bool}, I1, I...) - @_inline_meta - # grow checked by one - newchecked = (checked..., checked[M] & checkindex(Bool, indices(A, M), I1)) - _chkbnds(A, newchecked, I...) -end +checkbounds_logical(A::AbstractArray, I::AbstractArray{Bool}) = indices(A) == indices(I) +checkbounds_logical(A::AbstractArray, I::AbstractVector{Bool}) = length(A) == length(I) +checkbounds_logical(A::AbstractVector, I::AbstractArray{Bool}) = length(A) == length(I) +checkbounds_logical(A::AbstractVector, I::AbstractVector{Bool}) = indices(A) == indices(I) throw_boundserror(A, I) = (@_noinline_meta; throw(BoundsError(A, I))) diff --git a/base/essentials.jl b/base/essentials.jl index 6635d99670c0b..2f7ecbeff7645 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -171,7 +171,7 @@ start(v::SimpleVector) = 1 next(v::SimpleVector,i) = (v[i],i+1) done(v::SimpleVector,i) = (i > v.length) isempty(v::SimpleVector) = (v.length == 0) -indices(v::SimpleVector, d) = d == 1 ? OneTo(length(v)) : OneTo(1) +indices(v::SimpleVector) = (OneTo(length(v)),) linearindices(v::SimpleVector) = indices(v, 1) function ==(v1::SimpleVector, v2::SimpleVector) diff --git a/base/permuteddimsarray.jl b/base/permuteddimsarray.jl index dd64416fae86b..8ab7d60f26301 100644 --- a/base/permuteddimsarray.jl +++ b/base/permuteddimsarray.jl @@ -25,7 +25,7 @@ end Base.parent(A::PermutedDimsArray) = A.parent Base.size(A::PermutedDimsArray) = A.dims -Base.indices{T,N,perm}(A::PermutedDimsArray{T,N,perm}, d) = indices(parent(A), perm[d]) +Base.indices{T,N,perm}(A::PermutedDimsArray{T,N,perm}) = genperm(indices(parent(A)), perm) @inline function Base.getindex{T,N,perm,iperm}(A::PermutedDimsArray{T,N,perm,iperm}, I::Vararg{Int,N}) @boundscheck checkbounds(A, I...) diff --git a/base/subarray.jl b/base/subarray.jl index e41e1faf3b639..e12e3e039db1e 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -278,7 +278,6 @@ end # they are taken from the range/vector # Since bounds-checking is performance-critical and uses # indices, it's worth optimizing these implementations thoroughly -indices(S::SubArray, d::Integer) = 1 <= d <= ndims(S) ? indices(S)[d] : (d > ndims(S) ? OneTo(1) : error("dimension $d out of range")) indices(S::SubArray) = (@_inline_meta; _indices_sub(S, 1, S.indexes...)) _indices_sub(S::SubArray, dim::Int) = () _indices_sub(S::SubArray, dim::Int, ::Real, I...) = (@_inline_meta; _indices_sub(S, dim+1, I...)) @@ -290,9 +289,6 @@ _indices1(S::SubArray, dim, i1::Real, I...) = (@_inline_meta; _indices1(S, dim+1 _indices1(S::SubArray, dim, i1::Colon, I...) = (@_inline_meta; indices(parent(S), dim)) _indices1(S::SubArray, dim, i1::AbstractArray, I...) = (@_inline_meta; indices1(i1)) -# Moreover, incides(S) is fast but indices(S, d) is slower -indicesperformance{T<:SubArray}(::Type{T}) = IndicesSlow1D() - ## Compatability # deprecate? function parentdims(s::SubArray) From 18f7bf5c7f2b8bdfe8763bb3e8e26412c1ddbe1d Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 7 Jul 2016 06:16:58 -0500 Subject: [PATCH 13/15] Reduce use of splatting in sparse matrix size operations Splatting forces dynamic method lookup in places where that's a major cost --- base/sparse/sparsematrix.jl | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 9be5e09d73950..6e18bf43b08ed 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -251,7 +251,7 @@ end similar(S::SparseMatrixCSC, Tv::Type=eltype(S)) = SparseMatrixCSC(S.m, S.n, copy(S.colptr), copy(S.rowval), Array{Tv}(length(S.nzval))) similar{Tv,Ti,TvNew,TiNew}(S::SparseMatrixCSC{Tv,Ti}, ::Type{TvNew}, ::Type{TiNew}) = SparseMatrixCSC(S.m, S.n, convert(Array{TiNew},S.colptr), convert(Array{TiNew}, S.rowval), Array{TvNew}(length(S.nzval))) -similar{Tv}(S::SparseMatrixCSC, ::Type{Tv}, d::Dims) = spzeros(Tv, d...) +@inline similar{Tv}(S::SparseMatrixCSC, ::Type{Tv}, d::Dims) = spzeros(Tv, d...) function convert{Tv,Ti,TvS,TiS}(::Type{SparseMatrixCSC{Tv,Ti}}, S::SparseMatrixCSC{TvS,TiS}) if Tv == TvS && Ti == TiS @@ -1239,7 +1239,8 @@ function spzeros(Tv::Type, Ti::Type, m::Integer, n::Integer) ((m < 0) || (n < 0)) && throw(ArgumentError("invalid Array dimensions")) SparseMatrixCSC(m, n, ones(Ti, n+1), Array{Ti}(0), Array{Tv}(0)) end - +# de-splatting variant +spzeros(Tv::Type, Ti::Type, sz::Tuple{Integer,Integer}) = spzeros(Tv, Ti, sz[1], sz[2]) speye(n::Integer) = speye(Float64, n) speye(T::Type, n::Integer) = speye(T, n, n) @@ -1647,16 +1648,16 @@ end broadcast{Tv1,Ti1,Tv2,Ti2}(f::Function, A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = - broadcast!(f, spzeros(promote_type(Tv1, Tv2), promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))...), A_1, A_2) + broadcast!(f, spzeros(promote_type(Tv1, Tv2), promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) -broadcast_zpreserving!(args...) = broadcast!(args...) -broadcast_zpreserving(args...) = broadcast(args...) +@inline broadcast_zpreserving!(args...) = broadcast!(args...) +@inline broadcast_zpreserving(args...) = broadcast(args...) broadcast_zpreserving{Tv1,Ti1,Tv2,Ti2}(f::Function, A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = - broadcast_zpreserving!(f, spzeros(promote_type(Tv1, Tv2), promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))...), A_1, A_2) + broadcast_zpreserving!(f, spzeros(promote_type(Tv1, Tv2), promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) broadcast_zpreserving{Tv,Ti}(f::Function, A_1::SparseMatrixCSC{Tv,Ti}, A_2::Union{Array,BitArray,Number}) = - broadcast_zpreserving!(f, spzeros(promote_eltype(A_1, A_2), Ti, to_shape(broadcast_shape(A_1, A_2))...), A_1, A_2) + broadcast_zpreserving!(f, spzeros(promote_eltype(A_1, A_2), Ti, to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) broadcast_zpreserving{Tv,Ti}(f::Function, A_1::Union{Array,BitArray,Number}, A_2::SparseMatrixCSC{Tv,Ti}) = - broadcast_zpreserving!(f, spzeros(promote_eltype(A_1, A_2), Ti, to_shape(broadcast_shape(A_1, A_2))...), A_1, A_2) + broadcast_zpreserving!(f, spzeros(promote_eltype(A_1, A_2), Ti, to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) ## Binary arithmetic and boolean operators @@ -1676,7 +1677,7 @@ for (op, pro) in ((+, :eltype_plus), throw(DimensionMismatch("")) end Tv = ($pro)(A_1, A_2) - B = spzeros(Tv, promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))...) + B = spzeros(Tv, promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))) $body B end @@ -1718,15 +1719,15 @@ end # macro (.^)(A::Array, B::SparseMatrixCSC) = (.^)(A, full(B)) .+{Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = - broadcast!(+, spzeros(eltype_plus(A_1, A_2), promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))...), A_1, A_2) + broadcast!(+, spzeros(eltype_plus(A_1, A_2), promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) function .-{Tva,Tia,Tvb,Tib}(A::SparseMatrixCSC{Tva,Tia}, B::SparseMatrixCSC{Tvb,Tib}) - broadcast!(-, spzeros(eltype_plus(A, B), promote_type(Tia, Tib), to_shape(broadcast_shape(A, B))...), A, B) + broadcast!(-, spzeros(eltype_plus(A, B), promote_type(Tia, Tib), to_shape(broadcast_shape(A, B))), A, B) end ## element-wise comparison operators returning SparseMatrixCSC ## -.<{Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = broadcast!(<, spzeros( Bool, promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))...), A_1, A_2) -.!={Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = broadcast!(!=, spzeros( Bool, promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))...), A_1, A_2) +.<{Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = broadcast!(<, spzeros( Bool, promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) +.!={Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = broadcast!(!=, spzeros( Bool, promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) ## full equality function ==(A1::SparseMatrixCSC, A2::SparseMatrixCSC) From 5a52ff3a4a362df0cb9a11249e3eaabfb749e372 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 7 Jul 2016 11:44:32 -0500 Subject: [PATCH 14/15] Fixes for indices1(::Subarray) and indices1 specializations This leads to noticeable performance improvements for several benchmarks --- base/abstractarray.jl | 3 ++- base/subarray.jl | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 5bbc4d3a58d2a..b8feeffa4d745 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -45,7 +45,8 @@ function indices{T,N}(A::AbstractArray{T,N}) map(s->OneTo(s), size(A)) end -indices1(A) = (@_inline_meta; indices(A, 1)) +indices1{T}(A::AbstractArray{T,0}) = OneTo(1) +indices1{T}(A::AbstractArray{T}) = indices(A)[1] """ linearindices(A) diff --git a/base/subarray.jl b/base/subarray.jl index e12e3e039db1e..e9a4cf1c57765 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -283,11 +283,12 @@ _indices_sub(S::SubArray, dim::Int) = () _indices_sub(S::SubArray, dim::Int, ::Real, I...) = (@_inline_meta; _indices_sub(S, dim+1, I...)) _indices_sub(S::SubArray, dim::Int, ::Colon, I...) = (@_inline_meta; (indices(parent(S), dim), _indices_sub(S, dim+1, I...)...)) _indices_sub(S::SubArray, dim::Int, i1::AbstractArray, I...) = (@_inline_meta; (indices(i1)..., _indices_sub(S, dim+1, I...)...)) -indices1{T}(S::SubArray{T,0}) = OneTo(1) indices1(S::SubArray) = (@_inline_meta; _indices1(S, 1, S.indexes...)) +_indices1(S::SubArray, dim) = OneTo(1) _indices1(S::SubArray, dim, i1::Real, I...) = (@_inline_meta; _indices1(S, dim+1, I...)) _indices1(S::SubArray, dim, i1::Colon, I...) = (@_inline_meta; indices(parent(S), dim)) _indices1(S::SubArray, dim, i1::AbstractArray, I...) = (@_inline_meta; indices1(i1)) +_indices1{T}(S::SubArray, dim, i1::AbstractArray{T,0}, I...) = (@_inline_meta; _indices1(S, dim+1, I...)) ## Compatability # deprecate? From 1b2f11f666be79d75cd342ee6292b4c36abc559d Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 7 Jul 2016 16:28:05 -0500 Subject: [PATCH 15/15] Update the docs --- doc/stdlib/arrays.rst | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/doc/stdlib/arrays.rst b/doc/stdlib/arrays.rst index f2deb6bbf98be..960cca00318a7 100644 --- a/doc/stdlib/arrays.rst +++ b/doc/stdlib/arrays.rst @@ -43,18 +43,6 @@ Basic functions Returns the valid range of indices for array ``A`` along dimension ``d``\ . -.. function:: shape(A) - - .. Docstring generated from Julia source - - Returns a tuple specifying the "shape" of array ``A``\ . For arrays with conventional indexing (indices start at 1), this is equivalent to ``size(A)``\ ; otherwise it is equivalent to ``indices(A)``\ . - -.. function:: shape(A, d) - - .. Docstring generated from Julia source - - Specifies the "shape" of the array ``A`` along dimension ``d``\ . For arrays with conventional indexing (starting at 1), this is equivalent to ``size(A, d)``\ ; for arrays with unconventional indexing (indexing may start at something different from 1), it is equivalent to ``indices(A, d)``\ . - .. function:: length(A) -> Integer .. Docstring generated from Julia source @@ -283,31 +271,31 @@ Constructors 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 - See also ``allocate_for``\ . - -.. function:: allocate_for(storagetype, referencearray, [shape]) +.. function:: similar(storagetype, indices) .. Docstring generated from Julia source - Create an uninitialized mutable array analogous to that specified by ``storagetype``\ , but with type and shape specified by the final two arguments. The main purpose of this function is to support allocation of arrays that may have unconventional indexing (starting at other than 1), as determined by ``referencearray`` and the optional ``shape`` information. + Create an uninitialized mutable array analogous to that specified by ``storagetype``\ , but with ``indices`` specified by the last argument. ``storagetype`` might be a type or a function. .. code-block:: julia **Examples**: - allocate_for(Array{Int}, A) + similar(Array{Int}, indices(A)) - creates an array that "acts like" an ``Array{Int}`` (and might indeed be backed by one), but which is indexed identically to ``A``\ . If ``A`` has conventional indexing, this will likely just call ``Array{Int}(size(A))``\ , but if ``A`` has unconventional indexing then the indices of the result will match ``A``\ . + creates an array that "acts like" an ``Array{Int}`` (and might indeed be backed by one), but which is indexed identically to ``A``\ . If ``A`` has conventional indexing, this will be identical to ``Array{Int}(size(A))``\ , but if ``A`` has unconventional indexing then the indices of the result will match ``A``\ . .. code-block:: julia - allocate_for(BitArray, A, (shape(A, 2),)) + similar(BitArray, (indices(A, 2),)) would create a 1-dimensional logical array whose indices match those of the columns of ``A``\ . - The main purpose of the ``referencearray`` argument is to select a particular array type supporting unconventional indexing (as it is possible that several different ones will be simultaneously in use). + .. code-block:: julia + + similar(dims->zeros(Int, dims), indices(A)) - See also ``similar``\ . + would create an array of ``Int``\ , initialized to zero, matching the indices of ``A``\ . .. function:: reinterpret(type, A)