diff --git a/research/two-full-scratch-monomial-obstruction/PROOF.md b/research/two-full-scratch-monomial-obstruction/PROOF.md new file mode 100644 index 0000000000..0921315952 --- /dev/null +++ b/research/two-full-scratch-monomial-obstruction/PROOF.md @@ -0,0 +1,135 @@ +# Full monomial two-FULL routing still fails with identity scratch + +This note closes the simultaneous extension left open by +`research/two-full-central-obstruction/PROOF.md` and +`research/two-full-central-obstruction/grade/PROOF.md`. Those theorems grant +either arbitrary restored identity scratch, or full monomial routing on +exactly two owners, and they say the two grants are not the same theorem. +The argument below grants both at once for every finite scratch count. +It is an algebra obstruction. It is not a new word, a caller, or a kappa. + +## Setup + +Let G be the finite one-column Clifford group used by the predecessor, +including absolute phases, and let H be its monomial subgroup. Let A be the +twisted group algebra of G and B the twisted group algebra of H, over a +field of characteristic zero. A nonzero scalar cocycle does not affect the +argument: left multiplication by a group unit still permutes the basis up +to nonzero scalars. Fourier rank is subadditive, every element of H has +rank 0, and left multiplication by H preserves rank. + +Label owners `0,...,n-1` with `n>=2`. Owners 0 and 1 are the two main +owners. Owners `2,...,n-1` are identity scratch. Each main transverse +factor `a0,a1,b0,b1` is a pure group unit of rank at most `t`. The source +case is `t=8`. Write `pi` for the projection onto the span of group +elements of rank at most `2t`. It is a B-bimodule map. In the source case +`2t=16`, which is the cutoff in the grade note, because two rank-8 factors +multiply to rank at most 16. + +Each FULL factor is diagonal transverse multiplication after one elementary +shear on the two main owners. For an arbitrary central element `c` in A, +an upper shear is `I + e0 c e1^T` and a lower shear is `I + e1 c e0^T`. +The square of that off-diagonal term is zero, so every such shear is +invertible on `A^n`, with inverse given by the minus sign. The two FULL +factors may use different central elements, different orientations, and +different transverse units. Neither shear reads or writes a scratch owner. + +The interstage matrix `W` and the outer matrices `L,R` lie in `GL_n(B)`. +The same matrix inverse over B remains an inverse over A, because the +product that gives the identity only multiplies elements of B. Thus `W`, +`L` and `R` are K-linear automorphisms of `A^n`. + +The forbidden target is `diag(g0,g1,I,...,I)`, where `g0` and `g1` are pure +group elements of rank greater than `2t`, so `pi(g0)=pi(g1)=0`, and each +scratch coordinate is returned as the identity. + +## The free input subspace + +Let `r` be the main owner read by the first shear, and let `f` be the other +main owner. Let S be the coordinate subspace of `B^n` on which owner `r` +is zero and owner `f` together with every scratch owner is free. Then +`dim_K S = (n-1) dim_K B`. + +On S the first shear acts as the identity, because its only added term +contains the zero coordinate. The first transverse diagonal then multiplies +owner `f` by the unit `a_f` and each scratch owner by 1. That map is +injective, so the first FULL factor is injective on S. + +The image of S has a rigid rank bound. Owner `r` is zero. Owner `f` is +`a_f` times a B-element, hence Fourier rank at most `t`. Each scratch +coordinate is a B-element, hence rank 0. Applying `W`, whose entries have +rank 0, keeps every coordinate at rank at most `t`. Call this intermediate +vector `u`. Every coordinate of `u` is kept by `pi`, and in fact by the +stronger projection onto rank at most `t`. + +## The clean output kills the second shear + +Let `z` be the second FULL factor applied to `u`. Exactly one main output +is untouched by the second shear. That output equals its transverse unit +times the corresponding coordinate of `u`, with no central factor. The +product of a rank-at-most-`t` unit with a rank-at-most-`t` element has rank +at most `2t`, so `pi` keeps it. Vanishing of that projected output is +therefore vanishing of the algebra element, and the transverse unit forces +the corresponding coordinate of `u` to vanish. + +That coordinate is the one the second shear reads. After it vanishes, the +central term is multiplied by zero. The other main output collapses to its +own transverse unit times the remaining main coordinate of `u`, which `pi` +likewise keeps, so that coordinate vanishes too. Each scratch output equals +the corresponding scratch coordinate of `u`. Those coordinates already have +rank at most `t`, so `pi` keeps them and they vanish as well. + +Thus `pi(z)=0` implies `u=0`. Combined with injectivity of `W` and of the +first FULL factor on S, `pi` of the whole product is injective on S. +Therefore + +```text +rank pi(T2 W T1) >= (n-1) dim B. +``` + +The central elements never need a rank bound. They are multiplied only +after the coordinate they read has already been forced to zero. + +## Comparison with the identity-scratch target + +The projected target is `diag(0,0,I,...,I)`. Its image is the scratch +coordinates, so its rank is exactly `(n-2) dim B`. For `n=2` that rank is +zero. Because `pi` is a B-bimodule map and `L,R` are invertible over B, + +```text +rank pi(L T2 W T1 R) = rank pi(T2 W T1) >= (n-1) dim B + > (n-2) dim B = rank pi(diag(g0,g1,I,...,I)). +``` + +The two maps cannot be equal. No choice of orientations, central elements, +rank-at-most-`t` transverse units, monomial interstage, or monomial outer +change removes the gap. The gap is exactly one copy of B, coming from the +main input the first shear does not read. + +For `n=2` the same bound says the projected product has rank at least +`dim B`, while two pure high-rank targets project to zero. That recovers +the two-owner shear obstruction under these hypotheses. It does not replace +the predecessor's separated-address nullity theorem, which allows scratch +but only one nonmonomial update in each FULL factor. + +## All large widths + +The predecessor's separating-polynomial argument says that, for every +sufficiently large tensor width, distinct projective classes remain linearly +independent after the central-character specialization. An identity of +operators at every such width therefore implies the same identity in the +twisted group algebra. The rank gap above is an identity failure in that +algebra, so it forbids an all-width completion. A single small width can +still have an accidental numerical relation. This note does not compute +that width and does not change the predecessor's sufficient bound. + +## What remains out of scope + +A FULL shear that reads a scratch owner is a different macro. The +intermediate rank bound uses that the first shear does not. Transformed +helper states are not identity scratch; the separated-address note already +records that they cannot be absorbed into `L,W,R`. Rank of a transverse +factor above `t`, or a cutoff below `2t`, can drop the clean output and +the proof stops applying. Finite movement, precision, compiler reserve and +outer transfer are not reached, because the endpoint identity already +fails. No conditional kappa is claimed. diff --git a/research/two-full-scratch-monomial-obstruction/README.md b/research/two-full-scratch-monomial-obstruction/README.md new file mode 100644 index 0000000000..ac67b89578 --- /dev/null +++ b/research/two-full-scratch-monomial-obstruction/README.md @@ -0,0 +1,84 @@ +# Identity scratch does not unlock full monomial two-FULL routing + +**Negative result. No new κ.** The selected conditional exponent stays +`62009/20000000`. + +The separated-address theorem allows arbitrary restored identity scratch, and +the grade note allows full monomial routing, but only on exactly two owners. +Neither theorem grants both extensions. For every finite scratch count, the +projected product `T2 W T1` still has rank at least one full copy of the +monomial algebra above `diag(0,0,I,...,I)`. Monomial outer matrices cannot +close that gap. The endpoint `diag(g0,g1,I,...,I)` with two pure high-rank +targets is impossible in this algebra, at every sufficiently large tensor +width. + +## Scope and evidence + +Original card: `complex-new-helper-inventory`. Scope status: **refined**. The +card asks whether one changed helper inventory survives a complete bill. The +request actually taken is open handoff 8 on +[discussion 1166](https://github.com/CrocSwap/integer-mult-bounds/discussions/1166#discussioncomment-19134766), +as sharpened by the scratch question in +[discussion 4902](https://github.com/CrocSwap/integer-mult-bounds/discussions/4902#discussioncomment-19144315). +The predecessor package states that its scratch theorem and its full-monomial +theorem are not simultaneous. This note supplies the missing simultaneous +obstruction. It does not emit a helper word or replay the seventeen-axis group. + +The proof is in [PROOF.md](PROOF.md). The verifier authenticates the two +predecessor proofs, checks the target-rank arithmetic, and corroborates the +subspace rank on a noncommutative graded model (`S3` times an additive rank). +One hundred ninety-two invertible witnesses, both shear orientations, and +central ranks 0, 1 and 30 keep the gap equal to `|S3|`. A scratch-only input +does not produce that extra copy. Zeroing the clean transverse factor, or +cutting off below `2t`, drops the rank. Those controls are part of +[verify.py](verify.py). They are not a Clifford enumeration. + +Source pins are in [SOURCES.json](SOURCES.json). The two live inputs are the +predecessor proofs at the pinned base. They are not copied. + +## Reproduce + +From the repository root, Python 3.12, standard library only: + +```sh +python3 -B scripts/research_package.py --package research/two-full-scratch-monomial-obstruction --check-only +python3 -B scripts/research_package.py --package research/two-full-scratch-monomial-obstruction +``` + +Direct checks, each under two seconds here: + +```sh +python3 -I -W error -B research/two-full-scratch-monomial-obstruction/verify.py +python3 -I -O -W error -B research/two-full-scratch-monomial-obstruction/verify.py +``` + +Both print `PASS_TWO_FULL_SCRATCH_MONOMIAL_OBSTRUCTION` with `gap=6`. No +native suite, Lean build, selected cold replay or compiler/outer replay was +run. + +## Open next questions + +1. Can a FULL shear that reads a scratch owner, rather than only a monomial + matrix between the two macros, avoid the extra copy of B? This matters + because the intermediate rank bound uses that the first shear reads the + other main owner only. The first step is one explicit scratch-reading shear + and its projected image. The blocker is that the native two-FULL schedule + does not contain that macro. + +2. Does one already-transformed helper state, entered as a non-identity + summand, change the projected rank? This matters because identity scratch + is the only extra owner this note allows. The first step is to name that + helper's input operator and price the optimistic bill before any search. + The blocker is the predecessor's warning that such a state is not absorbed + into `L`, `W` and `R`. + +## Credit + +The separated-address nullity theorem and the two-owner full-monomial grade +note are Chris R / Clear1nsight's `research/two-full-central-obstruction`, +including the Cinna Davis / 1337isnot1337, Cao Yifan, zxv, Jacob Alan Sussman, +Ali Romman, Dugongue, Nick Petalas and Richard Hoekstra lineage recorded +there. The scratch question is JoyboyBrian's handoff on discussion 4902. +This package only proves the simultaneous rank gap those results leave open. + +Prepared by unpaidmoon with substantial Cursor Grok assistance. Apache-2.0. diff --git a/research/two-full-scratch-monomial-obstruction/SOURCES.json b/research/two-full-scratch-monomial-obstruction/SOURCES.json new file mode 100644 index 0000000000..44f8e0af17 --- /dev/null +++ b/research/two-full-scratch-monomial-obstruction/SOURCES.json @@ -0,0 +1,30 @@ +{ + "schema_version": 1, + "base_commit": "de5ec2e7a6074068452f45b1a191b5c72832df4a", + "files": [ + { + "path": "certificates/selected-result.json", + "usage": "provenance", + "git_blob": "581f8d87008354eb5b869f7ed9de1317ad837ce8", + "sha256": "2a0220e866417774606e0f991e41b9c1d26b1f43b97857238cf478dbc7d330b5" + }, + { + "path": "docs/research/agent-task-catalog.json", + "usage": "provenance", + "git_blob": "3404fbdd5e7529a0674eef26114b5071e4586cec", + "sha256": "ead6cdc9d0af6d58e8a49744d8ec8c7c15e13d2ba93c276c434bcf9b51b93a81" + }, + { + "path": "research/two-full-central-obstruction/PROOF.md", + "usage": "input", + "git_blob": "66df36c04d0f88018fd640aa095bb55eeacb17e1", + "sha256": "9babe77410543665f197cd1846d71d775d306c97716aedefc6f25f4256f5a699" + }, + { + "path": "research/two-full-central-obstruction/grade/PROOF.md", + "usage": "input", + "git_blob": "4cdf1e1a29b2113e88513f7a7d983f1e52adb6db", + "sha256": "8fd775a9903eca0f53b186d7fb684d9f5f74afc9c105082b3d4981bd950fca33" + } + ] +} diff --git a/research/two-full-scratch-monomial-obstruction/TASK.json b/research/two-full-scratch-monomial-obstruction/TASK.json new file mode 100644 index 0000000000..092c44fadb --- /dev/null +++ b/research/two-full-scratch-monomial-obstruction/TASK.json @@ -0,0 +1,80 @@ +{ + "schema_version": 1, + "task_id": "complex-new-helper-inventory", + "priority_revision": 8, + "base_commit": "de5ec2e7a6074068452f45b1a191b5c72832df4a", + "original_card": { + "id": "complex-new-helper-inventory", + "question": "Can a changed helper inventory earn productive credit that survives completion, compilation and the actual caller?", + "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" + ], + "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": "Specify all arbitrary inputs, owned fields, restored outputs and a physically legal consumer. Compare complete P and T, including target debits and precision/stock costs. #5599 is a positive control for target refinement, not permission to split every helper for free. #5614 is a hypothetical compiler budget, not an emitted 145-split word. Stop at the first unpaid boundary or a failing optimistic full bill.", + "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": [ + "productive helper", + "target refinement", + "5599", + "5614" + ], + "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": "refined", + "actual_scope": "Does full monomial two-FULL routing still obstruct diag(g0,g1,I,...,I) after arbitrarily many restored identity scratch owners are added? Prove the projected rank gap or give a complete three-owner identity.", + "scope_change_reason": "Open handoff 8 on discussion 1166, sharpened by discussion 4902, asks for this simultaneous extension. The catalogue card's helper-inventory question is narrowed to that one algebra obstruction. The predecessor already proved the two extensions separately and said they were not one theorem.", + "bottleneck_link": "The two-FULL invoice 224 H17+2H against 2P is positive before finite work, but only if this endpoint exists. The rank gap says the endpoint does not exist in the stated grammar, so that invoice is not a supplier.", + "budget_minutes": 60, + "stop_rule": "Stop once the simultaneous rank gap is proved and the graded S3 corroboration, including the scratch-only and broken-factor controls, passes. Do not search scratch-reading shears or transformed helper states in this session.", + "outcome": "negative-result", + "evidence": "Written rank comparison in PROOF.md. verify.py authenticates the predecessor proofs and checks 192 invertible witnesses; the gap equals dim S3. No native, Lean, or selected-frontier replay.", + "author": "unpaidmoon", + "ai_assistance": "Substantial Cursor Grok assistance. No independent human or proof-kernel review.", + "open_questions": [ + { + "question": "Can a FULL shear that reads a scratch owner avoid the extra copy of the monomial algebra?", + "why_it_matters": "The intermediate rank bound uses that the first shear reads only the other main owner.", + "first_step": "Write one scratch-reading shear, project T2 W T1, and test whether its rank can equal the identity-scratch target.", + "blocked_by": "The native two-FULL schedule has no such macro, and this session does not invent one." + }, + { + "question": "Does one already-transformed helper state change the projected rank when it is entered as a non-identity summand?", + "why_it_matters": "Identity scratch is the only extra owner this obstruction allows.", + "first_step": "Name the helper input operator and price the optimistic bill before any search.", + "blocked_by": "The predecessor records that a transformed helper state is not absorbed into L, W and R." + } + ] +} diff --git a/research/two-full-scratch-monomial-obstruction/VERIFY.json b/research/two-full-scratch-monomial-obstruction/VERIFY.json new file mode 100644 index 0000000000..de80d9da45 --- /dev/null +++ b/research/two-full-scratch-monomial-obstruction/VERIFY.json @@ -0,0 +1,37 @@ +{ + "schema_version": 1, + "status": "ready", + "python": "3.12", + "requirements": [], + "inputs": [ + "research/two-full-central-obstruction/PROOF.md", + "research/two-full-central-obstruction/grade/PROOF.md" + ], + "commands": [ + { + "argv": [ + "{python}", + "-I", + "-W", + "error", + "-B", + "{package}/verify.py" + ], + "timeout_seconds": 60, + "checks": "Authenticates the two predecessor proofs, checks target-rank arithmetic, and corroborates the subspace gap on S3 with both shear orientations, high-rank central letters, a scratch-only sharpness control, a zeroed clean factor, and a cutoff below 2t." + }, + { + "argv": [ + "{python}", + "-I", + "-O", + "-W", + "error", + "-B", + "{package}/verify.py" + ], + "timeout_seconds": 60, + "checks": "Same corroboration with assertions compiled out; every comparison is an explicit failure." + } + ] +} diff --git a/research/two-full-scratch-monomial-obstruction/verify.py b/research/two-full-scratch-monomial-obstruction/verify.py new file mode 100644 index 0000000000..43f0070b51 --- /dev/null +++ b/research/two-full-scratch-monomial-obstruction/verify.py @@ -0,0 +1,416 @@ +#!/usr/bin/env python3 +"""Corroborate the two-FULL identity-scratch rank gap. Apache-2.0.""" + +import hashlib +import random +from pathlib import Path + +P = 1_000_003 +T = 8 +CUTOFF = 2 * T +ROOT = Path(__file__).resolve().parents[2] +PACKAGE = Path(__file__).resolve().parent +EXPECTED = { + "research/two-full-central-obstruction/PROOF.md": + "9babe77410543665f197cd1846d71d775d306c97716aedefc6f25f4256f5a699", + "research/two-full-central-obstruction/grade/PROOF.md": + "8fd775a9903eca0f53b186d7fb684d9f5f74afc9c105082b3d4981bd950fca33", +} +MARKERS = { + "research/two-full-central-obstruction/PROOF.md": + "no theorem here simultaneously grants both extensions", + "research/two-full-central-obstruction/grade/PROOF.md": + "additional scratch mixing", +} + +# S3 acting on {0,1,2}, indexed so left multiplication is composition. +PERMS = ( + (0, 1, 2), + (1, 0, 2), + (2, 1, 0), + (0, 2, 1), + (1, 2, 0), + (2, 0, 1), +) +D = len(PERMS) + + +def fail(message): + raise SystemExit("FAIL_TWO_FULL_SCRATCH_MONOMIAL_OBSTRUCTION " + message) + + +def mul_perm(left, right): + return tuple(left[right[i]] for i in range(3)) + + +def build_table(): + index = {perm: i for i, perm in enumerate(PERMS)} + table = [[0] * D for _ in range(D)] + for i, left in enumerate(PERMS): + for j, right in enumerate(PERMS): + table[i][j] = index[mul_perm(left, right)] + ident = 0 + if any(table[ident][j] != j or table[j][ident] != j for j in range(D)): + fail("S3 identity") + seen = {table[1][j] for j in range(D)} + if len(seen) != D: + fail("S3 left multiplication is not bijective") + return table + + +TABLE = build_table() + + +def mod(value): + return value % P + + +class Elem: + """Sparse element of K[S3] tensor a nonnegative rank grade.""" + + def __init__(self, terms=None): + self.terms = {} + if terms: + for grade, coeffs in terms.items(): + self._put(grade, coeffs) + + def _put(self, grade, coeffs): + if any(coeffs): + self.terms[grade] = coeffs + + def copy(self): + return Elem({grade: coeffs[:] for grade, coeffs in self.terms.items()}) + + +def zero(): + return Elem() + + +def basis(index, grade=0): + coeffs = [0] * D + coeffs[index] = 1 + return Elem({grade: coeffs}) + + +def add(left, right): + out = left.copy() + for grade, coeffs in right.terms.items(): + acc = out.terms.get(grade, [0] * D)[:] + for i, value in enumerate(coeffs): + acc[i] = mod(acc[i] + value) + if any(acc): + out.terms[grade] = acc + else: + out.terms.pop(grade, None) + return out + + +def left_h(h, elem): + out = zero() + for grade, coeffs in elem.terms.items(): + acc = [0] * D + for j, value in enumerate(coeffs): + if value: + acc[TABLE[h][j]] = mod(acc[TABLE[h][j]] + value) + out._put(grade, acc) + return out + + +def scale_shift(elem, monomial, shift): + """Left multiply by one monomial and add `shift` to every grade.""" + coeff, h = monomial + out = zero() + for grade, coeffs in elem.terms.items(): + acc = [0] * D + for j, value in enumerate(coeffs): + if value: + acc[TABLE[h][j]] = mod(acc[TABLE[h][j]] + coeff * value) + out._put(grade + shift, acc) + return out + + +def b_mul(poly, elem): + out = zero() + for coeff, h in poly: + out = add(out, scale_shift(elem, (coeff, h), 0)) + return out + + +def unit(h, shift): + return (h, shift) + + +def apply_unit(factor, elem): + h, shift = factor + return scale_shift(elem, (1, h), shift) + + +def poly_id(): + return ((1, 0),) + + +def poly_h(h): + return ((1, h),) + + +def shear(vec, dest, src, central): + """vec[dest] += central * vec[src]. central is (h, shift) or None.""" + out = [item.copy() for item in vec] + if central is None: + return out + out[dest] = add(out[dest], apply_unit(central, vec[src])) + return out + + +def diagonal(vec, factors): + return [apply_unit(factor, item) if factor else item.copy() + for factor, item in zip(factors, vec)] + + +def full(vec, factors, orientation, central): + if orientation == "upper": + vec = shear(vec, 0, 1, central) + elif orientation == "lower": + vec = shear(vec, 1, 0, central) + else: + fail("orientation") + return diagonal(vec, factors) + + +def apply_w(matrix, vec): + out = [zero() for _ in vec] + for i, row in enumerate(matrix): + for j, poly in enumerate(row): + out[i] = add(out[i], b_mul(poly, vec[j])) + return out + + +def project(elem, cutoff): + return Elem({grade: coeffs[:] for grade, coeffs in elem.terms.items() + if grade <= cutoff and any(coeffs)}) + + +def pack(vec, cutoff): + grades = cutoff + 1 + flat = [] + for item in vec: + projected = project(item, cutoff) + for grade in range(grades): + flat.extend(projected.terms.get(grade, [0] * D)) + return flat + + +def identity_w(n): + return [[poly_id() if i == j else () for j in range(n)] for i in range(n)] + + +def permutation_w(n, swap): + matrix = [[() for _ in range(n)] for _ in range(n)] + perm = list(range(n)) + i, j = swap + perm[i], perm[j] = perm[j], perm[i] + for col, row in enumerate(perm): + matrix[row][col] = poly_h((col + 1) % D) + return matrix + + +def shear_w(n): + matrix = identity_w(n) + if n > 2: + matrix[0][2] = poly_h(3) + return matrix + + +def random_w(n, rng): + matrix = [] + for _ in range(n): + row = [] + for _ in range(n): + support = rng.randrange(1, 3) + row.append(tuple((rng.randrange(1, P), rng.randrange(D)) + for _ in range(support))) + matrix.append(row) + return matrix + + +def w_is_invertible(matrix): + n = len(matrix) + columns = [] + for col in range(n * D): + owner, index = divmod(col, D) + vec = [zero() for _ in range(n)] + vec[owner] = basis(index) + columns.append(pack(apply_w(matrix, vec), 0)) + return rank_mutable(columns) == n * D + + +def subspace_images(n, first, second, matrix, central_rank, cutoff, + kill_clean=False): + read = 0 if first == "lower" else 1 + free = 1 - read + central = (1, central_rank) + factors = [unit(2, T), unit(4, T)] + [unit(0, 0)] * (n - 2) + columns = [] + owners = [free] + list(range(2, n)) + clean = 0 if second == "lower" else 1 + for owner in owners: + for index in range(D): + vec = [zero() for _ in range(n)] + vec[owner] = basis(index) + if owner == read: + fail("subspace contains the read owner") + image = full(vec, factors, first, central) + image = apply_w(matrix, image) + if second == "upper": + image = shear(image, 0, 1, central) + else: + image = shear(image, 1, 0, central) + for owner_index, factor in enumerate(factors): + if kill_clean and owner_index == clean: + image[owner_index] = zero() + else: + image[owner_index] = apply_unit(factor, image[owner_index]) + columns.append(pack(image, cutoff)) + return columns + + +def scratch_only_images(n, first, second, matrix, central_rank, cutoff): + central = (1, central_rank) + factors = [unit(2, T), unit(4, T)] + [unit(0, 0)] * (n - 2) + columns = [] + for owner in range(2, n): + for index in range(D): + vec = [zero() for _ in range(n)] + vec[owner] = basis(index) + image = full(vec, factors, first, central) + image = apply_w(matrix, image) + image = full(image, factors, second, central) + columns.append(pack(image, cutoff)) + return columns + + +def target_rank(n): + columns = [] + for owner in range(2, n): + for index in range(D): + vec = [zero() for _ in range(n)] + vec[owner] = basis(index) + columns.append(pack(vec, CUTOFF)) + if not columns: + return 0 + return rank_mutable(columns) + + +def rank_mutable(columns): + if not columns: + return 0 + matrix = [list(column) for column in zip(*columns)] + width = len(columns) + pivot = 0 + for row_index, row in enumerate(matrix): + if pivot >= width: + break + source = None + for col in range(pivot, width): + if row[col] % P: + source = col + break + if source is None: + continue + if source != pivot: + for values in matrix: + values[pivot], values[source] = values[source], values[pivot] + row = matrix[row_index] + inv = pow(row[pivot] % P, P - 2, P) + for values in matrix: + values[pivot] = mod(values[pivot] * inv) + for other_index, other in enumerate(matrix): + if other_index == row_index or not other[pivot] % P: + continue + factor = other[pivot] % P + for col in range(pivot, width): + other[col] = mod(other[col] - factor * row[col]) + pivot += 1 + return pivot + + +def check_sources(): + for path, digest in EXPECTED.items(): + data = (ROOT / path).read_bytes() + actual = hashlib.sha256(data).hexdigest() + if actual != digest: + fail("sha256 " + path) + text = data.decode("utf-8") + if MARKERS[path] not in text: + fail("marker " + path) + + +def check_dimensions(): + if not (T <= CUTOFF and 8 + 8 <= CUTOFF): + fail("cutoff does not keep two transverse factors") + if target_rank(2) != 0 or target_rank(3) != D or target_rank(4) != 2 * D: + fail("target rank") + if (3 - 1) * D <= (3 - 2) * D: + fail("gap arithmetic") + + +def witness_family(n, rng): + family = [identity_w(n), shear_w(n), permutation_w(n, (0, 1))] + if n > 2: + family.append(permutation_w(n, (1, 2))) + produced = 0 + while produced < 4: + candidate = random_w(n, rng) + if w_is_invertible(candidate): + family.append(candidate) + produced += 1 + for matrix in family: + if not w_is_invertible(matrix): + fail("structured witness is singular") + return family + + +def run(): + check_sources() + check_dimensions() + rng = random.Random(5491) + orientations = ("lower", "upper") + gaps = [] + witnesses = 0 + for n in (3, 4): + for matrix in witness_family(n, rng): + for first in orientations: + for second in orientations: + for central_rank in (0, 1, 30): + columns = subspace_images( + n, first, second, matrix, central_rank, CUTOFF) + got = rank_mutable(columns) + expect = (n - 1) * D + if got != expect: + fail(f"subspace rank {got} != {expect} n={n}") + gap = got - (n - 2) * D + if gap != D: + fail("gap") + gaps.append(gap) + witnesses += 1 + scratch = scratch_only_images( + n, "lower", "upper", identity_w(n), 30, CUTOFF) + if rank_mutable(scratch) != (n - 2) * D: + fail("scratch-only sharpness") + killed = subspace_images( + n, "lower", "upper", identity_w(n), 30, CUTOFF, + kill_clean=True) + if rank_mutable(killed) >= (n - 1) * D: + fail("zero clean transverse factor still looked injective") + low_cut = subspace_images(3, "lower", "upper", identity_w(3), 1, T - 1) + if rank_mutable(low_cut) == 2 * D: + fail("cutoff below 2t unexpectedly preserved the rank") + print( + "PASS_TWO_FULL_SCRATCH_MONOMIAL_OBSTRUCTION " + f"witnesses={witnesses} gap={D} group=S3 " + f"transverse_rank={T} cutoff={CUTOFF}" + ) + + +if __name__ == "__main__": + run()