diff --git a/research/f7-weight-parity-census-correction/CHECKPOINT.json b/research/f7-weight-parity-census-correction/CHECKPOINT.json new file mode 100644 index 0000000000..3fe9e4377f --- /dev/null +++ b/research/f7-weight-parity-census-correction/CHECKPOINT.json @@ -0,0 +1,33 @@ +{ + "scientific_base_commit": "a23bdc1b07be96bea0101d44ae27d14de67a79ed", + "presubmission_main": "ec1f08b16f85937471950f986fffb2efcb4b223a", + "priority_revision": 5, + "pinned_engine_inputs_unchanged": true, + "selected_record_unchanged_since_scientific_base": true, + "guidance_delta": "Latest AGENTS/CONTRIBUTING add a maintainer queue procedure; this user-assigned contributor correction does not perform queue intake. Frozen predecessor packages remain unchanged.", + "coverage": { + "open_prs": { + "fetched_at": "2026-10-16T15:16:40.144235+00:00", + "complete_pagination": true, + "records": 239 + }, + "discussions": { + "fetched_at": "2026-10-16T15:11:56.709016+00:00", + "complete_pagination": true, + "records": 1668 + } + }, + "live_related_prs": [ + 2542, + 2634, + 2691, + 2773, + 2870, + 2911, + 2933, + 2961 + ], + "decision": "Continue existing word-census correction. Set-level successors expressly leave word enumeration outside their scope. No independent competing corrected word-census found in checked PR/discussion corpus; no-match is not proof of novelty.", + "integrated_replay": "FULL-REPLAY.txt; fresh six-case C replay plus independent small/capped controls passed.", + "claim_url": "https://github.com/CrocSwap/integer-mult-bounds/pull/2691#issuecomment-6218443989" +} diff --git a/research/f7-weight-parity-census-correction/FULL-REPLAY.txt b/research/f7-weight-parity-census-correction/FULL-REPLAY.txt new file mode 100644 index 0000000000..7b3e27ba25 --- /dev/null +++ b/research/f7-weight-parity-census-correction/FULL-REPLAY.txt @@ -0,0 +1,126 @@ +{ + "admissible_splits": { + "18": [ + [ + 9, + 9 + ] + ], + "20": [ + [ + 7, + 10 + ], + [ + 8, + 10 + ], + [ + 9, + 9 + ], + [ + 9, + 10 + ], + [ + 10, + 7 + ], + [ + 10, + 8 + ], + [ + 10, + 9 + ], + [ + 10, + 10 + ] + ] + }, + "small_word_checks": { + "n3_L6": { + "restored": 90, + "universal": 60, + "canonical_universal": 10, + "split_histogram": { + "2,3": 18, + "3,2": 18, + "3,3": 24 + } + }, + "n3_L8": { + "restored": 1260, + "universal": 1092, + "canonical_universal": 182, + "split_histogram": { + "2,3": 252, + "2,4": 12, + "3,2": 252, + "3,3": 408, + "3,4": 72, + "4,2": 12, + "4,3": 72, + "4,4": 12 + } + }, + "n4_L8": { + "restored": 2520, + "universal": 624, + "canonical_universal": 26, + "split_histogram": { + "3,4": 48, + "4,3": 48, + "4,4": 528 + } + } + }, + "tight_claim_countercheck": { + "9,9": { + "budget": 9, + "need": 9, + "max_upper": 29, + "rows_after_need_plus_one": 36 + }, + "10,7": { + "budget": 3, + "need": 3, + "max_upper": 5, + "rows_after_need_plus_one": 3 + } + }, + "simple_n4_counterexample": { + "word": "01021323", + "weight_split": [ + 4, + 4 + ], + "weight_excess": 0, + "bit0_split": [ + 6, + 2 + ], + "bit0_excess": 8, + "balanced_budget": 5, + "actual_bit0_budget": 9 + } +} +PASS_INDEPENDENT_MATH +PASS: 164 brute comparisons; 12 fail-closed checks; /var/folders/zg/zjljffh16qjdbpf5fg7yp9xc0000gn/T/f7-parity-replay-ywqp5a_0/small/corrected_c_results.json +PASS: 36 exact node/leaf/hit/prune-count comparisons; /var/folders/zg/zjljffh16qjdbpf5fg7yp9xc0000gn/T/f7-parity-replay-ywqp5a_0/capped/n7_crosscheck_results.json +SHARD - nodes=951416 candidate_restored_leaves=0 hits=0 complete=true seconds=0.097 +TOTAL N=7 L=18 split=9,9 nodes=951416 candidate_restored_leaves=0 hits=0 complete=true seconds=0.097 +SHARD - nodes=67643567 candidate_restored_leaves=0 hits=0 complete=true seconds=5.102 +TOTAL N=7 L=20 split=9,9 nodes=67643567 candidate_restored_leaves=0 hits=0 complete=true seconds=5.102 +SHARD - nodes=35241 candidate_restored_leaves=0 hits=0 complete=true seconds=0.002 +TOTAL N=7 L=20 split=10,7 nodes=35241 candidate_restored_leaves=0 hits=0 complete=true seconds=0.002 +SHARD - nodes=14450133 candidate_restored_leaves=0 hits=0 complete=true seconds=1.043 +TOTAL N=7 L=20 split=10,8 nodes=14450133 candidate_restored_leaves=0 hits=0 complete=true seconds=1.043 +SHARD - nodes=244301902 candidate_restored_leaves=0 hits=0 complete=true seconds=18.005 +TOTAL N=7 L=20 split=10,9 nodes=244301902 candidate_restored_leaves=0 hits=0 complete=true seconds=18.005 +SHARD - nodes=590371593 candidate_restored_leaves=0 hits=0 complete=true seconds=41.308 +TOTAL N=7 L=20 split=10,10 nodes=590371593 candidate_restored_leaves=0 hits=0 complete=true seconds=41.308 +PASS_F7_WEIGHT_PARITY_FULL fresh_six_case_census universal_words=0 f7_lower=22 f7_upper=24 diff --git a/research/f7-weight-parity-census-correction/MATH.md b/research/f7-weight-parity-census-correction/MATH.md new file mode 100644 index 0000000000..e0207d8ebd --- /dev/null +++ b/research/f7-weight-parity-census-correction/MATH.md @@ -0,0 +1,282 @@ +# Correct collision budgets and the scope of the word census + +This note applies to the PR943 singleton prefix-walk model. It separates the +mathematical necessities from the computational receipts. A corrected complete +zero-hit census at all five length-20 splits proves `f(7) >= 22`; with the +existing verified length-24 construction this gives `f(7) in {22,24}`. The old +bit-0 census receipts do not supply that corrected census. + +## 1. Prefix states and the partition that follows time + +For a word `w` of length `L`, put `q_0=0` and +`q_i=q_(i-1) XOR (1 << w_i)`. Restoration means every label is used a positive +even number of times, so `q_L=0` and `L` is even. Write `P` for the **set** of +prefix states. Universality is `P+P=F2^n`, with addition given by XOR. + +Each step flips weight parity, so `wt(q_i)=i (mod 2)`. Let `E` and `O` be +the distinct even- and odd-weight states, and `s=|E|`, `t=|O|`. At even length +`L`, each size is at most `L/2`: the `L/2+1` even-time positions have the +forced coincidence `q_0=q_L`, and there are `L/2` odd-time positions. + +This upper bound uses **weight parity**, not the value of any one state bit. + +## 2. Both necessary collision budgets + +For a nonzero even-weight mask `x`, let `A_x` and `B_x` count its unordered +representations by distinct points within `E` and within `O`, respectively. +Put + +``` +a = C(s,2) - |supp A|, +b = C(t,2) - |supp B|, +Q = 2^(n-1). +``` + +If `P` is universal, `supp A union supp B` is exactly the `Q-1` nonzero +even masks. Consequently + +``` +a+b <= C(s,2)+C(t,2)-(Q-1). (even budget) +``` + +For an odd mask `y`, let `r_y` count representations by `E x O`. Full odd +coverage means every one of the `Q` masks has `r_y>=1`; therefore + +``` +cross_excess := s*t - |supp r| = s*t-Q. (odd budget at a universal leaf) +``` + +For a partial search state, both excess counts are nonnegative and +nondecreasing when a **new distinct point** is added. A branch may be rejected +once either exceeds the target split's budget. A repeated visit to an existing +prefix state contributes no new point, no new pair, and no collision increment. +Visit multiplicities can support backtracking, but mathematical coverage and +collision counters remain set-based. In particular, the old description +"multiset collision semantics" must not be interpreted as counting repeated +visits as new elements of `E` or `O`. + +| Weight split | Even budget `a+b` | Odd excess at a universal leaf | +|---|---:|---:| +| `(10,10)` | 27 | 36 | +| `(10,9)` | 18 | 26 | +| `(10,8)` | 10 | 16 | +| `(9,9)` | 9 | 17 | +| `(10,7)` | 3 | 6 | + +These are necessary conditions, not sufficient conditions. Exact leaf coverage +is still required. A budget may skip a universality test or prune branches in a +search for universal words; it must not be used to assert that the survivors +are **all** restored words or all simple cycles. + +## 3. The pair identity and the complete split list + +Let `S=supp A intersection supp B`. Double-counting two distinct cross pairs +with equal sum gives + +``` +sum_y C(r_y,2) = 2 * sum_x A_x*B_x. +``` + +Indeed, each unordered `E`-pair and `O`-pair with the same sum produces exactly +two collisions of cross pairs. Since `C(r,2)>=r-1` for `r>=1`, a universal +set satisfies + +``` +sum_x A_x*B_x >= ceil((s*t-Q)/2) =: need. +``` + +On shared support expand `A_x*B_x` as +`1+(A_x-1)+(B_x-1)+(A_x-1)*(B_x-1)`. Summing bounds the same quantity by +`|S|+a+b+a*b`. Full even coverage gives +`|S|=C(s,2)+C(t,2)-a-b-(Q-1)`. Thus the necessary arithmetic rows are + +``` +B0 = C(s,2)+C(t,2)-(Q-1), +a+b <= B0, +B0+a*b >= need. +``` + +At `n=7, L=20`, counting first leaves the unordered splits +`{7,10},{8,9},{8,10},{9,9},{9,10},{10,10}`. The `(8,9)` case has `B0=1` +and `need=4`. Its rows have `a*b=0`, so `B0+a*b<=1<4`; the mirror is +identical. No other split is lost to this basic identity. + +Cyclic rotation by one label translates the prefix set by `q_1`: +`P'=P+q_1`. Translation preserves `P+P`, restoration, and the number of +distinct points; `q_1` has odd weight, so it swaps `E` and `O`. Relabeling +coordinates by first occurrence restores restricted-growth form and preserves +weight parity and coverage. Rotation commutes with coordinate relabeling, so +it also induces a bijection on canonical word classes. Thus existence at +`(s,t)` is equivalent to existence at `(t,s)`. + +It follows that **exactly five** length-20 split representatives require an +exclusion, unless one imports a separate stronger set-level exclusion: + +``` +(10,10), (10,9), (10,8), (9,9), (10,7). +``` + +At `(10,10)`, `|P|=20` and all `q_0,...,q_19` are distinct: self-avoidance +is valid there. It is not a valid global restriction on the other four +splits. At `L=18`, the same counting and identity analysis leaves only `(9,9)`; +then `|P|=18`, so the simple-cycle restriction is valid for that specific +length-18 exclusion. + +## 4. Why the fixed bit-0 budget is unsound + +The algebra in sections 2 and 3 is valid for **any nonzero linear functional** +`ell:F2^n->F2`, with its kernel and other coset replacing even and odd weight. +In particular, bit 0 is a legitimate partition for a set-level cover census +whose parameters `s,t` are the actual cardinalities in those two classes. +`joint_covers` is not invalid merely because it uses bit 0. + +The word-level error is substituting `L/2,L/2` into the budget while grouping +the states by bit 0. Time parity does not fix those class sizes. For a +20-point set of bit-0 sizes `10+d,10-d`, its valid even budget is `27+d^2`, +not invariably 27. A coordinate permutation preserves cube adjacency; a +general linear change of coordinates need not. Thus general set-cover +symmetry cannot turn this wrong word-level budget into the correct one. + +A counterexample lies inside the **simple balanced** word class itself: + +``` +n=4, L=8, canonical word 01021323 +prefix set {0,1,2,3,4,6,8,12} +coverage = all 16 masks +weight split = (4,4) +bit-0 split = (6,2) +balanced budget C(4,2)+C(4,2)-7 = 5 +bit-0 collision excess = 8 +correct bit-0 budget C(6,2)+C(2,2)-7 = 9 +``` + +All eight nonclosing states are distinct, and every label occurs twice. The +old word-level prune rejects this universal word because `8>5`. The +counterexample establishes unsoundness of the prune, not the existence of a +seven-label counterexample at length 18 or 20. Reproducing node counts between +the old Python and C implementations proves traversal agreement, not the +necessity of their shared prune. + +## 5. Correction to the earlier "zero slack" claim + +`session2/agent-c/BUDGETS.md` correctly derives both budgets and the split +list, but its conclusion that any strengthening of `need` would immediately +kill `(9,9)` and `(10,7)` is false. Although `need=B0`, the upper bound is +`B0+a*b`, not simply `B0`. + +For `(9,9)`, the arithmetic row `a=b=1` has `upper=10>need=9`; for +`(10,7)` it has `upper=4>need=3`. Even replacing `need` by `need+1` leaves +36 and 3 arithmetic rows, respectively. The maximum upper values under the +basic budget are 29 and 5. Such rows are necessary-condition rows, not +constructed covering sets; they suffice to refute the claimed logical +implication. A stronger bound could prune rows, but must be compared with +their actual upper values before claiming an exclusion. + +## 6. What a complete length-20 exclusion proves + +Suppose a restored universal word has even length `L<20`. Append `(j,j)` +repeatedly, for any label `j` already in the alphabet, until the length is 20. +Each appended pair takes the current state `0` to `1<=22` without using PR2542's old length-18 computation. +Together with a verified universal length-24 word: `f(7) in {22,24}`. + +A separate corrected length-18 `(9,9)` run remains useful for repairing +PR2542's specific numerical evidence. It is not an additional logical premise +of the new length-20 lower-bound proof. A length-20 witness, by contrast, +would establish only `f(7)<=20`; deciding whether 18 is possible would still +matter for the exact minimum. + +## 7. Scope of the historical evidence and later work + +[PR2542](https://github.com/CrocSwap/integer-mult-bounds/pull/2542) correctly +uses weight/time parity and the pair identity to reduce length 18 to simple +`(9,9)` cycles. Its decisive invocation +`cyclic_census(7,18,budget=9,simple=True)` uses bit-0 grouping and needs a +corrected receipt. The counting bound, identity, restoration parity, and +verified Gray constructions are not refuted by this implementation error. +The separate original overstatement about all 17-point sets is addressed by +the later set-level work below. + +[PR2634](https://github.com/CrocSwap/integer-mult-bounds/pull/2634) vendors +the same engine and replays that length-18 computation. Its partial +length-20 simple-cycle census also applies the fixed bit-0 budget. Its +explicit cover witnesses, template lemmas, and labelled heuristic search +failures have different evidence and are not revoked by this finding. + +[PR2691](https://github.com/CrocSwap/integer-mult-bounds/pull/2691) reproduces +the wrong-budget traversal in C and completes its 15 shards. The old +`6,305,209,079` nodes / `597,148,314` surviving leaves / zero hits remain +historical outputs of that filter. They do not exhaust the mathematically +claimed `(10,10)` candidate class. Its full-exclusion and sound-prune claims +need the corrected census. + +The following merged successors were checked by PR metadata/body at their +recorded heads (see `math/SOURCES.json`): + +| PR | Relevant scope | Effect on this correction | +|---|---|---| +| [2773](https://github.com/CrocSwap/integer-mult-bounds/pull/2773) | Set-level collision profiles and Sidon census exclude `(10,7)` and its mirror | Independent reason to remove that 17-point word split; no repair of old word traversal | +| [2870](https://github.com/CrocSwap/integer-mult-bounds/pull/2870) | Fourier/Parseval excludes `(11,6)` and its mirror, completing the 17-point set-level claim with earlier work | The new Fourier contradiction is separate from repeated old word receipts | +| [2911](https://github.com/CrocSwap/integer-mult-bounds/pull/2911) | 18-point set-level necessary conditions and remainder rows | No whole 18-point split excluded | +| [2933](https://github.com/CrocSwap/integer-mult-bounds/pull/2933) | Classifies vanishing-character cases for 18-point sets | No whole 18-point split excluded | +| [2961](https://github.com/CrocSwap/integer-mult-bounds/pull/2961) | Excludes three set-level classes, strengthens odd-odd multiplicity conditions | No whole 18-point split excluded; no corrected word census | + +These later packages cite or replay the old word census as predecessor state; +that replay does not restore its proof. Their independent set-level lemmas +must be assessed on their own hypotheses. Excluding one set cardinality or +split does not exclude longer words with other admissible prefix sets. + +## 8. Implementation proof obligations and affected material + +For the corrected census: partition the states by Hamming-weight parity (or +equivalently time parity after proving consistency); maintain **distinct** +prefix sets; increment and undo counters only on first visits; use each +target split's even and odd budgets; check exact restored status, split and +coverage at leaves; enumerate all restricted-growth words or a proven +partition of them; distinguish capped/aborted runs from complete runs; retain +the parity-swap argument if only `s>=t` is run. Self-avoidance is permitted +only when the target split forces `|P|=L`. + +Material needing a correction or explicit historical annotation: + +- PR2691 package `research/universal-f7-singleton-l20-census-complete-seedfd938f30/`: + `f7_census.c`, `README.md`, `AUDIT-RESULT.json`, and the old-count expectations + and pass-message semantics in `verify.py`; associated PR description. +- PR2542 package `research/universal-f7-singleton-seedb859517b/`: + `engine.py::cyclic_census` and the census-dependent statements/receipts in + `README.md`, `CERTIFICATE.json`, `CERTIFICATE-PROGRESS.json`, `EXPECTED.json`, + `verify.py`, `search.py`, and related tests and manifests if that package is + revised. Preserve frozen predecessor copies and describe the correction + separately rather than silently changing a pinned source. +- PR2634 package `research/universal-f7-singleton-l20-seed06704c39/`: + the vendored `engine.py`, `census20.py`, `CENSUS20-PROGRESS.json`, + `verify.py`, `EXPECTED.json`, and dependent census claims. Its hash pins + should continue to identify the old bytes if retained as frozen evidence. +- Session-2 `agent-c/BUDGETS.md`: remove the false strengthening conclusion; + `check_budgets.py` has a stale absolute import through the former playground + folder. The new independent script below avoids that path and dependency. +- Session-2 `agent-a/f7_census_revisit.c` defaults to bit-0 budgets; a user + running the default can reproduce the faulty mode. The corrected public + entry point should select only the proved mode and clearly label any + historical-replay option. + +## Reproduction of the mathematical checks + +`python3 math/check_math.py` uses only the standard library and imports no census +engine. It checks all restored words at `(n,L)=(3,6),(3,8),(4,8)`, both +budgets, the exact pair identity, every nonzero linear functional's valid +actual-size budget, rotation symmetry, both length-18/20 split lists, the +simple balanced counterexample, and the false-tightness countercheck. +Observed universal counts: 60, 1092, 624; canonical counts: 10, 182, 26. +The exact output is retained in `math/math-check-output.txt`. + +Open next question after a verified zero-hit length-20 census: decide length +22 versus the known length-24 construction. This note makes no length-22 +exclusion or multiplication-frontier claim. diff --git a/research/f7-weight-parity-census-correction/README.md b/research/f7-weight-parity-census-correction/README.md new file mode 100644 index 0000000000..0221748ce3 --- /dev/null +++ b/research/f7-weight-parity-census-correction/README.md @@ -0,0 +1,136 @@ +# Correct the word-census parity budget; exclude length 20 at seven labels + +The word-census engines retained by PR2542, PR2634 and PR2691 classify prefix +states by coordinate bit 0 but apply a collision budget calculated for the +Hamming-weight split. That rule can reject universal words. This package +demonstrates the error, supplies a corrected revisit-aware C census, and +completes every admissible length-20 weight split with zero universal words. + +Within **PR943's abstract singleton prefix-walk model**, the corrected finite +exclusion and padding argument give **`22 <= f(7) <= 24`**, hence +`f(7) in {22,24}`. This is a computational exclusion with a written pruning +proof and independent controls. It does not change a supplier, tariff, kappa, +or the selected multiplication result; it is not a formal-kernel proof. + +Contributor: Nicholas Stone, with substantial OpenAI Codex assistance, +including three complementary agents for implementation, independent validation +and mathematical/source-impact review. No independent human review claimed. +Base: `a23bdc1b07be96bea0101d44ae27d14de67a79ed`, priority revision 5. +[Canonical claim](https://github.com/CrocSwap/integer-mult-bounds/pull/2691#issuecomment-6218443989) +and [coordination pointer](https://github.com/CrocSwap/integer-mult-bounds/discussions/1166#discussioncomment-18976359). + +## The error and its correction + +For a closed word of even length `L`, weight parity alternates with time; +there are at most `L/2` distinct states of each weight parity. The sizes of +the bit-0 classes have no such bound. In dimension four, the canonical +simple restored word `01021323` has eight distinct prefix states and covers +all sixteen XOR masks. Its weight split is `(4,4)`, its bit-0 split `(6,2)`. +Its bit-0 collision excess is 8, exceeding the wrongly substituted balanced +budget 5; the valid actual-size bit-0 budget is 9. This disproves the generic +old prune, including within simple balanced walks, without asserting a +seven-dimensional counterexample. + +The corrected engine always uses distinct **weight-parity** sets `E,O`. +For target sizes `s,t`, universality necessarily implies + +``` +within_E_excess + within_O_excess <= C(s,2)+C(t,2)-63 +cross_excess <= s*t-64 +``` + +Both excess counts are monotone under addition of a new distinct state; +revisits add nothing. The last step closes the walk and does not add an +extra prefix point. See [MATH.md](MATH.md) for proofs, the exact split +classification, rotation symmetry, padding, and the correction to the earlier +session's overstatement about "zero slack". Bit 0 itself remains a valid +linear functional in a set-level enumeration using its actual class sizes. + +## Complete corrected enumeration + +Each row is one complete restricted-growth traversal from the empty prefix, +with both necessary budgets enabled, no node cap, process exit 0 and +`complete=true`. No self-avoidance condition is imposed on repeated-state +splits. Mirrored splits follow by cyclic rotation and coordinate relabeling. + +| Length | Weight split | Nodes | Universal words | +|---:|---|---:|---:| +| 20 | `(10,10)` | 590,371,593 | 0 | +| 20 | `(10,9)` | 244,301,902 | 0 | +| 20 | `(10,8)` | 14,450,133 | 0 | +| 20 | `(9,9)` | 67,643,567 | 0 | +| 20 | `(10,7)` | 35,241 | 0 | +| 18 | `(9,9)` | 951,416 | 0 | + +All rows have zero candidate restored leaves: the two necessary budgets +exclude every branch before a qualifying leaf survives. This is **not** a +count of all restored words or all simple cycles. The old 6,305,209,079-node +receipt uses different, unsound filtering and is preserved only as history. + +[engine/SUMMARIES.json](engine/SUMMARIES.json) binds the C source, commands, +environment and all six complete receipts. Fresh enumeration took about +63 seconds in total on the recorded Apple arm64 host; compiler and host +affect runtime, not the exact counts. The extra length-18 row repairs that +specific predecessor evidence. The new lower bound needs only the five +length-20 exclusions: a shorter restored universal word could be padded to +length 20 by appending repeated pairs of an existing label. + +The existing reflected-Gray length-24 construction is retained as the explicit +word `010201030102010345464546` in [UPPER-WITNESS.json](UPPER-WITNESS.json). +The verifier checks its restoration and all 128 XOR masks directly. + +## Reproduce + +Python 3.12 standard library and a C11 compiler supporting `unsigned __int128` +(Clang or GCC) are required. Run from the repository root: + +```sh +python3 -I -B research/f7-weight-parity-census-correction/verify.py +python3 -I -B research/f7-weight-parity-census-correction/verify.py --full +python3 scripts/research_package.py --package research/f7-weight-parity-census-correction --check-only +``` + +Quick mode freshly checks the mathematical controls, 164 small full-domain +C/brute comparisons (three `(n,L)` cases, every split, four budget modes), +12 input/cap/output controls, and 36 capped n=7 C/Python comparisons. +The independent Python implementation recomputes pair supports from scratch, +rather than copying the C bitsets and incremental collision counters. +Quick mode also validates stored-receipt integrity and the explicit upper +witness. Its marker is `PASS_F7_WEIGHT_PARITY_QUICK`; it does **not** claim +a fresh full n=7 enumeration. + +Full mode performs those checks, compiles the source afresh, and reruns all +six complete cases, comparing exact non-timing fields. Its marker is +`PASS_F7_WEIGHT_PARITY_FULL fresh_six_case_census universal_words=0 f7_lower=22 f7_upper=24`. +All generated files use temporary directories. Aborted/capped searches +return a different exit status and are not accepted as exclusions. +See [validation/REVIEW.md](validation/REVIEW.md) and +[engine/README.md](engine/README.md) for the component audits and direct CLI. + +## Historical scope and credit + +Frozen predecessor packages are unchanged. Their receipts can reproduce the +old traversal, but cannot justify its pruning premise. This package supersedes +their word-census exclusion evidence within the stated domain. Later set-level +work #2773/#2870/#2911/#2933/#2961 has separate hypotheses and is not broadly +revoked; exact source heads and effects are recorded in +[math/SOURCES.json](math/SOURCES.json) and MATH.md. + +Credit: Ross Richardson (PR943 model); Douglas Colkitt (PR1185 five/six-source +record); Aaron Wang with opencode (PR2542/PR2634 word model, census design, +Gray construction and the later set-level chain); Nicholas Stone (PR2691's +independent C implementation and this corrective continuation). The new C +engine derives from the prior local revisit extension and PR2691-style +restricted-growth enumeration; the validation and mathematical scripts use +separate direct computations. Apache-2.0. + +## Open next questions + +1. Does a restored universal length-22 seven-label word exist? Derive and + audit its complete split domain before a bounded search. This session + makes no length-22 exclusion. +2. Can a separate implementation replay the entire n=7 length-20 exclusion? + The present independent controls cover small full domains and capped n=7 + traversals; the complete n=7 replay uses this C implementation. + +Structured next steps and blockers are in [TASK.json](TASK.json). diff --git a/research/f7-weight-parity-census-correction/SOURCES.json b/research/f7-weight-parity-census-correction/SOURCES.json new file mode 100644 index 0000000000..debaab5575 --- /dev/null +++ b/research/f7-weight-parity-census-correction/SOURCES.json @@ -0,0 +1,36 @@ +{ + "schema_version": 1, + "base_commit": "a23bdc1b07be96bea0101d44ae27d14de67a79ed", + "files": [ + { + "path": "certificates/selected-result.json", + "usage": "provenance", + "git_blob": "b1fea0c94436b236cc2b7d41a7be032811b94c81", + "sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129" + }, + { + "path": "docs/research/agent-task-catalog.json", + "usage": "provenance", + "git_blob": "889b95d936746df124872133ae1e7cc339adcb92", + "sha256": "7d185cd8c2af35988d638d969748ad7b670f902606444dbfc748e4402c340df6" + }, + { + "path": "research/universal-f7-singleton-l20-census-complete-seedfd938f30/f7_census.c", + "usage": "input", + "git_blob": "37d1c1ecfb705a55543dddfe87f43467cd4c50a2", + "sha256": "b4722f59811b9920e1f364e68a228ad5ad34cf98f9c490a3655d78dc2e9ae23b" + }, + { + "path": "research/universal-f7-singleton-l20-seed06704c39/engine.py", + "usage": "input", + "git_blob": "210bd817b19beb20ed474d8946ea87a22121ac1c", + "sha256": "82e4c407b58864c74ffd14d47f00b6b1675005ee0e0173909b7bd092491175e4" + }, + { + "path": "research/universal-f7-singleton-seedb859517b/engine.py", + "usage": "input", + "git_blob": "210bd817b19beb20ed474d8946ea87a22121ac1c", + "sha256": "82e4c407b58864c74ffd14d47f00b6b1675005ee0e0173909b7bd092491175e4" + } + ] +} diff --git a/research/f7-weight-parity-census-correction/TASK.json b/research/f7-weight-parity-census-correction/TASK.json new file mode 100644 index 0000000000..0a7d2158d6 --- /dev/null +++ b/research/f7-weight-parity-census-correction/TASK.json @@ -0,0 +1,81 @@ +{ + "schema_version": 1, + "task_id": "joint-query-controller", + "priority_revision": 5, + "base_commit": "a23bdc1b07be96bea0101d44ae27d14de67a79ed", + "original_card": { + "id": "joint-query-controller", + "question": "Can one unresolved retention, output-consumer, hosting or physical-cost obligation complete a pinned QQ16 controller proposal within budget?", + "starting_points": [ + "docs/research/research-family-status.md", + "research/joint-query/README.md", + "research/joint-query/evidence/QQ16-SELECTED-WITNESS.md", + "research/joint-query/evidence/JOINT-QUERY-TRANSFER.md", + "docs/research/community-efficient1000-review.md" + ], + "methods": [ + "Design and price one consumer or retained-state boundary for a pinned local decoder, including final restoration.", + "Audit one claimed physical invoice or prove a target-specific obstruction for its explicit retention/hosting contract." + ], + "acceptance_gate": "Name the changed premise of the fixed F0/B1 observation obstruction. Show exactly which query is recovered, legal native reads and restoration, and a full favorable budget including cover maps, helpers and endpoints. Local inverse identities alone do not close the global controller. A scoped local result may be retained without a complete supplier. Expanding it into further construction rounds requires the family-level budget and obligation gate in research-family-status.md; an optimistic passing relaxation alone is not acceptance.", + "first_step": { + "command": [ + "python3", + "-B", + "scripts/research_baseline.py" + ], + "expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY", + "inspect": "Read the QQ16 row in research-family-status.md. The fixed-first-pair witness remains scoped; pending #2540 adds early records and claims a complete local native decoder, but not a favorable global controller. Read its output-frame and ownership contract before designing another inverse.", + "scope": "Source-bound orientation only. Read the named package and review before relying on its local evidence; this command is not its replay or supplier admission." + }, + "first_experiment": "Check live claims and pin the newest relevant completed local word (starting lead #2540). Choose one unresolved consumer, retention, whole-cover movement, hosting or precision obligation. Price the complete proposal and test that obligation; do not repeat its claimed local inverse or native reconstruction unless providing distinct independent verification.", + "bottleneck_link": "The native local words exist; retained query information and its physical access are the missing link to a composed recursive interface.", + "allowed_variations": [ + "One paid query, one changed first pair, or one precisely specified affine helper state.", + "A counterexample or lower bound for that exact proposed repair.", + "One unresolved obligation of a newer local decoder, with exact source heads and a distinct claim/domain." + ], + "scope_change_triggers": [ + "Retaining the fixed first pair and ordinary schedule while allowing only homogeneous selected-only control, already excluded by the witness.", + "Treating cover-map counts as zero-cost physical address operations.", + "Recreating the pending complete local decoder or early-record/output-bank coalescing already under investigation without a distinct audit or changed premise." + ], + "overlap_terms": [ + "QQ16", + "joint-query", + "changing-chart controller", + "selected observation", + "1969", + "2540", + "early record", + "output-bank retention", + "complete native decoder" + ] + }, + "scope_status": "changed", + "actual_scope": "User-assigned continuation and correction of PR2691: demonstrate the bit0 versus weight-parity budget mismatch; prove corrected necessary prunes; independently validate a revisit-aware C engine; fully enumerate all n=7,L=20 admissible weight splits and repair n=7,L=18 (9,9).", + "scope_change_reason": "The original session selected joint-query-controller before explicitly switching to the PR2634 audit. The user assigned this continuation after a local session reported an unsound word-census pruning premise. This session does not reroll the assigned task or contribute a QQ16 controller.", + "bottleneck_link": "A finite f(7) word-model bound used by singleton query discussions; no supplier, tariff, kappa or selected-frontier change.", + "budget_minutes": 60, + "stop_rule": "Stop after explicit counterexamples, independent small/capped gates, complete five-split L20 and one L18 census, reproducible package and public correction; no L22 census in this session.", + "outcome": "completed", + "evidence": "Fresh integrated verify.py --full passed: 164 independent small full-domain comparisons, 12 cap/input/output controls, 36 independent n7 capped comparisons, mathematical controls, direct length24 witness and six complete n7 C traversals. Exact receipts in engine/SUMMARIES.json and engine/results; second integrated run FULL-REPLAY.txt. Five L20 splits have zero hits, proving f(7)>=22 in the PR943 word model. Upper witness gives <=24. No kappa or selected-result change.", + "author": "Nicholas Stone", + "ai_assistance": "Substantial OpenAI Codex assistance, including three parallel agents for engine implementation, independent validation, and mathematics/source-impact audit. No independent human or formal-kernel review claimed.", + "open_questions": [ + { + "question": "Does a restored universal seven-label word of length 22 exist?", + "why_it_matters": "If the corrected length-20 exclusion passes, f(7) is confined to 22 or 24 within the PR943 word model.", + "first_step": "Derive and audit all L22 split reductions before a bounded feasibility probe with the corrected grouping and independent controls.", + "blocked_by": "No L22 exhaustive enumeration was attempted in this session." + }, + { + "question": "Can a separate implementation independently replay the complete n=7 L20 exclusion?", + "why_it_matters": "Small and capped independent validation does not constitute a second full n7 implementation replay.", + "first_step": "Use the stated E/O prune proof, exact root traversal scope and receipts to implement a separate full checker.", + "blocked_by": "The full six-case replay in this package uses the corrected C implementation." + } + ], + "claim_url": "https://github.com/CrocSwap/integer-mult-bounds/pull/2691#issuecomment-6218443989", + "coordination_url": "https://github.com/CrocSwap/integer-mult-bounds/discussions/1166#discussioncomment-18976359" +} diff --git a/research/f7-weight-parity-census-correction/UPPER-WITNESS.json b/research/f7-weight-parity-census-correction/UPPER-WITNESS.json new file mode 100644 index 0000000000..1aee86449f --- /dev/null +++ b/research/f7-weight-parity-census-correction/UPPER-WITNESS.json @@ -0,0 +1,7 @@ +{ + "n": 7, + "word": "010201030102010345464546", + "length": 24, + "construction": "Concatenated reflected Gray cycles on disjoint 4- and 3-coordinate blocks; reused construction of the pinned PR2542 engine.gray_word, not a new construction.", + "credit": "PR943 model: Ross Richardson. Reflected-Gray construction and engine source retained from Aaron Wang/opencode PR2542; five/six-source record Douglas Colkitt PR1185." +} diff --git a/research/f7-weight-parity-census-correction/VERIFY.json b/research/f7-weight-parity-census-correction/VERIFY.json new file mode 100644 index 0000000000..8ed805d069 --- /dev/null +++ b/research/f7-weight-parity-census-correction/VERIFY.json @@ -0,0 +1,25 @@ +{ + "schema_version": 1, + "status": "ready", + "python": "3.12", + "requirements": [], + "inputs": [ + "research/universal-f7-singleton-l20-census-complete-seedfd938f30/f7_census.c", + "research/universal-f7-singleton-l20-seed06704c39/engine.py", + "research/universal-f7-singleton-seedb859517b/engine.py" + ], + "commands": [ + { + "argv": [ + "{python}", + "-I", + "-B", + "{package}/verify.py", + "--full" + ], + "timeout_seconds": 600, + "checks": "Independent raw brute gates, n7 Python capped comparison, mathematical checks, concrete Gray length24 witness, then fresh complete corrected C enumeration of all five L20 split representatives plus L18 (9,9). Requires complete=true, exit0, exact stable receipt fields; caps are never accepted as complete." + } + ], + "notes": "C11 compiler with unsigned __int128 required (clang or GCC). No Python dependencies. Quick mode checks stored-receipt integrity and fresh small/capped controls only; it does not redo the full n7 census. Frozen predecessor packages remain unchanged." +} diff --git a/research/f7-weight-parity-census-correction/engine/README.md b/research/f7-weight-parity-census-correction/engine/README.md new file mode 100644 index 0000000000..5c9267158d --- /dev/null +++ b/research/f7-weight-parity-census-correction/engine/README.md @@ -0,0 +1,123 @@ +# Corrected Hamming-weight parity census + +This directory repairs the collision-group mismatch in the frozen predecessor +engines `audit-f7/f7_census.c` and `session2/agent-a/f7_census_revisit.c`. +Those sources are unchanged. The predecessors' fixed collision budgets were +proved for Hamming-weight parity but applied to coordinate bit 0 by default. +Consequently their zero-hit result did not establish completeness. This +implementation uses Hamming-weight parity throughout. + +The implementation retains Nicholas Stone's restricted-growth restored-word +scope and prefix-walk model, building on the PR2634 work credited to Ross +Richardson and Aaron Wang/opencode. Implementation and review involved +substantial OpenAI Codex assistance. + +## Scope and correctness argument + +A restored word of even length L uses each of N labels a positive even number +of times. Its prefix walk is `q_0=0`, `q_{i+1}=q_i XOR (1< +#include +#include +#include +#include +#include +#include +#include + +#ifndef N +#define N 7 +#endif +#ifndef L +#define L 20 +#endif +#if N < 1 || N > 7 || L < 2 || L > 20 || L % 2 +#error "Requires 1 <= N <= 7 and positive even 2 <= L <= 20" +#endif + +typedef unsigned __int128 mask_t; +typedef struct { char key[L+1]; int length; uint64_t cap; } Shard; +typedef struct { + uint64_t nodes, candidate_leaves, hits, even_prunes, odd_prunes; + int aborted; + double seconds; +} Result; +static int S,T, even_on=1,odd_on=1,even_budget,odd_budget; +static int word[L], counts[N], E[L+1],O[L+1],es=1,os=0; +static int q,used,odd_counts,even_excess,odd_excess; +static mask_t seen=1, ee,oo,eo; +static Result run; +static Shard *current; +static FILE *hitout; +static int io_error; +static int rem_even[L],rem_odd[L]; +static inline int pop(mask_t x) { + return __builtin_popcountll((uint64_t)x)+__builtin_popcountll((uint64_t)(x>>64)); +} +static inline int parity(int x) { return __builtin_popcount((unsigned)x)&1; } +static inline mask_t bit(int x) { return (mask_t)1 << x; } +static void rec(int i) { + if (run.aborted || io_error) return; + if (current->cap && run.nodes >= current->cap) { run.aborted=1; return; } + run.nodes++; + if (i==L) { + if (q || used!=N || odd_counts || es!=S || os!=T) return; + run.candidate_leaves++; + if (pop(ee|oo|eo|1)==(1<L-i) return; + int limit=used=L-2*(N-1)) continue; + if (ilength && j!=current->key[i]-'0') continue; + int nq=q^(1<S || nt>T || ns+rem_even[i]even_budget) { run.even_prunes++; continue; } + if (odd_on && odd_excess+dc_odd>odd_budget) { run.odd_prunes++; continue; } + } + mask_t old_seen=seen,old_ee=ee,old_oo=oo,old_eo=eo; + int old_q=q,old_used=used,old_odd=odd_counts; + word[i]=j; + counts[j]++; + odd_counts+=(counts[j]&1)?1:-1; + q=nq; + if (j==old_used) used++; + if (fresh) { + seen|=bit(nq); + if (p) { O[os++]=nq; oo|=add_same; } + else { E[es++]=nq; ee|=add_same; } + eo|=add_cross; + even_excess+=dc_even; odd_excess+=dc_odd; + } + rec(i+1); + if (fresh) { + if (p) os--; else es--; + even_excess-=dc_even; odd_excess-=dc_odd; + } + seen=old_seen; ee=old_ee; oo=old_oo; eo=old_eo; + counts[j]--; q=old_q; used=old_used; odd_counts=old_odd; + } +} +static int parse_uint(const char *s,uint64_t *out) { + if (!*s || *s<'0' || *s>'9') return 0; + errno=0; char *end; unsigned long long n=strtoull(s,&end,10); + if (errno || *end) return 0; + *out=(uint64_t)n; return 1; +} +static int parse_split(const char *s) { + const char *comma=strchr(s,','); + if (!comma || comma==s || strchr(comma+1,',') || comma-s>3) return 0; + char left[4]; memcpy(left,s,(size_t)(comma-s)); left[comma-s]=0; + uint64_t a,b; + if (!parse_uint(left,&a)||!parse_uint(comma+1,&b)||a<1||b<1||a>L/2||b>L/2||a>(1u<<(N-1))||b>(1u<<(N-1))) return 0; + S=(int)a; T=(int)b; return 1; +} +static int parse_shard(const char *s,Shard *dst,uint64_t cap) { + const char *colon=strchr(s,':'); + size_t len=colon?(size_t)(colon-s):strlen(s); + if (len==1 && s[0]=='-') len=0; + if (len>L || (colon && !parse_uint(colon+1,&cap))) return 0; + int introduced=0; + for (size_t k=0;k=N || label>introduced) return 0; + if (label==introduced) introduced++; + dst->key[k]=s[k]; + } + dst->key[len]=0; dst->length=(int)len; dst->cap=cap; + return 1; +} +static void write_result(FILE *f,const char *prefix,Result *r,int comma) { + fprintf(f," {\"prefix\":\"%s\",\"nodes\":%" PRIu64 ",\"candidate_restored_leaves\":%" PRIu64 ",\"all_restored_leaves\":",prefix,r->nodes,r->candidate_leaves); + if (!even_on&&!odd_on&&!r->aborted) fprintf(f,"%" PRIu64,r->candidate_leaves); else fputs("null",f); + fprintf(f,",\"hits\":%" PRIu64 ",\"even_budget_prunes\":%" PRIu64 ",\"odd_budget_prunes\":%" PRIu64 ",\"aborted\":%s,\"complete\":%s,\"seconds\":%.6f}%s\n",r->hits,r->even_prunes,r->odd_prunes,r->aborted?"true":"false",(!r->aborted&&!io_error)?"true":"false",r->seconds,comma?",":""); +} +int main(int argc,char **argv) { + Shard shards[128]; Result results[128]; int nshards=0; + const char *hits_path=NULL,*json_path=NULL; uint64_t cap=0; + if (argc<2||!parse_split(argv[1])) { fprintf(stderr,"usage: %s s,t --hits PATH [--json PATH] [--cap N] [--no-budgets|--no-even-budget|--no-odd-budget] [PREFIX[:CAP]...]\n",argv[0]); return 1; } + for (int a=2;a=argc) { fprintf(stderr,"missing option argument\n"); return 1; } + if (!strcmp(arg,"--hits")) { if(hits_path) return 1; hits_path=argv[a]; } + else if (!strcmp(arg,"--json")) { if(json_path) return 1; json_path=argv[a]; } + else if (!parse_uint(argv[a],&cap)) { fprintf(stderr,"invalid cap\n"); return 1; } + continue; + } + if (nshards==128 || !parse_shard(arg,&shards[nshards],0)) { fprintf(stderr,"invalid shard: %s\n",arg); return 1; } + nshards++; + } + if (!hits_path || (json_path&&!strcmp(hits_path,json_path))) { fprintf(stderr,"distinct explicit --hits PATH required\n"); return 1; } + if (!nshards) { memset(&shards[0],0,sizeof shards[0]); nshards=1; } + for(int a=0;alength?current->key:"-",run.nodes,run.candidate_leaves,run.hits,(!run.aborted&&!io_error)?"true":"false",run.seconds); fflush(stdout); + if (run.aborted||io_error) break; + } + printf("TOTAL N=%d L=%d split=%d,%d nodes=%" PRIu64 " candidate_restored_leaves=%" PRIu64 " hits=%" PRIu64 " complete=%s seconds=%.3f\n",N,L,S,T,total.nodes,total.candidate_leaves,total.hits,(!total.aborted&&!io_error)?"true":"false",total.seconds); + if(jsonout) { + fprintf(jsonout,"{\n \"schema\":1,\"n\":%d,\"length\":%d,\"split\":[%d,%d],\"groups\":\"hamming_weight_parity\",\"even_budget\":%d,\"odd_budget\":%d,\"even_budget_enabled\":%s,\"odd_budget_enabled\":%s,\n \"complete\":%s,\"aborted\":%s,\"requested_shards\":%d,\"completed_or_partial_shards\":%d,\n \"shards\":[\n",N,L,S,T,even_budget,odd_budget,even_on?"true":"false",odd_on?"true":"false",(!total.aborted&&!io_error)?"true":"false",total.aborted?"true":"false",nshards,done); + for(int a=0;a= 20 (no new kappa)" + }, + { + "pr": 2773, + "url": "https://github.com/CrocSwap/integer-mult-bounds/pull/2773", + "head_sha": "6739c799799621878a9249c93e2b12cc799c1482", + "merged": true, + "title": "KB3c: 17-point cover audit - PR 2542's 'no 17-point set covers F2^7' against its own lemma table (no new kappa)" + }, + { + "pr": 2870, + "url": "https://github.com/CrocSwap/integer-mult-bounds/pull/2870", + "head_sha": "9f55f291e9e79e628fd98f1e4f4debfc1c2106e5", + "merged": true, + "title": "KB3d: (11,6)/(6,11) remainder rows (5,1),(6,0) of the 17-point cover question impossible by an exact Fourier/Parseval identity (no new kappa)" + }, + { + "pr": 2911, + "url": "https://github.com/CrocSwap/integer-mult-bounds/pull/2911", + "head_sha": "cfb813f883e61519deedcd84151dc0c68b9d19cc", + "merged": true, + "title": "KB3e: the 18-point cover question for F2^7 narrowed to an exact finite remainder (outcome=partial, no new kappa)" + }, + { + "pr": 2933, + "url": "https://github.com/CrocSwap/integer-mult-bounds/pull/2933", + "head_sha": "25ea0e94250444db9ab05c3c8f1a66ba68e737b6", + "merged": true, + "title": "KB3f: the vanishing-character restriction on the 18-point cover question for F2^7 (outcome=partial, no new kappa)" + }, + { + "pr": 2961, + "url": "https://github.com/CrocSwap/integer-mult-bounds/pull/2961", + "head_sha": "df2eba6c6c3dcc88b49b3b9a9c9c2e710710d7fd", + "merged": true, + "title": "KB3g: the tight classes of the 18-point cover question for F2^7 - (7,1)/(8,2)/(6,0) PROVEN impossible, (11,7)/(9,9) gap pressed (outcome=partial, no new kappa)" + } + ] +} diff --git a/research/f7-weight-parity-census-correction/math/check_math.py b/research/f7-weight-parity-census-correction/math/check_math.py new file mode 100644 index 0000000000..2ac1478a21 --- /dev/null +++ b/research/f7-weight-parity-census-correction/math/check_math.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +"""Independent finite checks for MATH.md; no predecessor engine imports.""" +import sys +if not __debug__: + raise SystemExit("Run this mathematical checker without -O; its assertions are proof-relevant.") + +from collections import Counter +from itertools import combinations, product +from math import comb +import json + + +def rows(n, s, t): + q = 1 << (n - 1) + budget = comb(s, 2) + comb(t, 2) - (q - 1) + need = (s * t - q + 1) // 2 + if budget < 0 or s * t < q: + return [] + return [(a, b) for a in range(budget + 1) + for b in range(budget + 1 - a) if budget + a * b >= need] + + +def points(word): + q, out = 0, {0} + for j in word: + q ^= 1 << j + out.add(q) + return out + + +def normalize(word): + lab = {} + return tuple(lab.setdefault(j, len(lab)) for j in word) + + +def stats(P, functional): + E = {v for v in P if functional(v) == 0} + O = P - E + A = Counter(a ^ b for a, b in combinations(E, 2)) + B = Counter(a ^ b for a, b in combinations(O, 2)) + r = Counter(a ^ b for a in E for b in O) + a = comb(len(E), 2) - len(A) + b = comb(len(O), 2) - len(B) + c = len(E) * len(O) - len(r) + shared = set(A) & set(B) + ab = sum(A[x] * B[x] for x in shared) + assert sum(comb(v, 2) for v in r.values()) == 2 * ab + assert ab <= len(shared) + a + b + a * b + return (len(E), len(O)), a, b, c, A, B, r + + +def main(): + table = {} + for length in (18, 20): + splits = [(s, t) for s in range(1, length // 2 + 1) + for t in range(1, length // 2 + 1) if rows(7, s, t)] + table[str(length)] = splits + assert table['18'] == [(9, 9)] + assert table['20'] == [(7, 10), (8, 10), (9, 9), (9, 10), + (10, 7), (10, 8), (10, 9), (10, 10)] + checks = {} + for n, length in ((3, 6), (3, 8), (4, 8)): + restored = universal = 0 + histogram = Counter() + canonical_universal = set() + for word in product(range(n), repeat=length): + cnt = Counter(word) + if len(cnt) != n or any(v % 2 for v in cnt.values()): + continue + restored += 1 + P = points(word) + if len({a ^ b for a in P for b in P}) != 1 << n: + continue + universal += 1 + st, a, b, c, A, B, r = stats(P, lambda v: v.bit_count() % 2) + s, t = st + assert s <= length // 2 and t <= length // 2 + assert len(set(A) | set(B)) == (1 << (n - 1)) - 1 + assert a + b <= comb(s, 2) + comb(t, 2) - ((1 << (n - 1)) - 1) + assert c == s * t - (1 << (n - 1)) + assert len(r) == 1 << (n - 1) + histogram[st] += 1 + rotated = word[1:] + word[:1] + rt = stats(points(rotated), lambda v: v.bit_count() % 2)[0] + assert rt == (t, s) + if normalize(word) == word: + canonical_universal.add(word) + # The same algebra is valid for every nonzero linear functional. + for mask in range(1, 1 << n): + split2, a2, b2, c2, _, _, _ = stats( + P, lambda v, mask=mask: (v & mask).bit_count() % 2) + s2, t2 = split2 + assert a2 + b2 <= comb(s2, 2) + comb(t2, 2) - ((1 << (n - 1)) - 1) + assert c2 == s2 * t2 - (1 << (n - 1)) + rotated_canon = {normalize(w[1:] + w[:1]) for w in canonical_universal} + assert rotated_canon == canonical_universal + checks[f'n{n}_L{length}'] = dict( + restored=restored, universal=universal, + canonical_universal=len(canonical_universal), + split_histogram={f'{s},{t}': v for (s, t), v in sorted(histogram.items())}) + tight = {} + for s, t in ((9, 9), (10, 7)): + q = 64 + budget = comb(s, 2) + comb(t, 2) - 63 + need = (s * t - q + 1) // 2 + assert budget == need + # A one-unit stronger need still leaves arithmetic rows. + surviving_stronger = [(a, b) for a in range(budget + 1) + for b in range(budget + 1 - a) + if budget + a * b >= need + 1] + assert (1, 1) in surviving_stronger + tight[f'{s},{t}'] = dict(budget=budget, need=need, + max_upper=budget + budget * budget // 4, + rows_after_need_plus_one=len(surviving_stronger)) + witness = tuple(map(int, '01021323')) + P = points(witness) + assert len(P) == 8 + assert len({a ^ b for a in P for b in P}) == 16 + weight, a, b, *_ = stats(P, lambda v: v.bit_count() % 2) + bit0, a0, b0, *_ = stats(P, lambda v: v & 1) + assert weight == (4, 4) and bit0 == (6, 2) + assert a + b <= 5 and a0 + b0 == 8 + print(json.dumps(dict(admissible_splits=table, small_word_checks=checks, + tight_claim_countercheck=tight, + simple_n4_counterexample=dict(word='01021323', + weight_split=weight, weight_excess=a+b, + bit0_split=bit0, bit0_excess=a0+b0, + balanced_budget=5, + actual_bit0_budget=comb(6, 2)+comb(2, 2)-7)), indent=2)) + print('PASS_INDEPENDENT_MATH') + + +if __name__ == '__main__': + main() diff --git a/research/f7-weight-parity-census-correction/math/math-check-output.txt b/research/f7-weight-parity-census-correction/math/math-check-output.txt new file mode 100644 index 0000000000..e65606af25 --- /dev/null +++ b/research/f7-weight-parity-census-correction/math/math-check-output.txt @@ -0,0 +1,111 @@ +{ + "admissible_splits": { + "18": [ + [ + 9, + 9 + ] + ], + "20": [ + [ + 7, + 10 + ], + [ + 8, + 10 + ], + [ + 9, + 9 + ], + [ + 9, + 10 + ], + [ + 10, + 7 + ], + [ + 10, + 8 + ], + [ + 10, + 9 + ], + [ + 10, + 10 + ] + ] + }, + "small_word_checks": { + "n3_L6": { + "restored": 90, + "universal": 60, + "canonical_universal": 10, + "split_histogram": { + "2,3": 18, + "3,2": 18, + "3,3": 24 + } + }, + "n3_L8": { + "restored": 1260, + "universal": 1092, + "canonical_universal": 182, + "split_histogram": { + "2,3": 252, + "2,4": 12, + "3,2": 252, + "3,3": 408, + "3,4": 72, + "4,2": 12, + "4,3": 72, + "4,4": 12 + } + }, + "n4_L8": { + "restored": 2520, + "universal": 624, + "canonical_universal": 26, + "split_histogram": { + "3,4": 48, + "4,3": 48, + "4,4": 528 + } + } + }, + "tight_claim_countercheck": { + "9,9": { + "budget": 9, + "need": 9, + "max_upper": 29, + "rows_after_need_plus_one": 36 + }, + "10,7": { + "budget": 3, + "need": 3, + "max_upper": 5, + "rows_after_need_plus_one": 3 + } + }, + "simple_n4_counterexample": { + "word": "01021323", + "weight_split": [ + 4, + 4 + ], + "weight_excess": 0, + "bit0_split": [ + 6, + 2 + ], + "bit0_excess": 8, + "balanced_budget": 5, + "actual_bit0_budget": 9 + } +} +PASS_INDEPENDENT_MATH diff --git a/research/f7-weight-parity-census-correction/validation/REVIEW.md b/research/f7-weight-parity-census-correction/validation/REVIEW.md new file mode 100644 index 0000000000..17a9c188d6 --- /dev/null +++ b/research/f7-weight-parity-census-correction/validation/REVIEW.md @@ -0,0 +1,99 @@ +# Independent validation of the parity correction + +The generic fixed collision budget applied to coordinate-bit-0 groups is +unsound. The corrected C engine passes the independent gates below. These +small counterexamples invalidate the old pruning premise; they do **not** +constitute an n=7, L=20 universal word or by themselves settle that census. + +## Explicit counterexamples + +The canonical restored word `001212` at n=3 has prefix path +`[0,1,0,2,6,4]` and distinct states `{0,1,2,4,6}`. Direct pairwise XOR covers +all eight masks. Its Hamming-weight groups are E=`{0,6}`, O=`{1,2,4}`, +so the target split is (2,3) and the fixed even budget is +`C(2,2)+C(3,2)-(4-1)=1`. Its weight-group collision excess is 0. Its bit-0 +groups have sizes (4,1): the size-four group has six pairs but only three +pair-XOR values `{2,4,6}`, giving excess 3. The old rule rejects this universal +word because 3 > 1. + +The balanced, self-avoiding word `01021323` at n=4 is a stronger regression +for the original simple-walk use case. Its prefix path consists of eight +distinct states `[0,1,3,2,6,4,12,8]`; it is restored and its pairwise XOR +covers all sixteen masks. Its weight split is (4,4), with budget +`2*C(4,2)-(8-1)=5`. Its bit-0 split is (6,2). The size-six group has fifteen +pairs and seven distinct XORs; the size-two group has one pair and one XOR, +so the old-rule excess is 8 > 5. In the complete n=4, L=8, (4,4) brute census, +22 canonical words are universal and 6 of those violate the old bit-0 rule. + +For weight groups, universality forces `supp(EE) union supp(OO)` to cover all +`2^(n-1)-1` nonzero even-weight masks. Therefore +`C(s,2)-|supp(EE)|+C(t,2)-|supp(OO)| <= C(s,2)+C(t,2)-(2^(n-1)-1)`. +This proof uses s,t as **weight-group** cardinalities. A bit-0 partition also +has an analogous bound, but its pair capacities must use its actual group +cardinalities, which the target weight split does not fix. Likewise, the +correct odd-weight bound is `s*t-|supp(EO)| <= s*t-2^(n-1)`. + +## Independent gates + +`brute_validation.py` enumerates every raw word, tests positive even label +multiplicities directly, canonicalizes only for counting, and computes +universality by the literal set `{a XOR b : a,b in P}`. It imports no census +engine. Results are in `brute_results.json`. + +| n | L | Raw words examined | Canonical restored | Canonical universal | +|---|---|---|---|---| +| 3 | 6 | 729 | 15 | 10 | +| 3 | 8 | 6561 | 210 | 182 | +| 4 | 8 | 65536 | 105 | 26 | + +`check_corrected_c.py` compares every split in these three cases, including +zero-hit targets, in four budget modes (both, even only, odd only, none): +**164 successful comparisons**. All universal counts match. Every emitted +hit is independently checked for length, canonical form, restoration, target +weight split, direct XOR coverage, and uniqueness. Exact all-restored leaf +counts are compared only with budgets disabled; budgeted candidate leaf +counts are intentionally not equated with an unpruned histogram. + +It also passes **12 fail-closed checks**: caps 1, 10 and 50 return exit 2, +`aborted=true`, `complete=false`, and exactly the allowed nodes; malformed +inputs, missing/unopenable hit output, overlapping shards, and invalid caps +return exit 1; an existing hit file remains unchanged. The receipt is +`corrected_c_results.json`. A caller must require successful exit **and** +`complete=true` before treating a run as a complete exclusion. + +`crosscheck_n7.py` supplies a second Python reference with the C traversal's +node semantics. At each novel state it recomputes all pair-support sets from +scratch, instead of copying C's bitset and incremental collision bookkeeping. +It compares n=7 L=20 splits (10,10), (10,9), (10,8), (9,9), (10,7), plus +n=7 L=18 split (9,9), each at prefixes `0012`, `0102`, `0123` with both +budgets and with the even budget alone. All **36 capped comparisons** match +exactly in nodes, candidate leaves, hits, both prune counters, and abort +status. The cap is 5000 per comparison. These are cross-checks, not complete +n=7 census claims. Receipt: `n7_crosscheck_results.json`. + +The earlier `agent-b/slow_census.py` has different node and leaf semantics: +it gates only the universal test with budgets and retains all restored +leaves. Its leaf histogram therefore must not be equated to C's budgeted +candidate leaves. The independent n=7 reference here makes the traversal +semantics explicit before comparing counters. + +## Reproduction + +From the directory containing sibling `engine/` and `validation/` directories: + +```sh +python3 -I -B -O validation/brute_validation.py +python3 -I -B -O validation/check_corrected_c.py +python3 -I -B -O validation/crosscheck_n7.py +``` + +All three default to fresh system temporary output directories and leave the +package untouched. Each accepts `--output-dir PATH` to retain outputs in a +chosen directory. The two C gate scripts accept `--engine-source PATH`; +otherwise they locate sibling `engine/corrected_census.c`. They require a C11 +compiler available as `cc`. Critical checks use explicit exceptions, so +Python `-O` does not disable validation. The three commands were executed +successfully with `-I -B -O`. + +Freeze scope: the three Python source files, the three JSON receipts, and +this report. Compiled programs and caches are excluded. diff --git a/research/f7-weight-parity-census-correction/validation/brute_results.json b/research/f7-weight-parity-census-correction/validation/brute_results.json new file mode 100644 index 0000000000..532f022d3e --- /dev/null +++ b/research/f7-weight-parity-census-correction/validation/brute_results.json @@ -0,0 +1,486 @@ +{ + "method": "raw itertools.product, no pruning; direct XOR-set coverage", + "witnesses": [ + { + "word": "001212", + "n": 3, + "L": 6, + "counts": [ + 2, + 2, + 2 + ], + "prefix_path": [ + 0, + 1, + 0, + 2, + 6, + 4 + ], + "states": [ + 0, + 1, + 2, + 4, + 6 + ], + "restored": true, + "coverage": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7 + ], + "universal": true, + "groups": { + "weight": { + "groups": [ + [ + 0, + 6 + ], + [ + 1, + 2, + 4 + ] + ], + "split": [ + 2, + 3 + ], + "pairs": [ + 1, + 3 + ], + "supports": [ + [ + 6 + ], + [ + 3, + 5, + 6 + ] + ], + "collisions": 0 + }, + "bit0": { + "groups": [ + [ + 0, + 2, + 4, + 6 + ], + [ + 1 + ] + ], + "split": [ + 4, + 1 + ], + "pairs": [ + 6, + 0 + ], + "supports": [ + [ + 2, + 4, + 6 + ], + [] + ], + "collisions": 3 + } + }, + "weight_budget": 1, + "old_bit0_rule_rejects": true + }, + { + "word": "01021323", + "n": 4, + "L": 8, + "counts": [ + 2, + 2, + 2, + 2 + ], + "prefix_path": [ + 0, + 1, + 3, + 2, + 6, + 4, + 12, + 8 + ], + "states": [ + 0, + 1, + 2, + 3, + 4, + 6, + 8, + 12 + ], + "restored": true, + "coverage": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15 + ], + "universal": true, + "groups": { + "weight": { + "groups": [ + [ + 0, + 3, + 6, + 12 + ], + [ + 1, + 2, + 4, + 8 + ] + ], + "split": [ + 4, + 4 + ], + "pairs": [ + 6, + 6 + ], + "supports": [ + [ + 3, + 5, + 6, + 10, + 12, + 15 + ], + [ + 3, + 5, + 6, + 9, + 10, + 12 + ] + ], + "collisions": 0 + }, + "bit0": { + "groups": [ + [ + 0, + 2, + 4, + 6, + 8, + 12 + ], + [ + 1, + 3 + ] + ], + "split": [ + 6, + 2 + ], + "pairs": [ + 15, + 1 + ], + "supports": [ + [ + 2, + 4, + 6, + 8, + 10, + 12, + 14 + ], + [ + 2 + ] + ], + "collisions": 8 + } + }, + "weight_budget": 5, + "old_bit0_rule_rejects": true + } + ], + "cases": [ + { + "n": 3, + "L": 6, + "words_scanned": 729, + "raw_restored": 90, + "raw_universal": 60, + "canonical_restored": 15, + "canonical_universal": 10, + "splits": { + "1,1": { + "restored": 0, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "1,2": { + "restored": 0, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "1,3": { + "restored": 1, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "2,1": { + "restored": 0, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "2,2": { + "restored": 3, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "2,3": { + "restored": 3, + "universal": 3, + "old_bit0_rule_rejects_universal": 1 + }, + "3,1": { + "restored": 1, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "3,2": { + "restored": 3, + "universal": 3, + "old_bit0_rule_rejects_universal": 1 + }, + "3,3": { + "restored": 4, + "universal": 4, + "old_bit0_rule_rejects_universal": 0 + } + } + }, + { + "n": 3, + "L": 8, + "words_scanned": 6561, + "raw_restored": 1260, + "raw_universal": 1092, + "canonical_restored": 210, + "canonical_universal": 182, + "splits": { + "1,1": { + "restored": 0, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "1,2": { + "restored": 0, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "1,3": { + "restored": 6, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "1,4": { + "restored": 0, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "2,1": { + "restored": 0, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "2,2": { + "restored": 16, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "2,3": { + "restored": 42, + "universal": 42, + "old_bit0_rule_rejects_universal": 11 + }, + "2,4": { + "restored": 2, + "universal": 2, + "old_bit0_rule_rejects_universal": 0 + }, + "3,1": { + "restored": 6, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "3,2": { + "restored": 42, + "universal": 42, + "old_bit0_rule_rejects_universal": 11 + }, + "3,3": { + "restored": 68, + "universal": 68, + "old_bit0_rule_rejects_universal": 0 + }, + "3,4": { + "restored": 12, + "universal": 12, + "old_bit0_rule_rejects_universal": 0 + }, + "4,1": { + "restored": 0, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "4,2": { + "restored": 2, + "universal": 2, + "old_bit0_rule_rejects_universal": 0 + }, + "4,3": { + "restored": 12, + "universal": 12, + "old_bit0_rule_rejects_universal": 0 + }, + "4,4": { + "restored": 2, + "universal": 2, + "old_bit0_rule_rejects_universal": 0 + } + } + }, + { + "n": 4, + "L": 8, + "words_scanned": 65536, + "raw_restored": 2520, + "raw_universal": 624, + "canonical_restored": 105, + "canonical_universal": 26, + "splits": { + "1,1": { + "restored": 0, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "1,2": { + "restored": 0, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "1,3": { + "restored": 0, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "1,4": { + "restored": 1, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "2,1": { + "restored": 0, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "2,2": { + "restored": 0, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "2,3": { + "restored": 6, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "2,4": { + "restored": 6, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "3,1": { + "restored": 0, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "3,2": { + "restored": 6, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "3,3": { + "restored": 16, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "3,4": { + "restored": 18, + "universal": 2, + "old_bit0_rule_rejects_universal": 2 + }, + "4,1": { + "restored": 1, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "4,2": { + "restored": 6, + "universal": 0, + "old_bit0_rule_rejects_universal": 0 + }, + "4,3": { + "restored": 18, + "universal": 2, + "old_bit0_rule_rejects_universal": 2 + }, + "4,4": { + "restored": 27, + "universal": 22, + "old_bit0_rule_rejects_universal": 6 + } + } + } + ], + "scope": "Counterexamples disprove the generic bit0 pruning rule; they do not exhibit an n7 L20 universal word." +} diff --git a/research/f7-weight-parity-census-correction/validation/brute_validation.py b/research/f7-weight-parity-census-correction/validation/brute_validation.py new file mode 100644 index 0000000000..4c11e64695 --- /dev/null +++ b/research/f7-weight-parity-census-correction/validation/brute_validation.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +"""Independent raw-product ground truth and explicit bit-0 counterexamples. + +No census implementation is imported. Every word is enumerated, reduced to +restricted-growth form only for counting, then checked directly by P XOR P. +Run: python3 session2/review/validation/brute_validation.py +""" +from collections import Counter +from itertools import product +from pathlib import Path +import argparse +import json +import tempfile + +HERE = Path(__file__).resolve().parent + + +def canonical(word): + seen = {} + out = [] + for label in word: + if label not in seen: + seen[label] = len(seen) + out.append(seen[label]) + return tuple(out) + + +def describe(word, n): + counts = [word.count(label) for label in range(n)] + q = 0 + path = [] + for label in word: + path.append(q) + q ^= 1 << label + states = sorted(set(path)) + coverage = sorted({a ^ b for a in states for b in states}) + stats = {} + for mode in ("weight", "bit0"): + groups = [[x for x in states if + (x.bit_count() & 1 if mode == "weight" else x & 1) == p] + for p in (0, 1)] + supports = [sorted({a ^ b for i, a in enumerate(g) for b in g[i+1:]}) + for g in groups] + pairs = [len(g) * (len(g)-1) // 2 for g in groups] + stats[mode] = {"groups": groups, "split": [len(g) for g in groups], + "pairs": pairs, "supports": supports, + "collisions": sum(pairs) - sum(map(len, supports))} + s, t = stats["weight"]["split"] + budget = s * (s-1) // 2 + t * (t-1) // 2 - ((1 << (n-1))-1) + return {"word": "".join(map(str, word)), "n": n, "L": len(word), + "counts": counts, "prefix_path": path, "states": states, + "restored": q == 0 and all(c > 0 and c % 2 == 0 for c in counts), + "coverage": coverage, "universal": len(coverage) == (1 << n), + "groups": stats, "weight_budget": budget, + "old_bit0_rule_rejects": stats["bit0"]["collisions"] > budget} + + +def brute(n, length): + restored = Counter() + universal = Counter() + old_rule_rejects_universal = Counter() + raw_restored = raw_universal = 0 + words_scanned = 0 + for word in product(range(n), repeat=length): + words_scanned += 1 + counts = [word.count(label) for label in range(n)] + if not all(c > 0 and c % 2 == 0 for c in counts): + continue + raw_restored += 1 + if canonical(word) != word: + # Raw universality count does not rely on the canonical test. + q = 0 + states = set() + for label in word: + states.add(q) + q ^= 1 << label + if len({a ^ b for a in states for b in states}) == 1 << n: + raw_universal += 1 + continue + details = describe(word, n) + split = ",".join(map(str, details["groups"]["weight"]["split"])) + restored[split] += 1 + if details["universal"]: + universal[split] += 1 + raw_universal += 1 + if details["old_bit0_rule_rejects"]: + old_rule_rejects_universal[split] += 1 + # Include zero-hit feasible target splits, as well as all observed splits. + splits = {f"{s},{t}": {"restored": restored[f"{s},{t}"], + "universal": universal[f"{s},{t}"], + "old_bit0_rule_rejects_universal": + old_rule_rejects_universal[f"{s},{t}"]} + for s in range(1, length // 2 + 1) + for t in range(1, length // 2 + 1)} + return {"n": n, "L": length, "words_scanned": words_scanned, + "raw_restored": raw_restored, "raw_universal": raw_universal, + "canonical_restored": sum(restored.values()), + "canonical_universal": sum(universal.values()), "splits": splits} + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--output-dir", type=Path) + args = parser.parse_args() + output_dir = args.output_dir or Path(tempfile.mkdtemp(prefix="f7-brute-validation-")) + output_dir.mkdir(parents=True, exist_ok=True) + results = { + "method": "raw itertools.product, no pruning; direct XOR-set coverage", + "witnesses": [describe(tuple(map(int, "001212")), 3), + describe(tuple(map(int, "01021323")), 4)], + "cases": [brute(3, 6), brute(3, 8), brute(4, 8)], + "scope": "Counterexamples disprove the generic bit0 pruning rule; they do not exhibit an n7 L20 universal word." + } + for witness in results["witnesses"]: + if not (witness["restored"] and witness["universal"] and witness["old_bit0_rule_rejects"]): + raise RuntimeError(witness) + target = output_dir / "brute_results.json" + target.write_text(json.dumps(results, indent=2) + "\n") + print(target) + for case in results["cases"]: + print(f"n={case['n']} L={case['L']} canonical restored={case['canonical_restored']} universal={case['canonical_universal']}") + + +if __name__ == "__main__": + main() diff --git a/research/f7-weight-parity-census-correction/validation/check_corrected_c.py b/research/f7-weight-parity-census-correction/validation/check_corrected_c.py new file mode 100644 index 0000000000..7ca76aeff9 --- /dev/null +++ b/research/f7-weight-parity-census-correction/validation/check_corrected_c.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +"""Compile corrected C, compare all small splits with raw-product brute. + +Budgeted candidate leaf counts are deliberately NOT equated to all restored +leaf counts. All four budget modes must give the brute universal count. +""" +import argparse +import json +import subprocess +import tempfile +import sys +from pathlib import Path + +HERE = Path(__file__).resolve().parent +sys.path.insert(0, str(HERE)) +from brute_validation import canonical, describe, brute + + +def require(condition, detail): + if not condition: + raise RuntimeError(detail) + + +def run(binary, split, directory, mode="both", extra=()): + flags = {"both": [], "even": ["--no-odd-budget"], + "odd": ["--no-even-budget"], "none": ["--no-budgets"]}[mode] + hitfile, datafile = directory / "hits.txt", directory / "result.json" + command = [str(binary), split, "--hits", str(hitfile), "--json", + str(datafile), *flags, *extra] + proc = subprocess.run(command, capture_output=True, text=True) + data = json.loads(datafile.read_text()) if datafile.exists() else None + words = hitfile.read_text().splitlines() if hitfile.exists() else [] + return proc, data, words + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--engine-source", type=Path, default=HERE.parent / "engine" / "corrected_census.c") + parser.add_argument("--output-dir", type=Path) + args = parser.parse_args() + output_dir = args.output_dir or Path(tempfile.mkdtemp(prefix="f7-validation-")) + output_dir.mkdir(parents=True, exist_ok=True) + source = args.engine_source.resolve() + ground = {"method": "raw itertools.product, no pruning; direct XOR-set coverage", + "cases": [brute(3, 6), brute(3, 8), brute(4, 8)]} + records = [] + binaries = {} + for case in ground["cases"]: + n, length = case["n"], case["L"] + binary = output_dir / f"corrected_n{n}_l{length}" + compile_command = ["cc", "-O2", "-std=c11", "-Wall", "-Wextra", "-Werror", + f"-DN={n}", f"-DL={length}", str(source), "-o", str(binary)] + subprocess.run(compile_command, check=True, capture_output=True, text=True) + binaries[n, length] = binary + for split, expected in case["splits"].items(): + for mode in ("both", "even", "odd", "none"): + with tempfile.TemporaryDirectory(prefix="cgate-", dir=output_dir) as temporary: + proc, data, words = run(binary, split, Path(temporary), mode) + require(proc.returncode == 0, (n, length, split, mode, proc.stderr)) + require(data["complete"] and not data["aborted"], data) + total = data["total"][0] + require(total["hits"] == expected["universal"], (n, length, split, mode, total, expected)) + require(len(words) == len(set(words)) == total["hits"], (n, length, split, mode)) + for text in words: + word = tuple(map(int, text)) + details = describe(word, n) + require(len(word) == length and canonical(word) == word, text) + require(details["restored"] and details["universal"], text) + require(",".join(map(str, details["groups"]["weight"]["split"])) == split, text) + if mode == "none": + require(total["all_restored_leaves"] == expected["restored"], (n, length, split, total, expected)) + else: + require(total["all_restored_leaves"] is None, (n, length, split, mode)) + records.append({"n": n, "L": length, "split": split, + "mode": mode, "nodes": total["nodes"], + "candidate_restored_leaves": total["candidate_restored_leaves"], + "universal": total["hits"], "pass": True}) + safety = [] + binary = binaries[4, 8] + for cap in (1, 10, 50): + with tempfile.TemporaryDirectory(prefix="capgate-", dir=output_dir) as temporary: + proc, data, words = run(binary, "4,4", Path(temporary), extra=("--cap", str(cap))) + require(proc.returncode == 2 and data["aborted"] and not data["complete"], (cap, proc, data)) + require(data["total"][0]["nodes"] == cap, data) + require(data["total"][0]["all_restored_leaves"] is None, data) + safety.append({"check": "capped_run", "cap": cap, "returncode": proc.returncode, + "complete": data["complete"], "nodes": data["total"][0]["nodes"], "pass": True}) + invalid = [[], ["4,4"], ["4,4", "--hits", "/no/such/path/hits"], + ["4,4", "--hits", "/tmp/unused-census-output", "00", "000"], + ["4,4", "--hits", "/tmp/unused-census-output", "0124"], + ["4,4", "--hits", "/tmp/unused-census-output", "0123:abc"], + ["5,4", "--hits", "/tmp/unused-census-output"], + ["4,4", "--hits", "/tmp/unused-census-output", "--cap", "-1"]] + for arguments in invalid: + proc = subprocess.run([str(binary), *arguments], capture_output=True, text=True) + require(proc.returncode == 1, (arguments, proc)) + require("complete=true" not in proc.stdout, proc) + safety.append({"check": "invalid_input", "arguments": arguments, + "returncode": proc.returncode, "pass": True}) + with tempfile.TemporaryDirectory(prefix="filegate-", dir=output_dir) as temporary: + directory = Path(temporary) + existing = directory / "hits.txt" + existing.write_text("preserve me\n") + proc = subprocess.run([str(binary), "4,4", "--hits", str(existing)], + capture_output=True, text=True) + require(proc.returncode == 1 and existing.read_text() == "preserve me\n", proc) + safety.append({"check": "existing_hits_file_not_overwritten", "returncode": proc.returncode, "pass": True}) + output = {"source": str(source), "method": ground["method"], + "comparison": "all splits, universal counts; all restored leaves only when no budgets", + "runs": records, "safety": safety, "pass": True} + target = output_dir / "corrected_c_results.json" + target.write_text(json.dumps(output, indent=2) + "\n") + print(f"PASS: {len(records)} brute comparisons; {len(safety)} fail-closed checks; {target}") + + +if __name__ == "__main__": + main() diff --git a/research/f7-weight-parity-census-correction/validation/corrected_c_results.json b/research/f7-weight-parity-census-correction/validation/corrected_c_results.json new file mode 100644 index 0000000000..d639fed02e --- /dev/null +++ b/research/f7-weight-parity-census-correction/validation/corrected_c_results.json @@ -0,0 +1,1759 @@ +{ + "source": "/Users/nicholas/workspace/integer-mult-bounds/session2/review/engine/corrected_census.c", + "method": "raw itertools.product, no pruning; direct XOR-set coverage", + "comparison": "all splits, universal counts; all restored leaves only when no budgets", + "runs": [ + { + "n": 3, + "L": 6, + "split": "1,1", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "1,1", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "1,1", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "1,1", + "mode": "none", + "nodes": 3, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "1,2", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "1,2", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "1,2", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "1,2", + "mode": "none", + "nodes": 5, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "1,3", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "1,3", + "mode": "even", + "nodes": 7, + "candidate_restored_leaves": 1, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "1,3", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "1,3", + "mode": "none", + "nodes": 7, + "candidate_restored_leaves": 1, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "2,1", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "2,1", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "2,1", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "2,1", + "mode": "none", + "nodes": 6, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "2,2", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "2,2", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "2,2", + "mode": "odd", + "nodes": 16, + "candidate_restored_leaves": 3, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "2,2", + "mode": "none", + "nodes": 18, + "candidate_restored_leaves": 3, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "2,3", + "mode": "both", + "nodes": 16, + "candidate_restored_leaves": 3, + "universal": 3, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "2,3", + "mode": "even", + "nodes": 16, + "candidate_restored_leaves": 3, + "universal": 3, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "2,3", + "mode": "odd", + "nodes": 16, + "candidate_restored_leaves": 3, + "universal": 3, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "2,3", + "mode": "none", + "nodes": 16, + "candidate_restored_leaves": 3, + "universal": 3, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "3,1", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "3,1", + "mode": "even", + "nodes": 7, + "candidate_restored_leaves": 1, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "3,1", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "3,1", + "mode": "none", + "nodes": 7, + "candidate_restored_leaves": 1, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "3,2", + "mode": "both", + "nodes": 16, + "candidate_restored_leaves": 3, + "universal": 3, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "3,2", + "mode": "even", + "nodes": 16, + "candidate_restored_leaves": 3, + "universal": 3, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "3,2", + "mode": "odd", + "nodes": 16, + "candidate_restored_leaves": 3, + "universal": 3, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "3,2", + "mode": "none", + "nodes": 16, + "candidate_restored_leaves": 3, + "universal": 3, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "3,3", + "mode": "both", + "nodes": 16, + "candidate_restored_leaves": 4, + "universal": 4, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "3,3", + "mode": "even", + "nodes": 16, + "candidate_restored_leaves": 4, + "universal": 4, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "3,3", + "mode": "odd", + "nodes": 16, + "candidate_restored_leaves": 4, + "universal": 4, + "pass": true + }, + { + "n": 3, + "L": 6, + "split": "3,3", + "mode": "none", + "nodes": 16, + "candidate_restored_leaves": 4, + "universal": 4, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "1,1", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "1,1", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "1,1", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "1,1", + "mode": "none", + "nodes": 5, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "1,2", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "1,2", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "1,2", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "1,2", + "mode": "none", + "nodes": 16, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "1,3", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "1,3", + "mode": "even", + "nodes": 27, + "candidate_restored_leaves": 6, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "1,3", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "1,3", + "mode": "none", + "nodes": 27, + "candidate_restored_leaves": 6, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "1,4", + "mode": "both", + "nodes": 7, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "1,4", + "mode": "even", + "nodes": 7, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "1,4", + "mode": "odd", + "nodes": 7, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "1,4", + "mode": "none", + "nodes": 7, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "2,1", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "2,1", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "2,1", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "2,1", + "mode": "none", + "nodes": 19, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "2,2", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "2,2", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "2,2", + "mode": "odd", + "nodes": 78, + "candidate_restored_leaves": 16, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "2,2", + "mode": "none", + "nodes": 119, + "candidate_restored_leaves": 16, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "2,3", + "mode": "both", + "nodes": 172, + "candidate_restored_leaves": 42, + "universal": 42, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "2,3", + "mode": "even", + "nodes": 172, + "candidate_restored_leaves": 42, + "universal": 42, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "2,3", + "mode": "odd", + "nodes": 172, + "candidate_restored_leaves": 42, + "universal": 42, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "2,3", + "mode": "none", + "nodes": 172, + "candidate_restored_leaves": 42, + "universal": 42, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "2,4", + "mode": "both", + "nodes": 37, + "candidate_restored_leaves": 2, + "universal": 2, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "2,4", + "mode": "even", + "nodes": 37, + "candidate_restored_leaves": 2, + "universal": 2, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "2,4", + "mode": "odd", + "nodes": 37, + "candidate_restored_leaves": 2, + "universal": 2, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "2,4", + "mode": "none", + "nodes": 37, + "candidate_restored_leaves": 2, + "universal": 2, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "3,1", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "3,1", + "mode": "even", + "nodes": 32, + "candidate_restored_leaves": 6, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "3,1", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "3,1", + "mode": "none", + "nodes": 32, + "candidate_restored_leaves": 6, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "3,2", + "mode": "both", + "nodes": 183, + "candidate_restored_leaves": 42, + "universal": 42, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "3,2", + "mode": "even", + "nodes": 183, + "candidate_restored_leaves": 42, + "universal": 42, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "3,2", + "mode": "odd", + "nodes": 183, + "candidate_restored_leaves": 42, + "universal": 42, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "3,2", + "mode": "none", + "nodes": 183, + "candidate_restored_leaves": 42, + "universal": 42, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "3,3", + "mode": "both", + "nodes": 265, + "candidate_restored_leaves": 68, + "universal": 68, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "3,3", + "mode": "even", + "nodes": 265, + "candidate_restored_leaves": 68, + "universal": 68, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "3,3", + "mode": "odd", + "nodes": 265, + "candidate_restored_leaves": 68, + "universal": 68, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "3,3", + "mode": "none", + "nodes": 265, + "candidate_restored_leaves": 68, + "universal": 68, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "3,4", + "mode": "both", + "nodes": 75, + "candidate_restored_leaves": 12, + "universal": 12, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "3,4", + "mode": "even", + "nodes": 75, + "candidate_restored_leaves": 12, + "universal": 12, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "3,4", + "mode": "odd", + "nodes": 75, + "candidate_restored_leaves": 12, + "universal": 12, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "3,4", + "mode": "none", + "nodes": 75, + "candidate_restored_leaves": 12, + "universal": 12, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "4,1", + "mode": "both", + "nodes": 6, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "4,1", + "mode": "even", + "nodes": 6, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "4,1", + "mode": "odd", + "nodes": 6, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "4,1", + "mode": "none", + "nodes": 6, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "4,2", + "mode": "both", + "nodes": 28, + "candidate_restored_leaves": 2, + "universal": 2, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "4,2", + "mode": "even", + "nodes": 28, + "candidate_restored_leaves": 2, + "universal": 2, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "4,2", + "mode": "odd", + "nodes": 28, + "candidate_restored_leaves": 2, + "universal": 2, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "4,2", + "mode": "none", + "nodes": 28, + "candidate_restored_leaves": 2, + "universal": 2, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "4,3", + "mode": "both", + "nodes": 57, + "candidate_restored_leaves": 12, + "universal": 12, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "4,3", + "mode": "even", + "nodes": 57, + "candidate_restored_leaves": 12, + "universal": 12, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "4,3", + "mode": "odd", + "nodes": 57, + "candidate_restored_leaves": 12, + "universal": 12, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "4,3", + "mode": "none", + "nodes": 57, + "candidate_restored_leaves": 12, + "universal": 12, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "4,4", + "mode": "both", + "nodes": 22, + "candidate_restored_leaves": 2, + "universal": 2, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "4,4", + "mode": "even", + "nodes": 22, + "candidate_restored_leaves": 2, + "universal": 2, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "4,4", + "mode": "odd", + "nodes": 22, + "candidate_restored_leaves": 2, + "universal": 2, + "pass": true + }, + { + "n": 3, + "L": 8, + "split": "4,4", + "mode": "none", + "nodes": 22, + "candidate_restored_leaves": 2, + "universal": 2, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "1,1", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "1,1", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "1,1", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "1,1", + "mode": "none", + "nodes": 3, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "1,2", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "1,2", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "1,2", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "1,2", + "mode": "none", + "nodes": 5, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "1,3", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "1,3", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "1,3", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "1,3", + "mode": "none", + "nodes": 7, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "1,4", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "1,4", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "1,4", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "1,4", + "mode": "none", + "nodes": 9, + "candidate_restored_leaves": 1, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "2,1", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "2,1", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "2,1", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "2,1", + "mode": "none", + "nodes": 6, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "2,2", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "2,2", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "2,2", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "2,2", + "mode": "none", + "nodes": 19, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "2,3", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "2,3", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "2,3", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "2,3", + "mode": "none", + "nodes": 43, + "candidate_restored_leaves": 6, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "2,4", + "mode": "both", + "nodes": 15, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "2,4", + "mode": "even", + "nodes": 35, + "candidate_restored_leaves": 6, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "2,4", + "mode": "odd", + "nodes": 15, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "2,4", + "mode": "none", + "nodes": 35, + "candidate_restored_leaves": 6, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "3,1", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "3,1", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "3,1", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "3,1", + "mode": "none", + "nodes": 9, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "3,2", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "3,2", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "3,2", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "3,2", + "mode": "none", + "nodes": 47, + "candidate_restored_leaves": 6, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "3,3", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "3,3", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "3,3", + "mode": "odd", + "nodes": 24, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "3,3", + "mode": "none", + "nodes": 95, + "candidate_restored_leaves": 16, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "3,4", + "mode": "both", + "nodes": 40, + "candidate_restored_leaves": 2, + "universal": 2, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "3,4", + "mode": "even", + "nodes": 80, + "candidate_restored_leaves": 18, + "universal": 2, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "3,4", + "mode": "odd", + "nodes": 40, + "candidate_restored_leaves": 2, + "universal": 2, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "3,4", + "mode": "none", + "nodes": 80, + "candidate_restored_leaves": 18, + "universal": 2, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "4,1", + "mode": "both", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "4,1", + "mode": "even", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "4,1", + "mode": "odd", + "nodes": 0, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "4,1", + "mode": "none", + "nodes": 9, + "candidate_restored_leaves": 1, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "4,2", + "mode": "both", + "nodes": 11, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "4,2", + "mode": "even", + "nodes": 37, + "candidate_restored_leaves": 6, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "4,2", + "mode": "odd", + "nodes": 11, + "candidate_restored_leaves": 0, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "4,2", + "mode": "none", + "nodes": 37, + "candidate_restored_leaves": 6, + "universal": 0, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "4,3", + "mode": "both", + "nodes": 39, + "candidate_restored_leaves": 2, + "universal": 2, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "4,3", + "mode": "even", + "nodes": 85, + "candidate_restored_leaves": 18, + "universal": 2, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "4,3", + "mode": "odd", + "nodes": 41, + "candidate_restored_leaves": 2, + "universal": 2, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "4,3", + "mode": "none", + "nodes": 87, + "candidate_restored_leaves": 18, + "universal": 2, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "4,4", + "mode": "both", + "nodes": 81, + "candidate_restored_leaves": 22, + "universal": 22, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "4,4", + "mode": "even", + "nodes": 89, + "candidate_restored_leaves": 26, + "universal": 22, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "4,4", + "mode": "odd", + "nodes": 83, + "candidate_restored_leaves": 23, + "universal": 22, + "pass": true + }, + { + "n": 4, + "L": 8, + "split": "4,4", + "mode": "none", + "nodes": 91, + "candidate_restored_leaves": 27, + "universal": 22, + "pass": true + } + ], + "safety": [ + { + "check": "capped_run", + "cap": 1, + "returncode": 2, + "complete": false, + "nodes": 1, + "pass": true + }, + { + "check": "capped_run", + "cap": 10, + "returncode": 2, + "complete": false, + "nodes": 10, + "pass": true + }, + { + "check": "capped_run", + "cap": 50, + "returncode": 2, + "complete": false, + "nodes": 50, + "pass": true + }, + { + "check": "invalid_input", + "arguments": [], + "returncode": 1, + "pass": true + }, + { + "check": "invalid_input", + "arguments": [ + "4,4" + ], + "returncode": 1, + "pass": true + }, + { + "check": "invalid_input", + "arguments": [ + "4,4", + "--hits", + "/no/such/path/hits" + ], + "returncode": 1, + "pass": true + }, + { + "check": "invalid_input", + "arguments": [ + "4,4", + "--hits", + "/tmp/unused-census-output", + "00", + "000" + ], + "returncode": 1, + "pass": true + }, + { + "check": "invalid_input", + "arguments": [ + "4,4", + "--hits", + "/tmp/unused-census-output", + "0124" + ], + "returncode": 1, + "pass": true + }, + { + "check": "invalid_input", + "arguments": [ + "4,4", + "--hits", + "/tmp/unused-census-output", + "0123:abc" + ], + "returncode": 1, + "pass": true + }, + { + "check": "invalid_input", + "arguments": [ + "5,4", + "--hits", + "/tmp/unused-census-output" + ], + "returncode": 1, + "pass": true + }, + { + "check": "invalid_input", + "arguments": [ + "4,4", + "--hits", + "/tmp/unused-census-output", + "--cap", + "-1" + ], + "returncode": 1, + "pass": true + }, + { + "check": "existing_hits_file_not_overwritten", + "returncode": 1, + "pass": true + } + ], + "pass": true +} diff --git a/research/f7-weight-parity-census-correction/validation/crosscheck_n7.py b/research/f7-weight-parity-census-correction/validation/crosscheck_n7.py new file mode 100644 index 0000000000..a6e680b0b4 --- /dev/null +++ b/research/f7-weight-parity-census-correction/validation/crosscheck_n7.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 +"""Capped n=7 C/Python cross-check with matching DFS/node semantics. + +The Python oracle recomputes every pair-support set from scratch at each novel +prefix. It does not use the C bitsets, incremental pair collision bookkeeping, +or the earlier slow engine's soft budget flags. +""" +import argparse +import json +from pathlib import Path +import subprocess +import tempfile + +HERE = Path(__file__).resolve().parent + + +def require(condition, detail): + if not condition: + raise RuntimeError(detail) + + +def supports(group): + return {a ^ b for i, a in enumerate(group) for b in group[i+1:]} + + +def reference(n, length, split, prefix, cap, even_on=True, odd_on=True): + s, t = split + even_budget = s * (s-1) // 2 + t * (t-1) // 2 - ((1 << (n-1))-1) + odd_budget = s * t - (1 << (n-1)) + out = {"nodes": 0, "candidate_restored_leaves": 0, "hits": 0, + "even_budget_prunes": 0, "odd_budget_prunes": 0, + "aborted": False} + if (even_on and even_budget < 0) or (odd_on and odd_budget < 0): + return out + counts = [0] * n + + def visit(depth, q, used, evens, odds): + if out["aborted"]: + return + if cap and out["nodes"] >= cap: + out["aborted"] = True + return + out["nodes"] += 1 + if depth == length: + if q or used != n or any(c % 2 for c in counts) or len(evens) != s or len(odds) != t: + return + out["candidate_restored_leaves"] += 1 + states = evens + odds + if len({a ^ b for a in states for b in states}) == 1 << n: + out["hits"] += 1 + return + if sum(c % 2 for c in counts) + 2 * (n-used) > length-depth: + return + candidates = range(min(used+1, n)) + for label in candidates: + if out["aborted"]: + return + if counts[label] >= length - 2 * (n-1): + continue + if depth < len(prefix) and label != prefix[depth]: + continue + nq = q ^ (1 << label) + last = depth == length-1 + if last and nq: + continue + fresh = not last and nq not in evens and nq not in odds + e2, o2 = evens, odds + if fresh: + if nq.bit_count() % 2: + o2 = odds + [nq] + else: + e2 = evens + [nq] + rem_e = sum(p % 2 == 0 for p in range(depth+2, length)) + rem_o = sum(p % 2 == 1 for p in range(depth+2, length)) + if len(e2) > s or len(o2) > t or len(e2)+rem_e < s or len(o2)+rem_o < t: + continue + if fresh: + e_ex = (len(e2)*(len(e2)-1)//2 - len(supports(e2)) + + len(o2)*(len(o2)-1)//2 - len(supports(o2))) + if even_on and e_ex > even_budget: + out["even_budget_prunes"] += 1 + continue + odd_ex = len(e2)*len(o2) - len({a ^ b for a in e2 for b in o2}) + if odd_on and odd_ex > odd_budget: + out["odd_budget_prunes"] += 1 + continue + counts[label] += 1 + visit(depth+1, nq, used+(label == used), e2, o2) + counts[label] -= 1 + + visit(0, 0, 0, [0], []) + return out + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--engine-source", type=Path, default=HERE.parent / "engine" / "corrected_census.c") + parser.add_argument("--cap", type=int, default=5000) + parser.add_argument("--output-dir", type=Path) + args = parser.parse_args() + output_dir = args.output_dir or Path(tempfile.mkdtemp(prefix="f7-n7-crosscheck-")) + output_dir.mkdir(parents=True, exist_ok=True) + require(args.cap > 0, "cap must be positive") + cases = [(20, (10, 10)), (20, (10, 9)), (20, (10, 8)), + (20, (9, 9)), (20, (10, 7)), (18, (9, 9))] + records = [] + binaries = {} + for length in (18, 20): + binary = output_dir / f"corrected_n7_l{length}" + command = ["cc", "-O2", "-std=c11", "-Wall", "-Wextra", "-Werror", "-DN=7", + f"-DL={length}", str(args.engine_source.resolve()), "-o", str(binary)] + subprocess.run(command, check=True, capture_output=True, text=True) + binaries[length] = binary + for length, split in cases: + for text_prefix in ("0012", "0102", "0123"): + prefix = tuple(map(int, text_prefix)) + for mode in ("both", "even"): + expected = reference(7, length, split, prefix, args.cap, odd_on=mode == "both") + with tempfile.TemporaryDirectory(prefix="n7gate-", dir=output_dir) as temporary: + directory = Path(temporary) + datafile = directory / "result.json" + command = [str(binaries[length]), ",".join(map(str, split)), "--hits", str(directory / "hits.txt"), + "--json", str(datafile), "--cap", str(args.cap), text_prefix] + if mode == "even": + command.append("--no-odd-budget") + proc = subprocess.run(command, capture_output=True, text=True) + data = json.loads(datafile.read_text()) + actual = data["total"][0] + for key, value in expected.items(): + require(actual[key] == value, (length, split, text_prefix, mode, key, actual, expected)) + require(proc.returncode == (2 if expected["aborted"] else 0), (proc, expected)) + require(data["complete"] == (not expected["aborted"]), data) + records.append({"n": 7, "L": length, "split": list(split), "prefix": text_prefix, + "mode": mode, "cap": args.cap, **expected, "pass": True}) + target = output_dir / "n7_crosscheck_results.json" + target.write_text(json.dumps({"method": __doc__, "runs": records, "pass": True}, indent=2) + "\n") + print(f"PASS: {len(records)} exact node/leaf/hit/prune-count comparisons; {target}") + + +if __name__ == "__main__": + main() diff --git a/research/f7-weight-parity-census-correction/validation/n7_crosscheck_results.json b/research/f7-weight-parity-census-correction/validation/n7_crosscheck_results.json new file mode 100644 index 0000000000..a0b95a8b04 --- /dev/null +++ b/research/f7-weight-parity-census-correction/validation/n7_crosscheck_results.json @@ -0,0 +1,654 @@ +{ + "method": "Capped n=7 C/Python cross-check with matching DFS/node semantics.\n\nThe Python oracle recomputes every pair-support set from scratch at each novel\nprefix. It does not use the C bitsets, incremental pair collision bookkeeping,\nor the earlier slow engine's soft budget flags.\n", + "runs": [ + { + "n": 7, + "L": 20, + "split": [ + 10, + 10 + ], + "prefix": "0012", + "mode": "both", + "cap": 5000, + "nodes": 2, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 0, + "odd_budget_prunes": 0, + "aborted": false, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 10 + ], + "prefix": "0012", + "mode": "even", + "cap": 5000, + "nodes": 2, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 0, + "odd_budget_prunes": 0, + "aborted": false, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 10 + ], + "prefix": "0102", + "mode": "both", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 9, + "odd_budget_prunes": 20066, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 10 + ], + "prefix": "0102", + "mode": "even", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 709, + "hits": 0, + "even_budget_prunes": 590, + "odd_budget_prunes": 0, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 10 + ], + "prefix": "0123", + "mode": "both", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 2, + "odd_budget_prunes": 19507, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 10 + ], + "prefix": "0123", + "mode": "even", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 726, + "hits": 0, + "even_budget_prunes": 665, + "odd_budget_prunes": 0, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 9 + ], + "prefix": "0012", + "mode": "both", + "cap": 5000, + "nodes": 2, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 0, + "odd_budget_prunes": 0, + "aborted": false, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 9 + ], + "prefix": "0012", + "mode": "even", + "cap": 5000, + "nodes": 2, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 0, + "odd_budget_prunes": 0, + "aborted": false, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 9 + ], + "prefix": "0102", + "mode": "both", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 38, + "odd_budget_prunes": 21479, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 9 + ], + "prefix": "0102", + "mode": "even", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 513, + "hits": 0, + "even_budget_prunes": 2837, + "odd_budget_prunes": 0, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 9 + ], + "prefix": "0123", + "mode": "both", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 0, + "odd_budget_prunes": 22120, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 9 + ], + "prefix": "0123", + "mode": "even", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 494, + "hits": 0, + "even_budget_prunes": 2496, + "odd_budget_prunes": 0, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 8 + ], + "prefix": "0012", + "mode": "both", + "cap": 5000, + "nodes": 2, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 0, + "odd_budget_prunes": 0, + "aborted": false, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 8 + ], + "prefix": "0012", + "mode": "even", + "cap": 5000, + "nodes": 2, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 0, + "odd_budget_prunes": 0, + "aborted": false, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 8 + ], + "prefix": "0102", + "mode": "both", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 727, + "odd_budget_prunes": 22070, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 8 + ], + "prefix": "0102", + "mode": "even", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 70, + "hits": 0, + "even_budget_prunes": 5410, + "odd_budget_prunes": 0, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 8 + ], + "prefix": "0123", + "mode": "both", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 1161, + "odd_budget_prunes": 21529, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 8 + ], + "prefix": "0123", + "mode": "even", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 124, + "hits": 0, + "even_budget_prunes": 6138, + "odd_budget_prunes": 0, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 9, + 9 + ], + "prefix": "0012", + "mode": "both", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 916, + "odd_budget_prunes": 22235, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 9, + 9 + ], + "prefix": "0012", + "mode": "even", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 474, + "hits": 0, + "even_budget_prunes": 3565, + "odd_budget_prunes": 0, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 9, + 9 + ], + "prefix": "0102", + "mode": "both", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 196, + "odd_budget_prunes": 23289, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 9, + 9 + ], + "prefix": "0102", + "mode": "even", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 580, + "hits": 0, + "even_budget_prunes": 3114, + "odd_budget_prunes": 0, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 9, + 9 + ], + "prefix": "0123", + "mode": "both", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 1576, + "odd_budget_prunes": 21162, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 9, + 9 + ], + "prefix": "0123", + "mode": "even", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 409, + "hits": 0, + "even_budget_prunes": 3638, + "odd_budget_prunes": 0, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 7 + ], + "prefix": "0012", + "mode": "both", + "cap": 5000, + "nodes": 2, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 0, + "odd_budget_prunes": 0, + "aborted": false, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 7 + ], + "prefix": "0012", + "mode": "even", + "cap": 5000, + "nodes": 2, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 0, + "odd_budget_prunes": 0, + "aborted": false, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 7 + ], + "prefix": "0102", + "mode": "both", + "cap": 5000, + "nodes": 1151, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 96, + "odd_budget_prunes": 5039, + "aborted": false, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 7 + ], + "prefix": "0102", + "mode": "even", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 18, + "hits": 0, + "even_budget_prunes": 8355, + "odd_budget_prunes": 0, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 7 + ], + "prefix": "0123", + "mode": "both", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 900, + "odd_budget_prunes": 22077, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 20, + "split": [ + 10, + 7 + ], + "prefix": "0123", + "mode": "even", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 3, + "hits": 0, + "even_budget_prunes": 11891, + "odd_budget_prunes": 0, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 18, + "split": [ + 9, + 9 + ], + "prefix": "0012", + "mode": "both", + "cap": 5000, + "nodes": 2, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 0, + "odd_budget_prunes": 0, + "aborted": false, + "pass": true + }, + { + "n": 7, + "L": 18, + "split": [ + 9, + 9 + ], + "prefix": "0012", + "mode": "even", + "cap": 5000, + "nodes": 2, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 0, + "odd_budget_prunes": 0, + "aborted": false, + "pass": true + }, + { + "n": 7, + "L": 18, + "split": [ + 9, + 9 + ], + "prefix": "0102", + "mode": "both", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 196, + "odd_budget_prunes": 23300, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 18, + "split": [ + 9, + 9 + ], + "prefix": "0102", + "mode": "even", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 580, + "hits": 0, + "even_budget_prunes": 3116, + "odd_budget_prunes": 0, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 18, + "split": [ + 9, + 9 + ], + "prefix": "0123", + "mode": "both", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 0, + "hits": 0, + "even_budget_prunes": 1576, + "odd_budget_prunes": 21167, + "aborted": true, + "pass": true + }, + { + "n": 7, + "L": 18, + "split": [ + 9, + 9 + ], + "prefix": "0123", + "mode": "even", + "cap": 5000, + "nodes": 5000, + "candidate_restored_leaves": 409, + "hits": 0, + "even_budget_prunes": 3641, + "odd_budget_prunes": 0, + "aborted": true, + "pass": true + } + ], + "pass": true +} diff --git a/research/f7-weight-parity-census-correction/verify.py b/research/f7-weight-parity-census-correction/verify.py new file mode 100644 index 0000000000..1d7ae3080b --- /dev/null +++ b/research/f7-weight-parity-census-correction/verify.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +"""Quick independent controls; --full additionally re-enumerates all six cases. + +Nicholas Stone, with substantial OpenAI Codex assistance. Apache-2.0. +No stored receipt is treated as a freshly executed census. +""" +import argparse +import hashlib +import json +from pathlib import Path +import subprocess +import sys +import tempfile + +HERE = Path(__file__).resolve().parent +SOURCE = HERE / "engine/corrected_census.c" +CASES = {(20, 10, 10), (20, 10, 9), (20, 10, 8), (20, 9, 9), + (20, 10, 7), (18, 9, 9)} +FIELDS = ("nodes", "candidate_restored_leaves", "hits", "even_budget_prunes", + "odd_budget_prunes", "aborted", "complete", "all_restored_leaves") + + +def require(condition, detail): + if not condition: + raise RuntimeError(detail) + + +def read(path): + return json.loads(path.read_text()) + + +def sha(path): + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def check_case(data, length, s, t): + require(data["n"] == 7 and data["length"] == length and data["split"] == [s, t], data) + require(data["groups"] == "hamming_weight_parity", data) + require(data["even_budget_enabled"] and data["odd_budget_enabled"], data) + require(data["even_budget"] == s*(s-1)//2+t*(t-1)//2-63, data) + require(data["odd_budget"] == s*t-64, data) + require(data["complete"] and not data["aborted"], data) + require(data["requested_shards"] == data["completed_or_partial_shards"] == 1, data) + require(len(data["shards"]) == 1 and data["shards"][0]["prefix"] == "", data) + total = data["total"][0] + require(total["complete"] and not total["aborted"] and total["hits"] == 0, data) + require(total["all_restored_leaves"] is None, data) + return total + + +def receipts(): + manifest = read(HERE / "engine/SUMMARIES.json") + require(sha(SOURCE) == manifest["source_sha256"], "C source differs from recorded receipt") + require(len(manifest["results"]) == len(CASES), "Missing or duplicate production case") + expected = {} + for record in manifest["results"]: + name = record["case"].split("_") + length, s, t = int(name[1][1:]), int(name[2]), int(name[3]) + key = length, s, t + require(key in CASES and key not in expected, record) + path = HERE / "engine" / record["result_file"] + hits = HERE / "engine" / record["hits_file"] + require(sha(path) == record["result_sha256"] and sha(hits) == record["hits_sha256"], record) + require(record["process_exit_code"] == 0 and hits.read_bytes() == b"", record) + total = check_case(read(path), length, s, t) + require(all(total[field] == record[field] for field in FIELDS), record) + expected[key] = total + require(set(expected) == CASES, "Production cases do not cover stated domain") + return expected + + +def upper_witness(): + record = read(HERE / "UPPER-WITNESS.json") + word = tuple(map(int, record["word"])) + require(len(word) == record["length"] == 24 and record["n"] == 7, record) + require(all(word.count(j) > 0 and word.count(j) % 2 == 0 for j in range(7)), record) + q, points = 0, set() + for label in word: + require(0 <= label < 7, word) + points.add(q) + q ^= 1 << label + require(q == 0 and len({a ^ b for a in points for b in points}) == 128, record) + + +def python_script(path, *args): + # -I keeps inherited PYTHONOPTIMIZE from disabling the mathematical checks. + # Explicit path insertion permits the validator's local oracle import. + loader = "import runpy,sys; from pathlib import Path; sys.path.insert(0,str(Path(sys.argv[1]).parent)); p=sys.argv.pop(1); runpy.run_path(p,run_name='__main__')" + subprocess.run([sys.executable, "-I", "-B", "-c", loader, str(path), *map(str, args)], + check=True, timeout=180) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--full", action="store_true") + args = parser.parse_args() + expected = receipts() + upper_witness() + with tempfile.TemporaryDirectory(prefix="f7-parity-replay-") as temporary: + out = Path(temporary) + python_script(HERE / "math/check_math.py") + python_script(HERE / "validation/check_corrected_c.py", "--output-dir", out / "small") + python_script(HERE / "validation/crosscheck_n7.py", "--output-dir", out / "capped") + if args.full: + binaries = {} + for length in (18, 20): + binary = out / f"census-{length}" + subprocess.run(["cc", "-O3", "-Wall", "-Wextra", "-Werror", "-std=c11", + "-DN=7", f"-DL={length}", str(SOURCE), "-o", str(binary)], check=True) + binaries[length] = binary + for (length, s, t), wanted in sorted(expected.items()): + key = f"n7_l{length}_{s}_{t}" + datafile, hitfile = out / (key+".json"), out / (key+".hits") + subprocess.run([str(binaries[length]), f"{s},{t}", "--hits", str(hitfile), + "--json", str(datafile)], check=True, timeout=480) + actual = check_case(read(datafile), length, s, t) + require(all(actual[field] == wanted[field] for field in FIELDS), (key, actual, wanted)) + require(hitfile.read_bytes() == b"", (key, "unexpected universal word")) + if args.full: + print("PASS_F7_WEIGHT_PARITY_FULL fresh_six_case_census universal_words=0 f7_lower=22 f7_upper=24") + else: + print("PASS_F7_WEIGHT_PARITY_QUICK independent_controls_and_stored_receipts_only") + + +if __name__ == "__main__": + main()