-
Notifications
You must be signed in to change notification settings - Fork 307
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
Better reshaping, part 1: .to_shape() #982
Conversation
@jturner314 What do you think about these aspects of the shape + order argument?
It would be fast to just add |
I think this is fine. While it is true that the order parameter is being used in two different ways -- The primary case where I could see us wanting different options for these two cases would be if we wanted to support directly specifying strides for Another case is that we may want to define a
I agree that an enum works well for this. By the way, the idea of using associated consts for enum variant aliases is pretty clever. I haven't seen that before.
Personally, I don't think I'd ever use an
Yes, I think it's okay to "overload" the meaning of the order. In both cases, it represents a mapping from an n-D index to a 1-D index. It's basically equivalent to strides, except for the lack of holes in the 1-D space. I think that it would be worth adding a more flexible We could also consider allowing axis inversions in the order, but that's less important than the "axes order" variant, since inversions don't change the shape. (It's simple to call
First, it's worth noting that these will be written as a. once we drop support for using tuples to represent shapes, so the parentheses in the I wouldn't mind requiring the order parameter for We could remove support for using One more thought -- another use for the |
Thanks!
|
Okay, that's fine with me. I think the default should definitely be C order rather than e.g. an "automatic" (layout-dependent) order.
Yes, that will probably always be true for small ndims. I just assumed that we'd drop support for tuples at some point, because I don't see much reason to continue supporting them except for ease of upgrading to the next breaking release. Personally, I don't mind breaking changes in a breaking release as long as (1) there's at least a compiler warning (preferably an error) about the breakage and (2) it's easy to update the code. In the case of dropping tuple support, there will be a compiler error for each tuple, and the fix is to change There are a few reasons to drop support for tuples:
|
I'm setting up this PR to be just the non-breaking addition of What you said now brings up the future breaking change to |
Oh, that's a good point. I agree that the order for We should do what we can to avoid a silent breaking change, because that could cause a real headache for someone using Fortran-layout arrays if they don't notice the relevant item in the release notes. I see a few options:
I like option 1 the best, because it will result in a compilation error, and users will need to update their code only once (unless they use the temporary trait as a bound for something). Hopefully, if we keep the mitigation in place for a couple of breaking releases, we'll catch the vast majority of users. |
It seems there is no perfect solution for I'm contemplating now your solution (1) with the addition of an opt-in feature flag - |
The feature flag is a good idea. The primary issue is that if any crate in the build enables the feature, all crates depending on that version of |
Just have a question: as for non-standard C or Fortran arrays, can Order become smarter? Look at the following examples:
In these cases, it is completely feasible to output an ArrayView instead of a new Array. Therefore we can avoid array traversal, and these cases can also be supported in |
Yes, that's certainly a desired feature in to_shape and into_shape if it's not too complicated to implement. It's something that can be added after these initial implementations. For changes like I think a rule can be developed, I'd see that you walk through the incoming and outgoing dimensions and strides and "consume" them part by part and build up the corresponding stride sequence for the new shape. Potentially starting in the front/back depending on if it's C or F ordering. |
I thought about this problem again, and I think it can be implemented according to the following rules:
|
I'd suggest a preprocessing step on the input array to merge as many axes as possible toward the "inner" axes (according to the desired order). I think @bluss is adding functionality like this in #979. Then, you don't have to worry about merging axes of the input at the same time as dealing with the output shape/strides. |
Actually as soon as I had mentioned some details I went to trying to implement it, so I have a work in progress going too. i think it's already working for both C/F order and quite general (can handle things like 3, 4, 5 to 15, 4 and so on). We can compare code when I have time to post. It's a relief to get this functionality. |
58f2d06
to
f797045
Compare
b70085c
to
a0282ba
Compare
To generalize over Forward/Reverse indexing of dimensions, add simple traits Sequence/Mut that allow indexing forwards and reversed references of dimension values. In a future change, they will support strides (with isize values) too.
This function can compute the strides needed and if it's possible to reshape an array with given strides into a different shape. This happens using a given Order::{C, F}.
This is ready to merge |
New features:
.to_shape(impl IntoDimension) -> CowArray
.to_shape((impl IntoDimension, Order)) -> CowArray
to_shape
can return a view in all cases where it's technically possible to do so, saving copying. This is a big step forwards compared to previousinto_shape
and similar (and this improvement will eventually land in all the reshaping methods.)to_shape
returns a view if possible otherwise copies into an owned array. Always succeeds if the number of elements is correct.Method parameters:
.to_shape<Sh>(shape: Sh) where Sh: ShapeArg
(1, 2, 3)
like before. This uses the default Order.((1, 2, 3), Order::RowMajor)
to specify orderOrder enum:
RowMajor
(with aliasC
; this is the default if not specified)ColumnMajor
(with aliasF
)Notes
It's important to have reshaping methods that can do so without any clone bound or similar. For example a reshape operation that can be used on everything from raw views, views and other arrays.
into_shape
or similar should fill this need.Coming in a later change:
A version of
into_shape
that uses theOrder
parameter and that can always reshape owned arrays..into_shape() -> "Self"
(Same array storage type)into_shape
transforms the input but can only do so (for views) if the memory layout is exactly correct. ForArray
and uniquely ownedArcArray
, it always succeeds if the number of elements is correct.Order::Automatic
Order::Automatic
has been explored (and implemented) but is not part of this version of the change. No use case has been presented.Partially addresses #390