diff --git a/research/odd-cycle-fit-rigidity/PROOF.md b/research/odd-cycle-fit-rigidity/PROOF.md new file mode 100644 index 0000000000..e8cc272d30 --- /dev/null +++ b/research/odd-cycle-fit-rigidity/PROOF.md @@ -0,0 +1,147 @@ +# Rigidity of minimum-rank width-two odd-cycle fits + +produced by Nick Petalas. Apache-2.0. + +## Statement + +Let F be any field, n=2k+1 with k>=2, and let M be a 2n by 2n matrix +partitioned into two by two blocks indexed by Z/n. Require + + M_ii = I_2, + M_ij = 0 when i and j are distinct and nonadjacent on C_n. + +The two ordered blocks on each edge are arbitrary. They need not be +symmetric or commute. JoyboyBrian's PR5023 gives the minimum rank n for this +contract, as a special case of its unequal-width theorem. We use that rank +value as a cited predecessor result. + +Define D on the two ports (i,left) and (i,right) of every vertex. Its diagonal +blocks are I_2. Its only off-diagonal nonzero entries are + + D[(i,right),(i+1,left)] = D[(i+1,left),(i,right)] = 1. + +**Theorem.** If rank(M)=n, there are invertible two by two matrices L_i over +F such that, for L=diag(L_0,...,L_(n-1)), + + M = L D L^-1. + +Conversely every such matrix fits the contract and has rank n. Thus all +minimum-rank fits form one orbit under independent changes of basis at the +vertices. This classifies the actual fitting matrices, not just the rank value +or one attaining construction. Ordinary transpose, never conjugate transpose, +is used in the argument. + +## Adjacent intersection lines + +Factor M=A B^T through F^n, with A and B of size 2n by n and full column +rank. Let U_i and W_i be the row spaces of their two-row blocks A_i and B_i. +The identity A_i B_i^T=I_2 makes both spaces two-dimensional and their mutual +coordinate-bilinear pairing perfect. Forbidden blocks make U_i orthogonal to +W_j whenever i and j are distinct nonneighbors. + +For nonadjacent i,j, U_i intersect U_j is zero. A vector in the intersection +annihilates W_i by its membership in U_j, and perfect pairing on U_i forces +it to vanish. The same statement holds for the W spaces. + +Fix an edge {i,i+1}. The k-1 vertices + + J = {i+3,i+5,...,i+2k-1}, with indices modulo n, + +are independent and are nonadjacent to both endpoints. Their W_j form a direct +sum of dimension 2(k-1). Indeed, pairing a relation with U_j kills every other +summand and then perfect pairing kills its own summand. Both U_i and U_(i+1) +annihilate this direct sum. Therefore + + dim(U_i + U_(i+1)) <= n - 2(k-1) = 3, + dim(U_i intersect U_(i+1)) >= 1. + +Inside U_i the two intersections with its neighbors are disjoint, because +i-1 and i+1 are nonadjacent. Both have dimension at least one and U_i has +dimension two. Consequently, with H_i=U_i intersect U_(i+1), + + dim H_i = 1, + U_i = H_(i-1) direct-sum H_i. (1) + +This extends the C5 equality argument of Nick Petalas's PR3248. The independent +set J is the additional step for every longer odd cycle. Its use is also +consistent with the rotated one-edge-plus-isolates inequalities of PR5023. + +## A dual edge basis and local similarity + +Choose nonzero h_i in H_i. The row h_i annihilates W_j unless j is i or i+1. +For every other vertex j, at least one of those two endpoints is a nonneighbor +of j, which proves this assertion from the ordered zero blocks. + +The n rows h_i are independent. In a relation sum c_i h_i=0, pairing with W_j +leaves only c_(j-1)h_(j-1)+c_j h_j. This vector lies in U_j and annihilates +W_j, so perfect pairing makes it zero. Equation (1) then makes both +coefficients zero. This holds for every j. + +Let g_i be the dual row basis, h_i g_j^T=delta_ij. Each W_j annihilates all +h_i except its two incident edges, and has dimension two. Hence + + W_j = span(g_(j-1),g_j). + +For invertible matrices L_i and T_i we can therefore write + + A_i = L_i [h_(i-1); h_i], + B_i = T_i [g_(i-1); g_i]. + +The diagonal identity is L_i T_i^T=I_2, so T_i^T=L_i^-1. Pairing the displayed +dual edge bases gives exactly the block D_ij. Thus + + M_ij = L_i D_ij L_j^-1, + +which proves the claimed block-diagonal similarity over every field. + +## Spectral and local consequences + +A permutation of ports writes D as n disjoint copies of the all-ones two by +two matrix J_2. Each copy has rank one over every field and J_2^2=2J_2. +This proves the converse, and also proves + + M^2 = 2 M, + characteristic polynomial = x^n (x-2)^n. + +If char(F) is not two, M is diagonalizable with eigenvalues zero and two, each +of multiplicity n. If char(F)=2, M is square-zero with rank n, so its Jordan +form consists of exactly n nilpotent blocks of size two. No eigenvalue or +Jordan claim requires F to be algebraically closed because these forms split +over F itself. + +Every ordered edge block has rank one. At each vertex the two return maps + + P_i^- = M_(i,i-1) M_(i-1,i), + P_i^+ = M_(i,i+1) M_(i+1,i) + +are complementary rank-one idempotents. Products along two distinct consecutive +edges vanish, in either orientation. These are necessary structural conditions +on a proposed optimal fit. They do not impose symmetry in the original bases. + +The normal form is constructive. Choose a nonzero vector ell_i in im(P_i^-), +and put r_i=M_(i,i+1) ell_(i+1). Then L_i=[ell_i,r_i] is invertible and gives +the displayed similarity. The canonical form proves this assertion directly: +ell_i is a nonzero multiple of its left basis vector, and r_i has exactly the +matching scale at the other end of edge i. Each edge can be normalized +independently; no cyclic scalar consistency condition remains. + +## Hypotheses and limits + +Odd length is essential for this rigidity. On any even cycle of length at +least six, put an I_2 block on each ordered edge of a perfect matching, zero +on all other edges and I_2 on the diagonal. Its rank is n, but its ordered +edge ranks alternate between two and zero. Local similarity preserves these +ranks, so it cannot be equivalent to D. The independent-set lower bound makes +this a minimum-rank even-cycle example. + +The rank hypothesis is also essential. Replace one shared pair J_2 in D by +I_2. The odd-cycle mask still fits, its rank is n+1, and its nonzero identity +summands violate M^2=2M in every field. This is a boundary example, not an +error in the predecessor minimum-rank theorem. + +The proof does not classify unequal widths, higher-rank fits or other masks. +It gives no physical basis-change routine, complete-plane binary pairing +tariff, native caller, dirty-state restoration or recursive bill. It changes +neither kappa nor the selected frontier. Finite verification corroborates the +written all-field argument; independent human and proof-kernel review remain +unclaimed. diff --git a/research/odd-cycle-fit-rigidity/README.md b/research/odd-cycle-fit-rigidity/README.md new file mode 100644 index 0000000000..e53c1b9587 --- /dev/null +++ b/research/odd-cycle-fit-rigidity/README.md @@ -0,0 +1,133 @@ +# Every optimal width-two odd-cycle fit has one local normal form + +For every field and every odd n>=5, every rank-n fitting matrix on the cycle +C_n with identity two by two diagonal blocks is block-diagonally similar to +the canonical one-coordinate-per-edge construction. Thus every optimum obeys +`M^2=2M`. In characteristic two its Jordan form is exactly n nilpotent blocks +of size two. In other characteristics it is diagonalizable with eigenvalues +zero and two, each n times. + +Claim type: scoped mathematical proof with exact finite corroboration. This +contribution was produced by Nick Petalas. Independent human and formal +proof-kernel review are unclaimed. + +JoyboyBrian's [PR5023](https://github.com/CrocSwap/integer-mult-bounds/pull/5023) +already proves the rank value n as part of its unequal-payload theorem. This +package classifies **all attaining matrices**. It uses that rank value as a +cited premise and does not replay the predecessor's capacity computations. +The [proof](PROOF.md) extends Nick Petalas's older +[PR3248](https://github.com/CrocSwap/integer-mult-bounds/pull/3248) C5 edge-line +argument to all longer odd cycles, then obtains the local similarity and the +spectral identity from a dual edge basis. It permits arbitrary directed edge +blocks over any field, without symmetry, positivity or commuting-block +assumptions. + +## Concrete contract and consequence + +Write the two ports at vertex i as left and right. The canonical matrix D +has diagonal I_2 and one coefficient in each direction between right at i +and left at i+1. All other off-diagonal coefficients are zero. The theorem is + + rank(M)=n implies M=L D L^-1, L=diag(L_0,...,L_(n-1)), L_i invertible. + +A port permutation makes D a direct sum of n all-ones two by two matrices. +Every ordered edge block of an optimum has rank one; the two edge return maps +at each vertex are complementary rank-one projectors, and every directed +product along two distinct consecutive edges vanishes. These conditions can +reject a proposed optimum before a decoder construction is priced. + +The width-two and odd-length hypotheses remain explicit. Even-cycle optima +with alternating rank-two and zero edge blocks defeat the normal form. +Rank-(n+1) odd-cycle fits need not obey the quadratic identity. Both boundaries +are executable controls. + +## Reproduction and exact evidence + +From the repository root, use CPython 3.12 with its standard library: + +```sh +/Users/nick/git/integer-mult-bounds/.venv/bin/python3.12 -I -W error -B research/odd-cycle-fit-rigidity/verify.py +/Users/nick/git/integer-mult-bounds/.venv/bin/python3.12 -I -O -W error -B research/odd-cycle-fit-rigidity/verify.py +/Users/nick/git/integer-mult-bounds/.venv/bin/python3.12 -B scripts/research_package.py --package research/odd-cycle-fit-rigidity +/Users/nick/git/integer-mult-bounds/.venv/bin/python3.12 -B scripts/check_research_commit.py --package research/odd-cycle-fit-rigidity --check-source-history +``` + +Any CPython 3.12 interpreter can replace that local path. Each direct verifier +prints a hash-bound evidence summary and `PASS_ODD_CYCLE_FIT_RIGIDITY`, and +writes no files. [RESULT.json](RESULT.json) is generated by the verifier and +binds every package source and declared predecessor input. The separate +`--write-result PATH` option writes a fresh receipt outside the package. + +The bounded replay checks: + +- 200 exact fixtures over Q, GF(2), GF(3), GF(5) and GF(101), at cycle lengths + 5, 7, 9, 11 and 13, including rational nonintegral basis changes. +- All 7,776 local GL2(GF(2)) basis choices on C5. Their matrices are distinct; + direct reconstruction finds the canonical form and independent column-bit + elimination checks their rank. +- Every binary assignment to two ordered edge blocks of the canonical C5 or + C7, for every choice of the two slots. Among 11,520 assignments on C5, + 55 have rank five; among 23,296 assignments on C7, 105 have rank seven. + Every such optimum passes reconstruction and the local/spectral identities. + The counts enumerate assignments, including repeated matrices. They are + exhaustive only for this specified two-block completion domain. +- 22 rejection controls, covering rank slack in five fields, even-cycle + boundaries, forbidden and nonidentity blocks, malformed coefficients, + matrix capacities and an unsupported modulus. + +The all-field, all-odd-length result rests on the written proof. The finite +checks corroborate it and exercise the constructive normalizer; they do not +form an exhaustive census of all fitting matrices. + +## Scope, credit and family decision + +This changes the `complex-rank-shape-decoder` card to a structural side result +on an exact read-mask interface. It adds no new query-capacity value, physical +decoder tariff or kappa. It does not extend or recheck the recent bipartite +capacity computation. The selected frontier is unchanged. Canonical base is +`a5df48c55ea806d7952b65acc9c76d878ff09b13`, priority revision 7. +The [advance claim](https://github.com/CrocSwap/integer-mult-bounds/discussions/1166#discussioncomment-19144836) +records the target. Initial cached overlap covered 331 open PRs and 4,230 +Discussion records; 6,756 of 6,918 tracked notes were omitted by the sparse +local search. Relevant predecessor notes and main-tree/merged searches were +read separately. This coverage does not establish novelty for unpublished work. + +Credit JoyboyBrian #5023 for the all-field unequal-cycle rank theorem and the +rotated subspace inequalities; Nick Petalas #3248 for the C5 equality/dual-basis +argument; Tekalign #3362 and Sagar Thakrar #2484/#3232 for the cycle-mask and +decoder lineage. Their existing notices and frozen sources are unchanged. +New proof, constructive normalizer and finite checks are produced by Nick +Petalas. Apache-2.0. No literature priority is asserted for standard matrix +factorization, duality, change of basis or Jordan-form facts. + +Family decision: finish this classification and stop. Physical acquisition, +copies, numerical conditioning of the local bases, dirty restoration, binary +complete-plane tariffs, ownership, hosting, preparation, stock/precision, +tapes, compiler and outer recovery remain unpriced. A linear similarity is +not a free physical operation. No supplier or complete multiplication theorem +is supplied. + +## Open next questions + +Can an explicit minimum-rank unequal-width odd-cycle fit fail to be locally +equivalent to an integral edge/private-channel construction? Start with the +smallest width vectors for which the half-total bound has slack, inspect the +neighbor-intersection dimensions, and compare local similarity invariants. +The present dimension-two equality argument does not classify those cases. +Do not assume that finding a normal form pays for its physical realization. + +## Verification implementation limits + +The executable is intentionally bounded to matrices of at most 26 by 26 and +the five stated coefficient fields; the proof has no such finite restriction. +Input coefficients have numerator and denominator bit-length at most 256. +Python exact arithmetic allocates dynamically, a power-of-ten rule 3 +deviation. Live matrices have fixed bounded dimensions; the only retained +census set has 7,776 encodings of 100 bytes. The three source inputs are +fixed hash-bound documents. No recursion, solver, subprocess or background +work is used. Negative-control callbacks have three fixed call targets. +Normal and optimized registered runs retain every check. + +The isolated script adds only its fixed package directory before importing +its sibling module. The single E402 annotation documents that required import +order; the remaining selected Ruff diagnostics are enabled without exclusions. diff --git a/research/odd-cycle-fit-rigidity/RESULT.json b/research/odd-cycle-fit-rigidity/RESULT.json new file mode 100644 index 0000000000..b005c4bd3a --- /dev/null +++ b/research/odd-cycle-fit-rigidity/RESULT.json @@ -0,0 +1,36 @@ +{ + "claim_type": "scoped proof with exact finite corroboration", + "evidence": { + "binary_gauge_matrices": 7776, + "completion_digest": "a8915e04186cc3644afe59f54f428acf5e6ee8627567beae6d8ec651b9604097", + "field_fixtures": 200, + "negative_controls": 22, + "two_block_completions": [ + { + "minimum_rank": 55, + "n": 5, + "tested": 11520 + }, + { + "minimum_rank": 105, + "n": 7, + "tested": 23296 + } + ] + }, + "input_sha256": { + "research/c5-rank-tight-asymmetric/README.md": "446f0ed24183a1ae44672356d2b244ae60941b4a7449ad27c5dccdccd662851c", + "research/odd-cycle-query-capacity/PROOF.md": "fc99f405abd5df1c3f88563826f02b53783bba464d1a1852b5093126bf42637a", + "research/odd-cycle-query-capacity/README.md": "7da79bbc83e6612e094fc040d6857343c97254ee10415a386b86406413350961" + }, + "package_sha256": { + "PROOF.md": "a532f16fd77bc5b0c4834766461b8cf445b5ec9adf0d985107b7ef052a1e4f04", + "README.md": "5757438df68c47f495f89897d3ec7b3ba12dc0f37a1b6b0df756fffac2eb9923", + "SOURCES.json": "0f08866b2ed22d7bd9fff1074f5f142d26507ed94b0016c207d97d54447482c8", + "TASK.json": "9a6af80a7f200b9e05772d1199ae7cfdfbd1602cd62c93f2df89bedc742ca9c7", + "VERIFY.json": "54aeb8bd5ab38ad9512cd12ff48196da44665bb44884652a7f2e8a48c73384bf", + "linear.py": "5c7ade5f340affd4dec33ffdb943f41d8e88a8b764c761fdbe8db016d6b9aab5", + "verify.py": "7b463aa2ad035fde5f2f148d8e7fa59c5da07eac9b2cface434dcaec38d0e123" + }, + "schema_version": 1 +} diff --git a/research/odd-cycle-fit-rigidity/SOURCES.json b/research/odd-cycle-fit-rigidity/SOURCES.json new file mode 100644 index 0000000000..a857fa7c71 --- /dev/null +++ b/research/odd-cycle-fit-rigidity/SOURCES.json @@ -0,0 +1,36 @@ +{ + "schema_version": 1, + "base_commit": "a5df48c55ea806d7952b65acc9c76d878ff09b13", + "files": [ + { + "path": "certificates/selected-result.json", + "usage": "provenance", + "git_blob": "c35482cd9035843b15a634cc8276a7e1e40e8395", + "sha256": "6fe0a099b89aa2aed796a13c3a21dafc63f3388ebf397464e44e0451893b9375" + }, + { + "path": "docs/research/agent-task-catalog.json", + "usage": "provenance", + "git_blob": "f126dd2e2dad2b669e75d1d0d0bd05c600725602", + "sha256": "f7a48bfa2659b88378282a8eee7d9fe52cdf505594124c4d1686b58510fffd72" + }, + { + "path": "research/c5-rank-tight-asymmetric/README.md", + "usage": "input", + "git_blob": "f123c33527fc4f5e7f2ae3bfebbb438352c08316", + "sha256": "446f0ed24183a1ae44672356d2b244ae60941b4a7449ad27c5dccdccd662851c" + }, + { + "path": "research/odd-cycle-query-capacity/PROOF.md", + "usage": "input", + "git_blob": "5130e487b8f4e7c3f49cd4a654fe3fc51ce900b5", + "sha256": "fc99f405abd5df1c3f88563826f02b53783bba464d1a1852b5093126bf42637a" + }, + { + "path": "research/odd-cycle-query-capacity/README.md", + "usage": "input", + "git_blob": "55514f7ff0e239472ed2ca9c872c4cb2b8c53968", + "sha256": "7da79bbc83e6612e094fc040d6857343c97254ee10415a386b86406413350961" + } + ] +} diff --git a/research/odd-cycle-fit-rigidity/TASK.json b/research/odd-cycle-fit-rigidity/TASK.json new file mode 100644 index 0000000000..567668f999 --- /dev/null +++ b/research/odd-cycle-fit-rigidity/TASK.json @@ -0,0 +1,86 @@ +{ + "schema_version": 1, + "task_id": "complex-rank-shape-decoder", + "priority_revision": 7, + "base_commit": "a5df48c55ea806d7952b65acc9c76d878ff09b13", + "original_card": { + "id": "complex-rank-shape-decoder", + "question": "Can a decoder deliver the local paid mass in fewer, larger-rank children (block frame climbs instead of rank-1 to rank-3 steps)?", + "starting_points": [ + "docs/research/research-family-status.md", + "research/complex-local-word-ceiling/README.md", + "research/complex-decoder-field-extension-seed44/README.md", + "research/complex-frame-pair-decoders/README.md", + "research/selected-k8-unrestricted-ends/README.md", + "research/eisenstein-reduction-copy-floor/README.md" + ], + "methods": [ + "Find one exact small decoding identity whose frame path climbs in blocks, and price its complete per-call profile.", + "Bound the best effective child rank achievable for one fixed support alphabet, including copies, frames and restoration." + ], + "acceptance_gate": "A per-call local profile, with all copied information, frames and restoration counted, whose ceiling exceeds the selected word's by at least 5%; masked or overcomplete supports must change an assumption of the eight-term all-pairs bound. A scoped local result may be retained without a complete supplier. Expanding it into further construction rounds requires the family-level budget and obligation gate in research-family-status.md; an optimistic passing relaxation alone is not acceptance.", + "first_step": { + "command": [ + "python3", + "-B", + "research/complex-local-word-ceiling/verify.py" + ], + "expected": "PASS_COMPLEX_LOCAL_WORD_CEILING", + "inspect": "Read the selected word's per-call local profile, its packing ceiling and the lever table; run levers.py for the ideal budget of the lever you target.", + "scope": "Positive control and planning budget only; this is not a new construction or full cold replay." + }, + "first_experiment": "Before any synthesis, write the candidate per-call local profile (and centre charge / unpaid coordinates if changed) and screen it with ceiling.ceiling(); reject it if even the ideal ceiling does not beat the selected word's by your stated margin. Ideal budget: rank-16 delivery is 2.31x (levers.py).", + "bottleneck_link": "Rank-1 children are about half of recursive cost; the selected word's effective child rank is about 3.9. Larger-rank delivery lowers recursive cost per unit of paid mass.", + "allowed_variations": [ + "One masked, overcomplete or block decoder family with exact labels and supports.", + "A scoped lower bound on effective rank for one alphabet.", + "If the coverage law, template or guard changes, derive an applicable fully charged replacement bound instead of applying the incumbent ceiling outside its scope." + ], + "scope_change_triggers": [ + "Repacking idle remainders of an unchanged word (covered by the packing ceiling).", + "Repeating the eight-term all-pairs search inside its proved scope.", + "Only changing helper terminal subspaces and monotone frames within the fixed scalar word, entrances and cleanup contract already covered by #1995/#2013.", + "Searching below C=40 in the #2017 Eisenstein binary scalar-rectangle or fixed-support block-linear additive pairing model; specify a changed premise instead. This bound does not cover every complex decoder.", + "Continuing a partial cost improvement while leaving the same unpriced consumer, restoration or central-readout obligation unresolved; refresh the family expansion decision first. Local product-retiming evidence has a narrow scope and is not a ban on changed graphs or contracts." + ], + "overlap_terms": [ + "rank shape", + "block climb", + "masked decoder", + "overcomplete", + "effective rank" + ] + }, + "scope_status": "changed", + "actual_scope": "All-field local-similarity classification and spectral rigidity of every minimum-rank width-two odd-cycle fitting matrix", + "scope_change_reason": "Classify all equality cases of JoyboyBrian PR5023 rather than computing another query-capacity value; extend the older PR3248 C5 edge-line argument to longer odd cycles.", + "bottleneck_link": "Structural necessary conditions can reject proposed optimal local read-mask fits before physical decoder pricing; this is a side result with no supplier admission.", + "budget_minutes": 60, + "stop_rule": "Stop at the first verified classification; do not expand into unequal widths or physical construction.", + "outcome": "completed", + "evidence": "PROOF.md establishes universal coverage; RESULT.json binds the finite fixtures, two-block completion census, normal-form reconstruction and rejection controls.", + "author": "Nick Petalas", + "ai_assistance": "Independent human and formal proof-kernel review are unclaimed.", + "open_questions": [ + { + "question": "Can an unequal-width odd-cycle optimum fail to be locally equivalent to integral edge/private channels?", + "why_it_matters": "The present rigidity statement prevents new width-two optimum shapes but does not settle unequal payload interfaces.", + "first_step": "Inspect neighbor-intersection dimensions and local similarity invariants on the smallest half-total-slack width vectors.", + "blocked_by": "An explicit fitting matrix and equality structure outside the two-dimensional argument." + } + ], + "coordination": { + "claim": "https://github.com/CrocSwap/integer-mult-bounds/discussions/1166#discussioncomment-19144836", + "base": "a5df48c55ea806d7952b65acc9c76d878ff09b13", + "checked_at": "2026-10-23T11:40:05Z", + "open_prs": 331, + "discussion_records": 4230, + "local_notes_searched": 162, + "local_notes_in_head": 6918, + "limitation": "Sparse local corpus; no exhaustive novelty guarantee for historical diffs or unpublished work." + }, + "orientation": { + "historical_card_control": "complex-local-word-ceiling/verify.py fails with KeyError complex_profile against the changed selected schema; no shared change made.", + "current_control": "research_selected_baseline.py passes source-bound metadata orientation only; full selected replay unrun." + } +} diff --git a/research/odd-cycle-fit-rigidity/VERIFY.json b/research/odd-cycle-fit-rigidity/VERIFY.json new file mode 100644 index 0000000000..963d1c3b0b --- /dev/null +++ b/research/odd-cycle-fit-rigidity/VERIFY.json @@ -0,0 +1,38 @@ +{ + "schema_version": 1, + "status": "ready", + "python": "3.12", + "requirements": [], + "inputs": [ + "research/c5-rank-tight-asymmetric/README.md", + "research/odd-cycle-query-capacity/PROOF.md", + "research/odd-cycle-query-capacity/README.md" + ], + "commands": [ + { + "argv": [ + "{python}", + "-I", + "-W", + "error", + "-B", + "{package}/verify.py" + ], + "timeout_seconds": 120, + "checks": "Exact normal-form reconstruction, bounded field/gauge/completion fixtures, source-bound receipt and hypothesis/malformed-input controls." + }, + { + "argv": [ + "{python}", + "-I", + "-O", + "-W", + "error", + "-B", + "{package}/verify.py" + ], + "timeout_seconds": 120, + "checks": "Exact normal-form reconstruction, bounded field/gauge/completion fixtures, source-bound receipt and hypothesis/malformed-input controls." + } + ] +} diff --git a/research/odd-cycle-fit-rigidity/linear.py b/research/odd-cycle-fit-rigidity/linear.py new file mode 100644 index 0000000000..fa21782a88 --- /dev/null +++ b/research/odd-cycle-fit-rigidity/linear.py @@ -0,0 +1,145 @@ +"""Exact bounded linear algebra for the width-two odd-cycle normal form. + +produced by Nick Petalas. Apache-2.0. +""" + +from fractions import Fraction + +FIELDS = (0, 2, 3, 5, 101) +MAX_SIZE = 26 + + +def check(condition, message): + if not condition: + raise ValueError(message) + + +def scalar(value, prime): + return value % prime if prime else Fraction(value) + + +def inverse(value, prime): + check(value != 0, "zero pivot") + return pow(value, -1, prime) if prime else 1 / Fraction(value) + + +def matrix(data, prime): + check(type(prime) is int and prime in FIELDS, "unsupported field") + check(0 < len(data) <= MAX_SIZE, "row capacity") + width = len(data[0]) + check(0 < width <= MAX_SIZE, "column capacity") + result = [] + for row in data: + check(len(row) == width, "ragged matrix") + for value in row: + check(type(value) in (int, Fraction), "nonexact coefficient") + check(not prime or type(value) is int, "noninteger residue") + q = Fraction(value) + check(q.numerator.bit_length() <= 256, "numerator capacity") + check(q.denominator.bit_length() <= 256, "denominator capacity") + result.append(tuple(scalar(value, prime) for value in row)) + return tuple(result) + + +def multiply(left, right, prime): + check(len(left[0]) == len(right), "product dimensions") + return tuple( + tuple( + scalar(sum(a * b for a, b in zip(row, column, strict=True)), prime) + for column in zip(*right, strict=True) + ) + for row in left + ) + + +def rank(data, prime): + rows = [list(row) for row in data] + pivot = 0 + for column in range(len(rows[0])): + chosen = next((j for j in range(pivot, len(rows)) if rows[j][column]), None) + if chosen is None: + continue + rows[pivot], rows[chosen] = rows[chosen], rows[pivot] + scale = inverse(rows[pivot][column], prime) + rows[pivot] = [scalar(x * scale, prime) for x in rows[pivot]] + for j in range(pivot + 1, len(rows)): + coefficient = rows[j][column] + rows[j] = [ + scalar(x - coefficient * y, prime) + for x, y in zip(rows[j], rows[pivot], strict=True) + ] + pivot += 1 + if pivot == len(rows): + break + return pivot + + +def inverse2(data, prime): + (a, b), (c, d) = data + determinant = scalar(a * d - b * c, prime) + scale = inverse(determinant, prime) + return tuple( + tuple(scalar(scale * x, prime) for x in row) for row in ((d, -b), (-c, a)) + ) + + +def block(data, i, j): + return tuple(tuple(data[2 * i + a][2 * j + b] for b in range(2)) for a in range(2)) + + +def diagonal(blocks): + size = 2 * len(blocks) + return tuple( + tuple( + blocks[i // 2][i % 2][j % 2] if i // 2 == j // 2 else 0 for j in range(size) + ) + for i in range(size) + ) + + +def canonical(n): + check(type(n) is int and 3 <= n <= MAX_SIZE // 2, "cycle capacity") + rows = [[int(i == j) for j in range(2 * n)] for i in range(2 * n)] + for i in range(n): + a, b = 2 * i + 1, 2 * ((i + 1) % n) + rows[a][b] = rows[b][a] = 1 + return tuple(tuple(row) for row in rows) + + +def validate_fit(data): + size = len(data) + check(size % 2 == 0 and size == len(data[0]), "square width-two fit required") + n = size // 2 + check(n >= 5 and n % 2 == 1, "odd cycle of length at least five required") + for i in range(n): + for j in range(n): + if i == j: + check(block(data, i, j) == ((1, 0), (0, 1)), "identity block") + elif (i - j) % n not in (1, n - 1): + check(block(data, i, j) == ((0, 0), (0, 0)), "forbidden ordered block") + return n + + +def normal_form(data, prime): + data = matrix(data, prime) + n = validate_fit(data) + check(rank(data, prime) == n, "minimum-rank hypothesis") + left_vectors = [] + for i in range(n): + previous = (i - 1) % n + projector = multiply(block(data, i, previous), block(data, previous, i), prime) + columns = tuple(zip(*projector, strict=True)) + chosen = next((column for column in columns if any(column)), None) + check(chosen is not None, "missing left edge line") + left_vectors.append(chosen) + bases = [] + for i in range(n): + following = (i + 1) % n + column = tuple((x,) for x in left_vectors[following]) + right = multiply(block(data, i, following), column, prime) + bases.append(tuple((left_vectors[i][j], right[j][0]) for j in range(2))) + change = diagonal(bases) + undo = diagonal(tuple(inverse2(basis, prime) for basis in bases)) + reduced = multiply(multiply(undo, data, prime), change, prime) + check(reduced == matrix(canonical(n), prime), "canonical similarity failed") + return tuple(bases) diff --git a/research/odd-cycle-fit-rigidity/verify.py b/research/odd-cycle-fit-rigidity/verify.py new file mode 100644 index 0000000000..941be94e29 --- /dev/null +++ b/research/odd-cycle-fit-rigidity/verify.py @@ -0,0 +1,241 @@ +"""Bounded exact corroboration of odd-cycle fitting-matrix rigidity. + +produced by Nick Petalas. Apache-2.0. +""" + +import argparse +import hashlib +import itertools +import json +import sys +from fractions import Fraction +from pathlib import Path + +PACKAGE = Path(__file__).resolve().parent +sys.path.insert(0, str(PACKAGE)) +from linear import ( # noqa: E402 + block, + canonical, + check, + diagonal, + inverse2, + matrix, + multiply, + normal_form, + rank, + scalar, +) + + +def binary_rank(data): + """Independent column-bit elimination, used for the completion census.""" + pivots = {} + for column in zip(*data, strict=True): + value = sum(int(x) << i for i, x in enumerate(column)) + for _ in range(len(data)): + if not value: + break + leading = value.bit_length() - 1 + if leading not in pivots: + pivots[leading] = value + break + value ^= pivots[leading] + else: + check(value == 0, "binary elimination capacity") + return len(pivots) + + +def conjugate(n, bases, prime): + change = diagonal(bases) + undo = diagonal(tuple(inverse2(basis, prime) for basis in bases)) + return multiply(multiply(change, matrix(canonical(n), prime), prime), undo, prime) + + +def local_consequences(data, prime): + n = len(data) // 2 + zero = ((0, 0), (0, 0)) + identity = ((1, 0), (0, 1)) + for i in range(n): + before, after = (i - 1) % n, (i + 1) % n + left = multiply(block(data, i, before), block(data, before, i), prime) + right = multiply(block(data, i, after), block(data, after, i), prime) + check(rank(left, prime) == rank(right, prime) == 1, "projector rank") + check(multiply(left, left, prime) == left, "left idempotent") + check(multiply(right, right, prime) == right, "right idempotent") + total = tuple( + tuple(scalar(a + b, prime) for a, b in zip(x, y, strict=True)) + for x, y in zip(left, right, strict=True) + ) + check(total == identity, "complementary projectors") + check(multiply(left, right, prime) == zero, "projector disjointness") + for direction in (-1, 1): + j, k = (i + direction) % n, (i + 2 * direction) % n + check(rank(block(data, i, j), prime) == 1, "ordered edge rank") + check( + multiply(block(data, i, j), block(data, j, k), prime) == zero, + "two-edge product", + ) + twice = tuple(tuple(scalar(2 * x, prime) for x in row) for row in data) + check(multiply(data, data, prime) == twice, "quadratic identity") + return n + + +def field_fixtures(): + count = 0 + for prime, n, seed in itertools.product( + (0, 2, 3, 5, 101), (5, 7, 9, 11, 13), range(8) + ): + bases = [] + for i in range(n): + a, b = (seed + 3 * i) % 7 - 3, (3 * seed + i) % 7 - 3 + s = ( + Fraction(seed % 4 + 1, i % 3 + 1) + if not prime + else seed % (prime - 1) + 1 + ) + scale = 1 / s if not prime else pow(s, -1, prime) + bases.append(matrix(((s, s * a), (scale * b, scale * (1 + a * b))), prime)) + data = conjugate(n, bases, prime) + normal_form(data, prime) + local_consequences(data, prime) + if prime == 2: + check(binary_rank(data) == n, "independent fixture rank") + count += 1 + return count + + +def binary_gauge_census(): + gauges = tuple( + matrix((values[:2], values[2:]), 2) + for values in itertools.product(range(2), repeat=4) + if (values[0] * values[3] - values[1] * values[2]) % 2 + ) + check(len(gauges) == 6, "GL2(F2) order") + digests = set() + for bases in itertools.product(gauges, repeat=5): + data = conjugate(5, bases, 2) + normal_form(data, 2) + check(binary_rank(data) == 5, "independent gauge rank") + digests.add(bytes(x for row in data for x in row)) + check(len(digests) == 6**5, "distinct gauge matrices") + return len(digests) + + +def completion_census(): + counts = [] + digest = hashlib.sha256() + values = tuple(itertools.product(range(2), repeat=4)) + for n in (5, 7): + slots = tuple((i, (i + step) % n) for i in range(n) for step in (-1, 1)) + tested = accepted = 0 + for first, second in itertools.combinations(slots, 2): + for a, b in itertools.product(values, repeat=2): + rows = [list(row) for row in canonical(n)] + for (i, j), entries in ((first, a), (second, b)): + for x, y in itertools.product(range(2), repeat=2): + rows[2 * i + x][2 * j + y] = entries[2 * x + y] + dimension = binary_rank(rows) + tested += 1 + digest.update(bytes((n, dimension))) + if dimension == n: + normal_form(rows, 2) + local_consequences(rows, 2) + accepted += 1 + counts.append({"n": n, "tested": tested, "minimum_rank": accepted}) + return counts, digest.hexdigest() + + +def rejected(function, *args): + try: + function(*args) + except ValueError: + return 1 + raise ValueError("negative control accepted") + + +def controls(): + count = 0 + for prime in (0, 2, 3, 5, 101): + rows = [list(row) for row in canonical(5)] + rows[1][2] = rows[2][1] = 0 + data = matrix(rows, prime) + check(rank(data, prime) == 6, "rank-slack boundary") + count += rejected(normal_form, data, prime) + count += rejected(local_consequences, data, prime) + for n in (6, 8): + rows = [[int(i == j) for j in range(2 * n)] for i in range(2 * n)] + for i in range(0, n, 2): + for j in range(2): + rows[2 * i + j][2 * (i + 1) + j] = 1 + rows[2 * (i + 1) + j][2 * i + j] = 1 + check(binary_rank(rows) == n, "even-cycle minimum-rank witness") + count += rejected(normal_form, rows, 2) + count += rejected(local_consequences, rows, 2) + data = [list(row) for row in canonical(5)] + data[0][4] = 1 + count += rejected(normal_form, data, 2) + data = [list(row) for row in canonical(5)] + data[0][0] = 0 + count += rejected(normal_form, data, 2) + for bad in ([], [[1], [1, 0]], [[True]], [[Fraction(1, 2)]], [[2**257]]): + count += rejected(matrix, bad, 2) + count += rejected(matrix, [[1]], 4) + return count + + +def receipt(): + sources = json.loads((PACKAGE / "SOURCES.json").read_text()) + root = PACKAGE.parent.parent + inputs = {} + for item in sources["files"]: + if item["usage"] == "input": + digest = hashlib.sha256((root / item["path"]).read_bytes()).hexdigest() + check(digest == item["sha256"], "source drift: " + item["path"]) + inputs[item["path"]] = digest + completions, digest = completion_census() + evidence = { + "field_fixtures": field_fixtures(), + "binary_gauge_matrices": binary_gauge_census(), + "two_block_completions": completions, + "completion_digest": digest, + "negative_controls": controls(), + } + files = { + path.name: hashlib.sha256(path.read_bytes()).hexdigest() + for path in sorted(PACKAGE.iterdir()) + if path.is_file() and path.name != "RESULT.json" + } + return { + "schema_version": 1, + "claim_type": "scoped proof with exact finite corroboration", + "evidence": evidence, + "package_sha256": files, + "input_sha256": inputs, + } + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--write-result", type=Path) + args = parser.parse_args() + result = receipt() + if args.write_result: + check( + args.write_result.resolve().parent != PACKAGE, + "write receipts outside the package", + ) + args.write_result.write_text( + json.dumps(result, indent=2, sort_keys=True) + "\n" + ) + else: + saved = json.loads((PACKAGE / "RESULT.json").read_text()) + check( + json.dumps(result, sort_keys=True) == json.dumps(saved, sort_keys=True), + "receipt mismatch", + ) + print(json.dumps(result["evidence"], sort_keys=True)) + print("PASS_ODD_CYCLE_FIT_RIGIDITY") + + +if __name__ == "__main__": + main()