Fix SWAP-MTX: Fisher-Yates shuffle was never actually shuffling correctly
Found while building the analysis report for the ACL-RWT relaunch campaign: cfg=0 was missing from run coverage for 2 of 3 seeds, reproduced identically across all three architectures. Root-caused rather than worked around, per Captain Bob's "this is worrisome." SWAP-MTX (capsules/doe.4th Block 2104) never actually swapped two RUN-MATRIX cells -- it performed a lossy one-way copy (second MATRIX! call mis-targeted mat[i] again instead of mat[j]). Confirmed by direct empirical test on the hosted build: INIT-MATRIX gives mat[0]=0, mat[5]=5; after 0 5 SWAP-MTX, mat[0]=0 (unchanged, should be 5) and mat[5]=0 (correct), with the original value 5 permanently destroyed. Every Fisher-Yates shuffle this mechanism has ever run silently duplicated some values and dropped others -- not a true permutation. Not new, not introduced by item 4.6/Stadium work; predates this session. Fixed with explicit temp variables (SW-I/SW-J/SW-VI/SW-VJ), trivially verifiable by inspection over clever stack juggling. Verified on the hosted build for all three seeds used by the relaunch campaign: each now produces all 16 cfg values exactly 30 times, run_id 0-479 fully distinct. Three-arch QEMU acceptance clean: 1012/0/0 POST on all three, identical dict_hash (expected -- doe.4th isn't C-registered or auto-loaded at boot). BLOCK_MAP.md correctly shows only doe.4th's own hash changed. Also includes the R analysis/chart pipeline (analyse_stadium_relaunch.R) built for the relaunch campaign report, and the three acceptance boot logs. Retroactive caveat: the relaunch campaign's own run-matrix coverage (experiments/bare_metal/runs/acl-rwt-20260820/) is not a valid uniform permutation, having run against the buggy shuffle. Whether to re-run it against the fix is a separate call, not made here. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
79d160c1ca
commit
7e2fd9f044
+67
@@ -1993,3 +1993,70 @@ number.** `capsules/ACL.4th` is not self-activated in this repo's default `init.
|
||||
other boot in this document. Reproducing the original `+0.0054%–+0.0088%` measurement would
|
||||
need a paired run (ACL enabled vs. disabled) using this now-validated mechanism and tooling —
|
||||
scoped but not attempted here.
|
||||
|
||||
## N. `SWAP-MTX` real correctness bug found and fixed — Fisher-Yates shuffle was never actually shuffling correctly — 2026-08-20
|
||||
|
||||
While building the analysis/report for Section M's campaign, `cfg` (one of 16 L8 factor
|
||||
configs) was found completely absent from `run_id`'s output for 2 of the 3 seeds (12345,
|
||||
13579), present for the third (67890) — reproduced identically across all three architectures
|
||||
for a given seed. Captain Bob: "this is worrisome" — root-caused rather than worked around.
|
||||
|
||||
**Root cause, confirmed by direct trace and empirical test, not just code reading: `SWAP-MTX`
|
||||
(`capsules/doe.4th` Block 2104, feeding `SHUFFLE-MATRIX`'s Fisher-Yates permutation) does not
|
||||
swap — it performs a lossy one-way copy.**
|
||||
|
||||
```forth
|
||||
: SWAP-MTX ( i j -- )
|
||||
OVER MATRIX@ >R
|
||||
OVER MATRIX@ ROT MATRIX! \ writes mat[i] back into mat[i] -- a no-op, NOT mat[j]!
|
||||
R> SWAP MATRIX! ; \ writes old mat[i] into mat[j] -- only half a swap
|
||||
```
|
||||
|
||||
Direct empirical test on the hosted build: `INIT-MATRIX` gives `mat[0]=0, mat[5]=5`. After
|
||||
`0 5 SWAP-MTX`: `mat[0]=0` (unchanged — should have become `5`), `mat[5]=0` (correctly
|
||||
received old `mat[0]`, but the original value `5` is destroyed, never written anywhere). Every
|
||||
"Fisher-Yates shuffle" this mechanism has ever performed silently duplicates some values and
|
||||
permanently destroys others — not a true permutation. This directly explains the `cfg`
|
||||
absence pattern in Section M and calls into question the "Fisher-Yates shuffled" claim for
|
||||
**every** historical `EXEC-DOE`/`DOE` campaign run with this code, including the original
|
||||
ACL-RWT campaign (June 2026) Section M's own methodology was modeled on. Not new, not
|
||||
introduced by item 4.6/Stadium work — this bug predates this session entirely.
|
||||
|
||||
**Fix — explicit temp variables, no clever stack juggling (trivially verifiable by
|
||||
inspection):**
|
||||
|
||||
```forth
|
||||
VARIABLE SW-I VARIABLE SW-J VARIABLE SW-VI VARIABLE SW-VJ
|
||||
: SWAP-MTX ( i j -- )
|
||||
SW-J ! SW-I !
|
||||
SW-I @ MATRIX@ SW-VI !
|
||||
SW-J @ MATRIX@ SW-VJ !
|
||||
SW-VJ @ SW-I @ MATRIX!
|
||||
SW-VI @ SW-J @ MATRIX! ;
|
||||
```
|
||||
|
||||
Block 2104 stayed within both the 1024-byte and 16-content-line block limits (`mkcapsule
|
||||
--lint` clean); one line needed a `VARIABLE`-name shortening (`SWAP-*` → `SW-*`) to fit the
|
||||
64-char/line limit.
|
||||
|
||||
**Verification, hosted build, all three seeds used by Section M's campaign:** `12345 30
|
||||
EXEC-DOE`, `67890 30 EXEC-DOE`, `13579 30 EXEC-DOE` each now produce exactly 480 rows, all 16
|
||||
`cfg` values represented exactly 30 times each, `run_id` 0–479 fully distinct — a genuine
|
||||
uniform permutation for the first time. Confirmed by piping the stripped capsule source
|
||||
directly into the hosted binary (same method as Sections K/L), not by QEMU boot — `doe.4th`
|
||||
is capsule-level FORTH content, outside the C-registered `test_runner`/`WordTestSuite`
|
||||
harness's scope entirely, so this is the correct verification method for this class of fix,
|
||||
not a shortcut around it.
|
||||
|
||||
**Three-arch QEMU acceptance, clean:** amd64/aarch64/riscv64 all booted to `ok>`, identical
|
||||
`1012/0/0` POST totals and identical `dict_hash=0x5935ce53526d2152` on all three (expected —
|
||||
`doe.4th` isn't C-registered and isn't auto-loaded at boot, so base-dictionary content is
|
||||
untouched by this fix). `capsules/BLOCK_MAP.md` correctly shows only `doe.4th`'s own capsule
|
||||
hash changed (`0xb6ecf5374e8ee77c` → `0xf154616d248e861f`); every other capsule's hash
|
||||
unchanged, confirming the fix is isolated to its own file.
|
||||
|
||||
**Retroactive caveat, not re-litigated here:** Section M's campaign (all 9 cells, `experiments/
|
||||
bare_metal/runs/acl-rwt-20260820/`) ran with the buggy shuffle — its per-run-matrix coverage
|
||||
is not a valid uniform permutation, though total row count (480/cell) and the mechanism's
|
||||
completion/error-free behavior are unaffected by this bug and remain valid findings. Whether to
|
||||
re-run Section M's campaign against the fixed shuffle is Captain Bob's call, not made here.
|
||||
|
||||
Reference in New Issue
Block a user