Cosine + L2-normalize
# grade this stage (from your course directory)
npx theinference test --stage cosine-and-l2norm --watchTwo primitives power everything downstream: L2-normalize (put a vector on the unit sphere) and cosine (their dot product, once normalized). Build both now — this is the first green, and it's the whole course in miniature: make vectors point the right way under cosine.
l2normalize(v) = v / ‖v‖ where ‖v‖ = √(Σ vᵢ²)
cosine(u, v) = (u · v) / (‖u‖ ‖v‖)
What to build
Implement cosine + l2normalize
Implement cosine(u, v) and l2normalize(v) in src/tensor.ts, then wire runCosine in tests/adapters.ts. The adapter receives a list of { u, v } pairs and returns one cosine per pair (a number[]).
Guard the zero vector: if a norm is 0, return the zero vector from l2normalize and 0 from cosine — never divide by zero.
export function runCosine(pairs: { u: number[]; v: number[] }[]): number[] {
return pairs.map((p) => cosine(p.u, p.v));
}
Adapter mode: runCosine is called on the fixture pairs and compared to the reference with allclose, atol 1e-6. One of the fixture pairs has a zero vector — your guard must return 0 there, not NaN.
Use Math.sqrt of the sum of squares (or Math.hypot). If you forget the zero-norm guard, the zero-vector pair divides by 0 and emits NaN, which fails the whole allclose check — even though every other entry is correct.