# \[ANN\] Einops.jl: Concise, declarative tensor operations

**URL:** https://discourse.julialang.org/t/ann-einops-jl-concise-declarative-tensor-operations/130792
**Category:** Package Announcements
**Tags:** package, announcement, arrays, tensoroperations
**Created:** [July 16, 2025, 8:35pm UTC](https://discourse.julialang.org/t/ann-einops-jl-concise-declarative-tensor-operations/130792 "2025-07-16T20:35:37Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![AntonOresten](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/antonoresten/32/213750_2.png) [@AntonOresten](https://discourse.julialang.org/u/AntonOresten)
#### Post date: [July 16, 2025, 8:35pm UTC](https://discourse.julialang.org/t/ann-einops-jl-concise-declarative-tensor-operations/130792/1 "2025-07-16T20:35:38Z")

</div>

I’m excited to share that I’ve been working on [Einops.jl](https://github.com/MurrellGroup/Einops.jl), bringing the popular [einops](https://einops.rocks) 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:

```julia
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:

```julia
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}` or `Val{pattern}` for specialization, depending on the function, but the `einops` 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](https://github.com/under-Peter/OMEinsum.jl) package.

There is also [TensorCast.jl](https://github.com/mcabbott/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!

[Docs](https://MurrellGroup.github.io/Einops.jl/stable/) | [GitHub](https://github.com/MurrellGroup/Einops.jl) | [einops](https://einops.rocks)

---

<div class="post-metadata">

### Author: ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)
#### Post date: [July 16, 2025, 9:24pm UTC](https://discourse.julialang.org/t/ann-einops-jl-concise-declarative-tensor-operations/130792/2 "2025-07-16T21:24:46Z")

</div>

Welcome Anton!

> [@AntonOresten](#):
>
> There is also [TensorCast.jl](https://github.com/mcabbott/TensorCast.jl), a superset of [Einops.jl](https://juliaregistries.github.io/General/packages/redirect_to_repo/Einops) in terms of functionality, which uses a more sophisticated explicit macro syntax. In my experience, macros often slightly obscure what’s going on

Just a note here, you’re also using a macro, it just happens to be a string macro.

---

<div class="post-metadata">

### Author: ![AntonOresten](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/antonoresten/32/213750_2.png) [@AntonOresten](https://discourse.julialang.org/u/AntonOresten)
#### Post date: [July 16, 2025, 9:38pm UTC](https://discourse.julialang.org/t/ann-einops-jl-concise-declarative-tensor-operations/130792/3 "2025-07-16T21:38:36Z")

</div>

Yes, as I also noted. Perhaps I should’ve mentioned that there’s an alternative syntax using a `-->` operator, which the `einops` string macro expands to. You can write e.g. `(:a, :b, ..) --> (:b, :a, ..)` (hat tip, EllipsisNotation.jl) which puts both sides in type parameters so they can be specialized upon. This syntax is also just made up, of course, but I’ve come to favor the string macro simply because it’s slightly easier to write because of the colons, commas, and superfluous parentheses in the `-->` version. See also CallMode.jl if you despise parentheses.

---

<div class="post-metadata">

### Author: ![samuelsonric](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/samuelsonric/32/216687_2.png) [@samuelsonric](https://discourse.julialang.org/u/samuelsonric)
#### Post date: [July 17, 2025, 1:26am UTC](https://discourse.julialang.org/t/ann-einops-jl-concise-declarative-tensor-operations/130792/4 "2025-07-17T01:26:46Z")

</div>

Awesome! Are you optimizing the contraction schedule via OMEinsumContractionOrders.jl or EinExprs.jl or something similar?

---

<div class="post-metadata">

### Author: ![AntonOresten](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/antonoresten/32/213750_2.png) [@AntonOresten](https://discourse.julialang.org/u/AntonOresten)
#### Post date: [July 17, 2025, 7:40am UTC](https://discourse.julialang.org/t/ann-einops-jl-concise-declarative-tensor-operations/130792/5 "2025-07-17T07:40:16Z")

</div>

Thanks! and no, not currently, but I just opened a PR: [Optimize einsum contraction order by AntonOresten · Pull Request #30 · MurrellGroup/Einops.jl · GitHub](https://github.com/MurrellGroup/Einops.jl/pull/30).  
Any help on this front would be appreciated. Contraction order is nuanced, especially when I don’t want it to be user-facing. I acknowledge that Julia not in need of one more “One More Einsum” package! 😉
