Custom Indices in OffsetArrays

#1

Is it possible to declare an array whose indices leave out every second index, eg such that the set of indices is 0,2,4,6?

a[0] = 1
a[2] = 2
a[4] = 3
a[6] = 4

The naive try

using OffsetArrays
y=OffsetArray(collect(1:4),collect(0:2:6))

did not work

#2

I think that would be hard to implement.

struct MyOffsetVector{T,S} <: AbstractVector{T}
    data::Vector{T}
    offset::Int
end
@generated function offset_index(v::MyOffsetVector{T,S}, i) where {T,S}
    S == 1 && return :(i - v.offset + 1)
    log2S = log2(S)
    if round(log2S) == log2S
        return :( ((i - v.offset) >> $log2S) + 1)
    else
        return :( ((i - v.offset) ÷ $S) + 1)
    end
end
@inbounds Base.getindex(v::MyOffsetVector, i) = v.data[offset_index(v,i)]
@inbounds Base.setindex!(v::MyOffsetVector, i, x) = v.data[offset_index(v,i)] = x
Base.size(v::MyOffsetVector) = size(v.data)

You might want to replace offset_index with something that throws an error if there is a remainder (ie, use divrem, and throw an error for non-zero remainders).

I haven’t tested this, but I imagine you’ll see a lot of errors from here.
For example IndexStyle() is neither IndexLinear() or IndexCartesian(), meaning things like “print” will try generic Cartesian indices incrementing by a step at a time for printing and other array operations.

So if I wanted to do something like this, I’d probably look into defining a new IndexStyle.
AbstractArray Interface.

1 Like
#3

No, this violates the current interface, ie that axes returns a Tuple of AbstractUnitRange objects, which have a step of 1. See this discussion:

1 Like
#4

it is necessary? if its an even number, you can use a normal array and apply and operation over the index:

my_index(i) = Int(i/2)+1
a = 1:10
julia> a[my_index(0)] #=a[1]

if its not, you can raise an error or round to a corresponding integer

1 Like
#5

Yes, that is what I currently do.

No it is not necessary

#6

okay thank you for your clarification