``` ID: f-p8 Type: f This specifies f as necessary for p8v2 (the earlier p8 is obsolete). Per the answers in #pprog_p8v2_questions_20260727, this is the one place it gets defined, and it absorbs what would otherwise have been a separate arbitration block: there is no strict override of k=1 by k>=2 and no collision handling outside this block. So we are specifying the settling behavior, which emerges from P and the f we specify here. Dual to the trace sparsification rule in #omega-p8; the two have to be specified together. GENERATION 3 FOLDED FOUR THINGS INTO THIS BLOCK (#p8v2_gen3, choice F: "All of them"). Each had been decided in the parent-side C to get an implementation running and was stated nowhere a reader of the P-program could find it: the application rule, the window with its size and tiling, the clamping policy together with the readout's tie-break, and the initial state. They are now under THE RULES below. MJC's instruction with them: several are things he wants to explore dynamically, "so we may be adding some ambiguity back into f, from choices that were previously made by Claude in C code just to get an implementation up and running; these choices should get ramified through the visualization layer as variants". Where that applies it is marked AXIS CANDIDATE and generation 3's choices block asks which become axes. The word MESSAGE is gone from this design, per #p8v2_gen3 and #viz_standard's ruling. One iteration of settling is a TIME STEP IN f; what one pattern contributes to one position in a time step is a PATTERN APPLICATION. ## THE RULES THE STATE. One LSA activation vector of 256 entries per position of the window, over the byte ES (#byte). Entry x is the current log support for "the input byte here is x". This is T restricted to the window. THE WINDOW. Settling runs over a window of indeterminacy of W positions, W = 128 from #omega-p8-hyper, or whatever is left of the sample when that is smaller -- so a 64-byte sample is one window with W = 64. Windows are consecutive and NON-OVERLAPPING, beginning at 0, W, 2W and so on, and every position belongs to exactly one of them. AXIS CANDIDATE: both the tiling and W. A non- overlapping tiling means a position at a window edge settles from one side only, which is a property of the tiling and not of the model. #p8v2_gen3 on choice D wants to go further in this direction -- "not a sliding window at all, but a sequence of windows, once each one settles, then the next one comes up", with cross-window settling as a decode-cost-against-rate tunable. INITIALISATION. An unclamped position starts at 256 zeros. Nothing has reached it yet and 0 is the LSA floor, which is what "nothing is known here" is. There is no background state and no prior: the k=0 table that used to be read here was removed on 2026-08-06 because base frequencies applied outside P are not a pattern (#pprog_p8v2_gen1_choices). If a base rate is ever wanted, its correct form is a pattern from an always-present event, fired by f like any other. CLAMPING. A recorded position is clamped: 255 on its own byte event and 0 on the other 255, and it is never updated by a time step. That is the absolute of LSA -- the position is known -- and it is also exactly what the decoder has, which is the point: the encoder may only clamp what the decoder will also have. KNOWN DEFECT, and it is where generation 2's axis D failed to round-trip: the removal test settles a candidate with every OTHER recorded position clamped to the SAMPLE's bytes, while the decoder settles with the same clamped SET but with its own reconstruction in the unrecorded positions. The two are not the same state, so the encoder's "settling recovers this byte" does not survive to decode time. A TIME STEP. One synchronous time step: every application is computed from the state as it was at the start of the step, and all of them are written together at the end of it. A window runs W time steps -- one per position -- because an application travels one position per step and has to be able to cross the window before the state is read. AXIS CANDIDATE: the number of time steps, which is currently pinned to the window's own width for that reason and for no other. THE APPLICATION RULE (this was the "message rule" in #omega-p8-hyper until generation 3). A pattern of weight w_p driven by a source event at activation w_s contributes min(w_p, w_s) to its consequent: an application is as strong as its weakest link, so an absolute source delivers the pattern's full weight and a doubtful source caps it. FROZEN, and generation 3 confirms it -- #p8v2_gen3, choice B: "b1 is the rule." The form tried before it was w_p - (255 - w_s), read as LSA division by the source's doubt. It annihilates every non-absolute application: at these sample sizes a learned weight is 1 or 2 LSA units, so one hop off a clamped position takes everything to 0 and the whole window settles to byte 0. It is recorded because it is the kind of scaling mistake the generation exists to make visible, and because min() is the same arithmetic the AND gate already uses. THE k=1 STRENGTH. The k=1 section of the model file is 256 bytes of ARGMAX AND NOTHING ELSE, so the pattern's strength is not in the file: it is a constant of the design, and it is the SAME constant everywhere -- the generated settling, the replay reference, the query layer and the viewers. The strength is 8, delivered under the frozen application rule as min(8, w_s) in both directions, and it moves as a pair with a fall-off of 1 (#f-p8-cap8: the delivery and the fall-off only make sense together, so the pair is the unit). 8 is the weight that makes the argmax carry half the mass of an ES whose other 255 events sit at 0, and it is what the data says: the argmax successor carries 0.635 / 0.515 / 0.290 of its context's observations at e64 / e1k / e10k, and the weight expressing that share is 9 / 8 / 7 (tests/pprog/p8v2-k1-strength.py) -- so 8 is the measured value at e1k and within one unit of it at every sample size. A stated strength is a claim about partial knowledge, and the worked case shows why it matters: k1['m'] = 'e', and 'm' occurs at positions 1, 12, 29, 44 and 61 followed by e, l, e, l, l -- the rule holds twice in five occurrences in the only data there is. Under B2 the strength IS carried -- the stored 65536-byte matrix has the learned values -- so B2 is the ground truth any stated constant is scored against; the k=2 tokens carry their strength in the file and need no constant. Alternatives on axis A built before generation 4 keep the delivery they were measured with; the axis-A digit names which pair a variant ran. const: k1_strength = 8 THE ADMITTED OPTIMIZATION, with its unfolding (#um_optimizations). An application is computed from the SOURCE POSITION'S ARGMAX EVENT only, not from its whole distribution. Exact when the source is absolute, an approximation otherwise. The query layer unfolds it by naming the source event that produced each application. #p8v2_gen3 raises the price of this one: min "makes sense but somewhat ignores the rest of each ES", so the rest of the source ES has to be visible against the single event actually used. THE READOUT. A position's settled value is the argmax of its vector after the last time step, TIES GOING TO THE SMALLEST BYTE VALUE. AXIS CANDIDATE, and #p8v2_gen3 names it: a static tie-break rather than "dynamic settling effects breaking ties". It is not a formality -- at e64 most of the vector is zeros and ones, so the tie-break decides a large share of the answers, and it is the same class of hidden decision as generation 2's lsa_add nesting. WHAT VARIES. Given all of the above, what remains open is what a position DOES with the applications that reach it in a time step. That is axis A, and it is where generation 3 is working. ## LSA OPERATIONS USED BY f Every operation f performs, in ordinary arithmetic, so that no bare implementation identifier reaches a reader (#p8v2_gen3: "ordinary arithmetic, or LSA operations if a table is provided on the page that states what those operations are, no more lsa_ceil_log2"). LSA itself is defined in LSA.md; this is only the operation set this program uses. Columns are name, ordinary arithmetic, saturation, and whether the operation draws from the entropy stream -- the last column is what decides whether a reimplementation can reproduce a run. op: lsa_inc | w+1 with probability 1/2^w, else w | at 255 | draws w bits op: lsa_add | log2(2^a + 2^b): the larger of a and b, plus 1 with probability 1/2^d where d = abs(a-b) | at 255 | draws d bits op: lsa_sub | a-b when b= n; the LSA cost of choosing one of n alternatives | none | no The entropy stream is one xorshift32 sequence seeded at 0x5EED1234, SHARED between learning and settling. Two consequences, both load-bearing: any operation that draws makes the run's numbers irreproducible by a reimplementation that does not replay every prior draw in order (which is why a JS viewer can conformance-check A1 but not A2 or A3), and the ORDER in which drawing operations run is part of the definition, not an implementation detail -- see the accumulation nesting pinned in #pp_wordsv2, which was a real defect found in generation 2. THE CONSTANTS f USES, as data. Every numeric constant above, in one machine-readable place, lifted into axes.json exactly as the op: lines are, so no implementation holds its own copy and a viewer reads the number rather than restating it (#pprog_p8v2_gen4_goal_20260815, choice C). Alternatives that move a constant carry their own const: lines, which override these under that alternative. const: window_W = 128 const: time_steps_per_window = 128 const: clamp_weight = 255 const: entropy_seed = 0x5EED1234 ## THE ARGUMENT What follows is the derivation as written, unchanged, except that its closing passage is marked WITHDRAWN where generation 3 overturned it. An example of settling is the input "hello world" and the trace "h l w ", under a Markov model on letters and a token model. Considering only the first-order effects, we have the "hlw" events as absolute, and from these we have probabilities on all the later letters, as well as on tokens. Let's say we have some short words like "hi" which are compatible with "h l" but would require the token to be followed by another word char which is unlikely, but also compatible with "hill" and so on. So we define our token pattern precisely, it's a function (in one direction) from a sequence to a following byte. In the other direction, it's a probability distribution from bytes to tokens that come before them. In our example we have 11 bytes as a window of indeterminacy. This is an important parameter because it controls the cost of the settling process. So we have our patterns, already defined, but we'll define them again here, as generalizations of functions. First we generalize the function from a value to a distribution on the output space. Our functions are also bijections, so in fact we have a bijection from probability distributions on the input space to distributions on the output space. Then with this definition of a pattern, we only need to mention what the input space and output space are for each of the patterns we have. For our M-1 model on bytes, we have the input space and output space as a pair of adjacent byte. This means that one byte casts a shadow on the entire window. Diminishing returns mean that most of the calculations we would do would be irrelevant to the outcome, but within a limited window, we can disregard this effect and calculate everything. This means we can define a kind of settling process already even with just the M-1 model over bytes. Another definition of the M-1 pattern with one byte and the next byte being input and output spaces is that it is a sufficient statistic on the frequency of the joint events, i.e. the byte pairs. That is, the M-1 pattern, generalized, already induces, in the first order, a distribution over all the other bytes in the window. Taking the "h " state, we have probably some high distribution over the vowels in the second position, and then fairly quickly everything goes back to a background state. This background state, by the Markov property, returns to a static distribution. However, our patterns do not just carry a distribution but also have one more free parameter, which can be expressed as the total number of events from which the pattern is derived. If another pattern that is derived from a disjoint set of observations is brought in, these two distributions can be combined into the correct (per probability theory) combined distribution, which is not true if this additional parameter is not known (or if the observations are not disjoint, or more generally, are correlated in a way that is unknown). THAT FREE PARAMETER IS THE PATTERN'S SUPPORT, and generation 3's answer on axis A is built on it: see "What is open" below. LSA is the way we efficiently collect the sufficient statistic to characterize our pattern. On byte pairs, it is easy to see that all we need is the count of observations of each byte pair, but for a known bounded loss rate, all we actually need is the log count of observations with a bounded error rate, which is what LSA is designed to provide. LSA (log stochastic algebra) is defined by an increment rule, with addition, subtraction, multiplication and division derived in obvious ways. The increment rule takes a value $w$ which is a log estimate of an event count, and consumes $w$ bits of entropy. (Here, these $w$ are our T values in [0, 255].) The output of the increment operation is $w+1$ with probability $1/2^w$, and $w$ otherwise. Defining our token pattern: The input ES is a span of bytes, but this varies over sizes. So we can simplify the analysis by saying that it is the union of patterns of different lengths. We can define settling by starting from the goal of the process. The goal is that all information is integrated; that the answer is the probabilistically correct one given the information we have; that is is efficient to compute; and that the computation converges. Given that all of our patterns take time to be applied, and that applying a pattern always costs energy, we arrive at a specific approach: each atomic pattern fires at a specific rate; each atomic event activation (value in T) decays at a particular rate; pattern application is a simple operation on one atomic activation (t_i) given another (t_j) and a pattern (p_ij) between them. Given that we have already defined one simple operation on activation values, namely addition, (all operations hereinafter assumed to be LSA) we should see if this suffices. So, at a fixed frequency, we take the value of t_i and the weight p_ij, and find the value of t_j. First let us consider the case where t_i = 255, i.e. the antecedent event is absolute. Then we take as two candidate rules, either addition of p_ij to the current value of t_j, or assignment of p_ij as the new value of t_j. In the case of assigment, given the full joint pattern on the ESs in which t_i and t_j participate, we see immediately that softmax on the ES of t_j gives the correct probability distribution given the event e_i (corresponding to t_i = 255) and the information about the frequency of the joint events, the sufficient statistic, captured by the pattern. WITHDRAWN FROM HERE TO THE END OF THIS SECTION, by #p8v2_gen3's answer on choice A: "There is no constant sum ever in an ES. The only thing constant is total probability = 1 and that is after softmax. In an ES we are only ever talking about log support values, which don't have a constant sum. However, there is a constant sum in something like an m-1 model which is generated from a known DSS." The passage is kept because it is what #f-p8-add (A2) implements and therefore what generation 1 and generation 2 measured; it is no longer the justification for anything. Given a window of 11 bytes and the input "h ", i.e. $t_{1h} = 255$ we can say that the total information projected onto positions 2, 3, ..., 11 must be the same, and is the distribution that reverts to the frequency distribution over bytes, the M-0 model. Further, the information that we have at each position represents the same total number of observations. This is the second parameter that we mentioned which is independent of the distribution. This means that the sum is constant. Together these rules determine how the fall-off must work. If there is a fundamental frequency $f$ at which each atomic pattern is applied, then we have a rate $f$ at which this sum is copied from each memory cell to that to the left and right of it, or into it from the left and right, so we must have a fall-off at $2f$ to maintain the constant-sum invariant through time. This suggests that the settling process is now fixed and that $f$ is sufficiently constrained to be specified without any further decisions, only elaborating the consequences of those already made. (The result should be equivalent to other very well-known algorithms, and is nothing new.) ## WHAT IS OPEN Axis A, and #p8v2_gen3 gives the direction rather than the rule: firing depends on the probability interpretation of each ES; it stays in cheap integer operations on bytes; and the fall-off comes out of the patterns rather than being a subtracted constant. Its answer on the choices was a5 -- EACH PATTERN FIRES AT A RATE SET BY ITS OWN SUPPORT, the second parameter named in the derivation above. The two ends of the range are named there too: every event at w=1, where "the model is saying that we have seen every possibility happen once", and a w=255 antecedent, "an absolute rule from an absolute event"; and the case of one event at 255 with the rest at 1 or 2, where only the one event fires. Three things that answer does not settle, which generation 3's choices block asks: THE FORMULA. Support is an LSA byte; a rate is how often a pattern is applied. The map between them is not given, and the alternatives built for this generation are points in that family rather than a decision (#variant_protocol). WHERE SUPPORT COMES FROM AT DECODE TIME. This couples axis A to axis B. Under B1 the model file carries the 256-byte forward argmax table and nothing else, so the total count behind a k=1 rule is NOT in the file and the decoder cannot compute a rate from it; under B2 the whole 65536-byte matrix is there and a row or column sum recovers it. For k=2, learn_tokens already computes a per-context support (lsa_sum256 over its successors) but the token section stores only the argmax weight, so it is not serialized either. A rate set by support therefore either costs model bytes or must be derived from something the decoder already has. WHETHER IT MAY DRAW ENTROPY. A rate implemented by drawing from the stream (lsa_bits_zero at the support) is the natural LSA form, but it puts the axis in the same place as A2 and A3: not reproducible by a viewer that does not replay every draw, so not conformance-checkable, so not drivable in the visualization this generation is being designed in. A deterministic rate -- apply the pattern on the time steps where the step number divides its period -- is checkable, and the alternatives built here are deterministic for that reason. ```