Status Report — August 2026
DRAM-Native Bit-Logic Classification — 2026-08-09
August is the month the tooling caught up with the algorithm. One root cause
— the float32 score accumulator drifts on large sums — explains a whole
cluster of earlier anomalies (merge member counts, float-vs-int32 gaps, "the float tree is
exhausted"). Fixing it (exact 64-bit accumulation, SCORE_TYPE default → double)
made the merge deterministic, the archive self-describing, and the IFC — the delivery tool —
finally able to run a heterogeneous ensemble end to end: 66.36% == 66.36% == 66.36%
(merge == retrain == IFC import).
1. The float32 Accumulation Drift — Precision is the Feature
The ensemble scores reach ~1e9 — far beyond float32's exact range (224 ≈ 16.7M). Each addition rounds, the error grows with every member, and the beam's gain/argmax evaluations become noisy. The same corpus searched with different merge accumulators tells the story:
| Merge SCORE_TYPE | members | eval |
|---|---|---|
| float32 | 29 | 66.79% |
int64 (-int32) | 44 | 67.72% |
| double | 44 | 67.72% (= int64 exactly) |
The "32-bit is free / only top bits matter" assumption is refuted. The
bit-exact algorithm is sensitive to score-accumulation precision — not to the
container width. The DRAM chip accumulates popcounts exactly in integer arithmetic, so the
int64/double behavior is chip-faithful; the float32 drift was an emulation
artifact. SCORE_TYPE now defaults to double
(-DSCORE_TYPE=float reproduces the legacy behavior).
Why this explains earlier puzzles
- The "float tree is exhausted" (28-33 members vs 88-116 historically): the historical results were made with the int32 merge (pre-float-default); the float merge drifted.
--triesseemed necessary under drift (noisy gains → false attractors); with exact accumulation the single-try--maxbeam is deterministic.- The float-vs-int32 gap (68.15 vs 69.61 on int32-selected members) was a selection + drift artifact, not an int32 advantage.
2. Archive Format v12/v13 — Export Follows the Internal Format
The .ens archive used to store float32 (v11) even when the computation ran in
double — losing precision between computation and storage. Per decision, the export now
follows the internal format:
| version | score type | bytes | note |
|---|---|---|---|
| v8 | int32 (clamp) | 4 | legacy, read-only |
| v9-v11 | float | 4 | unchanged |
| v12 | double | 8 | double builds store losslessly |
| v13 | int64 | 8 | int64 mode, no int32 clamp |
- The version encodes the score type; the export picks it from
SCORE_TYPE. - The
.metanow recordsCOUNTER_TYPE=/SCORE_TYPE=; the trainer errors and the merge warns+skips on mixed-type corpora — a float32 archive can no longer silently mix into a double corpus. - A missed version bound in one reader path caused the "0 blocks filled" SEGV — fixed in all seven sites.
3. lib/ki-ens.h — the .ens Format is Now One Module
All version logic was scattered across four duplicated read paths. It is now centralized in
lib/ki-ens.h:
- Single version → {metadata layout, score width, type} mapping
(
ens_score_bytes,ens_score_type_str,ens_has_*,ens_version_for_score_type). - EnsReader replaces the 4 duplicated read paths in merge-ensemble (read_ens_meta, load_scores_directory, --check, archive_*) — net −420 lines.
ens_write+ens_verify(roundtrip, always-on): the trainer verifies every exported member at write time — a format regression fails on the first member, not after a full corpus.
4. --max = Beam-as-a-Whole — Deterministic with Exact Accumulation
--max (alone) now activates the old tolerant formalism (stop only
after beam_width consecutive dry steps) instead of zeroing --min-gain;
--union was removed. With exact accumulation the single-try --max beam is
deterministic:
| corpus | single-try | --tries 10 | Δ |
|---|---|---|---|
| M1-INT32 | 66.36% (51) | 66.36% (51) | 0 |
| M3-INT32 | 65.25% (88) | 65.25% (81) | 0 |
| M1-FLT32 | 66.88% (92) | 66.88% (92) | 0 |
| M3-FLT32 | 65.44% (55) | 65.44% (55) | 0 |
5. IFC v7 — the Delivery Tool Handles Heterogeneous Ensembles
model.otto v6 stored only W0/target/offset — it could not
represent a heterogeneous ensemble (CIFAR import gave 12.81% = garbage; MNIST
worked only because it is single-config). v7 adds a per-member section
(channel, encoding, width, xform strings) after the header. A second bug was the missing
enc-LUT initialization in the import (gave exactly 10.0% =
random; enc_lut_get returned garbage). Both fixed:
--debug-member on the import prints per-member evals for verification
([ 3/51] ens=42.1% mem=32.4% G:sig8 xf=id).
6. --no-ens-cache — Real-Live Inference on New Data
The IFC's purpose is inference on new data, so the file cache
(data/prepped/cex_*.pre) is a research/dev feature. --no-ens-cache
skips the file cache (read AND write) — inputs are computed from raw data every run; the
in-memory xform cache stays (it builds from the run's data). Validated:
| run | eval | time |
|---|---|---|
--import member.exp (cache) | 66.36% | 5690ms |
--import member.exp --no-ens-cache (real-live) | 66.36% | 6504ms |
Only +814ms — the h0 scoring dominates, so the real-live performance IS the performance.
7. The 4-Way Technology Comparison (xform=id, 377 members)
Four corpora built identically (H=256, EP=10, seed 42, uniform W0, .meta
type-bound), selection via --beam 10 --max:
| corpus | cumulative (best-en) | beam-selected |
|---|---|---|
| M1-INT32 (maj1, int32) | 62.63% | 66.36% (51) |
| M3-INT32 (maj3, int32) | 61.94% | 65.25% (88) |
| M1-FLT32 (maj1, float+double) | 62.70% | 66.88% (92) |
| M3-FLT32 (maj3, float+double) | 62.02% | 65.44% (55) |
- maj1 > maj3 consistently (+0.68-0.69pp cumulative, +1.11-1.44pp beam-selected) — the beam exploits maj1's better members more effectively.
- flt32-double ≈ int32 (±0.07pp cumulative; flt32 slightly ahead when beam-selected +0.19-0.52pp) — with exact 64-bit accumulation the container format is neutral: the Container Principle holds once the drift is gone.
- Winner: M1-FLT32 = 66.88% (maj1 + float counters + double accumulation).
8. Member Lists are Setup- & Arithmetic-Bound
There is no universal member list. A selection is only valid for the exact setup it was made under (maj, H, EP, VN/HN, seed, W0) and the arithmetic it was scored with:
| selection → retrain | eval | Δ |
|---|---|---|
| float-selected → float | 67.85% | — |
| float-selected → int32 | 67.12% | −0.73pp |
| int32-selected → int32 | 69.61% | — |
| int32-selected → float | 68.15% | −1.46pp |
Each arithmetic performs best on its own selection; the coupling is asymmetric (int32-selected
members hurt float more than the reverse). Always retrain with the corpus' exact flags — the
.meta type binding now enforces the arithmetic side.
9. Fashion-MNIST: The 91% Trap
→ Fashion-MNIST paper — a second, independent dataset that sharpened the August findings. The DRAM-native bit-logic stack (Otto Score + maj1, frozen random W0) reaches a 91.6% ceiling on Fashion-MNIST — and cannot break out of the 88.7–91.6% basin, no matter the search strategy:
| Strategy | eval range | Finding |
|---|---|---|
--beam 10 --max, H32..512, m1t 98..108 | 90.8–91.6% | H is flat (+0.14pp from H196→H512); the ceiling is architectural |
--tries N --tries-no-lock | 91.3–91.6% | full-pool search; MORE members ≠ better (pool-monotonicity disproven) |
--tries 3 + --beam-bestN 1/2/3 | 91.4–91.5% | breaking the 1st-best fixation moves the attractor by ~0.1pp |
--expansion-sort clarity | 86.8–89.2% | member clarity alone is WORSE — diversity beats strength |
| Bit-Voting (no W0, no maj1) | 88.7–89.0% | lower technology bound; the W0+maj1 advantage is maj1-driven |
The 3.4pp gap to CNN SOTA (94–96%) is architectural — frozen random W0, no convolution, no trainable features. Every search lever moves the needle by tenths of a percent only. What speaks for the approach: speed — a 221-member sweep runs in ~1 minute (PRF trainer), orders of magnitude faster than CNN training. For a DRAM-native chip the question is not "can we reach 95%" but "how little hardware do we need for 91.6%".
Two real bugs surfaced along the way (both fixed): the IFC import started
with CLI-default maj1-thresh (107) while the model was trained with 104 → every h0 bit
differed → 36.6% instead of 91.1%. The model.otto format is now
v8 (self-describing): it carries maj_mode, maj1-thresh, splitVN and splitHN,
so --import needs no CLI flags. The --dry-run import also SEGV'd on
the unloaded pixel buffer — now a clean "setup only" exit. See the
Fashion-MNIST paper
for the full leaderboard and the end-to-end 91.10% = 91.10% = 91.10% (merge = retrain = IFC)
verification.
Summary
| topic | result |
|---|---|
| float32 score accumulation | drifts at ~1e9 → refuted "32-bit is free"; SCORE_TYPE default → double |
| .ens archive | v12 (double) / v13 (int64); export follows the internal format |
| format code | centralized in lib/ki-ens.h; EnsReader + ens_write + ens_verify (−420 lines) |
| beam scan | --max = beam-as-a-whole, deterministic; --tries adds nothing; --union removed |
| IFC (delivery) | model.otto v7 (per-member metadata) + enc-LUT fix → heterogeneous 66.36% end-to-end |
| IFC self-describing | v8 carries maj_mode/maj1-thresh/splitVN/splitHN → import needs no CLI flags (Fashion 36.6% bug fixed) |
| real-live IFC | --no-ens-cache: compute from raw data, +814ms |
| technology comparison | maj1 > maj3; flt32-double ≈ int32; winner M1-FLT32 66.88% |
| Fashion-MNIST | 91.6% ceiling / 88.7–91.6% basin — architectural, not search-limited; 91.10% end-to-end (merge = retrain = IFC), see Fashion paper |