# How to Sensibly Implement Generic Slice Indexing for Custom Array Type

**URL:** <https://discourse.julialang.org/t/how-to-sensibly-implement-generic-slice-indexing-for-custom-array-type/109289>\
**Category:** General Usage\
**Tags:** question, gpu, performance, design\
**Created:** [January 26, 2024, 8:53am UTC](https://discourse.julialang.org/t/how-to-sensibly-implement-generic-slice-indexing-for-custom-array-type/109289 "2024-01-26T08:53:00Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![AlexanderNenninger](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexandernenninger/32/24602_2.png) [@AlexanderNenninger](https://discourse.julialang.org/u/AlexanderNenninger)\
**Post date:** [January 26, 2024, 8:53am UTC](https://discourse.julialang.org/t/how-to-sensibly-implement-generic-slice-indexing-for-custom-array-type/109289/1 "2024-01-26T08:53:00Z")

</div>

I have a custom array type that I want to implement custom `Base.getindex` for:

```julia
struct MyReadOnlyArray{T, N} <: AbstractArray{T, N}
    ...
    size::NTuple{N, Int}
end

```

Not it needs to be n-dimensional. For single entries, I can just implement

```julia
function Base.getindex(A::MyReadOnlyArray{T,N}, I::Vararg{Int,N}) where {T,N}
    ... # go reasonably fast sequentially
end

```

and the standard library gives me all of the fancy indexing Julia has to offer.

Now this custom array type can really profit from offloading the calculation of large slices to the GPU, e.g.

```julia
function Base.getindex(A::MyReadOnlyArray{T,N}, rows::Vector{CartesianIndex}, columns::Vector{CartesianIndex}) where {T,N}
... # go fast in parallel
end

```

I could of course provide methods for all kinds of indexing operations, but that’s not scalable. Continuing the example, I’d have to write

```julia
function Base.getindex(A::MyReadOnlyArray{T,N}, rows::Vector{CartesianIndex}, columns::UnitRange{Int}) where {T,N}
... # go fast in parallel too
end

function Base.getindex(A::MyReadOnlyArray{T,N}, rows::Vector, columns::UnitRange{Int}, stack::Int) where {T,N}
... # go fast in parallel too
end

```

Ignoring implementation details, _What type signature do I give specialized methods such that `getindex` will dispatch to the fast implementation for slices without writing one for each index combination separately?_

---

<div class="post-metadata">

**Author:** ![AlexanderNenninger](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexandernenninger/32/24602_2.png) [@AlexanderNenninger](https://discourse.julialang.org/u/AlexanderNenninger)\
**Post date:** [January 27, 2024, 11:56am UTC](https://discourse.julialang.org/t/how-to-sensibly-implement-generic-slice-indexing-for-custom-array-type/109289/2 "2024-01-27T11:56:55Z")

</div>

At least for a specific number of indices I can write

```julia

"""
    catindex(rowindex::Union{CartesianIndex{M},NTuple{M,T}}, columnindex::Union{CartesianIndex{N},NTuple{N,T}}) where {M,N,T}

Concatenate various indexing types.
"""
function catindex(rowindex::Union{CartesianIndex{M},NTuple{M,T}}, columnindex::Union{CartesianIndex{N},NTuple{N,T}}) where {M,N,T}
    ntuple(function (k)
            if k <= M
                rowindex[k]
            else
                columnindex[k-M]
            end
        end,
        M + N)
end

function catindex(rowindex::Union{CartesianIndex{M},NTuple{M,T}}, columnindex::T) where {M,T}
    ntuple(function (k)
            if k <= M
                rowindex[k]
            else
                columnindex
            end
        end,
        M + 1)
end

function catindex(rowindex::T, columnindex::Union{CartesianIndex{N},NTuple{N,T}}) where {N,T}
    ntuple(function (k)
            if k == 1
                rowindex
            else
                columnindex[k-1]
            end
        end,
        1 + N)
end

function catindex(rowindex::T, colindex::T) where {T<:Integer}
    NTuple((rowindex, colindex))
end

function Base.getindex(A::MyReadOnlyArray{T,N}, rows, columns) where {T,N}
    rows = collect(rows)
    columns = collect(columns)
    # map over index combinations in parallel
    ....
end

```

This works for combinations of `Int`, `Iterator`, and `Vec` of `Int`, `Tuple`, and `CartesianIndex`.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [January 27, 2024, 6:33pm UTC](https://discourse.julialang.org/t/how-to-sensibly-implement-generic-slice-indexing-for-custom-array-type/109289/3 "2024-01-27T18:33:45Z")

</div>

A tuple isn’t usually a [supported index](https://docs.julialang.org/en/v1/manual/arrays/#man-supported-index-types) for `AbstractArray`s. Maybe [`to_indices`](https://docs.julialang.org/en/v1/base/arrays/#Base.to_indices) can help simplify the indices (it’s how `:` and multiple `CartesianIndex` are handled) for dispatch and processing, but it’s beyond me how this would work into GPU-accelerated slicing of a seemingly non-GPU array with non-GPU-array indices.

---

<div class="post-metadata">

**Author:** ![AlexanderNenninger](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexandernenninger/32/24602_2.png) [@AlexanderNenninger](https://discourse.julialang.org/u/AlexanderNenninger)\
**Post date:** [January 29, 2024, 8:38am UTC](https://discourse.julialang.org/t/how-to-sensibly-implement-generic-slice-indexing-for-custom-array-type/109289/4 "2024-01-29T08:38:01Z")

</div>

The underlying array doesn’t really exist. I am working on an accelerated version of this: [Function as Type Parameter - #6 by AlexanderNenninger](https://discourse.julialang.org/t/function-as-type-parameter/102378/6). Can’t post code due to IP contracts with my employer, but once I have the indices as arrays of numbers/tuples/Cartesian indices I can work with them.
