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

    Linear Algebra · Lesson 16 · Linear algebra for modern ML

    Matrix differentials, matrix functions, Kronecker product and vec

    Advanced 85 min 5 interactive figures 5 videos Colab notebook

    Before this lesson: lesson 03 (inverse, determinant, trace), lesson 05 (A=XΛX−1A = X\Lambda X^{-1} and eJte^{Jt}), lesson 06 (S=QΛQ⊤S = Q\Lambda Q^\top) and lesson 15 (JVP and VJP). Time: about 85 minutes, plus the notebook.


    Why this matters #

    Many ML objectives are scalar functions of a matrix: a Gaussian log-likelihood contains log⁡det⁡Σ\log\det\Sigma and Σ−1\Sigma^{-1}, and cross-entropy and attention go through a softmax. Index-by-index derivatives are slow and error-prone; the differential method gives them in a few lines. Matrix functions say what eAe^{A} or S\sqrt{S} means. The matrix exponential solves every linear ODE x′=Axx' = Ax: heat diffusion on a grid, linearized dynamics, the state-space layers of Mamba. The Kronecker product and vec turn linear maps on matrices, such as X↦AXBX \mapsto AXB, into ordinary matrices: the algebra behind layer Jacobians, K-FAC and Shampoo, and fast Gaussian processes on space–time grids.

    1. Predict first #

    2. The picture #

    Differentials. Start with one variable. The differential df=f′(x) dxdf = f'(x)\,dx is the linear part of the change f(x+dx)−f(x)f(x + dx) - f(x); the rest shrinks faster than dxdx. The matrix case is the same idea. For a function of a matrix XX, dfdf is the part of f(X+dX)−f(X)f(X + dX) - f(X) that is linear in the small matrix dXdX. We use rules (sum, product, chain) on whole matrices and never write indices. The one new thing: matrices do not commute, so the order of the factors must stay.

    A scalar ff of an m×nm\times n matrix has a linear part ∑ijGij dXij\sum_{ij} G_{ij}\, dX_{ij} for some m×nm\times n matrix GG, and this sum is tr⁡(G⊤dX)\operatorname{tr}(G^\top dX). So if we bring dfdf into the form tr⁡(G⊤dX)\operatorname{tr}(G^\top dX), the gradient is GG. The tool for this is the cyclic property of the trace.

    How to test a derivative. Walk from XX along a direction dXdX for a step tt. A correct derivative predicts the new value up to a second-order term:

    f(X+t dX)=f(X)+t df(dX)+O(t2).f(X + t\,dX) = f(X) + t\,df(dX) + O(t^2).

    So the error of the prediction falls like t2t^2: halve tt and the error is divided by 4. A wrong derivative leaves an error of order tt: halve tt and the error is only divided by 2. On a log–log plot the first is a line of slope 2 and the second a line of slope 1. This is the standard gradient check of autodiff libraries, done along one random direction.

    Interactive figure: loading…

    Choose a function and press New direction. The blue curve uses the correct derivative and falls with slope 2. The orange curve uses a common mistake (X−1X^{-1} instead of X−⊤X^{-\top}, 2Ax2Ax instead of (A+A⊤)x(A + A^\top)x, −X−2dX-X^{-2}dX instead of −X−1dX X−1-X^{-1}dX\,X^{-1}) and falls only with slope 1. For tr⁡(AX)\operatorname{tr}(AX) the function is linear, so the correct prediction is exact and only rounding error is left.

    Quick check

    You test a gradient along a random direction. At t=0.01t = 0.01 the error of the first-order prediction is 4×10−64\times10^{-6}. The gradient is correct. What error do you expect at t=0.005t = 0.005?

    Matrix functions. Take S=diag⁡(4,1)S = \operatorname{diag}(4, 1). The obvious candidate for S\sqrt{S} is diag⁡(2,1)\operatorname{diag}(2, 1): it squares to SS. A symmetric S=QΛQ⊤S = Q\Lambda Q^\top is diagonal in its eigen-axes, so we apply ff to each axis on its own: f(S)f(S) scales the eigen-axis qiq_i by f(λi)f(\lambda_i). It is not the entrywise f(Sij)f(S_{ij}).

    Kronecker and vec. The map X↦AXBX \mapsto AXB is linear in XX, so it is a matrix acting on the coordinates of XX. Stack the columns of XX into one long vector vec⁡(X)\operatorname{vec}(X). The matrix of the map is then B⊤⊗AB^\top \otimes A: a block matrix made of copies of AA, each scaled by an entry of B⊤B^\top.

    Interactive figure: loading…

    Match the blocks. Press Next block or click a block of B⊤⊗AB^\top \otimes A. Block (k,j)(k, j) is bjkAb_{jk}A. It multiplies column jj of XX (the same colour in vec⁡X\operatorname{vec}X), and block row kk adds up to column kk of AXBAXB. Press New numbers: the check at the bottom always says equal.

    3. Definitions and main results #

    The differential method #

    Definition. For a map ff from matrices to matrices, the differential dfdf is the linear function of dXdX with f(X+dX)=f(X)+df+o(∥dX∥)f(X + dX) = f(X) + df + o(\|dX\|).

    Rules. Each follows from expanding f(X+dX)f(X + dX) and dropping second-order terms.

    • constant: dA=0dA = 0; sum: d(F+G)=dF+dGd(F + G) = dF + dG;
    • product: d(FG)=dF G+F dGd(FG) = dF\, G + F\, dG (keep the order);
    • transpose and trace: d(F⊤)=(dF)⊤d(F^\top) = (dF)^\top, dtr⁡F=tr⁡(dF)d \operatorname{tr} F = \operatorname{tr}(dF);
    • chain rule: the differential of g(F(X))g(F(X)) is the differential of gg at FF, applied to dFdF.

    Identification rule. If a scalar ff has df=tr⁡(G⊤dX)df = \operatorname{tr}(G^\top dX) for every dXdX, then ∇Xf=G\nabla_X f = G, the matrix of partial derivatives ∂f/∂Xij\partial f/\partial X_{ij} (same shape as XX). Reason: tr⁡(G⊤dX)=∑ijGij dXij\operatorname{tr}(G^\top dX) = \sum_{ij} G_{ij}\,dX_{ij}; put dX=EijdX = E_{ij}, the matrix with a single 1 at (i,j)(i, j). For vectors, df=g⊤dxdf = g^\top dx gives ∇f=g\nabla f = g.

    The rule entry by entry, for a 2×22\times2 matrix.

    1. Multiply out: G⊤dX=(g11g21g12g22)(dx11dx12dx21dx22)G^\top dX = \begin{pmatrix} g_{11} & g_{21} \\ g_{12} & g_{22}\end{pmatrix}\begin{pmatrix} dx_{11} & dx_{12} \\ dx_{21} & dx_{22}\end{pmatrix}. Its diagonal entries are g11dx11+g21dx21g_{11}dx_{11} + g_{21}dx_{21} and g12dx12+g22dx22g_{12}dx_{12} + g_{22}dx_{22}.
    2. Add them: the trace is the sum of the four terms g11dx11g_{11}dx_{11}, g12dx12g_{12}dx_{12}, g21dx21g_{21}dx_{21} and g22dx22g_{22}dx_{22}, that is, tr⁡(G⊤dX)=∑ijgij dxij\operatorname{tr}(G^\top dX) = \sum_{ij}g_{ij}\,dx_{ij}. Every entry of dXdX is multiplied by the entry of GG at the same position.
    3. Compare with the multivariable chain rule df=∑ij∂f∂xijdxijdf = \sum_{ij}\frac{\partial f}{\partial x_{ij}}dx_{ij}: so gij=∂f/∂xijg_{ij} = \partial f/\partial x_{ij}.

    Example. f(X)=tr⁡(AX)f(X) = \operatorname{tr}(AX) with A=(1234)A = \begin{pmatrix} 1 & 2 \\ 3 & 4\end{pmatrix}. The diagonal of AXAX gives f=(x11+2x21)+(3x12+4x22)f = (x_{11} + 2x_{21}) + (3x_{12} + 4x_{22}), so

    ∇f=(∂f/∂x11∂f/∂x12∂f/∂x21∂f/∂x22)=(1324)=A⊤.\nabla f = \begin{pmatrix} \partial f/\partial x_{11} & \partial f/\partial x_{12} \\ \partial f/\partial x_{21} & \partial f/\partial x_{22}\end{pmatrix} = \begin{pmatrix} 1 & 3 \\ 2 & 4\end{pmatrix} = A^\top.

    The rule gives the same in one line: df=tr⁡(A dX)=tr⁡((A⊤)⊤dX)df = \operatorname{tr}(A\,dX) = \operatorname{tr}\big((A^\top)^\top dX\big).

    Trace tools. A scalar equals its own trace; tr⁡(M⊤)=tr⁡(M)\operatorname{tr}(M^\top) = \operatorname{tr}(M); and tr⁡(ABC)=tr⁡(BCA)=tr⁡(CAB)\operatorname{tr}(ABC) = \operatorname{tr}(BCA) = \operatorname{tr}(CAB) (cyclic moves only).

    Two first examples.

    • f(X)=tr⁡(AX)f(X) = \operatorname{tr}(AX): df=tr⁡(A dX)=tr⁡((A⊤)⊤dX)df = \operatorname{tr}(A\,dX) = \operatorname{tr}\big((A^\top)^\top dX\big), so ∇f=A⊤\nabla f = A^\top.
    • f(x)=x⊤Axf(x) = x^\top A x: df=dx⊤Ax+x⊤A dxdf = dx^\top A x + x^\top A\, dx. The first term is a scalar, so it equals its transpose x⊤A⊤dxx^\top A^\top dx. Hence df=x⊤(A+A⊤) dxdf = x^\top (A + A^\top)\, dx and ∇f=(A+A⊤)x\nabla f = (A + A^\top)x, which is 2Ax2Ax for a symmetric AA.

    So the quadratic f(x)=12x⊤Ax−b⊤xf(x) = \tfrac12 x^\top A x - b^\top x has ∇f=12(A+A⊤)x−b\nabla f = \tfrac12(A + A^\top)x - b. Only the symmetric part of AA enters ff, because x⊤Kx=0x^\top K x = 0 for a skew-symmetric KK. The shortcut ∇f=Ax−b\nabla f = Ax - b is right only when A=A⊤A = A^\top.

    Interactive figure: loading…

    The gradient is perpendicular to the level sets. Drag the violet point xx. The blue arrow is the true gradient 12(A+A⊤)x−b\tfrac12(A + A^\top)x - b; it crosses the green contour lines at a right angle. The orange dashed arrow is Ax−bAx - b: for a non-symmetric AA it points in another direction. Choose I + skew part: the contours are circles, yet Ax−bAx - b is turned by a fixed angle. Switch the arrow field to Ax−bAx - b to see the error everywhere.

    Theorem (derivative of the inverse) #

    For an invertible XX: d(X−1)=−X−1 dX X−1\quad d(X^{-1}) = -X^{-1}\, dX\, X^{-1}.

    Proof.

    1. Differentiate the identity XX−1=IX X^{-1} = I. The right side is constant, so d(XX−1)=0d(XX^{-1}) = 0.
    2. Product rule, with the order kept: dX X−1+X d(X−1)=0dX\, X^{-1} + X\, d(X^{-1}) = 0.
    3. Multiply on the left by X−1X^{-1}: d(X−1)=−X−1 dX X−1d(X^{-1}) = -X^{-1}\, dX\, X^{-1}. ■\blacksquare

    Why −X−2 dX-X^{-2}\,dX is wrong. To get −X−2dX-X^{-2}dX you must move dXdX past X−1X^{-1}, and matrices do not commute. A small example shows the difference. Take X=diag⁡(1,2)X = \operatorname{diag}(1, 2) and change one off-diagonal entry: dX=(00.100)dX = \begin{pmatrix} 0 & 0.1 \\ 0 & 0\end{pmatrix}.

    • Exact: (X+dX)−1=(10.102)−1=(1−0.0500.5)(X + dX)^{-1} = \begin{pmatrix} 1 & 0.1 \\ 0 & 2\end{pmatrix}^{-1} = \begin{pmatrix} 1 & -0.05 \\ 0 & 0.5\end{pmatrix}, so the change is (0−0.0500)\begin{pmatrix} 0 & -0.05 \\ 0 & 0\end{pmatrix}.
    • −X−1dX X−1-X^{-1}dX\,X^{-1}: the entry 0.10.1 is multiplied by (X−1)11=1(X^{-1})_{11} = 1 from the left and by (X−1)22=0.5(X^{-1})_{22} = 0.5 from the right, which gives −0.05-0.05. Correct (here even exactly).
    • −X−2dX-X^{-2}dX: the entry is multiplied by (X−2)11=1(X^{-2})_{11} = 1 only, which gives −0.1-0.1. Twice too large.

    Only when dXdX commutes with XX (for example in the 1×11\times1 case) do the two agree.

    Example: f(X)=a⊤X−1bf(X) = a^\top X^{-1} b has df=−a⊤X−1dX X−1b=−tr⁡(X−1b a⊤X−1dX)df = -a^\top X^{-1} dX\, X^{-1} b = -\operatorname{tr}(X^{-1} b\, a^\top X^{-1} dX), so ∇f=−X−⊤a b⊤X−⊤\nabla f = -X^{-\top} a\, b^\top X^{-\top}.

    Quick check

    XX is invertible and depends on a parameter θ\theta. What is ddθX−1\frac{d}{d\theta}X^{-1}?

    Theorem (Jacobi's formula and log⁡det⁡\log\det) #

    For an invertible XX: ddet⁡X=det⁡X⋅tr⁡(X−1dX)d\det X = \det X \cdot \operatorname{tr}(X^{-1}dX). If det⁡X>0\det X > 0, then dlog⁡det⁡X=tr⁡(X−1dX)d\log\det X = \operatorname{tr}(X^{-1}dX), so ∇Xlog⁡det⁡X=X−⊤\nabla_X \log\det X = X^{-\top} (=X−1= X^{-1} for a symmetric XX).

    Proof.

    1. Factor: X+dX=X(I+E)X + dX = X(I + E) with E=X−1dXE = X^{-1}dX. The determinant is multiplicative, so det⁡(X+dX)=det⁡X⋅det⁡(I+E)\det(X + dX) = \det X \cdot \det(I + E).
    2. det⁡(I+E)=1+tr⁡E+O(∥E∥2)\det(I + E) = 1 + \operatorname{tr}E + O(\|E\|^2). For a 2×22\times2 matrix you can see it directly: det⁡(I+E)=(1+e11)(1+e22)−e12e21=1+tr⁡E+det⁡E\det(I + E) = (1 + e_{11})(1 + e_{22}) - e_{12}e_{21} = 1 + \operatorname{tr}E + \det E, and det⁡E\det E is second order. The fold below shows it for every size.
    3. Keep the linear part: ddet⁡X=det⁡X⋅tr⁡(X−1dX)d\det X = \det X \cdot \operatorname{tr}(X^{-1}dX).
    4. Chain rule with dlog⁡u=du/ud\log u = du/u: dlog⁡det⁡X=tr⁡(X−1dX)d\log\det X = \operatorname{tr}(X^{-1}dX).
    5. Identify: tr⁡(X−1dX)=tr⁡((X−⊤)⊤dX)\operatorname{tr}(X^{-1}dX) = \operatorname{tr}\big((X^{-\top})^\top dX\big), so the gradient is X−⊤X^{-\top}. ■\blacksquare
    Step 2 for every size: det⁡(I+E)=1+tr⁡E+O(∥E∥2)\det(I + E) = 1 + \operatorname{tr}E + O(\|E\|^2)

    In the permutation expansion of det⁡(I+E)\det(I + E), the identity permutation gives ∏i(1+Eii)=1+∑iEii+O(∥E∥2)\prod_i (1 + E_{ii}) = 1 + \sum_i E_{ii} + O(\|E\|^2). Every other permutation moves at least two indices, so it uses at least two off-diagonal entries, and each is an entry of EE: that term is O(∥E∥2)O(\|E\|^2). The argument uses no eigenvalues, so it holds for every square EE, also when XX is not diagonalizable.

    The same formula holds for log⁡∣det⁡X∣\log|\det X| at every invertible XX (for det⁡X<0\det X < 0, apply step 4 to −det⁡X-\det X).

    The softmax Jacobian #

    For z∈Rnz \in \mathbb{R}^n, s=softmax⁡(z)s = \operatorname{softmax}(z) has entries si=ezi/Zs_i = e^{z_i}/Z with Z=∑jezjZ = \sum_j e^{z_j}.

    Result. ds=J dzds = J\,dz with J=diag⁡(s)−ss⊤J = \operatorname{diag}(s) - ss^\top.

    One entry first. Use the quotient rule on s1=ez1/Zs_1 = e^{z_1}/Z, and note that ∂Z/∂zj=ezj\partial Z/\partial z_j = e^{z_j}.

    • Same index: ∂s1∂z1=ez1Z−ez1ez1Z2=s1−s12=s1(1−s1)\dfrac{\partial s_1}{\partial z_1} = \dfrac{e^{z_1}Z - e^{z_1}e^{z_1}}{Z^2} = s_1 - s_1^2 = s_1(1 - s_1).
    • Other index: ∂s1∂z2=0⋅Z−ez1ez2Z2=−s1s2\dfrac{\partial s_1}{\partial z_2} = \dfrac{0\cdot Z - e^{z_1}e^{z_2}}{Z^2} = -s_1s_2.

    So Jij=siδij−sisjJ_{ij} = s_i\delta_{ij} - s_is_j: the diagonal holds si(1−si)s_i(1 - s_i), the rest −sisj-s_is_j. In matrix form this is diag⁡(s)−ss⊤\operatorname{diag}(s) - ss^\top. Check: equal logits for n=2n = 2 give s=(0.5,0.5)s = (0.5, 0.5) and

    J=(0.5000.5)−(0.250.250.250.25)=(0.25−0.25−0.250.25).J = \begin{pmatrix} 0.5 & 0 \\ 0 & 0.5\end{pmatrix} - \begin{pmatrix} 0.25 & 0.25 \\ 0.25 & 0.25\end{pmatrix} = \begin{pmatrix} 0.25 & -0.25 \\ -0.25 & 0.25\end{pmatrix}.

    Raising z1z_1 raises s1s_1 and lowers s2s_2 by the same amount, because s1+s2=1s_1 + s_2 = 1.

    The same result without indices.

    1. Take logs: log⁡si=zi−log⁡∑jezj\log s_i = z_i - \log\sum_j e^{z_j}.
    2. Differentiate: dlog⁡∑jezj=∑jezjdzj/∑lezl=s⊤dzd\log\sum_j e^{z_j} = \sum_j e^{z_j} dz_j / \sum_l e^{z_l} = s^\top dz. So dlog⁡si=dzi−s⊤dzd \log s_i = dz_i - s^\top dz.
    3. Use dsi=si dlog⁡sids_i = s_i\, d\log s_i: dsi=si dzi−si (s⊤dz)ds_i = s_i\,dz_i - s_i\,(s^\top dz), that is, ds=(diag⁡(s)−ss⊤) dzds = (\operatorname{diag}(s) - ss^\top)\,dz.
    Interactive figure: loading…

    Move the logits and the temperature. The bars are the probabilities ss; the map is J=diag⁡(s)−ss⊤J = \operatorname{diag}(s) - ss^\top (orange positive, blue negative). The row sums are always 0. The preset z=(0,ln⁡2,ln⁡3)z = (0, \ln 2, \ln 3) is worked example (b). Lower the temperature TT: ss becomes one-hot and every entry of JJ goes to 0, which is why a saturated softmax passes almost no gradient.

    Properties.

    • JJ is symmetric, and J1=s−s (s⊤1)=0J\mathbf{1} = s - s\,(s^\top\mathbf{1}) = 0 (1\mathbf 1 is the all-ones vector): adding a constant to all logits does not change the softmax. So JJ is singular.
    • JJ is PSD: v⊤Jv=∑isivi2−(∑isivi)2v^\top J v = \sum_i s_i v_i^2 - \big(\sum_i s_i v_i\big)^2 is the variance of viv_i when the index ii is drawn with probabilities ss. If all si>0s_i > 0, it is zero only for constant vv, so rank⁡J=n−1\operatorname{rank} J = n - 1.
    • Cross-entropy L=−log⁡syL = -\log s_y for the true class yy: by step 2, dL=−(dzy−s⊤dz)=(s−ey)⊤dzdL = -(dz_y - s^\top dz) = (s - e_y)^\top dz, so ∇zL=s−ey\nabla_z L = s - e_y (eye_y is the one-hot vector of class yy).
    • Backprop never builds JJ. With the upstream gradient g=∂L/∂sg = \partial L/\partial s, the VJP is J⊤g=s⊙(g−(s⊤g)1)J^\top g = s \odot \big(g - (s^\top g)\mathbf{1}\big) (⊙\odot is the entrywise product): O(n)O(n) work, not O(n2)O(n^2).
    Quick check

    The softmax output is s=(0.2,0.3,0.5)s = (0.2, 0.3, 0.5). What is ∂s1/∂z2\partial s_1/\partial z_2?

    Matrix functions via the spectrum #

    Definition. Let S=QΛQ⊤S = Q\Lambda Q^\top be symmetric (lesson 06), and let ff be defined at every eigenvalue. Then

    f(S)=Q f(Λ) Q⊤=∑if(λi) qiqi⊤,f(Λ)=diag⁡(f(λ1),…,f(λn)).f(S) = Q\, f(\Lambda)\, Q^\top = \sum_i f(\lambda_i)\, q_i q_i^\top, \qquad f(\Lambda) = \operatorname{diag}\big(f(\lambda_1), \dots, f(\lambda_n)\big).

    For a diagonalizable A=XΛX−1A = X\Lambda X^{-1}, in the same way f(A)=Xf(Λ)X−1f(A) = X f(\Lambda) X^{-1}.

    Why this is the right definition. Sk=QΛkQ⊤S^k = Q\Lambda^k Q^\top, because the inner factors Q⊤Q=IQ^\top Q = I cancel. So p(S)=Q p(Λ) Q⊤p(S) = Q\,p(\Lambda)\,Q^\top for every polynomial pp. A power series such as ex=∑kxk/k!e^x = \sum_k x^k/k! is a limit of polynomials, so the definition agrees with eS=∑kSk/k!e^S = \sum_k S^k/k!. It also does not depend on the choice of eigenbasis: inside an eigenspace, ff multiplies the whole projector onto it by the single number f(λ)f(\lambda).

    The three main functions.

    • eSe^S: defined for every symmetric SS; it is symmetric positive definite, with eigenvalues eλi>0e^{\lambda_i} > 0. The series eA=∑kAk/k!e^A = \sum_k A^k/k! defines eAe^A for every square AA, even a defective one.
    • S1/2=QΛ1/2Q⊤S^{1/2} = Q\Lambda^{1/2}Q^\top for S⪰0S \succeq 0: the unique PSD matrix whose square is SS. It is used for whitening (S−1/2xS^{-1/2}x) and in the Fréchet distance between Gaussians.
    • log⁡S=Qlog⁡Λ Q⊤\log S = Q \log\Lambda\, Q^\top for S≻0S \succ 0: symmetric, with elog⁡S=Se^{\log S} = S. A useful identity: log⁡det⁡S=∑ilog⁡λi=tr⁡log⁡S\log\det S = \sum_i \log\lambda_i = \operatorname{tr}\log S.

    Rules that do not carry over from numbers. eA+B=eAeBe^{A+B} = e^A e^B holds when AB=BAAB = BA (then the binomial theorem works for (A+B)k(A + B)^k). Without this condition it fails in general. In the same way, ddteA(t)=A′(t) eA(t)\frac{d}{dt}e^{A(t)} = A'(t)\,e^{A(t)} holds when A′(t)A'(t) commutes with A(t)A(t), and fails in general.

    Quick check

    For which pair of square matrices is eA+B=eAeBe^{A+B} = e^Ae^B guaranteed?

    Theorem (linear ODEs) #

    For A∈Rn×nA \in \mathbb{R}^{n\times n} and x0∈Rnx_0 \in \mathbb{R}^n, the unique solution of x′(t)=Ax(t)x'(t) = Ax(t) with x(0)=x0x(0) = x_0 is x(t)=eAtx0x(t) = e^{At}x_0.

    The key step is the derivative of the series: ddteAt=AeAt\frac{d}{dt}e^{At} = Ae^{At}, just as ddteat=aeat\frac{d}{dt}e^{at} = ae^{at} for a number aa.

    Proof (convergence, derivative, uniqueness)
    1. The series eAt=∑k≥0tkAk/k!e^{At} = \sum_{k\ge0} t^k A^k/k! converges for every tt: in the spectral norm (∥Ak∥2≤∥A∥2k\|A^k\|_2 \le \|A\|_2^k) the kk-th term has norm at most (∣t∣ ∥A∥2)k/k!(|t|\,\|A\|_2)^k/k!, and these numbers sum to e∣t∣ ∥A∥2e^{|t|\,\|A\|_2}.
    2. Differentiate term by term (allowed for a convergent power series): ddteAt=∑k≥1k tk−1Akk!=A∑k≥0tkAkk!=AeAt\frac{d}{dt}e^{At} = \sum_{k\ge1} \frac{k\,t^{k-1}A^k}{k!} = A\sum_{k\ge0}\frac{t^kA^k}{k!} = Ae^{At}.
    3. So x(t)=eAtx0x(t) = e^{At}x_0 satisfies x′=Axx' = Ax, and x(0)=Ix0=x0x(0) = Ix_0 = x_0.
    4. Uniqueness. Let yy be any solution. e−Ate^{-At} commutes with AA, so ddt(e−Aty)=e−At(−Ay+y′)=0\frac{d}{dt}\big(e^{-At}y\big) = e^{-At}(-Ay + y') = 0. Hence e−Aty(t)=y(0)=x0e^{-At}y(t) = y(0) = x_0. As AtAt and −At-At commute, eAte−At=e0=Ie^{At}e^{-At} = e^{0} = I, so y(t)=eAtx0y(t) = e^{At}x_0. ■\blacksquare

    Reading the solution. If A=XΛX−1A = X\Lambda X^{-1} with eigenvectors xix_i and c=X−1x0c = X^{-1}x_0, then x(t)=∑icieλitxix(t) = \sum_i c_i e^{\lambda_i t} x_i: each eigen-direction grows or decays at its own rate. The solution goes to 0 for every x0x_0 exactly when all Re⁡λi<0\operatorname{Re}\lambda_i < 0. A 2×22\times2 Jordan block Jλ=λI+NJ_\lambda = \lambda I + N, with N=(0100)N = \begin{pmatrix}0&1\\0&0\end{pmatrix} and N2=0N^2 = 0, gives eJλt=eλt(I+tN)e^{J_\lambda t} = e^{\lambda t}(I + tN), the teλtt e^{\lambda t} terms of lesson 05. Sampling at a step Δ\Delta gives the exact recurrence xk+1=eAΔxkx_{k+1} = e^{A\Delta}x_k.

    Interactive figure: loading…

    Let the plane flow. Press Play or move tt. Every point moves along x(t)=etAx(0)x(t) = e^{tA}x(0); the green ring is the unit circle carried by etAe^{tA}, and it stays an ellipse because etAe^{tA} is a linear map. Try the presets: real eigenvalues give straight eigen-directions (dashed), complex ones give spirals (closed ellipses for the center, where Re⁡λ=0\operatorname{Re}\lambda = 0), a saddle stretches along one eigen-direction and squeezes along the other. The readout shows that det⁡etA\det e^{tA} and ettr⁡Ae^{t\operatorname{tr}A} are the same number.

    How the figure computes etAe^{tA}, and how libraries do it

    For a 2×22\times2 matrix the figure computes etAe^{tA} exactly from the Cayley–Hamilton theorem (A2=2τA−(det⁡A)IA^2 = 2\tau A - (\det A) I with τ=12tr⁡A\tau = \tfrac12\operatorname{tr}A): with δ2=τ2−det⁡A\delta^2 = \tau^2 - \det A,

    etA=eτt(cosh⁡(δt) I+sinh⁡(δt)δ (A−τI)),e^{tA} = e^{\tau t}\Big(\cosh(\delta t)\,I + \frac{\sinh(\delta t)}{\delta}\,(A - \tau I)\Big),

    read with cosh⁡(δt)=cos⁡(ωt)\cosh(\delta t) = \cos(\omega t) and sinh⁡(δt)/δ=sin⁡(ωt)/ω\sinh(\delta t)/\delta = \sin(\omega t)/\omega when δ2=−ω2<0\delta^2 = -\omega^2 < 0, and with 11 and tt when δ=0\delta = 0. For larger matrices libraries use scaling and squaring: eA=(eA/2s)2se^{A} = \big(e^{A/2^s}\big)^{2^s}, with a Padé or Taylor approximation of eA/2se^{A/2^s} (the notebook builds one).

    Kronecker product and vec #

    Definitions. vec⁡(X)∈Rmn\operatorname{vec}(X) \in \mathbb{R}^{mn} stacks the columns of X∈Rm×nX \in \mathbb{R}^{m\times n}, the first column on top. For A∈Rm×nA \in \mathbb{R}^{m\times n} and B∈Rp×qB \in \mathbb{R}^{p\times q}, the Kronecker product is the mp×nqmp \times nq block matrix

    A⊗B=(a11B⋯a1nB⋮⋮am1B⋯amnB).A \otimes B = \begin{pmatrix} a_{11}B & \cdots & a_{1n}B \\ \vdots & & \vdots \\ a_{m1}B & \cdots & a_{mn}B \end{pmatrix}.

    Rules. (A⊗B)(C⊗D)=AC⊗BD(A\otimes B)(C\otimes D) = AC \otimes BD (the mixed-product rule, when ACAC and BDBD exist); (A⊗B)⊤=A⊤⊗B⊤(A\otimes B)^\top = A^\top\otimes B^\top; (A⊗B)−1=A−1⊗B−1(A\otimes B)^{-1} = A^{-1}\otimes B^{-1} (for invertible AA, BB); vec⁡(uv⊤)=v⊗u\operatorname{vec}(uv^\top) = v\otimes u; tr⁡(A⊤X)=vec⁡(A)⊤vec⁡(X)\operatorname{tr}(A^\top X) = \operatorname{vec}(A)^\top\operatorname{vec}(X).

    Theorem (vec of a product) #

    For A∈Rp×mA \in \mathbb{R}^{p\times m}, X∈Rm×nX \in \mathbb{R}^{m\times n}, B∈Rn×qB \in \mathbb{R}^{n\times q}: vec⁡(AXB)=(B⊤⊗A)vec⁡(X)\quad \operatorname{vec}(AXB) = (B^\top \otimes A)\operatorname{vec}(X).

    Proof.

    1. Let xjx_j be column jj of XX and bjkb_{jk} the entries of BB. Column kk of XBXB is ∑jbjk xj\sum_j b_{jk}\,x_j.
    2. So column kk of AXBAXB is ∑jbjk (Axj)\sum_j b_{jk}\,(Ax_j).
    3. Block kk of vec⁡(AXB)\operatorname{vec}(AXB) is column kk of AXBAXB, and block jj of vec⁡(X)\operatorname{vec}(X) is xjx_j. Step 2 says: block kk of the output =∑j(bjkA) (block j of the input)= \sum_j (b_{jk}A)\,(\text{block } j \text{ of the input}).
    4. So the matrix of the map has block (k,j)(k, j) equal to bjkA=(B⊤)kjAb_{jk}A = (B^\top)_{kj}A. This is the definition of B⊤⊗AB^\top\otimes A. ■\blacksquare

    Special cases: vec⁡(AX)=(I⊗A)vec⁡X\operatorname{vec}(AX) = (I\otimes A)\operatorname{vec}X and vec⁡(XB)=(B⊤⊗I)vec⁡X\operatorname{vec}(XB) = (B^\top\otimes I)\operatorname{vec}X. Example: a linear layer Y=WXY = WX has dY=dW XdY = dW\,X, so the Jacobian of vec⁡Y\operatorname{vec}Y with respect to vec⁡W\operatorname{vec}W is X⊤⊗IX^\top\otimes I. A VJP never forms it: for the upstream gradient G=∂L/∂YG = \partial L/\partial Y, (X⊤⊗I)⊤vec⁡G=(X⊗I)vec⁡G=vec⁡(GX⊤)(X^\top\otimes I)^\top\operatorname{vec}G = (X\otimes I)\operatorname{vec}G = \operatorname{vec}(GX^\top), the familiar weight gradient GX⊤GX^\top.

    Theorem (eigenvalues of Kronecker products) #

    Let AA be n×nn\times n with eigenvalues λ1,…,λn\lambda_1, \dots, \lambda_n, and BB be m×mm\times m with eigenvalues μ1,…,μm\mu_1, \dots, \mu_m (with multiplicity). Then A⊗BA\otimes B has the nmnm eigenvalues λiμj\lambda_i\mu_j, and the Kronecker sum A⊗Im+In⊗BA\otimes I_m + I_n\otimes B has the nmnm eigenvalues λi+μj\lambda_i + \mu_j.

    Why (eigenvectors). If Ax=λxAx = \lambda x and By=μyBy = \mu y, the mixed-product rule gives (A⊗B)(x⊗y)=Ax⊗By=λμ (x⊗y)(A\otimes B)(x\otimes y) = Ax\otimes By = \lambda\mu\,(x\otimes y) and (A⊗I+I⊗B)(x⊗y)=λx⊗y+x⊗μy=(λ+μ) (x⊗y)(A\otimes I + I\otimes B)(x\otimes y) = \lambda x\otimes y + x\otimes\mu y = (\lambda + \mu)\,(x\otimes y). When AA and BB are diagonalizable, the nmnm vectors xi⊗yjx_i\otimes y_j form a basis, and the theorem follows.

    Proof with multiplicities, also for non-diagonalizable matrices (Schur form)
    1. Take Schur forms A=UTU∗A = UTU^* and B=VRV∗B = VRV^*, with U,VU, V unitary and T,RT, R upper triangular (diagonals: the eigenvalues).
    2. Mixed product: A⊗B=(U⊗V)(T⊗R)(U⊗V)∗A\otimes B = (U\otimes V)(T\otimes R)(U\otimes V)^*, and U⊗VU\otimes V is unitary. So A⊗BA\otimes B is similar to T⊗RT\otimes R.
    3. T⊗RT\otimes R is upper triangular: its blocks tijRt_{ij}R below the diagonal are zero, and its diagonal blocks tiiRt_{ii}R are upper triangular. Its diagonal entries are tiirjj=λiμjt_{ii}r_{jj} = \lambda_i\mu_j.
    4. In the same way A⊗I+I⊗BA\otimes I + I\otimes B is similar to T⊗I+I⊗RT\otimes I + I\otimes R, which is upper triangular with diagonal entries λi+μj\lambda_i + \mu_j. ■\blacksquare

    Consequences: tr⁡(A⊗B)=tr⁡A⋅tr⁡B\operatorname{tr}(A\otimes B) = \operatorname{tr}A\cdot\operatorname{tr}B, det⁡(A⊗B)=(det⁡A)m(det⁡B)n\det(A\otimes B) = (\det A)^m(\det B)^n, the singular values of A⊗BA\otimes B are σi(A)σj(B)\sigma_i(A)\sigma_j(B), so κ(A⊗B)=κ(A)κ(B)\kappa(A\otimes B) = \kappa(A)\kappa(B).

    The Sylvester equation AX+XB=CAX + XB = C is linear in XX. With vec it reads (I⊗A+B⊤⊗I)vec⁡X=vec⁡C(I\otimes A + B^\top\otimes I)\operatorname{vec}X = \operatorname{vec}C. That matrix is a Kronecker sum with eigenvalues λi(A)+μj(B)\lambda_i(A) + \mu_j(B) (B⊤B^\top has the eigenvalues of BB). So the solution is unique exactly when λi(A)+μj(B)≠0\lambda_i(A) + \mu_j(B) \ne 0 for all i,ji, j.

    Answers to section 1. (1) No. The derivative is −X−1 dX X−1-X^{-1}\,dX\,X^{-1}. It equals −X−2dX-X^{-2}dX only when dXdX commutes with XX, as in the 1×11\times1 case. (2) Not in general; it holds when AB=BAAB = BA. For A=(0100)A = \begin{pmatrix}0&1\\0&0\end{pmatrix} and B=A⊤B = A^\top: eAeB=(1101)(1011)=(2111)e^Ae^B = \begin{pmatrix}1&1\\0&1\end{pmatrix}\begin{pmatrix}1&0\\1&1\end{pmatrix} = \begin{pmatrix}2&1\\1&1\end{pmatrix}, but eA+B=(cosh⁡1sinh⁡1sinh⁡1cosh⁡1)≈(1.5431.1751.1751.543)e^{A+B} = \begin{pmatrix}\cosh 1&\sinh 1\\ \sinh 1&\cosh 1\end{pmatrix} \approx \begin{pmatrix}1.543&1.175\\1.175&1.543\end{pmatrix}.

    4. Worked example #

    (a) log⁡det⁡\log\det and the inverse, with numbers. X=(2112)X = \begin{pmatrix}2&1\\1&2\end{pmatrix} has det⁡X=3\det X = 3 and X−1=13(2−1−12)X^{-1} = \frac13\begin{pmatrix}2&-1\\-1&2\end{pmatrix}. Change one entry: dX=0.01 E11dX = 0.01\,E_{11}.

    • Prediction: dlog⁡det⁡X=tr⁡(X−1dX)=0.01⋅23≈0.0066667d\log\det X = \operatorname{tr}(X^{-1}dX) = 0.01\cdot\frac23 \approx 0.0066667. Exact: det⁡(X+dX)=2.01⋅2−1=3.02\det(X + dX) = 2.01\cdot 2 - 1 = 3.02 and log⁡3.02−log⁡3≈0.0066445\log 3.02 - \log 3 \approx 0.0066445. The difference, about 2.2×10−52.2\times10^{-5}, is second order.
    • Prediction: −X−1dX X−1=−0.01 (X−1e1)(e1⊤X−1)=−0.019(4−2−21)≈(−0.0044440.0022220.002222−0.001111)-X^{-1}dX\,X^{-1} = -0.01\,(X^{-1}e_1)(e_1^\top X^{-1}) = -\frac{0.01}{9}\begin{pmatrix}4&-2\\-2&1\end{pmatrix} \approx \begin{pmatrix}-0.004444&0.002222\\0.002222&-0.001111\end{pmatrix}. Exact: (X+dX)−1−X−1≈(−0.0044150.0022080.002208−0.001104)(X + dX)^{-1} - X^{-1} \approx \begin{pmatrix}-0.004415&0.002208\\0.002208&-0.001104\end{pmatrix}.

    (b) Softmax. z=(0,ln⁡2,ln⁡3)z = (0, \ln 2, \ln 3) gives ez=(1,2,3)e^z = (1, 2, 3) and s=(16,13,12)s = (\frac16, \frac13, \frac12). Then

    J=diag⁡(s)−ss⊤=136(5−2−3−28−6−3−69).J = \operatorname{diag}(s) - ss^\top = \frac1{36}\begin{pmatrix}5&-2&-3\\-2&8&-6\\-3&-6&9\end{pmatrix}.

    For example J11=16⋅56=536J_{11} = \frac16\cdot\frac56 = \frac5{36} and J12=−16⋅13=−236J_{12} = -\frac16\cdot\frac13 = -\frac2{36}. Each row sums to 0 (J1=0J\mathbf 1 = 0). With the true class y=3y = 3: ∇zL=s−e3=(16,13,−12)\nabla_z L = s - e_3 = (\frac16, \frac13, -\frac12).

    (c) A square root and a linear ODE. S=(2112)S = \begin{pmatrix}2&1\\1&2\end{pmatrix} has λ=3\lambda = 3 with q1=(1,1)/2q_1 = (1,1)/\sqrt2 and λ=1\lambda = 1 with q2=(1,−1)/2q_2 = (1,-1)/\sqrt2. With the projectors P1=q1q1⊤=12(1111)P_1 = q_1q_1^\top = \frac12\begin{pmatrix}1&1\\1&1\end{pmatrix} and P2=q2q2⊤=12(1−1−11)P_2 = q_2q_2^\top = \frac12\begin{pmatrix}1&-1\\-1&1\end{pmatrix}:

    S1/2=3 P1+1⋅P2=12(3+13−13−13+1)≈(1.3660.3660.3661.366).S^{1/2} = \sqrt3\,P_1 + 1\cdot P_2 = \frac12\begin{pmatrix}\sqrt3+1&\sqrt3-1\\\sqrt3-1&\sqrt3+1\end{pmatrix} \approx \begin{pmatrix}1.366&0.366\\0.366&1.366\end{pmatrix}.

    Squaring gives back SS. In the same way log⁡S=ln⁡3⋅P1\log S = \ln 3\cdot P_1 (because ln⁡1=0\ln 1 = 0), and tr⁡log⁡S=ln⁡3=log⁡det⁡S\operatorname{tr}\log S = \ln 3 = \log\det S.

    Now heat flow in two cells. Each cell exchanges heat with the other (rate 1) and loses heat to the surroundings (rate 1): x1′=(x2−x1)−x1x_1' = (x_2 - x_1) - x_1, and the same for x2x_2. So x′=Axx' = Ax with A=(−211−2)A = \begin{pmatrix}-2&1\\1&-2\end{pmatrix}, which has eigenvalue −1-1 for q1q_1 and −3-3 for q2q_2. Start with all heat in cell 1: x0=(1,0)=12(q1+q2)x_0 = (1, 0) = \frac1{\sqrt2}(q_1 + q_2). Then

    x(t)=eAtx0=12e−t(11)+12e−3t(1−1).x(t) = e^{At}x_0 = \tfrac12 e^{-t}\begin{pmatrix}1\\1\end{pmatrix} + \tfrac12 e^{-3t}\begin{pmatrix}1\\-1\end{pmatrix}.

    The average temperature decays at rate 1; the difference between the cells decays three times faster. At t=1t = 1: x≈(0.2088,0.1590)x \approx (0.2088, 0.1590).

    (d) vec and Kronecker, step by step. A=(1234)A = \begin{pmatrix}1&2\\3&4\end{pmatrix}, X=AX = A, and B=(0110)B = \begin{pmatrix}0&1\\1&0\end{pmatrix} (it swaps columns).

    1. Left side: AX=(7101522)AX = \begin{pmatrix}7&10\\15&22\end{pmatrix} and AXB=(1072215)AXB = \begin{pmatrix}10&7\\22&15\end{pmatrix}, so vec⁡(AXB)=(10,22,7,15)\operatorname{vec}(AXB) = (10, 22, 7, 15) (first column on top).
    2. vec⁡X=(1,3,2,4)\operatorname{vec}X = (1, 3, 2, 4): block 1 is x1=(1,3)x_1 = (1, 3), block 2 is x2=(2,4)x_2 = (2, 4).
    3. B⊤=BB^\top = B, so block (k,j)(k, j) of B⊤⊗AB^\top\otimes A is bjkAb_{jk}A: blocks (1,1)(1,1) and (2,2)(2,2) are 0⋅A0\cdot A, blocks (1,2)(1,2) and (2,1)(2,1) are 1⋅A1\cdot A. So B⊤⊗A=(0AA0)B^\top\otimes A = \begin{pmatrix}0&A\\A&0\end{pmatrix}.
    4. Block row 1 gives 0⋅x1+Ax2=A(2,4)=(10,22)0\cdot x_1 + A x_2 = A(2, 4) = (10, 22); block row 2 gives Ax1+0⋅x2=A(1,3)=(7,15)Ax_1 + 0\cdot x_2 = A(1, 3) = (7, 15).
    5. Stacked: (10,22,7,15)(10, 22, 7, 15), the same vector as in step 1. ✓

    Eigenvalues: A1=(2103)A_1 = \begin{pmatrix}2&1\\0&3\end{pmatrix} (eigenvalues 2, 3) and B1=(5027)B_1 = \begin{pmatrix}5&0\\2&7\end{pmatrix} (eigenvalues 5, 7). A1⊗B1A_1\otimes B_1 has eigenvalues 10,14,15,2110, 14, 15, 21, and A1⊗I+I⊗B1A_1\otimes I + I\otimes B_1 has 7,9,8,107, 9, 8, 10. Check: det⁡(A1⊗B1)=62⋅352=44100=10⋅14⋅15⋅21\det(A_1\otimes B_1) = 6^2\cdot35^2 = 44100 = 10\cdot14\cdot15\cdot21.

    Quick check

    What are the eigenvalues of diag⁡(2,3)⊗(1105)\operatorname{diag}(2, 3)\otimes\begin{pmatrix}1&1\\0&5\end{pmatrix}?

    5. In code #

    NumPy and SciPy check the hand computations above; finite differences stand in for autodiff. The notebook for this lesson has these cells and the exercises.

    import numpy as np
    from scipy.linalg import expm, sqrtm, logm
    from scipy.special import softmax
    
    X = np.array([[2., 1.], [1., 2.]])
    Xi = np.linalg.inv(X)
    dX = np.array([[0.01, 0.], [0., 0.]])
    
    # d log det X = tr(X^{-1} dX): equal to first order
    print(round(np.linalg.slogdet(X + dX)[1] - np.linalg.slogdet(X)[1], 7),
          round(np.trace(Xi @ dX), 7))                        # 0.0066445 0.0066667
    # d(X^{-1}) = -X^{-1} dX X^{-1}
    print(np.round(np.linalg.inv(X + dX) - Xi, 6))            # [[-0.004415  0.002208] [ 0.002208 -0.001104]]
    print(np.round(-Xi @ dX @ Xi, 6))                         # [[-0.004444  0.002222] [ 0.002222 -0.001111]]
    
    def num_grad(f, X, h=1e-6):
        """Central differences, one entry at a time."""
        G = np.zeros_like(X)
        for idx in np.ndindex(X.shape):
            E = np.zeros_like(X); E[idx] = h
            G[idx] = (f(X + E) - f(X - E)) / (2 * h)
        return G
    
    # the gradient of log det is X^{-T}, not X^{-1} (non-symmetric X here)
    Xn = np.array([[2., 1.], [0., 3.]])
    g = num_grad(lambda M: np.linalg.slogdet(M)[1], Xn)
    print(np.allclose(g, np.linalg.inv(Xn).T, atol=1e-8), np.allclose(g, np.linalg.inv(Xn)))   # True False
    
    # softmax Jacobian = diag(s) - s s^T (columns by finite differences)
    z = np.log(np.array([1., 2., 3.]))
    s = softmax(z)
    J = np.column_stack([(softmax(z + 1e-6 * e) - softmax(z - 1e-6 * e)) / 2e-6 for e in np.eye(3)])
    print(np.round(36 * J, 6))                                # [[5 -2 -3] [-2 8 -6] [-3 -6 9]]
    print(np.allclose(J, np.diag(s) - np.outer(s, s)), np.allclose(J @ np.ones(3), 0))  # True True
    
    # matrix functions: f(S) = Q f(Lambda) Q^T, not entrywise
    S = X
    lam, Q = np.linalg.eigh(S)
    R = Q @ np.diag(np.sqrt(lam)) @ Q.T
    print(np.round(R, 6), np.allclose(R, sqrtm(S)), np.allclose(R @ R, S))   # [[1.366025 0.366025] ...] True True
    print(np.allclose(expm(S), Q @ np.diag(np.exp(lam)) @ Q.T), np.allclose(expm(S), np.exp(S)))  # True False
    print(np.isclose(np.trace(logm(S)), np.log(np.linalg.det(S))))          # True: tr log S = log det S
    
    # x' = Ax is solved by x(t) = e^{At} x0 (the two-cell heat example)
    A = np.array([[-2., 1.], [1., -2.]])
    print(np.round(expm(A * 1.0) @ np.array([1., 0.]), 6))   # [0.208833 0.159046]
    
    # e^{A+B} != e^A e^B when AB != BA
    N = np.array([[0., 1.], [0., 0.]])
    print(np.round(expm(N) @ expm(N.T), 4))                   # [[2. 1.] [1. 1.]]
    print(np.round(expm(N + N.T), 4))                         # [[1.5431 1.1752] [1.1752 1.5431]]
    
    # vec(AXB) = (B^T kron A) vec(X); vec stacks COLUMNS, so use order="F"
    A2 = np.array([[1., 2.], [3., 4.]]); B2 = np.array([[0., 1.], [1., 0.]])
    vec = lambda M: M.reshape(-1, order="F")
    print(vec(A2 @ A2 @ B2), np.kron(B2.T, A2) @ vec(A2))    # [10. 22.  7. 15.] twice
    
    # eigenvalues: products for A kron B, sums for the Kronecker sum
    A1 = np.array([[2., 1.], [0., 3.]]); B1 = np.array([[5., 0.], [2., 7.]]); I2 = np.eye(2)
    print(np.sort(np.linalg.eigvals(np.kron(A1, B1)).real))                      # [10. 14. 15. 21.]
    print(np.sort(np.linalg.eigvals(np.kron(A1, I2) + np.kron(I2, B1)).real))    # [ 7.  8.  9. 10.]
    

    The gradient check of section 2, as code: the error of the first-order prediction along a random direction, for the correct gradient X−⊤X^{-\top} of log⁡det⁡\log\det and for the wrong X−1X^{-1}.

    import numpy as np
    
    rng = np.random.default_rng(0)
    X = np.array([[2., 1.], [0.5, 3.]])            # not symmetric, so X^{-T} != X^{-1}
    D = rng.standard_normal((2, 2)); D /= np.linalg.norm(D)
    f = lambda M: np.linalg.slogdet(M)[1]
    for G, name in [(np.linalg.inv(X).T, "X^{-T}"), (np.linalg.inv(X), "X^{-1}")]:
        err = [abs(f(X + t * D) - f(X) - t * np.sum(G * D)) for t in (1e-3, 5e-4)]
        print(name, round(err[0] / err[1], 2))      # X^{-T} 4.0 (O(t^2)), X^{-1} 2.0 (O(t))
    

    6. Where it shows up #

    • Gaussian maximum likelihood. For NN samples with known mean μ\mu, ℓ(Σ)=−N2(log⁡det⁡Σ+tr⁡(Σ−1S^))+const\ell(\Sigma) = -\frac N2\big(\log\det\Sigma + \operatorname{tr}(\Sigma^{-1}\hat S)\big) + \text{const}, with S^=1N∑n(xn−μ)(xn−μ)⊤\hat S = \frac1N\sum_n (x_n - \mu)(x_n - \mu)^\top. The two theorems give dℓ=−N2tr⁡((Σ−1−Σ−1S^Σ−1) dΣ)d\ell = -\frac N2\operatorname{tr}\big((\Sigma^{-1} - \Sigma^{-1}\hat S\Sigma^{-1})\,d\Sigma\big). This is zero for every symmetric dΣd\Sigma exactly when Σ=S^\Sigma = \hat S.
    • Gaussian processes and flows. GP hyperparameters need ∂θlog⁡det⁡K=tr⁡(K−1∂θK)\partial_\theta\log\det K = \operatorname{tr}(K^{-1}\partial_\theta K). Normalizing flows need log⁡∣det⁡J∣\log|\det J|, so they use triangular Jacobians (the change-of-variables formula).
    • Softmax. In attention backward, FlashAttention computes dSij=Pij(dPij−Di)dS_{ij} = P_{ij}(dP_{ij} - D_i) with Di=∑jPij dPijD_i = \sum_j P_{ij}\,dP_{ij}: the VJP s⊙(g−(s⊤g)1)s\odot(g - (s^\top g)\mathbf 1), row by row.
    • Linear dynamics. eAte^{At} solves semi-discretized heat equations (u′=Luu' = Lu), linearized climate models and continuous-time Markov chains. Mamba discretizes h′=Ah+Bxh' = Ah + Bx with step Δ\Delta by Aˉ=eΔA\bar A = e^{\Delta A} (zero-order hold). Its AA is diagonal, so here "entrywise" is right.
    • SPD square roots and logs. Whitening Σ−1/2x\Sigma^{-1/2}x; the Fréchet distance behind FID, ∥μ1−μ2∥2+tr⁡(Σ1+Σ2−2(Σ11/2Σ2Σ11/2)1/2)\|\mu_1-\mu_2\|^2 + \operatorname{tr}\big(\Sigma_1 + \Sigma_2 - 2(\Sigma_1^{1/2}\Sigma_2\Sigma_1^{1/2})^{1/2}\big); the log-Euclidean distance ∥log⁡S1−log⁡S2∥F\|\log S_1 - \log S_2\|_F between covariance matrices (lesson 19).
    • K-FAC and Shampoo. For a layer s=Was = Wa with backpropagated g=∂L/∂sg = \partial L/\partial s, the weight gradient is ga⊤ga^\top, and vec⁡(ga⊤)=a⊗g\operatorname{vec}(ga^\top) = a\otimes g. K-FAC approximates the Fisher block E[aa⊤⊗gg⊤]\mathbb E[aa^\top\otimes gg^\top] by Aˉ⊗Gˉ\bar A\otimes\bar G (Aˉ=E[aa⊤]\bar A = \mathbb E[aa^\top], Gˉ=E[gg⊤]\bar G = \mathbb E[gg^\top]). Then (Aˉ⊗Gˉ)−1vec⁡(ga⊤)=vec⁡(Gˉ−1 ga⊤ Aˉ−1)(\bar A\otimes\bar G)^{-1}\operatorname{vec}(ga^\top) = \operatorname{vec}(\bar G^{-1}\,ga^\top\,\bar A^{-1}): two small inverses instead of one huge one. Shampoo steps along HL−1/4MtHR−1/4H_L^{-1/4}M_tH_R^{-1/4} for the gradient matrix MtM_t, with HL=∑tMtMt⊤H_L = \sum_t M_tM_t^\top and HR=∑tMt⊤MtH_R = \sum_t M_t^\top M_t. By the vec rule this is the Kronecker preconditioner HR−1/4⊗HL−1/4H_R^{-1/4}\otimes H_L^{-1/4}, built from two matrix functions.
    • Separable kernels and grids (ML for science). A space–time GP on a grid often uses K=Ktime⊗KspaceK = K_{\text{time}}\otimes K_{\text{space}}. Then K+σ2IK + \sigma^2I has eigenvalues λiμj+σ2\lambda_i\mu_j + \sigma^2, so the solve and the log⁡det⁡\log\det need only the eigendecompositions of the two small factors. The 2-D Poisson matrix is Tm⊗I+I⊗TmT_m\otimes I + I\otimes T_m (TmT_m the 1-D second-difference matrix): its eigenvalues are sums of 1-D eigenvalues, which gives its condition number in lesson 14.

    7. Common mistakes #

    • Losing the order. d(X−1)≠−X−2dXd(X^{-1}) \ne -X^{-2}dX, and d(X2)=dX X+X dXd(X^2) = dX\,X + X\,dX, not 2X dX2X\,dX.
    • ∇log⁡det⁡X=X−1\nabla\log\det X = X^{-1} for every XX. It is X−⊤X^{-\top}. For symmetric XX, some books give 2X−1−diag⁡(X−1)2X^{-1} - \operatorname{diag}(X^{-1}): the same differential, written in the free entries only (so XijX_{ij} and XjiX_{ji} move together). Safer: use the full-matrix gradient with a symmetric dXdX.
    • ∇(12x⊤Ax−b⊤x)=Ax−b\nabla(\tfrac12 x^\top Ax - b^\top x) = Ax - b for every AA. It is 12(A+A⊤)x−b\tfrac12(A + A^\top)x - b.
    • Entrywise instead of matrix functions. np.exp(S) and np.sqrt(S) act entry by entry. Use scipy.linalg.expm, sqrtm, logm, or Qf(Λ)Q⊤Qf(\Lambda)Q^\top.
    • eA+B=eAeBe^{A+B} = e^Ae^B without AB=BAAB = BA. The same goes for log⁡(AB)=log⁡A+log⁡B\log(AB) = \log A + \log B.
    • Products vs sums. A⊗BA\otimes B has eigenvalues λiμj\lambda_i\mu_j; the Kronecker sum has λi+μj\lambda_i + \mu_j.
    • Row-major vec. NumPy's ravel() stacks rows; column-stacking vec is X.reshape(-1, order="F"). With row stacking the rule becomes vec⁡r(AXB)=(A⊗B⊤)vec⁡r(X)\operatorname{vec}_r(AXB) = (A\otimes B^\top)\operatorname{vec}_r(X).
    • Building Kronecker products or full Jacobians. B⊤⊗AB^\top\otimes A for two 1000×10001000\times1000 matrices has 101210^{12} entries. Apply it as AXBAXB.
    • Softmax Jacobian =diag⁡(s)= \operatorname{diag}(s). This misses −ss⊤-ss^\top; the true JJ is singular.
    • Trusting a gradient check at one step size. One small error at one tt can hide a wrong gradient (for example when tt is so small that rounding dominates). Compare two step sizes and look for the factor 4.

    8. Check yourself #

    1. Gradient of f(X)=a⊤Xbf(X) = a^\top X b?
      Answerdf=a⊤dX b=tr⁡(b a⊤dX)df = a^\top dX\,b = \operatorname{tr}(b\,a^\top dX), so ∇f=(ba⊤)⊤=ab⊤\nabla f = (ba^\top)^\top = ab^\top.
    2. Gradient of f(X)=tr⁡(X−1)f(X) = \operatorname{tr}(X^{-1})?
      Answerdf=−tr⁡(X−1dX X−1)=−tr⁡(X−2dX)df = -\operatorname{tr}(X^{-1}dX\,X^{-1}) = -\operatorname{tr}(X^{-2}dX) (cyclic move), so ∇f=−(X−2)⊤\nabla f = -(X^{-2})^\top.
    3. Why is det⁡eA=etr⁡A\det e^A = e^{\operatorname{tr}A}?
      AnswerWith a Schur form A=UTU∗A = UTU^*, eA=UeTU∗e^A = Ue^TU^* and eTe^T is upper triangular with diagonal eλie^{\lambda_i}. So det⁡eA=∏ieλi=e∑iλi=etr⁡A\det e^A = \prod_i e^{\lambda_i} = e^{\sum_i\lambda_i} = e^{\operatorname{tr}A}.
    4. Solve x′=Axx' = Ax for A=(01−10)A = \begin{pmatrix}0&1\\-1&0\end{pmatrix}, x(0)=(1,0)x(0) = (1, 0). Why does ∥x(t)∥\|x(t)\| stay 1?
      AnswerA2=−IA^2 = -I, so the series gives eAt=cos⁡t I+sin⁡t A=(cos⁡tsin⁡t−sin⁡tcos⁡t)e^{At} = \cos t\,I + \sin t\,A = \begin{pmatrix}\cos t&\sin t\\-\sin t&\cos t\end{pmatrix} and x(t)=(cos⁡t,−sin⁡t)x(t) = (\cos t, -\sin t). AA is skew-symmetric, so (eAt)⊤=e−At=(eAt)−1(e^{At})^\top = e^{-At} = (e^{At})^{-1}: eAte^{At} is orthogonal and keeps lengths.
    5. AA (2×22\times2) has eigenvalues 1,21, 2 and BB (2×22\times2) has eigenvalues −1,4-1, 4. Give the eigenvalues of A⊗BA\otimes B and of A⊗I+I⊗BA\otimes I + I\otimes B. Does AX+XB=CAX + XB = C have a unique solution?
      AnswerProducts: −1,4,−2,8-1, 4, -2, 8. Sums: 0,5,1,60, 5, 1, 6. The Sylvester matrix I⊗A+B⊤⊗II\otimes A + B^\top\otimes I has the eigenvalues λi+μj\lambda_i + \mu_j, and one of them is 1+(−1)=01 + (-1) = 0. The matrix is singular, so for a given CC there is either no solution or infinitely many.
    6. Why is the softmax Jacobian PSD, and why is it singular?
      Answerv⊤Jv=∑isivi2−(∑isivi)2v^\top Jv = \sum_i s_iv_i^2 - (\sum_i s_iv_i)^2 is a variance under the distribution ss, so it is ≥0\ge 0. J1=0J\mathbf 1 = 0, because adding the same constant to all logits does not change ss.
    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. State the identification rule of matrix calculus.

    2. Why must the product rule for matrices keep the order of the factors?

    3. What is d(X−1)d(X^{-1})?

    4. What is dlog⁡det⁡Xd\log\det X, and what is the gradient?

    5. Compute ∇Xtr⁡(AX)\nabla_X\operatorname{tr}(AX).

    6. What is the Jacobian of s=softmax⁡(z)s = \operatorname{softmax}(z)? Give it for s=(0.5,0.5)s = (0.5, 0.5).

    7. How do you compute f(S)f(S) for a symmetric matrix SS?

    8. What solves x′=Axx' = Ax with x(0)=x0x(0) = x_0?

    9. State the vec rule for AXBAXB.

    10. What are the eigenvalues of A⊗BA\otimes B and of A⊗I+I⊗BA\otimes I + I\otimes B?

    Open the review →

    9. Interview angle #

    • "Derive the MLE of a Gaussian covariance." — dlog⁡det⁡Σ=tr⁡(Σ−1dΣ)d\log\det\Sigma = \operatorname{tr}(\Sigma^{-1}d\Sigma) and dΣ−1=−Σ−1dΣ Σ−1d\Sigma^{-1} = -\Sigma^{-1}d\Sigma\,\Sigma^{-1} give Σ=S^\Sigma = \hat S (section 6).
    • "Jacobian of softmax? Gradient of softmax cross-entropy?" — diag⁡(s)−ss⊤\operatorname{diag}(s) - ss^\top; s−eys - e_y.
    • "How do you check a hand-written gradient?" — compare f(X+t dX)−f(X)f(X + t\,dX) - f(X) with ttr⁡(G⊤dX)t\operatorname{tr}(G^\top dX) for a random dXdX and two values of tt; the error must shrink by 4 when tt is halved (central differences shrink it faster).
    • "Why is log⁡det⁡\log\det concave on SPD matrices?" — the second differential is −tr⁡(X−1dX X−1dX)=−∥X−1/2 dX X−1/2∥F2≤0-\operatorname{tr}(X^{-1}dX\,X^{-1}dX) = -\|X^{-1/2}\,dX\,X^{-1/2}\|_F^2 \le 0 for symmetric dXdX.
    • "How do you solve x′=Axx' = Ax, and when does it decay?" — x(t)=eAtx0x(t) = e^{At}x_0; it decays for every x0x_0 exactly when all Re⁡λi(A)<0\operatorname{Re}\lambda_i(A) < 0.
    • "Why is K-FAC cheap to invert?" — (Aˉ⊗Gˉ)−1=Aˉ−1⊗Gˉ−1(\bar A\otimes\bar G)^{-1} = \bar A^{-1}\otimes\bar G^{-1}, applied as Gˉ−1(gradient) Aˉ−1\bar G^{-1}(\text{gradient})\,\bar A^{-1} by the vec rule.

    10. Practice #

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

    1. Gradients by the differential method: log⁡det⁡X\log\det X, a⊤X−1ba^\top X^{-1}b and tr⁡(X−1)\operatorname{tr}(X^{-1}), checked against finite differences.
    2. The softmax Jacobian diag⁡(s)−ss⊤\operatorname{diag}(s) - ss^\top and its O(n)O(n) VJP, and the cross-entropy gradient s−eys - e_y.
    3. Matrix functions Qf(Λ)Q⊤Qf(\Lambda)Q^\top for a symmetric matrix, checked against expm, sqrtm and logm.
    4. The matrix exponential by scaling and squaring with a Taylor polynomial, checked against scipy.linalg.expm; then a demo solves the two-cell heat equation with eAtx0e^{At}x_0.
    5. Solve the Sylvester equation AX+XB=CAX + XB = C with the vec rule and a Kronecker sum, and check it against scipy.linalg.solve_sylvester.

    11. Watch and read #

    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Open on YouTube · the player loads from youtube-nocookie.com when you press play