Field notes / Experiment guide
Does the edge-entry law survive a different lattice?
The square-lattice opening-run law leaves its constants unexplained. Do changing loop length and neighbour count distinguish what sets them?
New to these ants? Start here.
An ant moves on a grid of coloured squares. Each colour tells it to turn left (L) or right (R). It advances that square to the next colour, then steps forward. A rule such as RLR is the list of turns, starting at colour zero. On a blank grid the same rule always makes the same trajectory.
Territory means every cell visited so far. Its boundary consists of visited cells with an unvisited neighbour. A highway is a repeating pattern of motion that carries the ant away. A tether means repeated returns to a small home region.
Growing like the cube root of time means taking eight times as many steps to double a linear size. The rotor walker is a comparison model that follows and rotates arrows stored on cells. A seed makes a random starting pattern reproducible; the ant itself can still move deterministically.
We distinguish c, the chance of discovering a cell per boundary step, from kappa (κ), time at the boundary normalized by perimeter divided by area. Explaining their values is harder than using them to account for growth.
The idea under test
Registered hexagonal and triangular-cell comparisons test loop-length scaling and occupation against a lattice-specific uniform reference.
What we learned
Registered plan only; no lattice measurements have been run at registration.
What this leaves open
The predictions are prospective. Growth, support and calibration gates must pass before interpreting the proposed comparisons.
Read the evidence and reproduction recipe ↗
Inside the experiment
Methods, registered predictions, exact commands and saved analysis. These are the working research documents; expand them for the detail behind the introduction.
Methods and original experiment record
Does the c law (and kappa's deficit) survive a change of lattice? Hexagonal and triangular ants
Registered 2026-10-20 00:25 (+13:00), base commit 0de4d38. Nothing in this folder has been run; no lattice
c, kappa or lead slope has been computed on hex or triangular ants before this file was committed. (Disclosure: the old
experiments/2026-10-13-ext-other-lattices-turmites-noise screens measured area and extent exponents only, never c or kappa.)
Question
experiments/2026-10-19-c-law-mechanism found that c (births per boundary step) obeys 1/c = a + b * lead on the square lattice
(a ~ 1.36-1.40, b ~ 0.68-0.70), that 78% of circulating boundary reads return after exactly 4 steps (2x2 loops), and that the
constants a, b are not derived. The kappa deficit (ants at 0.81x the uniform reference) is also unexplained. The square
lattice has one loop length (4) and one coordination number (4), so it cannot say which lattice property sets b.
Hexagonal (z = 6) and triangular-cell (z = 3) ants change both, and hex offers two loop lengths on one lattice.
Definitions (frozen)
- Generic probe:
src/probe.rs's octave windows(2^(k-1), 2^k], with the neighbour set per lattice: square 4, hex 6 (HEX_DIRSaxial offsets), triangular 3 (tri_step, parity dependent).boundary step= step starting on a visited cell with= 1 unvisited neighbour;
birth= step onto a never-visited cell;c = births / boundary steps;P= visited cells with = 1 unvisited neighbour;kappa = (B/S) / (P_end/A_end). - Lead = length of the opening run of identical letters. On hex the alphabet is
{r, l, R, L}(60/60/120/120 degrees,hex_turns); a run ofr(orl) closes a loop of 6 cells around a centre cell, a run ofR(orL) closes a loop of 3 cells around a vertex (checked in gate G3 by stepping, not assumed). TriangularL/Ris +-2 edges: loop of 6 around a vertex. Square: loop of 4. Mirror-canonical:s_0 = R(square, tri),s_0 in {r, R}(hex). Hex strata are r-lead and R-lead. - kappa_unif(a, beta) =
2 * integral_{1/2}^{1} x^(-a(1-beta)) dx(experiments/2026-10-16-kappa-uniform-baseline), with a and beta fitted per run over k = 20..26 exactly asexperiments/_lib/analyse_probe.py. It is an analytic formula in the run's own (a, beta), so it ports to any lattice; what is untested is that its premise (P/A tracks the window) holds there: gate G4. kappa ratio = kappa(k=26) / kappa_unif. - Window and board: census convention, kappa and c from the last window k = 26 (2^26 steps), board 8192 x 8192, blank background.
- Filter (stationary blob, frozen): not escaped (never within 2 cells of the board edge), births > 0 in every window
k = 20..26,
c > 0,|g_k| < 0.05(slope of kappa on t over k = 20..26, asc_law.py), and width exponentalpha_win [0.28, 0.40] as a proxy forcls == blob(the censusclsis not reproduced by committed analysis code; this proxy is new and is a documented deviation from the census definition). - Retention (kept / run) is reported per lattice, stratum and lead. Cells with < 8 kept rules are reported as underpowered and are not topped up silently (a top-up is a logged deviation).
Rule lists (frozen, scripts/make_lists.py, seed 20261020)
Primitive rules, length uniform in 10..14, first letter canonical, opening run exactly m (letter m differs from s_0, uniform
among the other letters on hex), remaining letters uniform. 48 candidates per (lattice, stratum, lead m = 1..5): square 240,
triangular 240, hex 480 (240 r-lead + 240 R-lead). Every rule runs once (blank background is deterministic, so there is no seed).
Controls: random walk, 32 seeds per lattice (uniform neighbour step, seeds 1..32); mean-field walker (resample uniformly over
visited cells, then uniform neighbour), 8 seeds per lattice. Both use the generic probe windows.
Gates (instrument; fail = no claim on that lattice)
- G1 the generic probe reproduces
src/probe.rswindows exactly (all fields) on the square lattice, >= 20 rules. - G2 hex and triangular generic probes equal a naive HashMap re-implementation (all window fields) on >= 20 rules each, plus
HexSim/TriSimarea sequences, and a one-line mutation is caught. - G3 single-letter loop periods by stepping: square R 4; hex r 6, R 3; triangular R 6. A different value revises Definitions before any data is aggregated (logged deviation, not a failure of the science).
- G4 mean-field walker, each lattice: kappa ratio within 0.05 of 1.00 (square was 1.118 against 1.107 measured). If it fails on a lattice, kappa is reported raw there and no ratio claim is made.
- G5 square ants from this pipeline: median
1/clead slope in [0.55, 0.85] and intercept in [1.20, 1.60] (census 0.70/1.36).
Derivation of what the loop length can and cannot do
Little's law (confirmed to 1.6% on the square lattice): 1/c = mean boundary reads per born cell. Take lead m. A cell on the
circulating loop has been read once per lap, its colour advances by one per read, so the loop is "spent" after m reads of every
loop cell: reads per episode = m * l_b where l_b is the number of loop cells that are boundary cells. Hence
b = d(1/c)/dm = l_b / N_b, N_b = births per episode. The loop length l itself cancels per cell. What the derivation does
not fix is how N_b scales with l: if births per episode are set by a chain after the kill (about fixed), b ~ l_b ~ l;
if a larger loop wraps proportionally more fresh ground, N_b ~ l_b and b is lattice-independent. So the mechanism allows both
limits and the data must choose. A third, weaker idea ties b to coordination through the fresh fraction of a loop's neighbours.
The square b = 0.70 is the anchor for all three.
Predictions
Fitted 1/c = a + b * lead, leads 1..5, OLS over kept rules; b per stratum with a 2000-draw rule bootstrap, seed 20261020.
Signature vector of b = (triangular, hex R-lead, hex r-lead):
- H-A (b scales with loop length): b = 0.70 * l/4 -> (1.05, 0.52, 1.05). Within hex, b_r / b_R = 2.
- H-B (b lattice-independent): (0.70, 0.70, 0.70). b_r / b_R = 1.
- H-C (b ~ 4/z): (0.93, 0.47, 0.47). b_r / b_R = 1.
Decision rule: a hypothesis is supported if all three measured strata are within 0.15 of its vector and every other hypothesis misses at least one stratum by > 0.20; otherwise undecided (reported with the measured vector, no hypothesis forced). My odds: H-A 25%, H-B 40%, H-C 10%, none/undecided 25%. Independently of the vector:
- L1 within hex, the
b_r / b_Rbootstrap CI excludes 1 (H-A's signature alone): 30%. - L2 the intercept
ais positive and within 0.5 of 1.36 on every lattice: 50% (no mechanism predicts it).
Kappa:
- K1 kappa ratio median < 1 on hex and triangular (as on square, 0.81): 55%. Each median in [0.65, 0.97]: 40%.
- K2 random-walk kappa ratio >= 1.10 on each lattice (square rw 1.24-1.32): 70%. A ratio < 1 for the memoryless control means the formula or probe fails on that lattice and K1 is void for it.
- K3
|slope of ln kappa on lead|< 0.08 per step in every stratum (square marginal -0.022): 60%. - K4 rw
con each lattice is below the ant median c (square rw 0.448, ants higher): 60%.
Order of work and commands (filled in as built)
This file and NOTEBOOK.md are committed first. Then src/lattice_probe.rs and tests/lattice_probe.rs (G1-G3; these use hex and
triangular rules but aggregate no c; any peek at c before the instrument is final is disclosed in the notebook), then
scripts/make_lists.py, the run, and scripts/analyse.py writing to results/.
Results (run 2026-10-20; commit adf6677 for the instrument and lists, results committed after)
Command: bash experiments/2026-10-20-lattice-c-law/scripts/run.sh (fresh target dir, binary sha256 d135514050596c44078a9635582a4837faea55858d8004c4be0ab612e0f8a74e
in results/run.log; 120 controls 106 s, 960 ants 488 s wall on 6 threads), then uv run .../scripts/analyse.py -> results/analysis.{txt,json};
post hoc uv run .../scripts/explore.py -> results/explore.txt. Raw windows: results/{ants,controls}.csv. Deviations: runs go to 2^27 steps
(windows k = 12..26, the census octave), the lattice probe does not produce the gyration/ant-spread fields (G1 compares the seven it does),
the random walk uses windows k = 16..22 because it leaves the 8192 board before 2^27 (so the rw rows use a different window from the ants).
Gates. G1-G3 pass (cargo test --release --test lattice_probe; a deliberate mutation was caught). G4 passes: mean-field kappa ratio
0.994 (sd 0.021, square), 1.005 (0.013, triangular), 1.004 (0.014, hex), 8 seeds each, so the analytic kappa_unif(a, beta) is a valid
reference on all three lattices. G5 passes: square b = 0.696 [0.666, 0.724], a = 1.286 [1.188, 1.386] (census 0.70, 1.36).
Retention (kept / 48 per lead): square 31 18 29 27 28; triangular 36 47 47 47 46; hex r-lead 38 41 47 47 47; hex R-lead 33 28 37 42 34. No cell is underpowered, but square lead 2 (kept 18, alive 36) and hex R lead 2 (28) lose more than half, so survivor selection is not negligible there.
Registered fits of 1/c = a + b * lead (rule bootstrap, 95%):
| stratum | n | a | b | R^2 |
|---|---|---|---|---|
| square | 133 | 1.29 [1.19, 1.39] | 0.696 [0.666, 0.724] | 0.954 |
| triangular | 223 | 1.28 [1.22, 1.33] | 0.355 [0.340, 0.371] | 0.936 |
| hex R-lead | 174 | 2.80 [2.49, 3.09] | 0.876 [0.782, 0.983] | 0.689 |
| hex r-lead | 220 | -1.18 [-9.5, 2.5] | 1.98 [0.23, 5.82] | 0.011 |
The hex r-lead OLS is destroyed by a single rule that passed the frozen filters with 1/c = 390 (lead 5); it is reported as it came out.
Hypotheses. Measured vector (triangular, hex R, hex r) = (0.355, 0.876, 1.980) versus H-A (1.05, 0.52, 1.05), H-B (0.70, 0.70, 0.70), H-C (0.93, 0.47, 0.47). The frozen decision rule returns undecided (no vector within 0.15). L1 as registered: FAILED (b_r/b_R = 2.26 [0.25, 6.76], dominated by the outlier). Without relying on the outlier-corrupted fit, the triangular stratum alone refutes all three: its bootstrap CI [0.340, 0.371] is at least 0.33, 0.56 and 0.68 below B, C and A, and the hex R stratum (0.876, CI [0.78, 0.98]) is 0.18, 0.41 and 0.36 above B, C and A. So H-A, H-B and H-C are all disproven as stated (each predicted a triangular slope of 0.70 or more; observed 0.355, and b falls as the loop gets longer).
Post hoc, unregistered (results/explore.txt): slopes of per-lead medians of 1/c, which are insensitive to the outlier: square 0.68 (OLS on medians 0.70),
triangular 0.335, hex R-lead 0.86, hex r-lead 0.27-0.28 (per-lead medians 2.63, 2.77, 3.04, 3.39, 3.72). Against loop length l = 3, 4, 6, 6
(hex R, square, triangular, hex r) b = 0.86, 0.70, 0.34, 0.28. Within hex, b_r / b_R ~ 0.32, the opposite direction to H-A's 2. A straight line
b ~ 1.5 - 0.2 l fits the four points, but that is two parameters for four points, chosen after seeing them, and is a description, not a model.
The intercept is 1.3 on square and triangular but ~2.4-2.8 on hex (hex r from the lead-1 median 2.63 minus b).
Kappa. Ratio kappa/kappa_unif(a, beta), own (a, beta) per run, last window k = 26, kept ants: square median 0.770 (IQR 0.62-0.90, census 0.81),
triangular 0.744 (0.66-0.82), hex 0.860 (0.75-1.01). K1 held on both new lattices (medians < 1 and inside [0.65, 0.97]). Random walks (k = 16..22):
1.33 (square), 1.44 (triangular), 1.28 (hex) (K2 held). K3 held (|slope of ln kappa on lead| 0.007, 0.079, 0.059, 0.075; the triangular one only just).
K4 FAILED as registered: random-walk c (0.44 / 0.46 / 0.42) is above the ant median c (0.29 / 0.41 / 0.27) on every lattice. The pooled median is a design
artefact (leads 1-5 sampled uniformly, so it sits near lead 3). By lead-1 medians (results/explore.txt, 1/c = 2.09, 1.60, 2.63, 3.59) ant c is 0.48 (square) and
0.63 (triangular), above the random walk, but 0.38 (hex r-lead) and 0.28 (hex R-lead), below it: whether ants out-enter fresh ground than a random walk depends on lead and
lattice. (An earlier version of this paragraph blamed my memory of the square comparison; see the NOTEBOOK correction.) L2 FAILED (hex intercept 2.8).
Reading. The kappa deficit (ants at 0.74-0.86 of uniform) is not a square-lattice artefact: it appears on all three lattices with a validated reference. The c law form (1/c linear in lead, R^2 0.94-0.95 on square and triangular, 0.69 on hex R-lead) survives, but its slope and intercept are lattice- and loop-dependent, falling with loop length rather than rising. The 2x2-loop picture of the square slope therefore carries no information about the number 0.70 by itself; whatever sets b depends on the loop it circulates. Not tested: why hex fits are worse (four-letter alphabet, letters after the lead are not separated into types), any mechanism for b(l), more loop lengths, mixed-letter leads.
Outlier and provenance notes. The hex r-lead outlier is ant:rrrrrLrrrLrrLL (lead 5, 1/c = 390): its perimeter equals its area at every window (perimeter
exponent 1.0, a lacy territory) while its area and width exponents look blob-like, so the alpha_w proxy and |g_k| < 0.05 both let it through; the proxy leaks
on hex (results/outlier.txt). The next largest hex 1/c (12.9, 9.7, 8.8) are genuine blobs with beta 0.59-0.62. run.log says "dirty: 88 files"; those are stat and line-ending
noise (git diff --ignore-cr-at-eol --stat adf6677 -- src examples: 0 insertions, 0 deletions, appended to run.log). The refutation of A, B and C rests on the triangular stratum
alone, where none of the listed confounds can rescue any of them: retention 0.75-0.98 (highest of the three lattices), R^2 0.94, and the OLS slope 0.355 matches the per-lead-median slope 0.335.
Follow-up A: letter-typed hex lead with a beta filter (registered 2026-10-20 00:50 (+13:00), base commit 7df6d35)
Re-analysis of the existing results/ants.csv (no new runs). Nothing below has been computed; the only things seen so far are the pooled fits above,
the outlier's beta = 1.0, and the beta of the next three largest 1/c (0.59-0.62). It addresses ACTIVE's "do the hex letter types after the lead matter?".
Definitions (frozen). Hex rules only. Filter F2 = the registered filter plus per-run perimeter exponent beta < 0.8 (beta as in analyse.py, k = 20..26).
f_big = fraction of letters in {R, L} among the letters after the lead. Hex strata as before (r-lead, R-lead). Fit M0: 1/c = a + b*lead;
M1: 1/c = a + b*lead + d*f_big. Bootstrap as before (rule resampling, 2000 draws, seed 20261020). Compare M1 to M0 by 5-fold CV R^2 (folds by rule index mod 5).
Predictions.
- T1 F2 removes only the outlier-type rules: <= 5 rules per stratum drop from the kept sets (80% confident), and the r-lead fit becomes usable: R^2 >= 0.5 and b in [0.15, 0.45] (the per-lead-median slope was 0.28). 55%.
- T2 the R-lead b is unchanged by F2: within 0.1 of 0.876. 75%.
- T3 letter type matters: M1 beats M0 in CV R^2 by >= 0.05 in the R-lead stratum, and
dhas a bootstrap CI excluding 0 in at least one stratum. 40%. - T4 the hex intercept is partly composition: in M1 evaluated at f_big = 0 the intercept
afalls below 2.0 in the r-lead stratum. 25%. - T5 the b_r / b_R ratio (F2 fits) has a bootstrap CI below 1 (the opposite of H-A, as in the medians, ~0.3). 60%.
Null/negative outcomes are reported as such. Script scripts/hex_letters.py -> results/hex_letters.txt.
Follow-up A results (run 2026-10-20, scripts/hex_letters.py -> results/hex_letters.txt, ants.csv unchanged)
F2 dropped exactly one rule (rrrrrLrrrLrrLL, r-lead; none in R-lead). Fits (rule bootstrap 95%): r-lead M0 a = 2.41 [2.23, 2.60], b = 0.262 [0.210, 0.310], R^2 0.42
(n = 219); R-lead M0 a = 2.80, b = 0.876 [0.788, 0.987], R^2 0.69 (n = 174). R-lead M1 adds d = 0.99 [0.37, 1.66] on f_big; r-lead d = 0.08 [-0.27, 0.41].
- T1 PARTLY FAILED: the one-rule drop and b in [0.15, 0.45] held (b = 0.262), but R^2 = 0.42 < 0.5. The clean r-lead slope matches the per-lead-median slope 0.28.
- T2 held (R-lead b unchanged, 0.876). T5 held: b_r / b_R = 0.30 [0.24, 0.37], the opposite of H-A's 2 and now with a bootstrap CI free of the outlier.
- T3 FAILED as registered (both conjuncts were required): the R-lead CV gain is only +0.014 although
d's CI excludes 0 there; so tail letter type is real but tiny in R-lead (+0.1 in 1/c per 0.1 off_big) and absent in r-lead. T4 FAILED: r-lead intercept at f_big = 0 stays 2.37; R-lead falls to 2.33 (not registered). - Reading: the hex slope/intercept difference from square and triangular is not a mixed-letter artefact or an outlier artefact. The hex lead fits remain noisy (R^2 0.42 and 0.69 against 0.94-0.95), with most of the scatter unexplained by lead, composition or letter type. Not tested: other tail features (second run length, letters 6-8 as in the square law).
Follow-up B: a new loop length on the Moore lattice (registered 2026-10-20 01:30 (+13:00), base commit after lattice_probe: add Moore)
Nothing has been run on Moore ants. Instrument: Lattice::Moore in src/lattice_probe.rs (8 neighbours, headings N,NE,E,SE,S,SW,W,NW; r/l turn 45 degrees, R/L 90 degrees);
G2 (naive HashMap oracle, ant and random movers) and G3 (loops: r, l -> 8, R, L -> 4) pass in tests/lattice_probe.rs, and a mutation of the l turn was caught.
Rule lists: scripts/make_lists_moore.py (seed 20261021): 48 per lead 1..5 in two strata, r-lead (loop 8) and R-lead (loop 4, but z = 8 not 4); 480 ants; controls rw 32 and mf 8.
Run and filters exactly as the main experiment (2^27 steps, board 8192, windows 12..26, F2 = registered filter plus beta < 0.8, fit 1/c = a + b*lead, 2000-draw rule bootstrap seed 20261020).
Models for b(loop length l) fixed from the four existing strata (l, b) = (3, 0.86), (4, 0.70), (6, 0.34), (6, 0.28) (the hex values use the F2 fits where relevant):
- M-lin
b = 1.5 - 0.2 l(the post hoc line): l = 4 -> 0.70, l = 8 -> -0.10 (so b ~ 0). - M-inv
b = 2.47 / l(least squares through the origin on b vs 1/l): l = 4 -> 0.62, l = 8 -> 0.31. - M-z b depends on coordination as well as l, which neither M-lin nor M-inv covers; it is the residual hypothesis if both miss.
Predictions (odds in brackets).
- B1 Moore R-lead (l = 4, z = 8): b within 0.15 of the square value 0.70, i.e. [0.55, 0.85] (loop length, not z, sets b): 40%.
- B2 Moore r-lead (l = 8): M-lin is supported if b < 0.12 and the bootstrap CI excludes 0.31; M-inv is supported if b in [0.20, 0.42]; otherwise neither. Odds: M-lin 15%, M-inv 35%, neither 50%.
- B3 within Moore, b(r-lead) < b(R-lead), bootstrap CI of the ratio b_r/b_R below 1: 80%.
- B4 both Moore fits have R^2 >= 0.5 and retention >= 8 kept rules per (stratum, lead): 55%.
- B5 the Moore intercepts a lie within 0.5 of 1.3 (the square/triangular value): 30% (hex was 2.4-2.8, which scales with nothing I have).
- B6 random-walk kappa ratio >= 1.10 and mean-field ratio within 0.05 of 1 (gate G4) on Moore: 85%. If G4 fails, no kappa claim there.
Decision: M-lin and M-inv are scored on B2 only; a supported model must also not miss B1 by more than 0.15 of its own l = 4 prediction (0.70 / 0.62). Results will be appended below, not edited above.
Script scripts/analyse_moore.py -> results/analysis_moore.txt.
Follow-up B results (run 2026-10-20; scripts/run_moore.sh, commit 02f5d13, binary sha256 ae948ed0...8049d6a in results/run_moore.log; scripts/analyse_moore.py -> results/analysis_moore.txt)
Controls 103 s, 480 ants 303 s. Retention high (kept 39-48 of 48 in every cell). Fits (rule bootstrap 95%):
r-lead (loop 8) a = 2.39, b = 0.260 [0.235, 0.284], R^2 0.69 (n = 234); R-lead (loop 4, z = 8) a = 2.41, b = 0.775 [0.711, 0.857], R^2 0.74 (n = 207); b_r/b_R = 0.335 [0.291, 0.380].
- B1 held (0.775 in [0.55, 0.85]; the 0.075 excess over the square 0.70 is outside the Moore CI, so z has a small effect or none beyond it). B3 held. B4 held. B6 held (mean-field 0.999, random walk 1.19).
- B2: M-lin not supported (b = 0.26, not ~0): the linear description is refuted at l = 8. M-inv's number was met (b in [0.20, 0.42]) but it fails its own consistency clause (R-lead deviation from its l = 4 prediction 0.62 is 0.155 > 0.15), so by the frozen rule neither model is supported. Marginal and I do not round it up.
- B5 FAILED: Moore intercepts 2.39 and 2.41, like hex (2.4-2.8) and unlike square/triangular (1.3). Intercepts: z = 3: 1.3, z = 4: 1.3, z = 6: 2.4-2.8, z = 8: 2.4 (post hoc; not monotone in z, and equal for both Moore loops).
- Post hoc, unregistered: within a lattice, doubling the loop length from 3 to 6 (hex) and 4 to 8 (Moore) multiplies b by 0.30 and 0.335, i.e. b ~ l^-1.6 locally, steeper than both 1/l and the cross-lattice triangular/square ratio (0.49). A model must say why the within-lattice drop is steeper than the across-lattice one. Ant kappa ratio median 0.96 on Moore (higher than 0.74-0.86 elsewhere), so the kappa deficit weakens with z; also post hoc.
- Conclusion: the registered test of the b(l) models on a new loop length falls to "neither", but constrains the answer: b(8) = 0.26 is far from zero, b is flat at ~0.26-0.28 for l = 6 and 8 in different lattices, and the intercept looks set by the lattice (hex-like on Moore), not the loop. No mechanism yet.
Analysis notebook: notebook.ipynb
5 saved outputs · 4 code cells. Cells are not executed during publication. Download notebook
Does the c law survive a change of lattice? Hexagonal and triangular ants
Question
On the square lattice 1/c = 1.36 + 0.70 lead (c = births per boundary step, lead = opening same-letter run). Is the slope a property of the 2x2 loop, of the lattice
coordination, or of neither? Three hypotheses were registered before any lattice c existed (README): A b scales with loop length, B b is lattice-independent,
C b follows 4/z. Secondary: is the kappa deficit (ants at ~0.8 of the uniform reference) square-specific?
Assumptions and definitions
Generic octave-window probe on square / hex / triangular lattices (src/lattice_probe.rs); boundary step, birth, perimeter as src/probe.rs; kappa ratio = kappa / kappa_unif(a, beta)
with the run's own a, beta (k = 20..26). Instrument gates G1-G3 (cargo test --release --test lattice_probe) passed; G4 (mean-field walker) and G5 (square c law) are checked below.
Code
import hashlib, json, subprocess
from pathlib import Path
ROOT = next(p for p in [Path.cwd(), *Path.cwd().parents] if (p / "AGENTS.md").exists())
R = ROOT / "experiments/2026-10-20-lattice-c-law/results"
for n in ["ants.csv", "controls.csv", "analysis.json", "analysis.txt", "explore.txt", "run.log"]:
print(hashlib.sha256((R / n).read_bytes()).hexdigest()[:16], n)
print("git", subprocess.run(["git", "rev-parse", "--short", "HEAD"], cwd=ROOT, capture_output=True, text=True).stdout.strip() or "unresolvable here; see README (instrument adf6677)")
A = json.loads((R / "analysis.json").read_text())
Results
Gates and the c law per stratum (registered fit)
Code
k = A["kappa"]
print("G4 mean-field kappa ratio:", {l: round(k[l]["mf"]["ratio_mean"], 3) for l in k})
for key, f in A["fits"].items():
print(f"{key:9s} n={f['n']:3d} 1/c = {f['a']:6.2f} + {f['b']:5.2f} lead b 95% CI [{f['b_ci'][0]:.2f}, {f['b_ci'][1]:.2f}] R2 {f['r2']:.2f}")
print("retention (kept per lead 1..5):", {key: v["kept"] for key, v in A["retention"].items()})
print("decision rule verdict:", A["verdict"])
Square reproduces the census (G5 passes). Triangular b is half the square's, with a tight interval. The hex r-lead fit is corrupted by one rule with 1/c = 390 that passed the frozen filters; the registered decision rule returns undecided. It does not show a mechanism for b. The next cell is post hoc and unregistered.
Code
print((R / "explore.txt").read_text())
Per-lead medians give b of about 0.86 (hex R, loop 3), 0.70 (square, loop 4), 0.34 (triangular, loop 6) and 0.27 (hex r, loop 6): b falls as the loop gets longer, opposite to H-A.
All three registered hypotheses are disproven as stated: triangular alone is at least 0.33 away from each vector. A line b ~ 1.5 - 0.2 l fits but has two parameters for four points chosen after the fact.
Kappa
Code
for l, v in k.items():
print(f"{l:6s} ants median ratio {v['ants_median']:.3f} (n={v['ants_n']}, IQR {v['ants_q'][1]:.2f}-{v['ants_q'][2]:.2f}) random walk {v['rw']['ratio_mean']:.2f} mean-field {v['mf']['ratio_mean']:.3f}")
print("K3 slopes of ln kappa on lead:", {a: round(b, 3) for a, b in A["k3"].items()})
The deficit is not square-specific: all three lattices have ant medians of 0.74-0.86 with a mean-field reference at 1.00 and memoryless walkers at 1.3-1.4 (random-walk window k = 16..22). It does not explain the deficit.
Caveats
- Single deterministic run per rule; survivor selection (e.g. square lead 2 keeps 18 of 36 alive);
alpha_wis a proxy for the censusblobclass. - Hex has four letters; letters after the lead are not separated by type. Triangular loops enclose a vertex, so "loop length" may not be the variable that matters.
- Failed registered predictions: L1 (outlier), L2, K4. Deviations: 2^27 steps, no gyration fields, random walk window.
Next
Register b(l) and a letter-typed hex lead before using a new loop length; derive why b falls with l; check whether the intercept (1.3 square/triangular, ~2.5 hex) tracks coordination.
Read the dated lab log
Lab notebook: lattice c law (hex and triangular ants)
Append-only. Newest entry last. Never edit an old entry; correct it with a new one
(corrects: <timestamp>). Tags: intent, assumption, observation, interpretation,
surprise, decision, dead-end, question, deviation. See experiments/README.md.
2026-10-20 00:25 (+13:00) · 0de4d38 · intent
Start lattice-c-law: test whether the square-lattice c law (1/c = 1.36 + 0.70 lead) and the kappa deficit hold on hex and triangular
ants. Registered the three loop-length hypotheses (A: b ~ loop length, B: lattice-independent, C: ~ 4/z) with numeric signature vectors
before any lattice c exists. Decision it informs: whether the 2x2-loop picture of c is specific to square geometry.
2026-10-20 00:25 (+13:00) · 0de4d38 · assumption
kappa_unif(a, beta) ports to hex and triangular because it is analytic in the run's own (a, beta); its premise (a uniform walker has P/A tracking the window) is untested there. Falsified by gate G4 (mean-field walker ratio within 0.05 of 1). Status: untested.
2026-10-20 00:25 (+13:00) · 0de4d38 · question
My derivation says loop length cancels per loop cell (b = l_b / N_b) and the data must decide how N_b scales with the loop; I do not know
which limit is right, so the H-A vs H-B odds (25/40) are a guess. The census cls == blob is not reproducible from committed code, so I
use an alpha_w proxy (deviation from the census definition, stated in the README).
2026-10-20 01:10 (+13:00) · adf6677 · observation
Gates G1-G3 pass (cargo test --release --test lattice_probe, 7 tests): square windows equal probe.rs on the fields the lattice probe
produces; hex/triangular/square ants and both random movers equal a HashMap re-implementation; ant areas equal HexSim/TriSim at every
octave; one-letter loop cells are square 4, hex r 6, hex R 3, triangular 6 (the periods predicted from geometry). A deliberate mutation
(fresh_nbrs += unv + (t & 1)) failed 5 of 7 tests, then reverted. No c was aggregated on any lattice yet. A timing pilot ran 3 rules not
in the frozen lists (output discarded).
2026-10-20 01:10 (+13:00) · adf6677 · deviation
(1) The README said "all fields" for G1 and "2^26 steps": the lattice probe omits the radius-of-gyration and ant-spread fields (not needed
here), so G1 compares the seven fields it produces; and the census kappa window k = 26 is the octave (2^26, 2^27], so runs go to 2^27
steps (windows k = 12..26), as the census. Neither changes any prediction. (2) The kappa-ratio and width-exponent analyses use the
window set k = 20..26 as analyse_probe.py.
2026-10-20 01:45 (+13:00) · adf6677 · observation
Ran controls (106 s) and 960 ants (488 s). Gates G4 (mean-field ratio 0.994 / 1.005 / 1.004) and G5 (square b 0.696, a 1.286) pass. Registered
fits: b = 0.696 square, 0.355 triangular [0.340, 0.371], 0.876 hex R-lead; hex r-lead OLS is garbage (b 1.98, R^2 0.01) because one rule has
1/c = 390. Decision rule: undecided. L1, L2, K4 failed; K1, K2, K3 held. Files: results/analysis.txt.
2026-10-20 01:50 (+13:00) · adf6677 · surprise
b falls with loop length; none of A, B, C (all predicted b flat or rising with loop length / coordination) is close. Triangular is half the square.
Per-lead medians (post hoc, results/explore.txt) give hex r-lead b ~ 0.27, so the within-hex ratio is ~0.32, not 2.
2026-10-20 01:55 (+13:00) · adf6677 · interpretation
All three registered hypotheses are disproven as stated (triangular alone excludes each by >0.3). I do not have a replacement: b(l) = 0.86, 0.70, 0.34, 0.28 for l = 3, 4, 6, 6 is a post hoc description on four points. Alternatives not excluded: the hex fits mix letter types after the lead; the "loop around a vertex" lengths I used may not be the loops that matter on triangles (they enclose a vertex, not a cell); survivor selection (retention 0.4-0.98 by cell). Confidence in "b depends on lattice": high (CI-level). In "b falls with l": moderate (4 strata, 3 distinct l).
2026-10-20 01:55 (+13:00) · adf6677 · decision
Not extending the registered analysis (no outlier trimming added after the fact). Next experiment, if any, must register a b(l) prediction before using a new loop length or a letter-type-separated hex lead.
2026-10-20 03:10 (+13:00) · 3117e1b · interpretation
corrects: 01:45 observation and the README K4 sentence. K4 still failed as registered, but the explanation "I misremembered the square comparison" was wrong: the pooled ant median c reflects my uniform sampling of leads 1-5. Lead-1 medians of 1/c (explore.txt) give ant c = 0.48 square, 0.63 triangular (above rw 0.44, 0.46) and 0.38 hex r-lead, 0.28 hex R-lead (below rw 0.42).
2026-10-20 03:10 (+13:00) · 3117e1b · observation
The hex r-lead outlier is rrrrrLrrrLrrLL (lead 5): 1/c = 390, c = 0.0026 at k = 26, but perimeter = area at every window (perimeter exponent beta = 1.0:
every visited cell has an unvisited neighbour, a lacy territory) while the area exponent (0.65) and width exponent (0.34) look blob-like, so the alpha_w proxy and
|g_k| < 0.05 (g_k = 0.0 because P/A = 1) both let it through. The proxy therefore leaks on hex; a beta filter (e.g. beta < 0.8) would have excluded it. The next
three largest 1/c on hex (12.9, 9.7, 8.8) have beta 0.59-0.62 and are genuine. results/outlier.txt, scripts/outlier.py. Not re-filtering the registered analysis.
2026-10-20 03:10 (+13:00) · 3117e1b · observation
Provenance: the 88 "dirty" files in run.log are stat/line-ending noise: git diff --ignore-cr-at-eol --stat adf6677 -- src examples reports 0 insertions and 0 deletions.
The triangular stratum carries the refutation alone and none of the listed confounds can rescue A, B or C there: retention 0.75-0.98 (the highest of the three
lattices), R^2 0.94, OLS slope 0.355 agrees with the per-lead-median slope 0.335.
2026-10-20 03:25 (+13:00) · 3117e1b · observation
Full cargo test --release (WSL, after adding src/lattice_probe.rs): every test binary reports ok, no failures. Registered result and post hoc reading are kept
separate in README, notes and blog; the outlier was not trimmed from the registered fit.
2026-10-20 00:50 (+13:00) · 7df6d35 · intent
Registered follow-up A in README (letter-typed hex lead, beta < 0.8 filter, T1-T5) before running anything on it. Also added ipykernel/nbformat/nbclient to
pyproject.toml (user chose uv add). The b(l) model on a new loop length is not started: it needs a lattice with a new loop length and a registered model; see next entry.
2026-10-20 01:05 (+13:00) · 78122d9 · observation
Follow-up A ran as registered (results/hex_letters.txt). T2 and T5 held; T1 partly failed (r-lead R^2 0.42, b 0.262 clean); T3 and T4 failed. The r-lead slope was never an
outlier-driven artefact: b_r/b_R = 0.30 [0.24, 0.37].
2026-10-20 01:05 (+13:00) · 78122d9 · interpretation
Letter type after the lead has a small detectable effect in R-lead only (d ~ 1.0, CV gain 0.014); it does not explain the hex intercept or the poor fit. The open items "hex intercept" and "hex letter types" shrink to: why the hex scatter is large and why a ~ 2.4-2.8. The b(l) model on a new loop length is not attempted this session: it needs a new lattice with a new loop length (implementation plus oracle tests per AGENTS rule 4) and a model registered first; no model with a mechanism exists yet beyond the post hoc line.
2026-10-20 01:30 (+13:00) · plan
user selected the b(l) item. Added the Moore lattice (new loop length 8, plus a loop-4 control at z = 8) to lattice_probe with oracle tests; registered M-lin / M-inv and B1-B6 in README before any Moore ant was run. Reason for Moore: hex offers only loops 3 and 6, triangular 6; any new loop length needs a new lattice.
2026-10-20 02:05 (+13:00) · 02f5d13 · observation
Follow-up B ran as registered (results/analysis_moore.txt). B1, B3, B4, B6 held; B5 failed; B2 gave M-lin refuted, M-inv number met but its own consistency clause missed by 0.005, so by the frozen rule neither is supported. b = 0.260 (l=8), 0.775 (l=4, z=8); intercept 2.4 on both.
2026-10-20 02:05 (+13:00) · 02f5d13 · interpretation
b looks like ~0.26-0.28 for l = 6 and 8, so a floor-like or slowly falling dependence, not linear to zero. Intercept tracks the lattice (z >= 6 gives 2.4+), not the loop. Both remain without mechanism; the b(l) item is narrowed, not closed.