Skip to content

Commit da91a54

Browse files
committed
WIP: allow multiple inputs
1 parent 7b880a9 commit da91a54

File tree

3 files changed

+69
-15
lines changed

3 files changed

+69
-15
lines changed

docs/src/reference.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ OffsetArrays.IdOffsetRange
99
OffsetArrays.no_offset_view
1010
OffsetArrays.centered
1111
OffsetArrays.center
12+
OffsetArrays.no_offset_view_apply
1213
OffsetArrays.AxisConversionStyle
1314
```

src/OffsetArrays.jl

+67-14
Original file line numberDiff line numberDiff line change
@@ -298,14 +298,6 @@ if VERSION < v"1.6"
298298
end
299299
end
300300

301-
# Utils to translate a function to the parent while preserving offsets
302-
unwrap(x) = x, identity
303-
unwrap(x::OffsetArray) = parent(x), data -> OffsetArray(data, x.offsets, checkoverflow = false)
304-
function parent_call(f, x)
305-
parent, wrap_offset = unwrap(x)
306-
wrap_offset(f(parent))
307-
end
308-
309301
Base.similar(A::OffsetArray, ::Type{T}, dims::Dims) where T =
310302
similar(parent(A), T, dims)
311303
function Base.similar(A::AbstractArray, ::Type{T}, shape::Tuple{OffsetAxisKnownLength,Vararg{OffsetAxisKnownLength}}) where T
@@ -372,8 +364,8 @@ Base.trues(inds::NTuple{N, Union{Integer, AbstractUnitRange}}) where {N} =
372364
Base.falses(inds::NTuple{N, Union{Integer, AbstractUnitRange}}) where {N} =
373365
fill!(similar(BitArray, inds), false)
374366

375-
Base.zero(A::OffsetArray) = parent_call(zero, A)
376-
Base.fill!(A::OffsetArray, x) = parent_call(Ap -> fill!(Ap, x), A)
367+
Base.zero(A::OffsetArray) = no_offset_view_apply(zero, A)
368+
Base.fill!(A::OffsetArray, x) = no_offset_view_apply(Ap -> fill!(Ap, x), A)
377369

378370
## Indexing
379371

@@ -393,7 +385,7 @@ parentindex(r::IdOffsetRange, i) = i - r.offset
393385
end
394386

395387
@propagate_inbounds Base.getindex(A::OffsetArray{<:Any,N}, c::Vararg{Colon,N}) where N =
396-
parent_call(x -> getindex(x, c...), A)
388+
no_offset_view_apply(x -> getindex(x, c...), A)
397389

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

427419
Base.in(x, A::OffsetArray) = in(x, parent(A))
428-
Base.copy(A::OffsetArray) = parent_call(copy, A)
420+
Base.copy(A::OffsetArray) = no_offset_view_apply(copy, A)
429421

430422
Base.strides(A::OffsetArray) = strides(parent(A))
431423
Base.elsize(::Type{OffsetArray{T,N,A}}) where {T,N,A} = Base.elsize(A)
@@ -530,7 +522,7 @@ end
530522

531523
# eltype conversion
532524
# This may use specialized map methods for the parent
533-
Base.map(::Type{T}, O::OffsetArray) where {T} = parent_call(x -> map(T, x), O)
525+
Base.map(::Type{T}, O::OffsetArray) where {T} = no_offset_view_apply(x -> map(T, x), O)
534526
Base.map(::Type{T}, r::IdOffsetRange) where {T<:Real} = _indexedby(map(T, UnitRange(r)), axes(r))
535527
if eltype(IIUR) === Int
536528
# This is type-piracy, but there is no way to convert an IdentityUnitRange to a non-Int type in Base
@@ -634,6 +626,65 @@ _no_offset_view(::Tuple{<:Base.OneTo,Vararg{<:Base.OneTo}}, A::AbstractUnitRange
634626
_no_offset_view(::Any, A::AbstractArray) = OffsetArray(A, Origin(1))
635627
_no_offset_view(::Any, A::AbstractUnitRange) = UnitRange(A)
636628

629+
# Utils to translate a function to the parent while preserving offsets
630+
function unwrap(x, xs...)
631+
AT1 = _basetype(typeof(x))
632+
all(x->AT1==_basetype(typeof(x)), xs) || throw(ArgumentError("All arrays should be homogeneous, i.e., have the same container type."))
633+
_unwrap(x, xs...)
634+
end
635+
function _unwrap(x::AT, xs...) where AT<:OffsetArray
636+
o = x.offsets
637+
all(x->o==x.offsets, xs) || throw(DimensionMismatch("All arrays should have the same offsets."))
638+
wrap_offset(data) = OffsetArray(data, o, checkoverflow=false)
639+
(parent(x), map(parent, xs)...), wrap_offset
640+
end
641+
_unwrap(xs...) = xs, identity
642+
"""
643+
no_offset_view_apply(f, As...) -> C
644+
645+
Apply one-output function `f` to [`no_offset_view`](@ref OffsetArrays.no_offset_view)s of `As` while
646+
preserve the offsets for its output.
647+
648+
For `OffsetArray`s input, it's almost equivalent to `OffsetArray(f(map(parent, As)...), As[1].offsets)`
649+
650+
# Examples
651+
652+
One can use this to convert the internal array type:
653+
654+
```jldoctest; setup=:(using OffsetArrays)
655+
Ao = OffsetArray(CartesianIndices((4, 4)), -1, -1);
656+
parent(Ao) isa Array # false
657+
658+
# collect(Ao): strips the offsets
659+
# Array(Ao): DimensionMismatch
660+
dense_Ao = OffsetArrays.no_offset_view_apply(Array, Ao);
661+
parent(dense_Ao) isa Array
662+
663+
# output
664+
true
665+
```
666+
667+
Also, one can do some 1-based mathematics on it:
668+
669+
```jldoctest; setup=:(using OffsetArrays)
670+
A = OffsetArray(rand(4, 4), -1, -1)
671+
B = OffsetArray(rand(4, 4), -1, -1)
672+
673+
C = OffsetArrays.no_offset_view_apply(A, B) do x, y
674+
x * y
675+
end
676+
677+
parent(C) == parent(A) * parent(B)
678+
679+
# output
680+
true
681+
```
682+
"""
683+
function no_offset_view_apply(f, xs...)
684+
parent, wrap_offset = unwrap(xs...)
685+
wrap_offset(f(parent...))
686+
end
687+
637688
#####
638689
# center/centered
639690
# These two helpers are deliberately not exported; their meaning can be very different in
@@ -702,6 +753,8 @@ instead use [`center`](@ref OffsetArrays.center).
702753
"""
703754
centered(A::AbstractArray, r::RoundingMode=RoundDown) = OffsetArray(A, .-center(A, r))
704755

756+
# might be available in Base: https://github.com/JuliaLang/julia/issues/35543
757+
_basetype(::Type{T}) where T = Base.typename(T).wrapper
705758

706759
####
707760
# work around for segfault in searchsorted*
@@ -786,7 +839,7 @@ end
786839
# Adapt allows for automatic conversion of CPU OffsetArrays to GPU OffsetArrays
787840
##
788841
import Adapt
789-
Adapt.adapt_structure(to, O::OffsetArray) = parent_call(x -> Adapt.adapt(to, x), O)
842+
Adapt.adapt_structure(to, O::OffsetArray) = no_offset_view_apply(x -> Adapt.adapt(to, x), O)
790843

791844
if Base.VERSION >= v"1.4.2"
792845
include("precompile.jl")

test/runtests.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -864,7 +864,7 @@ end
864864
@testset "unwrap" begin
865865
for A in [ones(2, 2), ones(2:3, 2:3), ZeroBasedRange(1:4)]
866866
p, f = OffsetArrays.unwrap(A)
867-
@test f(map(y -> y^2, p)) == A.^2
867+
@test f(map(y -> y^2, p...)) == A.^2
868868
end
869869
end
870870

0 commit comments

Comments
 (0)