Skip to content

SVD, Eckart-Young low rank, and least squares

ModuleM03.5 · build · Python · Pass 3 · 4 to 5 h
You buildpython/tinyllm/linalg/svd.py: svd(A) (reduced SVD by one-sided Jacobi rotations, singular values descending, a fixed sign rule), low_rank(A, r) (the best rank-rr approximation as balanced factors BCB C), and lstsq(A, b) (least squares by QR)
Contractcourse/contracts/py/tinyllm/linalg/svd.pyi
Testscourse/tests/M03.5/test_svd.py (what they check: section 4)
NeedsM03.3 qr_householder, which lstsq factors with (or --ref-deps). Reading: M03.4 (eigenvalues; singular values are their square roots for A⊤AA^\top A), S-M03a
Used bylater L2.3 PPMI-SVD word vectors, L6.6 LoRA’s PiSSA initialization, L7.6 the MLA conversion, C1 the scaling-law fit, M10.6 Muon (each joins the registry with its batch) · later: M09.3
MilestoneMS-P3 (the tokens-and-data gate)
Optional depthTrefethen and Bau, Numerical Linear Algebra, lectures 4, 5, 11, 31; Demmel and Veselic, “Jacobi’s method is more accurate than QR” (1992); Eckart and Young, “The approximation of one matrix by another of lower rank” (1936); Meng, Wang, and Zhang, “PiSSA” (2024)
  • Every real matrix is A=UΣV⊤A = U \Sigma V^\top: rotate, stretch along the axes by the singular values, rotate again. The singular values are the square roots of the eigenvalues of A⊤AA^\top A, and the largest is the most AA can stretch a unit vector (test_hand_example_svd, test_random_matrices_match_numpy).
  • One-sided Jacobi rotates pairs of columns until all are orthogonal; the test for “orthogonal enough” must be relative to the column lengths, or a matrix scaled by 10−1210^{-12} is never rotated (test_scale_invariance).
  • Keeping the rr largest singular triples gives the closest rank-rr matrix, and the error is exactly the first dropped singular value (Eckart-Young, test_eckart_young_error); splitting σ\sqrt{\sigma} into each factor makes them start at the same scale (test_low_rank_factors_are_balanced).
  • Least squares goes through QR, never A⊤AA^\top A: the normal equations square the condition number and lose half the digits (test_lstsq_ill_conditioned_beats_normal_equations).
Terminal window
ol start M03.5 # stubs python/tinyllm/linalg/svd.py into your repo
ol tests M03.5 # read the test catalog first: rung R0, you write no tests here
ol check M03.5 # exit code is the verdict
ol check M03.5 --ref-deps # only if your M03.3 is not passing yet
ol diff M03.5 # after passing: your code against the reference

Pass 3 starts treating text as numbers you can factor. L2.3 counts which words appear near which others in a matrix with tens of thousands of rows and asks for 64 numbers per word that keep most of its structure: that is the best rank-64 approximation of the matrix, and the singular value decomposition is the only tool that gives it with a proof of optimality. The same decomposition comes back three more times in the system: L6.6 starts a LoRA adapter on the top singular directions of a weight matrix, L7.6 compresses attention’s keys and values into a low-rank latent, and the capstone fits a scaling law log⁡L=a+blog⁡N\log L = a + b \log N to a handful of runs by least squares. You already have the pieces: orthogonal matrices and QR (M03.3) and eigenvalues (M03.4). This module puts them together.

SymbolMeaningType / shape
AAthe matrix, mm rows and nn columnsfloat64[m, n]
k=min⁡(m,n)k = \min(m, n)number of singular valuesint
UUleft singular vectors, orthonormal columns (U⊤U=IU^\top U = I)float64[m, k]
σ1≥⋯≥σk≥0\sigma_1 \ge \dots \ge \sigma_k \ge 0singular values; Σ=diag⁡(σ)\Sigma = \operatorname{diag}(\sigma)float64[k]
VVright singular vectors, orthonormal columns; the contract returns V⊤V^\topfloat64[n, k]
ui,viu_i, v_icolumn ii of UU and of VVvectors
∥x∥\lVert x \rVertEuclidean length x⊤x\sqrt{x^\top x}scalar
∥A∥2=max⁡∥x∥=1∥Ax∥\lVert A \rVert_2 = \max_{\lVert x \rVert = 1} \lVert Ax \rVertspectral normscalar
∥A∥F=∑ijAij2\lVert A \rVert_F = \sqrt{\sum_{ij} A_{ij}^2}Frobenius normscalar
ArA_rthe truncated SVD ∑i≤rσiuivi⊤\sum_{i \le r} \sigma_i u_i v_i^\topfloat64[m, n]
wi,wjw_i, w_jtwo columns of the working matrix WWvectors
α,β,γ\alpha, \beta, \gammawi⊤wiw_i^\top w_i, wj⊤wjw_j^\top w_j, wi⊤wjw_i^\top w_jscalars
ζ,t,c,s\zeta, t, c, srotation parameters: t=tan⁡θt = \tan\theta, c=cos⁡θc = \cos\theta, s=sin⁡θs = \sin\thetascalars
ε\varepsilonfloat64 machine epsilon, 2−52≈2.2×10−162^{-52} \approx 2.2 \times 10^{-16}
κ(A)=σ1/σk\kappa(A) = \sigma_1 / \sigma_kcondition numberscalar
Q,RQ, Rthe QR factors of M03.3float64[m, k], float64[k, n]

Every real m×nm \times n matrix factors as

A=UΣV⊤=∑i=1kσi uivi⊤,A = U \Sigma V^\top = \sum_{i=1}^{k} \sigma_i\, u_i v_i^\top ,

with orthonormal columns in UU and VV and non-negative σi\sigma_i in descending order. Read it right to left on a vector xx: V⊤xV^\top x takes the coordinates of xx along the directions viv_i, Σ\Sigma stretches coordinate ii by σi\sigma_i, and UU sends the result out along the directions uiu_i. So Avi=σiuiA v_i = \sigma_i u_i: the unit sphere goes to an ellipsoid whose semi-axes are σiui\sigma_i u_i. This is the reduced SVD (UU is m×km \times k); the full one pads UU to a square matrix, which nothing in the course needs.

Each pair (ui,vi)(u_i, v_i) can be negated together without changing AA ((−ui)σi(−vi)⊤=uiσivi⊤(-u_i)\sigma_i(-v_i)^\top = u_i \sigma_i v_i^\top). When the singular values are distinct this is the only freedom, so the contract fixes it with a sign rule: the entry of largest absolute value in each row of V⊤V^\top is positive (the first such entry on ties). With it, your factors and LAPACK’s (after the same rule) agree entry by entry.

2.2 Singular values, eigenvalues, and norms

Section titled “2.2 Singular values, eigenvalues, and norms”

Multiply out A⊤A=VΣ⊤U⊤UΣV⊤=VΣ2V⊤A^\top A = V \Sigma^\top U^\top U \Sigma V^\top = V \Sigma^2 V^\top: the viv_i are eigenvectors of the symmetric matrix A⊤AA^\top A with eigenvalues σi2\sigma_i^2 (S-M03b q8 asks you to prove it). So everything M03.4 said about eigenvalues applies, with two differences: singular values exist for every matrix, square or not, and they are never negative or complex.

Two norms come straight from them. For a unit xx with coordinates c=V⊤xc = V^\top x, ∥Ax∥2=∑iσi2ci2≤σ12\lVert Ax \rVert^2 = \sum_i \sigma_i^2 c_i^2 \le \sigma_1^2, with equality at x=v1x = v_1: the spectral norm is ∥A∥2=σ1\lVert A \rVert_2 = \sigma_1. Orthogonal factors do not change the sum of squares of entries, so the Frobenius norm is ∥A∥F=∑iσi2\lVert A \rVert_F = \sqrt{\sum_i \sigma_i^2}.

Forming A⊤AA^\top A and calling an eigensolver would compute the SVD, but badly: squaring the singular values squares the condition number, and a σi\sigma_i below ε σ1≈10−8σ1\sqrt{\varepsilon}\,\sigma_1 \approx 10^{-8}\sigma_1 drowns in the rounding of σ12\sigma_1^2. Jacobi works on AA itself.

Work on a tall matrix (m≥nm \ge n; for a wide one, decompose A⊤A^\top and swap UU and VV at the end). Start with W=AW = A and V=IV = I, and keep the invariant W=AVW = AV with VV orthogonal. If all columns of WW were mutually orthogonal, their lengths would be the singular values and their directions the uiu_i: W=UΣW = U\Sigma, so A=UΣV⊤A = U \Sigma V^\top.

To make columns ii and jj orthogonal, rotate them in their plane:

wi←c wi−s wj,wj←s wi+c wj,w_i \leftarrow c\, w_i - s\, w_j, \qquad w_j \leftarrow s\, w_i + c\, w_j ,

and apply the same rotation to columns ii and jj of VV (so W=AVW = AV still holds; the rotation is orthogonal, so VV stays orthogonal). The new inner product is cs(α−β)+(c2−s2)γcs(\alpha - \beta) + (c^2 - s^2)\gamma. Setting it to zero with t=s/ct = s/c gives t2+2ζt−1=0t^2 + 2\zeta t - 1 = 0, ζ=(β−α)/(2γ)\zeta = (\beta - \alpha) / (2\gamma). Take the smaller root,

t=sign⁡(ζ)∣ζ∣+1+ζ2,c=11+t2,s=c t,t = \frac{\operatorname{sign}(\zeta)}{\lvert \zeta \rvert + \sqrt{1 + \zeta^2}}, \qquad c = \frac{1}{\sqrt{1 + t^2}}, \qquad s = c\,t ,

with sign⁡(0)=+1\operatorname{sign}(0) = +1: it is the rotation by at most 45°45°, the one that converges. A sweep visits every pair i<ji < j once. A rotation for one pair can spoil an earlier pair a little, but the off-diagonal mass shrinks every sweep and, near the end, quadratically; random 30×1230 \times 12 matrices finish in 7 or 8 sweeps, the last one only confirming that nothing needs rotating.

When is a pair “orthogonal enough”? The test must be relative: rotate only when ∣γ∣>mεαβ\lvert \gamma \rvert > m \varepsilon \sqrt{\alpha \beta}, that is when the cosine of the angle between the columns exceeds mεm\varepsilon. A fixed threshold such as ∣γ∣>10−15\lvert\gamma\rvert > 10^{-15} skips every rotation on a matrix scaled by 10−1210^{-12} (all inner products are about 10−2410^{-24}) and returns garbage; scaled by 101210^{12} it never stops. Stop after the first sweep that rotates nothing (the contract caps sweeps at 64).

Finally σj=∥wj∥\sigma_j = \lVert w_j \rVert, sorted descending (stable), uj=wj/σju_j = w_j / \sigma_j, and the sign rule.

If AA has rank ρ<k\rho < k, then k−ρk - \rho singular values are zero and their columns wjw_j are (numerically) zero: wj/σjw_j / \sigma_j is 0/00/0. The contract treats σj≤mεσ1\sigma_j \le m \varepsilon \sigma_1 as zero and fills uju_j with a unit vector orthogonal to the uu‘s already chosen: take the first standard basis vector eie_i that has more than half its length left after projecting out the previous columns (e←e−U(U⊤e)e \leftarrow e - U(U^\top e), done twice for accuracy), and normalize it. Any such completion is a valid SVD, because it multiplies a zero singular value; the contract fixes this one so the result is deterministic.

The truncated SVD Ar=∑i≤rσiuivi⊤A_r = \sum_{i \le r} \sigma_i u_i v_i^\top has rank rr, and A−Ar=∑i>rσiuivi⊤A - A_r = \sum_{i > r} \sigma_i u_i v_i^\top is itself an SVD, so

∥A−Ar∥2=σr+1,∥A−Ar∥F=∑i>rσi2.\lVert A - A_r \rVert_2 = \sigma_{r+1}, \qquad \lVert A - A_r \rVert_F = \sqrt{\textstyle\sum_{i > r} \sigma_i^2} .

The Eckart-Young theorem says no matrix of rank at most rr does better in either norm: the truncated SVD is the best low-rank approximation. That is why it is the right way to compress a co-occurrence matrix (L2.3) or initialize a low-rank adapter (L6.6).

low_rank returns ArA_r as two factors BB (m×rm \times r) and CC (r×nr \times n) with BC=ArBC = A_r. Many splits multiply to the same product; the contract uses the balanced one,

B=UrΣr1/2,C=Σr1/2Vr⊤,B = U_r \Sigma_r^{1/2}, \qquad C = \Sigma_r^{1/2} V_r^\top ,

so B⊤B=CC⊤=ΣrB^\top B = C C^\top = \Sigma_r: both factors carry the same scale. PiSSA initializes LoRA this way because gradient descent on BCBC updates each factor in proportion to the other’s size; a split such as (UrΣr,Vr⊤)(U_r\Sigma_r, V_r^\top) trains one factor much faster than the other.

For a tall AA with independent columns, Ax=bAx = b usually has no solution; least squares picks xx minimizing ∥Ax−b∥\lVert Ax - b \rVert. The minimizer makes the residual r=b−Axr = b - Ax orthogonal to every column of AA: A⊤r=0A^\top r = 0, the normal equations A⊤Ax=A⊤bA^\top A x = A^\top b. Solving them as written is the classic mistake. κ(A⊤A)=κ(A)2\kappa(A^\top A) = \kappa(A)^2, so a polynomial fit with κ(A)≈3.5×106\kappa(A) \approx 3.5 \times 10^6 (degree 9 at 40 points of [0,1][0, 1]) recovers its coefficients only to about 10−310^{-3}.

QR avoids the square. With A=QRA = QR (M03.3), QQ has orthonormal columns, so the residual splits into a part inside the column space and a part orthogonal to it, and only the first depends on xx:

∥Ax−b∥2=∥Rx−Q⊤b∥2+∥(I−QQ⊤)b∥2.\lVert Ax - b \rVert^2 = \lVert Rx - Q^\top b \rVert^2 + \lVert (I - QQ^\top) b \rVert^2 .

So xx solves the triangular system Rx=Q⊤bR x = Q^\top b, by back substitution from the last row up: xi=(zi−∑j>iRijxj)/Riix_i = (z_i - \sum_{j > i} R_{ij} x_j) / R_{ii} with z=Q⊤bz = Q^\top b. The error now grows like κ(A)ε\kappa(A)\varepsilon: 5×10−105 \times 10^{-10} for the same fit. If some ∣Rii∣\lvert R_{ii} \rvert is tiny (≤max⁡(m,n) εmax⁡j∣Rjj∣\le \max(m, n)\,\varepsilon \max_j \lvert R_{jj} \rvert), the columns are dependent, the minimizer is not unique, and the contract raises instead of dividing by almost zero.

A=(3045).A = \begin{pmatrix} 3 & 0 \\ 4 & 5 \end{pmatrix}.

Jacobi. One pair, (0,1)(0, 1).

QuantityValue
α=∥w0∥2\alpha = \lVert w_0 \rVert^2, β=∥w1∥2\beta = \lVert w_1 \rVert^2, γ=w0⊤w1\gamma = w_0^\top w_19+16=259 + 16 = 25, 0+25=250 + 25 = 25, 0+20=200 + 20 = 20
ζ=(β−α)/(2γ)\zeta = (\beta - \alpha)/(2\gamma)00, so t=1t = 1, c=s=1/2c = s = 1/\sqrt{2} (a 45°45° rotation)
new w0=c w0−s w1w_0 = c\,w_0 - s\,w_1(3−0, 4−5)/2=(3,−1)/2(3 - 0,\ 4 - 5)/\sqrt{2} = (3, -1)/\sqrt{2}, length 5\sqrt{5}
new w1=s w0+c w1w_1 = s\,w_0 + c\,w_1(3+0, 4+5)/2=(3,9)/2(3 + 0,\ 4 + 5)/\sqrt{2} = (3, 9)/\sqrt{2}, length 45=35\sqrt{45} = 3\sqrt{5}
new VV columnsv0=(1,−1)/2v_0 = (1, -1)/\sqrt{2}, v1=(1,1)/2v_1 = (1, 1)/\sqrt{2}
second sweepw0⊤w1=(9−9)/2=0w_0^\top w_1 = (9 - 9)/2 = 0: nothing to rotate, stop

Sorting puts σ1=35≈6.708\sigma_1 = 3\sqrt{5} \approx 6.708 first, then σ2=5≈2.236\sigma_2 = \sqrt{5} \approx 2.236; check: σ12+σ22=50=9+16+25=∥A∥F2\sigma_1^2 + \sigma_2^2 = 50 = 9 + 16 + 25 = \lVert A \rVert_F^2, and σ1σ2=15=∣det⁡A∣\sigma_1\sigma_2 = 15 = \lvert \det A \rvert. Dividing each ww by its length:

U=110(133−1),Σ=(35005),V⊤=12(111−1).U = \frac{1}{\sqrt{10}}\begin{pmatrix} 1 & 3 \\ 3 & -1 \end{pmatrix}, \quad \Sigma = \begin{pmatrix} 3\sqrt{5} & 0 \\ 0 & \sqrt{5} \end{pmatrix}, \quad V^\top = \frac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix}.

The sign rule holds already: each row of V⊤V^\top has its largest entries tied, and the first is positive. This is test_hand_example_svd.

Rank 1. A1=σ1u1v1⊤=35⋅120(1133)=(1.51.54.54.5)A_1 = \sigma_1 u_1 v_1^\top = 3\sqrt{5} \cdot \frac{1}{\sqrt{20}} \begin{pmatrix} 1 & 1 \\ 3 & 3 \end{pmatrix} = \begin{pmatrix} 1.5 & 1.5 \\ 4.5 & 4.5 \end{pmatrix}. The error A−A1=(1.5−1.5−0.50.5)=σ2u2v2⊤A - A_1 = \begin{pmatrix} 1.5 & -1.5 \\ -0.5 & 0.5 \end{pmatrix} = \sigma_2 u_2 v_2^\top has spectral and Frobenius norm 2.25+2.25+0.25+0.25=5=σ2\sqrt{2.25 + 2.25 + 0.25 + 0.25} = \sqrt{5} = \sigma_2, as Eckart-Young says. The balanced factors are B=451/4(1,3)⊤/10B = 45^{1/4}(1, 3)^\top/\sqrt{10} and C=451/4(1,1)/2C = 45^{1/4}(1, 1)/\sqrt{2}. This is test_hand_example_rank_one.

Least squares. Fit a line x0+x1tx_0 + x_1 t through (0,1)(0, 1), (1,2)(1, 2), (2,2)(2, 2): A=(101112)A = \begin{pmatrix} 1 & 0 \\ 1 & 1 \\ 1 & 2 \end{pmatrix}, b=(1,2,2)b = (1, 2, 2). By hand, the residual condition A⊤(b−Ax)=0A^\top(b - Ax) = 0 reads 3x0+3x1=53x_0 + 3x_1 = 5 and 3x0+5x1=63x_0 + 5x_1 = 6, so x1=1/2x_1 = 1/2 and x0=7/6x_0 = 7/6. Check: the residual b−Ax=(1−7/6, 2−5/3, 2−13/6)=(−1/6,1/3,−1/6)b - Ax = (1 - 7/6,\ 2 - 5/3,\ 2 - 13/6) = (-1/6, 1/3, -1/6) sums to 0 (orthogonal to the first column) and 0⋅(−1/6)+1⋅(1/3)+2⋅(−1/6)=00 \cdot (-1/6) + 1 \cdot (1/3) + 2 \cdot (-1/6) = 0 (orthogonal to the second). Writing out the normal equations is fine for a 2×22 \times 2 system on paper with exact fractions; your code must still go through QR, because in floating point the square of the condition number is what hurts. This is test_hand_example_lstsq.

def svd(A: ArrayLike) -> tuple[NDArray, NDArray, NDArray]:
"""Reduced SVD by one-sided Jacobi: U [m, k], S [k] descending, Vt [k, n],
A == U @ diag(S) @ Vt, the largest |entry| of each Vt row positive."""
def low_rank(A: ArrayLike, r: int) -> tuple[NDArray, NDArray]:
"""B = U_r sqrt(S_r) [m, r], C = sqrt(S_r) Vt_r [r, n]: B @ C is the best rank-r approximation."""
def lstsq(A: ArrayLike, b: ArrayLike) -> NDArray:
"""argmin ||A x - b|| for a tall, full-column-rank A, by QR and back substitution."""
TestKINDChecksWhy it matters downstream
test_hand_example_svdunit, smokesection 3’s UU, Σ\Sigma, V⊤V^\top to 10−1410^{-14}you and the tests agree on the rotation, the order, and the sign rule
test_hand_example_rank_oneunit, smokeA1A_1, the balanced factors, and the error 5\sqrt{5}the numbers of section 3
test_hand_example_lstsqunit, smokex=(7/6,1/2)x = (7/6, 1/2)the line fit of section 3
test_random_matrices_match_numpydifferentialtall, wide, square, and 1×n1 \times n shapes against np.linalg.svd with the same sign rule, entry by entrythe decomposition is the unique one
test_scale_invariancepropertysvd(c A) is cc times svd(A) for c=10−12,1012c = 10^{-12}, 10^{12}the rotation test is relative
test_rank_deficient_completes_uboundarya rank-2 matrix and the zero matrix: UU orthonormal, no NaNco-occurrence matrices are often rank deficient
test_eckart_young_errorpropertyspectral and Frobenius error of low_rank for r=1,2,4,6r = 1, 2, 4, 6the optimality L2.3 and L6.6 rely on
test_low_rank_factors_are_balancedpropertyB⊤B=CC⊤=ΣrB^\top B = C C^\top = \Sigma_rPiSSA’s equal-scale factors
test_low_rank_rejects_bad_rankboundaryr=0r = 0, r<0r < 0, r>min⁡(m,n)r > \min(m, n) raiseconfig errors fail early
test_lstsq_matches_numpydifferentialone and three right-hand sides against np.linalg.lstsqthe minimizer is unique
test_lstsq_residual_is_orthogonalpropertyA⊤(b−Ax)=0A^\top (b - Ax) = 0the defining property
test_lstsq_ill_conditioned_beats_normal_equationsboundarya degree-9 polynomial fit (κ≈3.5×106\kappa \approx 3.5 \times 10^6) recovers its coefficients to 10−610^{-6}the capstone’s scaling-law fit
test_lstsq_rejects_rank_deficient_and_wideboundarydependent columns, a wide system, and a wrong-length bb raiseno division by a zero pivot
test_inputs_not_modified_and_validatedboundaryAA is unchanged; 1-D and NaN inputs raiseL6.6 keeps the weight it decomposes
PitfallSymptomCaught by
1. solving the normal equations A⊤Ax=A⊤bA^\top A x = A^\top bfine on easy fits, three correct digits on a degree-9 polynomial fittest_lstsq_ill_conditioned_beats_normal_equations (mutant s01)
2. leaving singular values in sweep orderlow_rank keeps the wrong directions; the error is not σr+1\sigma_{r+1}test_hand_example_svd, test_eckart_young_error (mutant s02)
3. the rotation with the wrong sign (s=−cts = -ct)the columns never become orthogonal; 64 sweeps of noisetest_random_matrices_match_numpy (mutant s03)
4. stopping after one sweepthe columns are far from orthogonal: singular values of a random 30×1230 \times 12 matrix off by 0.2 to 0.8test_random_matrices_match_numpy (mutant s04)
5. the unbalanced split (UrΣr,Vr⊤)(U_r\Sigma_r, V_r^\top)same product, factors at different scalestest_low_rank_factors_are_balanced, test_hand_example_rank_one (mutant s06)
6. dividing a zero column by its zero lengthNaN in UU for rank-deficient matricestest_rank_deficient_completes_u (mutant s07)
7. no sign rulefactors disagree with LAPACK’s on some columns; downstream tests that fix a seed see flipped vectorstest_random_matrices_match_numpy (mutant s11)
8. an absolute threshold ∣γ∣>10−15\lvert\gamma\rvert > 10^{-15}tiny-scaled matrices are returned unrotatedtest_scale_invariance (mutant s12)
decomposing a wide matrix without transposingshapes come out wrongtest_random_matrices_match_numpy (mutant s05)
back substitution from the top rowwrong xx whenever RR is not diagonaltest_hand_example_lstsq, test_lstsq_matches_numpy (mutant s08)
no rank check in lstsqdivision by a zero diagonal entry of RR: inftest_lstsq_rejects_rank_deficient_and_wide (mutant s09)
rotating the caller’s array in placethe weight you decomposed is now UΣU\Sigmatest_inputs_not_modified_and_validated (mutant s10)

| Forward | M09.3 | Registered call site uses this module. |

DirectionModuleHow it uses this
BackM03.3lstsq factors A=QRA = QR with qr_householder and solves Rx=Q⊤bRx = Q^\top b
BackM03.4singular values are the square roots of the eigenvalues of A⊤AA^\top A (reading)
ForwardL2.3ppmi_svd_embeddings keeps the top singular directions of the PPMI matrix as word vectors
ForwardL6.6PiSSA initializes LoRA with low_rank(W, r) and trains the residual
ForwardL7.6mha_to_mla compresses the key and value projections to a rank-rr latent
ForwardC1the scaling-law fit is lstsq on log⁡\log loss against log⁡\log parameters
ForwardM10.6Muon’s Newton-Schulz iteration approximates UV⊤UV^\top, checked against this SVD

If you skip this module, the modules above stop with BLOCKED ... needs M03.5 once they land: build it, or pass --ref-deps.

Your pieceProduction equivalentWhat it addsWhere to look
one-sided JacobiLAPACK dgesvj (Drmac and Veselic)preconditioning by QR with column pivoting, de Rijk’s pivoting, blocked rotations; Jacobi’s high relative accuracy at near-LAPACK speedLAPACK SRC/dgesvj.f, dgejsv.f
svdLAPACK dgesdd (what numpy calls)Householder bidiagonalization, then divide and conquer on the bidiagonal matrix; much faster for large matricesnumpy/linalg/_linalg.py, LAPACK SRC/dgesdd.f
low_rankrandomized SVD (Halko, Martinsson, Tropp 2011)the top rr triples of a huge sparse matrix from a few matrix products with random vectorssklearn.utils.extmath.randomized_svd
lstsqnumpy.linalg.lstsq (LAPACK dgelsd)the minimum-norm solution for rank-deficient AA, via the SVDnumpy/linalg/_linalg.py
low_rank for LoRAPEFT’s PiSSA initializerthe same balanced split on GPU, then the residual W−BCW - BC frozen as the base weightpeft/tuners/lora/layer.py (pissa_init)