Type at least 2 letters. Enter opens the first result, Esc closes.

    ML for Science · Lesson 8 · Neural operators, symmetry and physics

    Fourier spectral methods and Fourier neural operators

    Intermediate 100 min 4 interactive figures 6 videos Colab notebook

    Before this lesson: circulant matrices and the DFT (linear algebra 19); the method of lines, RK4 and its stability region (lesson 02). Complex exponentials: eikx=cos⁡kx+isin⁡kxe^{ikx} = \cos kx + i\sin kx. Time: about 100 minutes, plus the notebook.


    Why this matters #

    • Weather and climate models. ECMWF's IFS is a spectral transform model: it computes derivatives in spherical-harmonic space (lesson 09 is the spherical version of this lesson). NeuralGCM couples a spectral dynamical core with learned physics.
    • Neural operators. The FNO (Li et al., 2021) learns maps between functions, for example from an initial condition to the solution at a later time. FourCastNet's adaptive FNO (AFNO) and the spherical FNO (SFNO) are its descendants in weather models.
    • Training data. Many datasets for neural PDE solvers (Burgers, Navier–Stokes, Kuramoto–Sivashinsky) are produced by pseudo-spectral solvers like the one in this lesson.
    • One shared trap. A product in a solver and a pointwise activation in a network both create frequencies that the grid cannot hold. Understanding aliasing tells you when "resolution-invariant" is true and when it is not.

    1. Predict first #

    2. The picture #

    Derivatives become multiplications #

    A Fourier mode is an eigenfunction of the derivative:

    ddx eikx=ik eikx.\frac{d}{dx}\,e^{ikx} = ik\,e^{ikx}.

    So in Fourier space, d/dxd/dx is multiplication by ikik, and d2/dx2d^2/dx^2 is multiplication by −k2-k^2. A linear PDE with constant coefficients splits into one scalar ODE per mode. Take the heat equation ut=νuxxu_t = \nu u_{xx} with viscosity ν>0\nu > 0. The coefficient u^k\hat u_k of mode kk obeys u^k′=−νk2u^k\hat u_k' = -\nu k^2 \hat u_k, so u^k(t)=e−νk2t u^k(0)\hat u_k(t) = e^{-\nu k^2 t}\,\hat u_k(0). High modes decay fastest.

    On a grid this gives the spectral derivative: FFT, multiply mode kk by ikik, inverse FFT. It uses all nn grid points at once. A finite difference uses only neighbours.

    Interactive figure: loading…

    Move the slider for nn. Top: the exact derivative, the spectral derivative and the second-order finite difference at the grid points. Bottom: the largest error against nn on log–log axes. For the analytic function 1/(2−cos⁡x)1/(2 - \cos x) the spectral error curve bends down (faster than any power of nn) until round-off near 10−1510^{-15}; the finite-difference error is a straight line of slope −2-2. Switch to ∣sin⁡x∣3\lvert\sin x\rvert^3 and ∣sin⁡x∣\lvert\sin x\rvert: with less smoothness the advantage shrinks or disappears.

    Spectral accuracy, in words. A smooth periodic function has Fourier coefficients that decay very fast. nn grid points hold the lowest nn modes, so the error is about the size of the first mode that is left out. For an analytic function the coefficients fall like e−a∣k∣e^{-a\lvert k\rvert}, so the error falls like e−an/2e^{-an/2}: each doubling of nn roughly squares the error. For sin⁡x\sin x, every grid with n≥3n \ge 3 gives the exact derivative.

    Aliasing: a fast wave can hide as a slow one #

    nn grid points can tell apart only nn modes. On the grid xj=2πj/nx_j = 2\pi j/n, the waves eikxe^{ikx} and ei(k+n)xe^{i(k+n)x} take the same values at every grid point, because einxj=e2πij=1e^{inx_j} = e^{2\pi i j} = 1.

    Interactive figure: loading…

    Move kk above n/2n/2. The true wave sin⁡kx\sin kx is sampled at nn points (aqua dots). When k>n/2k > n/2, the same samples belong to a lower mode k′=k−n⋅round(k/n)k' = k - n\cdot\mathrm{round}(k/n) (dashed). The bottom panel is the folding map from kk to the mode the grid sees: a zigzag, like a strip of paper folded at ±n/2\pm n/2. Try k=7k = 7, n=8n = 8: the grid sees mode −1-1.

    Products create high modes. A product u⋅uu \cdot u of two fields with modes ∣k∣≤K\lvert k\rvert \le K has modes up to 2K2K. In Fourier space a product is a convolution of coefficient vectors, which costs O(n2)O(n^2). The pseudo-spectral method instead goes to the grid (inverse FFT), multiplies point by point (O(n)O(n)) and comes back (FFT): O(nlog⁡n)O(n\log n) in total. But the new modes above n/2n/2 do not fit on the grid. They fold back onto lower modes and add wrong energy there.

    Quick check

    On n=8n = 8 grid points, which complex wave has exactly the same samples as e3ixe^{3ix}?

    The FNO picture #

    A neural operator learns a map between functions, for example from u(⋅,0)u(\cdot, 0) to u(⋅,T)u(\cdot, T). The FNO does it in three stages. Lift the input function to dvd_v channels with a pointwise linear map. Then repeat a Fourier layer: FFT, keep the lowest kmax⁡k_{\max} modes, multiply each kept mode by a learned complex dv×dvd_v \times d_v matrix, inverse FFT, add a pointwise linear map of the input, and apply a pointwise activation. Finally, project to the output with a pointwise network. The weights belong to wavenumbers, not to grid points. So one set of weights serves every grid. Section 3 says exactly when this gives the same output on every grid.

    3. Definitions and main results #

    Fourier series on a grid #

    Take the domain [0,2π)[0, 2\pi). (For a period LL, mode mm has the wavenumber k=2πm/Lk = 2\pi m/L.) The grid is xj=2πj/nx_j = 2\pi j/n, j=0,…,n−1j = 0, \dots, n - 1. A function has the Fourier series u(x)=∑kckeikxu(x) = \sum_k c_k e^{ikx} with coefficients ckc_k. The discrete Fourier transform (DFT), in NumPy's convention, is

    u^k=∑j=0n−1uj e−ikxj(np.fft.fft),uj=1n∑ku^k eikxj(np.fft.ifft).\hat u_k = \sum_{j=0}^{n-1} u_j\, e^{-ikx_j} \quad (\texttt{np.fft.fft}), \qquad u_j = \frac1n \sum_k \hat u_k\, e^{ikx_j} \quad (\texttt{np.fft.ifft}).

    For even nn, the FFT entries come in the order k=0,1,…,n/2−1,−n/2,…,−1k = 0, 1, \dots, n/2 - 1, -n/2, \dots, -1 (np.fft.fftfreq(n, d=1/n) returns exactly these integers). If uu has only modes ∣k∣<n/2\lvert k\rvert < n/2, then u^k=n ck\hat u_k = n\,c_k.

    The spectral derivative of order pp is ifft⁡((ik)p u^)\operatorname{ifft}\big((ik)^p\,\hat u\big). For odd pp and even nn, set the Nyquist mode k=n/2k = n/2 to zero. Reason: the real interpolant holds this mode as cos⁡(nx/2)\cos(nx/2). Its odd derivatives are multiples of sin⁡(nx/2)\sin(nx/2), which is zero at every grid point.

    Spectral accuracy (a standard result; see Boyd, ch. 2). If uu is periodic, analytic and bounded in the strip ∣Im⁡x∣<a\lvert\operatorname{Im} x\rvert < a, then ∣ck∣≤Ce−a∣k∣\lvert c_k\rvert \le C e^{-a\lvert k\rvert}, and the error of the spectral derivative on nn points falls like e−an/2e^{-an/2}, up to slowly varying factors. If uu has only finitely many continuous derivatives, the error falls only like a power of 1/n1/n. Example: u=1/(2−cos⁡x)u = 1/(2 - \cos x) has ck=(2−3)∣k∣/3=e−a∣k∣/3c_k = (2 - \sqrt3)^{\lvert k\rvert}/\sqrt3 = e^{-a\lvert k\rvert}/\sqrt3 with a=arccosh⁡2≈1.317a = \operatorname{arccosh} 2 \approx 1.317.

    Quick check

    For u=1/(2−cos⁡x)u = 1/(2 - \cos x) the spectral derivative has a largest error of 1.4⋅10−41.4\cdot10^{-4} on n=16n = 16 points. About how large is it on n=32n = 32 points?

    Aliasing #

    Theorem 1 (aliasing). On the grid xj=2πj/nx_j = 2\pi j/n, ei(k+qn)xj=eikxje^{i(k + qn)x_j} = e^{ikx_j} for every integer qq. So if u(x)=∑pcpeipxu(x) = \sum_p c_p e^{ipx} (absolutely convergent), the DFT of the samples is

    u^kn=∑q∈Zck+qn.\frac{\hat u_k}{n} = \sum_{q \in \mathbb{Z}} c_{k + qn}.
    Proof of Theorem 1
    1. eiqnxj=e2πiqj=1e^{iqnx_j} = e^{2\pi i qj} = 1, because qjqj is an integer.
    2. uj=∑pcpeipxju_j = \sum_p c_p e^{ipx_j}. Group the modes pp by their remainder k=p mod nk = p \bmod n. By step 1, eipxj=eikxje^{ipx_j} = e^{ikx_j}, so uj=∑k=0n−1(∑qck+qn)eikxju_j = \sum_{k=0}^{n-1}\big(\sum_q c_{k+qn}\big)e^{ikx_j}.
    3. The DFT on nn points is invertible, and uj=1n∑ku^keikxju_j = \frac1n\sum_k \hat u_k e^{ikx_j}. Compare coefficients: u^k/n=∑qck+qn\hat u_k/n = \sum_q c_{k+qn}. ■\blacksquare

    So the grid cannot tell mode kk from k±nk \pm n, k±2nk \pm 2n, and so on. Energy above the Nyquist mode n/2n/2 does not disappear; it shows up as wrong energy at a lower mode.

    Theorem 2 (Orszag's 2/3 rule). Let uu and ww have only modes ∣k∣≤K\lvert k\rvert \le K. Multiply them point by point on nn grid points, take the FFT, and keep the modes ∣k∣≤K\lvert k\rvert \le K. If 3K<n3K < n, the kept coefficients equal those of the exact product uwuw. If 3K≥n3K \ge n (and K<nK < n), aliasing can corrupt kept modes.

    Why this is true, in words. The product has modes up to 2K2K. A mode pp above n/2n/2 lands at p−np - n. The highest one, 2K2K, lands at 2K−n2K - n. If this is still below −K-K, every wrong mode lands outside the kept band, and the truncation throws it away.

    Proof of Theorem 2
    1. The exact product has modes ∣p∣≤2K\lvert p\rvert \le 2K; its coefficients are the convolution ∑qcqucp−qw\sum_q c^u_q c^w_{p-q}.
    2. Assume 3K<n3K < n, so 2K<n2K < n. A mode with ∣p∣<n/2\lvert p\rvert < n/2 appears on the grid as itself. A mode with n/2≤p≤2Kn/2 \le p \le 2K appears at p−np - n (Theorem 1), and p−np - n lies in [−n/2, 2K−n][-n/2,\ 2K - n]. Negative pp are the mirror image.
    3. The largest alias is 2K−n2K - n. It misses every kept mode when 2K−n<−K2K - n < -K, that is, 3K<n3K < n. Then all corrupted entries lie at ∣k∣>K\lvert k\rvert > K, and the truncation removes them.
    4. Sharpness: if 3K≥n3K \ge n (and K<nK < n), take u=w=cos⁡Kxu = w = \cos Kx. The product has mode p=2Kp = 2K, which aliases to 2K−n∈[−K,K)2K - n \in [-K, K), a kept mode. ■\blacksquare

    In a solver: keep the state band-limited with the mask ∣k∣<n/3\lvert k\rvert < n/3. Go to the grid, multiply, come back, and apply the mask again. The other classic option is padding (the 3/2 rule): compute the product on a grid with 3/23/2 times more points (zero-padded coefficients) and truncate back. It keeps all nn modes of the state, at the price of larger FFTs.

    Quick check

    A pseudo-spectral code uses n=96n = 96 grid points and the 2/3 rule for a quadratic term. Which modes may the state keep?

    Why aliasing matters: energy #

    Theorem 3 (energy conservation). Inviscid Burgers ut+(u2/2)x=0u_t + (u^2/2)_x = 0 on a periodic domain conserves the energy E=12∫u2 dxE = \frac12\int u^2\,dx for smooth solutions. Its Galerkin truncation ut=−PK (u2/2)xu_t = -P_K\,(u^2/2)_x, where PKP_K keeps the modes ∣k∣≤K\lvert k\rvert \le K and u=PKuu = P_K u, also conserves EE.

    Proof of Theorem 3
    1. dEdt=∫u ut dx=−∫u PK(u2/2)x dx\dfrac{dE}{dt} = \int u\,u_t\,dx = -\int u\,P_K\big(u^2/2\big)_x\,dx.
    2. PKP_K is an orthogonal projection, so it is self-adjoint: ∫u PKg dx=∫(PKu) g dx=∫u g dx\int u\,P_K g\,dx = \int (P_K u)\,g\,dx = \int u\,g\,dx. Hence dEdt=−∫u (u2/2)x dx=−∫u2ux dx\dfrac{dE}{dt} = -\int u\,(u^2/2)_x\,dx = -\int u^2 u_x\,dx.
    3. u2ux=(u3/3)xu^2 u_x = (u^3/3)_x, and the integral of the derivative of a periodic function is zero. (Without truncation, step 2 is not needed.) ■\blacksquare

    With the 2/3 rule, the pseudo-spectral right-hand side is the Galerkin one (Theorem 2). So the dealiased semi-discrete scheme (before time stepping; RK4 adds a small time-step error) conserves energy for ν=0\nu = 0. For ν>0\nu > 0 it gives dE/dt=−ν∫ux2 dx≤0dE/dt = -\nu\int u_x^2\,dx \le 0, as the PDE does. With aliasing, the computed term is PK(u2/2)xP_K(u^2/2)_x plus an aliasing error, step 2 fails, and energy can be created. In an under-resolved run this feeds a pile-up of energy at the highest modes (spectral blocking) and then a blow-up. By Parseval, E=πn2∑k∣u^k∣2E = \frac{\pi}{n^2}\sum_k \lvert\hat u_k\rvert^2 on the grid, so the energy is easy to monitor.

    The pseudo-spectral Burgers solver #

    The viscous Burgers equation ut+(u2/2)x=νuxxu_t + (u^2/2)_x = \nu u_{xx} on [0,2π)[0, 2\pi) becomes, mode by mode (method of lines),

    du^kdt=−ik2 (u2)^k−νk2 u^k,\frac{d\hat u_k}{dt} = -\frac{ik}{2}\,\widehat{(u^2)}_k - \nu k^2\,\hat u_k ,

    with (u2)^\widehat{(u^2)} computed on the grid and masked as in Theorem 2. RK4 then integrates this system of ODEs (lesson 02).

    An exact solution to test against (Cole–Hopf). The substitution u=−2ν ϕx/ϕu = -2\nu\,\phi_x/\phi turns Burgers into the heat equation ϕt=νϕxx\phi_t = \nu\phi_{xx} (derivation in section 8, question 3). For u(x,0)=sin⁡xu(x, 0) = \sin x, choose ϕ(x,0)=ecos⁡x/(2ν)\phi(x, 0) = e^{\cos x/(2\nu)}; then −2νϕx/ϕ=sin⁡x-2\nu\phi_x/\phi = \sin x. With the modified Bessel functions IkI_k and R=1/(2ν)R = 1/(2\nu), eRcos⁡x=I0(R)+2∑k≥1Ik(R)cos⁡kxe^{R\cos x} = I_0(R) + 2\sum_{k\ge1} I_k(R)\cos kx. Each mode decays as in the heat equation:

    ϕ(x,t)=I0(R)+2∑k≥1Ik(R) e−νk2tcos⁡kx.\phi(x, t) = I_0(R) + 2\sum_{k \ge 1} I_k(R)\, e^{-\nu k^2 t}\cos kx .

    ϕ\phi stays positive (the heat equation keeps positive data positive), so uu has no poles. For small ν\nu the series loses digits to cancellation; the widget below therefore uses the equivalent heat-kernel integral, which is stable for every ν\nu.

    The time step. The viscous term has eigenvalues −νk2-\nu k^2 (real, negative). The advection term behaves roughly like eigenvalues ±i kmax⁡∣u∣\pm i\,k\max\lvert u\rvert (imaginary). The RK4 stability region contains [−2.785,0][-2.785, 0] on the real axis and [−2.83i,2.83i][-2.83i, 2.83i] on the imaginary axis (lesson 02). With KK the largest kept mode, this gives h≤2.785/(νK2)h \le 2.785/(\nu K^2) and roughly h≲2.83/(Kmax⁡∣u∣)h \lesssim 2.83/(K\max\lvert u\rvert). The viscous limit shrinks like 1/n21/n^2: the problem is stiff. An integrating factor or ETDRK4 removes this limit (lesson 02).

    Interactive figure: loading…

    Choose ν=0.005\nu = 0.005 and press Play. The initial wave sin⁡x\sin x steepens into a front at x=πx = \pi near t=1t = 1. Top: the exact Cole–Hopf solution and the pseudo-spectral solution on 64 points. Bottom: the energy E(t)E(t), exact (blue), with the 2/3 rule (orange) and without it (magenta). With the rule, the energy never increases, as Theorem 3 says. Without it, the energy rises after the front forms and the run blows up near t=1.7t = 1.7. For ν=0.1\nu = 0.1 the two runs agree: aliasing matters only when the grid is too coarse for the solution.

    The Fourier neural operator #

    Operator learning. We want a map G:a↦u\mathcal G: a \mapsto u between function spaces, for example u(⋅,0)↦u(⋅,T)u(\cdot, 0) \mapsto u(\cdot, T) for Burgers. The data are pairs (ai,ui)(a_i, u_i), sampled on grids. Two well-known architectures are DeepONet (Lu et al., 2021), which combines a "branch" network for the input function with a "trunk" network for the output location, and the FNO. Kovachki, Lanthaler and Stuart (2024) review the theory.

    The Fourier layer. Let vℓ(x)∈Rdvv_\ell(x) \in \R^{d_v} be the dvd_v channels at the point xx. Then

    vℓ+1(x)=σ(Wvℓ(x)+b+(Kvℓ)(x)),(Kv)(x)=∫02πκ(x−y) v(y) dy,v_{\ell+1}(x) = \sigma\big(W v_\ell(x) + b + (\mathcal K v_\ell)(x)\big), \qquad (\mathcal K v)(x) = \int_0^{2\pi} \kappa(x - y)\,v(y)\,dy ,

    with W∈Rdv×dvW \in \R^{d_v \times d_v}, b∈Rdvb \in \R^{d_v}, a pointwise activation σ\sigma (GELU) and a learned matrix-valued kernel κ\kappa. By the convolution theorem, (Kv)^k=κ^k v^k\widehat{(\mathcal K v)}_k = \hat\kappa_k\,\hat v_k. The FNO learns Rk=κ^k∈Cdv×dvR_k = \hat\kappa_k \in \C^{d_v \times d_v} directly for the lowest modes k<kmax⁡k < k_{\max} and sets all higher modes to zero. On a grid: rfft along xx, multiply mode kk by RkR_k, irfft back to nn points. For one channel, K\mathcal K is a circulant matrix (linear algebra 19): a global convolution that is also a low-pass filter.

    • Parameters: kmax⁡dv2k_{\max}d_v^2 complex numbers per layer in 1-D, independent of nn.
    • Cost: O(dv nlog⁡n)O(d_v\,n\log n) for the FFTs plus O(kmax⁡dv2)O(k_{\max}d_v^2) for the mode mixing.
    • Shift equivariance: every part commutes with cyclic shifts of the grid. Appending the grid coordinates as extra input channels, as the reference code does, breaks this on purpose.

    Theorem 4 (the Fourier layer is discretisation-invariant). Let vv be a real function with modes ∣k∣≤M\lvert k\rvert \le M only. On every grid with n>2Mn > 2M points, K\mathcal K maps the samples of vv to the samples of the same function g(x)=∑∣k∣<kmax⁡Rk ck eikxg(x) = \sum_{\lvert k\rvert < k_{\max}} R_k\,c_k\,e^{ikx}, where ckc_k are the coefficients of vv and R−k=Rk‾R_{-k} = \overline{R_k} (so gg is real). We assume kmax⁡≤n/2k_{\max} \le n/2, so the layer never asks for a mode the grid does not have.

    Proof of Theorem 4
    1. n>2Mn > 2M, so all modes of vv lie below the Nyquist mode, and Theorem 1 has no extra terms: v^k=n ck\hat v_k = n\,c_k for ∣k∣≤M\lvert k\rvert \le M, and 00 for the other modes.
    2. The layer multiplies v^k\hat v_k by RkR_k for ∣k∣<kmax⁡\lvert k\rvert < k_{\max} and zeroes the rest. The RkR_k do not depend on nn. Modes that a coarse grid cannot hold have ck=0c_k = 0 anyway.
    3. The inverse DFT divides by nn: outputj=1n∑kRk n ck eikxj=g(xj)\text{output}_j = \frac1n\sum_k R_k\,n\,c_k\,e^{ikx_j} = g(x_j). The factor nn of step 1 cancels. ■\blacksquare

    The pointwise parts (WW, bb, lifting, projection) act on the values at each point, so they do not depend on the grid either.

    Why the full FNO is only approximately invariant. σ\sigma is applied point by point, and σ(v)\sigma(v) is not band-limited. By Theorem 1 its samples alias: on a coarse grid the next layer sees ckc_k plus an aliasing error, on a fine grid almost only ckc_k. So the outputs at two resolutions differ by an error that falls as the grid gets finer, as fast as the Fourier coefficients of σ(v)\sigma(v) decay. In addition, if the data have content above the coarse Nyquist mode, the coarse samples are aliased before the network sees them. And "zero-shot super-resolution" cannot create real detail above the modes the network learned.

    Interactive figure: loading…

    Switch the activation to "none", then back to GELU. A small FNO block (Fourier layer, pointwise weight, activation, second Fourier layer, 8 modes each) runs on the chosen grid. Top: its output and the reference on 1024 points. Bottom: the largest output error against nn. Without the activation the error is round-off on every grid (Theorem 4). With GELU it is 10−310^{-3} at n=16n = 16 and falls fast. Now add 0.5cos⁡20x0.5\cos 20x to the input with no activation: grids with n≤24n \le 24 fold mode 20 onto a kept mode, so the error is large before any activation.

    Quick check

    Is an FNO Fourier layer discretisation-invariant?

    Alias-aware neural operators

    The remedy for aliasing in a network is the same as in a solver: apply the nonlinearity on a finer grid and filter back. The convolutional neural operator (CNO; Raonić et al., 2023) upsamples before each activation and low-pass filters after it, and Bartolucci et al. (NeurIPS 2023) define "representation-equivalent" operators for which the discrete and the continuous network agree. In image generation, StyleGAN3 uses the same idea to remove texture sticking. The price is larger FFTs, as with padding in a solver.

    Answers to section 1. (1) u2=12+12cos⁡6xu^2 = \frac12 + \frac12\cos 6x. On 8 points, mode 6 appears as 6−8=−26 - 8 = -2, so the FFT shows 12+12cos⁡2x\frac12 + \frac12\cos 2x: a false mode 2. (2) The weights do not change: they live on the modes k<kmax⁡k < k_{\max}. Each Fourier layer computes the same function on both grids for band-limited input (Theorem 4). The full network is only approximately the same, because the activation aliases differently on the two grids.

    4. Worked example #

    (a) Aliasing by hand. n=8n = 8, u=cos⁡3xu = \cos 3x. The exact square is 12+12cos⁡6x\frac12 + \frac12\cos 6x. At the grid points, cos⁡6xj=cos⁡(2π⋅6j/8)=cos⁡(2π⋅(−2)j/8)=cos⁡2xj\cos 6x_j = \cos(2\pi \cdot 6j/8) = \cos(2\pi\cdot(-2)j/8) = \cos 2x_j. The FFT reports 12+12cos⁡2x\frac12 + \frac12\cos 2x, that is, u^k/n=14\hat u_k/n = \frac14 at k=±2k = \pm 2. A solver that keeps all modes below the Nyquist mode (∣k∣≤3\lvert k\rvert \le 3) adds this false cos⁡2x\cos 2x to the solution. The 2/3 rule keeps only ∣k∣<8/3\lvert k\rvert < 8/3, that is, ∣k∣≤2\lvert k\rvert \le 2: then cos⁡3x\cos 3x is never part of the state.

    (b) A dealiased product. u=cos⁡x+cos⁡2xu = \cos x + \cos 2x (modes ∣k∣≤2\lvert k\rvert \le 2, and 3⋅2=6<83 \cdot 2 = 6 < 8). Exactly, u2=cos⁡2x+2cos⁡xcos⁡2x+cos⁡22xu^2 = \cos^2 x + 2\cos x\cos 2x + \cos^2 2x. With cos⁡2a=12(1+cos⁡2a)\cos^2 a = \frac12(1 + \cos 2a) and 2cos⁡acos⁡b=cos⁡(a−b)+cos⁡(a+b)2\cos a\cos b = \cos(a - b) + \cos(a + b):

    u2=1+cos⁡x+12cos⁡2x+cos⁡3x+12cos⁡4x.u^2 = 1 + \cos x + \tfrac12\cos 2x + \cos 3x + \tfrac12\cos 4x .

    On 8 points, mode 3 stays mode 3 and mode 4 is the Nyquist mode. Nothing lands on ∣k∣≤2\lvert k\rvert \le 2. After the mask: 1+cos⁡x+12cos⁡2x1 + \cos x + \frac12\cos 2x, exactly the kept part of the true product.

    (c) A spectral derivative by hand. u=sin⁡x+12cos⁡2xu = \sin x + \frac12\cos 2x on n=8n = 8 points. Since sin⁡x=(eix−e−ix)/(2i)\sin x = (e^{ix} - e^{-ix})/(2i), the FFT gives u^1=8/(2i)=−4i\hat u_1 = 8/(2i) = -4i and u^−1=4i\hat u_{-1} = 4i. The cosine gives u^±2=8⋅12⋅12=2\hat u_{\pm 2} = 8 \cdot \frac12 \cdot \frac12 = 2. Multiply by ikik:

    • mode 11: i⋅(−4i)=4i \cdot (-4i) = 4; mode −1-1: (−i)⋅4i=4(-i)\cdot 4i = 4;
    • mode 22: 2i⋅2=4i2i \cdot 2 = 4i; mode −2-2: −2i⋅2=−4i-2i \cdot 2 = -4i.

    The inverse FFT divides by 8: (4eix+4e−ix)/8=cos⁡x(4e^{ix} + 4e^{-ix})/8 = \cos x and (4ie2ix−4ie−2ix)/8=−sin⁡2x(4ie^{2ix} - 4ie^{-2ix})/8 = -\sin 2x. So u′=cos⁡x−sin⁡2xu' = \cos x - \sin 2x, exact at every grid point.

    (d) The time step. n=64n = 64, ν=0.1\nu = 0.1, max⁡∣u∣≤1\max\lvert u\rvert \le 1. The 2/3 rule keeps ∣k∣≤21\lvert k\rvert \le 21 (3⋅21=63<643 \cdot 21 = 63 < 64). Viscous limit: h≤2.785/(0.1⋅212)≈0.063h \le 2.785/(0.1 \cdot 21^2) \approx 0.063. Advective limit: h≲2.83/21≈0.13h \lesssim 2.83/21 \approx 0.13. The viscous term decides. Without dealiasing (K=31K = 31) the viscous limit would be 2.785/(0.1⋅312)≈0.0292.785/(0.1 \cdot 31^2) \approx 0.029. Doubling nn divides the viscous limit by about 4.

    (e) FNO weights. With kmax⁡=16k_{\max} = 16 and dv=32d_v = 32, one Fourier layer has 16⋅32⋅32=16,38416 \cdot 32 \cdot 32 = 16{,}384 complex weights (plus 32⋅32+32=1,05632 \cdot 32 + 32 = 1{,}056 real numbers for WW and bb), on a grid of 64 points or of 4096 points.

    Quick check

    You double the grid from n=64n = 64 to n=128n = 128 in the explicit RK4 Burgers solver with the 2/3 rule, at fixed ν=0.1\nu = 0.1. What happens to the largest stable time step?

    5. In code #

    NumPy and SciPy check the claims of this lesson. First, aliasing on 8 points (worked examples a and b):

    import numpy as np
    
    n = 8
    x = 2 * np.pi * np.arange(n) / n                   # periodic grid on [0, 2π)
    k = np.fft.fftfreq(n, d=1 / n).astype(int)         # the integer wavenumber of each FFT entry
    print(k)                                           # [ 0  1  2  3 -4 -3 -2 -1]
    print(np.allclose(np.cos(6 * x), np.cos(2 * x)))   # True: mode 6 looks like mode 6 - 8 = -2
    
    coef = lambda w: (np.fft.fft(w) / n).real.round(3) + 0.0   # real signals that are even in x
    print(coef(np.cos(3 * x) ** 2))   # [0.5  0.   0.25 0.   0.   0.   0.25 0.  ]
                                      # exact: 1/2 + cos(6x)/2, so the 0.25 at k = ±2 is aliasing
    u = np.cos(x) + np.cos(2 * x)     # modes |k| <= 2: the 2/3-rule band for n = 8
    print(coef(u ** 2))               # [1.   0.5  0.25 0.5  0.5  0.5  0.25 0.5 ]
                                      # modes |k| <= 2 (1, 0.5, 0.25) are exact
    

    Second, spectral accuracy against second-order finite differences, for u=1/(2−cos⁡x)u = 1/(2 - \cos x):

    def spectral_derivative(u):
        n = len(u)
        k = np.fft.fftfreq(n, d=1 / n)
        k[n // 2] = 0                                   # Nyquist mode: zero for an odd derivative
        return np.fft.ifft(1j * k * np.fft.fft(u)).real
    
    a = np.arccosh(2.0)                                 # analytic in the strip |Im x| < a
    for n in [8, 16, 32, 64]:
        x = 2 * np.pi * np.arange(n) / n
        u, du = 1 / (2 - np.cos(x)), -np.sin(x) / (2 - np.cos(x)) ** 2
        fd = (np.roll(u, -1) - np.roll(u, 1)) / (2 * 2 * np.pi / n)   # periodic central differences
        print(n, f"{np.abs(spectral_derivative(u) - du).max():.1e}",
              f"{np.abs(fd - du).max():.1e}", f"{np.exp(-a * n / 2):.1e}")
    # 8 1.3e-02 1.0e-01 5.2e-03
    # 16 1.4e-04 4.2e-02 2.7e-05
    # 32 7.5e-09 1.1e-02 7.1e-10
    # 64 5.0e-15 2.8e-03 5.0e-19
    

    The spectral error follows e−an/2e^{-an/2} (last column) up to a slowly varying factor, so each doubling of nn roughly squares it, until it reaches round-off at n=64n = 64. The finite-difference error falls only by about 4 per doubling (order 2).

    Third, the pseudo-spectral Burgers solver, checked against Cole–Hopf, and the energy with and without the 2/3 rule:

    from scipy.special import ive
    
    def burgers_rhs(uh, k, nu, mask):
        """d(u_hat)/dt for u_t + (u^2/2)_x = nu u_xx, pseudo-spectral, with a mode mask."""
        u = np.fft.ifft(uh).real
        return mask * (-0.5j * k * np.fft.fft(u * u) - nu * k**2 * uh)
    
    def solve_burgers(u0, nu, T, h, dealias=True):
        n = len(u0)
        k = np.fft.fftfreq(n, d=1 / n)                     # integer wavenumbers
        mask = (np.abs(k) < n / 3) if dealias else (np.abs(k) < n / 2)
        uh = mask * np.fft.fft(u0)
        for _ in range(int(round(T / h))):                 # RK4 (lesson 02)
            k1 = burgers_rhs(uh, k, nu, mask)
            k2 = burgers_rhs(uh + h / 2 * k1, k, nu, mask)
            k3 = burgers_rhs(uh + h / 2 * k2, k, nu, mask)
            k4 = burgers_rhs(uh + h * k3, k, nu, mask)
            uh = uh + h / 6 * (k1 + 2 * k2 + 2 * k3 + k4)
        return np.fft.ifft(uh).real
    
    def cole_hopf(x, t, nu, K=200):
        """Exact solution for u(x, 0) = sin x: phi = I0(R) + 2 sum I_k(R) e^{-nu k^2 t} cos kx, R = 1/(2 nu)."""
        k = np.arange(K)
        c = ive(k, 1 / (2 * nu)) * np.exp(-nu * k**2 * t)  # scaled Bessel functions: the scale cancels
        c[1:] *= 2
        phi = c @ np.cos(np.outer(k, x))
        phi_x = -(c * k) @ np.sin(np.outer(k, x))
        return -2 * nu * phi_x / phi
    
    for n in [32, 64, 128]:
        x = 2 * np.pi * np.arange(n) / n
        u = solve_burgers(np.sin(x), nu=0.1, T=1.0, h=1e-3)
        print(n, f"{np.abs(u - cole_hopf(x, 1.0, 0.1)).max():.1e}")
    # 32 4.6e-03
    # 64 3.7e-05
    # 128 2.5e-09
    
    energy = lambda u: 0.5 * np.mean(u**2) * 2 * np.pi   # E = (1/2) ∫ u^2 dx
    x = 2 * np.pi * np.arange(64) / 64
    for T in [1.0, 1.5]:
        on = solve_burgers(np.sin(x), nu=0.005, T=T, h=2e-3, dealias=True)
        off = solve_burgers(np.sin(x), nu=0.005, T=T, h=2e-3, dealias=False)
        print(T, f"{energy(on):.3f} {energy(off):.3f}")
    # 1.0 1.546 1.548
    # 1.5 1.409 2.591      (exact: 1.546 at t = 1 and 1.337 at t = 1.5; E(0) = π/2 = 1.571)
    

    Fourth, the two facts behind discretisation invariance: a band-limited input has the same coefficients on every fine-enough grid (Theorem 4), and a pointwise GELU makes coarse grids alias:

    from scipy.special import erf
    
    gelu = lambda a: 0.5 * a * (1 + erf(a / np.sqrt(2)))
    grid = lambda n: 2 * np.pi * np.arange(n) / n
    coeffs = lambda vals, kmax=4: np.fft.rfft(vals)[: kmax + 1] / len(vals)   # c_0 .. c_kmax
    
    v = lambda x: 2 * np.sin(x) + np.cos(3 * x)            # band-limited: modes 1 and 3
    print(np.round(coeffs(v(grid(16))), 3) + 0)            # [0. +0.j 0. -1.j 0. +0.j 0.5+0.j 0. +0.j]
    print(np.abs(coeffs(v(grid(16))) - coeffs(v(grid(64)))).max() < 1e-14)   # True
    
    ref = coeffs(gelu(v(grid(4096))))                      # practically exact
    for n in [16, 32, 64]:
        print(n, f"{np.abs(coeffs(gelu(v(grid(n)))) - ref).max():.1e}")
    # 16 4.7e-03
    # 32 2.0e-06
    # 64 9.0e-15
    

    6. Where it shows up #

    • Weather and climate. Orszag published the 2/3 rule in the Journal of the Atmospheric Sciences (1971). Spectral transform models such as ECMWF's IFS, and NeuralGCM's dynamical core, use the spherical version of this lesson (lesson 09). FNO-type emulators (FourCastNet's AFNO, SFNO) learn global dynamics in Fourier or spherical-harmonic space (lesson 16).
    • Seismology. Fourier pseudo-spectral methods are a classic tool for seismic wave propagation. Neural operators have been trained to emulate 2-D acoustic wave propagation for any velocity model and source location, and reverse-mode automatic differentiation through them gives the gradients for tomography (Yang et al., The Seismic Record, 2021): the adjoint idea of lesson 04.
    • PINN baselines. The Burgers solver of this lesson is the reference against which lesson 11 measures physics-informed networks.
    • Deep learning. An FNO layer is a global convolution computed with the FFT; long-convolution sequence models use the same trick. Strided convolutions and pooling in CNNs alias too. Alias-free generators (StyleGAN3) upsample before the nonlinearity and low-pass after it, the same remedy as dealiasing.

    7. Common mistakes #

    • Wavenumbers in the wrong order, or without the factor 2π/L2\pi/L for a period L≠2πL \ne 2\pi. Use np.fft.fftfreq(n, d=L/n) * 2 * np.pi.
    • A nonzero Nyquist mode in an odd derivative. The result is no longer the derivative of a real interpolant.
    • Keeping ∣k∣≤n/3\lvert k\rvert \le n/3 instead of ∣k∣<n/3\lvert k\rvert < n/3. For n=96n = 96 this keeps mode 32, and mode 64 aliases onto −32-32.
    • Masking only after the product. The state itself must be band-limited, so mask the initial condition and every right-hand side.
    • Blaming the nonlinear term for a blow-up that is really the viscous stability limit h≤2.785/(νK2)h \le 2.785/(\nu K^2): check the time step first, then the energy.
    • FNO: inconsistent FFT normalisation (norm="ortho" in one place, the default in another). The output then scales with nn, and discretisation invariance is lost.
    • FNO: asking for more modes than the grid has (rfft gives n/2+1n/2 + 1 modes for even nn), or evaluating on non-periodic data without padding.

    8. Check yourself #

    1. On n=12n = 12 grid points, which mode does cos⁡10x\cos 10x look like?
      Answer10−12=−210 - 12 = -2: it looks like cos⁡2x\cos 2x (cosine is even, so modes ±2\pm 2 give the same real wave).
    2. For n=48n = 48, what is the largest kept ∣k∣\lvert k\rvert under the 2/3 rule? Show what goes wrong if you keep one more mode.
      AnswerKeep ∣k∣<16\lvert k\rvert < 16, so K=15K = 15 (3⋅15=45<483 \cdot 15 = 45 < 48). With K=16K = 16, the product has mode 32>2432 > 24, which aliases to 32−48=−1632 - 48 = -16: a kept mode.
    3. Derive the Cole–Hopf transform: show that u=−2νϕx/ϕu = -2\nu\phi_x/\phi solves ut+uux=νuxxu_t + uu_x = \nu u_{xx} when ϕt=νϕxx\phi_t = \nu\phi_{xx}.
      AnswerWrite u=ψxu = \psi_x with ψ=−2νlog⁡ϕ\psi = -2\nu\log\phi. Burgers is ∂x(ψt+12ψx2−νψxx)=0\partial_x\big(\psi_t + \tfrac12\psi_x^2 - \nu\psi_{xx}\big) = 0, so it is enough that ψt+12ψx2−νψxx=0\psi_t + \tfrac12\psi_x^2 - \nu\psi_{xx} = 0. With ψt=−2νϕt/ϕ\psi_t = -2\nu\phi_t/\phi, ψx=−2νϕx/ϕ\psi_x = -2\nu\phi_x/\phi and ψxx=−2ν(ϕxx/ϕ−ϕx2/ϕ2)\psi_{xx} = -2\nu(\phi_{xx}/\phi - \phi_x^2/\phi^2): the ϕx2/ϕ2\phi_x^2/\phi^2 terms cancel (12⋅4ν2=2ν2\tfrac12 \cdot 4\nu^2 = 2\nu^2 against −ν⋅2ν-\nu \cdot 2\nu), and the bracket equals −2νϕ(ϕt−νϕxx)-\tfrac{2\nu}{\phi}(\phi_t - \nu\phi_{xx}), which is zero for a heat-equation solution. ϕ\phi must stay positive, or uu has poles.
    4. Why is a Fourier layer discretisation-invariant, while a 3-tap convolution kernel learned at 64 points is not?
      AnswerThe Fourier weights RkR_k are attached to physical wavenumbers kk, and the DFT scaling cancels (Theorem 4). The 3 weights of a local kernel are attached to the grid spacing: at 256 points they cover a 4 times shorter distance, so they are a different operator with a different frequency response.
    5. Why does the dealiased pseudo-spectral scheme for ν=0\nu = 0 conserve ∑k∣u^k∣2\sum_k\lvert\hat u_k\rvert^2, and the aliased one not?
      AnswerDealiased, the right-hand side equals the Galerkin term −PK(u2/2)x-P_K(u^2/2)_x exactly (Theorem 2). Then dE/dt=−∫u (u2/2)x dx=−∫(u3/3)x dx=0dE/dt = -\int u\,(u^2/2)_x\,dx = -\int (u^3/3)_x\,dx = 0 (Theorem 3), and EE is proportional to ∑∣u^k∣2\sum\lvert\hat u_k\rvert^2 (Parseval). Aliased, the computed term contains an aliasing error whose product with uu does not integrate to zero.
    6. You double nn in the explicit RK4 Burgers solver at fixed ν\nu. What happens to the stable time step, and how can you avoid that?
      AnswerKK doubles, so the viscous limit 2.785/(νK2)2.785/(\nu K^2) falls by a factor 4 (the advective limit only by 2). Treat the stiff linear term exactly or implicitly: an integrating factor, ETDRK4 or an IMEX scheme (lesson 02). Then the step is limited mainly by advection.
    Recall cardsAnswer in your head first, then reveal. Add the cards to your review deck to see them again just before you would forget.
    1. How do you compute the first derivative of periodic samples with the FFT?

    2. What does spectral accuracy mean?

    3. State the aliasing rule on a grid of nn points.

    4. On n=8n = 8 points, where does mode 6 of u2u^2 appear?

    5. State Orszag's 2/3 rule.

    6. Why does aliasing matter for energy in Burgers?

    7. What is the Cole–Hopf transform?

    8. Write the FNO Fourier layer.

    9. Why is the linear Fourier layer discretisation-invariant?

    10. Why is the full FNO only approximately resolution-invariant?

    Open the review →

    9. Interview angle #

    • "What is aliasing, and how do you avoid it in a pseudo-spectral code?" — Theorem 1, then the 2/3 rule or padding, with the energy argument as the reason it matters.
    • "Is an FNO really resolution-invariant?" — Each Fourier layer is, for band-limited inputs (Theorem 4). The pointwise activations alias on coarse grids, so the network is only approximately invariant; test the error at several resolutions.
    • "FNO, U-Net or graph network for a PDE surrogate?" — FNO: global receptive field in each layer, O(nlog⁡n)O(n\log n), periodic uniform grids, smooth fields (sharp fronts need many modes). U-Net: local and multiscale, better at sharp features, tied to one resolution. Graph or mesh networks: irregular geometry (GraphCast uses an icosahedral mesh). The sphere: SFNO (lesson 09).
    • "Why are spectral methods so accurate, and when do they fail?" — Exponential convergence for smooth periodic functions; trouble with shocks (Gibbs), non-periodic boundaries (use Chebyshev points) and complex geometry.
    • "Your spectral solver blows up after 100 steps. How do you debug it?" — Check the time step against the viscous limit, check dealiasing, monitor the energy and the spectrum at the highest modes.

    10. Practice #

    The notebook for this lesson runs in Colab or in the browser. It has four exercises, each with a check cell and a folded solution:

    1. FFT wavenumbers and the spectral derivative; compare its error with finite differences.
    2. Aliasing of a product and the 2/3-rule mask; find the false modes of cos⁡23x\cos^2 3x on 8 points.
    3. A pseudo-spectral Burgers solver with RK4, checked against Cole–Hopf; energy with and without the mask.
    4. Learn a linear Fourier multiplier (the linear part of an FNO layer) from heat-equation data on 64 points by least squares, and test it on 256 points.

    11. Watch and read #

    Why a derivative becomes multiplication by ik in Fourier space.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    The spectral derivative in code, compared with finite differences, as in section 2.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    The method of lines in Fourier space, the step from the derivative to a PDE solver.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Sampling and aliasing from the signal-processing side; compare with Theorem 1.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    The FNO architecture and how it is trained, with the Fourier layer of section 3.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    A university lecture on FNOs, including aliasing and what resolution invariance really means.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play