Valuable to have 2 slides on einsum, and how it is done in numpy.
---
title: "Understanding einsum Notation"
subtitle: "Einstein Summation & NumPy Operations"
format:
revealjs:
theme: dark
slide-number: true
transition: slide
---
## Einsum Notation Anatomy
Einstein summation simplifies tensor operations by representing contractions through index labels.
::: {.columns}
::: {.column width="50%"}
### Expression Structure
`np.einsum('ij,jk->ik', A, B)`
* **Input Labels (`'ij,jk'`):** Defines 2D input tensor axes for $A$ ($i \times j$) and $B$ ($j \times k$).
* **Arrow Operator (`'->'`):** Explicitly defines output axis ordering.
* **Free Indices (`'i,k'`):** Appear in output subscript; preserved in result dimensions.
* **Summation Index (`'j'`):** Repeated in inputs, omitted from output $\rightarrow$ **contracted/summed over**.
:::
::: {.column width="50%"}
### Execution Modes & Rules
::: {.callout-note icon=false}
#### Explicit Mode
Using `->` explicitly controls output shape (e.g., `'i,i->'` produces a scalar sum).
:::
::: {.callout-tip icon=false}
#### Implicit Mode
Omitting `->` automatically sums repeated indices and orders remaining indices alphabetically.
:::
::: {.callout-important icon=false}
#### Efficiency
Executes in high-performance C loops without allocating intermediate product matrices.
:::
:::
:::
---
## NumPy Methods vs. Einsum Equivalents
Mapping standard multi-dimensional array operations directly to `np.einsum` expressions.
| Operation | Standard NumPy Call | Equivalent `np.einsum` | Math Notation |
|:---|:---|:---|:---|
| **Vector Inner Product** | `np.dot(u, v)` | `np.einsum('i, i ->', u, v)` | $\sum_{i} u_i v_i$ |
| **Outer Product** | `np.outer(u, v)` | `np.einsum('i, j -> ij', u, v)` | $u_i v_j$ |
| **Matrix Multiplication** | `A @ B` | `np.einsum('ij, jk -> ik', A, B)` | $\sum_{j} A_{ij} B_{jk}$ |
| **Matrix Transpose** | `A.T` | `np.einsum('ij -> ji', A)` | $A_{ji}$ |
| **Matrix Trace** | `np.trace(A)` | `np.einsum('ii ->', A)` | $\sum_{i} A_{ii}$ |
| **Extract Diagonal** | `np.diag(A)` | `np.einsum('ii -> i', A)` | $A_{ii}$ |
| **Batch MatMul (3D)** | `A @ B` | `np.einsum('bij, bjk -> bik', A, B)` | $\sum_{j} A_{bij} B_{bjk}$ |
| **Sum Along Axis** | `A.sum(axis=0)` | `np.einsum('ij -> j', A)` | $\sum_{i} A_{ij}$ |
::: {.footer}
Render with: `quarto render einsum_presentation.qmd`
:::
Valuable to have 2 slides on einsum, and how it is done in numpy.
Something like: