I’m excited to share that I’ve been working on Einops.jl, bringing the popular einops tensor manipulation syntax to Julia. While Julia’s array interface is already excellent thanks to multiple dispatch, there’s still value in having a more declarative way to express complex tensor operations.
What it does
Instead of chaining primitives:
reshape(repeat(permutedims(reshape(x, head_dim, :, size(x)[2:3]...), (1, 3, 2, 4)), inner=(1, 1, repeats, 1)), head_dim, size(x, 2), :)
You declare the pattern:
repeat(x, einops"(dim head) len batch -> dim len (r head batch)", d=head_dim, r=repeats)
# or without `@einops_str`:
repeat(x, ((:dim, :head), :len, :batch) --> (:dim, :len, (:r, :head, :batch)), d=head_dim, r=repeats)
(I’m slightly more generous to the Base primitives in the README)
Key points
-
Zero overhead: Expands directly to Base primitives like
reshape
,permutedims
,repeat
using generated functions, and thus it should necessarily be compatible with all array types. -
No unnecessary sub-operations: Only applies operations when necessary (e.g. don’t permute if just reshaping).
-
Type stable: Patterns are embedded in a
ArrowPattern{L,R}
orVal{pattern}
for specialization, depending on the function, but theeinops
string macro is meant to hide this. -
Self-documenting: Dimensions are explicitly named.
-
Familiar syntax: If you’ve used einops in Python, you already know the API, and porting to Julia becomes a breeze. Even if you haven’t, it will quickly click.
The package handles common operations like rearranging, reducing, and repeating dimensions (main focus) but also some extra stuff for parity, including a binding to the neat OMEinsum.jl package.
There is also TensorCast.jl, a superset of Einops.jl in terms of functionality, which uses a more sophisticated explicit macro syntax. In my experience, macros often slightly obscure what’s going on, so I created Einops.jl to serve a different niche – predictable and lightweight, with a well-defined scope.
Still exploring lazy permutations, but the core functionality is solid and ready for use. Feedback and contributions are welcome!