From 99220c8239a3a4f0b88fc0bd0ade01bd3d3f0c45 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 18 Oct 2026 12:23:49 +0000 Subject: [PATCH] Record the exact length of the semidihedral triple square. In F2[SD16] with three roles and the 56-call generator alphabet, diag([r^2],[r^2],[r^2]) has minimum paid length 6. Reflections do not reduce the disjoint bill, and selected kappa is unchanged. Co-authored-by: riquelme.pereira --- research/sd16-triple-square/README.md | 199 +++++++++++++ research/sd16-triple-square/SOURCES.json | 24 ++ research/sd16-triple-square/TASK.json | 66 +++++ research/sd16-triple-square/VERIFY.json | 14 + research/sd16-triple-square/verify.py | 354 +++++++++++++++++++++++ 5 files changed, 657 insertions(+) create mode 100644 research/sd16-triple-square/README.md create mode 100644 research/sd16-triple-square/SOURCES.json create mode 100644 research/sd16-triple-square/TASK.json create mode 100644 research/sd16-triple-square/VERIFY.json create mode 100644 research/sd16-triple-square/verify.py diff --git a/research/sd16-triple-square/README.md b/research/sd16-triple-square/README.md new file mode 100644 index 0000000000..dbb2187ec6 --- /dev/null +++ b/research/sd16-triple-square/README.md @@ -0,0 +1,199 @@ +# Semidihedral triple square: exact length 6 + +**Scoped negative result; no new κ.** In the three-role semidihedral register +model below, `diag([r^2], [r^2], [r^2])` has minimum paid length 6. The +matching uniform identity is the doubled coordinate word. Reflections are in +the paid alphabet and do not make a shorter word. The selected frontier is +unchanged. + +This does not escape the scoped three-register obstruction. It prices one +nonabelian alphabet outside that obstruction and shows that this target does +not undercut its disjoint bill. + +## Scope + +Assignment: track `bit-mechanisms`, card `bit-new-register-model`, selector +scope key `7084092dd726e7ba1f2f`, OS nonce `6f7a9ea6cd4e657dcf8c3a81f9840d4a`. +Exact base `ef5f39403610d231a0c27e8a6b4f08c10209bcd6`. Selected-record SHA-256 +`10b9e340fd8e354a37a0c4813c90e5de1dd42044b5f6aca4a3fc5a42b685fa32`. Catalogue +priority revision 2. + +**Changed hypothesis.** The predecessor in +[research/bit-three-register-lower-bound](../bit-three-register-lower-bound/README.md) +uses the additive abelian group `M_3(Q)` and forbids arbitrary free address +maps between calls. This package replaces that address group by the +semidihedral group of order 16, + +```text +SD16 = . +``` + +The relation is not the dihedral relation `s r s = r^{-1}`. Scope status is +**refined**: the card allows one specified nonabelian family, and the family +here is exactly this presentation, the charged labels `{r, s}`, and three +output roles. + +**Roles and alphabet.** There are three roles and no scratch role. A paid call +is + +```text +D_N([g]) = I + ([g] + [e]) N, +``` + +where `g` is `r` or `s` and `N = x y^T` runs through the 28 binary rank-one +idempotents on `F_2^3` (`y^T x = 1`). The alphabet has 56 calls. A final +matrix equality in `M_3(F_2[SD16])` is an operator identity, so it holds for +every dirty input state in `(F_2[SD16])^3`. + +**Target.** `diag([r^2], [r^2], [r^2])`. + +## Priced baselines + +| Bill | Length | What it says | +|---|---:|---| +| Disjoint diagonal word, two `r`-calls on each `E_ii` | 6 | Achieved by the identity below | +| First-order abelianization, ignoring the `αβ` term | 2 | Necessary for the image to be `I`, and not sufficient | +| Exact minimum in the 56-call alphabet | 6 | Shortest-word search | + +`SD16 / ≅ C_2 × C_2`. Write `α = [x] + 1` and `β = [y] + 1` for the +images of `r` and `s`. Then `α^2 = β^2 = 0`, while `αβ ≠ 0`. Every word maps +to `I + α (∑ N_r) + β (∑ N_s) + αβ K`. The target maps to `I`, so both +projector sums must vanish and so must `K`. Vanishing sums already force +length at least 2. That optimistic floor is the priced relaxation. It does +not meet the disjoint bill, so the exact search is what decides the target. + +## Uniform identity + +Each call `D_{E_ii}([g])` is diagonal, with `[g]` in position `i` and `[e]` +elsewhere. Diagonal entries therefore multiply in call order. The word + +```text +(E11, r), (E11, r), (E22, r), (E22, r), (E33, r), (E33, r) +``` + +has diagonal `[r][r] = [r^2]` in every role. The same reasoning gives two +companion identities used as controls and upper bounds: + +- `(E11, r)` twice equals `diag([r^2], [e], [e])`. +- `(E11, s), (E11, r), (E11, r), (E11, s)` equals `diag([r^6], [e], [e])`, + because `s r^2 s = r^6`. Two pure rotations do not isolate `[r^6]`. +- Four rotations on one `E_ii` equal `[r^4]` there. Repeating either long + element on all three roles costs 12. + +The length-4 reflection word is the smallest witness that the semidihedral +operation is not idle: `[r^6]` on one role has minimum length 4. + +## Exact minimum + +A shortest-path enumeration of the 56 calls produces 56, 2,051 and 68,899 +matrices at lengths 1, 2 and 3. The target is absent. Every length-2 and +length-3 matrix in that frontier is invertible as an operator on +`(F_2[SD16])^3`. Splitting a hypothetical shorter word into shortest halves +gives no length-4 or length-5 factorization (0 pairs). The same split at +length 6 finds 799 ordered pairs, in agreement with the explicit word. + +The search is a minimum-length argument. If a word of length `L ≤ 6` exists, +some word of length `ℓ = min length` exists, and that shortest word splits +into shortest halves of lengths `(2,2)`, `(3,2)` or `(3,3)`. Those halves lie +in the enumerated frontiers. One matrix reachable by a two-call word is the +identity itself: every repeated reflection square `(I + ([s] + [e]) N)^2` +equals `I`. Exact length 2 therefore has 2,052 distinct matrices, one more +than the shortest frontier. + +The same length-6 sieve shows that `diag([r^4], [r^4], [r^4])` and +`diag([r^6], [r^6], [r^6])` have **no** word of length at most 6. The +disjoint words give upper bound 12. Those two bounds are not claimed to be +sharp. + +A positive sieve control keeps the empty counts meaningful: +`diag([r], [r], [r])` has minimum length 3, and the length-5 split still +finds 196 non-minimal factorizations of it. + +## What is not claimed + +- No new κ and no change to the selected record. The complex primitive binds. + This note does not alter either supplier. `make frontier1094-verify` was + not run. +- No four-role or scratch-register statement. Three roles are the whole model. +- No claim about dihedral groups, quaternion groups, or other presentations. + The dihedral order-16 reflection model (central involution, two roles) and + the D8 central-commutator models are different groups, alphabets or targets. +- The length-6 absence for the two longer triples is not a minimum. Lengths + 7 through 11 were not searched. +- Finite `F_2`-column checks are not the certificate. The certificate is + equality of all nine group-algebra entries, recomputed by the diagonal + group word for the explicit identities and by the operator search for the + lower bound. +- This is not a proof-assistant formalization and not an integer-multiplication + theorem. + +## Reproduce + +Standard library only, from the repository root. `PYTHONOPTIMIZE` is unset; +the checks are explicit branches, not assertions. + +```sh +python3 -B research/sd16-triple-square/verify.py +python3 -B scripts/research_package.py --package research/sd16-triple-square --check-only +``` + +Expected final line of the verifier: + +```text +PASS_SD16_TRIPLE_SQUARE length=6 frontier=1,56,2051,68899 exact2=2052 len4=0 len5=0 len6=799 r4_through6=0 r6_through6=0 generator_len5_hits=196 rejected=6 no_new_kappa +``` + +The run is about 20 seconds on CPython 3.12. The registered replay command is +the same verifier. Session toolchain, not imported by this package: `c++` +already selected `/usr/bin/g++`, `libboost-dev` 1.83.0.1ubuntu2 was present, +and `research/e8-bit-p111-tuned/requirements.txt` was installed with +`python3 -m pip install --user`. The positive control +`python3 -B scripts/research_baseline.py` returned +`PASS_RESEARCH_BASELINE_BINDINGS_ONLY`. + +Rejected fixtures: a dropped final call, an `r → s` substitution, a projector +moved onto a role that already has its two calls, every trace-zero `x y^T`, +the dihedral relation `s r s = r^7`, and the dirty state +`([r^5], [s], [r^2] + [s])` on the dropped word. Accepted controls: the +length-6 word, the single-role square, the reflection square back to `I`, +the length-4 conjugation, the length-12 upper bounds, inverse roundtrip, and +the 196 length-5 hits on the generator triple. + +## Open next questions + +1. **One restored scratch role.** Can `diag([r^2], [r^2], [r^2], [e])` be + realized in `M_4(F_2[SD16])` with the same labels and all 120 rank-one + idempotents in fewer than 6 calls? A scratch role is the usual way past a + three-role disjoint bill; the D8 central-pair precedent needed an extra + role for a different target. First step: shortest-path BFS to depth 3 on + the 240-call alphabet. Blocker: that depth-3 set was not built here, so + this package claims nothing about four roles. + +2. **Sharp length of the longer triples.** Is the minimum of + `diag([r^4], [r^4], [r^4])`, and likewise `[r^6]`, equal to 12 or to some + length in 7..11? A value below 12 would be the first triple-target saving + in this alphabet. The length-6 sieve already excludes 1..6, and the + disjoint words give 12. First step: meet in the middle at length 8 on the + depth-4 frontier. Blocker: that frontier has 2,198,276 matrices and was + not inverted here. + +## Credit and coordination + +The abelian three-register model and its nine-call theorem are the pinned +predecessor, including eumemic's PR #288 scalar-idempotent model and the +local package above. Nearby register-model work uses different groups or +targets: D8 central commutators, the order-eight dihedral reflection alphabet +on two roles with target `[r^4]`, odd-dihedral joint tariffs, and the Dic3 +generator alphabet. Those results are not inputs to this search. + +Overlap refresh at 2026-10-18T12:23:05Z covered 348 open PRs and 2,454 +discussion records with complete pagination. The exact terms `semidihedral` +and `SD16` matched only this package. Closed PRs are visible to that search +only through local notes and the ledger. + +Discussion writes for this account return FORBIDDEN / Resource not accessible +by integration. No claim was posted, and the write was not retried. + +Prepared by an AI-assisted contributor. Substantial AI assistance was used to +set up the multiplication table, implement the shortest-word search, and write +these notes. New work is Apache-2.0. diff --git a/research/sd16-triple-square/SOURCES.json b/research/sd16-triple-square/SOURCES.json new file mode 100644 index 0000000000..1dc9af2874 --- /dev/null +++ b/research/sd16-triple-square/SOURCES.json @@ -0,0 +1,24 @@ +{ + "schema_version": 1, + "base_commit": "ef5f39403610d231a0c27e8a6b4f08c10209bcd6", + "files": [ + { + "path": "certificates/selected-result.json", + "usage": "provenance", + "git_blob": "ae6a99f28aa468649eb66a361e764d769363bf11", + "sha256": "10b9e340fd8e354a37a0c4813c90e5de1dd42044b5f6aca4a3fc5a42b685fa32" + }, + { + "path": "docs/research/agent-task-catalog.json", + "usage": "provenance", + "git_blob": "c05fafaa25ba95b2d5af86b1800427198e3d20eb", + "sha256": "89b5928bc92c81e2647b6d702a7ad85a6795b8f44e2eb267b2ff3145d8f2b2ce" + }, + { + "path": "research/bit-three-register-lower-bound/README.md", + "usage": "provenance", + "git_blob": "25e41c370c101a33a6deb978de08b03ed7539b79", + "sha256": "c12b33ef388deece0cbf891505a2ce2c0779a0c3a6cf1f0f7f7039bab59cbebd" + } + ] +} diff --git a/research/sd16-triple-square/TASK.json b/research/sd16-triple-square/TASK.json new file mode 100644 index 0000000000..bfa7088899 --- /dev/null +++ b/research/sd16-triple-square/TASK.json @@ -0,0 +1,66 @@ +{ + "schema_version": 1, + "task_id": "bit-new-register-model", + "priority_revision": 2, + "base_commit": "ef5f39403610d231a0c27e8a6b4f08c10209bcd6", + "original_card": { + "id": "bit-new-register-model", + "question": "Can an explicitly larger or nonabelian register model escape the scoped three-register obstruction?", + "starting_points": [ + "research/bit-three-register-lower-bound/README.md", + "certificates/selected-result.json" + ], + "methods": [ + "Construct the smallest exact identity in a newly specified legal operation alphabet.", + "Derive a target-specific relaxation bound for that same explicit alphabet." + ], + "acceptance_gate": "Name the changed hypothesis, all counted roles and a uniform identity; finite column tests alone do not certify it.", + "first_step": { + "command": [ + "python3", + "-B", + "scripts/research_baseline.py" + ], + "expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY", + "inspect": "Read Model section 1 of the three-register bound and identify the exact excluded free operations.", + "scope": "Positive control / source-bound orientation only; this is not a new construction or full cold replay." + }, + "first_experiment": "Write a complete operation alphabet outside one named premise and price a baseline before searching identities.", + "bottleneck_link": "A small-width completed saving changes the bit primitive; a finite-address identity alone is insufficient.", + "allowed_variations": [ + "Choose a fixed larger register count or a specified nonabelian operation family." + ], + "scope_change_triggers": [ + "Remaining inside the already-excluded three-register abelian model." + ], + "overlap_terms": [ + "three-register", + "nonabelian", + "group algebra" + ] + }, + "scope_status": "refined", + "actual_scope": "In F2[SD16] on exactly three roles, with paid labels {r,s} and all 28 binary rank-one idempotents, the minimum word length of diag([r^2],[r^2],[r^2]) is 6.", + "scope_change_reason": "The card allows one specified nonabelian family. The family is the semidihedral group of order 16 with relation srs=r^3, charged generators {r,s}, three output roles and this one target. The abelian M3(Q) three-register model is not reused.", + "bottleneck_link": "The bit primitive does not bind, and this identity meets rather than undercuts the disjoint bill of 6, so selected kappa is unchanged. It prices a nonabelian alphabet outside the three-register obstruction: reflections do not reduce this triple-square target.", + "budget_minutes": 60, + "stop_rule": "Stop once the exact minimum for diag([r^2],[r^2],[r^2]) is known. Do not open a four-role search or a kappa claim. The r^4 and r^6 triple sieves stop at length 6.", + "outcome": "negative-result", + "evidence": "Selector nonce 6f7a9ea6cd4e657dcf8c3a81f9840d4a, scope key 7084092dd726e7ba1f2f. Verifier PASS_SD16_TRIPLE_SQUARE: shortest frontiers 56, 2051 and 68899; length-4 and length-5 factorizations 0; length-6 factorizations 799; explicit doubled coordinate word. Abelianization relaxation floor 2. No new kappa. Discussion writes are forbidden for this account and were not retried.", + "author": "AI-assisted contributor", + "ai_assistance": "Substantial AI assistance was used to set up the semidihedral multiplication table, implement the shortest-word search, and write the package notes.", + "open_questions": [ + { + "question": "Can diag([r^2],[r^2],[r^2],[e]) be realized in M4(F2[SD16]) with labels {r,s} and all 120 rank-one idempotents in fewer than 6 calls?", + "why_it_matters": "A restored scratch role is the usual route past a three-role disjoint bill. This package only rules out savings with exactly three roles.", + "first_step": "Run a shortest-path search to depth 3 on the 240-call four-role alphabet and test the padded target.", + "blocked_by": "That four-role frontier was not built in this run." + }, + { + "question": "What is the exact minimum length of diag([r^4],[r^4],[r^4]) and of diag([r^6],[r^6],[r^6]) in the same three-role alphabet?", + "why_it_matters": "A length below the disjoint bill of 12 would be a genuine triple-target saving. The length-6 sieve only excludes lengths 1 through 6.", + "first_step": "Meet in the middle at length 8 using the depth-4 frontier of 2198276 matrices.", + "blocked_by": "The depth-4 frontier was counted in a scratch run but not inverted or retained as a certificate." + } + ] +} diff --git a/research/sd16-triple-square/VERIFY.json b/research/sd16-triple-square/VERIFY.json new file mode 100644 index 0000000000..fa5d770dc1 --- /dev/null +++ b/research/sd16-triple-square/VERIFY.json @@ -0,0 +1,14 @@ +{ + "schema_version": 1, + "status": "ready", + "python": "3.12", + "requirements": [], + "inputs": [], + "commands": [ + { + "argv": ["{python}", "-B", "{package}/verify.py"], + "timeout_seconds": 120, + "checks": "Semidihedral relation, 56-call alphabet, length-6 triple-square identity, dirty-state and mutation controls, shortest frontiers, and length-4/5/6 factorizations" + } + ] +} diff --git a/research/sd16-triple-square/verify.py b/research/sd16-triple-square/verify.py new file mode 100644 index 0000000000..72f68e0f73 --- /dev/null +++ b/research/sd16-triple-square/verify.py @@ -0,0 +1,354 @@ +#!/usr/bin/env python3 +"""Exact minimum length of one semidihedral triple-square register word. + +Model: G = SD16 = , three roles, and paid +calls D_N([g]) = I + ([g] + [e]) N for g in {r, s} and every binary rank-one +idempotent N on F2^3. Equality is in M_3(F2[G]), so it is an identity on +arbitrary dirty register states. + +The doubled coordinate word has length 6 and equals diag([r^2], [r^2], [r^2]). +No shorter word does. Reflections are in the alphabet and do not reduce it. +""" + +from array import array +import sys + + +def mul(a, b): + """Group law. Codes 0..7 are r^i and 8..15 are r^i s.""" + ai, bi = a & 7, b & 7 + if a < 8 and b < 8: + return (ai + bi) & 7 + if a < 8: + return 8 + ((ai + bi) & 7) + if b < 8: + return 8 + ((ai + 3 * bi) & 7) + return (ai + 3 * bi) & 7 + + +MUL = [[mul(a, b) for b in range(16)] for a in range(16)] +LEFT = [] +RIGHT = [] +for g in range(16): + left = array("H") + right = array("H") + for mask in range(1 << 16): + out_left = 0 + out_right = 0 + rest = mask + while rest: + bit = (rest & -rest).bit_length() - 1 + out_left ^= 1 << MUL[g][bit] + out_right ^= 1 << MUL[bit][g] + rest &= rest - 1 + left.append(out_left) + right.append(out_right) + LEFT.append(left) + RIGHT.append(right) + + +def times_generator_plus_one(mask, g): + return mask ^ LEFT[g][mask] + + +def dot(x, y): + return bin(x & y).count("1") % 2 + + +IDEMS = [(x, y) for x in range(1, 8) for y in range(8) if dot(x, y) == 1] +EXCLUDED = [(x, y) for x in range(1, 8) for y in range(8) if dot(x, y) == 0] +R = 1 +S = 8 +OPS = [(label, x, y) for label in (R, S) for x, y in IDEMS] +E11 = (1, 1) +E22 = (2, 2) +E33 = (4, 4) + + +def identity(): + cols = array("H", [0] * 9) + cols[0] = cols[4] = cols[8] = 1 + return cols + + +def apply(cols, op): + label, x, y = op + acc = [0, 0, 0] + for j in range(3): + if (x >> j) & 1: + acc[0] ^= cols[j] + acc[1] ^= cols[3 + j] + acc[2] ^= cols[6 + j] + acc = [times_generator_plus_one(entry, label) for entry in acc] + out = array("H", cols) + for j in range(3): + if (y >> j) & 1: + out[j] ^= acc[0] + out[3 + j] ^= acc[1] + out[6 + j] ^= acc[2] + return out + + +def word_matrix(word): + cols = identity() + for op in word: + cols = apply(cols, op) + return cols + + +def diagonal(mask_a, mask_b, mask_c): + cols = array("H", [0] * 9) + cols[0] = mask_a + cols[4] = mask_b + cols[8] = mask_c + return cols + + +def bit(element): + return 1 << element + + +def matmul(left, right): + out = array("H", [0] * 9) + for i in range(3): + for j in range(3): + acc = 0 + for k in range(3): + factor = right[3 * k + j] + entry = left[3 * i + k] + while factor: + g = (factor & -factor).bit_length() - 1 + acc ^= RIGHT[g][entry] + factor &= factor - 1 + out[3 * i + j] = acc + return out + + +def linear_rows(cols): + rows = [0] * 48 + for j in range(3): + for h in range(16): + column = j * 16 + h + for i in range(3): + rest = cols[3 * i + j] + while rest: + q = (rest & -rest).bit_length() - 1 + rows[i * 16 + MUL[q][h]] |= 1 << column + rest &= rest - 1 + return rows + + +def invert_rows(rows): + augmented = [rows[r] | (1 << (48 + r)) for r in range(48)] + for col in range(48): + pivot = next((r for r in range(col, 48) if (augmented[r] >> col) & 1), None) + if pivot is None: + return None + if pivot != col: + augmented[col], augmented[pivot] = augmented[pivot], augmented[col] + pivot_row = augmented[col] + for r in range(48): + if r != col and (augmented[r] >> col) & 1: + augmented[r] ^= pivot_row + return [augmented[r] >> 48 for r in range(48)] + + +def matrix_from_rows(rows): + cols = array("H", [0] * 9) + for j in range(3): + column = j * 16 + for i in range(3): + mask = 0 + for q in range(16): + if (rows[i * 16 + q] >> column) & 1: + mask |= 1 << q + cols[3 * i + j] = mask + return cols + + +def projector_ok(x, y): + rows = [[((x >> i) & 1) & ((y >> j) & 1) for j in range(3)] for i in range(3)] + square = [[0] * 3 for _ in range(3)] + for i in range(3): + for j in range(3): + square[i][j] = sum(rows[i][k] & rows[k][j] for k in range(3)) & 1 + return rows == square and dot(x, y) == 1 + + +def diagonal_group_word(indexed_calls): + """Product of diagonal calls (label, role). Independent of the matrix update.""" + acc = [0, 0, 0] + for label, role in indexed_calls: + acc[role] = mul(acc[role], label) + return tuple(acc) + + +def require(condition, message): + if not condition: + raise SystemExit("FAIL " + message) + + +def shortest_frontiers(): + start = identity() + parent = {start.tobytes(): (None, None)} + levels = [ {start.tobytes()} ] + for _depth in range(3): + current = set() + for key in levels[-1]: + cols = array("H") + cols.frombytes(key) + for index, op in enumerate(OPS): + nxt = apply(cols, op).tobytes() + if nxt not in parent: + parent[nxt] = (key, index) + current.add(nxt) + levels.append(current) + return levels + + +def inverses(keys): + found = {} + singular = 0 + for key in keys: + cols = array("H") + cols.frombytes(key) + rows = invert_rows(linear_rows(cols)) + if rows is None: + singular += 1 + continue + found[key] = matrix_from_rows(rows).tobytes() + return found, singular + + +def factor_hits(target, left_keys, right_inverses): + hits = 0 + for inverse_key in right_inverses.values(): + right = array("H") + right.frombytes(inverse_key) + if matmul(target, right).tobytes() in left_keys: + hits += 1 + return hits + + +def main(): + require(mul(mul(mul(S, R), R), S) == 6, "conjugation s r^2 s = r^6") + require(mul(mul(S, R), S) == 3, "semidihedral relation s r s = r^3") + require(mul(mul(S, R), S) != 7, "this is not the dihedral relation s r s = r^{-1}") + require(len({mul(a, b) for a in range(16) for b in range(16)}) == 16, "order") + require(len(IDEMS) == 28 and len(EXCLUDED) == 28, "idempotent count") + require(all(projector_ok(x, y) for x, y in IDEMS), "idempotent algebra") + require(not any(projector_ok(x, y) for x, y in EXCLUDED), "trace-zero exclusion") + require(len(OPS) == 56, "alphabet size") + + triple = [ + (R, *E11), (R, *E11), + (R, *E22), (R, *E22), + (R, *E33), (R, *E33), + ] + target = diagonal(bit(2), bit(2), bit(2)) + witness = word_matrix(triple) + require(list(witness) == list(target), "length-6 matrix identity") + require( + diagonal_group_word([(R, 0), (R, 0), (R, 1), (R, 1), (R, 2), (R, 2)]) == (2, 2, 2), + "length-6 diagonal group word", + ) + square = word_matrix([(R, *E11), (R, *E11)]) + require(list(square) == list(diagonal(bit(2), 1, 1)), "single-coordinate square") + require(list(word_matrix([(S, *E11), (S, *E11)])) == list(identity()), "reflection square is I") + + conjugation = [(S, *E11), (R, *E11), (R, *E11), (S, *E11)] + require( + list(word_matrix(conjugation)) == list(diagonal(bit(6), 1, 1)), + "reflection realizes r^6", + ) + require(diagonal_group_word([(S, 0), (R, 0), (R, 0), (S, 0)]) == (6, 0, 0), "group conjugation") + r4_one = [(R, *E11), (R, *E11), (R, *E11), (R, *E11)] + require(list(word_matrix(r4_one)) == list(diagonal(bit(4), 1, 1)), "four rotations give r^4") + + dropped = word_matrix(triple[:-1]) + relabeled = word_matrix([(S, *E11), *triple[1:]]) + moved = word_matrix(triple[:-1] + [(R, *E11)]) + require(list(dropped) != list(target), "dropped call") + require(list(relabeled) != list(target), "reflection substitution") + require(list(moved) != list(target), "moved projector") + + # Abelianization sums: image in F2[C2 x C2] is I only if both label-sums vanish. + sum_r = [0] * 9 + for label, x, y in triple: + require(label == R, "witness uses only rotations") + for i in range(3): + for j in range(3): + sum_r[3 * i + j] ^= ((x >> i) & 1) & ((y >> j) & 1) + require(sum_r == [0] * 9, "rotation projectors sum to 0") + + dirty = array("H", [bit(5), bit(8), bit(2) ^ bit(8)]) + dirty_matrix = array("H", [0] * 9) + for i in range(3): + dirty_matrix[3 * i] = dirty[i] + require( + list(matmul(witness, dirty_matrix)) == list(matmul(target, dirty_matrix)), + "dirty state follows the identity", + ) + require( + list(matmul(dropped, dirty_matrix)) != list(matmul(target, dirty_matrix)), + "dirty state rejects the dropped word", + ) + + levels = shortest_frontiers() + require([len(level) for level in levels] == [1, 56, 2051, 68899], "shortest frontier sizes") + exact_two = set() + for key in levels[1]: + cols = array("H") + cols.frombytes(key) + for op in OPS: + exact_two.add(apply(cols, op).tobytes()) + require(len(exact_two) == 2052 and identity().tobytes() in exact_two, "exact length-two collapse") + require(target.tobytes() not in levels[1] | levels[2] | levels[3], "target absent through length 3") + require(diagonal(bit(6), 1, 1).tobytes() not in levels[1] | levels[2] | levels[3], "r^6 absent through length 3") + require(diagonal(bit(4), 1, 1).tobytes() not in levels[1] | levels[2] | levels[3], "r^4 absent through length 3") + triple_r = diagonal(bit(1), bit(1), bit(1)) + require(triple_r.tobytes() in levels[3] and triple_r.tobytes() not in levels[2], "generator triple has length 3") + + inverse_two, singular_two = inverses(levels[2]) + require(singular_two == 0 and len(inverse_two) == 2051, "length-two factors invert") + sample_key = next(iter(levels[2])) + sample = array("H") + sample.frombytes(sample_key) + sample_inverse = array("H") + sample_inverse.frombytes(inverse_two[sample_key]) + require(list(matmul(sample, sample_inverse)) == list(identity()), "inverse roundtrip") + + hits4 = factor_hits(target, levels[2], inverse_two) + hits5 = factor_hits(target, levels[3], inverse_two) + generator_hits5 = factor_hits(triple_r, levels[3], inverse_two) + require(hits4 == 0 and hits5 == 0, "no length-4 or length-5 factorization") + require(generator_hits5 == 196, "length-five sieve sees the generator triple") + + inverse_three, singular_three = inverses(levels[3]) + require(singular_three == 0 and len(inverse_three) == 68899, "length-three factors invert") + hits6 = factor_hits(target, levels[3], inverse_three) + r4_hits6 = factor_hits(diagonal(bit(4), bit(4), bit(4)), levels[3], inverse_three) + r6_hits6 = factor_hits(diagonal(bit(6), bit(6), bit(6)), levels[3], inverse_three) + require(hits6 == 799, "length-six factorizations of the triple square") + require(r4_hits6 == 0 and r6_hits6 == 0, "r^4 and r^6 triples absent through length 6") + + r4_triple = [] + r6_triple = [] + for projector in (E11, E22, E33): + r4_triple.extend((R, *projector) for _ in range(4)) + r6_triple.extend((S, *projector) for _ in range(1)) + r6_triple.extend((R, *projector) for _ in range(2)) + r6_triple.extend((S, *projector) for _ in range(1)) + require(list(word_matrix(r4_triple)) == list(diagonal(bit(4), bit(4), bit(4))), "length-12 r^4 upper bound") + require(list(word_matrix(r6_triple)) == list(diagonal(bit(6), bit(6), bit(6))), "length-12 r^6 upper bound") + + print( + "PASS_SD16_TRIPLE_SQUARE length=6 frontier=1,56,2051,68899 " + "exact2=2052 len4=0 len5=0 len6=799 r4_through6=0 r6_through6=0 " + "generator_len5_hits=196 rejected=6 no_new_kappa" + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main())