Learn R Programming

einops

Flexible and powerful tensor operations for readable and reliable code. Supports R's built-in arrays and torch::torch_tensor's.

Installation

einops is available on CRAN:

install.packages("einops")

For the latest semi-stable development version try:

devtools::install_github("Qile0317/einops@dev")

Usage

einops has a minimalistic yet powerful API.

Three core operations provided (einops tutorial shows those cover stacking, reshape, transposition, squeeze/unsqueeze, repeat, tile, concatenate, view and numerous reductions)

library(einops)
# rearrange elements according to the pattern
output_tensor <- rearrange(input_tensor, 't b c -> b c t')
# combine rearrangement and reduction
output_tensor <- reduce(input_tensor, 'b c (h h2) (w w2) -> b h w c', 'mean', h2 = 2, w2 = 2)
# copy along a new axis (note: repeat is surrounded by ticks. einops.repeat() works too)
output_tensor <- `repeat`(input_tensor, 'h w -> h w c', c = 3)

A MAJOR CAVEAT is that R's base::arrays are column-major, while Python's multidimensional arrays are row-major. This is reflected in their respective indexing operations. To fully replicate a python expression using a column-major array, each of the three core functions have a .row_major optional argument which replicates the python row-major behaviour during einops operations, while ensuring the output is still column-major.

Vignettes

The vignette is the most convenient way to see einops in action

Kapil Sachdeva recorded a small intro to einops for the python version.

Naming

einops stands for Einstein-Inspired Notation for operations (though "Einstein operations" is more attractive and easier to remember).

Notation was loosely inspired by Einstein summation (in particular by numpy.einsum operation).

Why use einops notation

Semantic information (being verbose in expectations)

y <- x; dim(y) <- c(dim(y)[1], prod(dim(y)[-1]))
y <- rearrange(x, 'b c h w -> b (c h w)')

While these two lines are doing the same job in some context, the second one provides information about the input and output. In other words, einops focuses on interface: what is the input and output, not how the output is computed.

The next operation looks similar:

y <- rearrange(x, 'time c h w -> time (c h w)')

but it gives the reader a hint: this is not an independent batch of images we are processing, but rather a sequence (video).

Semantic information makes the code easier to read and maintain.

Convenient checks

Reconsider the same example:

y <- x; dim(y) <- c(dim(y)[1], prod(dim(y)[-1])) # x: (batch, 256, 19, 19)
y <- rearrange(x, 'b c h w -> b (c h w)')

The second line checks that the input has four dimensions, but you can also specify particular dimensions. That's opposed to just writing comments about shapes since comments don't prevent mistakes, not tested, and without code review tend to be outdated

y <- x; dim(y) <- c(dim(y)[1], prod(dim(y)[-1])) # x: (batch, 256, 19, 19)
y = rearrange(x, 'b c h w -> b (c h w)', c = 256, h = 19, w = 19)

Result is strictly determined

Below we have at least two ways to define the depth-to-space operation

# depth-to-space
rearrange(x, 'b c (h h2) (w w2) -> b (c h2 w2) h w', h2 = 2, w2 = 2)
rearrange(x, 'b c (h h2) (w w2) -> b (h2 w2 c) h w', h2 = 2, w2 = 2)

There are at least four more ways to do it. Which one is used by the underlying code?

These details are ignored, since usually it makes no difference, but it can make a big difference (e.g. if you use grouped convolutions in the next stage), and you'd like to specify this in your code.

Uniformity

reduce(x, 'b c (x dx) -> b c x', 'max', dx = 2)
reduce(x, 'b c (x dx) (y dy) -> b c x y', 'max', dx = 2, dy = 3)
reduce(x, 'b c (x dx) (y dy) (z dz) -> b c x y z', 'max', dx = 2, dy = 3, dz = 4)

These examples demonstrated that we don't use separate operations for 1d/2d/3d pooling, those are all defined in a uniform way.

Space-to-depth and depth-to space are defined in many frameworks but how about width-to-height? Here you go:

rearrange(x, 'b c h (w w2) -> b c (h w2) w', w2 = 2)

Citing Einops (for R)

If using the R implementation of Einops in your research work, please cite the original ICLR paper, in addition to this package with these bibtex entries:

@Misc{yang2025einops,
    title = {Einops for R},
    author = {Qile Yang},
    year = {2025},
    url = {https://qile0317.github.io/einops/},
}

@inproceedings{rogozhnikov2022einops,
    title={Einops: Clear and Reliable Tensor Manipulations with Einstein-like Notation},
    author={Alex Rogozhnikov},
    booktitle={International Conference on Learning Representations},
    year={2022},
    url={https://openreview.net/forum?id=oapKSVM2bcj}
}

Development

A Devcontainer and Dockerfile is provided in the sourcecode, this environment can be used locally, or on your server, or within github codespaces. To start with devcontainers in vs code, clone repo, and click 'Reopen in Devcontainer'.

All building, installing, and documentation can be generated with the standard tidyverse devtools pipeline, using functions such as devtools::document() and devtools::test()

If you'd like to contribute, Github pull requests from forked branches are more than welcome as it is mostly a solo-project at the moment. For major changes, please open an issue first to discuss what you would like to change. Please also make sure to update tests as appropriate.

Copy Link

Version

Install

install.packages('einops')

Version

0.2.1

License

MIT + file LICENSE

Issues

Pull Requests

Stars

Forks

Maintainer

Qile Yang

Last Published

September 3rd, 2025

Functions in einops (0.2.1)

UnderscoreAstNode

Create an UnderscoreAstNode
as_axis_names

Convert an AstNode into an AxisNames() object
as_iterables

Convert an object to a list of iterables (lists)
einop

Perform Einstein-style tensor operations
get_ordered_axis_names

Get the ordered axis names from a OneSidedAstNode, removing all nesting.
get_identifiers

Given a OneSidedAstNode object, get unique identifiers
create_execution_plan

Reconstruct all actual parameters using shape.
expand_ellipsis

Expand ellipses of an EinopsAst
EinopsAst

Create an EinopsAst root node
get_ungrouped_nodes

get flat list of all AstNodes in order, including children of GroupAstNodes.
prepare_transformation_recipe

Create the Transformation Recipe for an einops call
pprint

Pretty Print
apply_recipe

Apply a TransformRecipe to a Tensor
EinopsBackend

Base Backend Class for Einops Tensor Operations
parse_shape

Parse a tensor shape to dictionary mapping axes names to their lengths.
%>%

Pipe operator
ConstantAstNode

Create a ConstantAstNode
tail.GroupAstNode

Get the last n children of a GroupAstNode as a list of AstNodes.
append

Vector Merging
einops_image

Example 4D Image Tensor for Einops
rearrange

Reader-friendly smart element reordering for multidimensional tensors.
reduce

Rearrangement and reduction in one step
find_top_level_arrow_index

Find the arrow in a token sequence
get_backend

Main function to detect and return backend
thunk

Simple thunk: wraps an input in a no-argument function
NamedAxisAstNode

Create a NamedAxisAstNode
image_tensor

Image Tensor: A thin wrapper around 2-4D arrays
parse_onesided_ast

Parse a one-sided einops pattern into OneSidedAstNode
register_backend

Register a new backend for a tensor type
parse_einops_ast

Parse einops pattern into AST
validate_reduction_operation

Syntactically Validate the reduction operation in the einops expression
repr

Python-like Representation of Objects as Strings
einops-package

einops: Flexible, Powerful, and Readable Tensor Operations
einops.repeat

Allows reordering elements and repeating them in arbitrary combinations.
OneSidedAstNode

Create a OneSidedAstNode (wrapper for input/output axes lists)
parse_axes_iter

Parse a sequence of axis tokens
lex

Lexically analyze einops pattern into structured tokens
BackendRegistry

Singleton Backend Registry, managing all available backends.
EinopsTokenSequence

EinopsTokenSequence constructor
GroupAstNode

Create a GroupAstNode
AxisNames

Construct an instance of an AxisNames class
EllipsisAstNode

Create an EllipsisAstNode
AddOnlyOrderedMap

Create an AddOnlyOrderedMap instance
EinopsExecutionPlan

Constructor for an Execution Plan (CookedRecipe in the python sourcecode)
TransformRecipe

TransformRecipe S3 constructor