Skip to content

Commit 7291c1f

Browse files
authored
Specialize map for eltype conversion (#231)
* specialize map for eltype conversion * use unwrap and parent_call
1 parent bff8e7b commit 7291c1f

File tree

2 files changed

+30
-8
lines changed

2 files changed

+30
-8
lines changed

src/OffsetArrays.jl

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,14 @@ if VERSION < v"1.6"
306306
end
307307
end
308308

309+
# Utils to translate a function to the parent while preserving offsets
310+
unwrap(x) = x, identity
311+
unwrap(x::OffsetArray) = parent(x), data -> OffsetArray(data, x.offsets)
312+
function parent_call(f, x)
313+
parent, wrap_offset = unwrap(x)
314+
wrap_offset(f(parent))
315+
end
316+
309317
Base.similar(A::OffsetArray, ::Type{T}, dims::Dims) where T =
310318
similar(parent(A), T, dims)
311319
function Base.similar(A::AbstractArray, ::Type{T}, shape::Tuple{OffsetAxisKnownLength,Vararg{OffsetAxisKnownLength}}) where T
@@ -385,7 +393,7 @@ parentindex(r::IdOffsetRange, i) = i - r.offset
385393
end
386394

387395
@propagate_inbounds Base.getindex(A::OffsetArray{<:Any,N}, c::Vararg{Colon,N}) where N =
388-
OffsetArray(A.parent[c...], A.offsets)
396+
parent_call(x -> getindex(x, c...), A)
389397

390398
# With one Colon we use linear indexing.
391399
# In this case we may forward the index to the parent, as the information about the axes is lost
@@ -417,7 +425,7 @@ end
417425
end
418426

419427
Base.in(x, A::OffsetArray) = in(x, parent(A))
420-
Base.copy(A::OffsetArray) = OffsetArray(copy(A.parent), A.offsets)
428+
Base.copy(A::OffsetArray) = parent_call(copy, A)
421429

422430
Base.strides(A::OffsetArray) = strides(parent(A))
423431
Base.elsize(::Type{OffsetArray{T,N,A}}) where {T,N,A} = Base.elsize(A)
@@ -512,6 +520,10 @@ for OR in [:IIUR, :IdOffsetRange]
512520
end
513521
end
514522

523+
# eltype conversion
524+
# This may use specialized map methods for the parent
525+
Base.map(::Type{T}, O::OffsetArray) where {T} = parent_call(x -> map(T, x), O)
526+
515527
# mapreduce is faster with an IdOffsetRange than with an OffsetUnitRange
516528
# We therefore convert OffsetUnitRanges to IdOffsetRanges with the same values and axes
517529
function Base.mapreduce(f, op, As::OffsetUnitRange{<:Integer}...; kw...)
@@ -692,7 +704,7 @@ end
692704
# Adapt allows for automatic conversion of CPU OffsetArrays to GPU OffsetArrays
693705
##
694706
import Adapt
695-
Adapt.adapt_structure(to, x::OffsetArray) = OffsetArray(Adapt.adapt(to, parent(x)), x.offsets)
707+
Adapt.adapt_structure(to, O::OffsetArray) = parent_call(x -> Adapt.adapt(to, x), O)
696708

697709
if Base.VERSION >= v"1.4.2"
698710
include("precompile.jl")

test/runtests.jl

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,6 @@ for Z in [:ZeroBasedRange, :ZeroBasedUnitRange]
7878
end
7979
end
8080

81-
Base.@propagate_inbounds function Base.getindex(A::Array, r::ZeroBasedUnitRange)
82-
B = A[parent(r)]
83-
OffsetArrays._maybewrapoffset(B, axes(r))
84-
end
85-
8681
function same_value(r1, r2)
8782
length(r1) == length(r2) || return false
8883
for (v1, v2) in zip(r1, r2)
@@ -782,6 +777,13 @@ end
782777
@test axes(A) == (IdentityUnitRange(-1:1),)
783778
end
784779

780+
@testset "unwrap" begin
781+
for A in [ones(2, 2), ones(2:3, 2:3), ZeroBasedRange(1:4)]
782+
p, f = OffsetArrays.unwrap(A)
783+
@test f(map(y -> y^2, p)) == A.^2
784+
end
785+
end
786+
785787
@testset "Traits" begin
786788
A0 = [1 3; 2 4]
787789
A = OffsetArray(A0, (-1,2)) # IndexLinear
@@ -1750,6 +1752,14 @@ end
17501752
@test dest[1,7] == 2
17511753
@test dest[1,8] == 4
17521754
@test dest[1,9] == -2
1755+
1756+
@testset "eltype conversion" begin
1757+
a = OffsetArray(1:2, 1)
1758+
b = map(BigInt, a)
1759+
@test eltype(b) == BigInt
1760+
@test b == a
1761+
@test b isa OffsetArrays.OffsetRange
1762+
end
17531763
end
17541764

17551765
@testset "reductions" begin

0 commit comments

Comments
 (0)