arogozhnikov / einops

Flexible and powerful tensor operations for readable and reliable code (for pytorch, jax, TF and others)
https://einops.rocks
MIT License
8.54k stars 352 forks source link

Alias ellipsis to a star for rearrange #314

Open knyazer opened 7 months ago

knyazer commented 7 months ago

Hey, guys, the library is great!

But, there is a small non-intuitive thing that I consistently stumble upon (I mean, twice is consistent, right?). It is that for rearrange one have to use the ellipsis instead of a star to select the rest of dimensions. It seems a bit nicer to have an alias of ellipsis to a star, e.g.

rearrange('batch_dim_1 batch_dim_2 ... -> batch_dim_2 batch_dim_1 ...')
# proposal: rearrange('batch_dim_1 batch_dim_2 * -> batch_dim_2 batch_dim_1 *')

Probably I find the star notation more sensible, since I use jaxtyping a lot, and it uses the star notation for this. If you have a good argument for why the star should not be equivalent to the ellipsis, I am happy to just alias this in jaxtyping, so that the dimension selection convention is the same between the two: I guess many people use both of the libraries at the same time (at least I do :) )

P.S. I am new-ish with einops, so I might not know that this problem was already discussed a million times. In this case, just point me to the relevant discussion :smile:

arogozhnikov commented 7 months ago

Hi @knyazer

this problem was already discussed a million times

you'll be surprised, but it is the first time it is discussed in repo :)

First, how did we end up in where we are (that's how I reconstruct history) in chronological order

so multiple axes historically was always ellipsis, but introduction of star-unpacking makes it tempting to use star and align with less array-specific part of python.

Two considerations:

cc: @patrick-kidger

knyazer commented 7 months ago

Huh, interesting.


Next part is mostly outdated due to Patrick's comment, I had some misconceptions about jaxtyping…

Feel free to ignore it :)


I read about what the star does in einops, and it is equivalent to a underscore in jaxtyping! And ellipsis in einops is equivalent to a star in jaxtyping.

The only way I see to synchronize the APIs without breaking backwards compatibility, is to:

  1. Alias ellipsis to a star in jaxtyping, don't deprecate star, make ellipsis a "recommended" way.
  2. Alias underscore to a star in einops, prefer underscore over star.

Ta-dah! Now we can use underscore to mark a single arbitrary dimension, and an ellipsis to mark multiple arbitrary axes in both libraries.

But, this is a very significant API change. So, I don't know how you feel about that. However, I think it is useful: the difference between star and ellipsis is indeed confusing (to me). And underscore is probably a more pythonic way to show a single arbitrary axis (if you think about axis sizes as variables).

Whether star or ellipsis more pythonic for multiple dims is up to a debate, but only ellipsis does not break backwards compatibility, so there is not really a choice.

WDYT?

patrick-kidger commented 7 months ago

Hey folks!

So I think we have one baked-in inconsistency already. In einops, ... is (IIUC) a "named" group of dimensions: it must be the same on both sides. In jaxtyping, it is an anything-goes, and in particular can represent different groups of axes across its different usages. So if consistency was a goal then I think we'd both have to recommend avoiding ... entirely.

We'd then find some way of both libraries using * in compatible ways? FWIW a plain * on its own is not currently valid notation in jaxtyping, so we have an opportunity there. I'm not sure how important consistency really is between each other, but I'd be happy to tweak things if it's possible!

Frankly this inconsistency is my mistake. when I came up with the notation for jaxtyping, I completely forgot to check what notation einops was already using... 😅