Skip to content

Commit 0c8ecd6

Browse files
authored
Add the Transformer foundations course milestone
Add four tested interactive Transformer foundation lessons, exercises, course integration, and browser coverage for the approved AI from First Principles v0.2 roadmap.
1 parent 547660e commit 0c8ecd6

42 files changed

Lines changed: 2188 additions & 28 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ database, analytics, hosted executor, or LMS.
99

1010
The reference course is
1111
[AI from First Principles](examples/ai-from-first-principles). Its current
12-
eight-lesson foundation covers gradient descent, backpropagation, linear
13-
layers, losses and optimisers, BPE, self-attention, sampling, and evaluation
14-
leakage.
12+
twelve-lesson foundation builds from gradients and linear layers through
13+
embeddings, multi-head attention, a Transformer block, next-token training,
14+
sampling, and evaluation.
1515

1616
## Quick start
1717

apps/site/src/main.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ function App() {
125125

126126
<section className="section course-feature" id="course">
127127
<div>
128-
<p className="kicker">Course 001 · eight foundation lessons</p>
128+
<p className="kicker">Course 001 · twelve foundation lessons</p>
129129
<h2>
130130
AI from
131131
<br />
@@ -149,9 +149,13 @@ function App() {
149149
["03", "Linear layers", "Vectors, matrices, shapes, and projections"],
150150
["04", "Losses and optimisers", "Cross-entropy and parameter updates"],
151151
["05", "BPE tokenisation", "Pair counts and merge rules"],
152-
["06", "Self-attention", "Queries, keys, masks, and weights"],
153-
["07", "Sampling", "Temperature, top-k, and top-p"],
154-
["08", "Evaluation leakage", "Splits, contamination, and trust"],
152+
["06", "Embeddings and position", "Lookup tables and RoPE intuition"],
153+
["07", "Self-attention", "Queries, keys, masks, and weights"],
154+
["08", "Multi-head attention", "Independent mixtures and projections"],
155+
["09", "Transformer block", "RMSNorm, residuals, and SwiGLU"],
156+
["10", "Next-token training", "Shifted targets, loss, and updates"],
157+
["12", "Sampling", "Temperature, top-k, and top-p"],
158+
["13", "Evaluation leakage", "Splits, contamination, and trust"],
155159
].map(([number, title, detail]) => (
156160
<li key={number}>
157161
<span>{number}</span>

docs/implementation-status.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,17 @@ Updated: 28 July 2026
2828
- Implemented the first v0.2 foundation milestone: shape-aware linear
2929
projections, stable cross-entropy, clipping, SGD, momentum, AdamW, two
3030
interactive lessons, and two protected exercises.
31+
- Implemented the second v0.2 foundation increment: embedding lookup,
32+
RoPE-style positional rotation, causal multi-head attention, a pre-norm
33+
residual/SwiGLU block, next-token target alignment, four interactive lessons,
34+
and four protected exercises.
3135

3236
## Current work
3337

34-
The v0.1 runtime MVP remains verified. The first `AI from First Principles`
35-
v0.2 expansion milestone is implemented and verified. The next course
36-
milestone adds embeddings, positional information, multi-head attention, the
37-
complete Transformer block, and next-token training. External DNS and
38-
learner-study evidence also remain.
38+
The v0.1 runtime MVP remains verified. The first two `AI from First Principles`
39+
v0.2 implementation increments are complete. The next course increment adds
40+
autoregressive inference, KV caching, and the small Transformer capstone.
41+
External DNS and learner-study evidence also remain.
3942

4043
## Decisions
4144

@@ -70,14 +73,11 @@ npm view typescript@7 version 7.0.2
7073
npm view pnpm@11 version latest 11.15.1
7174
official Node release status Node 24 is LTS
7275
official Claude Desktop preview docs launch schema version 0.0.1 confirmed
73-
pnpm check pass (format, lint, TS7, 22 unit tests, validators)
74-
pnpm course:test pass (6 model suites; each starter fails/reference passes)
76+
pnpm check pass (format, lint, TS7, 18 suites/51 tests, validators)
77+
pnpm course:test pass (12 model suites/38 tests; 12 starter/reference pairs)
7578
pnpm build pass (9 package entries, both courses, static site)
76-
pnpm test:browser pass (sandbox interaction, navigation, axe, 720px)
79+
pnpm test:browser pass (4 tests; sandbox, Transformer failures, axe, 720px)
7780
pnpm site:test pass (content, no-tracking copy, axe, 720px)
78-
foundation model tests pass (8 suites, 21 tests)
79-
foundation exercise verification pass (8 starters fail, 8 references pass)
80-
foundation browser tests pass (3 tests, new failure and training states)
8181
generated course smoke pass (validate, 2 tests, static build)
8282
pnpm audit --audit-level high pass (no known vulnerabilities)
8383
Pages workflow YAML parse pass

examples/ai-from-first-principles/COURSE.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
id: ai-from-first-principles
33
title: AI from First Principles
4-
version: 0.2.0-foundations.1
4+
version: 0.2.0-transformer.1
55
summary: See, implement, and debug the foundations behind modern language models.
66
license: CC-BY-4.0
77
audience:
@@ -10,7 +10,7 @@ audience:
1010
prerequisites:
1111
- basic TypeScript
1212
- algebra and arrays
13-
estimatedHours: 8
13+
estimatedHours: 12
1414
repository: https://github.com/Doppp/explorables
1515
language: en
1616
tags:
@@ -30,6 +30,10 @@ tests. The coding agent is your tutor and debugger, not your substitute.
3030
3. [Vectors, matrices, and linear layers](lessons/03-vectors-matrices-linear-layers.md)
3131
4. [Losses and optimisers](lessons/04-losses-optimisers.md)
3232
5. [BPE tokenisation](lessons/05-bpe-tokenisation.md)
33-
6. [Self-attention](lessons/07-self-attention.md)
34-
7. [Sampling and generation](lessons/12-sampling.md)
35-
8. [Evaluation leakage](lessons/13-evaluation-leakage.md)
33+
6. [Embeddings and positional information](lessons/06-embeddings-positional-information.md)
34+
7. [Self-attention](lessons/07-self-attention.md)
35+
8. [Multi-head attention](lessons/08-multi-head-attention.md)
36+
9. [The Transformer block](lessons/09-transformer-block.md)
37+
10. [Next-token training](lessons/10-next-token-training.md)
38+
11. [Sampling and generation](lessons/12-sampling.md)
39+
12. [Evaluation leakage](lessons/13-evaluation-leakage.md)

examples/ai-from-first-principles/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# AI from First Principles
22

33
An expanding interactive course for software developers who use AI tools and
4-
want to understand the machinery underneath them. The current foundation
5-
milestone contains eight lessons, including a linear classifier built from
6-
matrix multiplication, cross-entropy, and optimiser updates.
4+
want to understand the machinery underneath them. The current twelve-lesson
5+
course builds from scalar gradients through embeddings, multi-head attention,
6+
a complete Transformer block, and next-token training.
77

88
## Prerequisites
99

1010
Basic TypeScript, algebra, arrays, a terminal, Git, and the ability to read a
11-
test failure. The current course takes roughly eight hours.
11+
test failure. The current course takes roughly twelve hours.
1212

1313
## Start
1414

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Implement embedding lookup and pair rotation
2+
3+
Complete `embeddingLookup` and `rotatePairs`. Return independent copies of the
4+
selected embedding rows, then apply a position-dependent rotation to each
5+
coordinate pair. The starter returns aliases into the table and adds the
6+
position as a scalar offset.
7+
8+
After the tests pass, explain why rotation preserves vector norm and why actual
9+
RoPE applies these rotations to queries and keys.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"id": "embedding-lookup-rope",
3+
"title": "Implement embedding lookup and pair rotation",
4+
"language": "typescript",
5+
"starter": "starter",
6+
"testCommand": "pnpm exec vitest run exercises/embeddings-position/tests --config vitest.exercise.config.ts",
7+
"estimatedMinutes": 40,
8+
"centralFiles": ["starter/embeddings.ts"],
9+
"protectedPaths": ["solution"]
10+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
function assertFinite(values: number[], name: string): void {
2+
if (values.some((value) => !Number.isFinite(value))) {
3+
throw new RangeError(`${name} must contain only finite values`);
4+
}
5+
}
6+
7+
export function embeddingLookup(table: number[][], tokenIds: number[]): number[][] {
8+
if (table.length === 0) throw new RangeError("table must not be empty");
9+
const width = table[0]?.length ?? 0;
10+
if (
11+
width === 0 ||
12+
table.some((row) => row.length !== width) ||
13+
table.some((row) => row.some((value) => !Number.isFinite(value)))
14+
) {
15+
throw new RangeError("table must be rectangular and finite");
16+
}
17+
return tokenIds.map((tokenId) => {
18+
if (!Number.isInteger(tokenId) || tokenId < 0 || tokenId >= table.length) {
19+
throw new RangeError("token id is outside the table");
20+
}
21+
return [...(table[tokenId] ?? [])];
22+
});
23+
}
24+
25+
export function rotatePairs(
26+
vector: number[],
27+
position: number,
28+
base = 10_000,
29+
): number[] {
30+
if (vector.length === 0 || vector.length % 2 !== 0) {
31+
throw new RangeError("vector width must be positive and even");
32+
}
33+
if (!Number.isInteger(position) || position < 0) {
34+
throw new RangeError("position must be a non-negative integer");
35+
}
36+
if (!(base > 1) || !Number.isFinite(base)) {
37+
throw new RangeError("base must be finite and greater than one");
38+
}
39+
assertFinite(vector, "vector");
40+
const result: number[] = [];
41+
for (let offset = 0; offset < vector.length; offset += 2) {
42+
const pair = offset / 2;
43+
const angle = position * base ** (-(2 * pair) / vector.length);
44+
const cosine = Math.cos(angle);
45+
const sine = Math.sin(angle);
46+
const first = vector[offset] ?? 0;
47+
const second = vector[offset + 1] ?? 0;
48+
result.push(first * cosine - second * sine, first * sine + second * cosine);
49+
}
50+
return result;
51+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function embeddingLookup(table: number[][], tokenIds: number[]): number[][] {
2+
return tokenIds.map((tokenId) => table[tokenId] ?? []);
3+
}
4+
5+
export function rotatePairs(vector: number[], position: number): number[] {
6+
return vector.map((value) => value + position);
7+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { describe, expect, it } from "vitest";
2+
3+
const { embeddingLookup, rotatePairs } = await import(
4+
process.env.EXPLORABLES_SOLUTION === "1"
5+
? "../solution/embeddings.ts"
6+
: "../starter/embeddings.ts"
7+
);
8+
9+
describe("embedding lookup and pair rotation", () => {
10+
it("looks up rows without aliasing the table", () => {
11+
const table = [
12+
[1, 0],
13+
[0, 1],
14+
];
15+
const result = embeddingLookup(table, [1, 0]);
16+
expect(result).toEqual([
17+
[0, 1],
18+
[1, 0],
19+
]);
20+
result[0]?.splice(0, 1, 9);
21+
expect(table[1]).toEqual([0, 1]);
22+
});
23+
24+
it("leaves position zero unchanged", () => {
25+
expect(rotatePairs([1, 2, 3, 4], 0)).toEqual([1, 2, 3, 4]);
26+
});
27+
28+
it("preserves norm while changing direction", () => {
29+
const vector = [1, 2, -0.5, 0.25];
30+
const rotated = rotatePairs(vector, 7);
31+
expect(rotated).not.toEqual(vector);
32+
expect(Math.hypot(...rotated)).toBeCloseTo(Math.hypot(...vector));
33+
});
34+
35+
it("validates identifiers and even vector width", () => {
36+
expect(() => embeddingLookup([[1, 0]], [1])).toThrow(/outside/i);
37+
expect(() => rotatePairs([1, 2, 3], 1)).toThrow(/even/i);
38+
});
39+
});

0 commit comments

Comments
 (0)