Notes — generation 3 on e64

What generation 3 changed, what the runs say, and what is still open. The instrument is here, and every claim below that names a cell links into it. This page carries no controls and computes nothing: it is the commentary that used to sit between a reader and the instrument. Frozen with it: generation 4's instrument is /hutter/pprog/p8v2-gen4-e64/.

Nothing here is a pick. Four variants were run; the choices are the programmer's (#variant_protocol), and this page is evidence put in front of that judgement.

What generation 3 changed, and what to watch here

MJC's answer on axis A was a5 — "each pattern fires at a rate set by its own support" — together with a ruling that removes the ground the previous rule stood on: "There is no constant sum ever in an ES. The only thing constant is total probability = 1 and that is after softmax." A2's decay of 2 was derived from a constant-sum invariant, so it keeps its measurements and loses its justification. Three alternatives were built against the answer, and none of them is a pick:

rulewhat it doeswhat to watch
A4accumulate, then decay by the support deficit of the strongest evidence that arrived: smax − s. the deficit is the same for all 256 entries of a position, so it moves the whole vector together.
A5no decay at all. A pattern of support s is applied only on time steps that are multiples of 2smax−s, so the rate is the fall-off. the only rule where the rate differs between the patterns feeding one position. Step A5 and watch applications switch off and on.
A6accumulate, then decay by the number of applications that actually arrived instead of the constant 2. the minimal change that drops the withdrawn argument while keeping its counting.
A UNIFORM FALL-OFF CANNOT REORDER AN EVENT SPACE. LSA subtraction is monotone, and A2, A4 and A6 all subtract the same amount from all 256 entries of a position. So a uniform decay can only change a position's settled argmax by pushing entries onto the 0 floor — never by reordering them. That is why A6, which replaces the constant 2 with the measured count and so usually decays half again as fast, lands within two positions of the baseline at e10k. A5 is the only one of the three where the rate differs between the patterns feeding one position, and it moved the most. If "a rate set by its own support" is to mean anything at the readout, it has to bite between the patterns feeding a position, not between positions. Measured in the generation 3 report; the mechanism is visible by driving A4 and A6 and watching the profile translate rather than re-sort.
A5 IS NOT CHEAPER, AND THE APPLICATION COUNT BELOW WILL SUGGEST IT IS. The generation 3 report first read A5's 6 percent drop in applications as the schedule buying energy, and corrected itself: the implementation computes all three vectors and counts them before the period decides whether to apply them, so a skipped application costs exactly what a taken one costs. The drop is indirect — under a different settling trajectory more positions reach a state whose backward predecessor set is empty, so fewer applications fire at all. A schedule that really bought energy would decide before computing, and #f-p8 is explicit that applying a pattern always costs energy. So the count is an implementation that does not yet mean what the alternative says, and it does not touch the settling numbers, which are what the axis is read on.
A5 REPORTS conv = 1 AND THAT IS NOT CONVERGENCE. conv records the first time step after which the window's argmax sequence stops changing. Under a scheduled rule the low-support patterns have not fired yet at step 1, so a window can look settled because nothing has happened. The column measures the wrong thing for a rule that fires on a schedule, and generation 3 records that rather than correcting it silently. Select any position under A5 and step: the panel shows the applications sitting out the steps their period does not divide.

Where the support comes from, and why this page had to borrow it

A4 and A5 need each pattern's own support. For a k=1 pattern that is c1[x × 256 + argmax], a value in the 65536-byte learned count matrix — and that matrix is not in a generation-3 model. #f-p8-deficit says so directly: support is read from the learned tables, "which under B1 are in memory but NOT in the model file". A page that ran the rule from a generation-3 model alone would be inventing the numbers.

It is recoverable exactly, and the recovery is checked rather than assumed. Generation 2's v013 stores that matrix, and axis A changes only settling, so v013's learned model either is or is not these variants' learned model. The builder compares the k=1 table and the token section byte for byte and refuses to build if they differ. They do not differ, so the matrix is theirs and this page uses it. Borrowing across a different learned model is exactly what the generation-2 page refuses for B2, and the same test decides both.

Two things fall out of it that are worth seeing rather than being told. The reference support smax at this sample is 2 — the largest learned weight over the k=1 argmax pairs and over the kept k=2 rules, floored at 1, which is what the runs used (confirmed by cmpr-src 2026-08-11: 2 at e64, 6 at e1k, 9 at e10k). So the whole axis has three rates here — every time step, every second, every fourth — because every support in the model is 0, 1 or 2. And which reading of smax should win is still an open choice, so it is a control on this page rather than a constant: an open sub-parameter earns one for the same reason an open axis does. The setting marked in model is the one the runs used.

AND THE k=1 STRENGTH GAP BITES INSIDE THE NEW RULES, NOT BESIDE THEM. A k=1 pattern's support is its learned weight, c1[x×256 + argmax] — not the 255 the settling shell delivers it at. So under A4 and A5 the same pattern delivers at 255 and is rated at 0, 1 or 2 in the same time step: it arrives as an absolute rule and is charged a fall-off as though it had been seen once or not at all. Open any forward application under A4 and the panel shows both numbers on the same line, because they are both true of it.

It is not a corner case here. 229 of the 256 entries of the support table are 0 — a byte never seen as a predecessor in this sample — so their deficit is the full smax and they decay at the maximum rate while delivering at the maximum strength. Whether that is the rule misfiring or the model file simply not carrying what the rule needs is the generation's choice C, and it is not ours to settle.
A5's PERIODS OUTGROW THE WINDOW, AND e64 IS THE SAMPLE THAT HIDES IT. The period is 2smax−s and a window is W time steps. Here smax is 2 and W is 64, so the periods are 1, 2 and 4 and everything fires. At e1k smax is 6 and the periods reach 64, so a support-0 pattern fires twice in a window. At e10k smax is 9, the periods reach 512, and a support-0 pattern never fires inside a window at all — for 169 of the 256 bytes. #f-p8-period named that as a possibility when it was written; at e10k it is what happens. So the rule you can drive comfortably on this page behaves qualitatively differently at the sizes that matter, and this page cannot show you that: it is one sample. Measured in the generation 3 report.
THE MODEL NAMES THE WRONG SUCCESSOR, AND LATD IS HOW YOU SEE IT. The k=1 pattern from m names e. Expand it: m occurs at positions 1, 12, 29, 44 and 61 of this sample, followed by e twice and l three times. The rule the model kept is the less frequent of the two, in the only data there is. It is not a tie and not a tie-break: the stored counts are c1[m][e] = 2 and c1[m][l] = 1, so the log estimate inverted the order of a 3-count and a 2-count — the uncorrelated-error property LSA is designed around, seen from the data side. It bears directly on axis A, because a5 sets a pattern's rate from a support whose ordering is unreliable at these sample sizes. Open it: position 2, settled, or the pattern itself, with its concordance.

What is still open

ONE PATTERN, TWO STRENGTHS — READ OFF THE C, 2026-08-15. The k=1 section of the model is 256 bytes of argmax and stores no weight at all, so anything that reads it has to supply one, and the two things that read it supply opposite ends of the scale. The query layer reports every k=1 pattern at 1 — all 256 of them, in every model queried. The settling shell applies them at 255. On a log-support scale those are not near-misses: 1 says seen once, 255 says an absolute rule.

NEITHER NUMBER IS WRITTEN THE WAY THAT SENTENCE SUGGESTS, AND THE ASYMMETRY MATTERS. In #pp_settle the forward k=1 application is one line — fwd[table[a]] = wsrc; — with no attenuation term at all. There is no 255 in the C. 255 is what the absence of the term amounts to, because 255 is min's identity on a byte, and the block says so in as many words: "its pattern weight is the absolute 255, so under the frozen message rule the message is the source's own activation on that one byte". Three lines below it the k=2 token does write its min: msg = (tok_w[ctx] < gate) ? tok_w[ctx] : gate. So the shell's 255 is a structural claim about the pattern, made by omission. The query layer's 1 is a bare literal, pq_atom(a, c, 1), sitting between a literal 255 for the memory chain and the model's own stored byte for the k=2 rules. Only one of the two has an argument behind it.

AND THE WEIGHT IS NOT MISSING AT COMPRESS TIME — IT IS DISCARDED. The 65536-byte count matrix is a parameter of settle_window. Under B1 the baseline's body opens with (void)c1;, and the spec block instructs it: "the parameters c1 and have_lpp are unused in this alternative and must not be referenced". So c1[a×256 + table[a]], the exact learned support of the rule about to fire, is in scope at the call site and deliberately dropped. What makes that defensible rather than an oversight is axis B and the decoder: under B1 the model file carries the argmax table and nothing else, so a decode-time settling pass has no matrix to attenuate from. Attenuating in the compressor and not in the decompressor would make the two disagree. The pass-through is the only rule that is symmetric across the model boundary — which is exactly the coupling p8v2-words.md names under "where support comes from at decode time", and it makes the k=1 strength an axis-B question rather than a constant anybody is free to change.

UNDER A4 AND A5 BOTH READINGS RUN IN THE SAME FUNCTION. Those variants open by building sup1[x] = c1[x×256 + table[x]] to set each pattern's fall-off rate, and then apply the forward message with the same unattenuated fwd[table[a]] = wsrc. The identical number decides how fast the pattern decays and is ignored when deciding what it delivers. That is what the instrument shows as delivered at 255, rated at 0, 1 or 2, and it is in the generated C rather than inferred from the viewer.

It shows up inside axis B as well. Under B2 the forward application is fwd[c] = lsa_min(c1[a×256+c], wsrc) over the whole row: the stored 65536-byte LPP carries 41 entries at strength 1 and 6 at 2, the same support the query layer reports. So the same pattern is absolute under B1 and support-1 under B2 — a difference in what the pattern says, not in how it was obtained, on an axis whose description is reconstruct-versus-store.

This page does not resolve it: the k=1 strength is ../cmpr-src's, in whichever direction it decides, and the two ends are not equally arbitrary. It reports both on every forward application rather than quietly printing the one that agrees with what the engine did. The k=2 token strengths have no such gap: the model stores them, the query layer reports them, and the shell applies them, and all three agree.
THERE IS NO k=0 BACKGROUND. An unclamped position initialises to 256 zeros, and under B1 a byte with no predecessor sends no backward application at all rather than falling back to a base rate. The model file carries no byte-frequency section: the k=1 argmax table starts at offset 48 and every offset past the header sits 256 lower than a model built before 2026-08-06, which the builder rejects on the size identity rather than misreading its argmax table as a background.
AXIS A CANNOT REACH THE RATE, AND THAT IS NOT A DEFECT OF THE AXIS. The trace is 5901 bytes at e10k for all four runs. Under the D1 baseline the trace is decided by the causal pass and the decoder reconstructs causally, so settling never runs at decode time and nothing axis A does can move the compression. The axis is read off settling instead, which is what this page draws. It is also why the support A4 and A5 need can live in memory rather than in the model file — and why combining either with D2 or D3 would first require serializing it.
C2 STILL NEVER BINDS. The AND gate is byte-identical to the baseline at every sample that can be run, because the learned k=2 weights are 1 or 2 and the gate has nothing to bite on. It is pruned as of generation 3 — it drops weak signals, so it cannot support settling — but if a rule ever changes what the weights look like, C becomes measurable for the first time. Both are measured in the generation 3 report, which is published beside this page rather than summarised on it.

Where these go on

Published under #viz_standard. Regenerated by docs/pprog/build-p8v2-gen3-e64 from docs/pprog/p8v2-gen3-e64-notes.tpl.html, never hand-edited.