Repository navigation
Unify slicing on single-axis Axis[L].at selectors: remove the tuple f… - #173
Conversation
…orm and take (now slice(Axis[L].at(indices))), and add the window selector Axis[L].at(start, windowSize), replacing Tensor1.dynamicSlice. set supports every selector and requires the value to have the shape of the selection.
|
I like the idea of cleaning up the selectors. But I am not sure if using slice for everything is a move in the right direction. Slice has now many different use cases, from selecting a single element over a continuous region to selecting irregularly spaced elements in the tensor. I wonder if we could instead use the following convention:
To me the difference of take/slice seems meaningful, as I would not associate taking elements 1, 3, 7 from a 1d-tensor with slice. |
|
I agree that Additionally, So, I propose:
|
`tensor(Axis[L].at(...))` forwards to select, so tensors read like Scala collections (`apply`/`updated`). Updates tests, examples and docs.
@marcelluethi I added a commit with my proposal |
marcelluethi
left a comment
There was a problem hiding this comment.
This looks good to me. Maybe we should keep the old methods (slice and take) around, forward their implementation to select and mark them as deprecated? what do you think?
| res.slice((Axis[C].at(1), Axis[D].at(0))) should approxEqual(AB) | ||
| res.slice((Axis[C].at(0), Axis[D].at(1))) should approxEqual(AB) | ||
| res.slice((Axis[C].at(1), Axis[D].at(1))) should approxEqual(AB) | ||
| res.select(Axis[C].at(0)).select(Axis[D].at(0)) should approxEqual(AB) |
There was a problem hiding this comment.
Here one slice is replaced by two selects. Is this intended and do we know how it affects the performance / memory overhead?
There was a problem hiding this comment.
Yes this was dropped in the first commit of this PR. I removed it as the main branch version is partly buggy and led to very complex implementation of "slice" - and this was never used so far (See "Remove tuple selectors from slice and set" in first comment of PR). It could be that doing two slices is slower. I also removed the same for set where there is no easy alternative.
The motivation of removal is that I overdid it in the first implementation (current main), leading to a partly correct, badly tested, and badly implemented, yet general version. We will probably add this generality back at some point, but I see it less important right now than focusing on keeping the supported features correct and clean.
There was a problem hiding this comment.
@marcelluethi As this comment highlights a breaking change in this PR I requested a re-review. To check if you are fully on board with this before I merge into main. I did not change code.
No we should not. I am for removing over deprecation as long as we are below v1.0.0. |
API change: Before → now
x[i]t.slice(Axis[L].at(i))L)x[i],lax.dynamic_index_in_dimt.slice(Axis[L].at(i: Tensor0[Int32]))L)x[a:b:s],lax.slicet.slice(Axis[L].at(a until b by s))L)x[[i, j]]t.slice(Axis[L].at(Seq(i, j)))orat((i, j))L)jnp.take(x, idx, axis)t.take(Axis[L])(idx)t.slice(Axis[L].at(idx: Tensor1[L2, Int32])),Lreplaced byL2lax.dynamic_sliceTensor1only:t.dynamicSlice(start, size)t.slice(Axis[L].at(start, windowSize)), any rank and axis;startis anIntor aTensor0[Int32]x[i, a:b]t.slice((Axis[L].at(i), Axis[K].at(a until b)))t.slice(Axis[L].at(i)).slice(Axis[K].at(a until b))x.at[...].set(v)t.set(selector)(v)for index, traced index, range, list, or a tuplet.set(selector)(v)for every selector above, including a vector of indices,at((i, j))and a window (lax.dynamic_update_slice)x.at[i, j].set(5.0)t.set((Axis[A].at(i), Axis[B].at(j)))(5f)Breaking changes
Remove tuple selectors from
sliceandsett.slice((Axis[A].at(Seq(0, 1)), Axis[B].at(Seq(0, 1))))returned the diagonal: runtime shape(2,), while the type claimed(A, B). Chained calls select every combination, as the type says.lax.dynamic_slice, not to indexing, so it could only have joined the tuple through special cases in the index builder.Other breaking changes
t.take(Axis[L])(idx)→t.slice(Axis[L].at(idx))t.dynamicSlice(start, size)(Tensor1only) →t.slice(Axis[L].at(start, size))Axis[L].at(a, b)without double parentheses now selects a window; a tuple of indices is writtenAxis[L].at((a, b)).setrequires the value to have the shape of the selection, instead of letting JAX broadcast it.AxesConditionalRemoveris removed; only the tuple form used it.