Gradient into the embeddings
# grade this stage (from your course directory)
npx theinference test --stage grad-into-embeddings --watchPropagate the upstream dScores back through the similarity matrix S = Q · Pᵀ to get gradients on the embeddings. This is the standard matmul backward — one line each for dQ and dP.
dQ[i] = Σ_j dScores[i][j] · P[j] # dQ = dScores · P
dP[j] = Σ_i dScores[i][j] · Q[i] # dP = dScoresᵀ · Q
What to build
Implement the sim-matrix backward
Implement runSimBackwardDQ(dScores, Q, P): given the upstream [B, B] dScores and the two embedding batches, return dQ — the [B, 32] gradient w.r.t. the query embeddings. (simBackward also returns dP; the grader checks dQ here.)
export function runSimBackwardDQ(
dScores: number[][], Q: number[][], P: number[][],
): number[][] {
// dQ[i] = Σ_j dScores[i][j] · P[j]
}
Adapter mode: dQ is compared to the reference with allclose, atol 1e-5, rtol 1e-4. The reference is itself cross-checked against central finite differences (ε = 1e-4) — so the fixture is a finite-diff-validated oracle, not a hand-wave.
This step stops at the embeddings. The next step pushes dQ/dP through L2-normalize and the MLP and into the table — and the L2-norm Jacobian is the part people get wrong, so we isolate it.