Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions research/f7-firing-n5-length-census/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# The fixed L/2 firing set at n = 5 beyond length 16

**Claim type: a scoped proof for L ≥ 26, plus an independent census through
L = 24. No new κ.** It answers #4322's open question 1: at `n = 5` the first
length at which every functional is sound is **26**. The selected conditional
κ stays `0.001672660197076918`.

## Object

[PR #4322](https://github.com/CrocSwap/integer-mult-bounds/pull/4322)
(unpaidmoon) shows that the fixed `L/2` substitution's firing set is not
monotone in length at `n = 4`: it is empty at `L = 14, 16` by a flat-excess
cap. At `n = 5` it found the firing set still monotone through `L = 16`.

Its open question 1 asks: **for n = 5, which even length first makes every
functional sound?**

The model is #4322's:

- canonical first-occurrence words;
- simple restored prefix walks on `F_2^n`, universal when the difference set
of the prefix states is all of `F_2^n`;
- a nonzero mask fires when `base < excess ≤ base + d²`, with
`base = (L/2)(L/2−1) − (2^(n−1) − 1)`.

## Results

**1. Soundness from L = 26 (proof).** At `n = 5` a level set is an affine
4-flat. By enumerating all `2^16` subsets of `F_2^4`, the maximum excess of a
`k`-subset is:

| k | 0–3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| max excess | 0 | 3 | 3 | 8 | 14 | 21 | 21 | 30 | 40 | 51 | 63 | 76 | 90 | 105 |

A split `(s, t)` therefore has excess at most `e(s) + e(t)`. This cap is at
most `base(5, L)` for every split exactly from `L = 26`. So every functional
is sound on **every** 26-, 28-, 30- or 32-point subset, not only on walks. At
`L = 24` the cap leaves only the `(8, 16)` split. A firing word there must
contain an entire level set of its mask.

**2. Census through L = 24.**

| L | closed | universal | false prunes | firing masks |
|---:|---:|---:|---:|---|
| 12 | 3,695 | 828 | 2,856 | 24 |
| 14 | 33,922 | 24,787 | 56,686 | 25 (all of weight ≤ 3) |
| 16 | 267,890 | 263,140 | 447,912 | 25 |
| 18 | 1,737,048 | 1,737,048 | 1,812,996 | **28**: adds `27, 29, 30` (weight 4) |
| 20 | 9,019,738 | 9,019,738 | 5,357,220 | **30**: adds `15, 23` |
| 22 | 35,938,056 | 35,938,056 | 11,569,998 | **30**: every mask but `31` |
| 24 | 103,944,140 | 103,944,140 | 14,151,156 | **28**: `15` and `23` fall silent again |

- **Not monotone at n = 5 either.** The firing set stays nested through
`L = 22`. At `L = 24` the weight-4 masks `15` and `23`, which fired at 20
and 22, fall silent, and the all-ones mask `31` never fires. By the cap,
every word that fires at `L = 24` contains an entire level set of its mask.
- **Answer.** Something still fires at `L = 24`, and the cap proves every
functional sound from `L = 26`. So the first all-sound length at `n = 5` is
**26**. This is two steps later than the length-14 collapse at `n = 4`.

**Agreement with the predecessors.** The census reproduces the published
per-mask rows exactly: #3699's `(4,8)`, `(4,10)` and `(5,12)`, #3733's
`(4,12)` and `(5,14)`, both on main, and #4322's new `(5,16)` row. It also
confirms #4322's `n = 4` silence at `L = 14, 16`.

## Method and independence

Nothing is imported from #4322.

- **`verify.py`**, standard library, rebuilds the 16-point flat caps, and
reruns the census itself for every `n = 4` case and for `n = 5` at
`L = 12, 14, 16`. That includes #4322's full `(5,16)` row, in about 50
seconds.
- **`census.c`** is a separate C enumerator for the lengths Python cannot
reach. Its recorded runs in `evidence/CENSUS-RUNS.txt` are bound to the
source by SHA256. They match every published row and the Python census at
`(5,16)`, and give the new lengths 18, 20, 22 and 24.
- **The C runs are local evidence.** CI reads them and does not recompile.
`(5,22)` took about 17 minutes and `(5,24)` about 56 CPU-minutes.
- **Vendored file.** #4322's `verify.py` is vendored byte-identically only
for its literal `EXPECTED_5_16` row, which is read with `ast.literal_eval`
and never run.

## Not covered

- `(7, 18)` and `(7, 20)`, and `n ≥ 6`.
- Any change to a retained seven-label exclusion.

## Reproduce

```sh
python3.12 -B research/f7-firing-n5-length-census/verify.py
cc -O2 -o census research/f7-firing-n5-length-census/census.c && ./census 5 24
```

The first prints `PASS_F7_FIRING_N5_LENGTH_CENSUS` in about a minute.

## Open next questions

None for `n = 5`: open question 1 is decided. #4322's open question 3, whether
a larger `n` repeats a collapse after its firing set fills, now has a second
data point. At `n = 5` the set shrinks from 30 to 28 masks at `L = 24` and is
empty from 26.

## Credit

The model, the `n = 4` theorem and the open question are unpaidmoon's (#4322),
building on Nick Petalas's #3699 and #3733, Nicholas Stone's #2973 and the
PR943 walk. Prepared by Ryanson Jonathan with substantial Claude Code (Claude
Opus 5.5) assistance. Apache-2.0.
18 changes: 18 additions & 0 deletions research/f7-firing-n5-length-census/SOURCES.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"schema_version": 1,
"base_commit": "12fa8c5c844be21bfefb38a19503fbd867a11244",
"files": [
{
"path": "certificates/selected-result.json",
"usage": "provenance",
"git_blob": "b1fea0c94436b236cc2b7d41a7be032811b94c81",
"sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129"
},
{
"path": "docs/research/agent-task-catalog.json",
"usage": "provenance",
"git_blob": "64bfc5af6709ed9811171e0a2108f05e1219cb65",
"sha256": "352a6184713b8843d96fd2c2071eb7aeaa655906b7c51d45ad444912666c8a96"
}
]
}
60 changes: 60 additions & 0 deletions research/f7-firing-n5-length-census/TASK.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"schema_version": 1,
"task_id": "census-pruning-audit",
"priority_revision": 7,
"base_commit": "12fa8c5c844be21bfefb38a19503fbd867a11244",
"original_card": {
"id": "census-pruning-audit",
"question": "Does the reported word-census parity-pruning error invalidate a specific exclusion we still rely on, and what bounded repair is justified?",
"starting_points": [
"research/f7-weight-parity-census-correction/README.md",
"docs/research/research-family-status.md"
],
"methods": [
"Independently reconstruct the tiny counterexample and trace one affected exclusion to its exact pruning code.",
"Compare corrected and unpruned enumeration on a small exhaustive domain, then audit the changed pruning implication."
],
"acceptance_gate": "Pin the old and proposed corrected engines, distinguish a disproved pruning rule from a disproved final bound, and state exactly which downstream exclusions need revalidation. #2973 is an author-checked source archive; neither its new f(7) claim nor all old conclusions are automatically accepted or rejected.",
"first_step": {
"command": [
"python3",
"-B",
"scripts/research_baseline.py"
],
"expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY",
"inspect": "Read #2973 error description and MATH.md; manually reconstruct the prefix states of 01021323 before running any large census.",
"scope": "Source-binding orientation only; no full enumeration or mathematical validation."
},
"first_experiment": "Reconstruct the eight prefix states of 01021323 and compare bit-0 versus Hamming-weight parity budgets independently. Trace the reported false prune to one pinned predecessor (#2542, #2634 or #2691), then test a small exhaustive domain. Stop after a source-impact handoff or corrected-prune proof; a full seven-label replay needs a separate resource budget and a named research decision it can change.",
"bottleneck_link": "An unsound exclusion can wrongly close a mechanism; correcting its dependency boundary is more useful than extending the same census.",
"allowed_variations": [
"One predecessor pruning rule, one independently exhaustive small domain, or one downstream dependency audit."
],
"scope_change_triggers": [
"Launching a larger census before validating the prune and explaining which active decision it changes.",
"Treating a small-domain counterexample to the old rule as a counterexample to every reported seven-label bound."
],
"overlap_terms": [
"2973",
"2542",
"2634",
"2691",
"weight parity",
"bit-0",
"01021323",
"census pruning"
]
},
"scope_status": "refined",
"actual_scope": "Decide for n = 5 which even length first makes every fixed L/2 functional sound (#4322 open question 1): prove soundness from L = 26 by the 16-point flat cap and run an independent census through L = 24.",
"scope_change_reason": "The card's census-pruning question at n = 5 was left open by #4322 beyond L = 16.",
"bottleneck_link": "Where the fixed substitution falsely prunes decides which census exclusions it can safely shortcut.",
"budget_minutes": 45,
"stop_rule": "Stop once the first all-sound length at n = 5 is decided.",
"outcome": "completed",
"evidence": "verify.py: 16-point flat caps by full enumeration prove every subset sound at n = 5 exactly from L = 26; Python census reproduces #3699, #3733 and #4322 rows including (5,16); recorded census.c runs match them and give L = 18, 20, 22, 24 with 1,737,048 / 9,019,738 / 35,938,056 / 103,944,140 closed (all universal) and firing sets of 28, 30, 30, 28 masks; nested through 22, then masks 15 and 23 fall silent at 24; mask 31 never fires; so the first all-sound length is 26 and the n = 5 firing set is not monotone.",
"author": "Ryanson Jonathan, with Claude Code (Claude Opus 5.5) assistance",
"ai_assistance": "Substantial Claude Code (Claude Opus 5.5) assistance in reading, verifier implementation and writing; no independent human or proof-kernel review.",
"no_open_questions_reason": "#4322's open question 1 is decided at n = 5: the first all-sound length is 26.",
"open_questions": []
}
18 changes: 18 additions & 0 deletions research/f7-firing-n5-length-census/VERIFY.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"schema_version": 1,
"status": "ready",
"python": "3.12",
"requirements": [],
"inputs": [],
"commands": [
{
"argv": [
"{python}",
"-B",
"{package}/verify.py"
],
"timeout_seconds": 300,
"checks": "16-point and 8-point flat caps by enumeration; cap soundness from L = 26; Python census for n = 4 (L = 8..16) and n = 5 (L = 12, 14, 16) against #3699/#3733 on main and #4322's vendored (5,16) row; recorded census.c runs bound by SHA256, matching all published rows; L = 18..24 firing sets of 28, 30, 30, 28, nested through 22, masks 15 and 23 silent at 24; first all-sound length 26."
}
]
}
66 changes: 66 additions & 0 deletions research/f7-firing-n5-length-census/census.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/* Canonical simple restored walks on F_2^n and the fixed L/2 firing census.
* Usage: ./census n L -> prints closed, universal, false prunes and per-mask counts.
* Ryanson Jonathan with Claude Code (Claude Opus 5.5) assistance. Apache-2.0.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

static int n, L, base;
static int word[64], st[64];
static uint64_t closed, universal, prunes, per_mask[64];

static void classify(void) {
closed++;
int N = 1 << n;
uint64_t sum = 0; /* difference set, as a bit mask over F_2^n */
for (int i = 0; i < L; i++)
for (int j = 0; j < L; j++) sum |= 1ULL << (st[i] ^ st[j]);
if (sum != (N == 64 ? ~0ULL : ((1ULL << N) - 1))) return;
universal++;
for (int m = 1; m < N; m++) {
uint64_t seen[2] = {0, 0};
int size[2] = {0, 0};
for (int i = 0; i < L; i++) size[__builtin_popcount(st[i] & m) & 1]++;
for (int i = 0; i < L; i++) {
int si = __builtin_popcount(st[i] & m) & 1;
for (int j = i + 1; j < L; j++)
if ((__builtin_popcount(st[j] & m) & 1) == si) seen[si] |= 1ULL << (st[i] ^ st[j]);
}
long excess = 0;
for (int s = 0; s < 2; s++)
excess += (long)size[s] * (size[s] - 1) / 2 - __builtin_popcountll(seen[s]);
long d = (size[0] - size[1]) / 2;
if (base < excess && excess <= base + d * d) { prunes++; per_mask[m]++; }
}
}

static void walk(int depth, int state, uint64_t visited, int maxlab) {
if (depth == L) { if (state == 0) classify(); return; }
int top = maxlab + 1 < n - 1 ? maxlab + 1 : n - 1;
for (int lab = 0; lab <= top; lab++) {
int nxt = state ^ (1 << lab);
if (depth == L - 1) { if (nxt != 0) continue; }
else if ((visited >> nxt) & 1) continue;
word[depth] = lab;
st[depth + 1 < L ? depth + 1 : 0] = depth + 1 < L ? nxt : 0;
walk(depth + 1, nxt, visited | (1ULL << nxt), lab > maxlab ? lab : maxlab);
}
}

int main(int argc, char **argv) {
if (argc != 3) { fprintf(stderr, "usage: census n L\n"); return 2; }
n = atoi(argv[1]); L = atoi(argv[2]);
if (n < 1 || n > 6 || L < 2 || L % 2 || L > (1 << n)) { fprintf(stderr, "domain\n"); return 2; }
base = (L / 2) * (L / 2 - 1) - ((1 << (n - 1)) - 1);
st[0] = 0;
walk(0, 0, 1ULL, -1);
printf("n=%d L=%d closed=%llu universal=%llu prunes=%llu", n, L,
(unsigned long long)closed, (unsigned long long)universal, (unsigned long long)prunes);
int fired = 0;
for (int m = 1; m < (1 << n); m++) fired += per_mask[m] > 0;
printf(" firing_masks=%d\n", fired);
for (int m = 1; m < (1 << n); m++)
if (per_mask[m]) printf("mask %d %llu\n", m, (unsigned long long)per_mask[m]);
return 0;
}
Loading