Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support CartesianIndex offset #132

Closed
johnnychen94 opened this issue Aug 15, 2020 · 8 comments · Fixed by #147
Closed

support CartesianIndex offset #132

johnnychen94 opened this issue Aug 15, 2020 · 8 comments · Fixed by #147

Comments

@johnnychen94
Copy link
Member

johnnychen94 commented Aug 15, 2020

The following usage could be helpful to deal with multi-dimensional arrays

OffsetArray(rand(4, 4), CartesianIndex(-1, -1))

I could do it later if you think this is a good idea.

@jishnub
Copy link
Member

jishnub commented Aug 19, 2020

One may convert it to a Tuple in the constructor:

OffsetArray(rand(4, 4), Tuple(CartesianIndex(-1, -1)))

or extract the underlying Tuple as

OffsetArray(rand(4, 4), CartesianIndex(-1, -1).I)

I wonder if this calls for a specialized constructor?

@johnnychen94
Copy link
Member Author

There are two behaviors wrt this as alternatives:

  1. an convenient interface that is equivalent to Tuple: OffsetArray(A::AbstractArray, offset::CartesianIndex) = OffsetArray(A, offset.I)
  2. offset the input array so that CartesianIndex(first.(axes(Ao))) == origin: CartesianIndex as an indicator of the origin of the offseted array.

When I opened I was thinking of the first semantic, but now I kind of prefer the second as it brings new functionalities. (and it reasons well to me) Thoughts?

@jishnub
Copy link
Member

jishnub commented Aug 30, 2020

I like this idea, it removes the extra step required to compute the offset corresponding to a specific origin. I just wonder if this might be non-intuitive? Eg if broadcasting is required, it's been suggested that one should use Tuple(I) instead of a CartesianIndex I, as the former supports broadcasting. In this case one might expect Tuple(CartesianIndex(1)) .+ 0 and CartesianIndex(1) to behave identically when passed as arguments to the constructor. However, if I understand correctly, the former would have axes

julia> OffsetVector(ones(2), Tuple(CartesianIndex(1)) .+ 0) |> axes
(2:3,)

whereas the latter would have axes

julia> OffsetVector(ones(2), CartesianIndex(1)) |> axes
(1:2,)

Then again, a CartesianIndex is not supposed to be viewed as a container, but as a scalar entity in itself. This might allow us to use it in this context. Thoughts?

@johnnychen94
Copy link
Member Author

Yes, this is a bit non-intuitive (or more accurately speaking, ambiguous), especially for those who are not familiar with the purpose of CartesianIndex. The logic behind reasons well to me.

it removes the extra step required to compute the offset corresponding to a specific origin

If we go with option 1, we'd still need such a helper, probably in other names.

I don't have a strong opinion here, ping @timholy for a triage

@timholy
Copy link
Member

timholy commented Aug 31, 2020

Interesting questions. I'd say that having CartesianIndex behaving differently from tuples would be a bit too confusing. But we could add a offset = ... keyword.

@timholy timholy closed this as completed Aug 31, 2020
@timholy timholy reopened this Aug 31, 2020
@timholy
Copy link
Member

timholy commented Aug 31, 2020

Sorry, finger slipped on the mouse pad and hit the wrong button. 😛

@jishnub
Copy link
Member

jishnub commented Sep 7, 2020

I guess this confusion is avoided by using a different type that behaves analogous to a CartesianIndex? Eg we may define a struct Origin that wraps a Tuple and behaves as a CartesianIndex. This doesn't need to be as functional as a CartesianIndex as it is not to be used for indexing, merely for construction.

Taking this one step further, I wonder if it might make sense to have a special constructor where all the dimensions have the same origin. This might make the transition from zero-based indexing easier. Something like this might work:

julia> struct NBasedIndexing
       n :: Int
       end;

julia> function OffsetArrays.OffsetArray(arr::AbstractArray{<:Any,N}, indsorigin::NBasedIndexing) where {N}
           OffsetArray(arr, ntuple(d -> indsorigin.n - first(axes(arr, d)), Val{N}()))
       end;

julia> const ZeroBasedIndexing = NBasedIndexing(0);

julia> OffsetArray(zeros(3,2), ZeroBasedIndexing)
3×2 OffsetArray(::Array{Float64,2}, 0:2, 0:1) with eltype Float64 with indices 0:2×0:1:
 0.0  0.0
 0.0  0.0
 0.0  0.0

@johnnychen94
Copy link
Member Author

This sounds like a good idea to me. I can play with it in the next couple of days.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants