From 376426f3b2b26ab430d10baf16bbd9ff42990a7c Mon Sep 17 00:00:00 2001 From: DreamingOfClouds Date: Sun, 11 Oct 2026 22:31:30 -0500 Subject: [PATCH] Punctured star bit unit: v = 111 ports, nine rank-7 totals, R = 489, bit c = 0.00165947204563779652541 Adds research/e8-punctured111-star: an E8 bit word with 111 live ports (9 of the 120 labelled lines punctured), nine rank-7 pseudo-target totals (decoder constant 63), R = 489 helpers, and the parameterisation of PR #493's hosted-audit chain that prices it (55 exact string edits, one configuration derived from the word). Bit c is 4.54% above the live bit c of PR #510; with PR #535's complex b, kappa = 0.001587467473762886 (complex-bound, +0.16% over #535). Regression: PR #503's R480 word is byte-identical through the stock and the adapted chain. Eight mutation controls, all rejected. Co-Authored-By: Claude Opus 5.5 --- research/e8-punctured111-star/EXPECTED.json | 127 ++++++ research/e8-punctured111-star/LICENSE | 201 ++++++++++ research/e8-punctured111-star/MANIFEST.json | 26 ++ research/e8-punctured111-star/NOTICE.md | 28 ++ research/e8-punctured111-star/README.md | 134 +++++++ research/e8-punctured111-star/code/adapt.py | 136 +++++++ .../e8-punctured111-star/code/adapt503.py | 41 ++ .../code/compose_control.py | 75 ++++ research/e8-punctured111-star/code/mutants.py | 178 +++++++++ .../code/price/price458.py | 71 ++++ .../e8-punctured111-star/code/price/pricex.py | 137 +++++++ .../e8-punctured111-star/code/punct_cfg.py | 79 ++++ .../code/word/gen_punct.py | 305 +++++++++++++++ .../e8-punctured111-star/code/word/pword.py | 136 +++++++ .../data/bitE8mut-v111-r489.json.gz | Bin 0 -> 41837 bytes .../e8-punctured111-star/data/complex-b.json | 19 + .../data/complex/pr535/EXPECTED.json | 62 +++ .../data/control-r480/EXPECTED-pr503.json | 75 ++++ .../data/control-r480/bitE8mut-r480.json.gz | Bin 0 -> 49414 bytes .../data/control-r480/hosting-r480.json | 240 ++++++++++++ .../data/hosting-v111-r489.json | 213 ++++++++++ research/e8-punctured111-star/verify.py | 366 ++++++++++++++++++ 22 files changed, 2649 insertions(+) create mode 100644 research/e8-punctured111-star/EXPECTED.json create mode 100644 research/e8-punctured111-star/LICENSE create mode 100644 research/e8-punctured111-star/MANIFEST.json create mode 100644 research/e8-punctured111-star/NOTICE.md create mode 100644 research/e8-punctured111-star/README.md create mode 100644 research/e8-punctured111-star/code/adapt.py create mode 100644 research/e8-punctured111-star/code/adapt503.py create mode 100644 research/e8-punctured111-star/code/compose_control.py create mode 100644 research/e8-punctured111-star/code/mutants.py create mode 100644 research/e8-punctured111-star/code/price/price458.py create mode 100644 research/e8-punctured111-star/code/price/pricex.py create mode 100644 research/e8-punctured111-star/code/punct_cfg.py create mode 100644 research/e8-punctured111-star/code/word/gen_punct.py create mode 100644 research/e8-punctured111-star/code/word/pword.py create mode 100644 research/e8-punctured111-star/data/bitE8mut-v111-r489.json.gz create mode 100644 research/e8-punctured111-star/data/complex-b.json create mode 100644 research/e8-punctured111-star/data/complex/pr535/EXPECTED.json create mode 100644 research/e8-punctured111-star/data/control-r480/EXPECTED-pr503.json create mode 100644 research/e8-punctured111-star/data/control-r480/bitE8mut-r480.json.gz create mode 100644 research/e8-punctured111-star/data/control-r480/hosting-r480.json create mode 100644 research/e8-punctured111-star/data/hosting-v111-r489.json create mode 100644 research/e8-punctured111-star/verify.py diff --git a/research/e8-punctured111-star/EXPECTED.json b/research/e8-punctured111-star/EXPECTED.json new file mode 100644 index 0000000000..ac7f102363 --- /dev/null +++ b/research/e8-punctured111-star/EXPECTED.json @@ -0,0 +1,127 @@ +{ + "word": { + "v": 111, + "R": 489, + "cst": 63, + "copy_ranks": [ + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7 + ], + "punctured": [ + 39, + 52, + 57, + 71, + 76, + 78, + 103, + 109, + 114 + ], + "content_sha256": "599dbe2184a0b99f01345543a6523e41df7b164513fc25dc7b5dadab548205d3", + "local_histogram": { + "1": 1182, + "2": 1332, + "3": 63, + "4": 153, + "5": 9, + "6": 129, + "7": 9 + }, + "five_stage_payload_bits": 91, + "eleven_stage_payload_bits": 91, + "source_destination_gates_A": 501 + }, + "eleven": { + "bit_lower": "165947204563779652541/100000000000000000000000", + "bit_lower_decimal": "0.00165947204563779652541", + "profile": { + "1": 104016, + "2": 120768, + "3": 5544, + "4": 17015, + "5": 792, + "6": 14904, + "7": 7896, + "28": 1, + "56": 1971 + }, + "emitted_program_sha256": "80c4e0f4d635a0c09a3e1a0ef81cc7e0590233410840d5b26d1149daa049718b", + "bit": { + "live": 8880, + "physical": 8889, + "maxchild": 56, + "rank_mass": 689304, + "hosted_residuals": 43032, + "prefix_bits": 1843, + "charged_prefix_bits": 1856, + "coefficient_bits": 1843, + "three_level_coefficient_bits": 5583, + "charged_three_level_coefficient_bits": 5632, + "row_coefficient": 19431, + "halving_depth": 3 + } + }, + "kappa": { + "pr535": { + "kappa": "793733736881443/500000000000000000", + "kappa_decimal": "0.001587467473762886", + "binding_component": "complex", + "complex_lower": "1589991533606044174031/1000000000000000000000000" + } + }, + "control": { + "word_content_sha256": "8639e22008c24a5690dd250eba702fe5fca85ddd036dcbec20baba77b7e213ba", + "five_stage_payload_bits": 102, + "bit_lower": "393688338238452162729/250000000000000000000000", + "bit_lower_decimal": "0.001574753352953808650916", + "profile": { + "1": 105600, + "2": 130560, + "4": 19680, + "6": 14400, + "7": 7680, + "8": 792, + "48": 1, + "56": 2742 + }, + "emitted_program_sha256": "7da5e3cb8daa231104f4e55f16c560fa56a59f10b8b2789e173923d9d2ca46fc", + "kappa": { + "pr484": "662515748707749/500000000000000000", + "pr471": "260706019542937/200000000000000000", + "pr458": "323880830669511/250000000000000000" + }, + "stacking": { + "calls": 88, + "calls_with_stacked_helpers": 80, + "max_helpers_on_one_family": 7 + } + }, + "live_bit": { + "source": "PR #510 (R480, nine stages), the bit c of PR #535", + "bit_lower": "396861776523137034277/250000000000000000000000", + "bit_lower_decimal": "0.001587447106092548137108" + }, + "pr535": { + "kappa": "1584931111785787/1000000000000000000", + "kappa_decimal": "0.001584931111785787", + "complex_lower": "1589991533606044174031/1000000000000000000000000" + }, + "hosting": { + "rule": "PR #442's pooled whole-residual integer programme (hosting_core.solve), eight replicas pooled, as in #472 / #493 / #503 / #510", + "stacking": { + "calls": 88, + "calls_with_stacked_helpers": 82, + "max_helpers_on_one_family": 7 + }, + "c_strict_per_stage": "0.001651282591289915996071", + "note": "strict = one helper per family copy per STAGE (stricter than #458 per invocation): a lower bound on the c of any #458-compliant hosting; kappa unchanged (complex-bound)" + } +} diff --git a/research/e8-punctured111-star/LICENSE b/research/e8-punctured111-star/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/research/e8-punctured111-star/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-punctured111-star/MANIFEST.json b/research/e8-punctured111-star/MANIFEST.json new file mode 100644 index 0000000000..33372c4ec6 --- /dev/null +++ b/research/e8-punctured111-star/MANIFEST.json @@ -0,0 +1,26 @@ +{ + "files": { + "EXPECTED.json": "dd078d075b153ea2ed02a4bbd1fe467315930f4d7dc90f2c34cd2c33e06a5029", + "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "NOTICE.md": "21fb6670dee5bf48cbee03db9273af62172e7d4c689102eda132ab3c4f30de19", + "README.md": "90e68ad9c861821ac0b8b5fa75470f6240aacdcc519b3879a53efab2bf44dfb8", + "code/adapt.py": "b9a50303147a3003ad94fc4b1a822d4fca7f9b9e917d165e3c45152575302a0e", + "code/adapt503.py": "04ec15262595e502796efc1f100f1efd4f28585add48377af1bda702a12c0e40", + "code/compose_control.py": "6eba9d46d6c176f4a3898b0b5c8d3047126becd8e552416b3eef5cea08250cc1", + "code/mutants.py": "796655c0eb43143ae6a7d8211a9eb473609818ca0e55e6949dd24b457b792070", + "code/price/price458.py": "25e314d5f020c895ce6753dda5339206ceed81b3909b29bf564ff68d33135983", + "code/price/pricex.py": "3d8aba64c8cad6694a31e0abd7689c139f23d84a8701d6c32e1253421fe8a131", + "code/punct_cfg.py": "7cedd3416d6a8ce2e6393cea32dc1d11032190787a593a0ad964a0b62fc407f3", + "code/word/gen_punct.py": "f2567d223a0fdbf59b14bb0c053fdeaf1178ca47d7616f84d30ac79b085ed101", + "code/word/pword.py": "a15ef6d2d110b69ed2a835ecd5d62df40ebfe76a95055a86603e56da9c04ea60", + "data/bitE8mut-v111-r489.json.gz": "c78317ab05ea175dc380f4dce2bafdbcd7e8b46f0c9d6baf350c2f604bb3ae4e", + "data/complex/pr535/EXPECTED.json": "cb734055c36681d38cf431a6926b7713351a94073cfba37d70a5384a9ef0d178", + "data/complex-b.json": "51426c86f95bc84b30c8c193b5b39b08742587a2eaadd8f83cca5ae3b36bdb49", + "data/control-r480/EXPECTED-pr503.json": "5e4a6b141ffd110a539f3be05be1bd249dc5ecec898cdd838642c71f9c42ac9b", + "data/control-r480/bitE8mut-r480.json.gz": "92e399e9a486b55229b5624632ac54f800dd9e022f2aee97f1e5f264bbc0ef5c", + "data/control-r480/hosting-r480.json": "29d55f5389a6bd7650e083b8318e725b505d8a6460a7afb4ddb08824675b6827", + "data/hosting-v111-r489.json": "862155e5c341053668ac1a18db05591417c38b9fe5f395d64a3b0a17a1502042", + "verify.py": "e897efb99839fab7140c26f255c57eb7e7c9bf78594184c6ddabe875b2a7e251" + }, + "parent_manifest_sha256": "7f25c41d0400ef5adf763eae6e166003a9b59e5a655b860c04b052f6731c7297" +} diff --git a/research/e8-punctured111-star/NOTICE.md b/research/e8-punctured111-star/NOTICE.md new file mode 100644 index 0000000000..623399dbaf --- /dev/null +++ b/research/e8-punctured111-star/NOTICE.md @@ -0,0 +1,28 @@ +# Notice + +Apache-2.0 (`LICENSE`). Prepared by DreamingOfClouds with Claude Opus 5.5 (Anthropic), via Claude Code. + +## New in this package + +- `code/punct_cfg.py`: the one configuration (v, R, live labels, totals map, CST) of the punctured chain. +- `code/adapt.py`: writes adapted copies of nine of PR #493's files (`emit`, `audit`, `local_audit`, `assemble`, + `host_select`, the nine- and eleven-stage `geometry`, the independent checker, `mutate_records`) by 56 listed string edits + (each anchor checked to occur exactly the listed number of times). The parent tree is never modified. +- `code/word/` (`pword.py`, `gen_punct.py`): the punctured word builder (PR #493's generator logic restricted to a live port + subset, nine pseudo-target totals). Written for this package; imports the parent's `geom`, `e8`, `wordkit` and plan. +- `code/price/pricex.py`: the exact pricing harness (local-histogram replay of the word, the same whole-residual hosting + programme, `moment.certify`), validated against the registered R489 / R480 values; only its package path was changed. +- `code/mutants.py`, `code/compose_control.py`: the mutation controls and the composition/control with the cited complex b. +- `code/adapt503.py`: PR #503's `adapt.py`, byte-identical (DreamingOfClouds, with Claude Opus 5.5), used only to build the + stock R480 control chain for the regression. +- `data/bitE8mut-v111-r489.json.gz` (the word; `verify.py` rebuilds it byte for byte), `data/hosting-v111-r489.json` (the hosting + selection nominated by `host_select.py` and rediscovered by `verify.py`), `data/control-r480/` (PR #503's R480 word, hosting + selection and EXPECTED.json, byte-identical copies), `data/complex/pr535/EXPECTED.json` (PR #535's receipt, byte-identical, + git blob id in `data/complex-b.json`), `verify.py`, `EXPECTED.json`, `MANIFEST.json`, `README.md`. + +## Used from PR #493 (research/e8-bit-carrier-skeleton, eumemic, commit c3a9a43), unchanged + +The skeleton modules and frozen plan, the vendored PR #370 checker and its relaxation, `interface.py` and the mutants, the +exporter, the official cohort checkers and the Gram check, PR #472's emitter, audit, invoice and assembly (as adapted by #493 +and by `code/adapt.py`), the hosting modules, PR #315's moment engines, the hub files under `code/hub/`. Provenance and +licences of those files are listed in PR #493's `NOTICE.md`; its `MANIFEST.sha256` is checked by step 0 of `verify.py`. diff --git a/research/e8-punctured111-star/README.md b/research/e8-punctured111-star/README.md new file mode 100644 index 0000000000..1c6fc7e48d --- /dev/null +++ b/research/e8-punctured111-star/README.md @@ -0,0 +1,134 @@ +# Punctured star bit unit: 111 ports, nine rank-7 totals, R = 489 + +**Bit c = 0.00165947204563779652541, +4.54% over the live bit c of PR #510.** κ is another matter: composed with #535's +complex b, κ = 0.001587467473762886 is **complex-bound, +0.16% over #535**. The bit word raises the cap on any future complex +gain; it does not move κ by more than that today. + +This package ships one E8 bit word and the parameterisation of PR #493's hosted-audit chain that prices it. The word is +PR #493's quad skeleton with 9 of the 120 labelled E8 lines punctured (neither source nor target), so it has **v = 111 live +ports**. The nine retained decoder totals are nine *pseudo-target* helpers, one per punctured line, each of frame rank 7, so the +decoder constant is **cst = 9 x 7 = 63** (PR #493 / #503: 9 x 8 = 72). The result is **R = 489** helpers (480 skeleton helpers ++ 9 totals). The chain (emit, independent audit, exact price, outer assembly) is #493's, with every hard-coded +120 / 72 / 9600 / 489 derived from the word. + +| quantity | value | +|---|---| +| bit c, eleven stages, eight pooled replicas, every residual whole and hosted (10^-24 grid; #472's pipeline as #493 runs it) | `165947204563779652541/100000000000000000000000` = **0.00165947204563779652541** | +| live bit c: PR #510's R480 word at nine stages (the bit supplier of #535) | 0.001587447106092548137108 (**+4.5372%**) | +| same pipeline on PR #503's R480 word (v = 120), reproduced by `verify.py` | 0.001574753352953808650916 (+5.3798%) | +| pricing harness `code/price/pricex.py` (independent local-histogram replay + the same hosting programme + `moment.certify`) | equal to all 24 digits, same profile | +| κ with #535's complex b = `1589991533606044174031/10^24` | **`793733736881443/500000000000000000` = 0.001587467473762886**, complex-bound; #535 publishes 0.001584931111785787 (bit-bound at the live bit c) | +| robustness: c under a stricter hosting (one helper per family copy per stage), `code/price/price458.py` | >= 0.001651282591289915996071 (at most 0.49% lower; still +4.02% over the live bit c); κ unchanged | + +#535's complex b (0.00158999) is 4.37% below this unit's c, so κ is complex-bound and rises +0.1600% +(0.001584931111785787 -> 0.001587467473762886). The remaining 4.37% of bit headroom pays only against a future complex b above +1.59e-3. The κ line is computed by the same outer code that reproduces #535's published κ from #510's bit c (step 9, control). + +## What the unit is + +* Word: `data/bitE8mut-v111-r489.json.gz`, format `bitE8/1+mut` (PR #493), `v = 111`, `R = 489`, `ports_geom` = the 111 live + labels as indices into the 120-line system, `punctured` = **[39, 52, 57, 71, 76, 78, 103, 109, 114]** (footprint (1^9): one + punctured line in each of nine 4-frames; none of lines 0..7, so the frame-zero residual basis 2e_0..2e_7 is made of live + labels and PR #493's residual-isometry check applies unchanged). `verify.py` rebuilds it byte for byte from PR #493's frozen + plan with `code/word/gen_punct.py`. +* Skeleton: #493's, verbatim, restricted to the live lines. Every helper register is kept; the carriers of punctured lines stay as + junk wires (18). The phase-A total for a punctured line y_j is a fresh helper that reads exactly what the removed target would + read (the two quad outputs and the Q delivery at D6[Hy], then its two needers at y_j-perp): its x-part is z_{y_j} restricted to + the live lines, its frame has rank 7, and it is issued at the end of phase A, so no reader needs an extra climb. Hosting the + totals on dead registers (as #503 does for rank 8) fails here: a rank-7 total can only combine dead registers whose frames lie + inside y_j-perp, and none has a frame in D6[Hy]. +* Frames are the live line sets of the closed 120-line frames; their ranks are recomputed by every checker from the word's own + `lines`. +* Choice of P: of 414 sampled punctures (W1's search), 304 reach R = 489 and 110 need 1-3 Q-node junk wires (R = 490-492); among the + R = 489 words c varies slightly (1.659350e-3 to 1.659472e-3, 8 distinct local histograms). The shipped word has the highest c + found (reached by 145 of the 414). A different representative of footprint (3,3,1,1,1), P = [16,22,32,43,54,70,76,90,103], + builds R = 491 and c = 1.656230e-3; it is not this word. No claim is made that 489 is a floor for other skeletons. + +## What was parameterised (`code/adapt.py`) + +`adapt.py` writes adapted copies of nine parent files by 55 exact string edits (each anchor must occur exactly the listed number +of times, as in #503). The parent tree is never modified. Nothing is read from the environment; the one configuration +(`code/punct_cfg.py`) is derived from the word (v, R, live labels, the totals map) and, in scripts that never see the word, from +the exported records. + +| file | constant (parent) | now | +|---|---|---| +| `emit.py` | `(h, v, form) == (8, 120, ...)` | v from the word; configuration checked against the word | +| | COPY rank `== 8` | `0 < rank <= 8`; the sum over the nine totals is the CST and must equal the word's totals map (63) | +| | `72` (local mass identity, rank-mass deficit, deficit field) | CST | +| | replica stride `1200` (3x) | `2 K v` | +| `audit.py` | `V, R = 120, 489`, `DATA = 9600`, physical `9609`, deficit `3264`, `72`, `len(ports) == 120`, helper offset `240`, stride `1200` (2x), `geometry_routes = 1320` | V, R from the records (`n = 2V + R`); CST from the audit's own replay of the records' COPY ranks; `DATA = 2 K V T` | +| `local_audit.py` | `969`, `729`, `240` (2x), `R489` status string, the word path | `2V + n`, `n`, `2V`, `R%d`, the word given to `adapt.py` (as #503 does) | +| `assemble.py` | `V = 120`, `72`, suppliers file | `V` from the bit's GLOBAL.json; the deficit from the audit (it must fit nine copy ranks); optional suppliers argument | +| `host_select.py` | `V = 120` | `V = word['v']` | +| nine-/eleven-stage `geometry.py` | all 120 ports emitted; `240`, `120`, `1080`, `1320` | only the live ports, re-indexed 0..v-1; counts from `len(LIVE)` | +| `chk/checkword.py` | `len(lines) == v == 120` | `100 <= v <= 120`, plus: every label is one of the 120 E8 root lines (the lines of #493's word, which #370's checker accepts); norm 2, pairwise \|ip\| <= 1, rank 8 still checked; `relax.py` is the parent's, unchanged | +| `mutate_records.py` | "COPY with a cheaper rank" sets the rank to the literal 7 | `rank - 1` (a literal 7 is the true rank of a rank-7 total) | + +Unchanged and already general: the C++ checkers (`-DVPORTS=v -DRHELP=R -DNCOPY=9`, `HDIM = 8`; their COPY check is +`z == dim(frame)`), the residual-isometry check (`validate_isometries`, stock call), `export_records.py`, `stage_cohort.py`, +`gram_frames.py`, `interface.py`, `mutate_mut.py`, `mutate_records_mut.py`, the moment engines, the row bound, and the outer +assembly (it sees only c, b and the bit's charged coefficient bits). The cited complex b enters as a bare rational; there is +**no port count on the complex side of the assembly**, so the bit v = 111 is not tied to the complex word's own v. + +## Checks (`verify.py`) + +0. This package's `MANIFEST.json` and #493's `MANIFEST.sha256`. +P. (full mode) #493's `verify.sh`, unchanged. +1. The word rebuilt from #493's plan is byte-identical; the configuration (v, R, totals map, CST = 63). +2. Independent checker (#370's, `v <= 120` and the E8-membership check, relaxed for mutable sources by #493's `relax.py`): exact F2 + replay of all 2v + R columns, frame nesting, Gram, price; `interface.py` (#425's hypotheses); the word-level mutants. +3. Official cohort checkers at (v, R): legality (NCOPY = 9), five-stage columns (payload 91 bits < 104), eleven-stage columns + (24/24 omitted-bridge controls), KPAIRS = 2 regression, record mutants. +4. Exact Gram on all 636 frames. +5. Geometry (live labels), #442 hosting programme (rediscovers `data/hosting-v111-r489.json`), emit, independent audit (22 template + mutations), assembly (47 strict inequalities and controls) with #535's complex b. +6. `pricex.py` agrees with the hosted-audit c to all 24 digits; hosting-stacking statistics; `price458.py` (stricter hosting). +7. **Regression**: PR #503's R480 word through the stock chain (parent `emit.py` / `assemble.py`, #503's `audit.py` / + `local_audit.py`) and through the adapted chain: `GLOBAL.json`, `GLOBAL-TEMPLATE.json.gz`, `AUDIT.json`, `RESULT.json` are + byte-identical (GEOMETRY.json stores its own run time and is shared), equal to #503's EXPECTED (c, profile, emitted-program + sha256, κ for three suppliers). +8. **Mutation controls** (`code/mutants.py`, 8): dropped gate, wrong frame, payload overflow (legal and F2-correct; refused only by + `columns_h`), cell collision (#442 rule), a helper hosted on the family the call acts on, a cheaper profile claim, v tied to 112 + (the cited complex word's port count), the stock #493 emitter on the 111-port word. M4a tests same-cell collisions only; it is + not a test of #458's one-helper-per-family rule (see Scope). +9. Composition with #535's complex b, and the control that reproduces #535's published κ from #510's bit c with the same code. + +## Rank-mass identity for v < 120 + +The chain asserts `M * live - mass = T (2 K v - L cst)` (here M = 78, T = 8, K = 5, L = 11, live = 2 K v T). It is a telescoping +identity, valid for any v: + +* locally, sources climb rank 1 to 8, targets 0 to 7 and helpers 0 to 8, so the local mass is `8 R + cst + 14 v`; +* at eleven stages the idle ranks sum to 308 per family (2 v families), and the 88 R hosted helper climbs of width 8 are paid out + of that idle rank, so the global mass is `T (11 (8 R + cst + 14 v) + 616 v - 88 R) = T (11 cst + 770 v)`; +* with `M * live = 780 v T` the difference is `T (10 v - 11 cst)`. + +For v = 111 and cst = 63 this is 8 x 417 = 3336 (v = 120, cst = 72: 3264). The same shape, `D = h ((s-1) v - s cst)`, is the one +PR #523 uses for its punctured complex word; emit and audit check the identity numerically on the emitted program. + +## Scope and what is not claimed + +* The word is checked as a `bitE8/1` word with 111 labels (norm 2, pairwise |ip| <= 1, rank 8, labels in the 120-line E8 system); + that a 111-port unit with cst = 63 is an admissible bit supplier for the outer assembly rests on the same hypotheses as v = 120. + Everything #493 and #472 assume (all-size compiler, common-label/mutable-basis interface, whole-call hosting) is assumed + unchanged. This package makes no unconditional claim. +* **Hosting rule.** The hosting is the pooled whole-residual programme of #442 / #472 / #493 (`hosting_core.solve`, eight replicas + pooled, `2 v T` copies per idle child): every helper residual is hosted whole on an idle data family, at disjoint cells. It does + **not** satisfy #458's literal rule that an entering helper is the only owner of its physical family in an invocation: in 82 of + the 88 calls several helpers of that call sit on one family (at most 7), at disjoint offsets. This is inherited: PR #503's + accepted emitted program does the same in 80 of 88 calls (reproduced by step 7). `verify.py` prints both counts. Under the stricter + rule "one helper per family copy per stage" (which implies #458's per-invocation rule) the unit still prices at + c >= 0.001651282591289915996071 (at most 0.49% lower, +4.02% over the live bit c); κ is complex-bound and unchanged. +* Payload margin: 91 bits against 104. Stage count: the unit is feasible only at eleven (and thirteen) stages; the nine-stage + hosting wall is R <= 4 v = 444. +* The pricing harness and the hosted emit/audit share `hosting_core`; their agreement at v = 111 checks the adapted emitter and + audit against an independent local-histogram replay, not the hosting programme itself. + +## Credit + +PR #493 (eumemic) is the skeleton, the word family and the chain; #503 (DreamingOfClouds) the dead-total variant and the +`adapt.py` pattern; #523 (icekylinx) the puncture pattern on the complex side (ports removed, copy ranks lowered, D recomputed); +#472 (chafreaky), #442 (DaysSky), #445, #386, #370 (Dylan Crow), #381, #315 the pipeline and checkers; #510 the live bit c; #535 +(icekylinx) the cited complex b; the independent audit of this package supplied `code/price/price458.py`. Prepared by +DreamingOfClouds with Claude Opus 5.5 (Anthropic), via Claude Code. Apache-2.0. diff --git a/research/e8-punctured111-star/code/adapt.py b/research/e8-punctured111-star/code/adapt.py new file mode 100644 index 0000000000..bd1552af18 --- /dev/null +++ b/research/e8-punctured111-star/code/adapt.py @@ -0,0 +1,136 @@ +# Parameterises PR #493's hosted-bit chain (emit, audit, local audit, assembly, hosting nominator, the two geometries, +# the independent checker and one record mutant) for a word with v <= 120 live ports and nine totals of any copy rank. +# Every change is an exact string edit of a pinned parent file; each anchor must occur exactly the listed number of +# times. Nothing is read from the environment: V, R and the sum of the nine copy ranks come from the word, or, in +# scripts that never see the word, from the exported records. For v = 120 and nine rank-8 totals (PR #493, PR #503) +# every adapted script computes exactly what the parent computes (verify.py, step 7, compares the outputs byte for byte). +# Prepared by DreamingOfClouds with Claude Opus 5.5 (Anthropic), via Claude Code; the parent files are PR #493's / #472's. Apache-2.0. +"""usage: adapt.py PARENT_PACKAGE WORD.json.gz OUTDIR + OUTDIR/emit.py, audit.py, local_audit.py, assemble.py, host_select.py, nine_geometry.py, eleven_geometry.py + OUTDIR/chk/checkword.py (+ relax.py) the pinned independent checker with ONE assertion changed (v == 120 -> v <= 120) + OUTDIR/mutate_records.py the record mutants, the 'cheaper COPY rank' one made relative to the true rank + WORD is baked into local_audit.py (the word whose records are audited), as PR #503's adapt.py does.""" +import sys +from pathlib import Path + +PARENT, WORD, OUT = Path(sys.argv[1]).resolve(), Path(sys.argv[2]).resolve(), Path(sys.argv[3]).resolve() +HERE = Path(__file__).resolve().parent # punct_cfg.py lives here +H_EMIT = str(PARENT / 'code/hub/e8-r740-replica32/code'); H_HOST = str(PARENT / 'code/hub/idle-axis-hosting') +PKG = str(PARENT) +PC = "sys.path.insert(0,%r)\nimport punct_cfg as PC\n" % str(HERE) + +# (anchor, replacement[, expected count]) +EDITS = { + 'code/eleven/emit.py': ('emit.py', [ + ('sys.path.insert(0,str(HERE.parent/"hub/e8-r740-replica32/code"))\n', "sys.path.insert(0,%r)\n" % H_EMIT), + ('sys.path.insert(0,str(HERE.parent/"hub/idle-axis-hosting"))\n', "sys.path.insert(0,%r)\n" % H_HOST), + ("import hosting_core as HC\n\nH,L,K,T,M=8,11,5,8,78\n", "import hosting_core as HC\n" + PC + "\nH,L,K,T,M=8,11,5,8,78\n"), + # V is the word's port count (a punctured word carries its live labels in ports_geom); config from the word + (" assert (w['h'],V,w['form'])==(8,120,'/2')\n", + " assert (w['h'],w['form'])==(8,'/2'); CFG=PC.config(w,%r); assert (CFG['v'],CFG['R'])==(V,R)\n" % PKG), + # the COPY rank of a total is the dimension of its frame (8 for #493 / #503, 7 for the star unit); the sum is derived + (" streams={}; checks={}; localH=None\n", " streams={}; checks={}; localH=None; CSTD=[]\n"), + ("norm=[1]*LOCAL+[0]; maxnorm=1; center=None; hist=Counter(); events=[]; copies=0; adds=0", + "norm=[1]*LOCAL+[0]; maxnorm=1; center=None; hist=Counter(); events=[]; copies=0; adds=0; crs=[]"), + ("copy_rank=abs(dim(c)-dim(f)); assert copy_rank==8", "copy_rank=abs(dim(c)-dim(f)); assert 0 6 else root / "data/complex-b.json").read_text())["suppliers"]}'), + ]), + 'discovery/host_select.py': ('host_select.py', [ + ('sys.path.insert(0, str(HERE.parent / "code" / "hub" / "idle-axis-hosting"))', "sys.path.insert(0, %r)" % H_HOST), + ("H, L, K, T, M, V = 8, 11, 5, 8, 78, 120\n", "H, L, K, T, M = 8, 11, 5, 8, 78\n"), + (" R = w['R']\n", " R = w['R']; V = w['v']\n"), + ]), + 'code/bit/independent/checkword.py': ('chk/checkword.py', [ + ("assert len(lines)==v==120 and h==8", + "assert len(lines)==v and 100<=v<=120 and h==8 # v live ports of the 120 E8 lines\n" + "# the live labels must be E8 root lines: members of the 120-line system of PR #493's word (the lines PR #370's checker accepts)\n" + "CANON={tuple(x) for l in json.load(gzip.open(%r,'rt'))['lines'] for x in (l,[-y for y in l])}\n" + "assert all(tuple(a) in CANON for a in lines), 'a label is not one of the 120 E8 root lines'" % str(PARENT / 'data/bit/bitE8mut-r489.json.gz')), + ("print('labels: 120 distinct lines of norm 2 (pairwise |ip| <= 1), spanning rank 8')", + "print('labels: %d distinct lines of norm 2 (pairwise |ip| <= 1), spanning rank 8'%v)"), + ]), + 'code/bit/mutate_records.py': ('mutate_records.py', [ + ("lambda e:e.__setitem__(5,7)", "lambda e:e.__setitem__(5,e[5]-1)"), + ]), +} +# the geometries build every port isometry by BFS over all 120 lines; only the live ports are emitted, re-indexed 0..v-1 +GEOM = lambda: [ + ("ap.add_argument('--out',type=Path,required=True); a=ap.parse_args(); t0=time.monotonic()", + "ap.add_argument('--out',type=Path,required=True); ap.add_argument('--word',type=Path,default=None); a=ap.parse_args(); t0=time.monotonic()"), + (" assert len(gs)==120\n", + " assert len(gs)==120\n" + " if a.word is None: LIVE=list(range(120))\n" + " else:\n" + " ww=json.loads(gzip.decompress(a.word.read_bytes())); LIVE=list(ww['ports_geom']) if 'ports_geom' in ww else list(range(120))\n" + " assert len(LIVE)==ww['v'] and all(ww['lines'][i]==list(map(int,lines[p])) for i,p in enumerate(LIVE))\n"), + (" for t,q in enumerate(lines):\n g=gs[t]; assert", " for t,src in enumerate(LIVE):\n q=lines[src]; g=gs[src]; assert"), + ("exact_bit_endpoint_operator_checks=240,", "exact_bit_endpoint_operator_checks=2*len(LIVE),"), + ("missing_route_factor_controls=120,", "missing_route_factor_controls=len(LIVE),"), +] +EDITS['code/nine/geometry.py'] = ('nine_geometry.py', GEOM() + [("route_checks=1080,", "route_checks=9*len(LIVE),")]) +EDITS['code/hub/r740-hosted-eleven/code/geometry.py'] = ('eleven_geometry.py', GEOM() + [("route_checks=1320,", "route_checks=11*len(LIVE),")]) + +OUT.mkdir(parents=True, exist_ok=True); (OUT / 'chk').mkdir(exist_ok=True) +n = 0 +for src, (dst, edits) in EDITS.items(): + text = (PARENT / src).read_text() + for e in edits: + old, new = e[0], e[1]; cnt = e[2] if len(e) > 2 else 1 + assert text.count(old) == cnt, (src, old, text.count(old)) + text = text.replace(old, new); n += 1 + (OUT / dst).write_text("# Adapted from PR #493's %s by research/e8-punctured111-star/code/adapt.py. Apache-2.0.\n" % src + text) +(OUT / 'chk/relax.py').write_bytes((PARENT / 'code/bit/independent/relax.py').read_bytes()) +print(' adapted %d parent files with %d listed edits (each anchor found exactly the listed number of times)' % (len(EDITS), n)) diff --git a/research/e8-punctured111-star/code/adapt503.py b/research/e8-punctured111-star/code/adapt503.py new file mode 100644 index 0000000000..6d855e7688 --- /dev/null +++ b/research/e8-punctured111-star/code/adapt503.py @@ -0,0 +1,41 @@ +# Writes the three adapted files this package runs, from PR #493's package (research/e8-bit-carrier-skeleton), +# by exact string edits; each edited string must occur exactly once in the parent file. Nothing else changes. +# Prepared by DreamingOfClouds with Claude Opus 5.5 (Anthropic), via Claude Code. Apache-2.0. +"""usage: adapt.py PARENT_PACKAGE R480_WORD OUTDIR + OUTDIR/gen_r480.py = parent code/skeleton/gen.py, totals hosted on the LAST eligible dead register + OUTDIR/eleven/audit.py = parent code/eleven/audit.py with R = 480 + OUTDIR/eleven/local_audit.py = parent code/eleven/local_audit.py with R = 480 (960 formal columns, 720 registers, + the audited word = the R480 word); parent package root for the shared hub files""" +import sys +from pathlib import Path + +PARENT, WORD, OUT = Path(sys.argv[1]).resolve(), Path(sys.argv[2]).resolve(), Path(sys.argv[3]).resolve() +EDITS = { + 'code/skeleton/gen.py': ('gen_r480.py', [ + # the parent modules (e8, geom, wordkit) are imported from the parent package + ("sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))", + "sys.path.insert(0, %r)" % str(PARENT / 'code/skeleton')), + # THE change: a total formed on a dead register is hosted by the last eligible register of its combination + # (the parent takes the first one; with the first, the payload bill is 129 bits > 104) + ("host = next((r for r in sorted(combo) if r not in used", + "host = next((r for r in sorted(combo, reverse=True) if r not in used"), + ]), + 'code/eleven/audit.py': ('eleven/audit.py', [ + ("K,L,M,T,V,R=5,11,78,8,120,489", "K,L,M,T,V,R=5,11,78,8,120,480"), + ]), + 'code/eleven/local_audit.py': ('eleven/local_audit.py', [ + ("ROOT=Path(__file__).resolve().parents[2]", "ROOT=Path(%r)" % str(PARENT)), + ("cc['formal_columns_checked']==969", "cc['formal_columns_checked']==960"), + ("(ROOT/'data/bit/bitE8mut-r489.json.gz')", "(Path(%r))" % str(WORD)), + ("range(729)", "range(720)"), + ("PASS_EXACT_R489_LOCAL_PROJECTORS", "PASS_EXACT_R480_LOCAL_PROJECTORS"), + ]), +} +n = 0 +for src, (dst, edits) in EDITS.items(): + text = (PARENT / src).read_text() + for old, new in edits: + assert text.count(old) == 1, (src, old, text.count(old)) + text = text.replace(old, new); n += 1 + out = OUT / dst; out.parent.mkdir(parents=True, exist_ok=True); out.write_text(text) +print(' adapted 3 parent files with %d listed edits (each anchor found exactly once)' % n) diff --git a/research/e8-punctured111-star/code/compose_control.py b/research/e8-punctured111-star/code/compose_control.py new file mode 100644 index 0000000000..05ba6fa625 --- /dev/null +++ b/research/e8-punctured111-star/code/compose_control.py @@ -0,0 +1,75 @@ +# Outer composition of a bit c with a cited complex b, and the control that reproduces PR #535's published kappa. +# The outer half is PR #472's / #493's assemble.py verbatim (six bootstrap levels, 47 strict inequalities, next-grid and +# invalid-parameter controls), here as a function of (c, b) so that the SAME code is run on +# control : PR #535's inputs (the live bit c of PR #510, #535's complex b) -> must give #535's published kappa exactly +# star : this package's c and the same b -> must equal the kappa that assemble.py wrote +# No bit port count enters: the bit unit has v = 111 ports, #535's complex word has its own; the outer assembly sees only +# the two real numbers c and b, the bit's charged coefficient bits (bootstrap cutoffs only) and the row bound. +# Prepared by DreamingOfClouds with Claude Opus 5.5 (Anthropic), via Claude Code; engines are PR #315's / #361's (pinned in the parent). Apache-2.0. +"""usage: compose_control.py PARENT_PACKAGE RESULT.json PR535_EXPECTED.json OUT.json""" +import importlib.util +import json +import sys +from fractions import Fraction as Q +from pathlib import Path + +parent, resp, e535p, outp = map(Path, sys.argv[1:5]) +eng = parent / 'code/hub/e8-r740-replica32/code/engines' + + +def load(name): + sp = importlib.util.spec_from_file_location('comp_' + name, eng / (name + '.py')) + mod = importlib.util.module_from_spec(sp); sp.loader.exec_module(mod); return mod + + +Outer = load('outer') + + +def ceil(x): return -((-x.numerator) // x.denominator) + + +def compose(c, b, coeff_bits, row=20161): + eta = beta = grid = Q(1, 10 ** 18) + s = min(c, (1 - beta) * b) - Q(1, 10 ** 24) + assert 0 < s < c and s < (1 - beta) * b + chain, cuts = [Q(384599, 10 ** 10)], [] + for _ in range(6): + old = chain[-1]; a = (1 - s) * s + s * old + gap = min(s - a, 1 - a - s, 1 - a - s * (1 - old), 1 - s); assert old < a < s < 1 - a and gap > 0 + cut = max(ceil(36 / gap ** 2), ceil(2 * (4 + coeff_bits) / gap)) + assert cut * gap ** 2 >= 36 and cut * gap >= 2 * (4 + coeff_bits) + cuts.append(str(cut)); chain.append(a) + q = chain[-1] * (1 - 2 * eta); margin = (1 - eta) * q / (1 + q); ticks = margin / grid + k = grid * ((ticks.numerator - 1) // ticks.denominator) + bridge = {'semantic': {'C0': {'base': 2, 'exponent': 210000}, 'strict_literal_gap': 1}, + 'rows': {'degree_gap': Q(10 ** 6) - Q(51 * row, 25)}} + a = Outer.assembly(chain[-1], b, bridge, k, eta=eta, beta=beta) + assert len(a['strict_constraints']) == 47 and all(x > 0 for x in a['strict_constraints'].values()) + ctl = [] + for name, kk, ee, bb in [('next_grid', k + grid, eta, beta), ('zero_eta', k, Q(0), beta), ('zero_beta', k, eta, Q(0)), ('unit_beta', k, eta, Q(1))]: + try: Outer.assembly(chain[-1], b, bridge, kk, eta=ee, beta=bb) + except AssertionError: ctl.append(name) + else: raise AssertionError(name) + return dict(kappa=k, binding='bit' if c < (1 - beta) * b else 'complex', controls=ctl, strict=47) + + +e535 = json.loads(e535p.read_text()); res = json.loads(resp.read_text()) +b = Q(e535['complex_lower']); c510 = Q(e535['bit_lower']); k535 = Q(e535['kappa']) +ctl = compose(c510, b, 512) # #535 adds the PR #510 nine-stage bit (charged two-level coefficient 512 bits) +assert ctl['kappa'] == k535 and ctl['binding'] == e535['binding_component'] == 'bit', (ctl, e535['kappa']) +c = Q(res['bit_lower']) +star = compose(c, b, res['bit']['charged_three_level_coefficient_bits']) +assert str(b) == res['kappa']['pr535']['complex_lower'] and star['kappa'] == Q(res['kappa']['pr535']['kappa']) +assert star['binding'] == res['kappa']['pr535']['binding_component'] +out = dict(complex_lower=str(b), pr535_published_kappa=str(k535), pr535_published_kappa_decimal=e535['kappa_decimal'], + control_bit_lower=str(c510), control_kappa=str(ctl['kappa']), control_binding=ctl['binding'], + control_reproduces_pr535_kappa=True, control_outer_controls=ctl['controls'], + star_bit_lower=str(c), kappa_star_vs_pr535=str(star['kappa']), star_binding=star['binding'], + star_outer_controls=star['controls'], star_strict_constraints=star['strict'], + kappa_gain_over_pr535=str(star['kappa'] / k535 - 1), bit_headroom_over_complex_b=str(c / ((1 - Q(1, 10 ** 18)) * b) - 1)) +outp.write_text(json.dumps(out, indent=1) + '\n') +print(' control: #510 bit c %s + #535 b -> kappa %s == #535 published %s (bit-bound), 47/47 strict, controls %s' + % (e535['bit_decimal'], ctl['kappa'], k535, ','.join(ctl['controls']))) +print(' star : bit c %s + #535 b -> kappa %s (%s-bound), 47/47 strict; %+.4f%% over #535; bit c exceeds (1-1e-18)b by %+.4f%%' + % (res['bit_lower_decimal'], star['kappa'], star['binding'], 100 * float(star['kappa'] / k535 - 1), + 100 * float(c / ((1 - Q(1, 10 ** 18)) * b) - 1))) diff --git a/research/e8-punctured111-star/code/mutants.py b/research/e8-punctured111-star/code/mutants.py new file mode 100644 index 0000000000..f47e4cb97e --- /dev/null +++ b/research/e8-punctured111-star/code/mutants.py @@ -0,0 +1,178 @@ +# Mutation controls for the punctured-word hosted chain. Every mutant is built from the SHIPPED word (or from the chain's +# own emitted files) by one change, pushed through the real chain stages, and must be refused by a named stage. +# Prepared by DreamingOfClouds with Claude Opus 5.5 (Anthropic), via Claude Code. Apache-2.0. +"""usage: mutants.py WORD.json.gz WD ADAPTED_DIR PARENT_PACKAGE HOSTING.json OUT.json +WD is verify.py's work dir after steps 1-5 (bin/, records/, cin/, cout/, nine/, bit/, interface.json). + + word-level (the mutated word goes through the relaxed independent checker, the exporter, the official legality and + five-stage / eleven-stage column checkers, and the emitter's gate on their receipts): + M1 dropped gate delete the phase-A gate that forms the first retained total + M2 wrong frame move a phase-B gate to the zero frame (below the frames its registers already stand at) + M3 payload overflow an F2-neutral block of Fibonacci-growth ADDs on two helpers: legal, restores every column, + but the signed prefix payload exceeds 2^104 -> refused by columns_h only + template-level (the chain's own emitted files, tampered; audit.py must refuse, the hashes of the tampered file re-written + so that only the semantic checks can catch it): + M4a cell collision (#442 rule) two helpers of one invocation given the same physical family AND hosted cells. + (NOT a test of #458's one-helper-per-family rule: the pooled whole-residual hosting of + #442/#472/#493/#503 stacks several helpers on one family at disjoint cells; see README) + M4b hosted on the invoked pair a helper's residual placed on the data family the call itself acts on + M5 profile claim GLOBAL.json claims a cheaper paid profile than the template's + assembly-level: + M6 v tied to the complex word GLOBAL.json v := 112 (the cited complex v); assemble.py must refuse + M7 the stock PR #493 emitter refuses any word with v != 120 (what the parameterisation had to change)""" +import copy +import gzip +import hashlib +import json +import shutil +import subprocess +import sys +from pathlib import Path + +WORD, WD, AD, PARENT, HOST, OUT = map(Path, sys.argv[1:7]) +PY = sys.executable +B = PARENT / 'code/bit'; C = PARENT / 'code' +sys.path.insert(0, str(Path(__file__).resolve().parent)) +import punct_cfg as PC # noqa: E402 + +w0 = PC.load(WORD); V, R = w0['v'], w0['R'] +BIN = WD / 'bin' +LEG = BIN / 'legality'; COLH = BIN / ('columns_h_v%d_r%d' % (V, R)); COLK = BIN / ('columns_k5_v%d_r%d' % (V, R)) +PROBE = BIN / ('columns_probe_v%d_r%d' % (V, R)) +MD = WD / 'mut'; shutil.rmtree(MD, ignore_errors=True); MD.mkdir() +controls = [] + + +def run(cmd, **kw): + return subprocess.run([str(x) for x in cmd], capture_output=True, text=True, **kw) + + +def last(p): + t = (p.stderr.strip().splitlines() or p.stdout.strip().splitlines() or ['?']) + return t[-1][:110] + + +def save_word(w, p): + with gzip.open(p, 'wt') as f: json.dump(w, f) + + +def through_chain(w, tag): + """returns [(stage, rejected, message)] and stops reading further stages' receipts as in the real chain.""" + d = MD / tag; d.mkdir(); wp = d / 'word.json.gz'; save_word(w, wp); out = [] + p = run([PY, '-I', '-B', AD / 'rchk/checkword_mut.py', wp, C / 'pricing']) + out.append(('relaxed independent checker (exact F2 replay, frames)', p.returncode != 0, last(p))) + p = run([PY, '-I', '-B', B / 'export_records.py', wp, d / 'records']); assert p.returncode == 0, p.stderr + p = run([PY, '-I', '-B', B / 'regression/stage_cohort.py', d / 'records', d / 'records', d / 'cin', d / 'cout']); assert p.returncode == 0, p.stderr + p = run([LEG, d / 'cin', d / 'cout', d / 'cout/legality.json']) + out.append(('official legality (frames, lifecycle, COPY ranks)', p.returncode != 0, last(p))) + p = run([COLH, d / 'cout/COHORT249-RECORDS.bin', d / 'cout/columns.json']) + out.append(('official five-stage columns_h (all columns, payload < 2^104)', p.returncode != 0, last(p))) + p = run([COLK, d / 'cout/COHORT249-RECORDS.bin', d / 'cout/columns_k5.json']) + out.append(('official eleven-stage columns_k', p.returncode != 0, last(p))) + # the emitter's gate: it needs legality.json and columns.json with a PASS status; a refused word leaves none + (d / 'bit').mkdir() + shutil.copy(WD / 'bit/GEOMETRY.json', d / 'bit/GEOMETRY.json') + p = run([PY, '-E', '-s', '-B', AD / 'emit.py', '--word', wp, '--local-check', d, '--out', d / 'bit', '--isometries', + PARENT / 'data/eleven/residual-isometries.json', '--hosting', HOST]) + out.append(('hosted emitter (needs PASS receipts of the two official checkers)', p.returncode != 0, last(p))) + return out, d + + +def record(name, what, stages, require_reject_at=None): + rej = [s for s in stages if s[1]] + assert rej, ('ACCEPTED', name, stages) + controls.append(dict(name=name, mutation=what, first_rejecting_stage=rej[0][0], message=rej[0][2], + stages=[dict(stage=s, rejected=r, message=m) for s, r, m in stages])) + print(' REJECTED %-26s first at: %s [%s]' % (name, rej[0][0], rej[0][2][:70])) + if require_reject_at: + assert any(s[0].startswith(require_reject_at) and s[1] for s in stages), (name, require_reject_at, stages) + + +# ---------------------------------------------------------------------------------------------------- word-level +w = copy.deepcopy(w0) +tot = w['ret'][0] +k = max(i for i, g in enumerate(w['A']) if g[1] == tot) +dropped = w['A'].pop(k) +st, _ = through_chain(w, 'm1') +record('M1 dropped gate', 'deleted phase-A gate %d (frame %d, destination %d = the first retained total, %d sources)' % (k, dropped[0], dropped[1], len(dropped[2])), + st, 'relaxed independent checker') +assert all(s[1] for s in st[:1] + st[2:3]), st # the checker and columns_h both refuse; the emitter cannot run + +w = copy.deepcopy(w0) +kb = len(w['B']) // 2 +old = w['B'][kb][0]; zero = w['start'][2 * V] +w['B'][kb][0] = zero +st, _ = through_chain(w, 'm2') +record('M2 wrong frame', 'phase-B gate %d moved from frame %d to the zero frame %d' % (kb, old, zero), st, 'relaxed independent checker') + +# M3: Fibonacci-growth block on helpers u, v (both at the zero frame at the start of phase A). Over F2 the six-gate cycle +# (u += v, v += u) x 3 is the identity, so every column is restored and every frame is untouched; the integer bound grows +# like Fibonacci numbers. Smallest block (in cycles) whose payload the probe reports >= 105 bits. +u, vv = 2 * V, 2 * V + 1 +assert w0['start'][u] == w0['start'][vv] == zero +for cycles in range(1, 40): + w = copy.deepcopy(w0) + blk = [] + for _ in range(cycles * 3): + blk += [[zero, u, [vv]], [zero, vv, [u]]] + w['A'] = blk + w['A'] + d = MD / ('m3probe%d' % cycles); d.mkdir(); wp = d / 'word.json.gz'; save_word(w, wp) + run([PY, '-I', '-B', B / 'export_records.py', wp, d / 'records']) + run([PY, '-I', '-B', B / 'regression/stage_cohort.py', d / 'records', d / 'records', d / 'cin', d / 'cout']) + p = run([PROBE, d / 'cout/COHORT249-RECORDS.bin', d / 'cout/probe.json']) + bits = json.loads((d / 'cout/probe.json').read_text())['payload_prefix_bits'] if p.returncode == 0 else None + if bits and bits >= 105: break +assert bits and bits >= 105, bits +st, _ = through_chain(w, 'm3') +assert not st[0][1] and not st[1][1], ('M3 must be a legal, F2-correct word that only the payload bound refuses', st) +record('M3 payload overflow', '%d cycles of the F2-neutral Fibonacci block (%d extra ADDs) at the start of phase A: payload %d bits >= 104 (shipped word: 95); ' + 'the relaxed checker and legality PASS' % (cycles, 12 * cycles, bits), st, 'official five-stage columns_h') +assert 'payloadfiniteprefix2^104' in st[2][2] or 'payload' in st[2][2], st[2] + +# ---------------------------------------------------------------------------------------------------- template-level +BIT = WD / 'bit' +tpl = json.loads(gzip.decompress((BIT / 'GLOBAL-TEMPLATE.json.gz').read_bytes())) +glob = json.loads((BIT / 'GLOBAL.json').read_text()) + + +def tamper(tag, edit, glob_edit=None, expect=None): + d = MD / tag; (d / 'bit').mkdir(parents=True) + for f in ('GEOMETRY.json',): shutil.copy(BIT / f, d / 'bit' / f) + t = copy.deepcopy(tpl); edit(t) + blob = json.dumps(t, separators=(',', ':'), sort_keys=True).encode() + (d / 'bit/GLOBAL-TEMPLATE.json.gz').write_bytes(gzip.compress(blob, mtime=0)) + g = copy.deepcopy(glob); g['template_sha256'] = hashlib.sha256(blob).hexdigest() # re-hashed: only semantics can catch it + if glob_edit: glob_edit(g) + (d / 'bit/GLOBAL.json').write_text(json.dumps(g, indent=2) + '\n') + p = run([PY, '-E', '-s', '-B', AD / 'audit.py', WD, WD / 'nine', d / 'bit']) + if expect: assert p.returncode != 0 and expect in p.stderr, (tag, 'refused for another reason', p.stderr[-600:]) + return [('independent hosted audit (audit.py)', p.returncode != 0, last(p) + ' :: ' + (expect or ''))], d + + +call0 = lambda t: t['calls'][0] +st, _ = tamper('m4a', lambda t: call0(t)['owners'].__setitem__(2 * V + 1, copy.deepcopy(call0(t)['owners'][2 * V])), + expect='len({(f,tuple(route)) for f,route in own})==2*V+R') +record('M4a cell collision', 'call 0: helper 1 given helper 0\'s physical family and hosted cells (two helpers on one owner AND one cell range; #442 disjoint-cells rule)', st) +fam0 = call0(tpl)['owners'][0][0] +st, _ = tamper('m4b', lambda t: call0(t)['owners'][2 * V].__setitem__(0, fam0), expect='pair[i]!=p') +record('M4b hosted on invoked pair', 'call 0 (pair %d): helper 0 re-homed to family %d, a data family the call itself acts on' % (call0(tpl)['pair'], fam0), st) +st, _ = tamper('m5', lambda t: None, lambda g: g.__setitem__('profile', dict(g['profile'], **{'56': g['profile']['56'] - 1, '55': 1})), expect='P==Counter(') +record('M5 profile claim', 'GLOBAL.json claims one rank-56 call as rank-55 (cheaper price) over the same template', st) + +# ---------------------------------------------------------------------------------------------------- assembly-level +d = MD / 'm6'; (d / 'bit').mkdir(parents=True) +for f in ('GLOBAL-TEMPLATE.json.gz', 'GEOMETRY.json', 'AUDIT.json'): shutil.copy(BIT / f, d / 'bit' / f) +g = copy.deepcopy(glob); g['v'] = 112 +(d / 'bit/GLOBAL.json').write_text(json.dumps(g, indent=2) + '\n') +p = run([PY, '-I', '-B', AD / 'assemble.py', d / 'bit', str(R), PARENT, d / 'RESULT.json', 'pr535', str(WORD.parent / 'complex-b.json')]) +assert p.returncode != 0 and 'rem == 0' in p.stderr, ('M6 refused for another reason', p.stderr[-600:]) +record('M6 v tied to complex v', 'GLOBAL.json v := 112 (the cited complex word\'s port count) with the 111-port bit audit', + [('assemble.py (rank-mass deficit must fit nine copy ranks at the bit v)', p.returncode != 0, last(p))]) +d = MD / 'm7'; (d / 'bit').mkdir(parents=True); shutil.copy(WD / 'bit/GEOMETRY.json', d / 'bit/GEOMETRY.json') +p = run([PY, '-E', '-s', '-B', C / 'eleven/emit.py', '--word', WORD, '--local-check', WD, '--out', d / 'bit', '--isometries', + PARENT / 'data/eleven/residual-isometries.json', '--hosting', HOST]) +assert p.returncode != 0 and "(8,120" in p.stderr, ('M7 refused for another reason', p.stderr[-600:]) +record('M7 stock #493 emitter', 'PR #493\'s unmodified emit.py on the 111-port word', [('stock emitter (hard-coded v == 120)', p.returncode != 0, last(p))]) + +OUT.write_text(json.dumps(dict(all_rejected=True, controls=controls), indent=1) + '\n') +print('PASS %d mutation controls, every one rejected' % len(controls)) diff --git a/research/e8-punctured111-star/code/price/price458.py b/research/e8-punctured111-star/code/price/price458.py new file mode 100644 index 0000000000..5e2a5f3c79 --- /dev/null +++ b/research/e8-punctured111-star/code/price/price458.py @@ -0,0 +1,71 @@ +"""Reprice under a stricter hosting than #442 / #472 / #493 / #503 use: every family copy hosts at most ONE helper per stage. +(PR #458's rule is per invocation = stage x replica; one helper per family copy per STAGE is stricter, so the c below is +a lower bound on the c of any #458-compliant hosting of the same word.) Whole width-8 residuals only (frame-zero +entrances, ENT = 0). Pattern = subset S of the window's stages with 8|S| <= rank; same objective as hosting_core.solve +(idle cost saved), same profile / mass identity / moment.certify as pricex.price. +Script written by the independent auditor of this package (star111 audit, 2026-10-11); only its CLI (a word instead of +V R hist cst) and the pricex import path were changed. +usage: price458.py WORD.json.gz s [s ...]""" +import gzip +import itertools +import json +import math +import os +import sys +from collections import Counter + +import numpy as np +from scipy.optimize import milp, LinearConstraint, Bounds + +sys.dont_write_bytecode = True +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import pricex as px # noqa: E402 + +MOM = px.MOM + + +def cost(r, m): return r * math.log(m / r) if r else 0.0 + + +def price458(V, R, hist, cst, s): + K, L, M, T, win, idle = px.stage_cfg(s) + C = 2 * V * T; need = R * T + var = [] + for i, (p, r, st) in win.items(): + for k in range(0, min(r // 8, len(st)) + 1): + for S in itertools.combinations(sorted(st), k): + var.append((i, S)) + n = len(var); c = np.zeros(n) + for j, (i, S) in enumerate(var): + r = win[i][1]; c[j] = -(cost(r, M) - cost(r - 8 * len(S), M)) + rows, lo, hi = [], [], [] + for i in win: + row = np.array([1.0 if vi == i else 0.0 for (vi, S) in var]); rows.append(row); lo.append(C); hi.append(C) + for st in range(L): + row = np.array([1.0 if st in S else 0.0 for (vi, S) in var]); rows.append(row); lo.append(need); hi.append(need) + res = milp(c, constraints=LinearConstraint(np.array(rows), lo, hi), integrality=np.ones(n), + bounds=Bounds(np.zeros(n), np.full(n, np.inf)), options=dict(time_limit=600, disp=False)) + if res.x is None: return dict(s=s, feasible=False, why=res.message) + x = [int(round(t)) for t in res.x] + paid = Counter({r: nn * L * T for r, nn in hist.items() if r}) + for i, r in idle: + if i not in win: paid[r] += C; continue + got = Counter() + for j, (vi, S) in enumerate(var): + if vi == i and x[j]: got[r - 8 * len(S)] += x[j] + assert sum(got.values()) == C + for rem, cnt in got.items(): + if rem: paid[rem] += cnt + LIVE = 2 * K * V * T + mass = sum(r * nn for r, nn in paid.items()); gap = M * LIVE - mass - (2 * K * V - L * cst) * T + assert gap == 0, gap + cert = MOM.certify(dict(paid), M, LIVE, True) + return dict(s=s, feasible=True, c=cert['lower'], profile=dict(sorted(paid.items())), + maxk=max(len(S) for j, (vi, S) in enumerate(var) if x[j])) + + +if __name__ == '__main__': + w = json.loads(gzip.open(sys.argv[1], 'rt').read()); d = px.word_hist(w); assert d['ENT'] == 0 + for s in map(int, sys.argv[2:]): + a = price458(d['V'], d['R'], d['hist'], d['cst'], s); b = px.price(d['V'], d['R'], d['hist'], d['cst'], s) + print(json.dumps(dict(V=d['V'], R=d['R'], s=s, c458=a.get('c'), feasible458=a['feasible'], c_stock=b.get('c'), maxk=a.get('maxk'))), flush=True) diff --git a/research/e8-punctured111-star/code/price/pricex.py b/research/e8-punctured111-star/code/price/pricex.py new file mode 100644 index 0000000000..b74f31bb00 --- /dev/null +++ b/research/e8-punctured111-star/code/price/pricex.py @@ -0,0 +1,137 @@ +"""pricex.py -- exact bit-c harness for E8 bit words of any port count V (R480 family; helpers enter at frame 0, width 8). + word -> local histogram (forward replay exactly as code/eleven/emit.py: MOVE ranks + copy ranks), V, R, cst + hist -> per-stage idle windows (eleven-stage: PR #445/#472 recipe generalised to K pairs; nine-stage: the reconstructed 9-window table that + reproduces R480 s=9 exactly) -> whole-residual hosting by the integer programme (hosting_core.solve, no banks) + -> profile = L*T*hist + idle remainders -> mass identity check -> exact rational certificate (moment.certify, bad envelope on). +usage: pricex.py WORD.json.gz [s ...] (s in 7 9 11 13; default 9 11 13 7) + pricex.py --hist 'R:{rank:count,..}:V:cst' s ... +Validation targets (run pricex.py --validate): R489 s=11 0.001565442026981656908596 ; R480 s=11 0.001574753352953808650916 ; R480 s=9 0.001587447106092548137108""" +import sys, json, gzip, importlib.util, time +from collections import Counter +from fractions import Fraction as Fr +sys.dont_write_bytecode = True +import os +PKG = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', '..', 'e8-bit-carrier-skeleton') # PR #493's package +sys.path.insert(0, PKG + '/code/hub/idle-axis-hosting') +import hosting_core as HC +_sp = importlib.util.spec_from_file_location('moment', PKG + '/code/pricing/moment.py'); MOM = importlib.util.module_from_spec(_sp); _sp.loader.exec_module(MOM) +H = 8 +# ---------------------------------------------------------------- word -> local histogram +def rank_dim(rows): + rows = [[Fr(x) for x in r] for r in rows]; r = 0 + for c in range(len(rows[0]) if rows else 0): + p = next((i for i in range(r, len(rows)) if rows[i][c] != 0), None) + if p is None: continue + rows[r], rows[p] = rows[p], rows[r] + for i in range(r + 1, len(rows)): + if rows[i][c] != 0: + f = rows[i][c] / rows[r][c]; rows[i] = [a - f * b for a, b in zip(rows[i], rows[r])] + r += 1 + return r +def word_hist(w): + """returns dict(V, R, hist, cst, copies, ENT)""" + V, R, h = w['v'], w['R'], w['h']; assert h == H + lines = w['lines']; assert len(lines) == V + dims = [rank_dim([lines[i] for i in idx]) for idx in w['frames']] + ZERO = [k for k, d in enumerate(dims) if d == 0]; assert len(ZERO) == 1; ZERO = ZERO[0] + cur = list(w['start']); hist = Counter(); copies = [] + def move(r, f): + if cur[r] != f: + d = dims[f] - dims[cur[r]]; assert d >= 0, ('non-nested', r, cur[r], f) + hist[d] += 1; cur[r] = f + def run(gates): + for f, d, srcs in gates: + for r in [d] + list(srcs): move(r, f) + run(w['D']); run(w['A']) + for q in w['ret']: + rk = dims[cur[q]] - dims[ZERO]; hist[rk] += 1; copies.append(rk) + run(w['B']); run(w['C']) + for r in range(len(cur)): move(r, w['final'][r]) + hist.pop(0, None) + n = len(cur) + ENT = sum(dims[w['start'][i]] for i in range(2 * V, n)) # = sum (H - width) of helper entrances (0 if all start at frame 0) + return dict(V=V, R=R, hist=dict(sorted(hist.items())), cst=sum(copies), copies=copies, ENT=ENT, mass=sum(r * c for r, c in hist.items())) +# ---------------------------------------------------------------- schedules / windows +def recipe(K): + out = [dict(kind='local', stage=0, pair=0)]; stage = 1 + for j in range(K): + out.append(dict(kind='local', stage=stage, pair=j)); stage += 1 + if j < K - 1: out.append(dict(kind='bridge', dst=j, src=j + 1)) + out.append(dict(kind='local', stage=stage, pair=j)); stage += 1 + if j < K - 1: out.append(dict(kind='bridge', dst=j, src=j + 1)) + for j in reversed(range(K - 1)): out.append(dict(kind='bridge', dst=j + 1, src=j)) + return out +def schedule(K): + levels = [-1] * K; sched = []; idx = 0 + for e in recipe(K): + if e['kind'] == 'local': + s, p = e['stage'], e['pair']; levels[p] = s; sched.append(dict(kind='local', stage=s, pair=p)); idles = [] + if s in range(1, 2 * K - 1, 2): j = (s + 1) // 2; levels[j] = s; idles.append((j, 7 * (s + 1))) + elif s in range(2, 2 * K, 2): j = s // 2; levels[j] = s; idles.append((j, 7)) + elif s == 2 * K: + for j in range(K): idles.append((j, 7 * (2 * K - levels[j]))); levels[j] = 2 * K + for j, r in idles: sched.append(dict(kind='idle', idx=idx, pair=j, rank=r)); idx += 1 + else: sched.append(e) + return sched +NINE = [(14, (0, 1)), (7, (2,)), (28, (0, 1, 2, 3)), (7, (4,)), (42, tuple(range(6))), (7, (6,)), (42, tuple(range(3, 9))), (28, tuple(range(5, 9))), (14, (7, 8))] +def stage_cfg(s): + """(K, L, M, T, windows{idx:(pair,rank,stages)}, idle_events[(idx,rank)]). s=9 uses the reconstructed nine-stage table (T = 32).""" + if s == 9: + win = {i: (i, r, frozenset(st)) for i, (r, st) in enumerate(NINE)} + return 4, 9, 64, 32, win, [(i, r) for i, (r, st) in enumerate(NINE)] + K = (s - 1) // 2; L = s; M = 7 * s + 1; T = 8 + sch = schedule(K) + win = {i: x for i, x in HC.windows(sch).items() if x[1] and x[2]} + idle = [(e['idx'], e['rank']) for e in sch if e['kind'] == 'idle' and e['rank']] + return K, L, M, T, win, idle +# ---------------------------------------------------------------- pricing +def price(V, R, hist, cst, s, check=True): + K, L, M, T, win, idle = stage_cfg(s) + avail = Counter({(st, H): R * T for st in range(L)}) + t0 = time.time() + try: + ys, zs, val = HC.solve(win, 2 * V * T, dict(avail), M, pad_value=0.0, group={H: 10 ** 9}) + except AssertionError as e: + return dict(s=s, feasible=False, why='hosting infeasible: %s' % str(e)[:60]) + used = Counter() + for (i, wd, st), n in zs.items(): used[st, wd] += n + if any(used[k] != a for k, a in avail.items()): return dict(s=s, feasible=False, why='unhosted residuals') + paid = Counter({r: n * L * T for r, n in hist.items() if r}) + hosted = Counter() # remainder per idle window copy + for i, (r) in idle: + if i not in win: # window with no residual access: full idle child paid + paid[r] += 2 * V * T + continue + got = Counter() + for (ii, pat), cnt in ys.items(): + if ii == i: got[r - sum(pat)] += cnt + assert sum(got.values()) == 2 * V * T, (i, got) + for rem, cnt in got.items(): + if rem: paid[rem] += cnt + LIVE = 2 * K * V * T + mass = sum(r * n for r, n in paid.items()) + gap = M * LIVE - mass - (2 * K * V - L * cst) * T + if check: assert gap == 0, ('mass identity violated', s, gap) + cert = MOM.certify(dict(paid), M, LIVE, True) + return dict(s=s, feasible=True, c=cert['lower'], cf=float(cert['lower']), profile=dict(sorted(paid.items())), gap=gap, R=R, V=V, cst=cst, sec=round(time.time() - t0, 1), M=M, T=T, K=K) +def main(): + a = sys.argv[1:] + if a and a[0] == '--validate': + ref = {('R489', 11): '0.001565442026981656908596', ('R480', 11): '0.001574753352953808650916', ('R480', 9): '0.001587447106092548137108'} + cases = {'R489': (489, {1: 1200, 2: 1440, 4: 180, 6: 120, 8: 18}), 'R480': (480, {1: 1200, 2: 1440, 4: 180, 6: 120, 8: 9})} + for (name, s), want in ref.items(): + R, h = cases[name]; r = price(120, R, h, 72, s) + print(name, 's=%d' % s, r['c'][:26], 'want', want, 'EXACT' if r['c'].startswith(want[:24]) else 'DIFF (%s)' % r['c'][:30], 'profile', r['profile'], flush=True) + return + if a and a[0] == '--hist': + R, rest0 = a[1].split(':', 1); h, V, cst = rest0.rsplit(':', 2); hist = {int(k): v for k, v in json.loads(h).items()}; rest = a[2:] + d = dict(V=int(V), R=int(R), hist=hist, cst=int(cst)) + else: + w = json.loads(gzip.open(a[0], 'rt').read()); d = word_hist(w); rest = a[1:] + print('word: V=%d R=%d cst=%d local hist %s ENT=%d mass %d' % (d['V'], d['R'], d['cst'], d['hist'], d['ENT'], d['mass'])) + assert d['ENT'] == 0 + assert d['mass'] == H * d['R'] + d['cst'] + 2 * d['V'] * (H - 1), 'local mass identity' + for s in (list(map(int, rest)) or [9, 11, 13, 7]): + r = price(d['V'], d['R'], d['hist'], d['cst'], s) + print(json.dumps(r), flush=True) +if __name__ == '__main__': main() diff --git a/research/e8-punctured111-star/code/punct_cfg.py b/research/e8-punctured111-star/code/punct_cfg.py new file mode 100644 index 0000000000..ee5ee42498 --- /dev/null +++ b/research/e8-punctured111-star/code/punct_cfg.py @@ -0,0 +1,79 @@ +# The one configuration of the punctured-word hosted chain: everything that was a hard-coded 120 / 72 / 9600 / 489 in +# PR #493's pipeline is derived here from the WORD (or, for scripts that never see the word, from the exported records). +# Prepared by DreamingOfClouds with Claude Opus 5.5 (Anthropic), via Claude Code. Apache-2.0. +"""Config of a bitE8/1 word for the hosted chain. + + v number of live ports (120 for PR #493 / #503, 111 for the punctured star unit) + R number of helper registers + ports_geom the live labels, as indices into the 120-line E8 system, in word order (absent for a full word) + totals map for each of the nine retained totals: the register, and the rank (frame dimension) it is COPYed from, + replayed here from the gates alone (72 = 9 x 8 for #493 / #503, 63 = 9 x 7 for the star unit) + +Nothing in here is read from the environment. `copy_ranks(w)` is an independent replay of the word's gates; the +emitter and the audit derive the same number from the exported records, and the chain asserts the two agree.""" +import gzip +import json +from fractions import Fraction as Fr +from pathlib import Path + + +def load(path): + p = Path(path) + return json.loads((gzip.decompress(p.read_bytes()) if p.suffix == '.gz' else p.read_bytes())) + + +def root120(pkg): + """The 120 labelled E8 root lines of PR #493's package (the order every geometry and port route is indexed by).""" + return load(Path(pkg) / 'data/bit/bitE8mut-r489.json.gz')['lines'] + + +def check_word(w, pkg): + """A full word (v = 120, no ports_geom) or a punctured one: live labels are a subsequence of the 120-line system.""" + v = w['v'] + if 'ports_geom' not in w: + assert v == 120 and len(w['lines']) == 120 + return + pg, r120 = w['ports_geom'], root120(pkg) + assert v < 120 and len(pg) == v == len(w['lines']) and list(pg) == sorted(set(pg)) and 0 <= pg[0] and pg[-1] < 120 + assert all(r120[p] == w['lines'][i] for i, p in enumerate(pg)), 'live lines are not the labelled 120-system lines' + if 'punctured' in w: + assert sorted(w['punctured'] + list(pg)) == list(range(120)) + # the frame-zero residual isometry of PR #493 (data/eleven/residual-isometries.json) is the coordinate frame 2e_0..2e_7, + # lines 0..7 of the 120-system; the stock validate_isometries(frames, lines) call needs them live at positions 0..7 + assert pg[:8] == list(range(8)), 'lines 0..7 must be live so the stock residual-isometry check applies unchanged' + + +def _rank(rows): + rows = [[Fr(x) for x in r] for r in rows] + r = 0 + for c in range(len(rows[0]) if rows else 0): + p = next((i for i in range(r, len(rows)) if rows[i][c] != 0), None) + if p is None: + continue + rows[r], rows[p] = rows[p], rows[r] + for i in range(r + 1, len(rows)): + if rows[i][c] != 0: + f = rows[i][c] / rows[r][c] + rows[i] = [a - f * b for a, b in zip(rows[i], rows[r])] + r += 1 + return r + + +def copy_ranks(w): + """Totals map: replay the gates D, A in order (a register stands at the frame of the last gate it was in) and read + the dimension of the frame each retained total is COPYed from. Frame 0 has dimension 0.""" + lines = w['lines'] + dims = [_rank([lines[i] for i in idx]) for idx in w['frames']] + cur = list(w['start']) + for gates in (w['D'], w['A']): + for f, d, srcs in gates: + for r in [d] + list(srcs): + cur[r] = f + return [dims[cur[q]] for q in w['ret']] + + +def config(w, pkg): + check_word(w, pkg) + cr = copy_ranks(w) + assert len(cr) == 9 and len(w['ret']) == 9 + return dict(v=w['v'], R=w['R'], copy_ranks=cr, cst=sum(cr), punctured=('ports_geom' in w)) diff --git a/research/e8-punctured111-star/code/word/gen_punct.py b/research/e8-punctured111-star/code/word/gen_punct.py new file mode 100644 index 0000000000..e290180974 --- /dev/null +++ b/research/e8-punctured111-star/code/word/gen_punct.py @@ -0,0 +1,305 @@ +"""Punctured quad-skeleton bit word (J3-A W1). gen.py of PR #493 with a removed-line set P (neither source nor target). +V1 = verbatim restriction of the R489/R480 skeleton (every helper register kept as a junk wire where its line is gone) + + totals as PSEUDO-TARGETS: for each y_j in P a fresh helper reads exactly what the (removed) target y_j would read + (qout x2 + qdeliv at D6[Hy], then its two needers at y_j-perp): x-part = J_{y_j}|L' = z_{y_j}|L', frame rank 7. +usage: python gen_punct.py OUT.json.gz P=0,11,38,... [PLAN=...]""" +import itertools, json, os, sys +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, HERE) +from pword import PWord, V, e8, solve_transform_lu, save # noqa +from geom import Geom # noqa + +GI = e8.GI +PKG = os.path.join(HERE, "..", "..", "..", "e8-bit-carrier-skeleton") # PR #493's package (plan, skeleton modules) + + +def load_plan(path): + d = json.load(open(path)) + return dict(match={tuple(f): k for f, k in d["match"]}, + ext={(q, tuple(H)): e for q, H, e in d["ext"]}, + plus={tuple(H): (l, r) for H, l, r in d["plus"]}) + + +def span_add(basis, v): + for pb, bv in basis: + if v >> pb & 1: + v ^= bv + if v: + basis.append((v.bit_length() - 1, v)); basis.sort(reverse=True) + return True + return False + + +def decoder_rows(cent, live): + rows = {} + for t in e8.bits(live): + want = (((1 << V) - 1) ^ sum((int(GI[t, b]) & 1) << b for b in range(V))) & live + found = None + for k in range(len(cent) + 1): + for C in itertools.combinations(range(len(cent)), k): + m = 0 + for c in C: + m ^= cent[c] + if m == want: + found = list(C); break + if found is not None: + break + assert found is not None, ("row not in span of totals", t) + rows[t] = found + return rows + + +def qnode_transform(inputs_x, outs, nlive_extra=0): + """register-level invertible transform on n = len(inputs) + junk wires: returns (nj, M rows, assign-ready outputs).""" + n0 = len(inputs_x) + # coordinates of each out in terms of the (independent) input x-parts + basis = [] + for i, v in enumerate(inputs_x): + combo = 1 << i + for pb, bv, bc in basis: + if v >> pb & 1: + v ^= bv; combo ^= bc + assert v, "live inputs dependent" + basis.append((v.bit_length() - 1, v, combo)); basis.sort(key=lambda z: -z[0]) + + def coords(w): + c = 0 + for pb, bv, bc in basis: + if w >> pb & 1: + w ^= bv; c ^= bc + assert w == 0, "output not in span of live inputs" + return c + A = [coords(o) for o in outs] + # independence of the A rows; add junk coordinates for dependent ones + bs = [] + rows = [] + nj = 0 + for a in A: + r = a + if not span_add(bs, r): + nj += 1 + r = a | (1 << (n0 + nj - 1)) + assert span_add(bs, r) + rows.append(r) + n = n0 + nj + # complete to a basis of F2^n with the remaining wires' unit vectors (live units first, then junk) + left = [] + for i in range(n): + if len(rows) + len(left) == n: + break + if span_add(bs, 1 << i): + left.append(1 << i) + assert len(rows) + len(left) == n + return nj, rows, left + + +def build(G, sol, P, verbose=True, totals="pseudo", zzleft=None): + zzleft = zzleft or {} + Pset = set(P) + live = ((1 << V) - 1) & ~sum(1 << s for s in Pset) + isl = lambda s: s not in Pset + W = PWord(live) + match, ext, plus = sol["match"], sol["ext"], sol["plus"] + mpairs, yy = {}, set() + for f in G.frames4: + a, b, c, d = f + mp = [((a, b), (c, d)), ((a, c), (b, d)), ((a, d), (b, c))][match[f]] + mpairs[f] = mp + yy.update(mp) + stats = dict(junk_carriers=0) + branch = {} + for f in G.frames4: + for s in f: + cs = list(G.cells_of[s]) + mcell = [c for c in cs if c in yy][0] + for c in cs: + if c == mcell: + branch[(s, c)] = s if isl(s) else None + else: + k = W.helper("carrier") + if isl(s): + W.gate(1 << s, k, [s]) + else: + stats["junk_carriers"] += 1 + branch[(s, c)] = k + delivery, keeper = {}, {} + cell_quads = {} + for q in range(len(G.quads)): + for c in G.qcells[q]: + cell_quads.setdefault(c, []).append(q) + owner_of_dcell = {G.dcell[H]: H for H in G.cells} + for c in G.cells: + F = G.cmask[c] + s, d = c + qa, qb = cell_quads[c] + if c in yy: + e1 = W.helper("empty"); e2 = W.helper("empty") + srcs = [branch[(x, c)] for x in (s, d) if branch[(x, c)] is not None] + if srcs: + W.gate(F, e1, srcs) + W.gate(F, e2, [e1]) + delivery[(c, qa)] = e1; delivery[(c, qb)] = e2 + keeper[(c, s)] = branch[(s, c)]; keeper[(c, d)] = branch[(d, c)] + else: + H = owner_of_dcell[c] + l, _rcv = plus[H] + l2 = d if l == s else s + e = W.helper("empty") + bl, bl2 = branch[(l, c)], branch[(l2, c)] + if isl(l): + W.gate(F, bl2, [bl]) # bl2 (junk or real carrier) <- + x_l + # else: bl junk (x-part 0): the original gate adds nothing to x + if isl(l) or isl(l2): + W.gate(F, e, [bl2]) + delivery[(c, qa)] = bl2 + delivery[(c, qb)] = e + keeper[(c, l)] = bl + # quads + qout = {} + for q in range(len(G.quads)): + U = G.quads[q] + cs = G.qcells[q] + wires = [delivery[(c, q)] for c in cs] + S = 0 + for c in cs: + S |= G.cmask[c] + owners = G.qowners[q] + outs = [] + for H in owners: + e = ext[(q, H)] + D = G.cmask[G.dcell[H]]; Qc = G.cmask[G.qcell[(q, H)]] + outs.append(S ^ D ^ Qc ^ ({"D": D, "Q": Qc, "0": 0}[e])) + full_inputs = [G.cmask[c] for c in cs] + ops, assign = solve_transform_lu(full_inputs, outs) + for a, b in ops: + W.gate(U, wires[a], [wires[b]]) + for k, H in enumerate(owners): + qout[(q, H)] = wires[assign[k]] + assert W.xpart(wires[assign[k]]) == outs[k] & live, ("quad out", q, H) + # (z,z) D6 + needer, dead = {}, [] + for H in G.cells: + if H in yy: + continue + q1, q2 = G.d6quads[H] + o1, o2 = qout[(q1, H)], qout[(q2, H)] + l, rcv = plus[H] + p = keeper[(G.dcell[H], l)] + F = G.D6[H] + other = H[0] if rcv == H[1] else H[1] + opt = zzleft.get(H, 0) + if opt & 1: + o1, o2 = o2, o1 + W.gate(F, o1, [o2]) + W.gate(F, p, [o1]) + if opt & 2: + W.gate(F, o2, [p, o1]) + needer[(H, rcv)] = p + needer[(H, other)] = o1 + dead.append(o2) + # Q needs + qneed = {} + for t in range(V): + f = G.phi[t] + m = [p for p in mpairs[f] if t in p][0] + Hy = tuple(sorted(m)) + acc = e8.HYP[t] & live + for q in G.d6quads[Hy]: + acc ^= W.xpart(qout[(q, Hy)]) + for e in f: + if e == t: + continue + H = tuple(sorted((t, e))) + if H == Hy: + continue + acc ^= W.xpart(needer[(H, t)]) + if Hy in qneed: + assert qneed[Hy] == acc, ("y-partners disagree", t) + qneed[Hy] = acc + # Q-nodes + qdeliv = {} + stats["qnode_junk"] = 0 + for bi, bmask in enumerate(G.blocks): + cb = G.comp[bi] + Hs = sorted(H for H in yy if G.cblk[H] == cb) + outs = [qneed[H] for H in Hs] + assert all((o & ~(bmask & live)) == 0 for o in outs) + wires = [] + for f in G.frames4: + if G.blk[f[0]] != bi: + continue + for c in mpairs[f]: + for s in c: + if isl(s): + wires.append(keeper[(c, s)]) + inputs = [W.xpart(w) for w in wires] + nj, rows, left = qnode_transform(inputs, outs) + stats["qnode_junk"] += nj + for j in range(nj): + wires.append(W.helper("qjunk")) + n = len(wires) + allrows = rows + left + ops, assign = solve_transform_lu([1 << i for i in range(n)], allrows) + for a, b in ops: + W.gate(bmask, wires[a], [wires[b]]) + for k, H in enumerate(Hs): + qdeliv[H] = wires[assign[k]] + assert W.xpart(wires[assign[k]]) == qneed[H], ("qdeliv", bi, H) + # totals + if totals == "pseudo": + for t in sorted(P): + f = G.phi[t] + m = [p for p in mpairs[f] if t in p][0] + Hy = tuple(sorted(m)) + tot = W.helper("total") + srcs = [qout[(q, Hy)] for q in G.d6quads[Hy]] + [qdeliv[Hy]] + W.gate(G.D6[Hy], tot, srcs) + nd = [] + for e in f: + if e == t: + continue + H = tuple(sorted((t, e))) + if H == Hy: + continue + nd.append(needer[(H, t)]) + W.gate(e8.HYP[t], tot, nd) + assert W.xpart(tot) == e8.HYP[t] & live, ("total", t) + W.ret.append(tot) + cvals = [W.xpart(q) for q in W.ret] + rows = decoder_rows(cvals, live) + # phase B + W.phase = "B" + for t in W.ports: + f = G.phi[t] + m = [p for p in mpairs[f] if t in p][0] + Hy = tuple(sorted(m)) + srcs = [qout[(q, Hy)] for q in G.d6quads[Hy]] + [qdeliv[Hy]] + W.gate(G.D6[Hy], V + t, srcs) + for t in W.ports: + f = G.phi[t] + srcs = [] + for e in f: + if e == t: + continue + H = tuple(sorted((t, e))) + if H in yy: + continue + srcs.append(needer[(H, t)]) + W.gate(e8.HYP[t], V + t, srcs) + word = W.build(rows) + word["stats"] = stats + return W, word + + +def main(): + args = dict(a.split("=", 1) for a in sys.argv[2:]) + P = [int(x) for x in args["P"].split(",")] + G = Geom() + sol = load_plan(args.get("PLAN", PKG + "/data/skeleton/plan-skB.json")) + W, word = build(G, sol, P) + print("R", word["R"], "v", word["v"], "gates D/A/B/C", len(word["D"]), len(word["A"]), len(word["B"]), len(word["C"]), word["stats"]) + save(word, sys.argv[1]) + + +if __name__ == "__main__": + main() diff --git a/research/e8-punctured111-star/code/word/pword.py b/research/e8-punctured111-star/code/word/pword.py new file mode 100644 index 0000000000..01bfe54747 --- /dev/null +++ b/research/e8-punctured111-star/code/word/pword.py @@ -0,0 +1,136 @@ +"""Word builder for punctured bit words (live port subset of the 120 E8 lines). +Internal ids follow wordkit.Word (x_s = s, y_t = V+t, helpers 2V+k, geometry line indices); build() renumbers to the +compact v = |live| layout of the bitE8/1 format. Frames are LIVE line sets (closed 120-line mask & live mask); their +rank is that of the live lines (checkers recompute it from the word's `lines`).""" +import os, sys +PARENT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "..", "e8-bit-carrier-skeleton") # PR #493's package +sys.path.insert(0, os.path.join(PARENT, "code", "skeleton")) +import e8 # noqa +from wordkit import solve_transform_lu, inverse_ops_nopivot, save # noqa + +V = e8.V + + +class PWord: + def __init__(self, live): + self.live = live + self.ports = e8.bits(live) + self.nh = 0 + self.cur, self.content, self.role = {}, {}, {} + for s in self.ports: + self.cur[s] = 1 << s + self.content[s] = 1 << s + for t in self.ports: + self.cur[V + t] = 0 + self.content[V + t] = 1 << (V + t) + self.A, self.B = [], [] + self.ret = [] + self.phase = "A" + self.cleanup = "literal" + + def helper(self, role=""): + r = 2 * V + self.nh + self.nh += 1 + self.cur[r] = 0 + self.content[r] = 1 << r + self.role[r] = role + return r + + def fr(self, F): + return (e8.closure(F)[0] if F else 0) & self.live + + def climb(self, r, F): + a = self.cur[r] + if a == F: + return + assert (a & ~F) == 0, ("frame not nested", r, e8.bits(a), e8.bits(F)) + self.cur[r] = F + + def gate(self, F, dst, srcs): + F = self.fr(F) + regs = [dst] + list(srcs) + assert len(set(regs)) == len(regs), regs + assert not (V <= dst < 2 * V and self.phase == "A"), "y written before the scatter" + for r in regs: + assert r in self.cur, ("absent register", r) + if V <= r < 2 * V: + assert self.phase == "B", "y used before the scatter" + self.climb(r, F) + for s in srcs: + self.content[dst] ^= self.content[s] + (self.A if self.phase == "A" else self.B).append((F, dst, list(srcs))) + + def xpart(self, r): + return self.content[r] & ((1 << V) - 1) + + def build(self, scatter_rows): + nh = self.nh + ports = self.ports + v = len(ports) + pidx = {s: i for i, s in enumerate(ports)} + + def rid(r): # internal -> compact id + if r < V: + return pidx[r] + if r < 2 * V: + return v + pidx[r - V] + return 2 * v + (r - 2 * V) + frames = {} + + def fid(mask): + if mask not in frames: + frames[mask] = len(frames) + return frames[mask] + FULL = self.live + fid(0) + fid(FULL) + A = [[fid(F), d, list(s)] for F, d, s in self.A] # internal ids + B = [[fid(F), d, list(s)] for F, d, s in self.B] + dv = {} + for h in range(nh): + dv[2 * V + h] = 1 << h + for s in ports: + dv[s] = 0 + dv[V + s] = 0 + for f, d, srcs in A: + for s in srcs: + dv[d] ^= dv[s] + cdirt = [dv[q] for q in self.ret] + for t in ports: + for k in scatter_rows[t]: + dv[V + t] ^= cdirt[k] + for f, d, srcs in B: + for s in srcs: + dv[d] ^= dv[s] + D = [] + for t in ports: + m = dv[V + t] + srcs = [] + while m: + b = m & -m + srcs.append(2 * V + b.bit_length() - 1) + m ^= b + if srcs: + D.append([fid(0), V + t, srcs]) + Cl = [] + for f, d, srcs in reversed(A + B): + if d < V or d >= 2 * V: + Cl.append([fid(FULL), d, srcs]) + fr = [None] * len(frames) + for mk, i in frames.items(): + fr[i] = [pidx[b] for b in e8.bits(mk)] + z = fid(0) + start = [fid(1 << s) for s in ports] + [z] * (v + nh) + final = [fid(FULL)] * v + [fid(e8.HYP[t] & self.live) for t in ports] + [fid(FULL)] * nh + # frames dict may have grown (fid for start/final): rebuild fr + fr = [None] * len(frames) + for mk, i in frames.items(): + fr[i] = [pidx[b] for b in e8.bits(mk)] + + def rg(gs): + return [[f, rid(d), [rid(s) for s in srcs]] for f, d, srcs in gs] + return dict(format="bitE8/1+mut", coords="ham", h=8, v=v, R=nh, lines=[list(e8.LINES[s]) for s in ports], + form="/2", frames=fr, start=start, final=final, ret=[rid(q) for q in self.ret], + scatter=[list(scatter_rows[t]) for t in ports], D=rg(D), A=rg(A), B=rg(B), C=rg(Cl), + punctured=[s for s in range(V) if not (self.live >> s) & 1], + ports_geom=ports) diff --git a/research/e8-punctured111-star/data/bitE8mut-v111-r489.json.gz b/research/e8-punctured111-star/data/bitE8mut-v111-r489.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..f69cc130937c90fedea71f69e9fc1720f41224bf GIT binary patch literal 41837 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 literal 0 HcmV?d00001 diff --git a/research/e8-punctured111-star/data/complex-b.json b/research/e8-punctured111-star/data/complex-b.json new file mode 100644 index 0000000000..3fec5ba6ce --- /dev/null +++ b/research/e8-punctured111-star/data/complex-b.json @@ -0,0 +1,19 @@ +{ + "note": "Cited complex coarse savings b (not rebuilt here). The value is read from the cited PR's own EXPECTED.json, vendored byte-identical under data/complex/pr535/ and pinned by git blob id; verify.py checks the value against the receipt. Only the outer assembly uses b; it carries no port count (the bit unit has v = 111, the cited complex word its own).", + "suppliers": [ + { + "name": "pr535", + "pr": 535, + "author": "icekylinx", + "commit": "25f9b7c2310d40c9efdfbe368a40ba6e74d3b0a2", + "receipt": "data/complex/pr535/EXPECTED.json", + "receipt_git_blob": "2c5a1a32628e2e93b29a54ecfb9ccfa5d84b9b17", + "field": "complex_lower", + "b": "1589991533606044174031/1000000000000000000000000", + "hosting": "post-center rank-four kernels on PR #531's complex word, whole-call hosting (research/post-center-rank4-global)", + "construction": "PR #535 composes its complex supplier with the live bit c of PR #510 (R480, nine stages) and reaches kappa 0.001584931111785787 (bit-bound)", + "status": "Conditional finite witness under the retained all-size compiler, common-label/mutable-basis, whole-call hosting, restored-work, precision/tape and analytic-transfer contracts. Fresh complex admission and finite invoice are separately hash-bound; no unconditional theorem is claimed.", + "role": "primary" + } + ] +} diff --git a/research/e8-punctured111-star/data/complex/pr535/EXPECTED.json b/research/e8-punctured111-star/data/complex/pr535/EXPECTED.json new file mode 100644 index 0000000000..2c5a1a3262 --- /dev/null +++ b/research/e8-punctured111-star/data/complex/pr535/EXPECTED.json @@ -0,0 +1,62 @@ +{ + "status": "PASS_ASYNC_COMPLEX_AND_CITED_BIT_EXACT_ASSEMBLY", + "kappa": "1584931111785787/1000000000000000000", + "kappa_decimal": "0.001584931111785787", + "complex_lower": "1589991533606044174031/1000000000000000000000000", + "complex_decimal": "0.001589991533606044174031", + "bit_lower": "396861776523137034277/250000000000000000000000", + "bit_decimal": "0.001587447106092548137108", + "binding_component": "bit", + "improvements": { + "PR531": { + "baseline": "1517760977820331/1000000000000000000", + "relative_gain": "67170133965456/1517760977820331", + "percent_display": "4.42560686" + }, + "rejected_POST208_checkpoint": { + "baseline": "771820390859371/500000000000000000", + "relative_gain": "41290330067045/1543640781718742", + "percent_display": "2.67486649" + }, + "PR533": { + "baseline": "381051987698311/250000000000000000", + "relative_gain": "60723160992543/1524207950793244", + "percent_display": "3.98391577" + } + }, + "global_template_content_sha256": "b9f4b60e56e5153c1fc89370e9ceb09f24b23e1c8ff56f2b47ef9d867aa04f2c", + "raw_word_content_sha256": "b9110e8ddb770a3874c2df2019e04a98c1a25684072f7f8e5772653f59f15db2", + "canonical_word_content_sha256": "7c84ac65c8b37173aa4293934f041915895e8ec30595844cc1534e2a19ae0d25", + "global_profile": { + "1": 158976, + "2": 52864, + "3": 15200, + "4": 20160, + "5": 4032, + "6": 4032, + "7": 352, + "8": 576, + "48": 1632 + }, + "local_profile": { + "1": 2208, + "2": 728, + "3": 184, + "4": 280, + "5": 56, + "6": 56, + "8": 8 + }, + "local_rank_mass": 6016, + "entrance_rank": 376, + "width_inventory": { + "5": 56, + "8": 152, + "9": 296 + }, + "global_prefix_bits": 225, + "live": 7168, + "physical": 7682, + "deficit": 2560, + "scope": "Conditional finite witness under the retained all-size compiler, common-label/mutable-basis, whole-call hosting, restored-work, precision/tape and analytic-transfer contracts. Fresh complex admission and finite invoice are separately hash-bound; no unconditional theorem is claimed." +} diff --git a/research/e8-punctured111-star/data/control-r480/EXPECTED-pr503.json b/research/e8-punctured111-star/data/control-r480/EXPECTED-pr503.json new file mode 100644 index 0000000000..e791affeea --- /dev/null +++ b/research/e8-punctured111-star/data/control-r480/EXPECTED-pr503.json @@ -0,0 +1,75 @@ +{ + "word": { + "R": 480, + "content_sha256": "8639e22008c24a5690dd250eba702fe5fca85ddd036dcbec20baba77b7e213ba", + "local_histogram": { + "1": 1200, + "2": 1440, + "4": 180, + "6": 120, + "8": 9 + }, + "five_stage_payload_bits": 102, + "eleven_stage_payload_bits": 102, + "source_destination_gates_A": 577 + }, + "control_first_register": { + "five_stage_payload_bits": 129, + "refused_by": "columns_h: payloadfiniteprefix2^104" + }, + "eleven": { + "bit_lower": "393688338238452162729/250000000000000000000000", + "bit_lower_decimal": "0.001574753352953808650916", + "profile": { + "1": 105600, + "2": 130560, + "4": 19680, + "6": 14400, + "7": 7680, + "8": 792, + "48": 1, + "56": 2742 + }, + "emitted_program_sha256": "7da5e3cb8daa231104f4e55f16c560fa56a59f10b8b2789e173923d9d2ca46fc", + "bit": { + "live": 9600, + "physical": 9609, + "maxchild": 56, + "rank_mass": 745536, + "hosted_residuals": 42240, + "prefix_bits": 5479, + "charged_prefix_bits": 5504, + "coefficient_bits": 5479, + "three_level_coefficient_bits": 16491, + "charged_three_level_coefficient_bits": 16512, + "row_coefficient": 19431, + "halving_depth": 3 + }, + "kappa": { + "pr484": { + "kappa": "662515748707749/500000000000000000", + "kappa_decimal": "0.001325031497415498", + "binding_component": "complex", + "complex_lower": "663394767670133/500000000000000000" + }, + "pr471": { + "kappa": "260706019542937/200000000000000000", + "kappa_decimal": "0.001303530097714685", + "binding_component": "complex", + "complex_lower": "1305231506267591/1000000000000000000" + }, + "pr458": { + "kappa": "323880830669511/250000000000000000", + "kappa_decimal": "0.001295523322678044", + "binding_component": "complex", + "complex_lower": "1297203880559577927527/1000000000000000000000000" + } + } + }, + "parent": { + "pr": 493, + "bit_lower": "391360506745414227149/250000000000000000000000", + "bit_lower_decimal": "0.001565442026981656908596", + "R": 489 + } +} \ No newline at end of file diff --git a/research/e8-punctured111-star/data/control-r480/bitE8mut-r480.json.gz b/research/e8-punctured111-star/data/control-r480/bitE8mut-r480.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..8e59c4b5380d522b1c1cb930029a71556b2fa65f GIT binary patch literal 49414 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 NLGjbq=6x`q{vQt@*LDB^ literal 0 HcmV?d00001 diff --git a/research/e8-punctured111-star/data/control-r480/hosting-r480.json b/research/e8-punctured111-star/data/control-r480/hosting-r480.json new file mode 100644 index 0000000000..e49abb321a --- /dev/null +++ b/research/e8-punctured111-star/data/control-r480/hosting-r480.json @@ -0,0 +1,240 @@ +{ + "bit": { + "patterns": [ + [ + 0, + [ + 8 + ], + 1920 + ], + [ + 1, + [], + 1920 + ], + [ + 2, + [ + 8, + 8, + 8 + ], + 1920 + ], + [ + 3, + [], + 1920 + ], + [ + 4, + [ + 8, + 8, + 8, + 8, + 8 + ], + 1920 + ], + [ + 5, + [], + 1920 + ], + [ + 6, + [], + 1918 + ], + [ + 6, + [ + 8 + ], + 1 + ], + [ + 6, + [ + 8, + 8, + 8, + 8, + 8, + 8, + 8 + ], + 1 + ], + [ + 7, + [], + 1920 + ], + [ + 8, + [], + 824 + ], + [ + 8, + [ + 8, + 8, + 8, + 8, + 8, + 8, + 8 + ], + 1096 + ], + [ + 9, + [ + 8, + 8, + 8, + 8, + 8 + ], + 1920 + ], + [ + 10, + [ + 8, + 8, + 8 + ], + 1920 + ], + [ + 11, + [ + 8 + ], + 1920 + ] + ], + "proxy_saving": 189827.32249716797, + "stages": [ + [ + 0, + 8, + 1, + 1920 + ], + [ + 2, + 8, + 0, + 3840 + ], + [ + 2, + 8, + 1, + 1913 + ], + [ + 2, + 8, + 2, + 7 + ], + [ + 4, + 8, + 2, + 3833 + ], + [ + 4, + 8, + 3, + 8 + ], + [ + 4, + 8, + 4, + 3840 + ], + [ + 4, + 8, + 5, + 1919 + ], + [ + 6, + 8, + 1, + 7 + ], + [ + 6, + 8, + 6, + 1 + ], + [ + 8, + 8, + 3, + 3832 + ], + [ + 8, + 8, + 9, + 1920 + ], + [ + 8, + 8, + 10, + 1920 + ], + [ + 9, + 8, + 5, + 1921 + ], + [ + 9, + 8, + 6, + 3839 + ], + [ + 9, + 8, + 8, + 3840 + ], + [ + 10, + 8, + 7, + 3840 + ], + [ + 10, + 8, + 9, + 1920 + ], + [ + 11, + 8, + 10, + 1920 + ] + ] + } +} diff --git a/research/e8-punctured111-star/data/hosting-v111-r489.json b/research/e8-punctured111-star/data/hosting-v111-r489.json new file mode 100644 index 0000000000..131d6cbc66 --- /dev/null +++ b/research/e8-punctured111-star/data/hosting-v111-r489.json @@ -0,0 +1,213 @@ +{ + "bit": { + "patterns": [ + [ + 0, + [ + 8 + ], + 1776 + ], + [ + 1, + [], + 1776 + ], + [ + 2, + [ + 8, + 8, + 8 + ], + 1776 + ], + [ + 3, + [], + 1776 + ], + [ + 4, + [ + 8, + 8, + 8, + 8, + 8 + ], + 1776 + ], + [ + 5, + [], + 1776 + ], + [ + 6, + [], + 1776 + ], + [ + 7, + [], + 1776 + ], + [ + 8, + [], + 195 + ], + [ + 8, + [ + 8, + 8, + 8, + 8, + 8, + 8, + 8 + ], + 1581 + ], + [ + 9, + [ + 8, + 8, + 8, + 8, + 8 + ], + 1776 + ], + [ + 10, + [], + 1 + ], + [ + 10, + [ + 8, + 8, + 8 + ], + 1775 + ], + [ + 11, + [ + 8 + ], + 1776 + ] + ], + "proxy_saving": 186085.65977536424, + "stages": [ + [ + 0, + 8, + 1, + 1776 + ], + [ + 2, + 8, + 0, + 3912 + ], + [ + 2, + 8, + 1, + 1416 + ], + [ + 4, + 8, + 1, + 720 + ], + [ + 4, + 8, + 2, + 3912 + ], + [ + 4, + 8, + 4, + 3912 + ], + [ + 4, + 8, + 5, + 336 + ], + [ + 8, + 8, + 3, + 3912 + ], + [ + 8, + 8, + 6, + 2520 + ], + [ + 8, + 8, + 9, + 2499 + ], + [ + 8, + 8, + 10, + 2136 + ], + [ + 9, + 8, + 5, + 3576 + ], + [ + 9, + 8, + 6, + 1392 + ], + [ + 9, + 8, + 8, + 3912 + ], + [ + 10, + 8, + 7, + 3912 + ], + [ + 10, + 8, + 9, + 1413 + ], + [ + 11, + 8, + 10, + 1776 + ] + ] + } +} diff --git a/research/e8-punctured111-star/verify.py b/research/e8-punctured111-star/verify.py new file mode 100644 index 0000000000..15574da2b4 --- /dev/null +++ b/research/e8-punctured111-star/verify.py @@ -0,0 +1,366 @@ +# Punctured star bit unit (v = 111 ports, nine rank-7 totals): the word through PR #493's hosted-audit chain. Verifier. +# Prepared by DreamingOfClouds with Claude Opus 5.5 (Anthropic), via Claude Code. Apache-2.0. +"""usage: python verify.py /absolute/new/output [--light] [--no-control] [--no-mutants] + +FULL runs PR #493's verify.sh unchanged (the parent: its word, checkers, pipeline, controls and compositions), then +everything below. --light skips the parent run. The parent's manifest is always checked. +Steps: 0 manifests; 1 the word rebuilt from the parent's plan; 2 the independent checker (v <= 120) and its mutants; +3 the official cohort checkers at (v, R); 4 exact Gram; 5 the eleven-stage whole-residual hosted pipeline (geometry, +hosting, emit, audit, assembly) with every constant derived from the word; 6 the pricing harness (pricex.py) must give +the same c to all 24 digits; 7 REGRESSION: the R480 word of PR #503 through the stock chain and through the adapted +chain, outputs compared byte for byte; 8 mutation controls; 9 composition with the cited complex suppliers (#535) and +the control that reproduces #535's published kappa; 10 summary against EXPECTED.json. +Needs Python 3.11+ with numpy, sympy, scipy, a C++17 compiler (CXX) and Boost headers (BOOST_INCLUDE), as the parent does.""" +import filecmp +import gzip +import hashlib +import json +import os +import subprocess +import sys +import time +from fractions import Fraction as F +from pathlib import Path + +HERE = Path(__file__).resolve().parent +PARENT = HERE.parent / 'e8-bit-carrier-skeleton' +args = [a for a in sys.argv[1:] if not a.startswith('--')] +LIGHT, NOCTL, NOMUT = '--light' in sys.argv, '--no-control' in sys.argv, '--no-mutants' in sys.argv +UNTIL = next((int(a.split('=')[1]) for a in sys.argv if a.startswith('--until=')), 99) # development aid: stop after step N +assert len(args) == 1, __doc__ +WD = Path(args[0]).resolve(); WD.mkdir(parents=True, exist_ok=True) +assert not str(WD).startswith(str(HERE.parent)), 'output dir must be outside research/' +PY = sys.executable; CXX = os.environ.get('CXX', 'c++'); BI = os.environ.get('BOOST_INCLUDE', '/usr/include') +ENV = dict(os.environ, PYTHONDONTWRITEBYTECODE='1') +C, B = PARENT / 'code', PARENT / 'code/bit'; CK = B / 'checkers'; HUB = C / 'hub' +WORD = HERE / 'data/bitE8mut-v111-r489.json.gz'; HOST = HERE / 'data/hosting-v111-r489.json' +CTL = HERE / 'data/control-r480'; CWORD, CHOST = CTL / 'bitE8mut-r480.json.gz', CTL / 'hosting-r480.json' +EXP = json.loads((HERE / 'EXPECTED.json').read_text()) +INC = ['-I%s' % (PARENT / 'portable-include'), '-I%s' % CK, '-I%s' % BI] +sys.dont_write_bytecode = True +sys.path.insert(0, str(HERE / 'code')) +import punct_cfg as PC # noqa: E402 +T0 = time.time() + + +def run(cmd, cwd=None, ok=True, quiet=True, env=None, out=None): + p = subprocess.run([str(x) for x in cmd], cwd=cwd, env=env or ENV, capture_output=True, text=True) + if out is not None: + Path(out).write_text(p.stdout) + if ok and p.returncode != 0: + sys.stderr.write(p.stdout[-3000:] + p.stderr[-3000:]); raise SystemExit('FAILED: %s' % ' '.join(map(str, cmd))) + if not quiet: + for line in p.stdout.rstrip().splitlines(): print(' ' + line) + return p + + +def sha(p): return hashlib.sha256(Path(p).read_bytes()).hexdigest() + + +def content_sha(w): return hashlib.sha256(json.dumps(w, sort_keys=True, separators=(',', ':')).encode()).hexdigest() + + +def rj(p): return json.loads(Path(p).read_text()) + + +def step(s): + n = s.split('.')[0] + if n.isdigit() and int(n) > UNTIL: raise SystemExit('stopped before step %s (--until)' % n) + print('== %s [%.0fs]' % (s, time.time() - T0), flush=True) + + +def stacking(bitdir, v): + """hosting disclosure: in how many of the 88 calls does one physical family host more than one helper of that call?""" + from collections import Counter + t = json.loads(gzip.decompress((Path(bitdir) / 'GLOBAL-TEMPLATE.json.gz').read_bytes())) + m = [max(Counter(o[0] for o in c['owners'][2 * v:]).values()) for c in t['calls']] + return dict(calls=len(m), calls_with_stacked_helpers=sum(1 for x in m if x > 1), max_helpers_on_one_family=max(m)) + + +def norm(p, drop=('seconds',)): + d = rj(p) + for k in drop: d.pop(k, None) + return d + + +# ------------------------------------------------------------------------------------------------------------------ +step('0. manifests: this package (MANIFEST.json) and the parent package (its MANIFEST.sha256, as its step 0)') +man = rj(HERE / 'MANIFEST.json') +for rel, h in man['files'].items(): assert sha(HERE / rel) == h, ('MISMATCH', rel) +listed = set(man['files']) | {'MANIFEST.json'} +present = {str(p.relative_to(HERE)) for p in HERE.rglob('*') if p.is_file() and '__pycache__' not in p.parts} +assert present == listed, ('unlisted or missing files', sorted(present ^ listed)) +assert sha(PARENT / 'MANIFEST.sha256') == man['parent_manifest_sha256'], 'parent MANIFEST.sha256 changed' +bad = 0 +for line in (PARENT / 'MANIFEST.sha256').read_text().splitlines(): + h, p = line.split(' ', 1) + if sha(PARENT / p) != h: print('PARENT MISMATCH', p); bad += 1 +assert not bad +print(' %d package files and %d parent files match their manifests' % (len(man['files']), len((PARENT / 'MANIFEST.sha256').read_text().splitlines()))) + +if not LIGHT: + step('P. parent: PR #493 verify.sh, unchanged') + run(['bash', PARENT / 'verify.sh', WD / 'parent'], env=dict(ENV, PYTHON=PY), quiet=False) + +# ------------------------------------------------------------------------------------------------------------------ +step('1. the word, rebuilt from the parent plan (code/word/gen_punct.py); the one configuration') +(WD / 'gen').mkdir(exist_ok=True) +P = EXP['word']['punctured'] +run([PY, '-I', '-B', HERE / 'code/word/gen_punct.py', WD / 'gen/word.json.gz', 'P=' + ','.join(map(str, P))], cwd=WD / 'gen') +assert (WD / 'gen/word.json.gz').read_bytes() == WORD.read_bytes(), 'rebuilt word differs from the shipped word' +w = PC.load(WORD); cfg = PC.config(w, PARENT) +assert content_sha(w) == EXP['word']['content_sha256'] and w['punctured'] == P +V, R, CST = cfg['v'], cfg['R'], cfg['cst'] +assert (V, R, CST) == (EXP['word']['v'], EXP['word']['R'], EXP['word']['cst']) and cfg['copy_ranks'] == EXP['word']['copy_ranks'] +print(' rebuilt word is byte-identical to data/bitE8mut-v111-r489.json.gz (content sha256 %s...)' % content_sha(w)[:16]) +print(' config: v = %d live ports (120 - %d punctured: %s), R = %d helpers, totals map: registers %s copy ranks %s => cst = %d' + % (V, len(P), P, R, w['ret'], cfg['copy_ranks'], CST)) +AD = WD / 'adapted' +run([PY, '-I', '-B', HERE / 'code/adapt.py', PARENT, WORD, AD], quiet=False) + +# ------------------------------------------------------------------------------------------------------------------ +SKIP2 = '--skip2' in sys.argv # development aid only (step 2 is the slow pure-Python checker) +iface = None +if not SKIP2: + step("2. PR #370's checker for v <= 120 ports: the stock-form file refuses source destinations; the relaxed copy passes; interface") + p = run([PY, '-I', '-B', AD / 'chk/checkword.py', WORD, C / 'pricing'], ok=False) + assert p.returncode != 0 and 'x register written' in p.stderr, 'stock-form checker did not refuse' + print(' stock-form checker refuses, as for the parent: x register written') + p = run([PY, '-I', '-B', B / 'independent/checkword.py', WORD, C / 'pricing'], ok=False) + assert p.returncode != 0 and 'AssertionError' in p.stderr and 'x register written' not in p.stderr + print(" the parent's own checker refuses the 111-line word at its v == 120 assertion (the one edit made in chk/checkword.py)") + run([PY, '-I', '-B', AD / 'chk/relax.py', AD / 'rchk']) + run([PY, '-I', '-B', AD / 'rchk/checkword_mut.py', WORD, C / 'pricing'], quiet=False) + run([PY, '-I', '-B', B / 'independent/interface.py', WORD, '--json', WD / 'interface.json'], quiet=False) + run([PY, '-I', '-B', B / 'independent/mutate_mut.py', WORD, AD / 'rchk/checkword_mut.py', C / 'pricing', + B / 'independent/interface.py'], quiet=False) + iface = rj(WD / 'interface.json') +else: + run([PY, '-I', '-B', AD / 'chk/relax.py', AD / 'rchk']) + +# ------------------------------------------------------------------------------------------------------------------ +step('3. official cohort checkers at (v, R): legality (NCOPY = 9), five-stage columns, eleven-stage columns (KPAIRS = 5)') +BIN = WD / 'bin'; BIN.mkdir(exist_ok=True) +cc = [CXX, '-O2', '-std=c++17', '-DHDIM=8'] + + +def build_checkers(v, r): + """compile the official checkers for (v, r); the compiles run concurrently""" + # payload probe = columns_h.cpp minus its payload assertion; used ONLY to report a refused control's bill + probe = (CK / 'columns_h.cpp').read_text() + anchor = 'need(payload<(cpp_int(1)<<104),"payloadfiniteprefix2^104");'; assert probe.count(anchor) == 1 + (BIN / 'columns_probe.cpp').write_text(probe.replace(anchor, '')) + flags = ['-DNCOPY=9', '-DVPORTS=%d' % v, '-DRHELP=%d' % r] + jobs = [cc + ['-DNCOPY=9'] + INC + ['-o', BIN / 'legality', CK / 'legality_h.cpp'], + cc + INC + ['-o', BIN / 'legality_ncopy_h', CK / 'legality_h.cpp'], + cc + flags + INC + ['-o', BIN / ('columns_probe_v%d_r%d' % (v, r)), BIN / 'columns_probe.cpp']] + for name, extra, src in (('columns_h', [], 'columns_h.cpp'), ('columns_k5', ['-DKPAIRS=5'], 'columns_k.cpp'), + ('columns_k2', ['-DKPAIRS=2'], 'columns_k.cpp')): + jobs.append(cc + flags + extra + INC + ['-o', BIN / ('%s_v%d_r%d' % (name, v, r)), CK / src]) + procs = [(j, subprocess.Popen([str(x) for x in j], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=ENV)) for j in jobs] + for j, p in procs: + o, e = p.communicate() + if p.returncode != 0: + sys.stderr.write(o[-3000:] + e[-3000:]); raise SystemExit('FAILED: %s' % ' '.join(map(str, j))) + + +def cohort(word, d): + run([PY, '-I', '-B', B / 'export_records.py', word, d / 'records']) + run([PY, '-I', '-B', B / 'regression/stage_cohort.py', d / 'records', d / 'records', d / 'cin', d / 'cout']) + + +def columns(v, r, d, kinds=('columns_h', 'columns_k5', 'columns_k2')): + co = d / 'cout' + run([BIN / 'legality', d / 'cin', co, co / 'legality.json']) + for k in kinds: + out = {'columns_h': 'columns.json', 'columns_k5': 'columns_k5.json', 'columns_k2': 'columns_k2.json'}[k] + run([BIN / ('%s_v%d_r%d' % (k, v, r)), co / 'COHORT249-RECORDS.bin', co / out]) + + +build_checkers(V, R) +cohort(WORD, WD); columns(V, R, WD) +co = WD / 'cout' +lg, cl, k5, k2 = rj(co / 'legality.json'), rj(co / 'columns.json'), rj(co / 'columns_k5.json'), rj(co / 'columns_k2.json') +assert lg['status'] == 'PASS_INDEPENDENT_COHORT_LEGALITY_SOURCE_OWNERS_AND_LIFECYCLE' and lg['copies'] == lg['erasures'] == 9 +assert cl['status'] == 'PASS_NEW_COHORT249_ALL_FIVE_STAGE_FORMAL_COLUMNS_AND_PREFIX_BILL' and cl['formal_columns_checked'] == 4 * V + R +assert k5['status'] == 'PASS_NEW_COHORT249_ALL_BRIDGED_STAGE_FORMAL_COLUMNS_AND_PREFIX_BILL' and k5['formal_columns_checked'] == 10 * V + R +assert len(k5['stages']) == 11 and k5['bridge_additions'] == 24 * V and all(x['rejected'] for x in k5['negative_controls']) +assert len(k5['negative_controls']) == 24 and dict(map(tuple, k5['local_raw_H'])) == dict(map(tuple, cl['local_raw_H'])) +assert {k: v for k, v in cl.items() if k != 'seconds'} == {k: v for k, v in k2.items() if k != 'seconds'} +assert cl['payload_prefix_bits'] < 104 and k5['payload_prefix_bits'] < 104 +print(' legality PASS: %d records, %d common-frame ADDs, rank mass %d, 9 COPY/ERASE (ranks %s)' + % (lg['records'], lg['common_frame_ADDs'], lg['rank_mass'], cfg['copy_ranks'])) +print(' five-stage formal columns PASS: %d columns over F2; prefix payload %d bits < 104 (eleven-stage: %d bits)' + % (cl['formal_columns_checked'], cl['payload_prefix_bits'], k5['payload_prefix_bits'])) +print(' eleven-stage formal columns PASS: %d columns, %d bridge additions, 24/24 omitted-bridge controls rejected;' + ' columns_k at KPAIRS = 2 reproduces the five-stage receipt' % (k5['formal_columns_checked'], k5['bridge_additions'])) +BK = lambda k, v=V, r=R: BIN / ('%s_v%d_r%d' % (k, v, r)) +run([PY, '-I', '-B', AD / 'mutate_records.py', WD / 'records', WD / 'cin', co, BIN / 'legality', BK('columns_k5'), + BIN / 'legality_ncopy_h'], quiet=False) +run([PY, '-I', '-B', B / 'mutate_records_mut.py', WORD, WD / 'records', WD / 'cin', co, BIN / 'legality', + BK('columns_h'), BK('columns_k5')], quiet=False) + +# ------------------------------------------------------------------------------------------------------------------ +step('4. exact nondegeneracy (E8 Gram) and prime witnesses on every frame') +run([PY, '-I', '-B', CK / 'gram_frames.py', WD / 'records', '--gram', 'e8ham', '--h', '8', '--positive'], quiet=False) + +# ------------------------------------------------------------------------------------------------------------------ +step('5. eleven-stage whole-residual hosting (PR #445 geometry, PR #472 emitter and audit), exact price, assembly') +NINE_G, ELEVEN_G = 'nine_geometry.py', 'eleven_geometry.py' + + +def hosted(word, d, hosting_out, suppliers, ad=AD, geom_word=True, outname='bit'): + """geometry (live labels), hosting nomination, emit, audit, assembly of ONE word with the adapted scripts.""" + (d / 'nine').mkdir(exist_ok=True); (d / outname).mkdir(exist_ok=True) + gw = ['--word', word] if geom_word else [] + run([PY, '-I', '-B', ad / NINE_G, '--source', PARENT, '--out', d / 'nine'] + gw) + run([PY, '-I', '-B', ad / ELEVEN_G, '--source', PARENT, '--out', d / outname] + gw) + run([PY, '-I', '-B', ad / 'host_select.py', word, hosting_out]) + return d / outname + + +def emit_audit_assemble(word, d, bitdir, hosting, ad, suppl, nine=None): + run([PY, '-E', '-s', '-B', ad / 'emit.py', '--word', word, '--local-check', d, '--out', bitdir, + '--isometries', PARENT / 'data/eleven/residual-isometries.json', '--hosting', hosting]) + run([PY, '-E', '-s', '-B', ad / 'audit.py', d, nine or d / 'nine', bitdir]) + Rr = rj(bitdir / 'GLOBAL.json')['R'] + run([PY, '-I', '-B', ad / 'assemble.py', bitdir, str(Rr), PARENT, d / 'RESULT.json', suppl[0]] + suppl[1:]) + + +bit = hosted(WORD, WD, WD / 'hosting-rediscovered.json', None) +assert rj(WD / 'hosting-rediscovered.json') == rj(HOST), 'hosting differs from the frozen selection' +print(" PR #442 hosting programme (host_select.py, v from the word) rediscovers data/hosting-v111-r489.json exactly") +emit_audit_assemble(WORD, WD, bit, HOST, AD, ['pr535', str(HERE / 'data/complex-b.json')]) +g, a, r = rj(bit / 'GLOBAL.json'), rj(bit / 'AUDIT.json'), rj(WD / 'RESULT.json') +assert a['status'] == 'PASS_INDEPENDENT_ELEVEN_STAGE_HOSTING_AUDIT' and g['R'] == R and g['v'] == V +assert g['hosted_residuals'] == 88 * R and g['live'] == 2 * 5 * V * 8 and g['deficit'] == 8 * (10 * V - 11 * CST) +print(' template: 88 calls, %d whole helper residuals hosted on idle data families, %d unbanked scalar columns restored;' + ' independent audit PASS: %d renamed instructions, %d template mutations rejected, local audit %s' + % (g['hosted_residuals'], g['independent_unbanked_scalar_columns'], a['emitted_events'], len(a['mutations_rejected']), + a['local_audit']['status'])) +print(' bit c (eleven stages, 10^-24 grid) = %s = %s; live %d, rank-mass deficit %d' % (r['bit_lower'], r['bit_lower_decimal'], g['live'], g['deficit'])) + +# ------------------------------------------------------------------------------------------------------------------ +step("6. pricing harness (code/price/pricex.py: local histogram replay + the same hosting programme + moment.certify) must agree to all digits") +p = run([PY, '-B', HERE / 'code/price/pricex.py', WORD, '11']) +line = [x for x in p.stdout.splitlines() if x.startswith('{')][0]; px = json.loads(line) +assert px['feasible'] and F(px['c']) == F(r['bit_lower_decimal']) and px['profile'] == r['bit_profile'] +assert px['c'].startswith(r['bit_lower_decimal'][:26]) +print(' pricex c = %s == hosted-audit c = %s (24 digits; same profile)' % (px['c'], r['bit_lower_decimal'])) +# robustness: the hosting rule used (PR #442's pooled whole-residual programme, as in #472 / #493 / #503 / #510) lets several helpers of one +# invocation share a family at disjoint cells. Under the STRICTER rule 'one helper per family copy per stage' (PR #458's rule is per invocation, +# so this is a lower bound for any #458-compliant hosting) the c is lower; kappa is complex-bound and unchanged (step 9) +stk = stacking(bit, V) +print(' hosting rule: #442/#472/#493 pooled whole-residual programme; %d of %d calls host several helpers of that call on one family ' + '(at most %d on one family) -- inherited, as in PR #503 (see README, Scope)' % (stk['calls_with_stacked_helpers'], stk['calls'], stk['max_helpers_on_one_family'])) +p458 = json.loads(run([PY, '-B', HERE / 'code/price/price458.py', WORD, '11']).stdout.strip().splitlines()[-1]) +assert p458['feasible458'] and F(p458['c_stock']) == F(r['bit_lower_decimal']) +print(' stricter hosting (one helper per family copy per stage): c >= %s (%+.4f%% vs the shipped c); kappa unchanged (complex-bound)' + % (p458['c458'], 100 * float(F(p458['c458']) / F(r['bit_lower_decimal']) - 1))) +# the harness's other stages: infeasible by the exact hosting wall at s = 9 and 7, feasible at s = 13 +px_all = {s: json.loads([x for x in run([PY, '-B', HERE / 'code/price/pricex.py', WORD, str(s)]).stdout.splitlines() if x.startswith('{')][0]) for s in (9, 13)} +print(' other stages (information): s = 9 feasible: %s (%s); s = 13 c = %s' % (px_all[9]['feasible'], px_all[9].get('why', ''), px_all[13].get('c'))) + +# ------------------------------------------------------------------------------------------------------------------ +if not NOCTL: + step("7. REGRESSION: PR #503's R480 word (v = 120, nine rank-8 totals) through the stock chain and the adapted chain") + CD = WD / 'ctl'; CD.mkdir(exist_ok=True) + cw = PC.load(CWORD); ccfg = PC.config(cw, PARENT) + assert content_sha(cw) == EXP['control']['word_content_sha256'] and (ccfg['v'], ccfg['R'], ccfg['cst']) == (120, 480, 72) + build_checkers(120, 480) + cohort(CWORD, CD); columns(120, 480, CD, kinds=('columns_h', 'columns_k5')) + assert rj(CD / 'cout/columns.json')['payload_prefix_bits'] == EXP['control']['five_stage_payload_bits'] + # (a) the checkers: the adapted copies print exactly what the parent's print on the R480 word + run([PY, '-I', '-B', B / 'independent/relax.py', CD / 'rchk0']) + s0 = run([PY, '-I', '-B', CD / 'rchk0/checkword_mut.py', CWORD, C / 'pricing']).stdout + ADC = CD / 'adapted-for-control' # local_audit.py bakes in the word it audits, so the control has its own adapted copy + run([PY, '-I', '-B', HERE / 'code/adapt.py', PARENT, CWORD, ADC]) + run([PY, '-I', '-B', ADC / 'chk/relax.py', ADC / 'rchk']) + s1 = run([PY, '-I', '-B', ADC / 'rchk/checkword_mut.py', CWORD, C / 'pricing']).stdout + assert s0 == s1, 'relaxed checker output differs on the R480 word' + m0 = run([PY, '-I', '-B', B / 'mutate_records.py', CD / 'records', CD / 'cin', CD / 'cout', BIN / 'legality', + BK('columns_k5', 120, 480), BIN / 'legality_ncopy_h']).stdout + m1 = run([PY, '-I', '-B', ADC / 'mutate_records.py', CD / 'records', CD / 'cin', CD / 'cout', BIN / 'legality', + BK('columns_k5', 120, 480), BIN / 'legality_ncopy_h']).stdout + assert m0 == m1 and 'PASS all 6' in m0, 'record-mutant output differs on the R480 word' + print(' (a) relaxed checker and the 6 record mutants: output identical to the parent files on the R480 word') + # (b) the hosted chain. STOCK = parent emit.py / assemble.py unchanged + PR #503's audit.py / local_audit.py (its adapt.py) + assert sha(HERE / 'code/adapt503.py') == '04ec15262595e502796efc1f100f1efd4f28585add48377af1bda702a12c0e40', "adapt503.py is not PR #503's adapt.py" + AD503 = CD / 'ad503' + run([PY, '-I', '-B', HERE / 'code/adapt503.py', PARENT, CWORD, AD503], quiet=False) + sd, cd_ = CD / 'stock', CD / 'adapted' + for x in (sd, cd_): (x / 'nine').mkdir(parents=True, exist_ok=True); (x / 'bit').mkdir(exist_ok=True) + run([PY, '-I', '-B', C / 'nine/geometry.py', '--source', PARENT, '--out', sd / 'nine']) + run([PY, '-I', '-B', HUB / 'r740-hosted-eleven/code/geometry.py', '--source', PARENT, '--out', sd / 'bit']) + run([PY, '-I', '-B', PARENT / 'discovery/host_select.py', CWORD, sd / 'hosting.json']) + run([PY, '-I', '-B', ADC / NINE_G, '--source', PARENT, '--out', cd_ / 'nine']) + run([PY, '-I', '-B', ADC / ELEVEN_G, '--source', PARENT, '--out', cd_ / 'bit']) + run([PY, '-I', '-B', ADC / 'host_select.py', CWORD, cd_ / 'hosting.json']) + assert norm(sd / 'nine/GEOMETRY.json') == norm(cd_ / 'nine/GEOMETRY.json') and norm(sd / 'bit/GEOMETRY.json') == norm(cd_ / 'bit/GEOMETRY.json') + assert rj(sd / 'hosting.json') == rj(cd_ / 'hosting.json') == rj(CHOST) + # both pipelines read the SAME geometry files (GEOMETRY.json stores its own run time, which is hashed into the template) + for sub in ('nine', 'bit'): (cd_ / sub / 'GEOMETRY.json').write_bytes((sd / sub / 'GEOMETRY.json').read_bytes()) + print(' (b) geometries (nine and eleven stage) and the hosting nomination: equal to the parent scripts modulo run time') + suppl = ['pr484,pr471,pr458'] + for tag, base_, ad_, emit_ in (('stock', sd, AD503, C / 'eleven/emit.py'), ('adapted', cd_, ADC, ADC / 'emit.py')): + run([PY, '-E', '-s', '-B', emit_, '--word', CWORD, '--local-check', CD, '--out', base_ / 'bit', '--isometries', + PARENT / 'data/eleven/residual-isometries.json', '--hosting', CHOST]) + run([PY, '-E', '-s', '-B', (ad_ / 'eleven/audit.py') if tag == 'stock' else (ADC / 'audit.py'), CD, base_ / 'nine', base_ / 'bit']) + run([PY, '-I', '-B', (C / 'eleven/assemble.py') if tag == 'stock' else (ADC / 'assemble.py'), base_ / 'bit', '480', PARENT, + base_ / 'RESULT.json', 'pr484,pr471,pr458']) + same = {} + gs_, ga_ = sd / 'bit/GLOBAL.json', cd_ / 'bit/GLOBAL.json' + same['GLOBAL.json (all fields but run time)'] = norm(gs_) == norm(ga_) + same['GLOBAL-TEMPLATE.json.gz'] = filecmp.cmp(sd / 'bit/GLOBAL-TEMPLATE.json.gz', cd_ / 'bit/GLOBAL-TEMPLATE.json.gz', shallow=False) + same['AUDIT.json'] = filecmp.cmp(sd / 'bit/AUDIT.json', cd_ / 'bit/AUDIT.json', shallow=False) + same['RESULT.json'] = filecmp.cmp(sd / 'RESULT.json', cd_ / 'RESULT.json', shallow=False) + assert all(same.values()), same + rc = rj(cd_ / 'RESULT.json'); ec = EXP['control'] + assert rc['bit_lower'] == ec['bit_lower'] and rc['bit_profile'] == ec['profile'] + assert rj(cd_ / 'bit/GLOBAL.json')['emitted_program_sha256'] == ec['emitted_program_sha256'] + for name, x in ec['kappa'].items(): assert rc['kappa'][name]['kappa'] == x + pc_ = json.loads([x for x in run([PY, '-B', HERE / 'code/price/pricex.py', CWORD, '11']).stdout.splitlines() if x.startswith('{')][0]) + assert F(pc_['c']) == F(rc['bit_lower_decimal']) + stk_c = stacking(cd_ / 'bit', 120) + assert stk_c == ec['stacking'], (stk_c, ec['stacking']) + print(' (c) emit/audit/assemble: GLOBAL.json, GLOBAL-TEMPLATE.json.gz, AUDIT.json and RESULT.json of the adapted chain are' + ' BYTE-IDENTICAL to the stock chain on the R480 word (%s)' % ', '.join(same)) + print(' R480 c = %s = %s, emitted-program sha256 %s... == PR #503 EXPECTED; kappa with #484/#471/#458 unchanged; pricex agrees' + % (rc['bit_lower'], rc['bit_lower_decimal'], ec['emitted_program_sha256'][:16])) + print(' hosting disclosure for the control: %d of %d calls stack several helpers on one family (max %d) -- the same inherited rule' + % (stk_c['calls_with_stacked_helpers'], stk_c['calls'], stk_c['max_helpers_on_one_family'])) + +# ------------------------------------------------------------------------------------------------------------------ +if not NOMUT: + step('8. mutation controls on the star word (code/mutants.py): every one must be rejected') + run([PY, '-I', '-B', HERE / 'code/mutants.py', WORD, WD, AD, PARENT, HOST, WD / 'mutants.json'], quiet=False) + mut = rj(WD / 'mutants.json'); assert mut['all_rejected'] and len(mut['controls']) >= 4 + +# ------------------------------------------------------------------------------------------------------------------ +step('9. composition with the cited complex supplier (#535): kappa; control = #535 reproduced from #510 bit c and the same b') +run([PY, '-I', '-B', HERE / 'code/compose_control.py', PARENT, WD / 'RESULT.json', HERE / 'data/complex/pr535/EXPECTED.json', + WD / 'compose.json'], quiet=False) +comp = rj(WD / 'compose.json') + +# ------------------------------------------------------------------------------------------------------------------ +step('10. summary against EXPECTED.json') +e = EXP +assert {str(k): v for k, v in cl['local_raw_H']} == e['word']['local_histogram'] +assert cl['payload_prefix_bits'] == e['word']['five_stage_payload_bits'] and k5['payload_prefix_bits'] == e['word']['eleven_stage_payload_bits'] +assert iface['source_destination_gates_A'] == e['word']['source_destination_gates_A'] +assert r['bit_lower'] == e['eleven']['bit_lower'] and r['bit_lower_decimal'] == e['eleven']['bit_lower_decimal'] and r['bit_profile'] == e['eleven']['profile'] +assert g['emitted_program_sha256'] == e['eleven']['emitted_program_sha256'] +assert stk == e['hosting']['stacking'] and p458['c458'] == e['hosting']['c_strict_per_stage'] and F(p458['c458']) < F(r['bit_lower']) +for k in ('prefix_bits', 'charged_prefix_bits', 'three_level_coefficient_bits', 'charged_three_level_coefficient_bits', 'row_coefficient', 'hosted_residuals', 'live', 'physical', 'rank_mass'): + assert r['bit'][k] == e['eleven']['bit'][k], k +assert r['kappa']['pr535']['kappa'] == e['kappa']['pr535']['kappa'] and r['kappa']['pr535']['binding_component'] == e['kappa']['pr535']['binding_component'] +assert comp['control_reproduces_pr535_kappa'] and comp['kappa_star_vs_pr535'] == e['kappa']['pr535']['kappa'] +c = F(r['bit_lower']) +print('PASS bit c (v = %d, R = %d, cst = %d; PR #472 eleven-stage whole-residual pipeline, 10^-24 grid) = %s = %s' % (V, R, CST, r['bit_lower'], r['bit_lower_decimal'])) +print(' %+.4f%% over PR #503 (R = 480, v = 120, c = %s) and %+.4f%% over the live bit c of PR #510 (%s)' + % (100 * float(c / F(e['control']['bit_lower']) - 1), e['control']['bit_lower_decimal'], + 100 * float(c / F(e['live_bit']['bit_lower']) - 1), e['live_bit']['bit_lower_decimal'])) +k = r['kappa']['pr535'] +print(" with PR #535's complex b = %s: kappa = %s = %s (%s-bound; #535 published %s from the live bit c)" + % (k['complex_lower'], k['kappa'], k['kappa_decimal'], k['binding_component'], comp['pr535_published_kappa'])) +print('PASS %s [%.0fs]' % ('LIGHT' if LIGHT else 'FULL', time.time() - T0))