Implement getindex for array indexing and range indexing

I have a struct which should behave like a matrix. I want to have all the standard indexing methods julia has, like A[:, j], A[1:3, j], A[[1,3,4], j] and so on.

Currently I have the following (simplified)

function Base.getindex(A::myMatrix, i::Int, j::Int)
    #Doing some stuff
    A(i, j)
end

Should I implement Base.getindex(A::myMatrix, I::Vector, j::Int) and Base.getindex(A::myMatrix, I::Range, j::Int) and the similar versions for j?
Or is there a simple way to make the standard indexing available for a custom struct?

Thank you.

Check the section on the AbstractArray interface. So long as myMatrix <: AbstractArray (or in your case, likely <: AbstractMatrix), the interface page tells you that you’ll get array/range indexing automatically. This will be implemented by looping over the scalar indexing you already defined.

If there are significant performance savings to be had from batching these multi-index accesses, you may consider implementing specialized methods for doing so. Exactly what to implement and for what input types is up to you and depends on the sort of savings you’re after.

If such batching efficiencies truly exist, I’d start with adding a Base.getindex(A::myMatrix, i, j) method with untyped i and j and write it generically for that. You can continue to add increasingly specialized methods from there, until you decide there’s no longer significant benefit. In very few cases will the types of i or j need to be more specialized than AbstractVector.

2 Likes

Thank you! I got the following

struct myMatrix <: AbstractMatrix{Float64}
    # definitions
    m::Int
    n::Int
end
Base.size(M::myMatrix) = (M.m, M.n)
Base.getindex(M::myMatrix, i::Int, j::Int) = #some stuff

This seems to work. The manual mentions Base.IndexStyle(). What is this for? Is necessary to implement this?

An AbstractArray in Julia is allowed to have one of two preferred indexing schemes: Cartesian or Linear. IndexLinear() is efficient when applicable and applies to normal Julia Array types because of the way they’re stored in memory (the memory is snaked through the columns to form the array), but IndexCartesian() is more general.

julia> collect(CartesianIndices(zeros(2,3)))
2×3 Matrix{CartesianIndex{2}}:
 CartesianIndex(1, 1)  CartesianIndex(1, 2)  CartesianIndex(1, 3)
 CartesianIndex(2, 1)  CartesianIndex(2, 2)  CartesianIndex(2, 3)

julia> collect(LinearIndices(zeros(2,3)))
2×3 Matrix{Int64}:
 1  3  5
 2  4  6

As you can see, the LinearIndices of an array are of the form a:b (but snake through the columns) while the CartesianIndices represent each coordinate explicitly.

Julia tries to access arrays using their preferred mode (since one is usually more efficient than the other). IndexCartesian() arrays implement getindex(x,coordinate1,coordinate2,coordinate3...) while IndexLinear() arrays implement getindex(x,linearindex). Julia will transform indexing operations to the preferred modality.

Since you wrote the method Base.getindex(::myMatrix, ::Int, ::Int), it sounds like you want Base.IndexStyle(::Type{<:myMatrix}) = IndexCartesian(). The reason you didn’t have write this is that it’s the default.

1 Like

Thank you for such a detailed explanation! I learned a lot.