ModularIndices.jl

Modular (periodic) indices for Julia arrays
Author ericphanson
Popularity
2 Stars
Updated Last
1 Year Ago
Started In
July 2019

ModularIndices

Build Status Codecov

Note: this package was made before I realized you could do e.g. A[mod1(4, end)] without any external packages, which probably suffices for most cases.

A very simple package (26 lines of code before comments, docstring, and tests) with one export: Mod. This is an object using for indexing, like Colon from Base, and Not from InvertedIndices.jl. Mod provides an easy way to have wrap-around indexing of vectors and arrays (which can otherwise be annoying with 1-based indexing).

Usage:

julia> A = rand(3)
3-element Array{Float64,1}:
 0.523471984061487
 0.3975791533002422
 0.3230510641200286

julia> A[Mod(4)]
0.523471984061487

julia> A[4]
ERROR: BoundsError: attempt to access 3-element Array{Float64,1} at index [4]
Stacktrace:
 [1] getindex(::Array{Float64,1}, ::Int64) at ./array.jl:729
 [2] top-level scope at none:0

Just like regular indexing, Mod accepts

  • scalars likeA[Mod(1)] (i.e. type Int),
  • ranges like A[Mod(1:2)] (AbstractRange{Int})
  • and vectors like A[Mod([1,2])] (AbstractVector{Int}). A non-allocating alternative is also provided here, namely A[Mod(1,2)] == A[Mod([1,2])].

and is able to index into collections A which are indexable and use Base.to_indices to process the indices (which I think mostly are AbstractArray's). For example, A could be an Array, OffsetArray, SubArray, StaticArray, etc.

This package should possibly be called PeriodicIndices.jl and Mod renamed to Periodic or similar.

This is similar to FFTViews.jl, but instead of constructing a periodic view type into an array, it provides an indexing object.

The code is heavily inspired by InvertedIndices.jl (but it's actually much simpler to do modular indexing than inverted indexing), and the idea for Mod was discussed on https://github.com/JuliaLang/julia/issues/32571.

Examples

julia> A = 1:3
1:3

julia> A[Mod(4)]
1

julia> A[Mod(2:4)]
3-element Array{Int64,1}:
 2
 3
 1

julia> A = reshape(1:8, 2, 4)
2×4 reshape(::UnitRange{Int64}, 2, 4) with eltype Int64:
 1  3  5  7
 2  4  6  8

julia> A[Mod(4),2]
 4

Works with OffsetArrays.jl too:

julia> using OffsetArrays

julia> A = OffsetArray([1,2,3], -1)
OffsetArray(::Array{Int64,1}, 0:2) with eltype Int64 with indices 0:2:
 1
 2
 3

julia> A[3]
ERROR: BoundsError: attempt to access OffsetArray(::Array{Int64,1}, 0:2) with eltype Int64 with indices 0:2 at index [3]
Stacktrace:
 [1] throw_boundserror(::OffsetArray{Int64,1,Array{Int64,1}}, ::Tuple{Int64}) at ./abstractarray.jl:484
 [2] checkbounds at ./abstractarray.jl:449 [inlined]
 [3] getindex(::OffsetArray{Int64,1,Array{Int64,1}}, ::Int64) at /Users/eh540/.julia/packages/OffsetArrays/vIbpP/src/OffsetArrays.jl:135
 [4] top-level scope at none:0

julia> A[Mod(3)]
1