// SPDX-License-Identifier: Apache-2.0 ; specs/course/ai-numbers.t27 -- the t27 course on AI numbers: formats, ternary weights, a network in logic ; Source of truth for apps/website/scripts/course-from-spec.mjs (gHashTag/trinity), which reads ; it the way it reads specs/course/course.t27: the constant schema, every test block below, every ; widget in specs/widgets/gallery.t27, every lesson spec compiled clean, the Russian bundle named ; by specs/course/ai-numbers-ru.t27. It writes this course into src/lib/course.generated.ts (the ; page at #/ai-numbers) and a byte-identical copy of this file at public/learn/ai-numbers.t27. ; ASCII only (L3), English only (LANG-EN). The Russian words live in the bundle. ; WHAT THE COURSE IS: course 2 of specs/course/courses.t27, 27 lessons in 9 modules of 3. It ; starts where the FPGA course ends. Modules 1 to 4 store numbers: a format of our own and an ; honest scoreboard (lab), the OCP Microscaling v1.0 block formats AI chips use (mx), ternary ; weights (ternary) and the Ternary Network Float (tnf). Modules 5 to 9 make them compute: ; signed arithmetic, the parts of a neuron, a training step, BitNet ternary neurons, and the ; ternary multiply-accumulate as a circuit. Every lesson opens one widget and one spec. ; WHAT THE COURSE DOES NOT DO: it says no number of its own. The lab lessons quote only the rule ; their widget tests (990 thousandths) and counts that are arithmetic (3 x 3 x 3 = 27). The MX ; lessons quote the constants of the OCP MX v1.0 standard their widgets compile (a block of 32, ; element widths of 4, 6 and 8 bits, scale code 255 for NaN, bias 127). Every other lesson quotes ; only the constants of the spec it opens and the counts its player or recording prints. A ; recording runs t27c and Zig on a real machine, plants one bug as a one-line edit, shows the ; one test that fails, and puts the line back in the same recording. ; WHY 27: 3 cubed lessons, the size of a TRI-27 word. Changing the shape changes the tests. ; phi^2 + 1/phi^2 = 3 | TRINITY module ai_numbers; pub const KIND : str = "course"; pub const ID : str = "ai-numbers"; pub const SCHEMA_VERSION : u8 = 1; ; The derived files the generator rewrites (relative to apps/website). pub const GENERATED : [2]str = ["src/lib/course.generated.ts", "public/learn/ai-numbers.t27"]; pub const ROUTE : str = "ai-numbers"; ; Share pages: learn/ for course 1, learn// for the next (specs/course/courses.t27). pub const SHARE_PATH : str = "learn/ai-numbers/"; pub const GALLERY : str = "specs/widgets/gallery.t27"; pub const LOCALES : [2]str = ["en", "ru"]; pub const RU_CONTRACT : str = "specs/course/ai-numbers-ru.t27"; ; Lesson marks the reader sets are kept in this browser only, under this key, one key for ; every course. pub const PROGRESS_KEY : str = "t27-course-done"; pub const SENDS_NOTHING : bool = true; ; The paid FPGA training that used to live at #/course. pub const COHORT_ROUTE : str = "fpga-training"; ; A lesson's widget is the page's main stage, so it is drawn taller than a ; gallery card (specs/widgets/gallery.t27 EMBED_* heights are for embeds in posts). pub const TOOL_FRAME_HEIGHT : u16 = 640; pub const PLAYER_FRAME_HEIGHT : u16 = 480; ; --- Words on the page --------------------------------------------------------------------- pub const TITLE : str = "AI numbers with t27: from number formats to a ternary network"; pub const DESCRIPTION : str = "Course 2, 27 lessons, one widget and one t27 spec each: a number format of our own, the OCP MX block formats AI chips use, ternary weights and the Ternary Network Float, then signed arithmetic, the parts of a neuron, a training step, BitNet ternary neurons and the ternary MAC as logic. Every new lesson plants one bug and shows the test that catches it."; pub const SAY_KICKER : str = "Course"; pub const SAY_LEAD : str = "It picks up where the FPGA course ends. Every lesson opens a real tool or a recording of t27c and Zig on a real machine, next to a spec you can run in your browser."; pub const SAY_SHAPE : str = "9 modules of 3 lessons, 27 cells. A filled cell is a lesson you marked done."; pub const SAY_START : str = "Start lesson 1"; pub const SAY_CONTINUE : str = "Continue"; pub const SAY_MODULE : str = "Module {0}"; pub const SAY_LESSON : str = "Lesson {0} of {1}"; pub const SAY_GOAL : str = "You will learn"; pub const SAY_TRY : str = "Try it"; pub const SAY_ALSO : str = "Also try"; pub const SAY_ALSO_HINT : str = "Each one swaps the widget above."; pub const SAY_BACK_TO_MAIN : str = "Back to this lesson's widget"; pub const SAY_SPEC : str = "Open the lesson's spec in the player"; pub const SAY_OPEN_PAGE : str = "Open the widget on its own page"; pub const SAY_WIDGET_LANG : str = "Widgets keep their own English words: each one lives in its own t27 spec."; pub const SAY_PREV : str = "Previous"; pub const SAY_NEXT : str = "Next"; pub const SAY_ALL : str = "All lessons"; pub const SAY_MARK : str = "Mark as done"; pub const SAY_MARKED : str = "Done"; pub const SAY_PROGRESS : str = "{0} of {1} done"; pub const SAY_PRIVATE : str = "Progress stays in this browser; nothing is sent."; pub const SAY_SOURCE : str = "This course is itself a t27 spec: read it"; pub const SAY_COHORT : str = "Looking for the paid FPGA training? It moved to its own page."; pub const SAY_NOT_FOUND : str = "No lesson has this address."; pub const SAY_OPEN_LESSON : str = "Open the interactive lesson"; pub const SAY_OPEN_COURSE : str = "Open the interactive course"; pub const SAY_SEO_TITLE : str = "{0}: AI numbers course, lesson {1} of {2}"; pub const SAY_SHARE : str = "Link to share, with a preview card"; pub const SAY_NEXT_COURSE : str = "Next course"; pub const SAY_PREV_COURSE : str = "Previous course"; ; --- Modules ------------------------------------------------------------------------------- pub const LESSONS_PER_MODULE : u8 = 3; pub const MODULE_COUNT : u8 = 9; pub const MODULE_IDS : [9]str = ["lab", "mx", "ternary", "tnf", "arith", "neuron", "training", "bitnet", "mac"]; pub const MODULE_TITLES : [9]str = [ "Lab: our own research", "AI numbers: the MX block", "Ternary weights", "The Ternary Network Float", "Arithmetic on signed numbers", "Parts of a neuron", "Learning from a mistake", "BitNet: ternary networks", "The ternary MAC as a chip" ]; pub const MODULE_LINES : [9]str = [ "A number format of our own, an honest scoreboard, and a model's tables multiplied on the board.", "How AI chips keep weights in a few bits: one shared scale per block, the scale byte itself, and what one outlier does to its neighbours.", "Weights that are only minus, zero or plus a scale, the five rules a ternary alphabet must pass, and a test pass that checked nothing.", "A rule the compiler enforces before any test runs, and a 17-bit float whose exponent is four balanced trits.", "Multiply two signed numbers, add them when their signs differ, and do both at once in a multiply-accumulate.", "A ReLU that bends at zero, a power of two for softmax, and an argmax that names the answer.", "A loss that prices a wrong guess in bits, one step that moves a weight against its gradient, and the hidden layer that XOR needs.", "A threshold that squeezes a sum back to three values, one neuron that becomes a different function when its weights change, and a neuron that reads its inputs 27 trits at a time.", "The 27-trit dot product as wires with no register, the same sum added into a register on every clock, and a small whole network to close the course." ]; ; --- Lessons ------------------------------------------------------------------------------- ; LESSON_WIDGETS names the widget the lesson opens; every one is a gallery id, and no two ; lessons of any course open the same one. LESSON_ALSO lists more gallery ids, comma ; separated, shown as chips that swap the widget. LESSON_SPECS is the spec under ; public/t27/files/ the lesson opens in the player; every lesson names one. pub const LESSON_COUNT : u8 = 27; pub const LESSON_IDS : [27]str = [ "t27-number-format", "an-honest-scoreboard", "a-model-on-the-board", "one-scale-per-block", "scale-byte-native", "one-outlier", "phi-scaled-ternary", "a-pass-that-checked-nothing", "five-rules-for-weights", "blocked-is-not-failed", "four-trit-exponent", "negate-twice", "a-sign-is-one-bit", "opposite-signs-subtract", "multiply-then-add", "a-bend-at-zero", "two-to-the-x", "pick-the-largest", "a-loss-in-bits", "one-step-downhill", "xor-needs-a-bend", "back-to-three-values", "same-neuron-new-weights", "a-neuron-in-chunks", "a-dot-product-in-wires", "add-it-up-every-clock", "a-whole-network" ]; pub const LESSON_MODULES : [27]str = [ "lab", "lab", "lab", "mx", "mx", "mx", "ternary", "ternary", "ternary", "tnf", "tnf", "tnf", "arith", "arith", "arith", "neuron", "neuron", "neuron", "training", "training", "training", "bitnet", "bitnet", "bitnet", "mac", "mac", "mac" ]; pub const LESSON_WIDGETS : [27]str = [ "t27-cell", "t27-vs-nvfp4", "uart-bucket", "play-e8m0", "t27c-e8m0", "t27c-ocp-mx", "play-gfternary", "t27c-gfternary", "play-golden-sieve", "t27c-golden-sieve", "play-tnf17", "t27c-tnf17", "t27c-gft-smul", "t27c-gft-sadd", "t27c-gft-signed-mac", "t27c-gft-relu", "t27c-gft-exp2", "t27c-gft-argmax4", "t27c-gft-nll", "t27c-gft-sgd-step", "t27c-gft-xornet", "t27c-activation-quantizer", "t27c-bitnet-majority", "t27c-bitnet-neuron-nchunk", "t27c-comb-ternary-dot", "t27c-stream-ternary-mac", "t27c-bitnet-mlp" ]; pub const LESSON_ALSO : [27]str = [ "gf16-calc", "t27-cell", "x7-board", "", "", "t27-vs-nvfp4", "t27-cell", "tri-x7-mutate", "lut-explain", "", "gf16-calc", "t27c-ocp-mx", "", "", "", "", "", "", "", "", "", "", "", "", "cost-curves", "test-waves", "" ]; pub const LESSON_SPECS : [27]str = [ "specs/numeric/tnf8.t27", "specs/numeric/posit_ladder_control.t27", "specs/igla/race/unit_weights_node.t27", "specs/numeric/formats_catalog.t27", "specs/numeric/e8m0.t27", "specs/numeric/gf4.t27", "specs/numeric/gft4.t27", "specs/numeric/gfternary.t27", "specs/numeric/tnf4.t27", "specs/numeric/golden_sieve.t27", "specs/numeric/tnf16.t27", "specs/numeric/tnf17.t27", "specs/ternary/gft_smul.t27", "specs/ternary/gft_sadd.t27", "specs/ternary/gft_signed_mac.t27", "specs/ternary/gft_relu.t27", "specs/ternary/gft_exp2.t27", "specs/ternary/gft_argmax4.t27", "specs/ternary/gft_nll.t27", "specs/ternary/gft_sgd_step.t27", "specs/ternary/gft_xornet.t27", "specs/ternary/activation_quantizer.t27", "specs/ternary/bitnet_majority.t27", "specs/ternary/bitnet_neuron_nchunk.t27", "specs/ternary/comb_ternary_dot.t27", "specs/ternary/stream_ternary_mac.t27", "specs/ternary/bitnet_mlp.t27" ]; pub const LESSON_TITLES : [27]str = [ "T27: a number format of our own", "An honest scoreboard", "A model's tables on the board", "One scale for a block of weights", "The scale byte on a real machine", "One outlier, many zeros", "Weights of minus phi, zero and plus phi", "A pass that checked nothing", "Five rules for a weight alphabet", "Blocked is not failed", "A float whose exponent is four trits", "Negate twice, get the number back", "A sign is one bit", "Opposite signs subtract", "Multiply, then add", "A bend at zero", "Two to the x", "Pick the largest", "A loss in bits", "One step downhill", "XOR needs a bend", "Back to three values", "Same neuron, new weights", "A neuron in chunks", "A dot product made of wires", "Add it up on every clock", "A whole network" ]; pub const LESSON_GOALS : [27]str = [ "How three trits give more levels than a 4-bit float, and what that costs in plain bits.", "How T27 was compared with NVFP4, MX+ and MXFP4 on a real language model, and what the result does not show.", "How the host feeds the board over a serial bridge, why the window size matters, and what the board computed.", "Why AI formats share one scale across a block of weights, and how the scale byte of OCP MX is read.", "What the native compiler checks in the same spec, including what the browser skipped.", "What a shared scale costs: how one large weight pushes the small weights of its block to zero, format by format.", "How a ternary weight is stored in 2 bits, what the fourth code is for, and what the spec does not claim about phi.", "What a vacuous pass is, why a green report can hide one, and how a planted bug tells real checks from empty ones.", "What values a ternary weight may take, how five measured rules leave one formula, and what a sixth rule removes from it.", "Why a rule checked while compiling stops a spec before any test runs, and how that differs from a failing test.", "How TNF17e packs a sign, a ternary exponent and a mantissa into 17 bits, and why 17 and not 16.", "How a test of a property catches a one-character bug that a single example misses.", "How a signed multiply sets the sign of its product, and which test notices when it never does.", "How a signed add turns into a subtraction when the signs differ, and what its 3 tests leave unchecked.", "What a multiply-accumulate does with two signed products, and how the swap from lesson 12 breaks it.", "Why a ReLU needs a test with a negative input before anyone can say it bends.", "How exp2 splits x into a whole part and a fraction, and why its tests only reach the whole part.", "How argmax names a class by comparing bits alone, and which rule settles a tie.", "How the loss -log2(p) prices a guess in bits, and why a perfect guess must cost exactly 0.", "How one SGD step moves a weight against its gradient, and why a test with two equal inputs cannot tell them apart.", "Why a network needs a hidden ReLU layer to compute XOR, and what one missing bend does.", "How a threshold turns a neuron's sum back into a trit, and why each edge of the dead band needs its own test.", "How one ternary neuron computes a majority vote with one set of weights and a different function with another.", "How a neuron with more than 27 inputs adds up its dot products chunk by chunk, and why a test with zero chunks matters.", "What on_comb turns a spec into, and how one wrong sign in tmul shows up in exactly one test.", "What on_clock adds to the dot product, and why this spec's tests never reach the register.", "How the pieces of this course fit into one small network, and what its tests and its Verilog still leave open." ]; pub const LESSON_TEXTS : [27]str = [ "A 4-bit float such as E2M1, the element of NVFP4 and MXFP4, has 16 codes, and two of them are zero. Three balanced trits have 3 x 3 x 3 = 27 states. T27 is our format that uses all of them: 27 levels placed where a trained network's weights actually fall, and one shared scale per block of weights. On ternary storage that is more levels in the same room. Packed into plain binary bits it is larger, not smaller, and the widget shows both counts.", "A format is judged by what it does to a model. We stored the weights of one small language model in each format and measured perplexity on text it had not seen: lower is better. The rule was written before the run: T27 beats a rival when its perplexity is below 990 thousandths of the rival's. It beat NVFP4, MX+ and MXFP4, and it lost to E2M2, a wider format. The panel under the bars lists what this test cannot claim. The one claim that holds: T27 beats NVFP4 on a ternary substrate.", "The board does the arithmetic; the host sends it work over a USB serial bridge. The board's answers wait in the bridge chip's small receive buffer, and when the host keeps more requests in flight than their answers fit, answers are lost. A smaller window removed the loss and did not slow the run. Over that link the board multiplied every ternary weight matrix of a small language model, and every row matched the reference bit for bit. The text itself is generated on the Mac, which does everything except the ternary dot products: the board is the calculator, not the whole model.", "A language model has millions of weights, and a full float for each is expensive to store and to move. The Microscaling (MX) formats of the Open Compute Project keep each weight in 4, 6 or 8 bits and give every block of 32 weights one shared scale. That scale is a single byte called E8M0: no sign and no mantissa, only an exponent, so code e means 2 to the power e minus 127, and code 255 means NaN. The player compiles e8m0.t27, the spec of that byte, in your browser and runs its tests. Where the browser cannot run a check, it skips it and says why.", "The player in the last lesson could not run every check in e8m0.t27, and it said so. This recording runs the native t27c and Zig on a real machine on the same spec: its constants, the test report, the Zig tests, and the Verilog for the NaN check. Some checks are invariants the compiler proves while it compiles, so compiling is the check. Every byte in the recording was printed by the command; only the typing is staged.", "A shared scale is set by the largest value in its block, so one outlier sets it for all 32 weights. Weights much smaller than the outlier then fall below the smallest step the element can show and are stored as zero. This recording runs ocp_mx.t27, our spec of OCP MX v1.0, natively on a block of 31 small weights and one outlier, and counts how many each element format flushes to zero. Then it plants a bug, one changed exponent bias, and exactly one test fails: a spec that carries its tests catches its own mistakes.", "Ternary networks such as BitNet keep each weight as minus one, zero or plus one times a scale, so a multiply becomes an add, a subtract or nothing. gfternary.t27 fixes that scale at phi, about 1.618, and stores the three values in 2 bits: 00 is zero, 01 is plus phi, 10 is minus phi. Two bits have a fourth code, 11, and the spec folds it to zero instead of leaving it undefined. The spec also says what it does not claim: that a phi scale beats plain integer ternary weights is an open conjecture, and it names the measured gap that would falsify it. The player compiles it in your browser; most checks it cannot run here, and each skip says why.", "The native t27c runs all 13 tests of gfternary.t27, and all 13 pass. The same report prints a line most tools never do: 8 of the 13 passed with no runtime assert executed. A vacuous pass is not a lie, but it is not evidence either. The 5 invariants are proved while the spec compiles, and one of them asserts only true, a placeholder. So the recording plants a bug: the reserved code 11 is no longer folded to zero. Exactly one test fails, the one written for that rule. Every byte in the recording was printed by the command; only the typing is staged.", "The spec golden_sieve.t27 gives a ternary weight five measured rules and a sixth that narrows what they leave. S1: the number of values is a power of three; S2: at most two trits, as a third gave no significant gain. S3: one accumulator lane, so any two weights have a rational ratio: plus and minus phi pass as a common scale, while 1 and phi need two lanes. S4: at most six input bits per neuron, a trade, not a law, says the spec; S5: no DSP48E1 or SRL16E cells, for which openXC7 wrote a wrong bitstream while every tool said OK. The five leave TNF(k, b) with 3 or 9 levels, and S6 removes the 9-level form: on every integer ladder the top weight outweighs the others added, so one input decides. The browser runs all 3 tests and all 8 invariants.", "In the browser, setting MAX_TRITS to 3 made two checks fail. The native t27c is stricter: the 8 invariants of golden_sieve.t27 are proved while it compiles, so with a third trit the spec does not compile at all. The report says BLOCKED, and explains that a blocked spec never produced a binary, so it has no test results to count. A rule checked while compiling cannot ship broken. Then the recording plants a different bug: the rule against shift-register cells now always says yes, and exactly one test fails, the one that checks the formula. Every byte in the recording was printed by the command; only the typing is staged.", "The name TNF covers two different things. In the sieve, TNF(k, b) is an alphabet of weights with 3 or 9 levels. TNF17e in tnf17.t27 is a Ternary Network Float, which the spec calls the signed accumulator format: a sign bit, a 7-bit offset and a 9-bit mantissa, worth (-1)^s x (1 + m/512) x 2^(offset - 40). Its trits are in the exponent, not in the weights: the offset takes 81 values, exactly 3^4, so the exponent is four balanced trits, from -40 to +40. Four trits cost 7 bits on a binary chip, so the rung is 17 bits, not 16; the spec records that its source counted positions, and a trit is not a bit. The browser runs 20 of its 44 checks, all 10 invariants among them, and names the cast behind each skip.", "The browser skipped 24 checks of tnf17.t27; the native t27c runs all 34 tests, all pass, and none is vacuous. Negation flips the sign bit with XOR. The recording swaps XOR for OR, a one-character bug. Negating a positive number still looks right, so the test that negates one still passes. But negating a negative number now leaves it negative, so negating twice no longer gives the number back. Exactly one test fails, negate_is_an_involution, the one that states that property. Every byte in the recording was printed by the command; only the typing is staged.", "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. Modules 1 to 4 were about storing numbers: the T27 cell, the OCP MX scale byte, ternary weights scaled by phi, and TNF; this module makes them do arithmetic. gft_smul.t27 uses the bits of tnf17.t27: 1.0 is 20480, and -1.0 is 86016, the same bits plus the sign bit 65536. smul sets the sign when the two input signs differ and leaves the size to magmul. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording makes the sign always 0, and exactly one test fails, m2, which multiplies 1.0 by -1.0. Every byte in the recording was printed by the command; only the typing is staged.", "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. sadd in gft_sadd.t27 adds two numbers of the same sign with magadd. When the signs differ it subtracts the smaller size from the larger with magsub, takes the sign of the larger, and returns 0 on an exact cancellation. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. Only a2, 1.0 plus -1.0, mixes signs, and it cancels: magsub returns 0 at once because both sizes are equal. No test subtracts two different sizes, so the sign of the larger never decides a result. The recording adds the sizes in the branch a2 takes, and exactly one test fails, a2. Every byte in the recording was printed by the command; only the typing is staged.", "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. A neuron sums products of weights and inputs, and on_comb in gft_signed_mac.t27 is such a sum with two terms: smul(a1, b1) plus smul(a2, b2). Here smul sets the sign with XOR and has no zero guard. The native t27c runs 4 tests and all pass; one of them, zero_clause, expects 0 x 1 + 0 x 1 to give 512, which is 2^-39, not 0, and its comment calls that the designed behaviour. The browser skips its tests because its runner does not know assert_eq yet. The recording repeats the bug of lesson 12 and swaps XOR for OR. Exactly one test fails, pp: (-1.0) x (-1.0) now comes out negative, so the sum is 0 instead of 2.0. Every byte in the recording was printed by the command; only the typing is staged.", "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. A neuron passes its sum through an activation, and in gft_relu.t27 that is relu(x) = max(0, x). on_comb returns 0 for zero and for any input with the sign bit set, and x itself otherwise. The header says ReLU has an exact 0/1 gradient and that a 2-layer ReLU net solves XOR, which a linear GF-T model cannot. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording lets negative inputs through, which turns relu into the identity, a straight line. Exactly one test fails, negz; no other test has a negative input. Every byte in the recording was printed by the command; only the typing is staged.", "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. The header of gft_exp2.t27 calls exp2 the missing building block for a GF-T softmax. on_comb splits x into a whole part k and a fraction f: k sets the offset, k + 40, and pow2_frac turns f into the mantissa. The header claims at most 1 ULP of error, measured in the prototype, but all 4 tests use whole inputs, 0, 1.0, -1.0 and 2.0, so f is 0 and pow2_frac returns 0 whatever its coefficients. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording drops the minus sign of k for negative x, and exactly one test fails, em1: 2^-1 comes out as 2.0, not 0.5. Every byte in the recording was printed by the command; only the typing is staged.", "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. A classifier ends by naming the class with the largest score. gft_argmax4.t27 does it with no arithmetic: gt ranks negatives below zero and zero below positives, then compares the low 16 bits, which grow with the size. Its header says the lowest index wins ties, strict >. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording turns > into >= for two positive scores, and exactly one test fails, tie_low: four scores of 1.0 now pick index 3, not 0. No test lets two negative scores decide the answer: reversing their comparison fails nothing. Every byte in the recording was printed by the command; only the typing is staged.", "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. Training starts with a number for how wrong a guess was. gft_nll.t27 takes p, the probability the model gave the true class, and returns -log2(p): p = 1.0 costs 0, p = 0.5 costs 1 bit, and p = 0.25 costs 2. on_comb is neg(log2v(p)), and neg flips the sign bit, except that it returns 0 for 0. One header line still says the output is log2(x); the code returns its negative. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording makes neg return 65536 for 0, a zero with the sign bit set, and exactly one test fails, perfect. Every byte in the recording was printed by the command; only the typing is staged.", "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. gft_sgd_step.t27 moves a weight against its gradient: w' = w - eta*g, where eta is the learning rate. Its 3 tests are step, with w = 1.0, g = 0.5 and eta = 0.5; nograd, where g = 0 leaves w alone; and ascend, where g = -1.0 and eta = 1.0 push w up to 2.0. The browser skips all 3 tests because its runner does not know assert_eq yet; the native t27c runs all 3, all pass, none vacuous. The recording multiplies g by itself instead of by eta. In step g equals eta, and in nograd both products are 0. So exactly one test fails, ascend, where the weight falls to 0 instead of rising to 2.0. Every byte in the recording was printed by the command; only the typing is staged.", "Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. XOR is 1 when exactly one input is 1, and a comment in gft_xornet.t27 says a single linear model cannot separate it. on_comb is a 2-layer net: two hidden sums, z0 and z1, each through relu, then a weighted sum of h0 and h1. It does not train: the tests pass hand-set weights, the analytic solution W=[[1,1],[1,1]], c=[0,-1], v=[1,-2]. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording removes relu from h1 alone, and exactly one test fails, x00: at (0,0) z1 is -1.0, and the output becomes 2.0 instead of 0. Every byte in the recording was printed by the command; only the typing is staged.", "A neuron in a ternary network adds up products of trits, and the sum can be any small integer. quantize in activation_quantizer.t27 squeezes it back: above the threshold it returns 2 (P), below minus the threshold 0 (N), and in between 1 (Z). The on_comb at the end makes it combinational: no register, the output follows the inputs. The browser skips all 7 tests because its runner does not know assert_eq yet; the native t27c runs all 7, all pass, none vacuous. The recording changes > to >= on line 6, so a sum equal to the threshold already counts as P. Exactly one test fails, quantize_boundary_hi, which asks quantize(10, 10) for Z. Every byte in the recording was printed by the command; only the typing is staged.", "maj3 in bitnet_majority.t27 packs three trits with pack3, takes dot27 against w_all_p, an all-P weight chunk, and quantizes at 0: the result is the sign of a + b + c. weighted_vote is the same neuron with weights pack3(2, 2, 0) and gives the sign of a + b - c, so the weights pick the function. The browser skips all 12 tests because it does not know assert_eq yet; the native t27c runs all 12, all pass, none vacuous. The recording lowers w_all_p by 16, which turns the weight on lane 2 from P to Z, so maj3 no longer sees c. Exactly one test fails, maj_p_n_n: +1 - 1 - 1 is N, but +1 - 1 is Z. Note that wv_p_z_p passes 0, which is N, as its middle input. Every byte in the recording was printed by the command; only the typing is staged.", "One packed chunk holds 27 trits, so a wider neuron needs several. neuronN in bitnet_neuron_nchunk.t27 takes up to 8 chunks of activations and weights, adds dot27 for nchunks pairs starting at chunk 0, and quantizes the total. The browser skips all 11 tests because it does not know assert_eq yet; the native t27c runs all 11, all pass, none vacuous. The recording changes the loop bound on line 36 from nchunks to 1, so the neuron always reads exactly one chunk. The other neuron tests fill every chunk alike, so one chunk lands on the same side of the threshold. Exactly one test fails, neuron_zero_chunks, which asks for no chunks and expects Z. Every byte in the recording was printed by the command; only the typing is staged.", "on_comb in comb_ternary_dot.t27 is combinational: the generated Verilog drives result with one assign and no register, so the output follows the inputs. It adds tp over all 27 lanes of a and b, and tp calls tmul, which returns 0 if either trit is Z, +1 if they match and -1 otherwise. So N times N is +1, and dot_all_n_x_all_n expects 27. The browser skips all 4 tests because it does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording changes return -1 to return 1 on line 22, and exactly one test fails, dot_all_n_x_all_p, the one test that sets N against P. Every byte in the recording was printed by the command; only the typing is staged.", "stream_ternary_mac.t27 keeps the same dot27 and adds a register, acc, and on_clock, which adds dot27(a, b) to acc. on_clock is clocked: it runs once per clock edge, and acc holds its value between edges. In the generated Verilog, rst_n low clears acc and acc changes only while en is high; the spec itself has no en. The native t27c runs all 4 tests, all pass, none vacuous; the browser skips them for want of assert_eq. All 4 call dot27 and none drives on_clock, so the test-waves widget, not a test, checks the accumulate line. The recording narrows the mask for a on line 26 from 3 to 1, so a P in a reads as N, and exactly one test fails, dot_all_p_x_all_p. Every byte in the recording was printed by the command; only the typing is staged.", "mlp2 in bitnet_mlp.t27 is a whole network: 3 neurons read the input, pack3 packs their 3 trits into a hidden chunk, and 2 neurons turn it into 2 output trits. The course went from number formats and arithmetic to a neuron, training, ternary weights and the chip; here are the neuron, the weights and an on_comb entry, and no training. The native t27c runs all 4 tests, all pass, none vacuous; the browser skips them, and none calls mlp2. The generated Verilog marks the chunk loop in neuronN NOT UNROLLED, and its note says yosys rejects it, so it is not yet a chip. The recording moves the third hidden trit from bit 4 to bit 3 on line 39, and exactly one test fails, pack3_zzz. Every byte in the recording was printed by the command; only the typing is staged." ]; pub const LESSON_TASKS : [27]str = [ "Move the weight across the range and find where E2M1 rounds worse than T27; then read the storage table in bits. In the spec frame, tnf8 spends three trits on a float exponent: find the test that counts its values, and the offset it reserves.", "Find the NVFP4 ratio and the threshold line; then name the format T27 loses to and what it pays for that. The spec frame is a control for another claim: find what its header says a posit match would falsify, and the test that holds es at 2 for every width.", "Drag the window until the bucket overflows and note the largest window that fits; then compare the two real runs. In the spec frame, weighted applies a weight by its sign alone; find apply_alpha, where each layer multiplies once, and the test that shows it rounds down.", "Run the tests and count the skips; then open the Verilog and find the function that checks for code 255. In the spec frame, the catalog gives FP4 E2M1 and MXFP4 the same bits: compare their storage fields and find what MXFP4 adds.", "Find how many invariants the test report proved at compile time, and compare its pass count with the player's.", "Read which element format flushes the most small weights to zero and which two flush none; then find the test the planted bug breaks. A 4-bit element has a narrow range: in the spec frame, find max_value and min_positive of GF4 and the two tests that pin them.", "Run the tests and read why the skips were skipped; then find the code that folds to zero and the line that folds it. The golden ratio can also size a format: in the spec frame, find golden_section and read how gft4 splits 4 positions between sign, trits and mantissa.", "Find the vacuous-pass line and the test the planted bug breaks; then open the spec and find the invariant that asserts only true.", "Run the tests; then change MAX_TRITS to 3 in the source and see which two checks fail. In the spec frame, trits size a float exponent, not a weight: find exp_values of tnf4 and the test that says which offset is not finite.", "Find the line that says why a blocked spec has no test results, and the test the second bug breaks.", "Read the reason on one skipped test; then find the invariant that says four trits span 81 values. In the spec frame, tnf16 has four exponent trits too and sums to 16: find width_rule and read whether that 16 counts bits or positions.", "Find how many passes were vacuous; then go back to the player of the previous lesson, change the XOR to OR yourself, and see whether the browser catches it too.", "In the recording, find the changed line and the test that fails; then in the spec frame find m1 and m3 and work out why a sign that is always 0 cannot break them.", "In the recording, find the changed line and the value a2 expects; then in the spec frame find bsign and check whether a1, a2 or a3 ever depends on it.", "In the recording, find the test that expects 512 and the test the planted bug breaks; then in the spec frame change the XOR in smul to OR yourself and see whether the browser catches it.", "In the recording, find the changed line and the value negz expects; then in the spec frame find the comment on that line and check whether it still describes the code after the change.", "In the recording, find the changed line and the value em1 expects; then in the spec frame find pow2_frac and work out what it returns when f is 0.", "In the recording, find the changed line and the test that fails; then in the spec frame find the line that compares two negative scores and the one test that reaches it.", "In the recording, find the changed line and the test that fails; then in the spec frame find the header line that says the output is log2(x) and compare it with on_comb.", "In the recording, find the changed line and the test that fails; then in the spec frame find step and decide which of its inputs must change for it to catch the bug.", "In the recording, find the changed line and the test that fails; then in the spec frame work out z1 for x10, x01 and x11 and see why relu changes nothing there.", "In the recording, find the changed line and the test that fails; then in the spec frame find quantize_boundary_lo and work out which one-character change on line 7 it would catch.", "In the recording, find the expected and the actual value of maj_p_n_n; then in the spec frame find wv_p_z_p and work out what weighted_vote(2, 1, 2) would return.", "In the recording, find the loop line before and after the change; then in the spec frame find neuron_two_chunks and explain why the same bug does not break it.", "In the recording, find the tmul line that changes and the test that fails; then in the spec frame find dot_all_n_x_all_n and explain why it expects 27 and not -27.", "In the recording, find the mask that changes and the test that fails; then in the spec frame find on_clock and check that no test calls it.", "In the recording, find the pack3 line before and after the change; then in the spec frame open Code, pick verilog and find the loop in neuronN marked NOT UNROLLED." ]; ; --- Claims -------------------------------------------------------------------------------- test the_course_is_three_cubed { assert LESSONS_PER_MODULE == 3; assert MODULE_COUNT == 9; assert LESSON_COUNT == 27; assert MODULE_COUNT * LESSONS_PER_MODULE == LESSON_COUNT; assert LESSON_COUNT == 3 * 3 * 3; } test it_starts_in_the_lab_and_ends_with_a_network { assert MODULE_IDS[0] == "lab"; assert LESSON_MODULES[0] == "lab"; assert LESSON_WIDGETS[0] == "t27-cell"; assert MODULE_IDS[8] == "mac"; assert LESSON_MODULES[26] == "mac"; assert LESSON_WIDGETS[26] == "t27c-bitnet-mlp"; assert LESSON_SPECS[26] == "specs/ternary/bitnet_mlp.t27"; } test the_mx_module_follows_the_lab { assert MODULE_IDS[1] == "mx"; assert LESSON_MODULES[3] == "mx"; assert LESSON_MODULES[5] == "mx"; assert LESSON_WIDGETS[3] == "play-e8m0"; assert LESSON_WIDGETS[4] == "t27c-e8m0"; assert LESSON_WIDGETS[5] == "t27c-ocp-mx"; assert LESSON_SPECS[4] == "specs/numeric/e8m0.t27"; } test the_ternary_modules_follow_mx { assert MODULE_IDS[2] == "ternary"; assert MODULE_IDS[3] == "tnf"; assert LESSON_WIDGETS[6] == "play-gfternary"; assert LESSON_WIDGETS[7] == "t27c-gfternary"; assert LESSON_WIDGETS[9] == "t27c-golden-sieve"; assert LESSON_WIDGETS[11] == "t27c-tnf17"; assert LESSON_SPECS[7] == "specs/numeric/gfternary.t27"; assert LESSON_SPECS[9] == "specs/numeric/golden_sieve.t27"; assert LESSON_SPECS[11] == "specs/numeric/tnf17.t27"; } test the_formats_come_before_they_compute { assert MODULE_IDS[4] == "arith"; assert MODULE_IDS[5] == "neuron"; assert MODULE_IDS[6] == "training"; assert MODULE_IDS[7] == "bitnet"; assert LESSON_MODULES[12] == "arith"; assert LESSON_SPECS[12] == "specs/ternary/gft_smul.t27"; assert LESSON_SPECS[21] == "specs/ternary/activation_quantizer.t27"; } test the_course_sends_nothing { assert SENDS_NOTHING == true; assert LOCALES[0] == "en"; assert LOCALES[1] == "ru"; }