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

    Linear Algebra · Lesson 19 · Linear algebra for modern ML

    Structured matrices in ML for science: circulants and FNO, spectral graphs and GNNs, equivariance, matrix manifolds

    Advanced 90 min 6 interactive figures 4 videos Colab notebook

    Before this lesson: lesson 02 (the incidence matrix), lesson 04 (the DFT is unitary), lesson 06 (spectral theorem, Rayleigh quotient) and lesson 08 (SVD and polar decomposition). Time: about 90 minutes, plus the notebook.


    Why this matters #

    Physical laws do not change when you shift, rotate or relabel the system. Such a symmetry is a group of matrices acting on the data, and a layer that respects it is equivariant. One fact connects the main architectures of ML for science: matrices that commute share eigenvectors. For shifts these are Fourier modes: CNNs and Fourier neural operators (FNOs). For graphs they are Laplacian eigenvectors: spectral GNNs such as GCN. Rotations give E(3)-equivariant interatomic potentials (NequIP, MACE). Orthogonality constraints and subspaces live on the Stiefel and Grassmann manifolds: PCA, orthogonal layers, Muon.

    1. Predict first #

    2. The picture #

    The simplest case, n=3n = 3. The cyclic shift PP maps (x0,x1,x2)(x_0, x_1, x_2) to (x2,x0,x1)(x_2, x_0, x_1), and P3=IP^3 = I. Take a "wave" f=(1,z,z2)f = (1, z, z^2) with z3=1z^3 = 1. Then Pf=(z2,1,z)=z−1fPf = (z^2, 1, z) = z^{-1}f: shifting a wave only multiplies it by a phase. There are three such waves, for the three cube roots of unity, with three different eigenvalues z−1z^{-1}.

    Now take any linear filter CC that does not care where the signal sits: CP=PCCP = PC. Then P(Cf)=C(Pf)=z−1(Cf)P(Cf) = C(Pf) = z^{-1}(Cf), so CfCf is again an eigenvector of PP for the same eigenvalue. Each eigenspace of PP is a line, so CfCf must be a multiple of ff. A shift-invariant filter can only scale each wave: it is diagonal in the Fourier basis. This is the convolution theorem.

    A computation by hand, n=4n = 4. Take the filter with first column c=(2,1,0,1)c = (2, 1, 0, 1): each output is twice the sample plus its two neighbors, with wrap-around. Its matrix has entry cj−lc_{j-l} in row jj, column ll (indices mod 4), so each row is the row above, shifted one place to the right:

    C=(2101121001211012).C = \begin{pmatrix} 2 & 1 & 0 & 1 \\ 1 & 2 & 1 & 0 \\ 0 & 1 & 2 & 1 \\ 1 & 0 & 1 & 2 \end{pmatrix}.

    For n=4n = 4, ω=e2πi/4=i\omega = e^{2\pi i/4} = i, and the first Fourier wave is f1=(1,ω,ω2,ω3)=(1,i,−1,−i)f_1 = (1, \omega, \omega^2, \omega^3) = (1, i, -1, -i). Multiply row by row:

    1. Row 0: 2⋅1+1⋅i+0⋅(−1)+1⋅(−i)=22\cdot 1 + 1\cdot i + 0\cdot(-1) + 1\cdot(-i) = 2.
    2. Row 1: 1⋅1+2⋅i+1⋅(−1)+0⋅(−i)=2i1\cdot 1 + 2\cdot i + 1\cdot(-1) + 0\cdot(-i) = 2i.
    3. Row 2: 0⋅1+1⋅i+2⋅(−1)+1⋅(−i)=−20\cdot 1 + 1\cdot i + 2\cdot(-1) + 1\cdot(-i) = -2.
    4. Row 3: 1⋅1+0⋅i+1⋅(−1)+2⋅(−i)=−2i1\cdot 1 + 0\cdot i + 1\cdot(-1) + 2\cdot(-i) = -2i.

    So Cf1=(2,2i,−2,−2i)=2 f1Cf_1 = (2, 2i, -2, -2i) = 2\,f_1: the same wave, scaled by 2. Theorem 1 below predicts the scale without any multiplication: c^1=∑mcmω−m=2+1⋅(−i)+0+1⋅(i)=2\hat c_1 = \sum_m c_m\omega^{-m} = 2 + 1\cdot(-i) + 0 + 1\cdot(i) = 2. All four eigenvalues are c^=fft(c)=(4,2,0,2)\hat c = \mathrm{fft}(c) = (4, 2, 0, 2).

    Interactive figure: loading…

    Pick a kernel, then move the mode kk. The heatmap is the circulant CC built from the first column cc; hover over a cell to see that CC is constant along each wrapped diagonal. The blue dots are a Fourier mode fkf_k (real part), the violet squares are CfkCf_k, computed as a matrix–vector product. They always form the same wave, only scaled by ∣c^k∣|\hat c_k| and shifted in phase: Cfk=c^kfkCf_k = \hat c_k f_k, with c^=fft(c)\hat c = \mathrm{fft}(c). Try "shift P": all eigenvalues lie on the unit circle. Try "difference": cc is not symmetric, so most eigenvalues are complex.

    Quick check

    You change the kernel of a circular convolution on signals of length 8 from "blur" to "difference". What happens to the eigenvectors of its matrix?

    Graphs. Replace the cycle by any graph, and the shift by the graph Laplacian LL. The eigenvectors of LL play the role of waves: small eigenvalues belong to signals that are smooth over the graph, large ones to signals that oscillate between neighbors.

    Interactive figure: loading…

    Colour the graph by an eigenvector of L=D−AL = D - A. Nodes are orange where uk>0u_k > 0 and blue where uk<0u_k < 0. For k=2k = 2 (the Fiedler vector) the signs cut the two clusters apart along the single bridge. Click the bridge to remove it: a second eigenvalue drops to 0, one per connected component. Raise kk and the vector changes sign more often. "Signal kept on u1…uku_1 \dots u_k" projects a noisy signal onto the kk smoothest eigenvectors: a low-pass filter on the graph. Try the karate club: the Fiedler vector almost predicts how the club split in 1977.

    Equivariance is the same commuting property for any symmetry: transform the input and then apply the layer, or apply the layer and then transform the output; both give the same result.

    Interactive figure: loading…

    Follow both paths around the square. Right, then down: Pf(x)P f(x) (dashed outlines). Down, then right: f(Px)f(Px) (filled bars). The layer is equivariant when they match, and the relative error ee is then 0 or at round-off level. Compare circular and zero-padded convolution, a convolution that also sees the position, and a dense layer. Then switch to "relabel graph nodes": a GCN layer passes the test only if the adjacency matrix is relabeled together with the features.

    Manifolds. A unit vector in Rn\mathbb{R}^n lives on a sphere, a curved set. A frame of pp orthonormal vectors lives on the Stiefel manifold. If only their span matters, it lives on the Grassmann manifold. Section 3 shows how to take a gradient step on a sphere and stay on it.

    3. Definitions and main results #

    Circulant matrices and the DFT #

    Indices run from 00 to n−1n-1 and are taken mod nn. The cyclic shift is (Px)j=xj−1(Px)_j = x_{j-1}. The circulant with first column c=(c0,…,cn−1)c = (c_0, \dots, c_{n-1}) is

    C(c)=∑m=0n−1cmPm,Cjl=cj−l,(Cx)j=∑lcj−l xl=(c⊛x)j,C(c) = \sum_{m=0}^{n-1} c_m P^m, \qquad C_{jl} = c_{j-l}, \qquad (Cx)_j = \sum_l c_{j-l}\,x_l = (c \circledast x)_j ,

    the circular convolution of cc and xx. For n=3n = 3: C=(c0c2c1c1c0c2c2c1c0)C = \begin{pmatrix} c_0 & c_2 & c_1 \\ c_1 & c_0 & c_2 \\ c_2 & c_1 & c_0 \end{pmatrix}. The DFT (lesson 04) uses ω=e2πi/n\omega = e^{2\pi i/n} and Fkj=ω−jkF_{kj} = \omega^{-jk}, so c^=Fc\hat c = Fc and F−1=F∗/nF^{-1} = F^*/n. The Fourier vector fkf_k has entries (fk)j=ωjk(f_k)_j = \omega^{jk}; it is column kk of F∗F^*.

    Theorem 1 (circulants are diagonalized by the DFT). C(c)fk=c^kfkC(c)f_k = \hat c_k f_k for k=0,…,n−1k = 0, \dots, n-1. Hence C(c)=F−1diag⁡(c^)FC(c) = F^{-1}\operatorname{diag}(\hat c)F and c⊛x=F−1(c^⊙Fx)c \circledast x = F^{-1}(\hat c \odot Fx), which costs O(nlog⁡n)O(n\log n) with the FFT.

    Proof.

    1. (Pfk)j=(fk)j−1=ω(j−1)k=ω−k(fk)j(Pf_k)_j = (f_k)_{j-1} = \omega^{(j-1)k} = \omega^{-k}(f_k)_j. The wrap-around index is fine because ωnk=1\omega^{nk} = 1. So Pfk=ω−kfkPf_k = \omega^{-k}f_k.
    2. Repeating, Pmfk=ω−mkfkP^m f_k = \omega^{-mk} f_k.
    3. Cfk=∑mcmPmfk=(∑mcmω−mk)fk=c^kfkC f_k = \sum_m c_m P^m f_k = \big(\sum_m c_m\omega^{-mk}\big) f_k = \hat c_k f_k.
    4. Put the nn equations side by side as columns: CF∗=F∗diag⁡(c^)CF^* = F^*\operatorname{diag}(\hat c). Since (F∗)−1=F/n(F^*)^{-1} = F/n, C=F∗diag⁡(c^)F/n=F−1diag⁡(c^)FC = F^*\operatorname{diag}(\hat c)F/n = F^{-1}\operatorname{diag}(\hat c)F. ■\blacksquare

    Theorem 2 (shift-equivariant linear maps are circulants). CP=PCCP = PC if and only if CC is circulant.

    Proof.

    1. (PC)jl=Cj−1,l(PC)_{jl} = C_{j-1,l}, because PP moves row j−1j-1 to row jj.
    2. (CP)jl=∑qCjqPql=Cj,l+1(CP)_{jl} = \sum_q C_{jq}P_{ql} = C_{j,l+1}, because Pql=1P_{ql} = 1 exactly when q=l+1q = l+1.
    3. So CP=PCCP = PC means Cj−1,l=Cj,l+1C_{j-1,l} = C_{j,l+1} for all j,lj, l, that is, Cj,l=Cj+1,l+1C_{j,l} = C_{j+1,l+1}: the entries are constant along the wrapped diagonals. Then CjlC_{jl} depends only on j−lj - l: CC is circulant.
    4. Conversely, a circulant is a polynomial in PP, so it commutes with PP. ■\blacksquare

    Consequences:

    • The eigenvalues c^k\hat c_k are the frequency response of the filter. For real cc, c^n−k=c^k‾\hat c_{n-k} = \overline{\hat c_k}; for symmetric cc (cm=cn−mc_m = c_{n-m}) they are real.
    • All circulants share one eigenbasis, so they commute with each other.
    • A 2-D periodic convolution is a block-circulant matrix with circulant blocks. The 2-D DFT, F⊗FF\otimes F (lesson 16), diagonalizes it.
    • Deep-learning "convolution" is cross-correlation (a flipped kernel, still circulant). Zero padding gives a Toeplitz matrix, not a circulant, so shift equivariance fails near the borders. Strides and pooling also break exact equivariance.

    The Fourier layer of an FNO #

    The FNO Fourier layer (Li et al. 2021). A function on a periodic grid with nn points and dvd_v channels is v∈Rn×dvv \in \mathbb{R}^{n\times d_v}. One layer computes v↦σ(vW⊤+Kv)v \mapsto \sigma\big(vW^\top + \mathcal{K}v\big), with W∈Rdv×dvW \in \mathbb{R}^{d_v\times d_v} acting pointwise and

    Kv=F−1[R⋅(Fv)],(R⋅Fv)k=Rk (Fv)k   for the kmax⁡ lowest modes, 0 otherwise,\mathcal{K}v = F^{-1}\big[R\cdot(Fv)\big],\qquad (R\cdot Fv)_k = R_k\,(Fv)_k \;\text{ for the } k_{\max} \text{ lowest modes, } 0 \text{ otherwise,}

    where (Fv)k∈Cdv(Fv)_k \in \mathbb{C}^{d_v} is mode kk of all channels and Rk∈Cdv×dvR_k \in \mathbb{C}^{d_v\times d_v} is learned. Read it with Theorem 1:

    • For one channel (dv=1d_v = 1), K\mathcal{K} is exactly a circulant with eigenvalue RkR_k on the kept modes (and Rk‾\overline{R_k} on the mirror modes n−kn - k, so a real input gives a real output; rfft and irfft do this for you) and 00 on the others: a learned global convolution that is also a low-pass filter. Its kernel F−1RF^{-1}R spreads over the whole domain, so one layer has a global receptive field.
    • With dvd_v channels, K\mathcal{K} is a dv×dvd_v\times d_v block matrix of circulants, one per pair of channels.
    • Parameters: kmax⁡dv2k_{\max}d_v^2 complex numbers per layer in 1-D (one RkR_k per kept mode), independent of nn. The paper uses 4 layers, kmax⁡=16k_{\max} = 16, dv=64d_v = 64 in 1-D. Cost: the FFTs, O(dv nlog⁡n)O(d_v\, n\log n), plus O(kmax⁡dv2)O(k_{\max}d_v^2) for the mixing.
    • Discretization invariance: RR lives on frequencies, not on grid points. On any uniform grid with n/2≥kmax⁡n/2 \ge k_{\max} that resolves the input, the layer computes the same function. This is why an FNO trained at one resolution can be evaluated at another.
    • Shift equivariance: K\mathcal{K}, WW and σ\sigma all commute with cyclic shifts of the grid. The reference code appends the grid coordinates as extra input channels, which breaks this on purpose.
    Interactive figure: loading…

    One Fourier layer with one channel. Move kmax⁡k_{\max}: the layer keeps the blue modes, drops the grey ones and multiplies mode kk by RkR_k. Then change the grid size nn: the violet dots (the output on your grid) stay on the violet curve (the same layer on a 1024-point grid), because RR belongs to frequencies, not to grid points. Choose "smooth + high modes" with n=32n = 32 to see the limit: modes above n/2n/2 alias onto the kept modes and the output changes. With "step", the truncation gives the Gibbs ripples.

    Why the grid does not matter, and when it does (aliasing)

    Sample a periodic function uu on nn uniform points xj=j/nx_j = j/n. The forward DFT of the samples grows like nn (it is a sum of nn terms) and the inverse DFT divides by nn, so the factors cancel: the layer works with the Fourier coefficients ak≈1n∑ju(xj)e−2πikj/na_k \approx \frac1n\sum_j u(x_j)e^{-2\pi ikj/n} of the function, not of the grid. If uu has no modes above n/2n/2, these sums are exact, and every such grid gives the same a0,…,akmax⁡−1a_0, \dots, a_{k_{\max}-1} and the same output.

    If uu has a mode m>n/2m > n/2, the samples cannot tell it apart from the mode m−nm - n (or n−mn - m): e2πimj/n=e2πi(m−n)j/ne^{2\pi imj/n} = e^{2\pi i(m-n)j/n} at every grid point. This is aliasing. The high mode is added to a low coefficient, the kept coefficients change a little, and the agreement between grids becomes approximate. FNOs on non-uniform meshes need another transform (for example the geometry-aware variants), because the FFT needs a uniform grid.

    Quick check

    A 1-D FNO layer with kmax⁡=16k_{\max} = 16 and dv=64d_v = 64 is trained on a grid of 256 points. How many complex weights does its Fourier part RR have when you evaluate it on a grid of 1024 points?

    The graph Laplacian #

    A graph has nn nodes and symmetric weights Aij=Aji≥0A_{ij} = A_{ji} \ge 0 (Aij>0A_{ij} > 0 exactly when ii and jj are joined). The degrees are di=∑jAijd_i = \sum_j A_{ij}, D=diag⁡(d)D = \operatorname{diag}(d), and the Laplacian is L=D−AL = D - A. For an unweighted graph, L=B⊤BL = B^\top B with the incidence matrix BB of lesson 02. Its quadratic form measures how much a signal x∈Rnx \in \mathbb{R}^n varies across edges:

    x⊤Lx=∑edges {i,j}Aij(xi−xj)2.x^\top L x = \sum_{\text{edges } \{i,j\}} A_{ij}(x_i - x_j)^2 .

    Theorem 3 (what the eigenvalues say). Sort the eigenvalues upward, λ1≤⋯≤λn\lambda_1 \le \dots \le \lambda_n (lesson 06 called them μ\mu). Then: (a) L⪰0L \succeq 0 and L1=0L\mathbf 1 = 0, so λ1=0\lambda_1 = 0; (b) the multiplicity of 00 equals the number of connected components; (c) λ2=min⁡x⊥1, x≠0x⊤Lx/x⊤x\lambda_2 = \min_{x\perp\mathbf 1,\,x\ne0} x^\top Lx/x^\top x, and λ2>0\lambda_2 > 0 exactly when the graph is connected; (d) λn≤2dmax⁡\lambda_n \le 2d_{\max}.

    Proof.

    1. (a) The quadratic form is a sum of non-negative terms. (L1)i=di−∑jAij=0(L\mathbf 1)_i = d_i - \sum_j A_{ij} = 0.
    2. (b) Since L⪰0L \succeq 0, Lx=0  ⟺  x⊤Lx=0  ⟺  xi=xjLx = 0 \iff x^\top Lx = 0 \iff x_i = x_j on every edge   ⟺  \iff xx is constant on each component. The indicator vectors of the components are independent and span this null space.
    3. (c) Courant–Fischer (lesson 06) with the first eigenvector 1\mathbf 1. By (b), λ2=0\lambda_2 = 0 exactly when there are at least two components.
    4. (d) (xi−xj)2≤2xi2+2xj2(x_i - x_j)^2 \le 2x_i^2 + 2x_j^2. Summing over the edges, x⊤Lx≤2∑idixi2≤2dmax⁡∥x∥2x^\top Lx \le 2\sum_i d_ix_i^2 \le 2d_{\max}\|x\|^2. ■\blacksquare

    How to read the spectrum:

    • The eigenvector uku_k has variation uk⊤Luk=λku_k^\top Lu_k = \lambda_k: small λ\lambda = smooth over the graph, large λ\lambda = oscillating. x^=U⊤x\hat x = U^\top x is the graph Fourier transform.
    • On the cycle graph, LL is the circulant with c=(2,−1,0,…,0,−1)c = (2, -1, 0, \dots, 0, -1): its eigenvalues are 2−2cos⁡(2πk/n)2 - 2\cos(2\pi k/n) and its eigenvectors are Fourier modes. On a cycle, graph Fourier = DFT.
    • The minimizer in (c) is the Fiedler vector. Its signs split the graph in two (spectral bisection); the first kk eigenvectors give spectral clustering. A small λ2\lambda_2 means a bottleneck. Cheeger's inequality makes this precise for the normalized Laplacian (next item): λ2/2≤ϕ(G)≤2λ2\lambda_2/2 \le \phi(G) \le \sqrt{2\lambda_2} with λ2=λ2(Lsym)\lambda_2 = \lambda_2(L_{\text{sym}}), where the conductance ϕ(G)\phi(G) is the smallest ratio of cut weight to the degree sum of the smaller side.
    • The normalized Laplacian Lsym=I−D−1/2AD−1/2L_{\text{sym}} = I - D^{-1/2}AD^{-1/2} (all di>0d_i > 0) has eigenvalues in [0,2][0, 2], with 22 exactly when a component is bipartite.

    From a spectral filter to the GCN layer #

    Theorem 4 (the GCN propagation rule; Kipf and Welling 2017, §2). Let Lsym=UΛU⊤L_{\text{sym}} = U\Lambda U^\top. The GCN layer is the end of six steps. Each step makes one approximation or one change; the "Changes" line says what it buys and what it costs.

    1. Spectral filter. Filter a signal xx by g(Lsym)x=Ug(Λ)U⊤xg(L_{\text{sym}})x = Ug(\Lambda)U^\top x. Changes: nothing yet. This is the exact graph analogue of a convolution, but it needs the eigendecomposition (O(n3)O(n^3)) and dense products (O(n2)O(n^2)), and the filter is not local.
    2. Polynomial filter (ChebNet). Take g(Λ)=∑k=0KθkTk(Λ~)g(\Lambda) = \sum_{k=0}^K\theta_kT_k(\tilde\Lambda) with Chebyshev polynomials TkT_k and Λ~=2Λ/λmax⁡−I\tilde\Lambda = 2\Lambda/\lambda_{\max} - I. Because UΛkU⊤=LsymkU\Lambda^kU^\top = L_{\text{sym}}^k, this is ∑kθkTk(L~)x\sum_k\theta_kT_k(\tilde L)x with L~=2Lsym/λmax⁡−I\tilde L = 2L_{\text{sym}}/\lambda_{\max} - I. Changes: no eigenvectors are needed; only nodes at most KK hops apart interact; the cost is O(K∣E∣)O(K|E|); the filter has K+1K+1 parameters.
    3. First order. Take K=1K = 1 and λmax⁡≈2\lambda_{\max} \approx 2 (its upper bound). Then L~=Lsym−I=−D−1/2AD−1/2\tilde L = L_{\text{sym}} - I = -D^{-1/2}AD^{-1/2} and g⋆x≈θ0x−θ1D−1/2AD−1/2xg \star x \approx \theta_0x - \theta_1D^{-1/2}AD^{-1/2}x. Changes: each layer sees only direct neighbors; a wider view comes from stacking layers.
    4. One parameter. Set θ=θ0=−θ1\theta = \theta_0 = -\theta_1: g⋆x≈θ(I+D−1/2AD−1/2)x=θ(2I−Lsym)xg\star x \approx \theta(I + D^{-1/2}AD^{-1/2})x = \theta(2I - L_{\text{sym}})x. Changes: fewer parameters (less overfitting). But 2I−Lsym2I - L_{\text{sym}} has eigenvalues in [0,2][0, 2], so stacking layers can multiply a signal by up to 2depth2^{\text{depth}}: signals and gradients explode or vanish.
    5. Renormalization trick. Replace I+D−1/2AD−1/2I + D^{-1/2}AD^{-1/2} by A^=D~−1/2A~D~−1/2\hat A = \tilde D^{-1/2}\tilde A\tilde D^{-1/2} with A~=A+I\tilde A = A + I (self-loops) and D~=diag⁡(A~1)\tilde D = \operatorname{diag}(\tilde A\mathbf 1). Changes: the eigenvalues move into (−1,1](-1, 1] (proof below), so repeated layers are stable.
    6. Channels. For node features H∈Rn×CH \in \mathbb{R}^{n\times C} and weights W∈RC×FW \in \mathbb{R}^{C\times F}: H′=σ(A^HW)H' = \sigma(\hat AHW), at cost O(∣E∣CF)O(|E|CF). Changes: CC input channels and FF filters; the same WW is shared by all nodes.
    Interactive figure: loading…

    Choose a propagation matrix, then add layers. The blue curve is the response g(λ)Lg(\lambda)^L after LL layers; the orange dots are the eigenvalues of the chosen graph (the violet dot has the largest ∣g∣|g|). With "2I − L_sym" (step 4) the eigenvalue 2 at λ=0\lambda = 0 makes the response grow like 2L2^L. With "I − L_sym" a bipartite graph (path, ring, star) has g=−1g = -1, which never decays. With the renormalized A^\hat A (step 5) every other eigenvalue shrinks to 0 as LL grows: only the top eigenvector survives. This is over-smoothing.

    A 3-node example. The path 1–2–3 has degrees d=(1,2,1)d = (1, 2, 1).

    • LsymL_{\text{sym}} has eigenvalues 0,1,20, 1, 2 (the path is bipartite, so 2 appears).
    • Step 4: 2I−Lsym2I - L_{\text{sym}} has eigenvalues 2,1,02, 1, 0. Ten layers on x=e1x = e_1 give (I+D−1/2AD−1/2)10e1≈(256.5,362.0,255.5)(I + D^{-1/2}AD^{-1/2})^{10}e_1 \approx (256.5, 362.0, 255.5): the signal exploded.
    • Step 5: with self-loops, d~=(2,3,2)\tilde d = (2, 3, 2) and A^ij=A~ij/d~id~j\hat A_{ij} = \tilde A_{ij}/\sqrt{\tilde d_i\tilde d_j}:
    A^=(1/21/601/61/31/601/61/2),eigenvalues 1, 12, −16.\hat A = \begin{pmatrix} 1/2 & 1/\sqrt6 & 0 \\ 1/\sqrt6 & 1/3 & 1/\sqrt6 \\ 0 & 1/\sqrt6 & 1/2 \end{pmatrix}, \qquad \text{eigenvalues } 1,\ \tfrac12,\ -\tfrac16 .
    • One layer on e1e_1: A^e1=(0.5,0.408,0)\hat Ae_1 = (0.5, 0.408, 0). The feature moved one hop.
    • Twenty layers: A^20e1≈(0.286,0.350,0.286)\hat A^{20}e_1 \approx (0.286, 0.350, 0.286). This is the projection of e1e_1 onto the top eigenvector d~/∥d~∥=(2,3,2)/7\sqrt{\tilde d}/\|\sqrt{\tilde d}\| = (\sqrt2, \sqrt3, \sqrt2)/\sqrt7, which is (2/7,6/7,2/7)(2/7, \sqrt6/7, 2/7). The other parts decayed like (1/2)20(1/2)^{20} and (1/6)20(1/6)^{20}.

    Why step 5 works: the eigenvalues of A^\hat A lie in (−1,1](-1, 1].

    1. D~−A~=D−A=L\tilde D - \tilde A = D - A = L, so I−A^=D~−1/2LD~−1/2⪰0I - \hat A = \tilde D^{-1/2}L\tilde D^{-1/2} \succeq 0: every eigenvalue is ≤1\le 1.
    2. L1=0L\mathbf 1 = 0 gives A^ D~1/21=D~1/21\hat A\,\tilde D^{1/2}\mathbf 1 = \tilde D^{1/2}\mathbf 1: the eigenvalue 1 is attained.
    3. I+A^=D~−1/2(D+A+2I)D~−1/2≻0I + \hat A = \tilde D^{-1/2}(D + A + 2I)\tilde D^{-1/2} \succ 0, because x⊤(D+A)x=∑edgesAij(xi+xj)2≥0x^\top(D + A)x = \sum_{\text{edges}}A_{ij}(x_i + x_j)^2 \ge 0: every eigenvalue is >−1> -1. ■\blacksquare

    Over-smoothing. Applying A^\hat A many times is power iteration (lesson 05). On a connected graph, A^kH\hat A^kH converges to the projection of HH onto D~1/21\tilde D^{1/2}\mathbf 1: every node's feature becomes proportional to d~i\sqrt{\tilde d_i}, and deep GCNs without residual connections lose the difference between nodes.

    Permutation equivariance. Relabeling the nodes with a permutation matrix Π\Pi maps A→ΠAΠ⊤A \to \Pi A\Pi^\top and H→ΠHH \to \Pi H. Then D~→ΠD~Π⊤\tilde D \to \Pi\tilde D\Pi^\top, A^→ΠA^Π⊤\hat A \to \Pi\hat A\Pi^\top, and σ(ΠA^Π⊤ΠHW)=Π σ(A^HW)\sigma(\Pi\hat A\Pi^\top\Pi HW) = \Pi\,\sigma(\hat AHW), because Π⊤Π=I\Pi^\top\Pi = I and σ\sigma acts entrywise.

    Equivariance #

    A group GG is a set of transformations that is closed under composition and inverses: shifts, permutations, rotations. A representation ρ\rho gives each g∈Gg \in G an invertible matrix ρ(g)\rho(g) with ρ(gh)=ρ(g)ρ(h)\rho(gh) = \rho(g)\rho(h).

    Definition. A map ff is equivariant if f(ρ(g)x)=ρ′(g)f(x)f(\rho(g)x) = \rho'(g)f(x) for all g∈Gg \in G and all inputs xx, where ρ\rho acts on inputs and ρ′\rho' on outputs. It is invariant if ρ′(g)=I\rho'(g) = I.

    Data Group ρ(g)\rho(g) on the input ρ′(g)\rho'(g) on the output Equivariant layers
    periodic signal x∈Rnx \in \mathbb{R}^n cyclic shifts PsP^s PsP^s circulant convolution, FNO layer
    graph (A,H)(A, H), H∈Rn×CH \in \mathbb{R}^{n\times C} permutations SnS_n (ΠAΠ⊤,ΠH)(\Pi A\Pi^\top, \Pi H) ΠH\Pi H (node outputs); II (graph outputs) GCN, message passing, DeepSets
    atoms X∈RN×3X \in \mathbb{R}^{N\times3} (one row per atom) E(3): Q∈O(3)Q \in O(3), t∈R3t \in \mathbb{R}^3 X↦XQ⊤+1t⊤X \mapsto XQ^\top + \mathbf 1t^\top forces F↦FQ⊤F \mapsto FQ^\top; energy unchanged EGNN, NequIP, MACE
    wind field on the sphere rotations SO(3) rotate the positions and the vectors the same spherical CNNs, SFNO

    Theorem 5 (equivariant linear maps form a subspace). A linear map WW is equivariant if and only if Wρ(g)=ρ′(g)WW\rho(g) = \rho'(g)W for all gg. It suffices to check a set of generators of GG. With vec and Kronecker products (lesson 16), the conditions read (ρ(g)⊤⊗I−I⊗ρ′(g))vec⁡(W)=0(\rho(g)^\top\otimes I - I\otimes\rho'(g))\operatorname{vec}(W) = 0: the equivariant layers are a null space.

    Proof of Theorem 5
    1. Equivariance as a matrix equation. For f(x)=Wxf(x) = Wx, equivariance says Wρ(g)x=ρ′(g)WxW\rho(g)x = \rho'(g)Wx for all xx. Two matrices that agree on every xx are equal: Wρ(g)=ρ′(g)WW\rho(g) = \rho'(g)W. The converse is the same line read backwards.
    2. Products. If the equation holds for gg and hh, then Wρ(gh)=Wρ(g)ρ(h)=ρ′(g)Wρ(h)=ρ′(g)ρ′(h)W=ρ′(gh)WW\rho(gh) = W\rho(g)\rho(h) = \rho'(g)W\rho(h) = \rho'(g)\rho'(h)W = \rho'(gh)W.
    3. Inverses. Multiply Wρ(g)=ρ′(g)WW\rho(g) = \rho'(g)W by ρ′(g)−1\rho'(g)^{-1} on the left and by ρ(g)−1\rho(g)^{-1} on the right: ρ′(g)−1W=Wρ(g)−1\rho'(g)^{-1}W = W\rho(g)^{-1}, that is, Wρ(g−1)=ρ′(g−1)WW\rho(g^{-1}) = \rho'(g^{-1})W.
    4. Every element of GG is a product of generators and their inverses, so by steps 2 and 3 the generators suffice.
    5. Vec form. The identity vec⁡(AXB)=(B⊤⊗A)vec⁡(X)\operatorname{vec}(AXB) = (B^\top\otimes A)\operatorname{vec}(X) (lesson 16) gives vec⁡(Wρ)=(ρ⊤⊗I)vec⁡(W)\operatorname{vec}(W\rho) = (\rho^\top\otimes I)\operatorname{vec}(W) and vec⁡(ρ′W)=(I⊗ρ′)vec⁡(W)\operatorname{vec}(\rho'W) = (I\otimes\rho')\operatorname{vec}(W). So each condition is one homogeneous linear equation in vec⁡(W)\operatorname{vec}(W), and the solutions of a set of such equations form a null space. ■\blacksquare

    Example (DeepSets, Zaheer et al. 2017). For permutations acting on Rn\mathbb{R}^n, the equivariant linear maps Rn→Rn\mathbb{R}^n \to \mathbb{R}^n are exactly W=aI+b11⊤W = aI + b\mathbf 1\mathbf 1^\top.

    1. ΠWΠ⊤=W\Pi W\Pi^\top = W for every permutation π\pi means Wπ(i)π(j)=WijW_{\pi(i)\pi(j)} = W_{ij} for all i,ji, j.
    2. A permutation can move any diagonal position (i,i)(i, i) to any other (k,k)(k, k): all diagonal entries are equal.
    3. It can move any off-diagonal position (i,j)(i, j), i≠ji \ne j, to any other (k,l)(k, l), k≠lk \ne l: all off-diagonal entries are equal.
    4. So W=aI+b11⊤W = aI + b\mathbf 1\mathbf 1^\top; conversely, Π1=1\Pi\mathbf 1 = \mathbf 1 shows that it commutes with every Π\Pi. ■\blacksquare

    Two parameters instead of n2n^2. With CC input and FF output channels, H↦HΛ+11⊤HΓH \mapsto H\Lambda + \mathbf 1\mathbf 1^\top H\Gamma (Λ,Γ∈RC×F\Lambda, \Gamma \in \mathbb{R}^{C\times F}): 2CF2CF parameters.

    Quick check

    A self-attention layer without positional encodings maps the token matrix XX (one row per token) to softmax(XWQWK⊤X⊤/d) XWV\mathrm{softmax}(XW_QW_K^\top X^\top/\sqrt{d})\,XW_V, with the softmax taken row by row. You permute the tokens: X→ΠXX \to \Pi X. What happens to the output?

    An invariant energy gives equivariant forces. If E(XQ⊤+1t⊤)=E(X)E(XQ^\top + \mathbf 1t^\top) = E(X) for all Q∈O(3)Q \in O(3) and tt, then the forces F(X)=−∇E(X)∈RN×3F(X) = -\nabla E(X) \in \mathbb{R}^{N\times3} satisfy F(XQ⊤+1t⊤)=F(X)Q⊤F(XQ^\top + \mathbf 1t^\top) = F(X)Q^\top.

    1. Let Y=XQ⊤+1t⊤Y = XQ^\top + \mathbf 1t^\top, so dY=dX Q⊤dY = dX\,Q^\top.
    2. Chain rule: ∇X[E(Y)]=∇E(Y) Q\nabla_X[E(Y)] = \nabla E(Y)\,Q.
    3. The left side equals ∇E(X)\nabla E(X), since E(Y)=E(X)E(Y) = E(X) for all XX. Multiply by Q⊤Q^\top on the right: ∇E(Y)=∇E(X)Q⊤\nabla E(Y) = \nabla E(X)Q^\top. ■\blacksquare

    This is why machine-learned interatomic potentials predict an invariant energy and get the forces by autodiff.

    Building E(3)-equivariant layers. Distances rij=∥xi−xj∥r_{ij} = \|x_i - x_j\| are invariant. Relative vectors xi−xjx_i - x_j rotate with QQ and ignore tt. So fi(X)=∑j≠iϕ(rij)(xi−xj)f_i(X) = \sum_{j\ne i}\phi(r_{ij})(x_i - x_j) is equivariant for any scalar function ϕ\phi; this is the coordinate update of EGNN (Satorras et al. 2021). Richer features (spherical harmonics of degree ℓ\ell, the irreducible representations of SO(3), combined with Clebsch–Gordan coefficients) are the language of e3nn, NequIP and MACE.

    How to test equivariance numerically.

    1. Draw random inputs xx and random group elements gg: shifts; permutations; Q∈O(3)Q \in O(3) (for example scipy.stats.ortho_group) and t∈R3t \in \mathbb{R}^3.
    2. Compute the relative error e=∥f(ρ(g)x)−ρ′(g)f(x)∥/∥f(x)∥e = \|f(\rho(g)x) - \rho'(g)f(x)\|/\|f(x)\|.
    3. An exactly equivariant layer gives ee at round-off level (≈10−15\approx10^{-15} in float64, ≈10−6\approx10^{-6} in float32) in every trial. Cover the generators, and for O(3) both det⁡Q=+1\det Q = +1 and −1-1.
    4. Run the same test on a layer that is not equivariant and check that e=O(1)e = O(1): the test must be able to fail.
    5. An approximately equivariant layer (zero padding under shifts, a CNN under 45° rotations of a pixel grid) gives a small ee that is far above round-off. Report it; do not call it equivariant.

    Matrix manifolds: Stiefel and Grassmann #

    The sphere first (p=1p = 1). The set of unit vectors {x∈Rn:x⊤x=1}\{x \in \mathbb{R}^n : x^\top x = 1\} is a sphere. Suppose we want to minimize a function ff over it, for example f(x)=−12x⊤Sxf(x) = -\tfrac12 x^\top Sx (its minimizer is the top eigenvector of SS, lesson 06). A plain gradient step x−η∇f(x)x - \eta\nabla f(x) leaves the sphere. Riemannian gradient descent fixes this in three steps:

    1. Tangent plane. Differentiate x(t)⊤x(t)=1x(t)^\top x(t) = 1 at t=0t = 0: 2x⊤x′(0)=02x^\top x'(0) = 0. So the directions in which we can move without leaving the sphere (to first order) are Tx={v:x⊤v=0}T_x = \{v : x^\top v = 0\}, the plane perpendicular to xx.
    2. Project the gradient. Remove the part of G=∇f(x)G = \nabla f(x) along xx: Px(G)=G−x(x⊤G)P_x(G) = G - x(x^\top G). Then x⊤Px(G)=x⊤G−(x⊤x)(x⊤G)=0x^\top P_x(G) = x^\top G - (x^\top x)(x^\top G) = 0.
    3. Step, then retract. The step v=−ηPx(G)v = -\eta P_x(G) is perpendicular to xx, so ∥x+v∥2=1+∥v∥2>1\|x + v\|^2 = 1 + \|v\|^2 > 1: the point x+vx + v is off the sphere. Divide by its length: Rx(v)=(x+v)/∥x+v∥R_x(v) = (x + v)/\|x + v\|.
    Interactive figure: loading…

    Take steps on the sphere. At the black point XX the grey arrow is the gradient G=−SXG = -SX of f(x)=−12x⊤Sxf(x) = -\tfrac12 x^\top Sx, S=diag⁡(1.6,1,0.4)S = \operatorname{diag}(1.6, 1, 0.4). The blue arrow PX(G)P_X(G) lies in the aqua tangent plane. The orange step V=−ηPX(G)V = -\eta P_X(G) ends at X+VX + V, slightly outside the sphere; the violet point is the retraction (X+V)/∥X+V∥(X+V)/\|X+V\|. Press Take one step a few times: XX moves to the minimizer ±e1\pm e_1, the top eigenvector of SS. Drag to rotate the view.

    The Stiefel manifold. The same three steps work for pp orthonormal columns. The Stiefel manifold is St⁡(n,p)={X∈Rn×p:X⊤X=Ip}\operatorname{St}(n, p) = \{X \in \mathbb{R}^{n\times p}: X^\top X = I_p\}, n≥pn \ge p. For p=1p = 1 it is the unit sphere; for p=np = n it is the orthogonal group O(n)O(n).

    • Dimension np−p(p+1)/2np - p(p+1)/2: npnp entries, minus p(p+1)/2p(p+1)/2 independent equations in the symmetric matrix equation X⊤X=IX^\top X = I. For p=1p = 1: n−1n - 1, the dimension of a sphere in Rn\mathbb{R}^n.
    • Tangent space. Differentiate X(t)⊤X(t)=IX(t)^\top X(t) = I: TX={V:X⊤V+V⊤X=0}T_X = \{V: X^\top V + V^\top X = 0\}, so X⊤VX^\top V is skew-symmetric.
    • Riemannian gradient. Project the Euclidean gradient GG onto TXT_X: PX(G)=G−Xsym⁡(X⊤G)P_X(G) = G - X\operatorname{sym}(X^\top G), with sym⁡(M)=(M+M⊤)/2\operatorname{sym}(M) = (M + M^\top)/2. For p=1p = 1, X⊤GX^\top G is a number and this is the formula of step 2.
    • Retraction (a smooth way back to the manifold). QR retraction: the Q factor of X+VX + V (with a positive diagonal in R). Polar retraction: UW⊤UW^\top, where X+V=UΣW⊤X + V = U\Sigma W^\top is a thin SVD; it is the closest point of St⁡(n,p)\operatorname{St}(n, p) to X+VX + V in the Frobenius norm. For p=1p = 1 both are "divide by the length".
    • One step of Riemannian gradient descent: X←RX(−η PX(∇f(X)))X \leftarrow R_X\big(-\eta\,P_X(\nabla f(X))\big).

    For p=np = n, the polar factor UW⊤UW^\top is orthogonal but not always a rotation: it contains a reflection when det⁡(X+V)<0\det(X + V) < 0 (see the box "orthogonal does not mean rotation" in lesson 04).

    Quick check

    What is the dimension of St⁡(3,1)\operatorname{St}(3, 1), and what is its tangent space at a point xx?

    The Grassmann manifold Gr⁡(n,p)\operatorname{Gr}(n, p) is the set of pp-dimensional subspaces of Rn\mathbb{R}^n. The frames XX and XQXQ (Q∈O(p)Q \in O(p)) span the same subspace, so Gr⁡(n,p)=St⁡(n,p)/O(p)\operatorname{Gr}(n, p) = \operatorname{St}(n, p)/O(p).

    • A unique representative of the subspace is the orthogonal projector XX⊤XX^\top.
    • Dimension p(n−p)p(n - p): the Stiefel dimension minus dim⁡O(p)=p(p−1)/2\dim O(p) = p(p-1)/2.
    • ff is a function on Gr⁡(n,p)\operatorname{Gr}(n, p) when f(XQ)=f(X)f(XQ) = f(X) for all Q∈O(p)Q \in O(p).
    • Two subspaces are compared through their principal angles (lesson 17), not through their bases.

    One ML use of each:

    • Stiefel: orthonormal weights. All singular values equal 1, so the layer keeps norms and is 1-Lipschitz (orthogonal RNNs, orthogonal initialization).
    • Grassmann: PCA. The objective tr⁡(X⊤SX)\operatorname{tr}(X^\top SX) over X∈St⁡(n,k)X \in \operatorname{St}(n, k) does not change under X→XQX \to XQ, so PCA is a problem on Gr⁡(n,k)\operatorname{Gr}(n, k): the objective finds the top-kk subspace. The individual principal directions inside it come from the eigenvectors of SS, and they are unique (up to sign) only when the eigenvalues are distinct (Boumal §2.4).
    Grassmann distances, LoRA subspaces and Muon
    • Distances. For orthonormal bases X,YX, Y of two subspaces, the principal angles θ1,…,θp\theta_1, \dots, \theta_p satisfy cos⁡θi=σi(X⊤Y)\cos\theta_i = \sigma_i(X^\top Y). The geodesic distance on Gr⁡(n,p)\operatorname{Gr}(n, p) is ∥θ∥2\|\theta\|_2; the projection distance is ∥XX⊤−YY⊤∥F=2 ∥sin⁡θ∥2\|XX^\top - YY^\top\|_F = \sqrt2\,\|\sin\theta\|_2, the sin⁡Θ\sin\Theta of Davis–Kahan (lesson 17).
    • Comparing learned subspaces. The LoRA paper (§7.2) compares learned update subspaces with the Grassmann-based similarity ∥Ui⊤Uj∥F2/min⁡(i,j)\|U_i^\top U_j\|_F^2/\min(i, j); climate scientists compare the EOF subspaces of two models by principal angles.
    • Muon (2024) replaces each momentum update G=UΣV⊤G = U\Sigma V^\top by an approximation of its polar factor UV⊤UV^\top (a few Newton–Schulz steps): the nearest point of the Stiefel manifold to GG, so every direction of the update gets the same size. "Manifold Muon" (Bernstein, 2025) keeps the weights themselves on the Stiefel manifold.

    Answers to section 1. (1) Yes: the Fourier vectors fkf_k. A circularly padded convolution commutes with the shift, so it is a circulant (Theorem 2), and Theorem 1 applies; the kernel only decides the eigenvalues c^k\hat c_k. (2) Three, by Theorem 3(b).

    4. Worked example #

    (a) The 4-cycle is a circulant. Nodes 1–2–3–4–1. Its Laplacian LC4=(2−10−1−12−100−12−1−10−12)L_{C_4} = \begin{pmatrix} 2&-1&0&-1\\-1&2&-1&0\\0&-1&2&-1\\-1&0&-1&2\end{pmatrix} has first column c=(2,−1,0,−1)c = (2, -1, 0, -1). With ω=i\omega = i: c^k=2−ω−k−ω−3k=2−2cos⁡(πk/2)\hat c_k = 2 - \omega^{-k} - \omega^{-3k} = 2 - 2\cos(\pi k/2), so for k=0,1,2,3k = 0, 1, 2, 3 the eigenvalues are 0,2,4,20, 2, 4, 2.

    • λ=0\lambda = 0 with f0=(1,1,1,1)f_0 = (1,1,1,1): one component.
    • λ=4=2dmax⁡\lambda = 4 = 2d_{\max} with f2=(1,−1,1,−1)f_2 = (1,-1,1,-1): the bound of Theorem 3(d) is reached (the cycle is bipartite).
    • λ=2\lambda = 2 twice, with f1,3=(1,±i,−1,∓i)f_{1,3} = (1, \pm i, -1, \mp i). Real versions: (1,0,−1,0)(1,0,-1,0) and (0,1,0,−1)(0,1,0,-1), the slowest waves around the cycle. Any combination is a Fiedler vector: it is not unique.

    (b) Cut one edge. Remove the edge 4–1 to get the path 1–2–3–4. It is no longer a circulant. Its eigenvalues are 2−2cos⁡(πk/4)2 - 2\cos(\pi k/4): 00, 2−2≈0.5862 - \sqrt2 \approx 0.586, 22, 2+2≈3.4142 + \sqrt2 \approx 3.414. The Fiedler vector ≈(0.653,0.271,−0.271,−0.653)\approx (0.653, 0.271, -0.271, -0.653) splits {1,2}∣{3,4}\{1, 2\} \mid \{3, 4\}, the cut in the middle. λ2\lambda_2 drops from 2 to 0.586: the path is easier to cut. Since LC4=Lpath+bb⊤L_{C_4} = L_{\text{path}} + bb^\top with b=e1−e4b = e_1 - e_4, we have Lpath⪯LC4L_{\text{path}} \preceq L_{C_4}, so no eigenvalue can go up (lesson 07): 0≤00 \le 0, 0.586≤20.586 \le 2, 2≤22 \le 2, 3.414≤43.414 \le 4. ✓

    Quick check

    What are the eigenvalues of the Laplacian L=D−AL = D - A of the path on 3 nodes (1–2–3)?

    (c) One GCN step on the path. With self-loops, d~=(2,3,3,2)\tilde d = (2, 3, 3, 2) and A^ij=A~ij/d~id~j\hat A_{ij} = \tilde A_{ij}/\sqrt{\tilde d_i\tilde d_j}:

    A^=(1/21/6001/61/31/3001/31/31/6001/61/2),1/6≈0.408.\hat A = \begin{pmatrix} 1/2 & 1/\sqrt6 & 0 & 0\\ 1/\sqrt6 & 1/3 & 1/3 & 0\\ 0 & 1/3 & 1/3 & 1/\sqrt6\\ 0&0&1/\sqrt6&1/2\end{pmatrix},\qquad 1/\sqrt6 \approx 0.408 .
    • A feature on node 1 only, x=e1x = e_1: A^x=(0.5,0.408,0,0)\hat Ax = (0.5, 0.408, 0, 0). It moved one hop.
    • Eigenvalues: 11, 14+3312≈0.729\tfrac14 + \tfrac{\sqrt{33}}{12} \approx 0.729, 16\tfrac16, 14−3312≈−0.229\tfrac14 - \tfrac{\sqrt{33}}{12} \approx -0.229, all in (−1,1](-1, 1]. Without the trick, I+D−1/2AD−1/2I + D^{-1/2}AD^{-1/2} has eigenvalues 0,0.5,1.5,20, 0.5, 1.5, 2.
    • Over-smoothing: A^20e1≈(0.201,0.245,0.245,0.199)\hat A^{20}e_1 \approx (0.201, 0.245, 0.245, 0.199). This is almost the projection of e1e_1 onto d~=(2,3,3,2)\sqrt{\tilde d} = (\sqrt2, \sqrt3, \sqrt3, \sqrt2), which is (0.2,0.245,0.245,0.2)(0.2, 0.245, 0.245, 0.2). The rest decays like 0.72920≈0.0020.729^{20} \approx 0.002. After 20 layers the network cannot tell where the feature started.

    (d) Stiefel and Grassmann. X=[e1  e2]X = [e_1\; e_2] and Y=[e1  (e2+e3)/2]Y = [e_1\; (e_2 + e_3)/\sqrt2] in R3\mathbb{R}^3: X⊤Y=diag⁡(1,1/2)X^\top Y = \operatorname{diag}(1, 1/\sqrt2), so the principal angles are 0°0° and 45°45°, and ∥XX⊤−YY⊤∥F=2sin⁡45°=1\|XX^\top - YY^\top\|_F = \sqrt2\sin45° = 1. St⁡(3,2)\operatorname{St}(3, 2) has dimension 6−3=36 - 3 = 3 and Gr⁡(3,2)\operatorname{Gr}(3, 2) has dimension 2⋅1=22\cdot1 = 2 (a plane through 0 in R3\mathbb{R}^3 is fixed by its normal direction).

    5. In code #

    NumPy and SciPy check the worked example and the main theorems. The notebook for this lesson has these cells and the exercises.

    import numpy as np
    from scipy.linalg import circulant, subspace_angles
    from scipy.sparse.csgraph import laplacian
    from scipy.stats import ortho_group
    
    rng = np.random.default_rng(0)
    
    # 1) A circulant matrix is diagonalized by the DFT; its eigenvalues are fft(c).
    c = np.array([2.0, -1.0, 0.0, -1.0])            # first column: Laplacian of the 4-cycle
    C = circulant(c)
    F = np.fft.fft(np.eye(4))                        # DFT matrix, F[k, j] = exp(-2 pi i jk / 4)
    print(np.fft.fft(c).real)                        # [0. 2. 4. 2.]
    print(np.allclose(C, np.linalg.inv(F) @ np.diag(np.fft.fft(c)) @ F))   # True
    x = rng.standard_normal(4)
    print(np.allclose(C @ x, np.fft.ifft(np.fft.fft(c) * np.fft.fft(x)).real))  # True
    P = circulant([0.0, 1.0, 0.0, 0.0])              # cyclic shift, (Px)_j = x_{j-1}
    print(np.allclose(C @ P, P @ C))                 # True
    
    # 2) Laplacians of the 4-cycle and of the path (the cycle without edge 4-1).
    A_cyc = circulant([0.0, 1.0, 0.0, 1.0])
    A_path = A_cyc.copy()
    A_path[0, 3] = A_path[3, 0] = 0.0
    print(np.linalg.eigvalsh(laplacian(A_cyc)).round(3) + 0.0)               # [0. 2. 2. 4.]
    print(np.linalg.eigvalsh(laplacian(A_cyc, normed=True)).round(3) + 0.0)  # [0. 1. 1. 2.]
    lam, U = np.linalg.eigh(laplacian(A_path))
    print(lam.round(3) + 0.0, U[:, 1].round(3))      # [0. 0.586 2. 3.414], Fiedler (up to sign)
    Pi = np.eye(4)[[2, 0, 3, 1]]                     # relabel the nodes
    print(np.allclose(laplacian(Pi @ A_path @ Pi.T), Pi @ laplacian(A_path) @ Pi.T))  # True
    
    # 3) Equivariance test under E(3): X (N atoms x 3) -> X Q^T + t.
    def rel_err(f, X, act_in, act_out):
        return np.linalg.norm(f(act_in(X)) - act_out(f(X))) / np.linalg.norm(f(X))
    
    def vector_layer(X):                             # f_i = sum_j exp(-r_ij^2) (x_i - x_j)
        D = X[:, None, :] - X[None, :, :]
        r2 = (D**2).sum(axis=-1, keepdims=True)
        return (np.exp(-r2) * D).sum(axis=1)
    
    W1, W2 = rng.standard_normal((3, 16)), rng.standard_normal((16, 3))
    mlp = lambda X: np.tanh(X @ W1) @ W2             # a plain MLP on raw coordinates
    X = rng.standard_normal((10, 3))
    for _ in range(3):
        Q = ortho_group.rvs(3, random_state=rng)     # random orthogonal: det +1 or -1
        t = rng.standard_normal(3)
        print(round(np.linalg.det(Q)),
              rel_err(vector_layer, X, lambda Y: Y @ Q.T + t, lambda Y: Y @ Q.T),  # ~5e-16
              rel_err(mlp, X, lambda Y: Y @ Q.T, lambda Y: Y @ Q.T))               # ~1: fails
    
    # 4) Shift test of a 1-D convolution: circular padding is exact, zero padding is not.
    k = rng.standard_normal(5)                       # kernel taps for offsets -2..2
    u = rng.standard_normal(64)
    c64 = np.zeros(64); c64[:3] = k[2:]; c64[-2:] = k[:2]   # the same kernel as a circulant
    conv_circular = lambda x: np.fft.ifft(np.fft.fft(c64) * np.fft.fft(x)).real
    conv_zeros = lambda x: np.convolve(x, k, mode="same")   # zero padding at both ends
    for name, conv in [("circular", conv_circular), ("zeros", conv_zeros)]:
        e = np.linalg.norm(conv(np.roll(u, 3)) - np.roll(conv(u), 3)) / np.linalg.norm(conv(u))
        print(name, e)                               # circular ~2e-16, zeros ~0.16
    
    # 5) Stiefel St(6, 2): tangent projection and polar retraction; Grassmann angles.
    X = np.linalg.qr(rng.standard_normal((6, 2)))[0]
    G = rng.standard_normal((6, 2))                  # a Euclidean gradient
    xi = G - X @ (X.T @ G + G.T @ X) / 2             # projection onto the tangent space
    print(np.abs(X.T @ xi + xi.T @ X).max())         # ~3e-16: xi is tangent
    Ur, _, Vrt = np.linalg.svd(X - 0.1 * xi, full_matrices=False)
    Y = Ur @ Vrt                                     # polar retraction of a gradient step
    print(np.abs(Y.T @ Y - np.eye(2)).max())         # ~4e-16: back on St(6, 2)
    Xg = np.eye(3)[:, :2]
    Yg = np.column_stack([[1, 0, 0], np.array([0, 1, 1]) / np.sqrt(2)])
    print(np.degrees(subspace_angles(Xg, Yg)))       # [45.  0.]
    print(np.linalg.norm(Xg @ Xg.T - Yg @ Yg.T))     # 1.0 = sqrt(2) * sin(45 deg)
    

    The equivariance test in part 3 passes for det⁡Q=−1\det Q = -1 too, because distances and relative vectors do not care about orientation. In part 4 the zero-padded layer misses by about 16%: the error comes only from the two borders, where zero padding replaces the wrapped values.

    6. Where it shows up #

    • Weather and climate. FourCastNet (Pathak et al. 2022) mixes tokens with learned Fourier-domain filters (AFNO). Spherical FNOs (Bonev et al. 2023) replace the DFT by the spherical harmonic transform: spherical harmonics are the eigenfunctions of the Laplacian on the sphere. GraphCast (Lam et al. 2023) is a message-passing GNN on an icosahedral mesh.
    • PDE surrogates. FNOs for Navier–Stokes and Darcy flow; MeshGraphNets on simulation meshes.
    • Sensor and station networks. Spectral clustering with the first eigenvectors of LL; smoothness penalties x⊤Lxx^\top Lx; graph diffusion e−tLxe^{-tL}x (lesson 16).
    • Molecules and materials. NequIP and MACE predict an invariant energy and take forces as −∇E-\nabla E.

    7. Common mistakes #

    • Mixing DFT conventions. In NumPy's convention the eigenvalues are fft of the first column. The first row gives c^n−k\hat c_{n-k} instead (conjugates for real cc).
    • Calling a zero-padded or strided CNN exactly shift equivariant. Zero padding gives a Toeplitz matrix, and the error at the borders is real. Strides and pooling break exact equivariance too.
    • Thinking an FNO works on any grid. It needs a uniform grid with at least the kmax⁡k_{\max} modes, and fine enough for the input; on a coarser grid the input aliases.
    • Mixing Laplacians. D−AD - A has eigenvalues up to 2dmax⁡2d_{\max}; LsymL_{\text{sym}} has them in [0,2][0, 2].
    • Expecting a unique Fiedler vector. With a repeated λ2\lambda_2 (the 4-cycle) every vector in the eigenspace is a minimizer.
    • Weak equivariance tests: one input only, only det⁡Q=+1\det Q = +1, no failing baseline.
    • Taking a plain gradient step on a manifold. X−η∇fX - \eta\nabla f leaves St⁡(n,p)\operatorname{St}(n, p): project the gradient onto the tangent space and retract.
    • Comparing subspaces through their bases. XX and XQXQ are different Stiefel points but the same subspace: compare XX⊤XX^\top or the principal angles.

    8. Check yourself #

    1. Give the eigenvalues and the determinant of the circulant with first column c=(1,2,0)c = (1, 2, 0).
      AnswerC=(102210021)C = \begin{pmatrix}1&0&2\\2&1&0\\0&2&1\end{pmatrix}. c^k=1+2ω−k\hat c_k = 1 + 2\omega^{-k} with ω=e2πi/3\omega = e^{2\pi i/3}: 33, 1+2(−12−32i)=−i31 + 2(-\tfrac12 - \tfrac{\sqrt3}{2}i) = -i\sqrt3, and i3i\sqrt3. det⁡C=3⋅(−i3)(i3)=9\det C = 3\cdot(-i\sqrt3)(i\sqrt3) = 9.
    2. A 1-D FNO layer has kmax⁡=16k_{\max} = 16 and dv=64d_v = 64. How many parameters are in RR? Does the number change between a grid of 1024 and 8192 points?
      Answer16⋅642=65,53616\cdot64^2 = 65{,}536 complex numbers (131,072 real), plus 64264^2 for WW. No: the parameters belong to Fourier modes, not to grid points.
    3. An unweighted graph on 5 nodes has Laplacian eigenvalues 0,0,1,2,30, 0, 1, 2, 3. How many components? How many edges? What can you say about the largest degree?
      AnswerTwo zero eigenvalues: two components, so it is not connected (λ2=0\lambda_2 = 0). The eigenvalues sum to tr⁡L=∑idi=2∣E∣=6\operatorname{tr}L = \sum_i d_i = 2|E| = 6: three edges. 3=λn≤2dmax⁡3 = \lambda_n \le 2d_{\max} gives dmax⁡≥1.5d_{\max} \ge 1.5, so some node has degree at least 2. (Example: a path on 3 nodes, eigenvalues 0,1,30, 1, 3, plus a separate edge, eigenvalues 0,20, 2.)
    4. How many parameters does the most general permutation-equivariant linear layer Rn→Rn\mathbb{R}^n \to \mathbb{R}^n have? Why?
      AnswerTwo: W=aI+b11⊤W = aI + b\mathbf 1\mathbf 1^\top. ΠWΠ⊤=W\Pi W\Pi^\top = W for all permutations forces all diagonal entries to be equal and all off-diagonal entries to be equal. With CC input and FF output channels: 2CF2CF.
    5. Your force model passes the rotation test (e≈10−7e \approx 10^{-7} in float32) but gives e≈0.5e \approx 0.5 for QQ with det⁡Q=−1\det Q = -1. What does this mean?
      AnswerIt is SO(3)- but not O(3)-equivariant: it uses orientation (for example a cross product or chirality-aware features). That is correct if mirror images may behave differently (chiral molecules), and a bug if the target is mirror symmetric.
    6. Give dim⁡St⁡(1000,10)\dim\operatorname{St}(1000, 10) and dim⁡Gr⁡(1000,10)\dim\operatorname{Gr}(1000, 10). Why is tr⁡(X⊤SX)\operatorname{tr}(X^\top SX) a function on the Grassmann manifold?
      Answer10,000−55=994510{,}000 - 55 = 9945 and 10⋅990=990010\cdot990 = 9900. tr⁡((XQ)⊤S(XQ))=tr⁡(Q⊤X⊤SXQ)=tr⁡(X⊤SX)\operatorname{tr}((XQ)^\top S(XQ)) = \operatorname{tr}(Q^\top X^\top SXQ) = \operatorname{tr}(X^\top SX) for Q∈O(10)Q \in O(10) (cyclic trace): it depends only on the subspace.
    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. Give the eigenvalues and eigenvectors of the circulant with first column c=(2,1,0,1)c = (2, 1, 0, 1).

    2. Which linear maps commute with the cyclic shift PP?

    3. What does one FNO Fourier layer compute (one channel)?

    4. Define the graph Laplacian and its quadratic form.

    5. What does the number of zero eigenvalues of L=D−AL = D - A tell you, and why?

    6. What is the Fiedler vector, and what is it used for?

    7. Write the GCN propagation rule.

    8. Define an equivariant map, and an invariant one.

    9. What is the Stiefel manifold St⁡(n,p)\operatorname{St}(n, p), and what is its dimension?

    10. How is the Grassmann manifold built from the Stiefel manifold, and what is its dimension?

    Open the review →

    9. Interview angle #

    • "Why is convolution diagonal in Fourier space?" — It commutes with the shift; the shift has the Fourier vectors as eigenvectors with distinct eigenvalues; commuting matrices share them.
    • "Derive the GCN layer." — First-order Chebyshev filter, λmax⁡≈2\lambda_{\max} \approx 2, one shared parameter, self-loops and symmetric normalization (renormalization trick): H′=σ(A^HW)H' = \sigma(\hat AHW).
    • "What is over-smoothing, in linear-algebra terms?" — A^k\hat A^k is power iteration; features converge to the top eigenvector D~1/21\tilde D^{1/2}\mathbf 1. Fixes: residual connections, normalization, fewer layers.
    • "Why can an FNO trained on 64 points run on 256?" — Its weights belong to Fourier modes; every grid that resolves those modes gives the same operator.
    • "How do you take a gradient step with an orthogonality constraint?" — Project the gradient onto {V:X⊤V skew}\{V: X^\top V \text{ skew}\}, step, and retract with QR (positive diagonal in R) or the polar factor.

    10. Practice #

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

    1. Write circular convolution with the FFT and the eigenvalues of a circulant, and check C=F−1diag⁡(c^)FC = F^{-1}\operatorname{diag}(\hat c)F against scipy.linalg.circulant.
    2. Write a one-channel FNO Fourier layer with np.fft.rfft, and show that it commutes with shifts and gives the same function on grids of 64 and 256 points.
    3. Build the Laplacian of a graph, count its components from the spectrum, and split a graph with two planted communities by the signs of the Fiedler vector.
    4. Build the GCN matrix A^\hat A, check that its eigenvalues lie in (−1,1](-1, 1], test permutation equivariance, and watch over-smoothing.
    5. Take Riemannian gradient steps on the Stiefel manifold (tangent projection + QR retraction) to find the top-2 eigenvector subspace of a symmetric matrix, and measure the principal angles to the true subspace.

    11. Watch and read #

    Strang derives Theorem 1: the Fourier vectors are the eigenvectors of every circulant.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    The FNO layer as FFT, truncation to the low modes, a learned multiplication and an inverse FFT.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Laplacian eigenvectors as graph drawings, and what the eigenvalues say about cuts (Theorem 3).
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Symmetry groups, equivariance and why CNNs, GNNs and transformers fit one framework.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play