What the backward pass and the optimizer step do to a Fourier-domain layer: the gradient of a convolution is a correlation, the weight gradient multiplies two data-dependent spectra, and the masks change every step. A training-step Amdahl analysis for transformer, Hyena-2, hybrid and block-circulant models, with a live explorer.
Concepts used here, and where they are explained. Each links to a glossary entry: this series glossary, or the Fourier Optics for Inference glossary and the Simulation Engineering Toolkit's for concepts those series already explain.
The inference series asked what share of a forward pass a Fourier-optical transform engine could take (FOptInf 02). Training runs the same forward pass and then two more kinds of work:
| Phase | For a layer y = W x | FLOPs, matmul layer | Counted? |
|---|---|---|---|
| Forward | y = W x, keeping x for later | 2 per weight per token | yes |
| Backward, dX | the activation gradient WT G, passed to the layer below | 2 per weight per token | yes |
| Backward, dW | the weight gradient G XT, summed over every token of the step | 2 per weight per token | yes |
| Optimizer | AdamW: a few element-wise operations per parameter, once per step | per parameter, not per token | no (as norms are not) |
Before trusting the ledger on new layers, check it on the old one. Training a dense model costs about 6N FLOPs per token (2 forward, 2 for dX, 2 for dW), plus an attention term that grows with the sequence (Kaplan et al., arXiv:2001.08361; the same accounting appears in the Chinchilla paper's appendix, Hoffmann et al., arXiv:2203.15556). Here N counts the parameters that multiply activations: the 8,029,995,008 of the untied model less the 128,256 × 4,096 input embedding, which is a lookup.
| Sequence | This script (GFLOP/token) | 6N + attention (GFLOP/token) | Ratio |
|---|---|---|---|
| 2,048 | 46.64 | 46.64 | 1.0000 |
| 4,096 | 48.25 | 48.25 | 1.0000 |
| 8,192 | 51.47 | 51.47 | 1.0000 |
| 32,768 | 70.80 | 70.80 | 1.0000 |
Source: results.md, section “1. Cross-check: the 6N rule”; the pytest suite checks the ratio to 1e-12.
FOptInf 02 counted the forward pass. The short version:
A Llama-style transformer is matmuls, attention, norms and activations. A Fourier transform engine (as against an optical MAC, which would speed every matmul) has nothing to do; its share is zero, forward or backward.
Long-convolution models (Hyena, Poli et al., arXiv:2302.10866) mix tokens by FFT. The inference series found that for a Hyena-2 model of Llama-3-8B's shape the transform work is 0.20% of prefill FLOPs at 2,048 tokens: the FFT is what makes Hyena cheap, and the projections and MLP dominate.
This script's forward column agrees: Hyena-2 at 2,048 tokens, 0.20% of the forward pass (slide 08). The physics of the engine (the lens as a Fourier transformer, the 4f system, masks and SLMs, ENOB) is in FOptInf 01 and is not repeated here.
Training has three passes for every one of inference, and FFT convolution was introduced for these models to make training affordable at length. Perhaps the backward pass is where transforms pile up? The next slides derive the backward pass of a long convolution, then count. The answer is no: the share goes down.
One channel of a causal long convolution, length L, with the output gradient g = ∂ℒ/∂y arriving from above:
forward y[i] = Σk h[k] u[i−k] Y = H · U
input grad ∂ℒ/∂u[j] = Σi g[i] ∂y[i]/∂u[j] = Σi g[i] h[i−j]
dU = G · conj(H)
filter grad ∂ℒ/∂h[k] = Σi g[i] u[i−k] dH = G · conj(U)
| Length | max abs error dU (spectral vs direct) | max abs error dH | dU vs finite difference |
|---|---|---|---|
| 64 | 6.2e-15 | 7.1e-15 | 1.4e-08 |
| 1,000 | 5.7e-14 | 5.7e-14 | 7.1e-08 |
Source: results.md, section “6. Numeric checks of the backward formulas”. The spectral formulas agree with direct sums to rounding; the finite-difference column is limited by the difference step, not the formula. Length 1,000 is not a power of two: it is padded to 2,048.
Mapped onto a 4f system (lens, Fourier-plane mask, lens; FOptInf 01, “The 4f System”), one channel of one layer needs three passes per training sequence. What matters is what sits on the mask:
The ledger splits every Fourier-domain product by its operands, because that decides whether a mask written once per optimizer step can hold it:
| Class in the ledger | Products | Mask written |
|---|---|---|
| transform | FFTs and inverse FFTs | none (the lenses) |
| weight-masked | U·H forward, G·conj(H) backward | once per step, per direction |
| data-by-data | G·conj(U) for the weight gradient | once per sequence (per token for block-circulant) |
Static share = transforms plus weight-masked products, over all step FLOPs: what an engine whose masks change only at optimizer steps could take. The optical share adds the data-by-data products as well.
The inference series' one large-share case made every projection and MLP matrix block-circulant (CirCNN, Ding et al., arXiv:1708.08917; block 256 here, illustrative): each block is defined by one vector, so a block product is an FFT, a pointwise product and an inverse FFT. Training applies the same split per block:
| Pass | Per token, per block row and column | Class |
|---|---|---|
| Forward | FFT the input blocks; multiply by each weight block's spectrum Wpq; sum over input blocks; IFFT the output blocks | transform, weight-masked |
| Backward dX | FFT the gradient blocks; multiply by conj(Wpq); sum; IFFT | transform, weight-masked |
| Backward dW | multiply Gp by conj(Xq), accumulated over every token of the step; one IFFT per block per step | data-by-data |
| Once per step | FFT of every weight block after the update; IFFT of every block gradient | transform, amortised |
No published LLM of this size is block-circulant throughout, as far as the inference series found. Whether such a model trains to useful quality is unknown, and training it would itself be the experiment.
Share of FLOPs that is transform work in the forward pass, the backward pass and the whole step; the static share; the data-by-data part of the optical work; and the Amdahl bounds 1/(1−share). The transformer is 0.00% throughout and omitted.
| Variant | Sequence | Fwd share | Bwd share | Step share | Static share | Data-by-data part of optical | Amdahl (all) | Amdahl (static) |
|---|---|---|---|---|---|---|---|---|
| Hyena-2 | 2,048 | 0.20% | 0.10% | 0.14% | 0.13% | 2.32% | 1.001x | 1.001x |
| Hyena-2 | 4,096 | 0.21% | 0.11% | 0.15% | 0.14% | 2.15% | 1.001x | 1.001x |
| Hyena-2 | 8,192 | 0.23% | 0.12% | 0.16% | 0.15% | 2.01% | 1.002x | 1.002x |
| Hyena-2 | 32,768 | 0.27% | 0.14% | 0.18% | 0.18% | 1.75% | 1.002x | 1.002x |
| Hybrid 1:3 attention:Hyena | 2,048 | 0.15% | 0.08% | 0.10% | 0.10% | 2.32% | 1.001x | 1.001x |
| Hybrid 1:3 attention:Hyena | 4,096 | 0.16% | 0.08% | 0.11% | 0.11% | 2.15% | 1.001x | 1.001x |
| Hybrid 1:3 attention:Hyena | 8,192 | 0.17% | 0.09% | 0.12% | 0.11% | 2.01% | 1.001x | 1.001x |
| Hybrid 1:3 attention:Hyena | 32,768 | 0.18% | 0.09% | 0.12% | 0.12% | 1.75% | 1.001x | 1.001x |
| Hyena-2 + block-circulant weights | 2,048 | 13.99% | 11.06% | 12.06% | 9.53% | 20.98% | 1.137x | 1.105x |
| Hyena-2 + block-circulant weights | 4,096 | 14.18% | 11.16% | 12.19% | 9.66% | 20.73% | 1.139x | 1.107x |
| Hyena-2 + block-circulant weights | 8,192 | 14.36% | 11.26% | 12.32% | 9.79% | 20.49% | 1.140x | 1.109x |
| Hyena-2 + block-circulant weights | 32,768 | 14.75% | 11.47% | 12.59% | 10.07% | 19.98% | 1.144x | 1.112x |
Source: results.md, section “2. A training step”. The hybrid's ratio and the circulant block size are illustrative.
Every row of the table has the same shape: backward share well below forward share, step share in between. The ledger shows why, per Hyena channel and per matmul:
| Work | Forward | Backward | Backward / forward |
|---|---|---|---|
| Long FFTs per channel | FFT(u), IFFT(y) | FFT(G), IFFT(dU) | the same |
| Fourier-domain products per channel | U·H | G·conj(H), G·conj(U) | twice |
| Matmuls (projections, MLP, LM head) | W x | WT G and G XT | twice |
Training does not rescue the transform share. FFT convolution makes long-context training cheap, and that is exactly why it is a small part of what a training step spends.
The FLOP ledger of train_flop_share.py (step_per_sequence for the four variants, its op classes and the section-4 mask budget), ported to JavaScript and tested to give identical numbers (js/train_flop_model.js, checked by analysis/test_js_parity.py). The default state reproduces the results.md section 2 row “Hyena-2 + block-circulant weights, 4,096”: 14.18%, 11.16%, 12.19%, 9.66%, 20.73%, 1.139x, 1.107x.
Things to try: move the step size and watch the shares stay put while the digital step time grows and the weight-mask frames do not; then switch to the transformer and see the bars turn entirely digital. Sequences beyond the model's trained context are shape-only extrapolations, and the 64-GPU cluster and mask device are illustrative.
An inference engine writes its filter spectra once and keeps them. A training engine must rewrite every weight spectrum after every optimizer step, for the forward pass (H) and the backward pass (conj(H)). On the inference series' illustrative device, a 2-megapixel micromirror device at 1,031 Hz with 8-bit depth, holding one complex value per pixel (optimistic):
| Variant | Sequence | Sequences/step | Digital step time | Weight-mask frames (fwd + bwd) | Time | Data-by-data frames | Time |
|---|---|---|---|---|---|---|---|
| Hyena-2 | 2,048 | 512 | 2.05 s | 538 | 0.52 s | 137,507 | 133 s |
| Hyena-2 | 8,192 | 128 | 2.05 s | 2,148 | 2.08 s | 137,456 | 133 s |
| Hyena-2 + block-circulant weights | 2,048 | 512 | 0.15 s | 554 | 0.54 s | 397,218 | 385 s |
| Hyena-2 + block-circulant weights | 8,192 | 128 | 0.15 s | 2,164 | 2.10 s | 397,168 | 385 s |
Source: results.md, section “4. Holding the masks”. Digital step time = step FLOPs on an illustrative 64 GPUs at 400 TFLOP/s sustained each.
The frame counts above assume the most favourable schedule: layer-major order, in which every sequence of the step passes through one layer while that layer's mask is loaded, before the mask is rewritten for the next layer.
Whether a cluster can run layer-major for the layers that touch the engine, and what that does to memory, parallelism and step time, is the subject of deck 03, “Optical Engines in a Training Cluster”.
| Work | Why a Fourier transform engine does not help |
|---|---|
| Attention, forward and backward | Data-dependent, causal, softmax-normalised products: not convolutions. The backward pass recomputes or stores the score matrix and does twice the forward's matmul work; none of it is a transform |
| Dense projections, MLP, LM head (dX and dW) | General matmuls, now three times over: work for an optical MAC, not a Fourier engine, unless the weights are structured (slide 07) |
| Weight gradients of Fourier-domain layers | Data-by-data products (slide 06): a mask write per sequence, or per token for block-circulant |
| Norms, activations, softmax and the loss | Non-linear; stay digital, forward and backward |
| The optimizer's element-wise work | AdamW (Loshchilov and Hutter, arXiv:1711.05101) updates each parameter from its gradient and two moment estimates: element-wise arithmetic, not a transform |
| AdamW's states | Two moments per parameter, kept in memory between steps: a capacity and bandwidth problem. A Fourier-plane mask is not a store for them |
| MoE routing | Data-dependent dispatch of tokens to experts, with its own load-balancing gradients: control flow, not a convolution |
| Gradient all-reduce | Communication. DeMo DCT-compresses the momentum before it is sent, but its transforms are 4.76e-06 of a step's FLOPs: the saving is in bytes, the subject of deck 03 and the Compute in Transit series. That saving has been measured (a 15.8M-parameter GPT on TinyStories, 8 simulated workers): DeMo's fixed DCT band cuts what a worker originates 16× against a dense gradient, but the bytes it puts on the wire only 4×, because an all-gather's traffic grows with the number of workers and an all-reduce's does not. It costs convergence: final validation loss 3.12–3.16 for the fixed band against 1.646 for AdamW (CiT results, what each worker sends; CiT 04) |
And the hidden costs that apply even where it maps, now per step instead of once: mask rewrites, conversions per value, and the precision a gradient format asks for (deck 02).