diff --git a/research/p104-one-frame-indicator-word/PROOF.md b/research/p104-one-frame-indicator-word/PROOF.md new file mode 100644 index 0000000000..4638cf710f --- /dev/null +++ b/research/p104-one-frame-indicator-word/PROOF.md @@ -0,0 +1,146 @@ +# One frame-indicator row as a length-4 label word + +No new κ. The selected record stays `781451/250000000`. This note hosts one +frame-indicator row and charges the monomials of that word. It does not host +the other twelve rows and it does not compare a histogram with the selected +profile. + +## 1. Hypotheses + +Ports and the odd graph are those of PR #6933. Two distinct ports are adjacent +when their `F_2` dot product is 1. Frame 0 of the #7014 witness is the ordered +8-tuple + +```text +11, 21, 38, 56, 140, 146, 161, 191. +``` + +A **letter** is a root-line functional `λ_i(j) = ( + 1) mod 2` on the +120 root lines. A **word** is a product of letters. The product is evaluated in +`F_2`, so the value is 1 only when every factor is 1. The word **hosts** the +frame-indicator row when, on all 120 lines, the product equals 1 on these eight +lines and equals 0 on the other 112. Restriction to the 104 P104 lines is then +the indicator of this frame. + +Labels use the inherited simple-root chart. If `B` is the doubled simple-root +basis of `e8model.py`, the label of a root `r` is + +```text +u(r) = (coordinates of r in B, reduced mod 2) with a ninth coordinate 1. +``` + +The symmetric form `S9` is the Cartan matrix of `B` on the first eight +coordinates and 1 on the ninth diagonal entry. The chart is accepted only +because `S9(u_i, u_j) = ( + 1) mod 2` holds for every pair of the 120 +lines. That identity is checked exhaustively; it is the pairing (1) of the +PR968 model. + +Square-free monomials are written in `x0, ..., x7`, the first eight label +coordinates. The relation `x_i^2 = x_i` is used throughout, so a function +`F_2^8 → F_2` has one algebraic normal form. + +## 2. The word + +**Theorem.** Under the hypotheses above, no product of fewer than four letters +hosts the row. Every product of four letters that does host it expands to the +same 48 monomials + +```text +(1 + x0)(1 + x1 + x2 + x4)(1 + x5)(1 + x6 + x7). +``` + +Eight of those monomials have degree at most 1: the constant `1` and +`x0, x1, x2, x4, x5, x6, x7`. The other **40** have degree 2, 3, or 4 +(17, 17, and 6). Those 40 are the new monomials charged for this row. + +One such word, the lexicographically first length-4 subset in the frame order +above, is the product of the letters of ports `11, 21, 38, 140`: + +```text +λ_11 = 1 + x1 + x2 + x4 + x5, +λ_21 = 1 + x0 + x1 + x2 + x4 + x5, +λ_38 = 1 + x0 + x1 + x2 + x4, +λ_140 = 1 + x0 + x5 + x6 + x7. +``` + +The support of this function on the chart `x8 = 1` is the 16-point flat + +```text +x0 = 0, x5 = 0, x1 + x2 + x4 = 0, x6 + x7 = 0 +``` + +(`x3` is free). Exactly eight of those points are root-line labels, and those +eight are the frame. On every root line the function is therefore the +frame indicator. It is not the indicator of those eight points inside all of +`F_2^8`. + +## 3. Proof + +**Factor restriction.** Suppose a product of letters equals 1 at a line. Each +factor is 0 or 1, so each factor equals 1 at that line. The indicator equals 1 +on the eight frame lines, so every factor of a hosting word equals 1 on all +eight. Checking the 120 letters, the only ones with that property are the +eight frame letters. Every hosting word is therefore a product of a subset of +these eight letters, repetitions included. A repeated letter does not change +an `F_2` value, so the 255 nonempty subsets exhaust the words. + +**Census.** Each of those 255 products was evaluated on all 120 lines. The +products equal to the indicator are 56 of length 4, 56 of length 5, 28 of +length 6, 8 of length 7, and 1 of length 8. None is shorter than 4. This is an +exhaustive census of a finite domain of size 255, after the factor restriction. +It is not a sample. + +**Reduction.** Write `p = 1 + x1 + x2 + x4`, `q = x0`, `r = x5`, and +`s = x6 + x7`. The four letters are the functions + +```text +λ_38 = p + q, λ_11 = p + r, λ_21 = p + q + r, λ_140 = 1 + q + r + s. +``` + +On `F_2`, every function satisfies `f^2 = f`. Hence + +```text +(p + q)(p + q + r) = (p + q)(1 + r), +(1 + r)(p + r) = p(1 + r), +(p + q)(p + r)(p + q + r) = p(1 + q)(1 + r). +``` + +The last step uses `p(p + q) = p(1 + q)`. Multiplying by `λ_140` gives +`p(1 + q)(1 + r)(1 + q + r + s)`. The target form is +`p(1 + q)(1 + r)(1 + s)`. Their difference is killed because + +```text +(1 + q)(q + r) = r(1 + q), so p(1 + q)(1 + r)(q + r) = p(1 + q) · r(1 + r) = 0. +``` + +Thus the four-letter product equals `(1 + x0)(1 + x1 + x2 + x4)(1 + x5)(1 + x6 + x7)` +as a function on `F_2^8`. The four factors use disjoint variables, so the +square-free expansion has `2 · 4 · 2 · 3 = 48` monomials and no cancellation. +The eight monomials in which at most one factor contributes a variable are +`1, x0, x1, x2, x4, x5, x6, x7`. The remaining 40 are new relative to that +degree-at-most-1 alphabet. + +**Same expansion for every shortest word.** The same evaluation shows that all +56 length-4 hosting products determine one polynomial: each agrees with this +expansion on all 256 points of the chart. Longer hosting products agree with +the indicator on the 120 lines. Their chart polynomials are not charged here. + +**Pairing rank, not added.** On the eight frame lines the pairing matrix is +the all-ones matrix, which has `F_2`-rank 1. That is the inherited copy-cost +of the support of `χ χ^T`. It is a different unit from the 40 monomials and is +not added to them. + +## 4. What the census does not decide + +The factor restriction uses products of root-line letters. A sum of such +products, a coordinate change, or a circuit that is not a product can be a +different hosting, and this note gives no lower bound for those larger +alphabets. The 40 monomials are the expansion of this word in the fixed +simple-root chart. They are not a count of Clifford monomial routes, Gaussian +children, or selected-invoice primitives. No conversion into the selected paid +histogram is claimed, so this bill does not beat the selected profile and does +not change κ. + +The other twelve frames of the witness are not expanded. A monomial shared +with one of them would make a joint bill smaller than the sum of the separate +bills. That comparison is not made. diff --git a/research/p104-one-frame-indicator-word/README.md b/research/p104-one-frame-indicator-word/README.md new file mode 100644 index 0000000000..550327086f --- /dev/null +++ b/research/p104-one-frame-indicator-word/README.md @@ -0,0 +1,104 @@ +# One P104 frame-indicator word + +**No new κ.** The selected frontier stays `781451/250000000`. This package hosts +one frame-indicator row as a legal product of root-line labels and charges the +new monomials of that word. It does not host the other twelve rows and it does +not price a signed profile. + +## Result + +Frame 0 of the vendored 104-by-13 witness is the odd-graph clique + +```text +11, 21, 38, 56, 140, 146, 161, 191. +``` + +A letter is the pairing of one root line with all 120 root lines. In `F_2`, a +product equals 1 only when every factor equals 1, so every factor of a hosting +word is identically 1 on these eight lines. The only such letters are the eight +frame letters. The 255 nonempty products of those letters were evaluated on all +120 lines. The products equal to the indicator begin at length 4: 56, 56, 28, +8, and 1 products of lengths 4 through 8. None is shorter. + +Every length-4 hosting product expands, in the inherited simple-root chart, to +the same 48 square-free monomials + +```text +(1 + x0)(1 + x1 + x2 + x4)(1 + x5)(1 + x6 + x7). +``` + +The lexicographically first such word uses ports `11, 21, 38, 140`. Eight +monomials have degree at most 1 (`1` and `x0, x1, x2, x4, x5, x6, x7`). The +other **40** have degree 2, 3, or 4, in counts 17, 17, and 6. Those 40 are the +new charges for this row. The proof of the factor restriction and of the +disjoint-variable identity is in [PROOF.md](PROOF.md). The support of the +function on the chart is a 16-point flat; exactly eight of those points are +root-line labels, and those eight are the frame. + +The pairing matrix on the eight frame lines is the all-ones matrix and has +rank 1. That copy-cost is a different unit and is not added to the 40. + +## Evidence class + +The factor restriction and the reduction to the four-factor form are written +proofs. The census of 255 products, the check that only the eight frame letters +are admissible, the 120-by-120 chart identity, and the agreement of all 56 +length-4 expansions are exhaustive finite checks. This is not a heuristic and +not an incomplete search inside that alphabet. + +It is not a lower bound for sums of letters, for a different chart, or for a +circuit that is not a product. It is not a selected-invoice histogram. It does +not beat the selected profile. + +## Scope + +Catalogue card `complex-rank-shape-decoder`, priority revision 8, pinned at +`17469fff78c22941280f04d39dc5a1faee09c840`. The card asks whether a decoder or +coframe change improves the full signed moment. The live handoff in open PR +[#7014](https://github.com/CrocSwap/integer-mult-bounds/pull/7014) asks a prior +question: host one frame-indicator row and charge every new monomial before +touching the other twelve. Scope is **changed** to that first step, for frame 0 +only. The connection to the bottleneck is recorded in [TASK.json](TASK.json): +a later joint bill cannot be priced until each row's new monomials are known, +and this package stops at the first row. + +`data/frames.json` is the frame partition from that open pull request (head +`949c0ada285aae619468ae49317c8d9f4df130f7`), vendored and hashed inside +`verify.py`. This package does not reprove the rank-13 factorisation. Ports and +the E8 line map are the #6933 inputs pinned in [SOURCES.json](SOURCES.json). +The label chart is the simple-root chart of the inherited `e8model.py` pairing. + +## Reproduce + +From the repository root, Python 3.12, standard library only: + +```sh +python3 -B research/p104-one-frame-indicator-word/verify.py +python3 -I -O -B research/p104-one-frame-indicator-word/verify.py +python3 -B scripts/research_package.py --package research/p104-one-frame-indicator-word +python3 -B scripts/check_research_commit.py --package research/p104-one-frame-indicator-word --check-source-history +``` + +The verifier prints `PASS_P104_ONE_FRAME_INDICATOR` with `word=11,21,38,140`, +`monomials=48`, and `new=40`. Failure controls delete one letter of that word, +count the 14 length-4 products that miss the row, delete the constant monomial, +and reject a letter from outside the frame. + +## Open next questions + +1. Host frame 1 by the same census and compare its degree-at-least-2 monomials + with these 40. A shared monomial would make a joint bill smaller than the + sum, which is the first place the thirteen rows can interact. +2. Decide whether a sum of letters, rather than a product, can host this same + row with fewer than 40 new monomials. The product census does not speak + about sums. +3. Name these 40 monomials as primitives on the selected #6030 invoice before + asking whether thirteen rows beat the profile. No conversion is claimed here, + and no κ change is available until that naming exists. + +## Credit + +Frame partition: unpaidmoon, PR #7014. Ports and odd-graph window: PR #6933. +Label pairing: the simple-root chart used by the complex-frame decoder notes. +Prepared by unpaidmoon with substantial Cursor Grok assistance. New work is +Apache-2.0. diff --git a/research/p104-one-frame-indicator-word/SOURCES.json b/research/p104-one-frame-indicator-word/SOURCES.json new file mode 100644 index 0000000000..e0c65df5a4 --- /dev/null +++ b/research/p104-one-frame-indicator-word/SOURCES.json @@ -0,0 +1,30 @@ +{ + "schema_version": 1, + "base_commit": "17469fff78c22941280f04d39dc5a1faee09c840", + "files": [ + { + "path": "certificates/selected-result.json", + "usage": "provenance", + "git_blob": "d59b5035e7b4cbaa9d688a1d32dd0d6eb79c466a", + "sha256": "9b8d0eea95b3bf0dfcb7938a732a6439b87a2685dec2a5ab9e525b3b34debbfa" + }, + { + "path": "docs/research/agent-task-catalog.json", + "usage": "provenance", + "git_blob": "3404fbdd5e7529a0674eef26114b5071e4586cec", + "sha256": "ead6cdc9d0af6d58e8a49744d8ec8c7c15e13d2ba93c276c434bcf9b51b93a81" + }, + { + "path": "research/p104-port-window-haemers/data/e8_line_map.json", + "usage": "input", + "git_blob": "19ea2d553aff60e53af50dd9582ab70730e93450", + "sha256": "b37530e8e9b2b3bd5be418d5ed3060a42ceb66e60ed3789f8ee79f29dabde437" + }, + { + "path": "research/p104-port-window-haemers/data/ports_p104.json", + "usage": "input", + "git_blob": "8037fd10877f49063a1c8a530b40c27f2453dac0", + "sha256": "691c7b7d212112115b1bfc76db89f90d304ec448dc8c4a0c43d9a5954317146c" + } + ] +} diff --git a/research/p104-one-frame-indicator-word/TASK.json b/research/p104-one-frame-indicator-word/TASK.json new file mode 100644 index 0000000000..986ba06087 --- /dev/null +++ b/research/p104-one-frame-indicator-word/TASK.json @@ -0,0 +1,87 @@ +{ + "schema_version": 1, + "task_id": "complex-rank-shape-decoder", + "priority_revision": 8, + "base_commit": "17469fff78c22941280f04d39dc5a1faee09c840", + "original_card": { + "id": "complex-rank-shape-decoder", + "question": "Can a legal decoder or coframe change improve the full signed moment, including changed helper targets?", + "starting_points": [ + "docs/research/current-priorities.md", + "docs/research/research-family-status.md", + "certificates/selected-result.json", + "docs/research/pr-intake-evidence/pr5599-completion/PROOF-REVIEW.md", + "docs/research/recursive-invoice-preflight.md" + ], + "methods": [ + "Construct and independently check one source-bound instance of the stated first experiment.", + "Seek an exact counterexample or target-specific complete-budget obstruction to that same proposed change." + ], + "acceptance_gate": "A source-bound arbitrary-input identity and legal producer/consumer boundary with the complete paid and productive target invoice, including restoration, ownership, native alignment and extra charges. Price under the actual current model; historical ceiling.py assumptions must be established before use. A favorable optimistic relaxation keeps a route open but is not a completed supplier. A scoped local result remains useful; expansion requires the family-level budget gate.", + "first_step": { + "command": [ + "python3", + "-B", + "scripts/research_selected_baseline.py" + ], + "expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY", + "inspect": "Read revision 8, the selected review and live exact-source handoffs. Then follow this card's bounded first_experiment.", + "scope": "Source-bound orientation only, not a supplier replay or theorem proof." + }, + "first_experiment": "Price one complete changed itinerary with both paid and productive histograms. Check #5693/#5698 for overlap and their exact hypotheses before another coframe search. Use the signed invoice preflight where its boundary contract agrees; an inconclusive concave comparison is not a power crossing. Stop if an optimistic complete budget fails at the chosen target or native delivery remains unspecified.", + "bottleneck_link": "Judge the actual current supplier contract and complete signed target/paid bill; frozen-profile ceilings require an applicability proof.", + "allowed_variations": [ + "One exact source head, helper/cut region or quantified proposition and changed hypothesis; check live handoffs first. Different seeds are not different scopes." + ], + "scope_change_triggers": [ + "Changing source head, helper/cut domain, operation alphabet or caller boundary without recording the new scope.", + "Treating an archived proposal or local numerical pass as complete supplier admission." + ], + "overlap_terms": [ + "coframe", + "signed moment", + "5693", + "5698" + ], + "existing_checks": [ + { + "scope": "Selected PR5599: complete helper-subspace construction and independent caller replay; five framework assumptions remain.", + "evidence": "docs/research/pr-intake-evidence/pr5599-completion/PROOF-REVIEW.md", + "remaining": "The next experiment must identify uncovered source-specific work. Successor archive intake does not transfer this admission.", + "status": "reviewed", + "source_commit": "09dd0965711ca7eeb169057a420abab1c9052137", + "selected_record_sha256": "2a0220e866417774606e0f991e41b9c1d26b1f43b97857238cf478dbc7d330b5" + } + ] + }, + "scope_status": "changed", + "actual_scope": "Host frame 0 of the #7014 P104 factorisation as a product of root-line labels and charge every new monomial of that word, before touching the other twelve frames.", + "scope_change_reason": "The catalogue card asks whether a decoder or coframe change improves the full signed moment. Open PR #7014 asks a prior physical question: host one frame-indicator row as a legal label word and charge every new monomial before touching the other twelve. This package answers that first step for frame 0 only. It does not price a changed itinerary, so it cannot improve the signed moment.", + "bottleneck_link": "A complete signed target bill needs the new charges of any legal producer that is added. This package records the 40 new monomials of one frame-indicator word and stops. Those charges are a prerequisite for pricing the thirteen rows together; they are not themselves a signed histogram or a supplier admission.", + "budget_minutes": 60, + "stop_rule": "Stop after the length-4 word for frame 0, its 40 new monomials, and the statement that the other twelve frames are unexpanded. Do not convert the monomials into a selected-invoice histogram and do not claim a kappa change.", + "outcome": "completed", + "evidence": "verify.py prints PASS_P104_ONE_FRAME_INDICATOR word=11,21,38,140 monomials=48 new=40. Evidence class: a written F2 factor restriction and a written reduction to (1+x0)(1+x1+x2+x4)(1+x5)(1+x6+x7), plus an exhaustive census of the 255 products of the only eight admissible letters. Not a heuristic. Not a selected-profile comparison. Selected kappa remains 781451/250000000.", + "author": "unpaidmoon", + "ai_assistance": "Substantial Cursor Grok assistance. The census, chart identity, and polynomial reduction are rechecked by verify.py.", + "open_questions": [ + { + "question": "Which degree-at-least-2 monomials of frame 1 coincide with the 40 monomials charged for frame 0?", + "why_it_matters": "A shared monomial makes the joint bill of two rows smaller than the sum of the separate bills, which is the first interaction among the thirteen rows.", + "first_step": "Rerun the same census with frames[1] in place of frames[0] and intersect the two sets of degree-at-least-2 exponents.", + "blocked_by": "none in this alphabet; the other frame has not been expanded" + }, + { + "question": "Can a sum of root-line letters host the frame-0 indicator with fewer than 40 new monomials in this chart?", + "why_it_matters": "The 40-count is exact for products. A sum is a larger alphabet, so the product census is not a lower bound for it.", + "first_step": "Search linear combinations of the 120 letters, reduced in the simple-root chart, for a function that matches the indicator on all 120 lines and has fewer than 40 monomials of degree at least 2.", + "blocked_by": "the product census does not cover sums" + }, + { + "question": "What is the selected #6030 invoice price of these 40 monomials, with no other rows attached?", + "why_it_matters": "Until each monomial is named as a paid primitive, the word cannot be compared with the selected profile and cannot change kappa.", + "first_step": "Match each of the 40 square-free monomials to a primitive the #6030 invoice already prices, or record that the match is undefined.", + "blocked_by": "this package does not identify those primitives" + } + ] +} diff --git a/research/p104-one-frame-indicator-word/VERIFY.json b/research/p104-one-frame-indicator-word/VERIFY.json new file mode 100644 index 0000000000..61e5739c26 --- /dev/null +++ b/research/p104-one-frame-indicator-word/VERIFY.json @@ -0,0 +1,32 @@ +{ + "schema_version": 1, + "status": "ready", + "python": "3.12", + "requirements": [], + "inputs": [ + "research/p104-port-window-haemers/data/e8_line_map.json", + "research/p104-port-window-haemers/data/ports_p104.json" + ], + "commands": [ + { + "argv": [ + "{python}", + "-B", + "{package}/verify.py" + ], + "timeout_seconds": 120, + "checks": "Exhaustive census of the 255 products of the eight frame-0 letters on all 120 root lines; identity of every length-4 hosting word with (1+x0)(1+x1+x2+x4)(1+x5)(1+x6+x7) and its 48-monomial ANF (40 of degree at least 2). Failure controls delete one canonical letter, count 14 length-4 misses, delete the constant monomial, and reject a non-frame letter. Prints PASS_P104_ONE_FRAME_INDICATOR with word=11,21,38,140 and new=40. Does not replay the selected supplier and does not change kappa." + }, + { + "argv": [ + "{python}", + "-I", + "-O", + "-B", + "{package}/verify.py" + ], + "timeout_seconds": 120, + "checks": "Same census and identity with isolated mode and assert stripping, confirming the checks are need() failures rather than assertions." + } + ] +} diff --git a/research/p104-one-frame-indicator-word/data/frames.json b/research/p104-one-frame-indicator-word/data/frames.json new file mode 100644 index 0000000000..793a32f489 --- /dev/null +++ b/research/p104-one-frame-indicator-word/data/frames.json @@ -0,0 +1,157 @@ +{ + "description": "Disjoint 8-cliques of the odd graph. frames partition the 104 P104 ports. excluded_odd_cliques are the two 8-cliques in the 120 weight-3-mod-4 vectors that P104 omits; together with frames they partition those 120 vectors. Integers are F_2^9 ports, bit i coordinate i.", + "frames": [ + [ + 11, + 21, + 38, + 56, + 140, + 146, + 161, + 191 + ], + [ + 13, + 26, + 131, + 148, + 262, + 273, + 392, + 415 + ], + [ + 14, + 69, + 164, + 239, + 276, + 351, + 446, + 501 + ], + [ + 19, + 41, + 74, + 112, + 280, + 290, + 321, + 379 + ], + [ + 22, + 37, + 196, + 247, + 268, + 319, + 478, + 493 + ], + [ + 28, + 49, + 133, + 168, + 265, + 292, + 400, + 445 + ], + [ + 35, + 81, + 176, + 194, + 274, + 352, + 385, + 499 + ], + [ + 44, + 84, + 152, + 224, + 304, + 328, + 388, + 508 + ], + [ + 50, + 70, + 138, + 254, + 259, + 375, + 443, + 463 + ], + [ + 73, + 98, + 208, + 251, + 324, + 367, + 477, + 502 + ], + [ + 88, + 100, + 193, + 253, + 322, + 382, + 475, + 487 + ], + [ + 134, + 145, + 200, + 223, + 416, + 439, + 494, + 505 + ], + [ + 261, + 296, + 336, + 381, + 386, + 431, + 471, + 506 + ] + ], + "excluded_odd_cliques": [ + [ + 7, + 25, + 42, + 52, + 76, + 82, + 97, + 127 + ], + [ + 67, + 104, + 137, + 162, + 266, + 289, + 448, + 491 + ] + ] +} diff --git a/research/p104-one-frame-indicator-word/verify.py b/research/p104-one-frame-indicator-word/verify.py new file mode 100644 index 0000000000..887ce8890a --- /dev/null +++ b/research/p104-one-frame-indicator-word/verify.py @@ -0,0 +1,372 @@ +#!/usr/bin/env python3 +"""Host one P104 frame-indicator row and charge its new monomials. + +The row is frame 0 of the 104-by-13 factorisation witness. A legal letter is a +root-line pairing. The shortest product of such letters that equals the +indicator on every root line has length 4, and every such product expands to +the same 48 monomials in the simple-root chart. Forty of those monomials have +degree at least 2. The other twelve frames are not expanded. + +Standard library only. Checks are need() failures, so python -O still runs them. +""" +import hashlib +import json +from collections import Counter +from fractions import Fraction +from itertools import combinations, product +from pathlib import Path + +ROOT = Path(__file__).resolve().parent +REPO = ROOT.parents[1] +FRAMES_SHA256 = "c75915c18fc06ac19cc90fb9911209517c071862fdf401b998d5075efc0cda0d" +ANF_SHA256 = "26803d29aa3141a357da4aa551195fe6378d3f2e1cba0c4291a4dedbce6ded2a" +CANONICAL_PORTS = (11, 21, 38, 140) +DEGREE_COUNTS = {0: 1, 1: 7, 2: 17, 3: 17, 4: 6} +CENSUS = {4: 56, 5: 56, 6: 28, 7: 8, 8: 1} +DEGREE1_MASKS = (1, 2, 4, 16, 32, 64, 128) + + +def need(condition, message): + if not condition: + raise SystemExit(message) + + +def parity(value): + return value.bit_count() & 1 + + +def e8_roots(): + roots = [] + for left, right in combinations(range(8), 2): + for sign_left in (1, -1): + for sign_right in (1, -1): + vector = [0] * 8 + vector[left] = 2 * sign_left + vector[right] = 2 * sign_right + roots.append(tuple(vector)) + for signs in product((1, -1), repeat=8): + if signs.count(-1) % 2 == 0: + roots.append(tuple(signs)) + return roots + + +def simple_basis(): + basis = [ + (1, -1, -1, -1, -1, -1, -1, 1), + (2, 2, 0, 0, 0, 0, 0, 0), + (-2, 2, 0, 0, 0, 0, 0, 0), + ] + for index in range(1, 6): + vector = [0] * 8 + vector[index] = -2 + vector[index + 1] = 2 + basis.append(tuple(vector)) + return basis + + +def dot(left, right): + return sum(a * b for a, b in zip(left, right)) // 4 + + +def cartan_mod2(basis): + return [[dot(basis[i], basis[j]) & 1 for j in range(8)] for i in range(8)] + + +def solve_coordinates(matrix, target): + rows = [[Fraction(entry) for entry in matrix[i]] + [Fraction(target[i])] for i in range(8)] + for column in range(8): + pivot = next(row for row in range(column, 8) if rows[row][column]) + rows[column], rows[pivot] = rows[pivot], rows[column] + scale = 1 / rows[column][column] + rows[column] = [entry * scale for entry in rows[column]] + for row in range(8): + if row != column and rows[row][column]: + factor = rows[row][column] + rows[row] = [rows[row][col] - factor * rows[column][col] for col in range(9)] + solution = [rows[i][8] for i in range(8)] + need(all(entry.denominator == 1 for entry in solution), "root coordinates are not integral") + return tuple(int(entry) for entry in solution) + + +def pairing(form, left, right): + value = 0 + for i in range(8): + if (left >> i) & 1: + for j in range(8): + if (right >> j) & 1 and form[i][j]: + value ^= 1 + if (left >> 8) & 1 and (right >> 8) & 1: + value ^= 1 + return value + + +def label_of(root, matrix): + coordinates = solve_coordinates(matrix, root) + bits = 0 + for index, value in enumerate(coordinates): + if value & 1: + bits |= 1 << index + return bits | (1 << 8) + + +def negate(vector): + return tuple(-entry for entry in vector) + + +def root_lines(): + lines = [] + seen = set() + for root in e8_roots(): + key = root if root > negate(root) else negate(root) + if key not in seen: + seen.add(key) + lines.append(root) + need(len(lines) == 120, "expected 120 root lines") + return lines + + +def f2_rank(rows): + basis = {} + for row in rows: + value = row + while value: + pivot = value.bit_length() - 1 + if pivot in basis: + value ^= basis[pivot] + else: + basis[pivot] = value + break + return len(basis) + + +def polynomial_multiply(left, right): + product = {} + for left_exp, left_coeff in left.items(): + for right_exp, right_coeff in right.items(): + exponent = left_exp | right_exp + product[exponent] = (product.get(exponent, 0) + left_coeff * right_coeff) & 1 + return {exponent: coeff for exponent, coeff in product.items() if coeff} + + +def linear_factor(mask, constant): + factor = {0: constant} + for index in range(8): + if (mask >> index) & 1: + factor[1 << index] = 1 + return factor + + +def letter_form(label, form): + mask = 0 + for index in range(8): + if pairing(form, label, 1 << index): + mask |= 1 << index + return mask, pairing(form, label, 1 << 8) + + +def evaluate(polynomial, point): + value = 0 + for exponent in polynomial: + if (exponent & point) == exponent: + value ^= 1 + return value + + +def monomial_name(exponent): + if exponent == 0: + return "1" + return "*".join("x{0}".format(index) for index in range(8) if (exponent >> index) & 1) + + +def load_inputs(): + frame_bytes = (ROOT / "data" / "frames.json").read_bytes() + need(hashlib.sha256(frame_bytes).hexdigest() == FRAMES_SHA256, "frames.json sha256 mismatch") + frames = json.loads(frame_bytes.decode())["frames"] + need(len(frames) == 13 and all(len(frame) == 8 for frame in frames), "expected 13 frames of size 8") + ports = json.loads((REPO / "research/p104-port-window-haemers/data/ports_p104.json").read_text())["ports"] + line_map = json.loads((REPO / "research/p104-port-window-haemers/data/e8_line_map.json").read_text()) + vector_to_root = {vector: tuple(root) for vector, root in zip(line_map["vectors"], line_map["roots2"])} + need(len(ports) == 104 and len(set(ports)) == 104, "expected 104 distinct P104 ports") + return frames, set(ports), vector_to_root + + +def build_labels(form, matrix, lines): + labels = [label_of(root, matrix) for root in lines] + need(len(set(labels)) == 120, "root-line labels are not distinct") + need(all((label >> 8) & 1 for label in labels), "a label is missing the ninth coordinate") + index = {} + for position, root in enumerate(lines): + index[root] = position + index[negate(root)] = position + mismatches = 0 + for left in range(120): + for right in range(120): + expected = (dot(lines[left], lines[right]) + 1) & 1 + if pairing(form, labels[left], labels[right]) != expected: + mismatches += 1 + need(mismatches == 0, "simple-root chart does not reproduce the label pairing") + return labels, index + + +def frame_geometry(frame, ports, vector_to_root, index): + need(set(frame) <= ports, "frame 0 is not contained in P104") + need(len(set(frame)) == 8, "frame 0 has a repeated port") + for left, right in combinations(frame, 2): + need((left & right).bit_count() & 1, "frame 0 is not an odd-graph clique") + need(dot(vector_to_root[left], vector_to_root[right]) == 0, "frame 0 roots are not pairwise orthogonal") + positions = [index[vector_to_root[port]] for port in frame] + need(len(set(positions)) == 8, "frame 0 does not use eight root lines") + return positions + + +def product_values(rows, mask): + values = [1] * 120 + for letter, row in enumerate(rows): + if (mask >> letter) & 1: + for point in range(120): + values[point] &= row[point] + return values + + +def census(rows, frame_positions): + target = tuple(1 if point in set(frame_positions) else 0 for point in range(120)) + counts = Counter() + shortest = [] + for mask in range(1, 256): + if tuple(product_values(rows, mask)) == target: + length = mask.bit_count() + counts[length] += 1 + if length == 4: + shortest.append(mask) + need(dict(counts) == CENSUS, "product census counts changed: {0}".format(dict(counts))) + need(sum(counts[length] for length in range(1, 4)) == 0, "a product shorter than 4 hosts the row") + need(len(shortest) == 56, "expected 56 length-4 words") + return min(shortest), shortest, target + + +def expand_mask(mask, forms): + polynomial = {0: 1} + for letter in range(8): + if (mask >> letter) & 1: + polynomial = polynomial_multiply(polynomial, linear_factor(*forms[letter])) + return polynomial + + +def simplified_polynomial(): + factors = ( + {0: 1, 1: 1}, + {0: 1, 1 << 1: 1, 1 << 2: 1, 1 << 4: 1}, + {0: 1, 1 << 5: 1}, + {0: 1, 1 << 6: 1, 1 << 7: 1}, + ) + polynomial = {0: 1} + for factor in factors: + polynomial = polynomial_multiply(polynomial, factor) + return polynomial + + +def admissible_letters(labels, form, frame_positions): + admissible = [] + for position, label in enumerate(labels): + if all(pairing(form, label, labels[point]) == 1 for point in frame_positions): + admissible.append(position) + return admissible + + +def run_controls(letter_rows, frame_positions, canonical, polynomial, value_at): + target = tuple(1 if point in set(frame_positions) else 0 for point in range(120)) + dropped_bit = 1 << (canonical.bit_length() - 1) + need(tuple(product_values(letter_rows, canonical ^ dropped_bit)) != target, + "deleting one canonical letter still hosts the row") + successes = { + mask for mask in range(256) + if mask.bit_count() == 4 and tuple(product_values(letter_rows, mask)) == target + } + need(len(successes) == 56, "length-4 success count drifted inside the control") + need(sum(1 for mask in range(256) if mask.bit_count() == 4) - len(successes) == 14, + "length-4 failure count is not 14") + without_constant = {exponent: coeff for exponent, coeff in polynomial.items() if exponent} + need(any(evaluate(without_constant, point) != evaluate(polynomial, point) for point in range(256)), + "deleting the constant monomial did not change the chart function") + outside = next(point for point in range(120) if point not in set(frame_positions)) + need(any(value_at(outside, point) == 0 for point in frame_positions), + "a non-frame label is 1 on every frame line") + need(any(value_at(outside, point) == 0 and target[point] == 1 for point in frame_positions), + "multiplying by a non-frame label would not destroy a frame value") + + +def main(): + frames, ports, vector_to_root = load_inputs() + basis = simple_basis() + matrix = list(zip(*basis)) + form = cartan_mod2(basis) + lines = root_lines() + labels, index = build_labels(form, matrix, lines) + frame = frames[0] + positions = frame_geometry(frame, ports, vector_to_root, index) + admissible = admissible_letters(labels, form, positions) + need(set(admissible) == set(positions), "the letters equal to 1 on the frame are not exactly the frame") + letter_rows = [[pairing(form, labels[source], labels[point]) for point in range(120)] for source in positions] + forms = [letter_form(labels[source], form) for source in positions] + canonical, shortest, _target = census(letter_rows, positions) + ports_used = tuple(frame[letter] for letter in range(8) if (canonical >> letter) & 1) + need(ports_used == CANONICAL_PORTS, "canonical word ports changed: {0}".format(ports_used)) + expansions = [expand_mask(mask, forms) for mask in shortest] + need(all(polynomial == expansions[0] for polynomial in expansions), + "the 56 length-4 words do not share one expansion") + polynomial = expansions[0] + simplified = simplified_polynomial() + need(polynomial == simplified, "the label product does not equal the disjoint-variable form") + need(all(evaluate(polynomial, point) == evaluate(simplified, point) for point in range(256)), + "chart evaluation disagrees with the factored form") + exponents = tuple(sorted(polynomial)) + payload = ",".join(str(exponent) for exponent in exponents) + need(hashlib.sha256(payload.encode()).hexdigest() == ANF_SHA256, "ANF sha256 mismatch") + degrees = dict(Counter(exponent.bit_count() for exponent in exponents)) + need(degrees == DEGREE_COUNTS, "degree counts changed: {0}".format(degrees)) + degree1 = tuple(exponent for exponent in exponents if exponent.bit_count() == 1) + need(degree1 == DEGREE1_MASKS, "degree-1 monomials changed") + chart_support = [point for point in range(256) if evaluate(polynomial, point)] + label_hits = [point for point in range(120) if evaluate(polynomial, labels[point] & 255)] + need(len(chart_support) == 16, "chart support is not the 16-point flat") + need(label_hits == sorted(positions), "on root lines the word is not the frame indicator") + port_of = {root: port for port, root in vector_to_root.items()} + port_of.update({negate(root): port for port, root in vector_to_root.items()}) + p104_hits = [ + point for point in range(120) + if port_of[lines[point]] in ports and evaluate(polynomial, labels[point] & 255) + ] + need(sorted(p104_hits) == sorted(positions), "on P104 the word is not the frame indicator") + direct = product_values(letter_rows, canonical) + need(all(direct[point] == (1 if point in set(positions) else 0) for point in range(120)), + "direct product disagrees with the indicator") + run_controls( + letter_rows, + positions, + canonical, + polynomial, + lambda source, point: pairing(form, labels[source], labels[point]), + ) + new_monomials = [exponent for exponent in exponents if exponent.bit_count() >= 2] + need(len(new_monomials) == 40, "new monomial count is not 40") + support_rows = [] + for left in range(8): + mask = 0 + for right, point in enumerate(positions): + if letter_rows[left][point]: + mask |= 1 << right + support_rows.append(mask) + need(support_rows == [0xFF] * 8, "the frame pairing is not the all-ones matrix") + need(f2_rank(support_rows) == 1, "the frame support does not have pairing rank 1") + print( + "PASS_P104_ONE_FRAME_INDICATOR word={word} monomials=48 new=40 " + "constant=1 degree1=7 census=4:56,5:56,6:28,7:8,8:1 support_pairing_rank=1".format( + word=",".join(str(port) for port in ports_used) + ) + ) + print("MONOMIALS " + " ".join(monomial_name(exponent) for exponent in exponents)) + print("NEW_MONOMIALS " + " ".join(monomial_name(exponent) for exponent in new_monomials)) + + +if __name__ == "__main__": + main()