diff --git a/research/char2-threshold/LICENSE b/research/char2-threshold/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/research/char2-threshold/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/research/char2-threshold/MANIFEST.sha256 b/research/char2-threshold/MANIFEST.sha256 new file mode 100644 index 0000000000..96a7c281fc --- /dev/null +++ b/research/char2-threshold/MANIFEST.sha256 @@ -0,0 +1,20 @@ +c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4 LICENSE +493bc38a8f3643bfabad4f25e3f818a223f5968c85ff4015735db5721b9b6ac6 NOTICE.md +65e4b34276b5b6c31f105d4c36fa5a8a5f530f4a8bc1f41711f2e73dee1f8ea1 README.md +c22026fe0d1e93cd942bb96ff9a0707bf5182d59c589ca2f64457da576b824dc SOURCES.json +af0b7f2342927e8a3a5ee1e2eab7f39f0e8b2a3443760301fe2c05fd414451a2 c/kgap.c +fb18332a3adb4842c3662346aa7f6b5115debc04f1a392b6e9cbb423b7b83c8d checks/expect.py +3fb435bd31c3d30cc3040f1961dd2af041c6cbdfe9d8d542e9ffe2ab6f1a773b checks/fano.py +2982b6b81592263192a054874d6c0316aeccd0407a75ed46bad1bb04453af116 checks/units.py +80bb03e235b297ebf961cdc921268e7b1d40701128b1cb0c0c30449263270bcf data/e6_best.json +fed66f706661db5ea706aebc7097f1dd0f9432ae926f4b3689ac5372091a0974 data/fano_T4.json +a3d4bae1b1a8100c762b57c1b9b6b24908b1a31b02a0b771782fbb9efe832eff data/fano_T5.json +3f2b0891955df4b48cb10688d60fccc961aae8e2eb87090801401278b8463db8 evidence/KPR4-GAP-NOTES.md +23c955140e49f7a4b005a5cbabcbeba744bf3704e2da15d1eaa397dc00e42c58 lib/roots.py +db1f2bc4e99058a3eb7b3cee2ed0f2255deacdcd9b55416c3a04e7495e70ce22 lib/units.py +a840308c845b45136b57124df866ec7c053e2f5c91ad7aaa5a0dcf1aa95fa024 requirements.txt +15fb11f512404508259f1b3a701b62bf34ff881fca1ebc43313aec2c5ca3881f scripts/make_fano.py +6c4c71c693e63a9b27b327c181d308878dd7c8ec966d70fea708e8bed4d9355b scripts/provenance.py +070bc3758a459b495ae429f16e8f94fac17a6eeb19a662229f26c6fe60f63e4e scripts/realize.py +3cee17d758cd91c438fcddf2d3a6a3fb0651db9bc7acca5e59cf79c3d542e9c7 scripts/search_e6.py +e1c06ece517a00dd6e44d03d1759649348c54599a76491021577b826d26d4ccb verify.sh diff --git a/research/char2-threshold/NOTICE.md b/research/char2-threshold/NOTICE.md new file mode 100644 index 0000000000..7f94a9049b --- /dev/null +++ b/research/char2-threshold/NOTICE.md @@ -0,0 +1,16 @@ +# Notice + +Prepared by DreamingOfClouds with Claude Opus 5.5 (Anthropic), via Claude Code; Apache-2.0 (`LICENSE`, copied from the repository root). + +## Credits + +- **#446** (Theorem C, closed) and **#595** (merged): the cost formula and the pairing model; **#1590** (merged): exact decoders, Lemma A, Lemma B, ERR1 (E7 and E8 dyadic decoders with cost 49 and 72), and the pattern of `scripts/provenance.py` and `checks/expect.py` (both adapted from there); **#2111** (open): the T = 3 side (tied_min, the lift route), which this package does not repeat. #606, #1698 and #1830 are cited as context for the surrounding decoder discussion; nothing here depends on them. +- Our working card `KPR4-GAP` (2026-10-14): `c/kgap.c`, the searches and the numerical realisations; its notes are shipped verbatim in `evidence/`. + +## New in this package + +`README.md`, `verify.sh`, `SOURCES.json`, `requirements.txt`, `MANIFEST.sha256`, `lib/roots.py`, `lib/units.py` (pure-Python exact versions of the card's `units.py`/`kg.py` logic), `checks/units.py`, `checks/fano.py`, `scripts/search_e6.py`, `scripts/make_fano.py`, and the data files. + +## Adapted from our earlier work + +`c/kgap.c` (unchanged), `scripts/realize.py` (unchanged), `evidence/KPR4-GAP-NOTES.md` (verbatim). diff --git a/research/char2-threshold/README.md b/research/char2-threshold/README.md new file mode 100644 index 0000000000..33f55f270d --- /dev/null +++ b/research/char2-threshold/README.md @@ -0,0 +1,68 @@ +# Where characteristic 2 starts to pay: a T² threshold for root-unit decoders (E6 ties, E7 first gain found; no κ claim) + +> **What is claimed, and what is not.** This package explains, with exact computations and recorded searches, *where* the F2 cost of an exact root-unit decoder first drops below the number of lines v: the dense F2 cost of the full orthogonal pattern is about T·(rank of a block), the block rank is about T, so a gain over v needs v to exceed roughly **T²** lines. The data: D4, D5, D6, F4 and E6 tie (cost = v; E6 exactly 36 = 6²), A4–A6 are above v, **E7 is the first gain found (cost 49 < 63 at T = 7)**, E8 (72 < 120) is known (#1590, ERR1). +> **Claimed.** (1) CHECKED, exact: the F2 costs of the table in §2 (each is the cost of a stored or fully specified admissible pairing B for an exact decoder M = Gram). (2) CHECKED, exact: a realisation over Q of a Fano Levi graph has exact minimum cost 9 (T = 4) and 13 (T = 5, rank(PQᵀ) = 5, all five blocks nonempty) against v = 7: no gain at that size. (3) RECORDED: realisability of finite-geometry incidence structures (numerical), the annealing / clique / lattice-pool searches. +> **Not claimed.** **No claim that T ≤ 6 can never beat v**: the searches are not exhaustive and the law is an explanation plus evidence, not a theorem. The table's costs are *upper bounds on the minimum F2 cost* (cost of a specific B); that no smaller B exists is RECORDED (annealing), not proved. No κ gain, no window closed, nothing about T = 3 (see #2111). + +## 0. Reading this package + +Statuses: **PROVEN** (proof in this README), **CHECKED** (recomputed by `verify.sh`, exact), **RECORDED** (a result of our own working card `KPR4-GAP`, searches or numerical runs, shipped as the verbatim notes `evidence/KPR4-GAP-NOTES.md` and as the scripts used, but **not** re-run by `verify.sh`), **ARGUED / OPEN**. + +```sh +bash research/char2-threshold/verify.sh /fresh/output/dir # from a checkout or `git archive`; Python >= 3.11 (standard library) and cc +``` + +Step 0 checks every file against `MANIFEST.sha256` and every pin of `SOURCES.json` and stops with exit 1 on any mismatch; then `c/kgap.c` is compiled and two groups run in one process (8 PASS lines after step 0, 10 with step 0; seconds). Model, Theorem C cost and lemmas are those of the merged #1590 (`research/root-unit-decoder-lemmas`: exact decoder, pairing B, Lemma A for T ≤ 2, Lemma B lift identity, ERR1) and of #2111 (three-total decoders: reductions, the tight configuration tied_min, the lift route); #446 (Theorem C; closed) and #595 (merged) carry the cost formula. This package does not re-prove them. + +**Setting.** A root unit is a set of v root lines with Gram matrix G. An exact decoder with T totals is M = Σ_{j≤T} p_j q_jᵀ over Q with M_tt ≠ 0 and M_ts = 0 wherever B_ts = 1 (s ≠ t); here M = G is realised by taking the coordinates of the roots in a basis of T roots as p(t) and q(t) = p(t)·(Gram of the basis) (`lib/units.py`, exact Fractions; checked: M = Gram). R_j = supp p_j, A_j = supp q_j, r_j = rank_F2 B[R_j, A_j]; **cost = Σ_j r_j** (Theorem C without the per-total floor). B0 denotes the full orthogonal pattern (B_ts = 1 iff t = s or r_t ⊥ r_s); any admissible B lies inside it. By Lemma B (#1590) the cost over Q is always ≥ v, so a certified gap is exactly an admissible F2 matrix with Σ_j r_j < v. + +## 1. The law (ARGUED; the numbers behind it are CHECKED in §2) + +For these units the pairing is the orthogonality (or symplectic) incidence of a finite geometry, and an "odd-or-zero" labelling of the lines realises F2 incidence exactly: for B = J + abᵀ-type blocks the F2 rank is about k + 1 where k is the rank of the label system. Each total j sees a block whose F2 rank is *about T*, so **the dense cost is at most about T·(k + 1) ≈ T²** while v is the number of lines. A gain over v therefore needs **more than about T² lines**. For the root systems v = 12, 20, 30, 36, 63, 120 at T = 4, 5, 6, 6, 7, 8 against T² = 16, 25, 36, 36, 49, 64: D4–D6 (v = T(T−1)) are below T² (and tie, since each block has rank T − 1); E6 sits exactly at T² (tie, 36 = 6²); E7 is the first with v > T² (63 > 49); E8 has v = 120 > 64. The exact block ranks in §2 show the mechanism (T − 1 for D, T for E6 and E7, T + 1 for E8: cost = T × block rank). The law is an **explanation**: it predicts where gains are possible, it is not a proof that none occurs below, and the block-rank behaviour (k + 1) is observed, not derived for arbitrary pairings. + +## 2. The table (CHECKED, exact: `checks/units.py`, verify groups G1.*) + +Cost of an admissible B for the exact decoder of the unit (the basis is stated). "cost − v" is computed, not asserted. + +| unit | T | v | T² | pairing B | block ranks | cost | cost − v | check | +|---|---|---|---|---|---|---|---|---| +| D4 | 4 | 12 | 16 | B0, standard coordinates | 3,3,3,3 | 12 | 0 (tie) | G1.D-ties | +| D5 | 5 | 20 | 25 | B0, standard coordinates | 4×5 | 20 | 0 (tie) | G1.D-ties | +| D6 | 6 | 30 | 36 | B0, standard coordinates | 5×6 | 30 | 0 (tie) | G1.D-ties | +| E6 | 6 | 36 | 36 | stored B in a stored basis (`data/e6_best.json`, found by local search from B0) | 6×6 | 36 | 0 (tie, exactly T²) | G1.E6-tie | +| E7 | 7 | 63 | 49 | B0, first independent lines as basis | 7×7 | **49** | **−14 (first gain found)**; also checked by #1590 (G3.E7-dyadic) | G1.E7-gain | +| E8 | 8 | 120 | 64 | B0, first independent lines as basis | 9×8 | 72 | −48 (known; #1590 ERR1) | G1.E8-gain | +| A4 / A5 / A6 | 4 / 5 / 6 | 10 / 15 / 21 | 16 / 25 / 36 | B0 (upper bounds) | | 16 / 25 / 36 | +6 / +10 / +15 | G1.A-above | + +- **E6 depends on the basis**: B0 costs 37 in the first-independent basis (this value, and E7 = 49, are also checked in #1590: G4.E6, G3.E7-dyadic) and 36 in the stored basis (the card found 36 as best, other bases 40–42 after annealing). The tie at 36 is the cost of the stored B (an upper bound on the F2 minimum). +- **RECORDED** (annealing over all admissible B, `kgap.c sa`, card KPR4-GAP notes): the best costs found are D4/D5/D6 = 12/20/30 (ties), F4 (24 rays, T = 4) = 24 (tie, 150 bases tried), E6 = 36 (best basis), A4/A5/A6 = 16/21/34 (above v; the B0 upper bounds above are weaker for A5, A6), E7 = 49 in any basis (annealing from scratch also finds the gain 14; subsets of 57–60 roots also gain 14). Nothing below v was found at T ≤ 6. +- Lemma B (#1590) shows the cost over Q is ≥ v, so each "tie" means the F2 matrix reaches exactly the Q lower bound; the gains at E7 and E8 are genuine char-2 cancellation. + +## 3. Realisability of finite geometries (RECORDED, numerical) + +For a symplectic geometry Sp_{2m}(2) (points = nonzero vectors of F2^{2m}, orthogonality = vanishing symplectic form), a real exact decoder with the incidence pattern needs vectors p, q ∈ R^T with p_t·q_s = 0 on orthogonal pairs and p_t·q_t = 1. Levenberg–Marquardt on this system (`scripts/realize.py`, residual² ~ 1e-25 means yes), card KPR4-GAP: +- the **Sp4 doily (15 points)** realises at **T = 4** and **not at T = 3** (residual² ≈ 2.0); (re-run once while preparing this package: seeds 0, 1 at T = 4 give 8.6e-26 and 3.0e-28, T = 3 gives 2.0); +- the **63-point Sp6** realises at **T = 7** and Levenberg–Marquardt **found no realisation at T = 6 or T = 5** (residual not near zero; the card records "10–21"); +- E6's 36 nonsingular points plus up to 5 singular points of Sp6 realise at T = 6 (41 points, F2 rank 7: 41 < 42, no gain); greedy extension beyond that fails. +These are numerical (floating point) results: no exact rational realisation is shipped, so none is CHECKED. Consistent with the law: 63 = v(E7) needs T = 7 and 7² = 49 < 63. + +## 4. The Fano Levi graph (CHECKED, exact: `checks/fano.py`, groups G2.*) + +Seven points P_i ∈ Z^T, seven lines Q_L ∈ Z^T (null vectors of the three points of each Fano line L = (i, i+1, i+3)), matched by a permutation so that every p_t·q_t ≠ 0: a configuration of v = 7 lines whose orthogonality pattern contains the Levi graph of the Fano plane. The realisation exists over Q already at **T = 4**. `data/fano_T4.json`, `fano_T5.json` store the best realisation among 100 random draws each (found by `scripts/make_fano.py`); `verify.sh` checks that the zero pattern contains a Fano plane, that the points are distinct and the diagonal products nonzero, and computes the **exact minimum** of the F2 cost over all 2^f admissible B (`kgap ex`): **9 at T = 4 (f = 14) and 13 at T = 5 (f = 17), against v = 7**; both realisations have exact rank(PQᵀ) = T with all T blocks nonempty (checked). So there is no gain at that size. (The card, over 30 random draws, recorded 11 at T = 4 and 8 at T = 5, but its T = 5 realisations used only four coordinates, i.e. were rank 4; those are not used.) `scripts/make_fano.py` keeps the least cost over 100 random draws. The pattern is richer than the Levi graph (accidental orthogonalities are included, which can only lower the minimum), so the statement is: *this realisation* has cost > v. It is consistent with the law: T² = 16 or 25 ≫ 7. + +## 5. Scope and negative searches (RECORDED) + +- The searches are not exhaustive. Card KPR4-GAP: `ksubset` simulated annealing over subsets of pools (T = 4, 5; H = I, A, D, E, random), lattice pools of E6/D5/A5/D6 with norm ≤ 4, `ksearch` outer annealing at T = 3, 4 (only ties), maximum odd-or-zero cliques in lattice pools (E6, D5, D6, A6, Z^T), which equal the root counts; random symmetric / nonsymmetric forms at T = 4 give cliques up to 16 but gain ≤ 0. In all, no gain at T ≤ 6. +- An attempted E7 → T = 6 projection (dropping the minuscule node) fails: the 27 weight lines would need W–W orthogonal pairs of E7 to vanish in M′. +- **There is no claim that T ≤ 6 can never beat v.** A decoder with fewer totals than the T of the rank, or a non-root unit, is not covered; a unit with more than about T² lines and a low-rank F2 pattern is the thing to look for. +- Not done on the card: an exact Gröbner Q side (not needed: Lemma B gives cost over Q ≥ v), real-unit pricing, a minimal characterisation of the T = 7 seed, growth of the Fano gadget beyond v = 7. + +## 6. Open problems + +1. Is the threshold sharp: does every exact decoder with T totals on v ≤ T² lines (or on root units with v ≤ T²) have F2 cost ≥ v? (T = 3: the question of #2111.) The tie at E6 (v = T² = 36) suggests the boundary case. +2. A derivation of the block rank k + 1 for general odd-or-zero systems (the law in §1 is stated from examples). +3. A unit or a sub-configuration of a unit with T ≤ 6 and more than T² lines where B0-type patterns have block rank ≈ T. + +## 7. Files + +`verify.sh`, `scripts/provenance.py`, `MANIFEST.sha256`, `SOURCES.json`, `requirements.txt`, `LICENSE`, `NOTICE.md`; `c/kgap.c` (exhaustive / annealing F2 cost for T blocks, from the card, unchanged); `lib/roots.py`, `lib/units.py` (pure Python: root lines, exact decoders, ranks); `checks/units.py`, `checks/fano.py`, `checks/expect.py`; `data/e6_best.json`, `data/fano_T4.json`, `data/fano_T5.json`; authors' scripts not run by verify.sh: `scripts/search_e6.py`, `scripts/make_fano.py`, `scripts/realize.py`; `evidence/KPR4-GAP-NOTES.md` (verbatim notes of the card, snapshot 2026-10-14). diff --git a/research/char2-threshold/SOURCES.json b/research/char2-threshold/SOURCES.json new file mode 100644 index 0000000000..73b751609e --- /dev/null +++ b/research/char2-threshold/SOURCES.json @@ -0,0 +1,66 @@ +{ + "description": "Pins for research/char2-threshold. files = code, data and authors scripts; evidence = verbatim snapshots of the working card KPR4-GAP. scripts/provenance.py checks the pins (sha256 as stored) and MANIFEST.sha256.", + "files": [ + { + "file": "c/kgap.c", + "sha256": "af0b7f2342927e8a3a5ee1e2eab7f39f0e8b2a3443760301fe2c05fd414451a2" + }, + { + "file": "lib/roots.py", + "sha256": "23c955140e49f7a4b005a5cbabcbeba744bf3704e2da15d1eaa397dc00e42c58" + }, + { + "file": "lib/units.py", + "sha256": "db1f2bc4e99058a3eb7b3cee2ed0f2255deacdcd9b55416c3a04e7495e70ce22" + }, + { + "file": "checks/expect.py", + "sha256": "fb18332a3adb4842c3662346aa7f6b5115debc04f1a392b6e9cbb423b7b83c8d" + }, + { + "file": "checks/fano.py", + "sha256": "3fb435bd31c3d30cc3040f1961dd2af041c6cbdfe9d8d542e9ffe2ab6f1a773b" + }, + { + "file": "checks/units.py", + "sha256": "2982b6b81592263192a054874d6c0316aeccd0407a75ed46bad1bb04453af116" + }, + { + "file": "data/e6_best.json", + "sha256": "80bb03e235b297ebf961cdc921268e7b1d40701128b1cb0c0c30449263270bcf" + }, + { + "file": "data/fano_T4.json", + "sha256": "fed66f706661db5ea706aebc7097f1dd0f9432ae926f4b3689ac5372091a0974" + }, + { + "file": "data/fano_T5.json", + "sha256": "a3d4bae1b1a8100c762b57c1b9b6b24908b1a31b02a0b771782fbb9efe832eff" + }, + { + "file": "scripts/make_fano.py", + "sha256": "15fb11f512404508259f1b3a701b62bf34ff881fca1ebc43313aec2c5ca3881f" + }, + { + "file": "scripts/provenance.py", + "sha256": "6c4c71c693e63a9b27b327c181d308878dd7c8ec966d70fea708e8bed4d9355b" + }, + { + "file": "scripts/realize.py", + "sha256": "070bc3758a459b495ae429f16e8f94fac17a6eeb19a662229f26c6fe60f63e4e" + }, + { + "file": "scripts/search_e6.py", + "sha256": "3cee17d758cd91c438fcddf2d3a6a3fb0651db9bc7acca5e59cf79c3d542e9c7" + } + ], + "evidence": [ + { + "file": "evidence/KPR4-GAP-NOTES.md", + "sha256": "3f2b0891955df4b48cb10688d60fccc961aae8e2eb87090801401278b8463db8" + } + ], + "sources": { + "evidence/KPR4-GAP-NOTES.md": "cards/KPR4-GAP/NOTES.md" + } +} \ No newline at end of file diff --git a/research/char2-threshold/c/kgap.c b/research/char2-threshold/c/kgap.c new file mode 100644 index 0000000000..1766e21230 --- /dev/null +++ b/research/char2-threshold/c/kgap.c @@ -0,0 +1,65 @@ +// kgap: min F2 cost for T-block decoder. input: T v ; v lines of 2T ints (p then q). +// usage: kgap ex [slack] | kgap sa restarts steps seed [maxtemp] +// output: "best f n " then for ex: all minimisers as bitstrings over free list; for sa: best B bits. +#include +#include +#include +#include +#include +typedef uint64_t u64; +int T,v,f; long long P[16][64],Q[16][64]; +u64 Rm[16],Am[16]; int ft[4096],fs[4096]; +u64 row[64]; int rk[16]; +static inline int blockrank(int j){u64 basis[64];memset(basis,0,sizeof basis);int r=0; + u64 m=Rm[j];while(m){int t=__builtin_ctzll(m);m&=m-1;u64 x=row[t]&Am[j]; + while(x){int b=__builtin_ctzll(x);if(basis[b])x^=basis[b];else{basis[b]=x;r++;break;}}} + return r;} +static inline int cost(){int c=0;for(int j=0;j>7;rng_s^=rng_s<<17;return rng_s;} +static inline double urand(){return (rnd()>>11)*(1.0/9007199254740992.0);} +int *fblocks[4096]; int nfb[4096]; +int main(int argc,char**argv){ + if(scanf("%d %d",&T,&v)!=2)return 1; + for(int t=0;t>t&1)&&(Am[j]>>s&1))ok=1; if(ok){ft[f]=t;fs[f]=s;f++;}} + for(int t=0;t1&&!strcmp(argv[1],"ex")){ + int slack=argc>2?atoi(argv[2]):0; if(f>40){printf("too many free %d\n",f);return 3;} + u64 N=1ULL<5?atof(argv[5]):1.0; + int gbest=1000;u64 gb[64];memset(gb,0,sizeof gb); + for(int r=0;r> s) & 1 for s in range(len(L))] for t in range(len(L))] +row('E6', L, P, Q, B, 'stored basis and stored B (found by local search from B0)') +print("UN E6 B0 in the stored basis costs %d; in the first-independent basis %d" % (sum(block_ranks(P, Q, b0(L))), sum(block_ranks(*decoder(L, first_basis(L)), b0(L))))) +for nm in ('E7', 'E8'): + L = unit(nm); P, Q = decoder(L, first_basis(L)); row(nm, L, P, Q, b0(L), 'first independent lines as basis, B = B0') +for nm in ('A4', 'A5', 'A6'): + L = unit(nm); P, Q = decoder(L, first_basis(L)); row(nm, L, P, Q, b0(L), 'first independent lines as basis, B = B0 (upper bound only)') +print("UN T^2 vs v for roots: D4 16 vs 12, D5 25 vs 20, D6 36 vs 30, E6 36 vs 36, E7 49 vs 63, E8 64 vs 120") diff --git a/research/char2-threshold/data/e6_best.json b/research/char2-threshold/data/e6_best.json new file mode 100644 index 0000000000..ee5760953b --- /dev/null +++ b/research/char2-threshold/data/e6_best.json @@ -0,0 +1 @@ +{"basis": [16, 7, 19, 20, 29, 25], "rows": [4279222275, 64441270275, 13904919564, 54815112204, 22995481392, 45723366192, 40422657984, 28295876544, 16169829360, 52550173680, 24253762764, 44465065164, 41177641020, 27540910140, 27487432899, 41231118531, 44386421811, 24332405811, 52408615695, 16311386895, 62412991146, 56978134618, 47819676006, 30358742422, 38143370601, 20707695001, 11612542629, 6309980757, 34871810709, 17939457637, 9850939737, 6561633705, 6592042326, 9745033638, 17762248346, 34652658282], "cost": 36} \ No newline at end of file diff --git a/research/char2-threshold/data/fano_T4.json b/research/char2-threshold/data/fano_T4.json new file mode 100644 index 0000000000..50d9359872 --- /dev/null +++ b/research/char2-threshold/data/fano_T4.json @@ -0,0 +1 @@ +{"T": 4, "P": [[-3, 1, 2, -2], [-3, -2, 0, -2], [0, 0, 0, 3], [-2, -2, -2, 3], [-1, 0, 1, 1], [0, -2, 0, 2], [-1, -1, 0, 1]], "Q": [[2, -3, 2, 0], [-1, 0, 1, 0], [8, -26, 39, 14], [0, 0, 1, 0], [1, -1, 2, 0], [3, -1, 1, 2], [-3, 1, -4, 1]], "cost_found": 9} \ No newline at end of file diff --git a/research/char2-threshold/data/fano_T5.json b/research/char2-threshold/data/fano_T5.json new file mode 100644 index 0000000000..225887bdf2 --- /dev/null +++ b/research/char2-threshold/data/fano_T5.json @@ -0,0 +1 @@ +{"T": 5, "P": [[1, -3, 3, 0, -3], [0, 3, -2, -1, 1], [-3, 3, 0, 0, 3], [1, -1, -2, -3, -2], [0, 0, -1, 0, 2], [1, -3, 0, 0, 1], [1, -1, 3, -3, -3]], "Q": [[-5, -2, -6, 3, -3], [0, 0, -3, 2, 0], [39, -4, -6, 11, 11], [29, 14, 21, 13, 13], [3, 3, 2, 2, 0], [-2, 1, 0, -1, 0], [3, 1, 0, -2, 0]], "cost_found": 13} \ No newline at end of file diff --git a/research/char2-threshold/evidence/KPR4-GAP-NOTES.md b/research/char2-threshold/evidence/KPR4-GAP-NOTES.md new file mode 100644 index 0000000000..d7a6932c6e --- /dev/null +++ b/research/char2-threshold/evidence/KPR4-GAP-NOTES.md @@ -0,0 +1,19 @@ +# KPR4-GAP notes (partial; stopped early for report) +Tools: kgap.c (T blocks; `ex` exhaustive Gray code, `sa` annealing; validated at T=3: cex_joint_v7 12/4 minimisers, cex_wholeTS_v9 18/16, tied_min 12 via SA), +ksubset.c (SA over subsets of a pool maximising |S|-Cfull), mclique.c (odd-or-zero cliques), realize.py (real LM realisability of F2 incidence structures), kg.py, units.py, pools.py. +Note: minQ >= v (Lemma B), so a certified gap <=> minF2 < v; no exact-Groebner Q side was needed (nothing below v found at T<=6). + +| unit / family | T | v | best F2 cost found | note | +|---|---|---|---|---| +| D4,D5,D6 (std coords) | 4,5,6 | 12,20,30 | 12,20,30 | ties (cost = v) | +| F4 (24 rays) | 4 | 24 | 24 | tie, 150 bases | +| E6 | 6 | 36 | 36 | tie (best basis; other bases 40-42) | +| A4,A5,A6 | 4,5,6 | 10,15,21 | 16,21,34 | above v | +| E7 (any basis) | 7 | 63 | 49 | GAP 14 (SA on roots finds gain 14 from scratch; subsets of 57-60 roots also gain 14) | +| Fano Levi (7 pts, 7 planes, random Q realisation) | 4 / 5 | 7 | 11 / 8 | realises trivially at T=4 but cost>v | + +Structural picture (mod-2 labels): "odd-or-zero" systems realise F2 geometry exactly; B=J+ab^T has F2 rank k+1, dense cost <= T(k+1). Roots give v = 12,20,36,63,120 for T=4..8 versus T^2 = 16,25,36,49,64: tie at E6 (T=6), first strict win at T=7. Max odd-or-zero cliques in tested lattice pools (E6,D5,D6,A6, Z^T) equal the root counts; random symmetric/nonsymmetric forms T=4 give cliques up to 16 but gain <= 0. +Real realisability (LM, residual 1e-24 = yes): Sp4 doily (15 pts) T=4 yes, T=3 no; Sp6 (63 pts) T=7 yes, T=6 residual 10-21 (no), T=5 no. E6's 36 nonsingular points + up to 5 singular points of Sp6 realise at T=6 (41 pts, F2 rank 7: 41 < 42, no gain); greedy beyond that fails. +Negative searches (all gain <= 0): ksubset SA on pools T=4,5 (H = I,A,D,E, random), E6/D5/A5/D6 lattice pools norm<=4, ksearch outer SA T=3,4 (only ties). +Attempted E7 -> T=6 projection (drop minuscule node): fails because the 27 weight lines would need W-W orth pairs of E7 to vanish in M'. +Not done: exact Groebner Q-side, stretch (real-unit pricing), minimal T=7 seed characterisation, Fano gadget growth beyond v=7. diff --git a/research/char2-threshold/lib/roots.py b/research/char2-threshold/lib/roots.py new file mode 100644 index 0000000000..54d37bdd30 --- /dev/null +++ b/research/char2-threshold/lib/roots.py @@ -0,0 +1,39 @@ +"""Root lines of the units A_n, D_n, E6, E7, E8 in pure Python (integer coordinates scaled by 2, so the half-integer E8 roots are integral). +A 'line' is a root up to sign; lines are returned with first nonzero coordinate positive, in a fixed generation order.""" +import itertools +def canon(r): + f = next(x for x in r if x) + return tuple(r) if f > 0 else tuple(-x for x in r) +def lines(R): + seen = set(); out = [] + for r in R: + c = canon(r) + if c not in seen: seen.add(c); out.append(c) + return out +def A(n): + R = [] + for i in range(n + 1): + for j in range(i + 1, n + 1): + v = [0] * (n + 1); v[i] = 2; v[j] = -2; R.append(v) + return lines(R) +def D(n): + R = [] + for i in range(n): + for j in range(i + 1, n): + for s in (2, -2): + v = [0] * n; v[i] = 2; v[j] = s; R.append(v) + return lines(R) +def E8(): + R = [list(r) for r in D(8)] + for sg in itertools.product((1, -1), repeat=8): + if sg.count(-1) % 2 == 0: R.append(list(sg)) + return lines(R) +def dot(a, b): return sum(x * y for x, y in zip(a, b)) +def E7(): + return [r for r in E8() if sum(r) == 0] # orthogonal to s = (1,...,1)/2 +def E6(): + r1 = [2, -2, 0, 0, 0, 0, 0, 0]; r2 = [0, 2, -2, 0, 0, 0, 0, 0] # = -1 in the root normalisation: an A2 + return [r for r in E8() if dot(r, r1) == 0 and dot(r, r2) == 0] +def unit(name): + return {'D4': lambda: D(4), 'D5': lambda: D(5), 'D6': lambda: D(6), 'A4': lambda: A(4), 'A5': lambda: A(5), 'A6': lambda: A(6), + 'E6': E6, 'E7': E7, 'E8': E8}[name]() diff --git a/research/char2-threshold/lib/units.py b/research/char2-threshold/lib/units.py new file mode 100644 index 0000000000..080677f9b6 --- /dev/null +++ b/research/char2-threshold/lib/units.py @@ -0,0 +1,69 @@ +"""Exact T-block decoders of root units and F2 costs. decoder(L, basis): P = coordinates of each root line in the chosen basis of the unit, +Q = P . Gram(basis) (exact Fractions), so that M = P Q^T = Gram(L) (coordinates scaled by 2, entries are 4 ). The F2 cost of an admissible B +is sum_j rank_F2 B[R_j, A_j] with R_j = supp p_j, A_j = supp q_j (Theorem C without the floor).""" +from fractions import Fraction as F +from roots import dot +def solve_coords(basis, r): + """exact coordinates x with sum_i x_i basis_i = r (basis independent rows): Gaussian elimination over Q.""" + n = len(basis); m = len(r) + M = [[F(basis[i][k]) for i in range(n)] + [F(r[k])] for k in range(m)] + piv = []; row = 0 + for c in range(n): + p = next((i for i in range(row, m) if M[i][c] != 0), None) + if p is None: raise ValueError('dependent basis') + M[row], M[p] = M[p], M[row]; M[row] = [x / M[row][c] for x in M[row]] + for i in range(m): + if i != row and M[i][c] != 0: + f = M[i][c]; M[i] = [a - f * b for a, b in zip(M[i], M[row])] + row += 1 + for i in range(n, m): assert M[i][n] == 0, 'vector not in span' + return [M[i][n] for i in range(n)] +def first_basis(L): + idx = []; cur = [] + for i, r in enumerate(L): + try: + solve_coords(cur + [r], r) if False else None + except Exception: pass + if rank(cur + [r]) > len(cur): idx.append(i); cur.append(r) + return idx +def rank(rows): + M = [[F(x) for x in r] for r in rows]; rk = 0 + if not M: return 0 + for c in range(len(M[0])): + p = next((i for i in range(rk, len(M)) if M[i][c] != 0), None) + if p is None: continue + M[rk], M[p] = M[p], M[rk] + for i in range(rk + 1, len(M)): + if M[i][c] != 0: + f = M[i][c] / M[rk][c]; M[i] = [a - f * b for a, b in zip(M[i], M[rk])] + rk += 1 + return rk +def decoder(L, bidx): + basis = [L[i] for i in bidx]; n = len(basis) + C = [[dot(basis[i], basis[k]) for k in range(n)] for i in range(n)] + P = [solve_coords(basis, r) for r in L] + Q = [[sum(P[t][i] * C[i][k] for i in range(n)) for k in range(n)] for t in range(len(L))] + return P, Q +def exact(L, P, Q): + v = len(L) + return all(sum(P[t][j] * Q[s][j] for j in range(len(P[0]))) == dot(L[t], L[s]) for t in range(v) for s in range(v)) +def rank2(rows): + piv = [] + for x in rows: + for p in piv: x = min(x, x ^ p) + if x: piv.append(x); piv.sort(reverse=True) + return len(piv) +def block_ranks(P, Q, B): + v = len(P); T = len(P[0]); out = [] + for j in range(T): + R = [t for t in range(v) if P[t][j] != 0]; A = [s for s in range(v) if Q[s][j] != 0] + out.append(rank2([sum(B[t][s] << i for i, s in enumerate(A)) for t in R])) + return out +def b0(L): + """full orthogonal pattern: B_ts = 1 iff t = s or r_t orthogonal to r_s.""" + v = len(L); return [[1 if (t == s or dot(L[t], L[s]) == 0) else 0 for s in range(v)] for t in range(v)] +def admissible(L, B): + v = len(L); return all(B[t][t] == 1 and all(B[t][s] == 0 or dot(L[t], L[s]) == 0 for s in range(v) if s != t) for t in range(v)) +def decoder_std(L): + """standard coordinates (basis e_1..e_n, Gram 4I in the x2 scaling): P = Q = the root coordinates; M = P Q^T = .""" + return [[F(x) for x in r] for r in L], [[F(x) for x in r] for r in L] diff --git a/research/char2-threshold/requirements.txt b/research/char2-threshold/requirements.txt new file mode 100644 index 0000000000..d71720a129 --- /dev/null +++ b/research/char2-threshold/requirements.txt @@ -0,0 +1,2 @@ +# Tested set (Python 3.14.7, Apple clang 16). Standard library only (Fractions); a C compiler (cc, C99, -lm) builds c/kgap.c. +# The authors scripts scripts/search_e6.py, make_fano.py (sympy) and scripts/realize.py (numpy) are not run by verify.sh. diff --git a/research/char2-threshold/scripts/make_fano.py b/research/char2-threshold/scripts/make_fano.py new file mode 100644 index 0000000000..449b1422fc --- /dev/null +++ b/research/char2-threshold/scripts/make_fano.py @@ -0,0 +1,40 @@ +"""Authors' script (NOT run by verify.sh; sympy, and the C program c/kgap.c compiled to c/kgap_tmp): realises the Levi graph of the Fano plane over Q. Seven points +P_i in Z^T (random), seven lines Q_L in Z^T orthogonal to the three points of the Fano line L = (i, i+1, i+3) (a null vector), and a perfect matching sigma +with P_t . Q_sigma(t) != 0 that makes the seven (point, line) pairs the 'lines' t of a configuration with decoder pairing M_ts = P_t . Q_sigma(s). +Keeps, for T = 4 and T = 5, the realisation of least exact F2 cost over 100 random draws; writes data/fano_T4.json and data/fano_T5.json.""" +import sys, os, json, random, itertools, subprocess +from math import lcm, gcd +import sympy +H = os.path.dirname(os.path.abspath(__file__)); fl = [(i, (i + 1) % 7, (i + 3) % 7) for i in range(7)] +def realize(T, rng, m=3): + while True: + P = [[rng.randint(-m, m) for _ in range(T)] for _ in range(7)]; Q = []; ok = True + for L in fl: + ns = sympy.Matrix([P[i] for i in L]).nullspace() + if len(ns) != T - 3: ok = False; break + n = ns[0] + for extra in ns[1:]: n = n + rng.randint(-2, 2) * extra # generic null vector (all T coordinates can be used) + l = 1 + for x in n: l = lcm(l, int(sympy.Rational(x).q)) + q = [int(x * l) for x in n]; g = 0 + for x in q: g = gcd(g, abs(x)) + Q.append([x // g for x in q]) + if ok and sympy.Matrix(P).rank() == T and all(any(P[t][j] for t in range(7)) and any(q[j] for q in Q) for j in range(T)): return P, Q +def kg_cost(P, Q): + T = len(P[0]); inp = "%d 7\n" % T + "\n".join(" ".join(map(str, P[t] + Q[t])) for t in range(7)) + "\n" + out = subprocess.run([os.path.join(H, '..', 'c', 'kgap_tmp'), 'ex'], input=inp, capture_output=True, text=True).stdout.split('\n') + return int([l for l in out if l.startswith('best')][0].split()[1]) +rng = random.Random(1) +for T in (4, 5): + best = None + for tr in range(100): + P, Q = realize(T, rng) + for sg in itertools.permutations(range(7)): + if all(sum(a * b for a, b in zip(P[t], Q[sg[t]])) != 0 for t in range(7)): + Qp = [Q[sg[t]] for t in range(7)] + if sympy.Matrix(P).T.shape and sympy.Matrix(P) * sympy.Matrix(Qp).T == sympy.zeros(7, 7): continue + if (sympy.Matrix(P) * sympy.Matrix(Qp).T).rank() != T: continue + c = kg_cost(P, Qp) + if best is None or c < best[0]: best = (c, P, Qp) + break + print(T, best[0]); json.dump({'T': T, 'P': best[1], 'Q': best[2], 'cost_found': best[0]}, open(os.path.join(H, '..', 'data', 'fano_T%d.json' % T), 'w')) diff --git a/research/char2-threshold/scripts/provenance.py b/research/char2-threshold/scripts/provenance.py new file mode 100644 index 0000000000..53abd0bee9 --- /dev/null +++ b/research/char2-threshold/scripts/provenance.py @@ -0,0 +1,45 @@ +"""Step 0 of verify.sh (standard library only). Prints one PASS/FAIL line per group and exits 1 on any mismatch. + 0.manifest every file of the package matches MANIFEST.sha256 (sha256), none is missing and none is extra + 0.sources every pin of SOURCES.json holds: the sha256 of each pinned package file (checks, libraries, C sources, data, evidence snapshots) +usage: python3 provenance.py [--write-manifest] (--write-manifest is for the authors: it rewrites MANIFEST.sha256; SOURCES.json is written by --write-sources)""" +import hashlib, json, os, sys +PKG = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +bad = 0 +def sha(b): return hashlib.sha256(b).hexdigest() +def listing(): + out = [] + for d, ds, fs in os.walk(PKG): + ds[:] = sorted(x for x in ds if x != '__pycache__' and x != 'bin') + for f in fs: + if f.endswith('.pyc') or f == '.DS_Store': continue + p = os.path.relpath(os.path.join(d, f), PKG) + if p != 'MANIFEST.sha256': out.append(p) + return sorted(out) +if '--write-manifest' in sys.argv: + with open(os.path.join(PKG, 'MANIFEST.sha256'), 'w') as h: + for p in listing(): h.write('%s %s\n' % (sha(open(os.path.join(PKG, p), 'rb').read()), p)) + print('wrote MANIFEST.sha256 (%d files)' % len(listing())); sys.exit(0) +if '--write-sources' in sys.argv: + S = json.load(open(os.path.join(PKG, 'SOURCES.json'))) + for sect in ('files', 'evidence'): + for e in S[sect]: e['sha256'] = sha(open(os.path.join(PKG, e['file']), 'rb').read()) + json.dump(S, open(os.path.join(PKG, 'SOURCES.json'), 'w'), indent=1); print('wrote SOURCES.json pins'); sys.exit(0) +want = {} +for l in open(os.path.join(PKG, 'MANIFEST.sha256'), encoding='utf-8'): + h, p = l.rstrip('\n').split(' ', 1); want[p] = h +have = listing(); n = 0 +for p in sorted(set(want) | set(have)): + f = os.path.join(PKG, p) + if p not in want: print('FAIL 0.manifest extra file not in MANIFEST: ' + p); bad += 1 + elif not os.path.isfile(f): print('FAIL 0.manifest missing file: ' + p); bad += 1 + elif sha(open(f, 'rb').read()) != want[p]: print('FAIL 0.manifest hash differs: ' + p); bad += 1 + else: n += 1 +print('%s 0.manifest %d files match MANIFEST.sha256' % ('PASS' if not bad else 'FAIL', n), flush=True) +S = json.load(open(os.path.join(PKG, 'SOURCES.json'))); b0 = bad; k = 0 +for sect in ('files', 'evidence'): + for e in S[sect]: + k += 1; f = os.path.join(PKG, e['file']) + if not os.path.isfile(f): print('FAIL 0.sources missing ' + e['file']); bad += 1 + elif sha(open(f, 'rb').read()) != e['sha256']: print('FAIL 0.sources pin differs: ' + e['file']); bad += 1 +print('%s 0.sources %d pins hold (checks, libraries, C sources, data, evidence snapshots)' % ('PASS' if bad == b0 else 'FAIL', k - (bad - b0)), flush=True) +sys.exit(1 if bad else 0) diff --git a/research/char2-threshold/scripts/realize.py b/research/char2-threshold/scripts/realize.py new file mode 100644 index 0000000000..cfe761a1f2 --- /dev/null +++ b/research/char2-threshold/scripts/realize.py @@ -0,0 +1,42 @@ +import numpy as np,itertools,sys +def lm(zero_pairs,v,T,seed=0,iters=300,verbose=False): + rng=np.random.RandomState(seed) + X=rng.randn(2*v*T)*0.7 # [p;q] + zp=np.array(zero_pairs);nz=len(zp) + def resid(X): + P=X[:v*T].reshape(v,T);Q=X[v*T:].reshape(v,T) + r1=np.einsum('ij,ij->i',P[zp[:,0]],Q[zp[:,1]]) + r2=np.einsum('ij,ij->i',P,Q)-1.0 + return np.concatenate([r1,r2]) + def jac(X): + P=X[:v*T].reshape(v,T);Q=X[v*T:].reshape(v,T) + J=np.zeros((nz+v,2*v*T)) + for k,(t,s) in enumerate(zp): + J[k,t*T:(t+1)*T]=Q[s];J[k,v*T+s*T:v*T+(s+1)*T]=P[t] + for t in range(v): + J[nz+t,t*T:(t+1)*T]=Q[t];J[nz+t,v*T+t*T:v*T+(t+1)*T]=P[t] + return J + lam=1.0;r=resid(X);f=r@r + for it in range(iters): + J=jac(X);g=J.T@r;A=J.T@J + while True: + try: dx=np.linalg.solve(A+lam*np.eye(len(X)),-g) + except np.linalg.LinAlgError: lam*=10;continue + r2=resid(X+dx);f2=r2@r2 + if f21e12: return f,X + if f<1e-22: break + return f,X +def sp_points(m): + k=2*m;pts=[tuple(x) for x in itertools.product((0,1),repeat=k) if any(x)] + def om(a,b): return sum(a[i]*b[i+m]+a[i+m]*b[i] for i in range(m))%2 + return pts,om +def zero_pairs_of(pts,om): + v=len(pts);return [(t,s) for t in range(v) for s in range(v) if t!=s and om(pts[t],pts[s])==0] +if __name__=='__main__': + m=int(sys.argv[1]);T=int(sys.argv[2]);seeds=int(sys.argv[3]) + pts,om=sp_points(m);zp=zero_pairs_of(pts,om);v=len(pts) + for sd in range(seeds): + f,X=lm(zp,v,T,sd,iters=200) + print('Sp',2*m,'v',v,'T',T,'seed',sd,'resid^2',f,flush=True) diff --git a/research/char2-threshold/scripts/search_e6.py b/research/char2-threshold/scripts/search_e6.py new file mode 100644 index 0000000000..094fb258e8 --- /dev/null +++ b/research/char2-threshold/scripts/search_e6.py @@ -0,0 +1,21 @@ +"""Authors' script (NOT run by verify.sh): looks for a basis of E6 and a local modification of the full orthogonal pattern B0 with F2 cost 36 = v +(B0 itself costs 37 in every basis tried). Random basis, random single-entry flips of free entries accepted if the cost does not rise; writes data/e6_best.json.""" +import sys, os, json, random +H = os.path.dirname(os.path.abspath(__file__)); sys.path.insert(0, os.path.join(H, '..', 'lib')) +from roots import *; from units import * +L = unit('E6'); v = len(L); rng = random.Random(5) +for tr in range(1000): + idx = list(range(v)); rng.shuffle(idx); bidx = [] + for i in idx: + if rank([L[k] for k in bidx + [i]]) > len(bidx): bidx.append(i) + if len(bidx) == 6: break + P, Q = decoder(L, bidx); B = b0(L) + free = [(t, s) for t in range(v) for s in range(v) if t != s and dot(L[t], L[s]) == 0] + c = sum(block_ranks(P, Q, B)); print(tr, c, flush=True) + for it in range(400): + t, s = rng.choice(free); B[t][s] ^= 1; c2 = sum(block_ranks(P, Q, B)) + if c2 <= c: c = c2 + else: B[t][s] ^= 1 + if c <= 36: break + if c <= 36: + json.dump({'basis': bidx, 'rows': [sum(B[t][s] << s for s in range(v)) for t in range(v)], 'cost': c}, open(os.path.join(H, '..', 'data', 'e6_best.json'), 'w')); print('found', c); break diff --git a/research/char2-threshold/verify.sh b/research/char2-threshold/verify.sh new file mode 100644 index 0000000000..74569dd37c --- /dev/null +++ b/research/char2-threshold/verify.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# One-command verification of research/char2-threshold (no kappa claim): exact F2 costs of the full-orthogonal-pattern pairings of root units D4, D5, D6, E6, E7, E8 +# (and A4-A6 as upper bounds) against T^2, and the exact minimum cost of a Fano Levi-graph realisation over Q at T = 4, 5. Step 0 checks the package against +# MANIFEST.sha256 and every pin of SOURCES.json and stops hard on any mismatch. Single process; standard library Python and a C compiler only. +# usage: bash verify.sh [/fresh/output/dir] (inside a checkout or `git archive` of the repository; nothing outside the package is read) +# set PYTHON to choose the interpreter (>= 3.11), CC for the compiler. Not re-run here: the annealing / clique / lattice-pool searches and the numerical (Levenberg-Marquardt) realisability runs (README, RECORDED). +set -uo pipefail +HERE=$(cd "$(dirname "$0")" && pwd) +OUT=${1:-$(mktemp -d "${TMPDIR:-/tmp}/char2-threshold.XXXXXX")} +mkdir -p "$OUT"; OUT=$(cd "$OUT" && pwd) +case "$OUT/" in "$HERE"/*) echo "output dir must be outside the package"; exit 2;; esac +PY="${PYTHON:-python3}"; CC="${CC:-cc}"; export PYTHONDONTWRITEBYTECODE=1 +T0=$SECONDS +echo "== 0. manifest and provenance" +if ! "$PY" -B "$HERE/scripts/provenance.py" | tee "$OUT/provenance.log" | sed 's/^/ /' || grep -q '^FAIL' "$OUT/provenance.log" \ + || [ "$(grep -c '^PASS' "$OUT/provenance.log")" != 2 ]; then + echo "step 0 FAILED"; exit 1 +fi +R="$OUT/tree"; mkdir -p "$R" "$OUT/logs" "$R/bin" +cp -R "$HERE/checks" "$HERE/lib" "$HERE/data" "$HERE/c" "$R/" +export C2T_BIN="$R/bin" +E_="$PY -B $R/checks/expect.py" +run() { local n=$1; shift; ( cd "$R/checks" && "$@" > "$OUT/logs/$n.log" 2>&1; echo $? > "$OUT/logs/$n.rc" ); } +chk() { local l=$1 n=$2; shift 2; $E_ "$l" "$OUT/logs/$n.log" "$@" | tee -a "$OUT/all.out"; } + +echo "== G0 compile the C program from source" +if "$CC" -O2 -o "$R/bin/kgap" "$R/c/kgap.c" -lm > "$OUT/logs/cc.log" 2>&1; then echo "PASS G0.compile kgap" | tee -a "$OUT/all.out"; else echo "FAIL G0.compile see $OUT/logs/cc.log" | tee -a "$OUT/all.out"; fi + +echo "== G1 root units: exact decoders and F2 costs against T^2" +run units $PY -B units.py +chk G1.D-ties units --has '^UN D4 v=12 T=4 .*decoder exact \(M = Gram\): True; B admissible: True; .*block ranks \[3, 3, 3, 3\]; cost 12; T\^2 = 16; cost - v = 0$' \ + --has '^UN D5 v=20 T=5 .*decoder exact \(M = Gram\): True; B admissible: True; .*block ranks \[4, 4, 4, 4, 4\]; cost 20; T\^2 = 25; cost - v = 0$' \ + --has '^UN D6 v=30 T=6 .*decoder exact \(M = Gram\): True; B admissible: True; .*block ranks \[5, 5, 5, 5, 5, 5\]; cost 30; T\^2 = 36; cost - v = 0$' +chk G1.E6-tie units --has '^UN E6 v=36 T=6 .*decoder exact \(M = Gram\): True; B admissible: True; stored basis and stored B .*block ranks \[6, 6, 6, 6, 6, 6\]; cost 36; T\^2 = 36; cost - v = 0$' \ + --has '^UN E6 B0 in the stored basis costs 36; in the first-independent basis 37$' +chk G1.E7-gain units --has '^UN E7 v=63 T=7 rank\(L\)=7 decoder exact \(M = Gram\): True; B admissible: True; .*block ranks \[7, 7, 7, 7, 7, 7, 7\]; cost 49; T\^2 = 49; cost - v = -14$' +chk G1.E8-gain units --has '^UN E8 v=120 T=8 rank\(L\)=8 decoder exact \(M = Gram\): True; B admissible: True; .*block ranks \[9, 9, 9, 9, 9, 9, 9, 9\]; cost 72; T\^2 = 64; cost - v = -48$' +chk G1.A-above units --has '^UN A4 v=10 T=4 .*cost 16; T\^2 = 16; cost - v = 6$' --has '^UN A5 v=15 T=5 .*cost 25; T\^2 = 25; cost - v = 10$' --has '^UN A6 v=21 T=6 .*cost 36; T\^2 = 36; cost - v = 15$' --absent 'Traceback' --absent 'False' + +echo "== G2 Fano Levi graph over Q: exact minimum cost" +run fano $PY -B fano.py +chk G2.fano-T4 fano --has '^FL T=4: rank\(P Q\^T\) = 4 with all 4 blocks nonempty: True; zero pattern contains a Fano plane .* True; 7 distinct points True, diagonals nonzero True, .*exact minimum F2 cost over all 2\^f admissible B = 9, v = 7, cost - v = 2$' +chk G2.fano-T5 fano --has '^FL T=5: rank\(P Q\^T\) = 5 with all 5 blocks nonempty: True; zero pattern contains a Fano plane .* True; 7 distinct points True, diagonals nonzero True, .*exact minimum F2 cost over all 2\^f admissible B = 13, v = 7, cost - v = 6$' --absent 'Traceback' + +EXPECT=8 +npass=$(grep -c '^PASS' "$OUT/all.out"); nfail=$(grep -c '^FAIL' "$OUT/all.out") +echo "== done in $((SECONDS - T0))s: $((npass + 2)) PASS (2 in step 0), $nfail FAIL; logs in $OUT" +[ "$nfail" = 0 ] && [ "$npass" = "$EXPECT" ] || { echo "FAILED: expected $EXPECT assertion PASS lines after step 0, got $npass PASS / $nfail FAIL"; exit 1; }