@@ -298,14 +298,6 @@ if VERSION < v"1.6"
298
298
end
299
299
end
300
300
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
-
309
301
Base. similar (A:: OffsetArray , :: Type{T} , dims:: Dims ) where T =
310
302
similar (parent (A), T, dims)
311
303
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} =
372
364
Base. falses (inds:: NTuple{N, Union{Integer, AbstractUnitRange}} ) where {N} =
373
365
fill! (similar (BitArray, inds), false )
374
366
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)
377
369
378
370
# # Indexing
379
371
@@ -393,7 +385,7 @@ parentindex(r::IdOffsetRange, i) = i - r.offset
393
385
end
394
386
395
387
@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)
397
389
398
390
# With one Colon we use linear indexing.
399
391
# In this case we may forward the index to the parent, as the information about the axes is lost
425
417
end
426
418
427
419
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)
429
421
430
422
Base. strides (A:: OffsetArray ) = strides (parent (A))
431
423
Base. elsize (:: Type{OffsetArray{T,N,A}} ) where {T,N,A} = Base. elsize (A)
530
522
531
523
# eltype conversion
532
524
# 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)
534
526
Base. map (:: Type{T} , r:: IdOffsetRange ) where {T<: Real } = _indexedby (map (T, UnitRange (r)), axes (r))
535
527
if eltype (IIUR) === Int
536
528
# 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
634
626
_no_offset_view (:: Any , A:: AbstractArray ) = OffsetArray (A, Origin (1 ))
635
627
_no_offset_view (:: Any , A:: AbstractUnitRange ) = UnitRange (A)
636
628
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
+
637
688
# ####
638
689
# center/centered
639
690
# These two helpers are deliberately not exported; their meaning can be very different in
@@ -702,6 +753,8 @@ instead use [`center`](@ref OffsetArrays.center).
702
753
"""
703
754
centered (A:: AbstractArray , r:: RoundingMode = RoundDown) = OffsetArray (A, .- center (A, r))
704
755
756
+ # might be available in Base: https://github.com/JuliaLang/julia/issues/35543
757
+ _basetype (:: Type{T} ) where T = Base. typename (T). wrapper
705
758
706
759
# ###
707
760
# work around for segfault in searchsorted*
786
839
# Adapt allows for automatic conversion of CPU OffsetArrays to GPU OffsetArrays
787
840
# #
788
841
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)
790
843
791
844
if Base. VERSION >= v " 1.4.2"
792
845
include (" precompile.jl" )
0 commit comments