From c248962c3cf641d4f6226d63d86843f2c096a7d0 Mon Sep 17 00:00:00 2001 From: Chafik Boukhalfa Date: Sun, 11 Oct 2026 09:52:31 +0400 Subject: [PATCH 1/3] e8-complex-far-retiming: Sussman's E8 complex unit re-timed by far piece relocation (same operator), in #375's unsplit B3; conditional kappa = 9.5464544449649e-4 (+0.0421% over PR375) Co-Authored-By: Claude --- .gitattributes | 2 + .github/workflows/e8-complex-far-retiming.yml | 37 ++ research/e8-complex-far-retiming/LICENSE | 201 +++++++ .../e8-complex-far-retiming/MANIFEST.sha256 | 26 + research/e8-complex-far-retiming/NOTICE.md | 21 + research/e8-complex-far-retiming/PROOF.md | 34 ++ research/e8-complex-far-retiming/README.md | 111 ++++ .../e8-complex-far-retiming/code/cxcheck.py | 23 + .../e8-complex-far-retiming/code/cxfar.py | 85 +++ .../e8-complex-far-retiming/code/cxfinal.py | 29 ++ .../e8-complex-far-retiming/code/cxkernel.py | 222 ++++++++ research/e8-complex-far-retiming/code/cxop.py | 32 ++ .../e8-complex-far-retiming/code/cxpiece.py | 64 +++ .../e8-complex-far-retiming/code/cxretime.py | 169 ++++++ .../e8-complex-far-retiming/code/cxsplit.py | 92 ++++ .../e8-complex-far-retiming/code/e8core.py | 203 ++++++++ .../code/engines/base_two_moment.py | 63 +++ .../code/engines/moment.py | 113 ++++ .../code/engines/outer.py | 58 +++ .../e8-complex-far-retiming/code/kappa.py | 52 ++ .../e8-complex-far-retiming/code/layout_bk.py | 63 +++ .../complex/LICENSE-wht-power-saving-lean | 201 +++++++ .../complex/NOTICE-wht-power-saving-lean | 489 ++++++++++++++++++ .../complex/gcert1-e8-r783.json.gz | Bin 0 -> 21883 bytes .../e8-complex-far-retiming/complex/gx/gx.py | 296 +++++++++++ .../complex/gx/gxcore.py | 375 ++++++++++++++ .../data/bit/bitE8-r865.json.gz | Bin 0 -> 33865 bytes .../data/gcert1-e8-r783-far.json.gz | Bin 0 -> 22008 bytes research/e8-complex-far-retiming/verify.sh | 67 +++ 29 files changed, 3128 insertions(+) create mode 100644 .github/workflows/e8-complex-far-retiming.yml create mode 100644 research/e8-complex-far-retiming/LICENSE create mode 100644 research/e8-complex-far-retiming/MANIFEST.sha256 create mode 100644 research/e8-complex-far-retiming/NOTICE.md create mode 100644 research/e8-complex-far-retiming/PROOF.md create mode 100644 research/e8-complex-far-retiming/README.md create mode 100644 research/e8-complex-far-retiming/code/cxcheck.py create mode 100644 research/e8-complex-far-retiming/code/cxfar.py create mode 100644 research/e8-complex-far-retiming/code/cxfinal.py create mode 100644 research/e8-complex-far-retiming/code/cxkernel.py create mode 100644 research/e8-complex-far-retiming/code/cxop.py create mode 100644 research/e8-complex-far-retiming/code/cxpiece.py create mode 100644 research/e8-complex-far-retiming/code/cxretime.py create mode 100644 research/e8-complex-far-retiming/code/cxsplit.py create mode 100644 research/e8-complex-far-retiming/code/e8core.py create mode 100644 research/e8-complex-far-retiming/code/engines/base_two_moment.py create mode 100644 research/e8-complex-far-retiming/code/engines/moment.py create mode 100644 research/e8-complex-far-retiming/code/engines/outer.py create mode 100644 research/e8-complex-far-retiming/code/kappa.py create mode 100644 research/e8-complex-far-retiming/code/layout_bk.py create mode 100644 research/e8-complex-far-retiming/complex/LICENSE-wht-power-saving-lean create mode 100644 research/e8-complex-far-retiming/complex/NOTICE-wht-power-saving-lean create mode 100644 research/e8-complex-far-retiming/complex/gcert1-e8-r783.json.gz create mode 100644 research/e8-complex-far-retiming/complex/gx/gx.py create mode 100644 research/e8-complex-far-retiming/complex/gx/gxcore.py create mode 100644 research/e8-complex-far-retiming/data/bit/bitE8-r865.json.gz create mode 100644 research/e8-complex-far-retiming/data/gcert1-e8-r783-far.json.gz create mode 100644 research/e8-complex-far-retiming/verify.sh diff --git a/.gitattributes b/.gitattributes index 6825630198..660ca3e3e0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -18,3 +18,5 @@ research/coordinated-frames-and-entrance-banks/** -text -whitespace # Frozen PR210 package and round-nine evidence retain their audited bytes. research/five-stage-source527-banks/** -text -whitespace research/community-round9-audit/** -text -whitespace +# E8 complex unit with far piece relocation (e8st): MANIFEST.sha256 and the git-blob pins fix exact bytes (Sussman gx.py keeps its original whitespace). +research/e8-complex-far-retiming/** -text -whitespace diff --git a/.github/workflows/e8-complex-far-retiming.yml b/.github/workflows/e8-complex-far-retiming.yml new file mode 100644 index 0000000000..6ea1a85aa1 --- /dev/null +++ b/.github/workflows/e8-complex-far-retiming.yml @@ -0,0 +1,37 @@ +name: e8-complex-far-retiming + +on: + pull_request: + paths: + - 'research/e8-complex-far-retiming/**' + - '.github/workflows/e8-complex-far-retiming.yml' + push: + paths: + - 'research/e8-complex-far-retiming/**' + - '.github/workflows/e8-complex-far-retiming.yml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + verify: + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + python-version: ['3.12', '3.14'] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + persist-credentials: false + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 + with: + python-version: ${{ matrix.python-version }} + - name: Quick verification (manifest, pins, Sussman's checkers, operator identity, layout, kappa) + run: bash research/e8-complex-far-retiming/verify.sh --quick "$RUNNER_TEMP/e8st-quick" + - name: Full verification (adds the regeneration of the certificate from Sussman's original) + run: bash research/e8-complex-far-retiming/verify.sh "$RUNNER_TEMP/e8st-full" + - name: Require an unchanged tree + run: git diff --exit-code diff --git a/research/e8-complex-far-retiming/LICENSE b/research/e8-complex-far-retiming/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/research/e8-complex-far-retiming/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/e8-complex-far-retiming/MANIFEST.sha256 b/research/e8-complex-far-retiming/MANIFEST.sha256 new file mode 100644 index 0000000000..7a4a1f1d13 --- /dev/null +++ b/research/e8-complex-far-retiming/MANIFEST.sha256 @@ -0,0 +1,26 @@ +c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4 LICENSE +b4d2d9f6ed3419a56e76cc36bcab5059993c4853532401b56e4e77a8455d0f93 NOTICE.md +9d09e322645bdf4952758a76aa8a802ee1d58702bd66d4ad27285d5e5f6ed977 PROOF.md +fe46088149ee73dc8847004931c1e0175207d1ccfb6f7258961f09f5e6607e19 README.md +97b461509656798d30b2610b4755ce825b1f84ae8772b9b4efd5c84d7404385b code/cxcheck.py +316cbf7d7c777e1fbe8a5d5fb249f0970b591330f4c2769c6827965a0e9c1adf code/cxfar.py +7b2afb0be9591c72d31d7fbcce4e2d45bbe09daf7caa21123bca050b92cd2882 code/cxfinal.py +8fc2a43e70fa8c1ce3f3025c9568b562accc5cf25b2ed53d7c0a1cbf954a0599 code/cxkernel.py +2655e1e9ec94fb39512329ec86d261959ccd7c635c7535238f254a82861c2db1 code/cxop.py +1c78b3025203c802c3aab9cc4113c48a7357d582f5bc75c47bde7953787f2e82 code/cxpiece.py +e7e06595c12fbafb707a6846719c54b85e0c2bb105927e7e7ee20f028a37438b code/cxretime.py +9f6c0718f371120b015733983b3c91285a3825d306b557f0b9013059c6c88b0e code/cxsplit.py +1cb286196c1530d06f50f0997febc5993b388f33464e295611b920ba30a24eeb code/e8core.py +aee70c242a2f1d225528fabe2002e1564d1dc884797c07d28a831c7878ee9098 code/engines/base_two_moment.py +81cbc864e106418fc7854322746f72180cb8110ff39f74450a3b8d55a82c1482 code/engines/moment.py +c1ebb52163a7219504c728a5c2b098bf1e50bf20148a2879c5a74c3cadc7c03c code/engines/outer.py +bd2c7d5bf546babf4700de802f9f49b45e8da0b3e75edcb4a5dfed914d186c6e code/kappa.py +e650d16538db8d80495fda5109cdfa0775e1d5f3b56b0aa39e5c1efc660877e7 code/layout_bk.py +c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4 complex/LICENSE-wht-power-saving-lean +557c9ad4660eebd14a00583736c4d12d2c45f3fc9bc148845f9f3efec72c9434 complex/NOTICE-wht-power-saving-lean +4b92f00fc7454b9b15e6d71a3b05062792eacca0a0ca4efee46f87827d57c8aa complex/gcert1-e8-r783.json.gz +6f3dc8bb639933227a8dae181ec4a542e85c7276be9c9e330f49518e7b630b95 complex/gx/gx.py +eb972c06fe8c322db2d9a8d9a7d05596e8f40c4ca8e5f42eb6df3c7361b5d517 complex/gx/gxcore.py +1fb726ea3818fe0837cceb0dd22a71a0814f064e5e781628104610d6468e0e55 data/bit/bitE8-r865.json.gz +f2664ee7bcdfb55893abe9f1be8ee6a394a602e8262e611de2a0328bbd508aa7 data/gcert1-e8-r783-far.json.gz +779c48fc33d1dd967ed0b7125d86dfcac38f6e36dce82f6fbb2ac01742974530 verify.sh diff --git a/research/e8-complex-far-retiming/NOTICE.md b/research/e8-complex-far-retiming/NOTICE.md new file mode 100644 index 0000000000..ba19bb7de1 --- /dev/null +++ b/research/e8-complex-far-retiming/NOTICE.md @@ -0,0 +1,21 @@ +# Notice + +Apache-2.0 (`LICENSE`). Prepared by Chafik Boukhalfa (chafreaky) with substantial Anthropic Claude assistance. + +- `complex/gcert1-e8-r783.json.gz`, `complex/gx/gx.py`, `complex/gx/gxcore.py`: Jacob Sussman, + jacobalansussman/wht-power-saving-lean at `9c94857`, Apache-2.0. The bytes are identical (by git blob id) to the copies vendored in + DaysSky's #363/#375 and aliromman's #365. The upstream LICENSE and NOTICE are kept as `complex/LICENSE-wht-power-saving-lean` + and `complex/NOTICE-wht-power-saving-lean`. +- `data/gcert1-e8-r783-far.json.gz`: this package's certificate, derived from Sussman's by `code/cxfar.py` and `code/cxfinal.py`. + It keeps his format, and its `derived_from` field records the derivation. +- `data/bit/bitE8-r865.json.gz`: utcorvusvolat-dotcom's (Dylan Crow) #370 E8 bit word, byte-identical to #370's and #373's + `data/bit` (Apache-2.0). +- `code/engines/moment.py`, `base_two_moment.py`, `outer.py`: PR #315's engines (DreamingOfClouds; eumemic; the #144/#23 + lineage), as vendored in our #329/#359 packages. +- `code/layout_bk.py`: our reimplementation of DaysSky's B_K rule (#363), on Sussman's B₂ and stage geometry. #363's `bridged.py` was read as data + and not run. +- `code/e8core.py`, `cxcheck.py`, `cxop.py`, `kappa.py`, `cxretime.py`, `cxsplit.py`, `cxpiece.py`, `cxfar.py`, `cxfinal.py`, + `cxkernel.py`: our own code. The retiming and reorder mechanisms follow our #329/#359 stage lineage (#287/#291/#299/#306), and + aliromman's #365 is generalised (read as data, not run). `cxkernel.py` also holds the exterior-gauge kernel census, which is + unprofitable on this unit, together with the helpers it shares with the search (`rehist`, `price`). +- PR #375's halving-degree-two guard (DaysSky) is cited, not vendored. diff --git a/research/e8-complex-far-retiming/PROOF.md b/research/e8-complex-far-retiming/PROOF.md new file mode 100644 index 0000000000..9599a367b3 --- /dev/null +++ b/research/e8-complex-far-retiming/PROOF.md @@ -0,0 +1,34 @@ +# Why the new certificate prices as claimed + +**1. Same operator.** Every change is one of these: +- re-framing a gate or a set of gates; +- splitting a fan-out (s → t₁…t_k) or fan-in (t += s₁…s_k) gate into elementary additions in the same order; +- moving an elementary addition u += c·s across gates that neither read nor write u and do not write s; +- merging consecutive elementary additions that share kind, frame and the shared register (distinct partners). + +Splitting and merging leave the sequence of scalar operations unchanged. In a fan-out every piece reads the unchanged source, and in a fan-in every piece adds into one destination from distinct sources. Moving an addition across operations on disjoint data, or across operations that only read s, commutes, over every coefficient ring and for all initial values. + +`cxop.py` checks the result directly: the complete rational operator on all 1,023 registers (x, y and every dirty slot) after A, the scatter and B, and the 8 retained totals at the scatter, equal Sussman's original. The dirt gate and the undo gate that Sussman's invocation theorem derives from the product are therefore the same. + +**2. Same family data.** These are asserted equal to the original: +- h, v, R, cst and N; +- the ports, the start and final frames, the scatter table and the retained totals with their frames. + +The frame table only grows, by two frames, and is still reduced echelon with no repeats (E0). + +**3. Legal frames.** `gx.check1` (E1) re-checks the following, replayed from the gates: +- every step is nested; +- every gate is at one frame; +- the y roles are at frame 0 before the scatter, and the retained slots are at their frames. + +The block histograms (E6) and N are recomputed and must match the certificate's fields. In addition, every gate frame contains the x-content support of each non-y operand and of a slot destination after the gate (the source-span rule; zero violations on both certificates). + +**4. Price.** In B₃ the profile is 7 × the block histogram plus 2v copies of each idle climb of the layout, with W = 6v + R and deficit 6v − 7·cst = 216. Only the block histogram changes, and b is the largest 10⁻¹⁸ grid point below the exact moment root by both rational engines with the 10⁻¹⁶ fallback, the next grid point excluded. + +With the unsplit idle climbs (#375), the largest child is 38 of m = 63, so the halving degree is 2. This is #375's admitted condition, and the guard quantities it uses (m, W, R, h, cst) are unchanged. + +**5. Assembly.** PR #315's `outer.assembly` with the bit c = 968197503705007/10¹⁸ (#373) and this b: +- all 47 strict constraints hold; +- the complex side binds; +- κ = 95464544449649/10¹⁷; +- κ + 10⁻¹⁸ is rejected. diff --git a/research/e8-complex-far-retiming/README.md b/research/e8-complex-far-retiming/README.md new file mode 100644 index 0000000000..a5c9deb0a3 --- /dev/null +++ b/research/e8-complex-far-retiming/README.md @@ -0,0 +1,111 @@ +κ = 9.5464544449649e-4 + +# Jacob Sussman's E8 complex unit, re-timed by far piece relocation, in the seven-stage bridged word B₃ + +A conditional finite construction with **κ = 95464544449649/10¹⁷ ≈ 9.5464544449649 × 10⁻⁴**: + +- +4.0188·10⁻⁷ (+0.0421 %) over DaysSky's #375 (9.5424356568133e-4), which is Sussman's original unit in the same layout; +- +2.5031·10⁻⁷ (+0.0262 %) over aliromman's #365 certificate in the same layout (9.54395132518163e-4, computed here). + +The complex side binds. The bit side is #370's E8 bit word in #373's B₃ construction. It is unchanged and does not bind. + +| side | supplier | layout | coarse saving | +| --- | --- | --- | ---: | +| complex | **this package's certificate** `gcert1-e8-r783-far` (Sussman's E8 unit, same operator, new frames and gate order) | B₃ unsplit (#375): m = 63, W = 6v + R = 1,503, deficit 216, max child 38, halving degree 2 | b = 477778832512643/(5·10¹⁷) ≈ **9.55557665025286e-4** | +| bit | #370's `bitE8-r865` in #373's B₃ (split at 27) | m = 56, W = 1,585, deficit 216 | c = 968197503705007/10¹⁸ (= #373) | + +| complex certificate | B₃ split b (#363/#373) | B₃ unsplit b (#375) | κ (unsplit, bit #373) | +| --- | ---: | ---: | ---: | +| Sussman's `gcert1-e8-r783` | 9.32783231884153e-4 | 9.55155017966616e-4 | 9.5424356568133e-4 (= #375) | +| #365 (aliromman, 4 moves) | 9.32928054566567e-4 | 9.55306874505079e-4 | 9.54395132518163e-4 | +| **this package** | 9.33167230469274e-4 | **9.55557665025286e-4** | **9.5464544449649e-4** | + +## What changed in the certificate + +The scalar program is the same. The verifier checks that the full rational operator on all 1,023 registers, with every +initial value a variable, and the 8 retained totals are identical to Sussman's original. h, v, R = 783, cst = 72, +N = 9,039, the ports, start/final frames, scatter table and retained totals are also unchanged. + +Only the frames at which additions happen, and the position of some elementary additions, change: + +1. **Split.** Every fan-out or fan-in gate is split into elementary single additions, in the original order. +2. **Retiming.** Moves stay inside each gate's feasible frame interval, the source-span rule included. There are three kinds: + - single gates; + - connected same-frame blocks; + - sub-blocks (time prefixes and suffixes of a block). These generalise #365's move 4, which re-frames two gates of a four-gate block. + + The candidate frames are: + - the participants' neighbour frames; + - the join of the lower bounds; + - the meet of the upper bounds; + - the lower join plus one vector of the meet. +3. **In-group piece relocation.** A subset of one gate's additions moves to the start or end of its group, at a frame of its own. +4. **Far piece relocation** (new here). An elementary addition moves forward or backward across gates it commutes with + as a scalar operation: they neither read nor write its written register, and do not write its read register. It + then takes its best frame between its new neighbours. This is the generalisation of #365's moves 1 and 2. +5. **Merge.** Consecutive elementary additions with the same kind, frame and shared register are merged back into fan-out + or fan-in gates. The result has 775/1,226 gates in A/B, against 773/1,220, and two new frames. + +The greedy search, which uses the χ ledger χ(r) = r((m/r)^a − 1) at m = 63, first reaches #365's certificate value +exactly (b = 9.32928054566567e-4 split). It then finds 14 further far relocations, each worth 0.253·a. Started from +#365's certificate it reaches the same final value. The block-histogram change against Sussman's original is +x {1:+1, 2:+1, 3:+1, 4:+1, 5:−2}, y {1:+2, 2:+8, 4:−22, 5:+2, 6:+10}, s {1:−1, 2:−7, 3:+5}. + +Not profitable on this unit, and not used: +- a shared-donor response kernel through exterior gauges (Sussman's E7/Q2 rule). The 783 slot responses have rank 120, but every relation at a line entrance loses, and higher entrances have no dependent members; +- lowering a retained total's copy rank. No rational combination of the 8 totals is supported in any of the 511 hyperplanes of F₂⁹. + +## Verify + + bash research/e8-complex-far-retiming/verify.sh --quick /tmp/e8st-q # ~15 s + bash research/e8-complex-far-retiming/verify.sh /tmp/e8st-full # adds the regeneration (~1 min) + +Only Python 3.11+ and the standard library are needed. The steps are: + +0. `MANIFEST.sha256`: every file listed, none unlisted. +1. Inputs pinned by git blob id: + - Sussman's certificate, `gx.py` and `gxcore.py`, the same bytes as in #363/#365/#375; + - #370's bit word, the same bytes as #373's `data/bit`. +2. On both Sussman's original and ours: + - Sussman's `gx.check1` (E0–E7, including the exact scalar replay E5) and the `gxcore` mirror histogram; + - a flipped-sign control, which must be rejected with E5; + - our source-span rule (the x-content support of every non-y operand, and of a slot destination, lies in the gate frame). + + Then the full rational operator identity, the unchanged family data, and the histogram delta. +3. Our reimplementation of the bridged layout B_K (#363's rule, Sussman's coordinate-frame stage geometry): + - nested climbs, equal-frame twin gates, stage entries, final frames, and m − 1 moves per role; + - the exchange identity X_p = −y_p, Y_p = x_p; + - B₂ gives Sussman's idle climbs, and B₃ gives #363's for h = 9 and #373's for h = 8. +4. Coarse savings and κ: + - bit c in B₃ (split, = #373) from our own replay of the bit word's local histogram; + - complex b in B₃, split and unsplit, by both rational engines with the 10⁻¹⁶ fallback and the next grid point excluded; + - PR #315's `outer.assembly`: 47 strict constraints, adjacent κ rejected, complex-bound. + + Regressions: Sussman's split b is #363's 932783231884153/10¹⁸, and the unsplit κ is #375's 95424356568133/10¹⁷. +5. Full mode only: our search (`code/cxfar.py`, `code/cxfinal.py`) re-run from Sussman's original must regenerate the shipped certificate byte for byte (decompressed). + +## Admitted conditions and open obligations + +- **No Lean proof of B₃** on either side, and **no Lean proof of the new certificate**. Sussman's Lean theorem + `wht_main_block_B2Ge8x` covers his original certificate in B₂. Here the certificate is checked with his `gx.check1` and + `gxcore`, plus the full operator identity against the original. +- **#375's halving-degree-two guard** for the unsplit complex idle climbs (max child 38 at m = 63: 63² > 2·38²) is + admitted as #375 states it: the row coefficient is 2·1903 + 9909 + 252 = 13,967 < 20,161, with m ≤ 72. The stock + quantities in it (m, W, R, h, cst) are unchanged here. +- **#352/#363's portable complex checks are not re-run on the new certificate.** That covers labels, scalar words and lifetimes, the splice, and the parts of the finite guard that read scalar-macro counts. Its gate count changes from 773/1,220 to 775/1,226 (+0.4 %). +- **The bit side is #373's claim.** We re-certify only c from #370's word with our own engines and layout. #373's checks + of the bit word (seven-stage formal columns, geometry, banks, finite bill) are not re-run here. +- Everything #375/#373/#363/#352 admits: the complex interfaces, precision/recovery and rows, and the analytic transfer. +- This is a conditional finite construction, not a formal verification of the multiplication theorem. + +## Credits + +- **Jacob Sussman:** the E8 unit `gcert1-e8-r783`, `gx`/`gxcore`, the bridged five-stage word and the stage geometry (wht-power-saving-lean, Apache-2.0). +- **aliromman (#365):** the first coupled re-timing of this unit. Our block, sub-block and piece moves generalise its four moves, and our search reproduces its certificate value before going further. +- **DaysSky (#363, #373, #375):** B_K, the seven-stage bit construction and the halving-degree-two complex layout. +- **utcorvusvolat-dotcom (#370):** the E8 bit word. +- **rohanarun (#374):** the #365 composition in split B₃, which we compare against. +- **Our stage lineage:** retiming/descent (#287/#291/#299 via our #329/#359) and reorder (#299/#306), ported to Sussman's format. The far piece relocation is new here. +- PR #315's moment engines and outer assembly (DreamingOfClouds). + +Prepared by Chafik Boukhalfa (chafreaky) with substantial Anthropic Claude assistance. Apache-2.0 (`LICENSE`). See `NOTICE.md` and `PROOF.md`. diff --git a/research/e8-complex-far-retiming/code/cxcheck.py b/research/e8-complex-far-retiming/code/cxcheck.py new file mode 100644 index 0000000000..4973f511e5 --- /dev/null +++ b/research/e8-complex-far-retiming/code/cxcheck.py @@ -0,0 +1,23 @@ +"""Complex certificate checks: Jacob Sussman's gx.check1 (E0-E7: sizes, labels/nesting, equal frames at gates, scatter +cut, gate shapes, bracket rule, exact scalar x-content replay E5, price data E6, exterior gauges E7) and his gxcore +mirror histogram, a flipped-sign control (must fail E5), and the source-span rule (our check: the x-content support of +every non-y operand, and of a slot destination after the gate, lies in the gate frame). +usage: cxcheck.py CERT.json.gz GXDIR""" +import sys, os, gzip, json, copy +from collections import Counter +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from cxretime import CX +c = json.load(gzip.open(sys.argv[1])); sys.path.insert(0, sys.argv[2]); import gx, gxcore +st = {}; gx.check1(c, scalar=True, stats=st) +Hinv = Counter() +for w in c['blocks'].values(): + for r, n in w.items(): Hinv[int(r)] += n +assert gxcore.mirror(gxcore.normal(copy.deepcopy(c)))['hist'] == dict(Hinv), 'gxcore histogram' +c2 = copy.deepcopy(c) +for g in c2['A']: + if g[0] in ('out', 'in') and g[3]: t, a, b = g[3][0]; g[3][0] = [t, -a, b]; break +try: gx.check1(c2, scalar=True); raise SystemExit('flipped sign accepted') +except AssertionError as e: assert str(e).startswith('E5'), str(e) +sv = CX(c).span_ok(); assert sv == 0, sv +print(' %s: gx.check1 PASS (E0-E7, exact scalar), gxcore mirror PASS, flipped sign rejected (E5), source-span violations 0 | h %d v %d R %d cst %d N %d, gates A/B %d/%d, frames %d' + % (os.path.basename(sys.argv[1]), c['h'], c['v'], c['R'], c['cst'], c['N'], len(c['A']), len(c['B']), len(c['frames']))) diff --git a/research/e8-complex-far-retiming/code/cxfar.py b/research/e8-complex-far-retiming/code/cxfar.py new file mode 100644 index 0000000000..a8e53204f9 --- /dev/null +++ b/research/e8-complex-far-retiming/code/cxfar.py @@ -0,0 +1,85 @@ +"""Far piece relocation on a split gcert/1 program: an elementary piece (out: u += c s with s only read; in: s += c u with +u only read) moves forward/backward across gates that commute with it as scalar operations (they touch neither u, nor +write the read register; for an in-piece, other gates may only ADD into s from sources other than u, never read s), +within its phase, and gets its own best frame between the new neighbours. Greedy best move per pass. +usage: cxfar.py CERT(.json.gz or anneal output) GXDIR OUT [passes]""" +import sys, os, gzip, json, math, copy, random +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from cxpiece import * +from cxretime import sweep as bsweep + + +def rw(g): + """(reads, writes) of a gate""" + if g[0] == 'neg': return {g[2]}, {g[2]} + if g[0] == 'out': return {g[2]} | {t for t, _, _ in g[3]} | set(g[4]), {t for t, _, _ in g[3]} + return {g[2]} | {s for s, _, _ in g[3]}, {g[2]} + + +def far_pass(c, tag, a, m): + M = CX(c); d = lambda f: len(M.fr[f]); chi = lambda r: 0.0 if r <= 0 else r * (math.exp(a * math.log(m / r)) - 1) + G = M.gates; best = None + for i, (ph, g) in enumerate(G): + if g[0] == 'out' and len(g[3]) == 1 and not g[4]: u = g[3][0][0]; s = g[2]; kind = 'out' + elif g[0] == 'in' and len(g[3]) == 1: u = g[2]; s = g[3][0][0]; kind = 'in' # u written (dst), s read + else: continue + # registers: w = written register (u for out, u for in): naming: wreg written, rreg read + wreg, rreg = u, s + iw, ir = M.where[i][wreg], M.where[i][rreg] + f_i = M.gf[i] + pw, nw = M.F(wreg, iw - 1), M.F(wreg, iw + 1) + a_r, b_r = M.F(rreg, ir - 1), M.F(rreg, ir + 1) + rem_r = chi(d(f_i) - d(a_r)) + chi(d(b_r) - d(f_i)) - chi(d(b_r) - d(a_r)) + for step in (1, -1): + j = i + while True: + j += step + if j < 0 or j >= len(G) or G[j][0] != ph: break + rj, wj = rw(G[j][1]) + if wreg in rj or rreg in wj: break # does not commute (another addition into wreg also breaks: keep simple) + if rreg not in rj: continue # read register untouched: same neighbours + # new position just after j (forward) or just before j (backward): rreg neighbours change + jr = M.where[j][rreg] + if step == 1: ps, ns = M.F(rreg, jr), M.F(rreg, jr + 1) if jr + 1 != ir else b_r + else: ps, ns = (M.F(rreg, jr - 1) if jr - 1 != ir else a_r), M.F(rreg, jr) + if step == 1 and jr + 1 == ir: continue + loE = echelon(list(M.lb[i]) + list(M.fr[ps]) + list(M.fr[pw])) + hiV = span_vecs(M.fr[ns]) & span_vecs(M.fr[nw]) + if not all(x in hiV for x in loE): continue + cands = {M.fid(loE), M.fid(echelon(sorted(hiV))), ps, ns, pw, nw} + for x in sorted(hiV - span_vecs(loE))[:64]: cands.add(M.fid(echelon(loE + [x]))) + old_w = chi(d(f_i) - d(pw)) + chi(d(nw) - d(f_i)) + for F in cands: + if not (inside(M.fr[ps], M.fr[F]) and inside(M.fr[F], M.fr[ns]) and inside(M.fr[pw], M.fr[F]) and inside(M.fr[F], M.fr[nw]) and inside(loE, M.fr[F])): continue + gain = old_w - chi(d(F) - d(pw)) - chi(d(nw) - d(F)) + rem_r - (chi(d(F) - d(ps)) + chi(d(ns) - d(F)) - chi(d(ns) - d(ps))) + if gain > 1e-9 and (best is None or gain > best[0]): best = (gain, i, j, step, F) + if best is None: return c, tag, 0.0 + gain, i, j, step, F = best + gates = [copy.deepcopy(x) for x in G] + for k in range(len(gates)): gates[k][1][1] = M.gf[k] + gates[i][1][1] = F + order = [k for k in range(len(gates)) if k != i] + pos = order.index(j) + (1 if step == 1 else 0); order.insert(pos, i) + c2 = copy.deepcopy(c); c2['frames'] = [list(x) for x in M.fr]; c2['A'] = []; c2['B'] = []; tag2 = [] + for k in order: + c2[gates[k][0]].append(gates[k][1]); tag2.append(list(tag[k][:2]) + (['far', k] if k == i else list(tag[k][2:]))) + return rehist(c2), tag2, gain + + +if __name__ == '__main__': + src = json.load(gzip.open(sys.argv[1])); sys.path.insert(0, sys.argv[2]); import gx + if 'cert' in src: c, tag = src['cert'], [tuple(t) for t in src['tag']] + else: c, tag = split(src) + K = 3; h = c['h']; m = (2 * K + 1) * h; b0 = price(c, K); a = float(b0); rng = random.Random(1) + print('start b3', b0, float(b0)); passes = int(sys.argv[4]) if len(sys.argv) > 4 else 100 + for p in range(passes): + c, tag, g = far_pass(c, tag, a, m) + M = CX(c) + for s in range(10): + m1, _ = single_sweep(M, a, m, rng); m2, _ = bsweep(M, a, m, rng) + if not (m1 or m2): break + c = rehist(to_cert(M)); c, tag, g2 = piece_pass(c, tag, a, m) + b = price(c, K); print('pass', p, 'far gain/a %.3f piece %.3f' % (g / a, g2 / a), 'b3', b, '%.12e' % float(b), flush=True) + if g == 0 and g2 == 0: break + gx.check1(c, scalar=True); print('gx.check1 PASS') + json.dump(dict(cert=c, tag=[list(t) for t in tag]), gzip.open(sys.argv[3], 'wt')) diff --git a/research/e8-complex-far-retiming/code/cxfinal.py b/research/e8-complex-far-retiming/code/cxfinal.py new file mode 100644 index 0000000000..3c55ca4c63 --- /dev/null +++ b/research/e8-complex-far-retiming/code/cxfinal.py @@ -0,0 +1,29 @@ +"""Finalize an optimised split program: merge consecutive elementary gates of one phase that share kind, frame and the +read register (fan-out: same source, distinct targets) or the destination (fan-in: same destination, distinct sources), +drop unused appended frames, recompute the block histograms and N. usage: cxfinal.py IN(anneal/far output) ORIG OUT""" +import sys, os, gzip, json, copy +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from cxkernel import rehist +def finalize(c, c0): + c = copy.deepcopy(c) + for ph in ('A', 'B'): + out = [] + for g in c[ph]: + p = out[-1] if out else None + if p and p[0] == g[0] == 'out' and p[1] == g[1] and p[2] == g[2] and not ({t for t, _, _ in g[3]} | set(g[4])) & ({t for t, _, _ in p[3]} | set(p[4])): + p[3] = p[3] + g[3]; p[4] = p[4] + g[4] + elif p and p[0] == g[0] == 'in' and p[1] == g[1] and p[2] == g[2] and not {s for s, _, _ in g[3]} & {s for s, _, _ in p[3]}: + p[3] = p[3] + g[3] + else: out.append(copy.deepcopy(g)) + c[ph] = out + used = set(c['start']) | set(c['final']) | {g[1] for g in c['A'] + c['B']} | {f for _, _, f in c['ret']} + keep = list(range(len(c0['frames']))) + [i for i in range(len(c0['frames']), len(c['frames'])) if i in used] + ren = {o: n for n, o in enumerate(keep)}; c['frames'] = [c['frames'][i] for i in keep] + for ph in ('A', 'B'): + for g in c[ph]: g[1] = ren[g[1]] + c['derived_from'] = c0['derived_from'] + ' | e8st far piece relocation + block retiming (chafreaky)' + return rehist(c) +if __name__ == '__main__': + x = json.load(gzip.open(sys.argv[1])); c = x['cert'] if 'cert' in x else x; c0 = json.load(gzip.open(sys.argv[2])) + w = finalize(c, c0); print('gates A/B', len(w['A']), len(w['B']), 'frames', len(w['frames'])) + with gzip.GzipFile(sys.argv[3], 'wb', mtime=0) as f: f.write(json.dumps(w, separators=(',', ':')).encode()) diff --git a/research/e8-complex-far-retiming/code/cxkernel.py b/research/e8-complex-far-retiming/code/cxkernel.py new file mode 100644 index 0000000000..32b26a1755 --- /dev/null +++ b/research/e8-complex-far-retiming/code/cxkernel.py @@ -0,0 +1,222 @@ +"""Shared-donor response kernel on a gcert/1 complex program (Sussman's format), via exterior gauges (rule E7/Q2). +Entry: pivot slot p, entrance E (a frame inside p's first frame and inside every donor's first frame), donors d with +rational coefficients c_d such that resp(p) + sum c_d resp(d) = 0, where resp(s) in Q^v = the column of slot s's +initial value in the y rows of the product (A, scatter, B). Transform: p starts at E (ext gauge [p, E]); at the start +of A each donor climbs to E and takes d += c_d p ('in' gate at E). Then Q2 holds for p. +Price (entrance-residual rule, as the bit side's #320/#329 lineage): W = 2Kv + sum_slots (h - dim ext)/h, deficit fixed. +usage: cxkernel.py CERT.json.gz GXDIR OUT.json.gz [K]""" +import sys, os, gzip, json, math, copy +from fractions import Fraction as Fr +from collections import Counter +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from e8core import coarse +from layout_bk import layout + + +def reduce(u, basis): + for b in basis: + if u >> (b.bit_length() - 1) & 1: u ^= b + return u +def inside(a, b): return all(reduce(u, b) == 0 for u in a) +def echelon(vecs): + """reduced echelon (descending pivots) of the F2 span of vecs""" + B = [] + for u in vecs: + u = reduce(u, B) + if u: + B = [b ^ u if b >> (u.bit_length() - 1) & 1 else b for b in B] + [u] + B.sort(reverse=True) + return B + + +def responses(c): + v, R = c['v'], c['R']; n = 2 * v + R + reg = [dict() for _ in range(n)] + for r in range(2 * v, n): reg[r] = {r: Fr(1)} + def axpy(t, src, co): + d = reg[t] + for k, u in src.items(): + w = d.get(k, 0) + co * u + if w: d[k] = w + else: d.pop(k, None) + def run(gates): + for g in gates: + if g[0] == 'neg': reg[g[2]] = {k: -u for k, u in reg[g[2]].items()} + elif g[0] == 'out': + src = dict(reg[g[2]]) + for t, a, b in g[3]: axpy(t, src, Fr(a, b)) + else: + for s, a, b in g[3]: axpy(g[2], reg[s], Fr(a, b)) + run(c['A']) + tot = {k: dict(reg[s]) for k, s, _ in c['ret']} + for t in range(v): + for k, a, b in c['scat']['table'][t]: axpy(v + t, tot[k], Fr(a, b)) + run(c['B']) + resp = {s: {} for s in range(2 * v, n)} + for t in range(v): + for s, co in reg[v + t].items(): resp[s][t] = co + return resp + + +def first_frames(c): + v, R = c['v'], c['R']; n = 2 * v + R; first = {} + for ph in ('A', 'B'): + for g in c[ph]: + regs = [g[2]] + ([t for t, _, _ in g[3]] + list(g[4]) if g[0] == 'out' else [s for s, _, _ in g[3]] if g[0] == 'in' else []) + for r in regs: + if r >= 2 * v and r not in first and g[1] != 0: first[r] = g[1] + return first + + +class QBasis: + """incremental rational basis over Q^v with combination tracking (donor slots)""" + def __init__(self): self.rows = [] # (pivot, vec dict, combo dict) + def reduce(self, x): + x = dict(x); cm = {} + for p, vec, co in self.rows: + if p in x: + f = x[p] / vec[p] + for k, u in vec.items(): + w = x.get(k, 0) - f * u + if w: x[k] = w + else: x.pop(k, None) + for k, u in co.items(): + w = cm.get(k, 0) - f * u + if w: cm[k] = w + else: cm.pop(k, None) + return x, cm + def add(self, x, slot): + r, cm = self.reduce(x) + cm = {k: u for k, u in cm.items()}; cm[slot] = cm.get(slot, 0) + 1 + p = min(r); self.rows.append((p, r, cm)) + + +def select(c, a, K=3): + h, v, R = c['h'], c['v'], c['R']; m = (2 * K + 1) * h + fr = [tuple(f) for f in c['frames']]; dim = [len(f) for f in fr] + chi = lambda r: 0.0 if r <= 0 else r * (math.exp(a * math.log(m / r)) - 1) + resp = responses(c); first = first_frames(c) + ret = {s for _, s, _ in c['ret']} + H = [s for s in range(2 * v, 2 * v + R) if s not in ret and s in first and c['start'][s] == 0] + # candidate entrances: frames of the table inside some first frame, plus pairwise intersections not in table skipped + fset = sorted(set(first[s] for s in H)) + cand = [E for E in range(2, len(fr)) if dim[E] > 0 and dim[E] < h and any(inside(fr[E], fr[f]) for f in fset)] + ins = {} + def isin(E, f): + k = (E, f) + if k not in ins: ins[k] = inside(fr[E], fr[f]) + return ins[k] + groups = {E: [s for s in H if isin(E, first[s])] for E in cand} + avail_p = set(H); avail_d = set(H); donor_at = {} + def plan(E): + e = dim[E]; G = groups[E]; mem = [] + for s in G: + d1 = dim[first[s]] + dc = (0.0 if donor_at.get(s) == E else (chi(e) + chi(d1 - e) - chi(d1)) if s in avail_d else None) + if s in donor_at and donor_at[s] != E: dc = None + pg = chi(d1) - chi(d1 - e) if s in avail_p else None + if dc is None and pg is None: continue + mem.append((s, dc, pg)) + mem.sort(key=lambda t: (t[1] if t[1] is not None else 1e9, t[2] if t[2] is not None else -1)) + B = QBasis(); slots = {}; piv = [] + for s, dc, pg in mem: + r, cm = B.reduce(resp[s]) + if not r and pg is not None and pg > 0: piv.append((s, cm, pg)) + elif r and dc is not None: B.add(resp[s], s); slots[s] = dc + if not piv: return None + keep = list(piv) + def users(): + u = Counter() + for _, cm, _ in keep: + for k in cm: u[k] += 1 + return u + while keep: + u = users(); best = None + for i, (s, cm, pg) in enumerate(keep): + freed = sum(slots[k] for k in cm if u[k] == 1) + if freed > pg and (best is None or freed - pg > best[0]): best = (freed - pg, i) + if best is None: break + keep.pop(best[1]) + u = users(); gain = sum(pg for _, _, pg in keep) - sum(slots[k] for k in u) + if gain <= 1e-15: return None + # coefficients: resp(p) = sum cm_k resp(k) -> donor gate d += (-cm_d) p + return gain, [(s, {k: -x for k, x in cm.items()}) for s, cm, _ in keep] + plans = {E: plan(E) for E in cand} + chosen = []; total = 0.0 + while True: + live = [(p[0], E) for E, p in plans.items() if p] + if not live: break + g, E = max(live); _, ents = plans[E] + touched = set() + for p, cm in ents: + chosen.append((E, p, cm)); avail_p.discard(p); avail_d.discard(p); touched.add(p) + for d in cm: donor_at[d] = E; avail_p.discard(d); avail_d.discard(d); touched.add(d) + total += g + for E2 in cand: + if plans.get(E2) and any(s in touched for s in groups[E2]): plans[E2] = plan(E2) + plans[E] = plan(E) + return chosen, total + + +def apply(c, chosen): + c = copy.deepcopy(c); head = [] + ext = [] + for E, p, cm in chosen: + ext.append([p, E]); c['start'][p] = E + byd = {} + for E, p, cm in chosen: + for d, co in cm.items(): byd.setdefault((d, E), []).append([p, co.numerator, co.denominator]) + for (d, E), lst in sorted(byd.items()): head.append(['in', E, d, sorted(lst)]) + c['A'] = head + c['A']; c['ext'] = sorted(ext) + return c + + +def rehist(c): + """recompute block histograms and N exactly as gx.check1 counts them (climbs per class, COPY ranks)""" + v, R = c['v'], c['R']; n = 2 * v + R; dim = [len(f) for f in c['frames']] + cls = lambda r: 'x' if r < v else 'y' if r < 2 * v else 's' + cur = list(c['start']); H = {'x': Counter(), 'y': Counter(), 's': Counter(), 'c': Counter()} + def climb(r, f): + if cur[r] != f: H[cls(r)][dim[f] - dim[cur[r]]] += 1; cur[r] = f + for ph in ('A', 'B'): + if ph == 'B': + for k, s_, f in c['ret']: H['c'][dim[f]] += 1 + for g in c[ph]: + regs = [g[2]] + ([t for t, _, _ in g[3]] + list(g[4]) if g[0] == 'out' else [x for x, _, _ in g[3]] if g[0] == 'in' else []) + for r in regs: climb(r, g[1]) + for r in range(n): climb(r, c['final'][r]) + c['blocks'] = {k: {str(r): x for r, x in sorted(w.items())} for k, w in H.items()} + c['N'] = sum(r * x for w in H.values() for r, x in w.items()) + return c + + +def price(c, K): + """returns b in B_K with the entrance-residual W""" + h, v, R, cst = c['h'], c['v'], c['R'], c['cst'] + Hinv = Counter() + for w in c['blocks'].values(): + for r, n in w.items(): Hinv[int(r)] += n + s = 2 * K + 1; m = s * h; lay = layout(K, h, split=(m - 1) // 2 if K > 2 else None) + tails = sum(len(c['frames'][f]) for _, f in c['ext']) + T = 1 if tails % h == 0 else h + H = Counter({r: T * s * n for r, n in Hinv.items()}) + for r in lay['blocks']: H[r] += T * 2 * v + W = (2 * K * v + R) * T - tails * T // h; assert tails * T % h == 0 + assert m * W - sum(r * n for r, n in H.items()) == T * (2 * K * v - s * cst), 'deficit' + assert 2 * max(H) < m + return coarse(H, m, W) + + +if __name__ == '__main__': + c = json.load(gzip.open(sys.argv[1])); sys.path.insert(0, sys.argv[2]); import gx, gxcore + K = int(sys.argv[4]) if len(sys.argv) > 4 else 3 + b0 = price(c, K); print('start b(B_%d) =' % K, b0, float(b0)) + chosen, tot = select(c, float(b0), K) + fr = c['frames'] + print('entries', len(chosen), 'chi %.5f' % tot, 'ranks', Counter(len(fr[E]) for E, _, _ in chosen), + 'donors', len({d for _, _, cm in chosen for d in cm})) + w = rehist(apply(c, chosen)); w['derived_from'] = c['derived_from'] + ' + shared-donor kernel via exterior gauges' + st = {}; gx.check1(w, scalar=True, stats=st); print('gx.check1 (E0-E7 incl. exact scalar E5 and Q2) PASS', st) + for K2 in (2, 3, 4): + b = price(w, K2); print('b(B_%d) =' % K2, b, float(b)) + json.dump(w, gzip.open(sys.argv[3], 'wt')) diff --git a/research/e8-complex-far-retiming/code/cxop.py b/research/e8-complex-far-retiming/code/cxop.py new file mode 100644 index 0000000000..772d6698da --- /dev/null +++ b/research/e8-complex-far-retiming/code/cxop.py @@ -0,0 +1,32 @@ +"""Full rational operator of a gcert/1 program (A, scatter, B) on all registers, every initial value a variable; +compare two programs. usage: cxop.py CERT1 CERT2 [GXDIR]""" +import sys, gzip, json +from fractions import Fraction as Fr +def load(p): + x = json.load(gzip.open(p)); return x['cert'] if 'cert' in x else x +def op(c): + v, R = c['v'], c['R']; n = 2 * v + R + reg = [{r: Fr(1)} for r in range(n)] + def axpy(t, src, co): + d = reg[t] + for k, u in src.items(): + w = d.get(k, 0) + co * u + if w: d[k] = w + else: d.pop(k, None) + def run(G): + for g in G: + if g[0] == 'neg': reg[g[2]] = {k: -u for k, u in reg[g[2]].items()} + elif g[0] == 'out': + src = dict(reg[g[2]]) + for t, a, b in g[3]: axpy(t, src, Fr(a, b)) + else: + for s, a, b in g[3]: axpy(g[2], reg[s], Fr(a, b)) + run(c['A']); tot = {k: dict(reg[s]) for k, s, _ in c['ret']} + for t in range(v): + for k, a, b in c['scat']['table'][t]: axpy(v + t, tot[k], Fr(a, b)) + run(c['B']); return reg, tot +a, ta = op(load(sys.argv[1])); b, tb = op(load(sys.argv[2])) +assert ta == tb, 'retained totals differ' +diff = [r for r in range(len(a)) if a[r] != b[r]] +print('full rational operator on all %d registers (every initial value a variable) and the retained totals: %s' % (len(a), 'IDENTICAL' if not diff else 'DIFFER at %d registers' % len(diff))) +assert not diff diff --git a/research/e8-complex-far-retiming/code/cxpiece.py b/research/e8-complex-far-retiming/code/cxpiece.py new file mode 100644 index 0000000000..6cebe19c80 --- /dev/null +++ b/research/e8-complex-far-retiming/code/cxpiece.py @@ -0,0 +1,64 @@ +"""Piece relocation on a split gcert/1 program: a fan-out piece (s -> t) or fan-in piece (t += s) of an original gate +moves to the end (frame F >= the group frame) or the start (F <= the group frame) of its group, with its own frame +F between the outside neighbours of the shared register and of its own register (source-span rule kept). +Pieces of one original gate commute (fan-out pieces read an unchanged source; fan-in pieces add into one destination).""" +import sys, os, gzip, json, math, random, copy +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from cxsplit import * + + +def piece_pass(c, tag, a, m, eps=1e-9): + M = CX(c); d = lambda f: len(M.fr[f]); chi = lambda r: 0.0 if r <= 0 else r * (math.exp(a * math.log(m / r)) - 1) + groups = {} + for gi, t in enumerate(tag): groups.setdefault(tuple(t[:2]), []).append(gi) + best = None + for t, gis in groups.items(): + if len(gis) < 2: continue + f = M.gf[gis[0]] + if any(M.gf[g] != f for g in gis): continue + g0 = M.gates[gis[0]][1]; shared = g0[2] + if any(M.gates[g][1][2] != shared for g in gis): continue + ish = [M.where[x][shared] for x in gis] + info = [] + for gi in gis: + uniq = [r for r in M.where[gi] if r != shared] + if len(uniq) != 1: info.append(None); continue + u = uniq[0]; iu = M.where[gi][u]; info.append((u, M.F(u, iu - 1), M.F(u, iu + 1))) + for side in ('end', 'start'): + if side == 'end': ps, ns = f, M.F(shared, max(ish) + 1) + else: ps, ns = M.F(shared, min(ish) - 1), f + cands = set() + for k, gi in enumerate(gis): + if info[k] is None: continue + u, pu, nu = info[k] + loE = echelon(list(M.lb[gi]) + list(M.fr[ps]) + list(M.fr[pu])) + hiV = span_vecs(M.fr[ns]) & span_vecs(M.fr[nu]) + if not all(x in hiV for x in loE): continue + cands |= {M.fid(loE), M.fid(echelon(sorted(hiV))), ps, ns, pu, nu} + for x in sorted(hiV - span_vecs(loE)): cands.add(M.fid(echelon(loE + [x]))) + for F in cands: + if not (inside(M.fr[ps], M.fr[F]) and inside(M.fr[F], M.fr[ns])): continue + chosen = []; gain = 0.0 + for k, gi in enumerate(gis): + if info[k] is None: continue + u, pu, nu = info[k] + if not (inside(M.fr[pu], M.fr[F]) and inside(M.fr[F], M.fr[nu]) and inside(M.lb[gi], M.fr[F])): continue + du = chi(d(f) - d(pu)) + chi(d(nu) - d(f)) - chi(d(F) - d(pu)) - chi(d(nu) - d(F)) + if du > eps: chosen.append(gi); gain += du + if not chosen or len(chosen) == len(gis): continue + gain -= chi(d(F) - d(ps)) + chi(d(ns) - d(F)) - chi(d(ns) - d(ps)) + if gain > eps and (best is None or gain > best[0]): best = (gain, chosen, side, F, gis) + if best is None: return c, tag, 0.0 + g_, chosen, side, F, gis = best + gates = [copy.deepcopy(M.gates[i]) for i in range(len(M.gates))] + for i in range(len(gates)): gates[i][1][1] = M.gf[i] + for gi in chosen: gates[gi][1][1] = F + rest = [i for i in range(len(gates)) if i not in set(chosen)] + anchor = max(set(gis) - set(chosen)) if side == 'end' else min(set(gis) - set(chosen)) + pos = rest.index(anchor) + (1 if side == 'end' else 0) + order = rest[:pos] + sorted(chosen) + rest[pos:] + c2 = copy.deepcopy(c); c2['frames'] = [list(x) for x in M.fr]; c2['A'] = []; c2['B'] = []; tag2 = [] + for i in order: + c2[gates[i][0]].append(gates[i][1]) + tag2.append(list(tag[i][:2]) + ([side, len(tag)] if i in chosen else [])) + return rehist(c2), tag2, g_ diff --git a/research/e8-complex-far-retiming/code/cxretime.py b/research/e8-complex-far-retiming/code/cxretime.py new file mode 100644 index 0000000000..06f79f8280 --- /dev/null +++ b/research/e8-complex-far-retiming/code/cxretime.py @@ -0,0 +1,169 @@ +"""Retiming / descent on a gcert/1 complex program: move gate frames inside +[span(LB_g + prev frames of participants), AND of next frames] with the source-span rule (the x-content support of every +non-y operand, and of a slot destination after the gate, lies in the gate frame). Pins: start, scatter (y at 0, retained +slots at their ret frame), final. Greedy best-improvement with the chi ledger at m = (2K+1)h. +usage: cxretime.py CERT.json.gz GXDIR OUT.json.gz [K] [sweeps]""" +import sys, os, gzip, json, math, random, copy +from fractions import Fraction as Fr +from collections import Counter +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from cxkernel import reduce, inside, echelon, rehist, price + + +def regs_of(g): + if g[0] == 'neg': return [g[2]] + if g[0] == 'out': return [g[2]] + [t for t, _, _ in g[3]] + list(g[4]) + return [g[2]] + [s for s, _, _ in g[3]] + + +class CX: + def __init__(self, c): + self.c = c; v = c['v']; self.v = v; n = 2 * v + c['R']; self.n = n + self.fr = [tuple(f) for f in c['frames']]; self.key = {f: i for i, f in enumerate(self.fr)} + self.gates = [['A', g] for g in c['A']] + [['B', g] for g in c['B']] + self.gf = [g[1] for _, g in self.gates] + nA = len(c['A']) + occ = [[('p', c['start'][r])] for r in range(n)] + retf = {s: f for _, s, f in c['ret']} + for gi, (ph, g) in enumerate(self.gates): + if gi == nA: + for t in range(v): occ[v + t].append(('p', 0)) + for s, f in retf.items(): occ[s].append(('p', f)) + for r in regs_of(g): occ[r].append(('g', gi)) + for r in range(n): occ[r].append(('p', c['final'][r])) + self.occ = occ; self.where = [dict() for _ in self.gates] + for r in range(n): + for i, (k, x) in enumerate(occ[r]): + if k == 'g': self.where[x][r] = i + self.lb = self._lb() + + def _lb(self): + c = self.c; v = self.v; ports = c['ports'] + sup = [{r} if r < v else set() for r in range(self.n)] + lb = [] + for ph, g in self.gates: + need = set() + if g[0] == 'out': + for t, _, _ in g[3]: sup[t] |= sup[g[2]] + if g[2] < v or g[2] >= 2 * v: need |= sup[g[2]] + for t, _, _ in g[3]: + if t >= 2 * v: need |= sup[t] + elif g[0] == 'in': + for s, _, _ in g[3]: + sup[g[2]] |= sup[s] + if s < v or s >= 2 * v: need |= sup[s] + if g[2] >= 2 * v: need |= sup[g[2]] + lb.append(tuple(echelon([ports[x] for x in need]))) + return lb + + def fid(self, f): + f = tuple(f) + if f not in self.key: self.key[f] = len(self.fr); self.fr.append(f) + return self.key[f] + + def F(self, r, i): + k, x = self.occ[r][i]; return x if k == 'p' else self.gf[x] + + def span_ok(self): + return sum(1 for gi in range(len(self.gates)) if not inside(self.lb[gi], self.fr[self.gf[gi]])) + + +_sv = {} +def span_vecs(B): + B = tuple(B) + if B not in _sv: + out = {0} + for b in B: out |= {x ^ b for x in out} + _sv[B] = frozenset(out) + return _sv[B] + + +def block_of(M, gi): + f = M.gf[gi]; S = {gi}; stack = [gi] + while stack: + g = stack.pop() + for r, i in M.where[g].items(): + for j in (i - 1, i + 1): + k, x = M.occ[r][j] + if k == 'g' and x not in S and M.gf[x] == f: S.add(x); stack.append(x) + return S + + +def try_move(M, S, chi, d, temp=0.0, rng=None): + """move gate set S (all at one frame) to the best feasible frame; returns gain or None""" + old = M.gf[next(iter(S))]; regs = {} + for g in S: + for r, i in M.where[g].items(): regs.setdefault(r, []).append(i) + parts = [] + for r, ii in regs.items(): + lo_i, hi_i = min(ii), max(ii) + if len(ii) != hi_i - lo_i + 1: return None + parts.append((M.F(r, lo_i - 1), M.F(r, hi_i + 1))) + loE = echelon([u for g in S for u in M.lb[g]] + [u for p, _ in parts for u in M.fr[p]]) + hiV = None + for _, q in parts: + vs = span_vecs(M.fr[q]); hiV = vs if hiV is None else hiV & vs + if not all(u in hiV for u in loE): return None + cands = {M.fid(loE), M.fid(echelon(sorted(hiV)))} | {p for p, _ in parts} | {q for _, q in parts} + for u in sorted(hiV - span_vecs(loE)): cands.add(M.fid(echelon(loE + [u]))) + def ok(F): + return all(inside(M.fr[p], M.fr[F]) and inside(M.fr[F], M.fr[q]) for p, q in parts) and inside(loE, M.fr[F]) + def score(F): return sum(chi(d(F) - d(p)) + chi(d(q) - d(F)) for p, q in parts) + base = score(old); opts = [(score(F), F) for F in cands if F != old and ok(F)] + if not opts: return None + if temp > 0: + sc, F = rng.choice(opts) + if sc < base - 1e-12 or rng.random() < math.exp(-(sc - base) / temp): + for g in S: M.gf[g] = F + return base - sc + return None + sc, F = min(opts) + if sc < base - 1e-12: + for g in S: M.gf[g] = F + return base - sc + return None + + +def subblocks(M, S): + T = sorted(S); out = [S] + for k in range(1, len(T)): out.append(set(T[:k])); out.append(set(T[k:])) + out += [{g} for g in T] if len(T) > 1 else [] + return out + + +def sweep(M, a, m, rng, temp=0.0): + d = lambda f: len(M.fr[f]); chi = lambda r: 0.0 if r <= 0 else r * (math.exp(a * math.log(m / r)) - 1) + moved = 0; gain = 0.0; idx = list(range(len(M.gates))); rng.shuffle(idx); done = set() + for gi in idx: + if gi in done: continue + S = block_of(M, gi); done |= S + subs = subblocks(M, S) if len(S) <= 12 else [S] + if temp > 0: subs = [rng.choice(subs)] + for sub in subs: + g = try_move(M, sub, chi, d, temp, rng) + if g is not None: moved += 1; gain += g; break + return moved, gain + + +def to_cert(M): + c = copy.deepcopy(M.c); c['frames'] = [list(f) for f in M.fr] + A = []; B = [] + for gi, (ph, g) in enumerate(M.gates): + g2 = copy.deepcopy(g); g2[1] = M.gf[gi]; (A if ph == 'A' else B).append(g2) + c['A'] = A; c['B'] = B + return rehist(c) + + +if __name__ == '__main__': + c = json.load(gzip.open(sys.argv[1])); sys.path.insert(0, sys.argv[2]); import gx + K = int(sys.argv[4]) if len(sys.argv) > 4 else 3; sw = int(sys.argv[5]) if len(sys.argv) > 5 else 20 + h = c['h']; m = (2 * K + 1) * h + M = CX(c); print('span violations on input', M.span_ok()) + b0 = price(c, K); print('start b', b0, float(b0)); rng = random.Random(1) + for s in range(sw): + mv, g = sweep(M, float(b0), m, rng); print('sweep', s, 'moved', mv, 'chi/a %.4f' % (g / float(b0)), flush=True) + if not mv: break + w = to_cert(M); print('span violations', M.span_ok()) + st = {}; gx.check1(w, scalar=True, stats=st); print('gx.check1 PASS') + for K2 in (2, 3): b = price(w, K2); print('b(B_%d) =' % K2, b, float(b)) + json.dump(w, gzip.open(sys.argv[3], 'wt')) diff --git a/research/e8-complex-far-retiming/code/cxsplit.py b/research/e8-complex-far-retiming/code/cxsplit.py new file mode 100644 index 0000000000..e9cd44a1fe --- /dev/null +++ b/research/e8-complex-far-retiming/code/cxsplit.py @@ -0,0 +1,92 @@ +"""Split every multi-addition gate of a gcert/1 program into elementary single-addition gates in the same order (same +scalar operator: fan-out pieces read an unchanged source, fan-in pieces add into one destination), then run block and +single-gate retiming (cxretime) on the split program, then re-merge adjacent pieces of one original gate that kept a +common frame. usage: cxsplit.py CERT.json.gz GXDIR OUT.json.gz [K] [sweeps] [seed]""" +import sys, os, gzip, json, math, random, copy +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from cxretime import * +from cxretime import sweep as block_sweep + + +def split(c): + c = copy.deepcopy(c); tag = [] + for ph in ('A', 'B'): + out = [] + for gi, g in enumerate(c[ph]): + if g[0] == 'out' and len(g[3]) + len(g[4]) > 1: + pieces = [['out', g[1], g[2], [x], []] for x in g[3]] + if g[4]: pieces[0][4] = list(g[4]) if pieces else None + for p_ in pieces: out.append(p_); tag.append((ph, gi)) + elif g[0] == 'in' and len(g[3]) > 1: + for x in g[3]: out.append(['in', g[1], g[2], [x]]); tag.append((ph, gi)) + else: out.append(g); tag.append((ph, gi)) + c[ph] = out + return c, tag + + +def merge(c, tag): + c = copy.deepcopy(c); k = 0 + for ph in ('A', 'B'): + out = []; prev = None + for g in c[ph]: + t = tag[k]; k += 1 + if prev is not None and prev[0] == t and out[-1][0] == g[0] and out[-1][1] == g[1] and out[-1][2] == g[2]: + out[-1][3] = out[-1][3] + g[3] + if g[0] == 'out': out[-1][4] = out[-1][4] + g[4] + else: out.append(copy.deepcopy(g)) + prev = (t,) + c[ph] = out + return c + + +def single_sweep(M, a, m, rng): + d = lambda f: len(M.fr[f]); chi = lambda r: 0.0 if r <= 0 else r * (math.exp(a * math.log(m / r)) - 1) + moved = 0; gain = 0.0; idx = list(range(len(M.gates))); rng.shuffle(idx) + for gi in idx: + old = M.gf[gi]; parts = [(M.F(r, i - 1), M.F(r, i + 1)) for r, i in M.where[gi].items()] + loE = echelon(list(M.lb[gi]) + [u for p, _ in parts for u in M.fr[p]]) + hiV = None + for _, q in parts: + vs = span_vecs(M.fr[q]); hiV = vs if hiV is None else hiV & vs + if not all(u in hiV for u in loE): continue + cands = {M.fid(loE), M.fid(echelon(sorted(hiV)))} | {p for p, _ in parts} | {q for _, q in parts} + loS = span_vecs(loE) + for u in sorted(hiV - loS): cands.add(M.fid(echelon(loE + [u]))) + def ok(F): return all(inside(M.fr[p], M.fr[F]) and inside(M.fr[F], M.fr[q]) for p, q in parts) and inside(loE, M.fr[F]) + def score(F): return sum(chi(d(F) - d(p)) + chi(d(q) - d(F)) for p, q in parts) + base = score(old); best, bs = old, base + for F in cands: + if F != old and ok(F): + sc = score(F) + if sc < bs - 1e-12: best, bs = F, sc + if best != old: M.gf[gi] = best; moved += 1; gain += base - bs + return moved, gain + + +def prune_frames(c): + """drop unused appended frames (keep the original table prefix), renumber""" + return c + + +if __name__ == '__main__': + c0 = json.load(gzip.open(sys.argv[1])); sys.path.insert(0, sys.argv[2]); import gx + K = int(sys.argv[4]) if len(sys.argv) > 4 else 3; sw = int(sys.argv[5]) if len(sys.argv) > 5 else 30 + rng = random.Random(int(sys.argv[6]) if len(sys.argv) > 6 else 1) + h = c0['h']; m = (2 * K + 1) * h; b0 = price(c0, K); print('start b', b0, float(b0)) + c, tag = split(c0); M = CX(c); a = float(b0) + for s in range(sw): + m1, g1 = single_sweep(M, a, m, rng); m2, g2 = block_sweep(M, a, m, rng) + print('sweep', s, 'single', m1, 'block', m2, 'chi/a %.4f' % ((g1 + g2) / a), flush=True) + if not (m1 or m2): break + w = to_cert(M); w = rehist(merge(w, tag)) + # drop unused frames beyond the original table + used = set(w['start']) | set(w['final']) | {g[1] for g in w['A'] + w['B']} | {f for _, _, f in w['ret']} + keep = list(range(len(c0['frames']))) + [i for i in range(len(c0['frames']), len(w['frames'])) if i in used] + ren = {o: n for n, o in enumerate(keep)}; w['frames'] = [w['frames'][i] for i in keep] + for ph in ('A', 'B'): + for g in w[ph]: g[1] = ren[g[1]] + w = rehist(w) + print('span violations', CX(w).span_ok(), '| gates A/B', len(w['A']), len(w['B']), 'vs', len(c0['A']), len(c0['B']), '| new frames', len(w['frames']) - len(c0['frames'])) + st = {}; gx.check1(w, scalar=True, stats=st); print('gx.check1 PASS (E0-E7, exact scalar E5)') + for K2 in (2, 3): b = price(w, K2); print('b(B_%d) =' % K2, b, float(b)) + json.dump(w, gzip.open(sys.argv[3], 'wt')) diff --git a/research/e8-complex-far-retiming/code/e8core.py b/research/e8-complex-far-retiming/code/e8core.py new file mode 100644 index 0000000000..78983b3687 --- /dev/null +++ b/research/e8-complex-far-retiming/code/e8core.py @@ -0,0 +1,203 @@ +"""Our own engine for bitE8/1 words (E8 root-line labels, h = 8): frame algebra over Q via root-line masks, +legality/F2 replay, five-stage histogram, exact bit coarse saving and PR #315 outer assembly (our engine copies). +Chafik Boukhalfa (chafreaky) with Anthropic Claude assistance; Apache-2.0.""" +import gzip, json, os, importlib.util +from fractions import Fraction as Q +from collections import Counter + +HERE = os.path.dirname(os.path.abspath(__file__)) +def _load(n): + s = importlib.util.spec_from_file_location('e8_' + n, os.path.join(HERE, "engines", n + ".py")) + m = importlib.util.module_from_spec(s); s.loader.exec_module(m); return m +moment = _load('moment'); other = _load('base_two_moment'); outer = _load('outer') + +B2 = Q(876248285600677, 10**18) # Sussman E8 five-stage (PR #352) +B3 = Q(932783231884153, 10**18) # DaysSky seven-stage B_3 (PR #363; no Lean proof) + + +def load_word(p): + with (gzip.open(p, 'rt') if p.endswith('.gz') else open(p)) as f: + return json.load(f) + + +def save_word(c, p): + with gzip.open(p, 'wt') as f: + json.dump(c, f, separators=(',', ':')) + + +def rank(vecs): + rows = [[Q(x) for x in r] for r in vecs]; rk = 0 + n = len(rows[0]) if rows else 0 + for col in range(n): + p = next((i for i in range(rk, len(rows)) if rows[i][col] != 0), None) + if p is None: continue + rows[rk], rows[p] = rows[p], rows[rk] + for i in range(len(rows)): + if i != rk and rows[i][col] != 0: + f = rows[i][col] / rows[rk][col]; rows[i] = [a - f * b for a, b in zip(rows[i], rows[rk])] + rk += 1 + return rk + + +class Lat: + """Root-spanned subspaces of Q^8 represented by the mask of root lines they contain.""" + def __init__(self, lines): + self.lines = [tuple(x) for x in lines]; self.v = len(lines) + self._dim = {0: 0}; self._close = {} + # Gram (form /2) for orthogonality + self.ip = [[sum(a * b for a, b in zip(x, y)) // 2 for y in self.lines] for x in self.lines] + + def close(self, mask): + """mask of all root lines in span(roots of mask)""" + if mask in self._close: return self._close[mask] + idx = [i for i in range(self.v) if mask >> i & 1] + basis = [] + for i in idx: + if rank([self.lines[j] for j in basis] + [self.lines[i]]) > len(basis): basis.append(i) + d = len(basis); out = 0 + if d == 8: out = (1 << self.v) - 1 + elif d: + B = [self.lines[j] for j in basis] + for i in range(self.v): + if rank(B + [self.lines[i]]) == d: out |= 1 << i + self._close[mask] = out; self._close[out] = out; self._dim[out] = d + return out + + def dim(self, mask): + if mask not in self._dim: self.close(mask) + return self._dim[mask] + + def basis(self, mask): + b = [] + for i in range(self.v): + if mask >> i & 1 and rank([self.lines[j] for j in b] + [self.lines[i]]) > len(b): b.append(i) + return b + + def perp(self, t): + return self.close(sum(1 << j for j in range(self.v) if self.ip[t][j] == 0)) + + +def to_masks(c, L): + return [L.close(sum(1 << i for i in fr)) for fr in c['frames']] + + +PHASES = ('D', 'A', 'B', 'C') + + +def replay(c, L=None, masks=None, check_f2=True, span=True, span_mode='z'): + """Exact legality + F2 replay + histogram of a bitE8/1 word. Returns dict(H_local, steps, ...). + Legality: each register's frames nested (mask inclusion = exact Q-subspace inclusion for root-spanned frames) and + strictly increasing at each move; every gate at one common frame; D compensation at frame 0; y at frame 0 at the + scatter; COPY of retained totals; final frames (x, helpers FULL; y_t at its cap t-perp). + Source-span: every non-target operand's x-support lies in the gate frame.""" + L = L or Lat(c['lines']); masks = masks or to_masks(c, L) + v, R, h = c['v'], c['R'], c['h']; n = 2 * v + R; FULL = (1 << v) - 1 + cur = [masks[f] for f in c['start']] + for r in range(v): assert cur[r] == L.close(1 << r), 'x start' + for r in range(v, 2 * v): assert cur[r] == 0, 'y start at 0' + sig = [L.dim(cur[r]) for r in range(n)] + H = Counter(); steps = 0; viol = [] + val = [1 << i for i in range(n)] + zsupp = [{i: 1} if i < v else {} for i in range(n)] # integer support of each register over the x columns (all ADDs are +1) + def climb(r, m): + nonlocal steps + if cur[r] == m: return + assert cur[r] & ~m == 0 and cur[r] != m, ('not nested', r) + H[L.dim(m) - L.dim(cur[r])] += 1; steps += 1; cur[r] = m + def run(gates, ph): + for k, (f, d, srcs) in enumerate(gates): + m = masks[f] + assert d >= v and d not in srcs and len(set(srcs)) == len(srcs), (ph, k) + for x in [d] + srcs: climb(x, m) + for s in srcs: + val[d] ^= val[s] + if span: + if s < v or s >= 2 * v: + ms = sum(1 << i for i in zsupp[s]) + if ms & ~m: viol.append((ph, k, s)) + zd = zsupp[d] + for i, a in zsupp[s].items(): + w = zd.get(i, 0) + a + if w: zd[i] = w + else: zd.pop(i, None) + if span_mode == 'f2': + zsupp[d] = {i: 1 for i in range(v) if val[d] >> i & 1} + if span and d >= 2 * v: + md = sum(1 << i for i in zsupp[d]) + if md & ~m: viol.append((ph, k, d)) + run(c['D'], 'D') + for r in range(v, n): assert cur[r] == masks[c['start'][r]], 'D compensation at frame 0 (helpers untouched otherwise)' + run(c['A'], 'A') + for t in range(v): assert cur[v + t] == 0, 'y at frame 0 at the scatter' + copies = [val[q] for q in c['ret']] + for q in c['ret']: + assert q >= 2 * v; H[L.dim(cur[q])] += 1 + for t, row in enumerate(c['scatter']): + for k in row: val[v + t] ^= copies[k] + run(c['B'], 'B'); run(c['C'], 'C') + for r in range(n): climb(r, masks[c['final'][r]]) + for r in range(v): assert cur[r] == FULL + for r in range(2 * v, n): assert cur[r] == FULL + for t in range(v): assert cur[v + t] == L.perp(t), 'y final at cap' + if check_f2: + for s in range(v): assert val[s] == 1 << s + for t in range(v): assert val[v + t] == (1 << (v + t)) | (1 << t), ('target', t) + for r in range(2 * v, n): assert val[r] == 1 << r, ('helper', r) + return dict(sig=sig, viol=viol, H=H, steps=steps, n=n, v=v, R=R, h=h, L=L, masks=masks, ncopy=len(c['ret'])) + + +def profile(H_local, v, h=8): + prof = Counter({j: 5 * c for j, c in H_local.items()}) + for j in (2 * h - 2, h - 1, 2 * h + 2, 4): prof[j] += 2 * v + return prof + + +def coarse(H, m, W): + grid = Q(1, 10**18) + root = moment.certify(dict(H), m, W, True); c = Q(int(Q(root['lower']) * 10**18), 10**18) + assert moment.moment(H, m, W, c, True)[1] < 1 < moment.moment(H, m, W, c + grid, True)[0] + fb = 32 * m * m * sum(H.values()) + _, up = other.moment(m, W, list(H.items()), c); _, bad = other.moment(m, W, [(1, fb)], c) + lo, _ = other.moment(m, W, list(H.items()), c + grid); blo, _ = other.moment(m, W, [(1, fb)], c + grid) + assert up + Q(1, 10**16) * bad < 1 < lo + Q(1, 10**16) * blo + return c + + +def kappa(c, b): + grid = Q(1, 10**18); eta = Q(1, 10**12); beta = Q(1, 10**9); cap = (1 - beta) * b + sv = min(c, Q(((cap - grid) * 10**18).__floor__(), 10**18)); binding = 'bit' if sv == c else 'complex' + chain = [Q(384599, 10**10)] + for _ in range(3): + a = (1 - sv) * sv + sv * chain[-1]; assert chain[-1] < a < sv < 1 - a; chain.append(a) + bit = chain[-1] + bridge = dict(proof='PROOF.md', representation='Exact powers with source-bound finite overcharges', + semantic=dict(G=dict(base=2, exponent=30000), E=dict(base=2, exponent=100000), B_upper=dict(base=2, exponent=100001), + C0=dict(base=2, exponent=210000), C1=1, strict_literal_gap=1, induction_gap_lower=1), + rows=dict(coefficient=20161, degree=10**6, suffix_slope=4 * 10**6, degree_gap=Q(10**6) - Q(51 * 20161, 25))) + q = bit * (1 - 2 * eta); mn = (1 - eta) * q / (1 + q); t = mn * 10**18; k = Q((t.numerator - 1) // t.denominator, 10**18) + asm = outer.assembly(bit, b, bridge, k, eta=eta, beta=beta) + assert len(asm['strict_constraints']) == 47 and min(asm['strict_constraints'].values()) > 0 + try: + outer.assembly(bit, b, bridge, k + grid, eta=eta, beta=beta); raise SystemExit('ADJACENT ADMITTED') + except AssertionError: pass + return k, binding + + +def price_word(c, **kw): + r = replay(c, **kw) + v, h, R = r['v'], r['h'], r['R']; m = 5 * h + resid = sum(h - r['sig'][x] for x in range(2 * v, 2 * v + R)) + T = 1 if resid % h == 0 else h + W = (4 * v * h + resid) * T // h; assert (4 * v * h + resid) * T % h == 0 + prof = Counter({j: T * k for j, k in profile(r['H'], v, h).items()}); mass = sum(a * b for a, b in prof.items()) + L_copy = h * r['ncopy'] + assert m * W - mass == T * (4 * v - 5 * L_copy), ('deficit', m * W - mass) + assert 2 * max(prof) < m + cc = coarse(prof, m, W) + return dict(c=cc, H=r['H'], prof=prof, W=W, T=T, resid=resid, m=m, deficit=m * W - mass, replay=r) + + +def phi_fn(m, W, a): + """linearized moment term per child of rank r (float), for search scoring""" + import math + return lambda r: r / (m * W) * math.exp(a * math.log(m / r)) diff --git a/research/e8-complex-far-retiming/code/engines/base_two_moment.py b/research/e8-complex-far-retiming/code/engines/base_two_moment.py new file mode 100644 index 0000000000..14a90fa0e7 --- /dev/null +++ b/research/e8-complex-far-retiming/code/engines/base_two_moment.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +"""Maintainer arithmetic cross-check of the pinned PR39 certificate. + +Uses no producer/checker imports. Counts are inputs, not proved by this script. +80-term rational logarithms and 12-term exponentials independently enclose +the two moments. The accompanying written review addresses their realization. +""" +import argparse +from fractions import Fraction as Q +from hashlib import sha256 +import json +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +GRID = 10**30 + + +def require(condition, message): + if not condition: + raise ValueError(message) + + +def log_unit(x): + require(1 <= x <= 2, "logarithm range") + z = (x-1)/(x+1) + lo = 2*sum((z**(2*j+1)/(2*j+1) for j in range(80)), Q(0)) + return lo, lo+2*z**161/(161*(1-z*z)) + + +def log_bounds(x): + k = 0 + while x > 2: + x /= 2 + k += 1 + lo, hi = log_unit(x) + l2, u2 = log_unit(Q(2)) + lo, hi = lo+k*l2, hi+k*u2 + # Outward rational rounding keeps later certificates compact. + return Q((lo*GRID).__floor__(), GRID), Q((hi*GRID).__ceil__(), GRID) + + +def exp_bounds(x): + require(0 <= x < 1, "exponential range") + term = total = Q(1) + for j in range(1, 13): + term *= x/j + total += term + tail = term*x/13/(1-x/14) + return total, total+tail + + +def moment(m, W, entries, saving): + lo = hi = Q(0) + for t, count in entries: + t, count = int(t), int(count) + require(0 < t < m and count > 0, "improper child") + l, u = log_bounds(Q(m, t)) + lower = exp_bounds(saving*l)[0] + upper = exp_bounds(saving*u)[1] + lo += Q(t*count, m*W)*lower + hi += Q(t*count, m*W)*upper + return Q((lo*GRID).__floor__(), GRID), Q((hi*GRID).__ceil__(), GRID) + diff --git a/research/e8-complex-far-retiming/code/engines/moment.py b/research/e8-complex-far-retiming/code/engines/moment.py new file mode 100644 index 0000000000..7d697cb452 --- /dev/null +++ b/research/e8-complex-far-retiming/code/engines/moment.py @@ -0,0 +1,113 @@ +"""Independent prospective ordinary five-stage cost, never supplier admission. + +Consumes a raw one-invocation ledger made from the actual source/internal/target +paths. No packed profile is used as an input. Missing physical/finite/compiler +obligations must remain explicit in the accompanying report. +""" +from collections import Counter +from decimal import Decimal, localcontext, ROUND_FLOOR +from fractions import Fraction as F +from functools import lru_cache +from math import factorial +import hashlib, json, sys +from pathlib import Path + +DYAD = 1 << 224 + +def floor(x): return F(x.numerator*DYAD//x.denominator, DYAD) +def ceil(x): return -floor(-x) + +@lru_cache(None) +def logarithm(x): + """Mercator bounds after binary reduction; ln(2) via two Mercator sums. + + For 1 <= y < 2, log(y) is an alternating series in z=y-1. + We instead reduce to 1 <= y < 3/2, and bound log(3/2) and + log(2) by the same alternating series at z=1/2 and z=1/3, + using log(2)=log(3/2)+log(4/3). All sums are exact rationals. + """ + assert x >= 1 + k = 0 + while x >= 2: x /= 2; k += 1 + j = int(x >= F(3, 2)) + if j: x /= F(3, 2) + def mercator(y): + z=y-1 + assert 0 <= z <= F(1,2) + # An even partial sum is below log; the next term bounds its tail. + t=z; s=F(0) + for n in range(1, 225): + s += (t if n & 1 else -t)/n + t *= z + return s, s+t/225 + a,b=mercator(x); c,d=mercator(F(3,2)); e,f=mercator(F(4,3)) + return floor(a+(k+j)*c+k*e), ceil(b+(k+j)*d+k*f) + +def exponential(l,u): + assert 0 <= l <= u < F(1,10) + def poly(x): + s=t=F(1) + for j in range(1,25): t=t*x/j; s+=t + return s,t + lo,_=poly(l);hi,last=poly(u) + return floor(lo),ceil(hi+last*u/25/(1-u/26)) + +def moment(H,m,W,a,bad=False): + lo=hi=F(0) + for r,n in H.items(): + assert 0 < r < m and n > 0 + l,u=logarithm(F(m,r)); e,f=exponential(a*l,a*u) + p=F(r*n,m*W);lo+=p*e;hi+=p*f + if bad: + # Existing envelope shape, only conditionally portable to the new cover. + l,u=logarithm(F(m));e,f=exponential(a*l,a*u) + p=F(32*m*sum(H.values()),10**16*W) + lo+=p*e;hi+=p*f + return floor(lo),ceil(hi) + +def decimal(x): + with localcontext() as c: + c.prec=70 + return str(Decimal(x.numerator)/Decimal(x.denominator)) + +def certify(H,m,W,bad=False): + with localcontext() as c: + c.prec=70 + terms=[(Decimal(r*n)/Decimal(m*W),(Decimal(m)/Decimal(r)).ln()) for r,n in H.items()] + fallback=Decimal(32*m*sum(H.values()))/Decimal(10**16*W) if bad else Decimal(0) + lm=Decimal(m).ln();lo=Decimal(0);hi=Decimal('.003') + for _ in range(210): + a=(lo+hi)/2 + if sum(p*(a*l).exp() for p,l in terms)+fallback*(a*lm).exp()<1:lo=a + else:hi=a + q=int(((lo+hi)/2*10**24).to_integral_value(rounding=ROUND_FLOOR)) + lo=F(q,10**24);hi=lo+F(1,10**24) + lv=moment(H,m,W,lo,bad);hv=moment(H,m,W,hi,bad) + assert lv[1]<10 for v in slacks.values()),{k:str(v) for k,v in slacks.items() if v<=0} + assert min(margins.values())==minimum + assert margins['balanced_prefix']-minimum==eta and 1-eps-r==eta/2 + assert 1-eps*(1+c)==eta-eps*eta/4 + return dict(parameters=dict(a_bit=a,a_complex=b,tau=tau,sigma=sigma, + eta=eta,beta=beta,q=q,c=c,epsilon=eps,lambda_=lam, + lambda_prime=lp,alpha_squared_power=r,delta=delta, + C0=bridge['semantic']['C0'],C1=1,kappa=kappa), + strict_constraints=slacks,margins=margins,minimum_margin=minimum, + absorption_gap=minimum-kappa, + recurrence=dict(internal=internal,leaf=leaf,reservations=1-c)) diff --git a/research/e8-complex-far-retiming/code/kappa.py b/research/e8-complex-far-retiming/code/kappa.py new file mode 100644 index 0000000000..3209b54672 --- /dev/null +++ b/research/e8-complex-far-retiming/code/kappa.py @@ -0,0 +1,52 @@ +"""Exact coarse savings in the seven-stage bridged word B_3 and PR #315's outer assembly. +Complex: (2K+1) x Sussman-format block histogram + 2v x B_3 idle climbs, UNSPLIT (38 | 16, 8, 22 | 32, 8, 6 at h = 9, +m = 63; max child 38: halving degree two, the admitted condition of DaysSky's PR #375), W = 6v + R, deficit 6v - 7 cst. +Bit (non-binding): #370's E8 bit word (as in #373) local histogram (our replay) x 7 + 2v x B_3 idle climbs at h = 8, +split at 27 (#373), m = 56, W = 6v + R, deficit 6v - 7 x 72. Both rational engines, 1e-16 fallback, adjacent grid +point excluded; kappa by outer.assembly (47 strict constraints, adjacent kappa rejected). +usage: kappa.py COMPLEX_CERT ORIGINAL_CERT BITWORD OUT.json""" +import sys, os, gzip, json +from collections import Counter +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from e8core import coarse, kappa, price_word, load_word, Q +from layout_bk import layout + + +def complex_b(c, K=3, split=False): + h, v, R, cst = c['h'], c['v'], c['R'], c['cst']; Hinv = Counter() + for w in c['blocks'].values(): + for r, n in w.items(): Hinv[int(r)] += n + s = 2 * K + 1; m = s * h; lay = layout(K, h, split=(m - 1) // 2 if split else None) + H = Counter({r: s * n for r, n in Hinv.items()}) + for r in lay['blocks']: H[r] += 2 * v + W = 2 * K * v + R; assert m * W - sum(r * n for r, n in H.items()) == 2 * K * v - s * cst + mx = max(H); t = 1 + while m ** t <= 2 * mx ** t: t += 1 + assert m <= 72 and t == (1 if split else 2) + return coarse(H, m, W), mx, t, lay['per_pair'] + + +def bit_c(word, K=3): + p = price_word(word); h, v = 8, 120; s = 2 * K + 1; m = s * h + assert p['T'] == 1; R = word['R'] + lay = layout(K, h, split=(m - 1) // 2) + H = Counter({r: s * n for r, n in p['H'].items()}) + for r in lay['blocks']: H[r] += 2 * v + W = 2 * K * v + R; assert m * W - sum(r * n for r, n in H.items()) == 2 * K * v - s * 72 and 2 * max(H) < m + return coarse(H, m, W) + + +ours = json.load(gzip.open(sys.argv[1])); orig = json.load(gzip.open(sys.argv[2])); word = load_word(sys.argv[3]) +c = bit_c(word); assert c == Q(968197503705007, 10**18), c +print(' bit side (non-binding): #370 E8 bit word in B_3 (split at 27, as #373): c = %s = %.15e (= #373)' % (c, float(c))) +out = dict(bit_coarse=str(c)) +for name, cert in (('original', orig), ('ours', ours)): + bs, _, _, _ = complex_b(cert, split=True) + b, mx, t, idle = complex_b(cert) + k, bd = kappa(c, b); assert bd == 'complex' + print(' %-8s complex B_3 split b = %.15e | unsplit (max child %d, halving degree %d, idle %s) b = %s = %.15e | kappa = %s = %.15e (%s-bound)' + % (name, float(bs), mx, t, idle, b, float(b), k, float(k), bd)) + out[name] = dict(b_split=str(bs), b_unsplit=str(b), kappa=str(k), kappa_float=float(k), binding=bd) +assert out['original']['b_split'] == '932783231884153/1000000000000000000' # #363 +assert out['original']['kappa'] == '95424356568133/100000000000000000' # #375 reproduced +json.dump(out, open(sys.argv[4], 'w'), indent=1) diff --git a/research/e8-complex-far-retiming/code/layout_bk.py b/research/e8-complex-far-retiming/code/layout_bk.py new file mode 100644 index 0000000000..57aa98085b --- /dev/null +++ b/research/e8-complex-far-retiming/code/layout_bk.py @@ -0,0 +1,63 @@ +"""Our own check of the bridged layout B_K (Sussman's B_2, Work/Bridge/Net.lean; DaysSky's B_K generalisation, PR #363), +reimplemented from the schedule stated in PR #363's README and Sussman's coordinate-frame stage geometry: +K bank pairs, 2K+1 stages on H + 2K.H (m = (2K+1)h). Pair 1: forward, backward, forward; pair p >= 2: backward, forward, +borrowing its first forward from pair p-1 through twin gates (+-1 additions at equal frames). +Checks: frames are coordinate sets; every climb nested; twin gates at equal frames; each invocation entered at its +stage frame; final frames; m-1 moves per data role; exchange identity X_p = -y_p, Y_p = x_p (exact integer +linear algebra on symbolic coefficient vectors). Returns the idle climbs per role pair (optionally split at s). +Chafik Boukhalfa (chafreaky) with Anthropic Claude assistance; Apache-2.0.""" +def layout(K, h, split=None): + s = 2 * K + 1; m = s * h; l = 0 + blk = lambda name, skip=None: {(name, j) for j in range(h) if j != skip} + B0 = blk('b0'); F = ['F%d' % i for i in range(s - 1)] + ALL = set(B0).union(*[blk(f) for f in F]); assert len(ALL) == m + def enter(t, X): + if t == 0: return {('b0', l)} if X else set() + base = set(B0) if X else B0 - {('b0', l)} + for f in F[:t - 1]: base |= blk(f, l) + return base + def leave(t, X): + return (set(B0) if X else B0 - {('b0', l)}) if t == 0 else enter(t, X) | blk(F[t - 1], l) + sched = [(0, 'F', 1), (1, 'B', 1), (2, 'F', 1)] + for p in range(2, K + 1): sched += [(2 * p - 1, 'B', p), (2 * p, 'F', p)] + fr = {(X, p): enter(0, X) for p in range(1, K + 1) for X in (True, False)} + inv = {k: 0 for k in fr}; idle = {k: [] for k in fr} + # symbolic values: vector over symbols x1..xK, y1..yK + sym = lambda nm, p: tuple(int(q == (nm, p)) for q in [(a, b) for a in 'xy' for b in range(1, K + 1)]) + val = {(True, p): sym('x', p) for p in range(1, K + 1)} | {(False, p): sym('y', p) for p in range(1, K + 1)} + add = lambda u, w, c: tuple(a + c * b for a, b in zip(u, w)) + def go(k, new, is_idle): + assert fr[k] <= new, 'climb not nested' + if new != fr[k]: + (idle[k].append(len(new) - len(fr[k])) if is_idle else None) + if not is_idle: inv[k] += len(new) - len(fr[k]) + fr[k] = set(new) + def gate(d, s_, c): + assert fr[d] == fr[s_], 'twin gate at unequal frames' + val[d] = add(val[d], val[s_], c) + for t, kind, p in sched: + for X in (True, False): + go((X, p), enter(t, X), True); go((X, p), leave(t, X), False) + if kind == 'F': val[(False, p)] = add(val[(False, p)], val[(True, p)], 1) + else: val[(True, p)] = add(val[(True, p)], val[(False, p)], -1) + for q in range(2, K + 1): + if t in (2 * q - 3, 2 * q - 2): + for X in (True, False): go((X, q), fr[(X, q - 1)], True) + if t == 2 * q - 3: gate((True, q - 1), (True, q), 1); gate((False, q), (False, q - 1), -1) + else: gate((False, q), (False, q - 1), 1); gate((True, q - 1), (True, q), -1) + for k in fr: go(k, ALL if k[0] else ALL - {('b0', l)}, True) + for q in range(K, 1, -1): gate((False, q - 1), (False, q), -1); gate((True, q), (True, q - 1), 1) + for k in fr: assert inv[k] + sum(idle[k]) == m - 1 + for p in range(1, K + 1): + assert val[(True, p)] == tuple(-a for a in sym('y', p)) and val[(False, p)] == sym('x', p), ('exchange', p) + assert idle[(True, p)] == idle[(False, p)] + blocks = [r for p in range(1, K + 1) for r in idle[(True, p)]] + if split: + out = [] + for r in blocks: + while r > split: out.append(split); r -= split + if r: out.append(r) + blocks = out + return dict(m=m, per_pair={p: idle[(True, p)] for p in range(1, K + 1)}, blocks=blocks) +if __name__ == '__main__': + print(layout(2, 9)); print(layout(3, 9)); print(layout(3, 9, split=31)) diff --git a/research/e8-complex-far-retiming/complex/LICENSE-wht-power-saving-lean b/research/e8-complex-far-retiming/complex/LICENSE-wht-power-saving-lean new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/research/e8-complex-far-retiming/complex/LICENSE-wht-power-saving-lean @@ -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/e8-complex-far-retiming/complex/NOTICE-wht-power-saving-lean b/research/e8-complex-far-retiming/complex/NOTICE-wht-power-saving-lean new file mode 100644 index 0000000000..7008ce01c1 --- /dev/null +++ b/research/e8-complex-far-retiming/complex/NOTICE-wht-power-saving-lean @@ -0,0 +1,489 @@ +Walsh-Hadamard power saving (wht-power-saving-lean) +Copyright 2026 Jacob Sussman + +Repository: https://github.com/jacobalansussman/wht-power-saving-lean + +This repository is not a project of OpenAI. OpenAI did not write, review or +endorse it. The package name "OAI" and the namespace "OAI.PowerSaving" are +those of openai/math and are kept only because the new files extend that +development. + +This repository is not a project of CrocSwap/integer-mult-bounds either. Its +authors did not write, review or endorse it. Section 6 says what is taken +from their repository. + +This repository contains files from, and files derived from, the repository +"openai/math" (https://github.com/openai/math), family 130, "Exact Fourier +transforms below n log n", at commit fd4aeeb2ee4fc729c18d98444fed42fd0529eeeb. +That repository is published under the Apache License, Version 2.0. A copy of +the licence is in the file LICENSE. The upstream repository has no NOTICE file +and its licence text names no copyright holder; the files are attributed here +to the authors of openai/math (OpenAI). + +1. Unmodified upstream files. + The 89 files under OAI/Computability/ are byte-for-byte copies of the files + with the same paths under lean/ in openai/math at the commit above. They + have not been changed. Modified copies of some of them are under + Work/Fourier/ and Work/FourierE8/ and are listed in section 7. + One more file of openai/math is here unchanged, outside the Lean build: + third-party/openai-math/lean/ComparatorChallenges/UniformFourier.lean is a + byte-for-byte copy of lean/ComparatorChallenges/UniformFourier.lean at the + commit above (the statement of OpenAI's Fourier challenge). It is included + so that the modified copy named in section 2 can be compared with it. + +2. Files that contain upstream text. + - Work/CarrierCheck/ChallengeB2Ke16x.lean, + Work/CarrierCheck/ChallengeB2Ke16xR.lean, + Work/GCert/Data/ChallengeB2Gp193x.lean, + Work/GCert/Data/ChallengeB2Ge8x.lean and the text template + tools/gen/templates/ChallengeB2L16x.lean.txt each contain, as their + lines 22-270, lines 9-257 of + lean/ComparatorChallenges/UniformFourier.lean of openai/math (the RAM cost + model), unchanged. Everything else in these files was written for this + repository. + - Work/Fourier/UniformFourierChallenge.lean is a modified copy of + lean/ComparatorChallenges/UniformFourier.lean of openai/math (the + statement of OpenAI's Fourier challenge, 336 lines). Changes: a comment + was added at the top; the exponent 1 - 1/(10^(13:ℕ)) was replaced by + 1 - 7474546/(10^(10:ℕ)); seven names received the suffix "Z" + (decimalExponent, decimalTime, TimeBounds, DFTGoal, ConvGoal, + transform_main, convolution_main). Everything else in the file is the + upstream text, unchanged. See section 7. + - Work/FourierE8/UniformFourierChallenge.lean is a second modified copy + of the same upstream file, for the fourth result, with the same three + kinds of change: a comment added at the top; the exponent replaced by + 1 - 8762478/(10^(10:ℕ)); the suffix "E8" on the same seven names. + Everything else in the file is the upstream text, unchanged. See + section 7. + - Work/Scratch/Engine.lean and Work/Block/Engine.lean contain copies of + proofs of openai/math, changed for this repository: sections 1, 2 and 5 + of Work/Scratch/Engine.lean are upstream proofs with the batch layout + renamed, and both files contain a copy of the private upstream theorem + "hills_project" (lean/OAI/Computability/FourierTransform/ + TensorProgram.lean) with its envelope generalised. The comments in the + two files mark these places. + - lakefile.lean is adapted from lean/lakefile.lean of openai/math: the same + package name, toolchain, package option and Mathlib pin; every other + dependency was removed and the libraries of this repository were added. + lean-toolchain is a byte-for-byte copy of lean/lean-toolchain. + lake-manifest.json lists Mathlib and its dependencies at the revisions of + lean/lake-manifest.json. + - RELATED-WORK.md reproduces the BibTeX entries of two manuscripts of + openai/math. + +3. New files that build on upstream proofs. + The other files under WHTCheck/ and Work/ were written for this + repository. They import the upstream files, and several of them generalise + or adapt proofs of upstream files (for example Work/Block/Recursion.lean, + which says so in its header). They are changes and additions made for this + repository and are not part of openai/math. + +4. How the new files were made. + Jacob Sussman made the new files on 2026-10-08, 2026-10-09 and 2026-10-10 + by directing a team of AI agents (Claude). The agents wrote the new Lean + files, the certificate data and the generator scripts. They made the + rebuild scripts under tools/rebuild/, in part by adapting programs of + CrocSwap/integer-mult-bounds (section 6): ten of the thirteen on + 2026-10-09, tidied for this repository on 2026-10-10, and three on + 2026-10-10. On 2026-10-10 they also wrote the search programs under + tools/e8/ and found with them the certificate of the fourth result + (section 8). See README.md, section 4. + +5. Ideas taken from other public work. + The network of the first result uses ideas published by others, among them + the helper circuit "NStar3" (Swapnil-jain/integer-mult-kappa, which credits + CrocSwap pull request #36 and Paureel for parts of it), its transfer to + family 130 (shea256/fourier-transform-below-nlogn), and "carrier links" and + "whole-residual batching" (pull requests to CrocSwap/integer-mult-bounds). + No file of Swapnil-jain/integer-mult-kappa or of + shea256/fourier-transform-below-nlogn is included here. One comment in + Work/Scratch/Engine.lean quotes three counts reported in + Swapnil-jain/integer-mult-kappa and names that source. The general frame + lemma that Work/GFrame proves is stated in + notes/general-clifford-frames.tex of CrocSwap/integer-mult-bounds; its Lean + proof was written for this repository. The credits, with links, are in + RELATED-WORK.md. + +6. Data derived from, and programs adapted from, + CrocSwap/integer-mult-bounds (second result). + The helper circuit of the second result is the paired-cube circuit of the + repository "CrocSwap/integer-mult-bounds" + (https://github.com/CrocSwap/integer-mult-bounds), in the state of its pull + request #193. It is the design of that community: icekylinx (pull requests + #144 and #184), eumemic (#168, and #117 behind its modules), ikeboy / + Avi Eisenberg (#191 and #193), with an664 (#128) credited there for the + workspace-sharing principle, and DaysSky (#162) and chafreaky (#163) + credited by #168 for its closure-compiled carrier arcs and for the + fusion of its single-target outputs. That repository is published under + the Apache + License, Version 2.0 (the same licence text as the file LICENSE). Its + NOTICE file begins "A sharper exponent for integer multiplication / + Copyright 2026 Douglas Colkitt" and says that the community release "is + not solely Douglas Colkitt's work". A complete copy of that NOTICE file, as + it is at the head of pull request #193, is in + third-party/CrocSwap-integer-mult-bounds/NOTICE. + + The data were taken from these files of that repository. They were loaded + as data by the programs under tools/rebuild/. No program of that + repository was run, and none is included here unchanged; six of the + programs under tools/rebuild/ are adapted from programs of that + repository ("Programs", below). + - at the head of pull request #168 (eumemic), + 4a3c769e5c5430e7114c4d3e099ff34664677f17: + references/paired-cube/sources/local_L1.json + references/paired-cube/sources/tmod_TE_TD_TB3_1_1_4_full_6.0617964e-4.json + references/paired-cube/sources/pmod_J0_full_6.0666810e-4.json + references/paired-cube/sources/qmod_climb3u_best.json + references/paired-cube/selected-module/matching-arcs.json + references/paired-cube/physical/frames.json + - at the head of pull request #193 (ikeboy), + 187e1010ac8b259af8e9b5166f68b64bc27b4b47: + research/source-assisted-v4/data/physical-pairs.json + + Two more files at the head of pull request #168 are read for comparison + only: the programs print whether their graph and their counts agree + with them. Nothing in the certificate is taken from them: + references/paired-cube/selected-module/SOURCE.json + certificates/paired-cube-complex-input.json + + One more file is fetched and read only on request. No certificate and + no Lean module of this repository is made from it; the documentation + gives its sha256 and the price that tools/gx/gxdry.py computes for + the certificate that the rebuild makes from it: + - at the head of pull request #233 (chafreaky), + 109a857a329d18ed5552d5573f17ddfa886ae57f: + research/source-assisted-v4-layer/witness/physical-pairs.json + + None of these ten files is included in this repository. + tools/rebuild/fetch_inputs.py fetches them and checks their sha256. + + Files of this repository that contain data derived from them: + tools/certificate/gcert1-p11-pr193.json.gz and the Lean data modules + generated from it, Work/GCert/Data/Gen/P193*.lean and the files they import + from Work/GCert/Data/Gen/P193/; its block counts are also in the generated + modules Work/GCert/Data/P193Price.lean, RateB2Gp193.lean, RateB2Gp193x.lean + and InstB2Gp193.lean. + Changes made: the circuit was rebuilt from those data as one explicit, + renumbered list of additions with a table of frames, in the certificate + format "gcert/1" of this repository; its 1,980 in-place steps were written + as pairs of additions with rescaled coefficients; the Lean data modules + were generated from that list. No file of that repository is included + unchanged, except the copy of its NOTICE file named above. + + Programs. The Python files under tools/rebuild/ were made by the agents + (section 4), who read programs of that repository as text and ran none + of them. Six of the thirteen files are adapted from those programs: + the parts named below follow the outside program statement by statement + or method by method, with the layout changed, some names changed and + the parts that this certificate does not use left out. Each of the six + says so in its first lines. + - tools/rebuild/compile.py: the function closure follows + compile_closure of scripts/paired_cube/closure.py; the function + select follows the first part of select of + scripts/paired_cube/gauges.py. + - tools/rebuild/graphgen.py: the class Gen follows the class Graph of + scripts/paired_cube/graph.py; its method fuse follows the branch + "f8:" of merge_outputs of scripts/paired_cube/modules.py, which + fuses the three single-target outputs of a port into one. That + branch came with pull request #168 (eumemic), which says of the + fusion that it "follows #163's same-singleton fusion" and credits + it so: "Same-singleton output fusion: @chafreaky (#163); the fold + orders here come from our own search." + - tools/rebuild/aligned.py: the functions op_tags, local_values and + build follow op_tags, local_values and main of + research/source-assisted-v4/source_aligned_local_v4.py, except its + search for pairs and the records it writes. + - tools/rebuild/stage.py: the order of the events, the backward pass + (responses) and many names are those of + research/terminal-sinks/sinks_gate.py and of + scripts/paired_cube_physical.py; the event records and the exact + arithmetic were written for this repository, and the frame scan + was rewritten so that it returns every block. + - tools/rebuild/build.py: the function target_hist and the field names + of profile follow the last part of select of + scripts/paired_cube/gauges.py; the calls that make the graph and its + hash follow regenerate of scripts/paired_cube_producer.py. + - tools/rebuild/f2.py: the function perp follows perp, and the second + half of the function rref follows the second half of basis, of + scripts/paired_cube/frames.py. + The outside programs are those at the two commits named above + (source_aligned_local_v4.py at the second, the others at the first). + Of the other seven files, rebuild.py, fetch_inputs.py, rbpaths.py, + rewrite.py, phys.py and emit.py were written for this repository. + modules.py was written for this repository too; for the three module + files it checks the contract that the outside + scripts/paired_cube/modules.py states for them in its functions + triple_module_from, pair_module_from and all_but_one_from, each of + which is marked there "eumemic, Claude assistance; Apache-2.0". + + Notices and attribution sentences in the first lines of those outside + programs, quoted with the line numbers of each file at its commit. + The other lines of each header say what the program does and name no + person: + + scripts/paired_cube/closure.py, lines 4-5 (pull request #168, + eumemic; from pull request #162, DaysSky): + Copyright 2026 DaysSky (PR162, + research/extended-links/cxlinks.py), Apache-2.0; moved here + unchanged apart from this header and the imports (eumemic, Claude + assistance). + + scripts/paired_cube/graph.py, modules.py, frames.py, gauges.py and + scripts/paired_cube_producer.py (pull request #144, icekylinx; + graph.py, modules.py and paired_cube_producer.py changed in #168, + eumemic), the two comment lines at the top of each: + Copyright 2026 icekylinx. Apache-2.0. + Developed with GPT-6 Astra assistance; integrated with Codex + assistance. + graph.py, lines 6-9: + The coarse positive query modules restrict the already adopted + PR117 DAG (eumemic; Apache-2.0; Claude assistance; + cbb05ce504d571546d9b7794c186a613c659c3bf). The paired-channel + decomposition and this assembly are new work for icekylinx with + OpenAI GPT-6 Astra assistance. + modules.py, lines 6-7: + The pair-disjoint module is a specialization of the already + adopted PR117 positive DAG (eumemic, Apache-2.0, pinned + cbb05ce504d571546d9b7794c186a613c659c3bf). + modules.py, line 272, above merge_outputs: + Output merging (pmerge.py variants; eumemic, Claude assistance; + Apache-2.0). + frames.py, line 7: + New work for icekylinx, OpenAI GPT-6 Astra assistance. + paired_cube_producer.py, line 8: + Copyright 2026 icekylinx, Apache-2.0; OpenAI GPT-6 Astra + assistance, inherited PR117 notices. + + scripts/paired_cube_physical.py, lines 4-7 (pull request #168, + eumemic): + Prepared by eumemic with Anthropic Claude assistance. Apache-2.0. + The signed word, frozen arcs, gauges, chronology and shared-core + accounting are icekylinx's PR144. Frame descent under PR130's + general Clifford frames and compensated birth-cut reuse follow + PR131 and jamesyc's PR124; deadline (late) compensation reads + follow PR143. + + research/terminal-sinks/sinks_gate.py, lines 4-6 (pull request + #168, eumemic): + The substitution is the terminal-sink lemma of PR #166 by jamesyc + (closed by its author), independently audited here: this gate's + own frame scans, replay and controls carry the soundness argument + and do not use #166's checker. Gate, audit and integration by + eumemic with Anthropic Claude assistance. Apache-2.0. + + research/source-assisted-v4/source_aligned_local_v4.py, lines 4-9 + (pull requests #191 and #193, ikeboy / Avi Eisenberg): + Derived from + research/source-assisted/decision/source_aligned_local.py + (icekylinx, GPT-6 Astra assistance, Apache-2.0). Only the three + frozen query modules change: this copy reads PR168 v4's selected + modules (eumemic, Claude assistance) from + references/paired-cube/sources instead of the PR168 fd25adb7 + modules. The local configuration, arc transport, frame + inheritance and physical layer code are unchanged. + + Notices of that NOTICE file that pertain to these data, quoted: + + Paired-cube and shared-core extension, 9 October 2026 + Copyright 2026 icekylinx + + Source-assisted common-frame extension (2026-10-09) + Copyright 2026 icekylinx. Apache-2.0. + Substantial dependency: eumemic PR168, + fd25adb7fbaa12ee761d02c733c54d1d2a7687ee + + The full text of both notices, and the notices of the work they build on, + are in the copy named above. The credits, with links and with the words of + pull request #193 about its own sources, are in README.md ("Whose circuit + this is") and in RELATED-WORK.md, section 1. + +7. Modified copies of upstream files (third and fourth results). + The files listed below are copies of files of openai/math at the commit + above, changed for this repository. Each begins with a comment that names + the upstream file it is derived from and states what was changed. The + upstream originals under lean/OAI/Computability/ are in this repository + as well, unchanged (section 1), and the copies import them. The upstream + challenge file lean/ComparatorChallenges/UniformFourier.lean is in this + repository in both forms: unchanged under third-party/openai-math/ + (section 1) and modified (section 2). + + - Work/Fourier/ArbitraryLength.lean: modified copy of + lean/OAI/Computability/FourierTransform/ArbitraryLength.lean (224 + lines; 159 of them unchanged in the copy). Changes: identifiers renamed + on 64 lines (18 names: 15 with the suffix Z, 3 with Z inserted); 1 + import line replaced by 1; 15 comment lines added; no other change to + the code. + - Work/Fourier/Asymptotics.lean: modified copy of + lean/OAI/Computability/FourierTransform/Asymptotics.lean (187 lines; + 112 of them unchanged in the copy). Changes: identifiers renamed on 74 + lines (32 names: 26 with the suffix Z, 6 with Z inserted); 1 import + line replaced by 1; 15 comment lines added; no other change to the + code. + - Work/Fourier/Axioms.lean: written for this repository (not a copy of an + upstream file). + - Work/Fourier/ConvolutionProgram.lean: modified copy of + lean/OAI/Computability/FourierTransform/ConvolutionProgram.lean (100 + lines; 77 of them unchanged in the copy). Changes: identifiers renamed + on 22 lines (10 names: 9 with the suffix Z, 1 with Z inserted); 1 + import line replaced by 1; 15 comment lines added; no other change to + the code. + - Work/Fourier/Goal.lean: written for this repository; contains lines of + upstream files: of its 7 code lines, 6 are lines of + lean/OAI/Computability/FourierTransform/Goal.lean (original lines 35, + 75-77, 79, 81): unchanged: its lines 25; with identifiers renamed: its + lines 21, 24, 26, 28, 30. + - Work/Fourier/Main.lean: modified copy of + lean/OAI/Computability/FourierTransform/Main.lean (151 lines; 90 of + them unchanged in the copy). Changes: identifiers renamed on 59 lines + (40 names: 35 with the suffix Z, 5 with Z inserted); 1 import line + replaced by 2; 24 comment lines added; other code lines changed: 1 of + the original, 1 of the copy (copy line 59). + - Work/Fourier/Seam.lean: written for this repository; contains lines of + upstream files: of its 122 code lines, 15 are lines of + lean/OAI/Computability/FourierTransform/TensorProgram.lean (original + lines 36-37, 95-96): unchanged: its lines 93-94, 113-114, 141-142, + 163-164, 185-187, 194-196; with identifiers renamed: its lines 184; 3 + are lines of lean/OAI/Computability/FourierTransform/TensorSaving.lean + (original lines 83-84, 114): with identifiers renamed: its lines 45-46, + 59; 1 is a line of lean/OAI/Computability/FourierTransform/Main.lean + (original line 60): with identifiers renamed: its lines 213. + - Work/Fourier/SectorAlgorithm.lean: modified copy of + lean/OAI/Computability/FourierTransform/SectorAlgorithm.lean (224 + lines; 162 of them unchanged in the copy). Changes: identifiers renamed + on 61 lines (17 names: 13 with the suffix Z, 4 with Z inserted); 1 + import line replaced by 1; 15 comment lines added; no other change to + the code. + - Work/Fourier/SynchronizedAlgorithm.lean: modified copy of + lean/OAI/Computability/FourierTransform/SynchronizedAlgorithm.lean (126 + lines; 94 of them unchanged in the copy). Changes: identifiers renamed + on 31 lines (12 names: 10 with the suffix Z, 1 with Z inserted, 1 + other), the other being "canopy" to "Grove.canopy" (5 lines); 1 import + line replaced by 1; 16 comment lines added; no other change to the + code. + - Work/Fourier/TransformProgram.lean: modified copy of + lean/OAI/Computability/FourierTransform/TransformProgram.lean (146 + lines; 94 of them unchanged in the copy). Changes: identifiers renamed + on 51 lines (21 names: 19 with the suffix Z, 2 with Z inserted); 1 + import line replaced by 1; 15 comment lines added; no other change to + the code. + - Work/Fourier/UniformBounds.lean: modified copy of + lean/OAI/Computability/FourierTransform/UniformBounds.lean (183 lines; + 115 of them unchanged in the copy). Changes: identifiers renamed on 67 + lines (29 names: 25 with the suffix Z, 4 with Z inserted); 1 import + line replaced by 1; 15 comment lines added; no other change to the + code. + - Work/Fourier/UniformFourierChallenge.lean: modified copy of + lean/ComparatorChallenges/UniformFourier.lean (336 lines; 328 of them + unchanged in the copy). Changes: identifiers renamed on 7 lines (7 + names: 7 with the suffix Z); 12 comment lines added; other code lines + changed: 1 of the original, 1 of the copy (copy line 281). + - Work/Fourier/WorkingCompiler.lean: modified copy of + lean/OAI/Computability/FourierTransform/WorkingCompiler.lean (153 + lines; 104 of them unchanged in the copy). Changes: identifiers renamed + on 48 lines (14 names: 14 with the suffix Z); 1 import line replaced by + 1; 15 comment lines added; no other change to the code. + - Work/Fourier/WorkingPreparation.lean: modified copy of + lean/OAI/Computability/FourierTransform/WorkingPreparation.lean (212 + lines; 156 of them unchanged in the copy). Changes: identifiers renamed + on 55 lines (30 names: 28 with the suffix Z, 2 with Z inserted); 1 + import line replaced by 1; 15 comment lines added; no other change to + the code. + - Work/Fourier/WorkingTransform.lean: modified copy of + lean/OAI/Computability/FourierTransform/WorkingTransform.lean (119 + lines; 91 of them unchanged in the copy). Changes: identifiers renamed + on 27 lines (9 names: 8 with the suffix Z, 1 with Z inserted); 1 import + line replaced by 1; 15 comment lines added; no other change to the + code. + + Changes made to the eleven copied proof files: every declaration received + the letter "Z" in its name (as a suffix of its own name, or of the name + of the declaration it belongs to), so that it does not collide with the + upstream declaration of the same name; the upstream exponent "alpha" + (1 - 2/10^11) and the bound "hills" defined from it were replaced by + "alphaZ" (1 - 7474547/10^10) and "hillsZ", which are defined in + Work/Fourier/Seam.lean, and the single use of the upstream theorem + "hills_program" by "hillsZ_program"; the five definitions of the upstream + statement that contain its exponent (decimalExponent, decimalTime, + TimeBounds, DFTGoal, ConvGoal) were replaced by the versions of + Work/Fourier/Goal.lean, whose exponent is 1 - 7474546/(10^(10:ℕ)); + the import lines point to the copies; a header comment was added. + Two further changes: in the copy of Main.lean the same constant stands + once as the literal 2/10^11 and was replaced there too, and the two + final theorems of that copy received a documentation comment; in the + copy of SynchronizedAlgorithm.lean the name "canopy" was written out as + "Grove.canopy" in 5 places, because the short name is shadowed in the + larger environment of the copy. The script also renamed three names + inside upstream comments; the comments are otherwise the upstream ones + and describe the upstream network. + The proofs are those of openai/math. The copies were made by the script + tools/fourier/mkchain.py of this repository. + + Work/Fourier/Goal.lean contains copies of those five definitions of + lean/OAI/Computability/FourierTransform/Goal.lean, with the exponent + replaced and the suffix "Z" on the five names. + + Work/Fourier/Seam.lean was written for this repository. It repeats the + statement of the upstream theorem "hills_program" (TensorProgram.lean) and + the definition of "hills" (TensorSaving.lean) with the two names replaced; + its header says so. + + Fourth result. The 15 files under Work/FourierE8/ repeat the 15 files + under Work/Fourier/ for the kernel program of the fourth result, file + for file and under the same file names: ArbitraryLength.lean, + Asymptotics.lean, ConvolutionProgram.lean, Goal.lean, Main.lean, + SectorAlgorithm.lean, SynchronizedAlgorithm.lean, TransformProgram.lean, + UniformBounds.lean, UniformFourierChallenge.lean, WorkingCompiler.lean, + WorkingPreparation.lean, WorkingTransform.lean, Seam.lean and + Axioms.lean. The eleven copied proof files, + Goal.lean and UniformFourierChallenge.lean are modified copies of the + same upstream files as their namesakes above, made by the script + tools/fourier/mkchain_e8.py (a copy of tools/fourier/mkchain.py with + its constants changed). The changes are those described above with + three differences: the suffix is "E8" where the copies of the third + result have "Z"; "alphaE8" is 1 - 8762479/10^10; and the exponent of + Work/FourierE8/Goal.lean is 1 - 8762478/(10^(10:ℕ)). Each of these + files begins with a comment that names the upstream file it is derived + from and states what was changed. Work/FourierE8/Seam.lean and + Work/FourierE8/Axioms.lean were written for this repository: they are + Work/Fourier/Seam.lean and Work/Fourier/Axioms.lean with names and + numbers replaced, and the seam repeats the same upstream statement and + definition as the seam of the third result. The proofs are those of + openai/math. + + The work of others that the third result follows (danadran01, shea256, + eumemic) is credited in README.md ("Whose reduction this is") and in + RELATED-WORK.md, section 1. No file of their repositories is included + here. + +8. The certificate of the fourth result and the search programs. + tools/certificate/gcert1-e8-r783.json.gz, the Lean data modules + generated from it (Work/GCert/Data/Gen/E8*.lean, Work/GCert/Data/Gen/E8/ + and the modules of Work/GCert/Data/ with E8 or B2Ge8 in their names) and + the Python programs under tools/e8/ were made by the agents for this + repository on 2026-10-10 (section 4). The circuit in that certificate + is not the circuit of section 6, and no data file of + CrocSwap/integer-mult-bounds was loaded to make it. It uses three + devices that were published by others in pull requests to that + repository: + - the in-place pair, a + b and a - b formed on the two arrays that held + a and b: icekylinx (#184), carried in #191 and #193 (ikeboy); + - the re-use of a finished helper with a compensating read: jamesyc + (#124) and eumemic (#143); + - the late pairing rule, the choice of which finished helper a new + value takes: Chafik Boukhalfa (the account chafreaky), #200 and #233, + and pull request #2 of this repository. The search programs apply the + idea with a greedy rule of their own; his program, his pairs and his + certificate were not used. + No program of that repository is included in tools/e8/, imported or + run by it. The programs of tools/e8/ have not been compared statement + by statement with the programs of that repository, as those of + tools/rebuild/ were (section 6). The integer programme of + tools/e8/ is solved with the open-source solver HiGHS, called through + SciPy (scipy.optimize.milp), with NumPy; none of the three is included + here. The credits, with links, are in README.md ("Whose ideas the new + unit uses") and in RELATED-WORK.md, section 8. + +Not included in this repository and fetched by the build: Mathlib +(https://github.com/leanprover-community/mathlib4, Apache License 2.0) and its +dependencies. The comparator (https://github.com/leanprover/comparator) is a +separate tool and is not included. diff --git a/research/e8-complex-far-retiming/complex/gcert1-e8-r783.json.gz b/research/e8-complex-far-retiming/complex/gcert1-e8-r783.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..0bd0bacf744f1c31419e05b834e34a92be77ae57 GIT binary patch literal 21883 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