Train your own embedding model
The contrastive loss·25 min

The cosine-similarity matrix

0 / 1
# grade this stage (from your course directory)
npx theinference test --stage cosine-matrix --watch

Build the B×B similarity matrix S where S[i][j] = cosine(query_i, positive_j). Because both batches are already L2-normalized (you did that in encode), cosine is just the dot product — so the whole matrix is S = Q · Pᵀ.

S = Q @ Pᵀ (rows are unit-norm)
Q : [B × 32]   (query embeddings, ‖row‖ = 1)
P : [B × 32]   (positive embeddings, ‖row‖ = 1)

S[i][j] = Σ_k Q[i][k] · P[j][k]        →  S : [B × B]

What to build

Your task

Implement the similarity matrix

Implement runSimMatrix(Q, P): return the [B, B] matrix of dot products between every query row and every positive row. Since the rows are unit-norm, this dot product is the cosine — no extra normalization here.

tests/adapters.ts
export function runSimMatrix(Q: number[][], P: number[][]): number[][] {
  // S[i][j] = Σ_k Q[i][k] · P[j][k]
}
How it's graded

Adapter mode: your [B, B] matrix is compared to the reference with allclose, atol 1e-5, rtol 1e-4. The fixture ships two fixed, already-normalized batches.

Pᵀ, not P

S[i][j] pairs query i with positive j — you dot row i of Q against row j of P. The diagonal S[i][i] is each query against its own positive; that is the entry the loss wants to be largest.