From fccac36fa9bcc063c4fda733299a22349ce10e56 Mon Sep 17 00:00:00 2001 From: ross Date: Wed, 14 Oct 2026 00:44:58 +1100 Subject: [PATCH] Research audit: characterize retained Fourier layout cancellation --- research/source-permutation-audit/CLAIM.json | 99 +++ .../source-permutation-audit/EXPECTED.json | 656 ++++++++++++++++++ research/source-permutation-audit/README.md | 267 +++++++ research/source-permutation-audit/SOURCE.json | 36 + .../source-permutation-audit/test_verify.py | 91 +++ research/source-permutation-audit/verify.py | 404 +++++++++++ 6 files changed, 1553 insertions(+) create mode 100644 research/source-permutation-audit/CLAIM.json create mode 100644 research/source-permutation-audit/EXPECTED.json create mode 100644 research/source-permutation-audit/README.md create mode 100644 research/source-permutation-audit/SOURCE.json create mode 100644 research/source-permutation-audit/test_verify.py create mode 100644 research/source-permutation-audit/verify.py diff --git a/research/source-permutation-audit/CLAIM.json b/research/source-permutation-audit/CLAIM.json new file mode 100644 index 0000000000..22ec7b38fc --- /dev/null +++ b/research/source-permutation-audit/CLAIM.json @@ -0,0 +1,99 @@ +{ + "track": "Adversarial mathematical audit", + "assignment_seed": 68, + "seed_method": "secrets.randbelow(100), backed by operating-system randomness; single draw", + "base_commit": "91a2094f2e022930069e8cfdc810a3ea1b81cd09", + "claim_time_utc": "2026-10-13T13:25:19+00:00", + "expected_finish": "2026-10-14 01:11 Australia/Melbourne (2026-10-13 14:11 UTC)", + "budget_minutes": 50, + "verification_reserved_minutes": 12.5, + "object": "upstream/build/sections/07-resampling.tex, lem:chirp-permutation, eq:source-permutation-cancel", + "hypothesis": "For the standard product-group character pairing, the same read permutation can surround the inverse Fourier map exactly when it is a self-adjoint group automorphism; arbitrary common layouts need a matching inverse layout.", + "method": "Written character-separation argument; complete permutation censuses on cyclic groups of orders 2 through 8 and products (2,2), (2,3), (2,4), (2,2,2); direct exact cyclotomic convolution controls.", + "seed_diversification": "68 modulo 3 = 2 selects the third candidate; prioritize product groups and non-self-adjoint automorphisms, beyond the cyclic unit case.", + "deliverable": "Scoped mathematical audit with a proof of the exact extension criterion, reproducible finite census, explicit counterexamples, source pins and no kappa claim.", + "candidates": [ + { + "task": "Packed selected-bit repair boundary and invariant exceptional set", + "decision": "Not chosen: direct overlap with PR735 and PR773." + }, + { + "task": "Fixed-block signed extraction at the positive half-radix boundary", + "decision": "Not chosen: PR693 already supplies the carry census and coefficient-alias controls." + }, + { + "task": "Necessary and sufficient retained-layout condition for source-permutation cancellation", + "decision": "Chosen: no matching open PR found; PR479 audits phase generation and PR879 audits physical transform schedules with matched inverse layouts." + } + ], + "coordination": "No pinned sprint issue, and the only open issues were 650 and 913, neither a claim board. Claim recorded locally as instructed; no new issue posted.", + "overlap_snapshot": { + "open_pr_count": 722, + "max_pr_number": 932, + "preclaim_recent_max": 934 + }, + "related_prs": [ + { + "number": 479, + "head": "40dd01058439cdffae3e3b41c4fe52edaccf5f91" + }, + { + "number": 684, + "head": "14dd937d9e2d3e51092c0c35a8c2c9cdf8ba81f4" + }, + { + "number": 693, + "head": "7d857bb58fb55ada6d05364a4b6f01bba8e2283f" + }, + { + "number": 735, + "head": "8d36139e49add1e55795e283b3bfa45e1cfe1bf0" + }, + { + "number": 773, + "head": "dbd9da3eed9a0045b77de861099e869259f1ad13" + }, + { + "number": 879, + "head": "12012c9478d121d6d404a0d94e446016e3f51a2e" + } + ], + "status": "Completed: exact extension criterion, finite census and failure witnesses; no source defect or new kappa.", + "completed_utc": "2026-10-13T13:44:27+00:00", + "result": { + "permutations": 127616, + "census_basis_pairs": 2960, + "dense_adjoint_pairs": 205, + "convolution_counterexamples": 8, + "admission_rejections": 1, + "positive_controls": 4, + "local_result": "README.md", + "result_link": "https://github.com/RossRichardson/integer-mult-bounds/tree/research/audit-seed68-20261014/research/source-permutation-audit", + "reproduce": "python3 -B -I -O research/source-permutation-audit/verify.py", + "validation": "Exact receipt on CPython 3.12.12 and 3.14.6, seven failure-sensitivity tests, relocated package, receipt-corruption rejection, four inherited finite-identity tests and selected-record check." + }, + "final_overlap_check": { + "open_pr_count": 708, + "highest_open_number": 937, + "new_arrivals_inspected": [ + 933, + 934, + 936, + 937 + ], + "body_searches": [ + "permutation cancellation", + "self-adjoint", + "source-permutation" + ], + "body_search_result": "Only source-permutation returned a hit, PR793: JP8 source compensation ordering, unrelated to Fourier layout cancellation.", + "related_heads_unchanged": [ + 479, + 684, + 879 + ], + "main_commit": "47481c89fd30a43f8f882eb4b391e89c29273fd0", + "selected_kappa": "0.001661957774647029", + "source_changes": "No change to the audited resampling source or upstream manifest; only historical selected-result context moved." + } +} diff --git a/research/source-permutation-audit/EXPECTED.json b/research/source-permutation-audit/EXPECTED.json new file mode 100644 index 0000000000..fd2e6f9e6e --- /dev/null +++ b/research/source-permutation-audit/EXPECTED.json @@ -0,0 +1,656 @@ +{ + "arithmetic": "integer character residues and exact cyclotomic integer polynomials", + "assignment_seed": 68, + "base_commit": "91a2094f2e022930069e8cfdc810a3ea1b81cd09", + "census": [ + { + "adjoint_pairs_checked": 1, + "automorphisms": 1, + "classification_sha256": "8f0b10095a999cb62275a892f97ee3b886db6de0872a2c43ecd22fc71aedc44e", + "direct_basis_pairs": 4, + "direct_dense_pairs": 1, + "moduli": [ + 2 + ], + "permutations": 2, + "rejected_fixing_zero": 0, + "same_layout_accepted": 1 + }, + { + "adjoint_pairs_checked": 2, + "automorphisms": 2, + "classification_sha256": "d13c642bc9b64c77d0ab63fa4f24cc6ada210d42a7513a4796e215ba39e1d69e", + "direct_basis_pairs": 18, + "direct_dense_pairs": 2, + "moduli": [ + 3 + ], + "permutations": 6, + "rejected_fixing_zero": 0, + "same_layout_accepted": 2 + }, + { + "adjoint_pairs_checked": 2, + "automorphisms": 2, + "classification_sha256": "88f3661b2a9f32126ffcf0e4caedac09d0ddd65eac45605514dd64ea3708873b", + "direct_basis_pairs": 32, + "direct_dense_pairs": 2, + "moduli": [ + 4 + ], + "permutations": 24, + "rejected_fixing_zero": 4, + "same_layout_accepted": 2 + }, + { + "adjoint_pairs_checked": 4, + "automorphisms": 4, + "classification_sha256": "6d575db72af798497f5c83f94fae19bd8bdf2be66248f05d47a23b5737ed308b", + "direct_basis_pairs": 100, + "direct_dense_pairs": 4, + "moduli": [ + 5 + ], + "permutations": 120, + "rejected_fixing_zero": 20, + "same_layout_accepted": 4 + }, + { + "adjoint_pairs_checked": 2, + "automorphisms": 2, + "classification_sha256": "84bd945241c2bd46101c5d6bdc07b6a5b9ab786738668513e888f5e6a2069b08", + "direct_basis_pairs": 72, + "direct_dense_pairs": 2, + "moduli": [ + 6 + ], + "permutations": 720, + "rejected_fixing_zero": 118, + "same_layout_accepted": 2 + }, + { + "adjoint_pairs_checked": 6, + "automorphisms": 6, + "classification_sha256": "6e9ff6592fb4336d114a92dc4aa4d09470ead49716df5357b1a058c23bdbd513", + "direct_basis_pairs": 294, + "direct_dense_pairs": 6, + "moduli": [ + 7 + ], + "permutations": 5040, + "rejected_fixing_zero": 714, + "same_layout_accepted": 6 + }, + { + "adjoint_pairs_checked": 4, + "automorphisms": 4, + "classification_sha256": "40fbd0599bf91a876af38b61761907e118cf0778176c9794ee7a964f49845400", + "direct_basis_pairs": 256, + "direct_dense_pairs": 4, + "moduli": [ + 8 + ], + "permutations": 40320, + "rejected_fixing_zero": 5036, + "same_layout_accepted": 4 + }, + { + "adjoint_pairs_checked": 6, + "automorphisms": 6, + "classification_sha256": "02fc02fc633c8385641e073fef3fe7b485896f7baa7b7a40c8687cdf0bf8ddc4", + "direct_basis_pairs": 64, + "direct_dense_pairs": 6, + "moduli": [ + 2, + 2 + ], + "permutations": 24, + "rejected_fixing_zero": 2, + "same_layout_accepted": 4 + }, + { + "adjoint_pairs_checked": 2, + "automorphisms": 2, + "classification_sha256": "bd11b9130aeda659af3a1fb614bbc32b946845a583e3ce4f3a80c842871c94c3", + "direct_basis_pairs": 72, + "direct_dense_pairs": 2, + "moduli": [ + 2, + 3 + ], + "permutations": 720, + "rejected_fixing_zero": 118, + "same_layout_accepted": 2 + }, + { + "adjoint_pairs_checked": 8, + "automorphisms": 8, + "classification_sha256": "80e7754e64a6ee65851d3f74ee1668b8db571536d8f77105a1b68101d14e6f16", + "direct_basis_pairs": 256, + "direct_dense_pairs": 8, + "moduli": [ + 2, + 4 + ], + "permutations": 40320, + "rejected_fixing_zero": 5036, + "same_layout_accepted": 4 + }, + { + "adjoint_pairs_checked": 168, + "automorphisms": 168, + "classification_sha256": "64bb53af39b00b70a42f61951f497accc4cb54b7c270fb471ab8fec793262df9", + "direct_basis_pairs": 1792, + "direct_dense_pairs": 168, + "moduli": [ + 2, + 2, + 2 + ], + "permutations": 40320, + "rejected_fixing_zero": 5012, + "same_layout_accepted": 28 + } + ], + "controls": { + "admission_rejections": [ + { + "map": [ + 0, + 2, + 4, + 0, + 2, + 4 + ], + "moduli": [ + 6 + ], + "name": "nonunit multiplier rejected", + "reason": "not a permutation" + } + ], + "convolution_counterexamples": [ + { + "expected_numerator_factor": 3, + "forward_read_map": [ + 1, + 2, + 0 + ], + "inverse_read_map": [ + 1, + 2, + 0 + ], + "inverse_sign": 1, + "moduli": [ + 3 + ], + "name": "translation does not cancel", + "u_basis": [ + 0 + ], + "v_basis": [ + 0 + ], + "witness": { + "actual_numerator": [ + 0, + 0 + ], + "coordinate": [ + 0 + ], + "denominator": 27, + "expected_numerator": [ + 3, + 0 + ], + "root_order": 3 + } + }, + { + "expected_numerator_factor": 4, + "forward_read_map": [ + 0, + 2, + 1, + 3 + ], + "inverse_read_map": [ + 0, + 2, + 1, + 3 + ], + "inverse_sign": 1, + "moduli": [ + 4 + ], + "name": "zero-fixing involutive bit reversal does not cancel", + "u_basis": [ + 0 + ], + "v_basis": [ + 1 + ], + "witness": { + "actual_numerator": [ + 2, + -2 + ], + "coordinate": [ + 1 + ], + "denominator": 64, + "expected_numerator": [ + 4, + 0 + ], + "root_order": 4 + } + }, + { + "expected_numerator_factor": 4, + "forward_read_map": [ + 0, + 3, + 2, + 1 + ], + "inverse_read_map": [ + 0, + 3, + 2, + 1 + ], + "inverse_sign": 1, + "moduli": [ + 2, + 2 + ], + "name": "an automorphism need not be self-adjoint", + "u_basis": [ + 0, + 0 + ], + "v_basis": [ + 0, + 1 + ], + "witness": { + "actual_numerator": [ + 0 + ], + "coordinate": [ + 0, + 1 + ], + "denominator": 64, + "expected_numerator": [ + 4 + ], + "root_order": 2 + } + }, + { + "expected_numerator_factor": 9, + "forward_read_map": [ + 0, + 4, + 8, + 3, + 7, + 2, + 6, + 1, + 5 + ], + "inverse_read_map": [ + 0, + 7, + 5, + 3, + 1, + 8, + 6, + 4, + 2 + ], + "inverse_sign": 1, + "moduli": [ + 3, + 3 + ], + "name": "inverse is not the adjoint", + "u_basis": [ + 0, + 0 + ], + "v_basis": [ + 0, + 1 + ], + "witness": { + "actual_numerator": [ + 0, + 0 + ], + "coordinate": [ + 0, + 1 + ], + "denominator": 729, + "expected_numerator": [ + 9, + 0 + ], + "root_order": 3 + } + }, + { + "expected_numerator_factor": 5, + "forward_read_map": [ + 0, + 2, + 4, + 1, + 3 + ], + "inverse_read_map": [ + 0, + 3, + 1, + 4, + 2 + ], + "inverse_sign": 1, + "moduli": [ + 5 + ], + "name": "inverting a valid source multiplier is wrong", + "u_basis": [ + 0 + ], + "v_basis": [ + 1 + ], + "witness": { + "actual_numerator": [ + 0, + 0, + 0, + 0 + ], + "coordinate": [ + 1 + ], + "denominator": 125, + "expected_numerator": [ + 5, + 0, + 0, + 0 + ], + "root_order": 5 + } + }, + { + "expected_numerator_factor": 5, + "forward_read_map": [ + 0, + 2, + 4, + 1, + 3 + ], + "inverse_read_map": [ + 0, + 2, + 4, + 1, + 3 + ], + "inverse_sign": -1, + "moduli": [ + 5 + ], + "name": "omitting Fourier-sign conjugation", + "u_basis": [ + 0 + ], + "v_basis": [ + 1 + ], + "witness": { + "actual_numerator": [ + 0, + 0, + 0, + 0 + ], + "coordinate": [ + 1 + ], + "denominator": 125, + "expected_numerator": [ + 5, + 0, + 0, + 0 + ], + "root_order": 5 + } + }, + { + "expected_numerator_factor": 25, + "forward_read_map": [ + 0, + 2, + 4, + 1, + 3 + ], + "inverse_read_map": [ + 0, + 2, + 4, + 1, + 3 + ], + "inverse_sign": 1, + "moduli": [ + 5 + ], + "name": "one factor of N is missing from normalization", + "u_basis": [ + 0 + ], + "v_basis": [ + 0 + ], + "witness": { + "actual_numerator": [ + 5, + 0, + 0, + 0 + ], + "coordinate": [ + 0 + ], + "denominator": 125, + "expected_numerator": [ + 25, + 0, + 0, + 0 + ], + "root_order": 5 + } + }, + { + "expected_numerator_factor": 1, + "forward_read_map": [ + 0, + 2, + 4, + 1, + 3 + ], + "inverse_read_map": [ + 0, + 2, + 4, + 1, + 3 + ], + "inverse_sign": 1, + "moduli": [ + 5 + ], + "name": "one extra factor of N in normalization", + "u_basis": [ + 0 + ], + "v_basis": [ + 0 + ], + "witness": { + "actual_numerator": [ + 5, + 0, + 0, + 0 + ], + "coordinate": [ + 0 + ], + "denominator": 125, + "expected_numerator": [ + 1, + 0, + 0, + 0 + ], + "root_order": 5 + } + } + ], + "positive_controls": [ + { + "direct_basis_pairs": 25, + "forward_read_map": [ + 0, + 2, + 4, + 1, + 3 + ], + "inverse_read_map": [ + 0, + 2, + 4, + 1, + 3 + ], + "moduli": [ + 5 + ], + "name": "valid multiplier need not be involutive" + }, + { + "direct_basis_pairs": 16, + "forward_read_map": [ + 0, + 3, + 1, + 2 + ], + "inverse_read_map": [ + 0, + 3, + 1, + 2 + ], + "moduli": [ + 2, + 2 + ], + "name": "self-adjoint mixing need not be involutive" + }, + { + "direct_basis_pairs": 64, + "forward_read_map": [ + 0, + 5, + 2, + 7, + 6, + 3, + 4, + 1 + ], + "inverse_read_map": [ + 0, + 5, + 2, + 7, + 6, + 3, + 4, + 1 + ], + "moduli": [ + 2, + 4 + ], + "name": "unequal cyclic moduli need weighted adjoints" + }, + { + "direct_basis_pairs": 81, + "forward_read_map": [ + 0, + 4, + 8, + 3, + 7, + 2, + 6, + 1, + 5 + ], + "inverse_read_map": [ + 0, + 1, + 2, + 4, + 5, + 3, + 8, + 6, + 7 + ], + "moduli": [ + 3, + 3 + ], + "name": "a distinct adjoint repairs the shear" + } + ] + }, + "format": "source-permutation-audit-v1", + "scope": "Finite replay plus written all-group proof; no formal proof, source defect or new kappa claimed.", + "source_manifest_sha256": "a0a5d1967256b956df537aa36a6422d055c4ed4cda197d4faa923ef4bda74740", + "source_multipliers": { + "cyclic_moduli_inclusive": [ + 2, + 68 + ], + "unit_multipliers": 1425 + }, + "total_direct_basis_pairs": 2960, + "total_direct_dense_pairs": 205, + "total_permutations": 127616 +} diff --git a/research/source-permutation-audit/README.md b/research/source-permutation-audit/README.md new file mode 100644 index 0000000000..33e7cbafd0 --- /dev/null +++ b/research/source-permutation-audit/README.md @@ -0,0 +1,267 @@ +# Exact scope of retained source-permutation cancellation + +**Mathematical audit / scoped negative result; no new κ and no defect in the +stated source lemma.** The retained convolution identity is valid for the +source's coordinate multipliers. Extending it to a shared arbitrary layout, +or even to an arbitrary group automorphism, is not valid when the inverse +pass is obtained just by conjugating the same forward routine. + +This note gives the precise condition, a short all-group argument, and exact +finite evidence. For the usual character pairing on a product of cyclic +groups, the same permutation works on the forward and conjugate passes +**if and only if it is a self-adjoint group automorphism**. An arbitrary +automorphism works with its *adjoint* on the conjugate pass. The adjoint is +generally different from both the original permutation and its inverse. + +The concrete contribution is an explicit admission criterion and failure +witnesses for this retained-layout interface, not a claim of novelty for +Fourier duality. A shared layout still commutes with pointwise products; +that fact alone does not establish the complete convolution identity. + +## Source and reproducibility + +- Repository base: `91a2094f2e022930069e8cfdc810a3ea1b81cd09`. +- Mathematical source: OpenAI's `openai/math` at + `adc7f1241b42e322a6451854ab7e4b4c146bf78a`, retained at + [`upstream/build/sections/07-resampling.tex`](../../upstream/build/sections/07-resampling.tex), + `lem:chirp-permutation`, specifically `eq:source-permutation-cancel`. +- The read convention is `(R_c z)_j = z_(c j)`, not the push convention. +- [SOURCE.json](SOURCE.json) pins the actual source bytes and original + manifest as required inputs. The prior audit and accepted-result hashes + are historical context, not dependencies or gates on later frontiers. + No pending PR code is imported. +- The selected conditional value at this base was + `κ = 1661634653669567 / 10^18 = 0.001661634653669567`, from #799 with #589. + Before submission, main advanced to + `47481c89fd30a43f8f882eb4b391e89c29273fd0`, selecting #890 at + `κ = 0.001661957774647029`. The audited source and its manifest are + byte-identical there. This package changes neither selected construction. + +From the repository root, with Python 3.10 or later and no third-party packages: + +```sh +python3 -B -I -O research/source-permutation-audit/verify.py +``` + +Expected result: + +```text +PASS_SOURCE_PERMUTATION: 127616 permutations; 2960 census basis pairs; 205 dense adjoint pairs; 8 exact counterexamples; 1 admission rejection; 4 positive controls +``` + +The command checks source SHA-256 pins, recomputes the whole bounded domain +and compares every field with [EXPECTED.json](EXPECTED.json). No floating +point, transform library or supplier code is used. To write a fresh receipt +without overwriting the committed one, use `--write /tmp/permutation-replay.json`. + +## Statement and proof + +Let `G = ∏_i Z/q_i`, `N = |G|`, and use the symmetric, nondegenerate pairing + +\[ +\chi(x,y)=\exp\left(2\pi i\sum_i x_i y_i/q_i\right). +\] + +Write `F−` and `F+` for the negative- and positive-sign Fourier transforms, +each normalized by `1/N`. For a permutation `π` of `G`, define the read map +`(P_π f)(x) = f(π(x))`. Convolution here is the **unnormalized** group +convolution. For two permutations `π, ρ`, consider the identity + +\[ +(P_\rho F^+)\left[(P_\pi F^-u)\cdot(P_\pi F^-v)\right] +=\frac{u*v}{N^2} \qquad\text{for all }u,v\in\mathbb C^G. \tag{1} +\] + +**Claim.** (1) holds exactly when `π` is a group automorphism and +`ρ = π†`, where the adjoint is defined by + +\[ +\chi(\pi^\dagger x,y)=\chi(x,\pi y). \tag{2} +\] + +In particular, setting `ρ = π` is allowed exactly for self-adjoint +automorphisms. + +**Reduction to one operator.** Every permutation commutes with pointwise +multiplication, and normalized Fourier inversion gives + +\[ +F^+[(F^-u)\cdot(F^-v)]=(u*v)/N^2. +\] + +The products `(F−u)·(F−v)` span all of `C^G`: fix `u = δ_0`, whose +transform is the nonzero constant `1/N`, and vary `v`, using invertibility +of `F−`. Thus (1) is equivalent to + +\[ +P_\rho F^+P_\pi=F^+. \tag{3} +\] + +The coefficient at row `x`, column `z` on the left of (3) is +`χ(ρ(x), π⁻¹(z))/N`. Substitute `z = π(y)`. Equality of all entries is +therefore exactly + +\[ +\chi(\rho(x),y)=\chi(x,\pi(y))\quad\text{for every }x,y. \tag{4} +\] + +**Necessity.** Set `y = 0` in (4). Nondegeneracy gives `π(0) = 0`. +For any `y,z`, multiplicativity of the left side in its second argument +implies + +\[ +\chi(x,\pi(y+z))=\chi(x,\pi(y)+\pi(z))\quad\text{for every }x. +\] + +Nondegeneracy gives `π(y+z) = π(y)+π(z)`. A bijective homomorphism is an +automorphism. For such `π`, (4) determines `ρ(x)` uniquely, namely (2). +Existence follows because every character on the product group is +`χ(w, ·)` for a unique `w`; composition with `π` preserves characters. +The adjoint is itself an automorphism, since composition with a bijection +permutes characters. This also proves there is no output permutation `ρ` +of this form that repairs a nonadditive `π`. + +**Sufficiency.** For an automorphism and its adjoint, (4) holds by +definition. Reversing the entry calculation proves (3), hence (1). + +For unequal cyclic factors, ordinary integer matrix symmetry is not the +criterion. If `a_i = π(e_i)` are the generator images, self-adjointness is + +\[ +\frac{(a_i)_j}{q_j}=\frac{(a_j)_i}{q_i}\pmod 1. +\] + +Both representatives lie in `[0,1)`, so the checker compares the exact +integer cross-products `(a_i)_j q_i = (a_j)_i q_j`. It separately checks +the generator orders and reconstructs the image of every group element. + +**Why the retained source is valid.** Its `R` has +`π(x)_i = t_i x_i mod s_i`, with `gcd(t_i,s_i)=1`. This is a diagonal +automorphism and +`Σ_i (t_i x_i)y_i/s_i = Σ_i x_i(t_i y_i)/s_i`. It is self-adjoint, even +when it is not involutive. This argument applies to all the stated source +moduli; the finite census is not needed for the universal implication. + +**Different inverse-layout contract.** For *any* permutation `π`, including +nonadditive layouts, the inverse routine `F+ P_π⁻¹` always undoes the layout +before the inverse transform. The theorem above concerns replacing this +routine with `P_ρ F+`. These operators coincide precisely in the case +just proved. In particular, this audit does not challenge a physical +schedule that explicitly implements the matching inverse layout. + +## Exact experiment and controls + +Assignment seed **68** selected the audit track and the third of three +narrow candidates. [CLAIM.json](CLAIM.json) records the alternatives, +overlap check, base, method, scope and stopping rule. The complete domain +is all permutations of the eleven group presentations below. It includes +isomorphic groups with different product presentations deliberately. + +| Cyclic factors | Permutations | Automorphisms | Same-layout admissible | +|---|---:|---:|---:| +| 2 | 2 | 1 | 1 | +| 3 | 6 | 2 | 2 | +| 4 | 24 | 2 | 2 | +| 5 | 120 | 4 | 4 | +| 6 | 720 | 2 | 2 | +| 7 | 5,040 | 6 | 6 | +| 8 | 40,320 | 4 | 4 | +| 2, 2 | 24 | 6 | 4 | +| 2, 3 | 720 | 2 | 2 | +| 2, 4 | 40,320 | 8 | 4 | +| 2, 2, 2 | 40,320 | 168 | 28 | + +For every permutation, two algebraically different calculations agree: +the literal entries of `P_π F+ P_π` match `F+` exactly when reconstruction +from generator images gives a self-adjoint automorphism. Every rejected +permutation has a differing character-matrix entry; its two root powers +are also checked to remain distinct in the exact cyclotomic ring. A digest +binds the entire classification stream for each presentation. + +A separate direct Fourier/convolution implementation executes all 2,960 +ordered basis pairs for accepted layouts, and one seed-68 dense integer +input pair for each of the 205 automorphisms with its computed adjoint. +It constructs polynomial values in `Z[ζ_L] = Z[z]/Φ_L(z)` and carries the +three transform normalizations as the common denominator `N^3`. It does +not call the character-table test or structural classification to decide +whether the resulting convolution is correct. Integer inputs suffice for +these exact algebraic tests; no disk-grid numerical routine is executed. + +The source's multiplier family additionally passes all 1,425 unit choices +on cyclic moduli `2 ≤ q ≤ 68`. Four positive controls add 186 direct basis +pairs, including unequal moduli and an automorphism with a distinct adjoint. +These are algebraic test parameters, not a claim that each tuple meets the +Gaussian resampling feasibility conditions. + +The receipt includes eight actual differing convolution coefficients: + +1. A translation on `Z/3` does not cancel. +2. Bit reversal `(0,2,1,3)` on `Z/4` is zero-fixing and involutive but + nonadditive. With `u=δ_0, v=δ_1`, output coordinate 1 is + `(2−2i)/64`, whereas the required coefficient is `4/64`. +3. The involutive shear `(x,y) ↦ (x+y,y)` on `(Z/2)^2` is an automorphism + but is not self-adjoint. +4. On `(Z/3)^2`, using that shear's inverse in place of its adjoint fails. +5. On `Z/5`, incorrectly inverting the valid source multiplier `c=2` fails. +6. Omitting conjugation of the Fourier sign fails. +7. Normalizing the convolution by `N` rather than `N^2` fails. +8. Normalizing it by `N^3` rather than `N^2` fails. + +A ninth, separate **admission** control rejects multiplication by 2 modulo +6 because it is not a permutation. It is not counted as a convolution +counterexample. The positive controls show that involutivity is unnecessary, +weighted adjoints matter, and replacing the non-self-adjoint shear by its +correct adjoint on the conjugate pass repairs every tested basis pair. + +Enumeration stops after the fixed domain is exhausted; there is no timeout +or incomplete search. The bounded census is finite evidence. The written +character argument proves the general criterion; it has not been checked +by a proof assistant or an independent human reviewer. + +## Verification of this package + +The main receipt agrees on macOS arm64 with CPython 3.14.6 and 3.12.12, +including optimized isolated execution. The supplementary command is + +```sh +python3 -B -I -O research/source-permutation-audit/test_verify.py +``` + +Its seven passing tests include five deliberately faulty implementations: +wrong permutation direction, admitting every automorphism, unweighted +symmetry, wrong normalization, and a missing Fourier sign. They also check +source relocation/corruption and known cyclotomic polynomials with exact +character orthogonality. All failure checks remain active under `-O`. + +The main command also passed from an isolated, relocated source closure +whose path contains spaces; a corrupted expected receipt was rejected. +The four existing `test_finite_identities.py` tests and +`make selected-record-check` passed at the exact base. Full `make verify`, +supplier cold replay, proof-assistant checking and remote CI are not claimed: +no existing supplier or generator is changed. The result is a separately +checkable research package confined to this directory. + +## Dependencies, limitations and credit + +This is a restriction on one exact algebraic interface. It does not charge +or eliminate a physical permutation, construct a fixed-tape implementation, +or certify the numerical Gaussian routines. All inherited compilation, +mutable-source/common-label, hosting, precision, tape and analytic +assumptions of the selected result remain. There is no new cost recurrence +or κ claim, and no counterexample to the source's actual hypotheses. + +Credit OpenAI for the original manuscript and Douglas Colkitt / CrocSwap +for the retained dependency and normalization audit. Related concurrent +work is explicitly separate: Chafik Boukhalfa's [#479](https://github.com/CrocSwap/integer-mult-bounds/pull/479) +audits exact streamed chirp phases; Philipp / mightyOil's +[#879](https://github.com/CrocSwap/integer-mult-bounds/pull/879) checks physical +transform schedules and their matching inverse layouts. Their exact +inspected heads are recorded in SOURCE.json. Neither contribution is +replayed, imported or contradicted here. The already-covered packed-bit +and signed-extraction candidates were not reopened. + +Prepared by **Ross Richardson (@RossRichardson)** with substantial +**OpenAI Codex assistance** in task selection, mathematics, implementation, +verification and writing. New material is Apache-2.0 under the repository +license. No original source or license is changed. No priority claim for +the elementary Fourier-adjoint characterization is intended. diff --git a/research/source-permutation-audit/SOURCE.json b/research/source-permutation-audit/SOURCE.json new file mode 100644 index 0000000000..dc628873d2 --- /dev/null +++ b/research/source-permutation-audit/SOURCE.json @@ -0,0 +1,36 @@ +{ + "base_commit": "91a2094f2e022930069e8cfdc810a3ea1b81cd09", + "upstream_repository": "https://github.com/openai/math", + "upstream_commit": "adc7f1241b42e322a6451854ab7e4b4c146bf78a", + "statement": "build/sections/07-resampling.tex: eq:source-permutation-cancel in lem:chirp-permutation; read convention (R_c z)_j = z_(c j)", + "selected_kappa": "1661634653669567/1000000000000000000", + "files": { + "upstream/build/sections/07-resampling.tex": "8a6b91670f9163afb54892489fa2210c77fe1b058f0325ec7d18b44b94be6e2c", + "upstream/manifest.json": "16ab13971694cabab09440c47ebf2fc0496a9d35fc3af33950fda3498f8e2392" + }, + "attribution": { + "original_statement": "OpenAI source manuscript; retained upstream license and notices apply.", + "prior_audit": "Douglas Colkitt / CrocSwap, docs/audit.md; normalization and common-permutation reasoning.", + "related_work": [ + { + "pr": 479, + "author": "Chafik Boukhalfa / chafreaky", + "head": "40dd01058439cdffae3e3b41c4fe52edaccf5f91", + "scope": "Exact streamed chirp phases; not imported." + }, + { + "pr": 879, + "author": "Philipp / mightyOil", + "head": "12012c9478d121d6d404a0d94e446016e3f51a2e", + "scope": "Exact physical layout and matching inverse transform; not imported." + } + ] + }, + "dependency_scope": "Only the files map is a required immutable mathematical input. Context-at-base hashes record historical documentation and selected-result bytes; they are not dependencies and are not admission gates on a later selected frontier. No PR code is imported.", + "context_at_base": { + "upstream/build/sections/06-transforms.tex": "924316e4bc905066281d8f9e8746a49d8829aeb393688608deb1e61c3c87853c", + "docs/audit.md": "3858925e9dc44e46df3c22dd77d48614c52ba24a3f42c1db99b09b6fd80540c4", + "docs/research/community-frontier799-review.md": "bd91c10c67f2a873fd456b95cbb43f715a43dac0e0a66938737d591c6b754052", + "certificates/selected-result.json": "9873519224e2db72d6a8c926f51e3e75129671e467948c6f6b03744f8bcfc699" + } +} diff --git a/research/source-permutation-audit/test_verify.py b/research/source-permutation-audit/test_verify.py new file mode 100644 index 0000000000..6b263740ce --- /dev/null +++ b/research/source-permutation-audit/test_verify.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +"""Failure-sensitivity checks for this audit, not an additional theorem proof. + +Copyright 2026 Ross Richardson. SPDX-License-Identifier: Apache-2.0 +Developed with substantial OpenAI Codex assistance. +""" + +import importlib.util +from pathlib import Path +import shutil +import tempfile +import unittest + + +PACKAGE = Path(__file__).resolve().parent +SPEC = importlib.util.spec_from_file_location("permutation_audit", PACKAGE / "verify.py") +AUDIT = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(AUDIT) +SOURCE = (PACKAGE / "verify.py").read_text() + + +def mutant(old, new): + if SOURCE.count(old) != 1: + raise ValueError("mutation target is not unique") + namespace = {"__name__": "permutation_mutant", "__file__": str(PACKAGE / "verify.py")} + exec(compile(SOURCE.replace(old, new), "", "exec"), namespace) + return namespace + + +class FailureSensitivity(unittest.TestCase): + def test_using_permutation_instead_of_inverse_in_matrix_entry(self): + changed = mutant("self.phase[rho[x]][inverse[z]]", "self.phase[rho[x]][pi[z]]") + with self.assertRaisesRegex(ValueError, "classification disagreement"): + changed["census"]((5,)) + + def test_treating_every_automorphism_as_admissible(self): + changed = mutant("return True, symmetric", "return True, True") + with self.assertRaisesRegex(ValueError, "classification disagreement"): + changed["census"]((2, 2)) + + def test_unweighted_symmetry_on_mixed_moduli(self): + changed = mutant("columns[i][j] * self.moduli[i]\n" + " == columns[j][i] * self.moduli[j]", + "columns[i][j] == columns[j][i]") + with self.assertRaisesRegex(ValueError, "classification disagreement"): + changed["census"]((2, 4)) + + def test_wrong_convolution_normalization(self): + changed = mutant("factor = group.size if", "factor = group.size ** 2 if") + with self.assertRaisesRegex(ValueError, "direct adjoint convolution failed"): + changed["census"]((2,)) + + def test_missing_forward_fourier_sign(self): + changed = mutant("for x in u], -1)", "for x in u], 1)") + with self.assertRaisesRegex(ValueError, "direct adjoint convolution failed"): + changed["census"]((3,)) + + def test_source_relocation_and_corruption(self): + root = PACKAGE.parents[1] + source = AUDIT.check_sources(root) + with tempfile.TemporaryDirectory(prefix="permutation-source-control-") as tmp: + isolated = Path(tmp) + for name in source["files"]: + destination = isolated / name + destination.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(root / name, destination) + self.assertEqual(AUDIT.check_sources(isolated), source) + target = isolated / "upstream/build/sections/07-resampling.tex" + target.write_bytes(target.read_bytes() + b"\n% mutation control\n") + with self.assertRaisesRegex(ValueError, "source pin mismatch"): + AUDIT.check_sources(isolated) + + def test_known_cyclotomic_polynomials_and_orthogonality(self): + # Independent integer coefficients for every period used by the + # census, plus the order-nine group's period-three controls. + known = {2: (1, 1), 3: (1, 1, 1), 4: (1, 0, 1), + 5: (1, 1, 1, 1, 1), 6: (1, -1, 1), + 7: (1, 1, 1, 1, 1, 1, 1), 8: (1, 0, 0, 0, 1)} + for period, polynomial in known.items(): + self.assertEqual(AUDIT.cyclotomic(period), polynomial) + ring = AUDIT.ExactRoots(period) + for frequency in range(period): + total = ring.zero + for j in range(period): + total = ring.add(total, ring.roots[frequency * j % period]) + expected = ring.scale(ring.one, period) if frequency == 0 else ring.zero + self.assertEqual(total, expected) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/research/source-permutation-audit/verify.py b/research/source-permutation-audit/verify.py new file mode 100644 index 0000000000..b145a0b14d --- /dev/null +++ b/research/source-permutation-audit/verify.py @@ -0,0 +1,404 @@ +#!/usr/bin/env python3 +"""Exact finite audit of the retained Fourier permutation interface. + +Copyright 2026 Ross Richardson. SPDX-License-Identifier: Apache-2.0 +Developed with substantial OpenAI Codex assistance. Standard library only. +The written all-group argument is in README.md; this is a finite replay. +""" + +import argparse +from functools import cache +import hashlib +from itertools import permutations, product +import json +from math import factorial, gcd, lcm +from pathlib import Path +import random + + +PACKAGE = Path(__file__).resolve().parent +DOMAIN = ((2,), (3,), (4,), (5,), (6,), (7,), (8,), + (2, 2), (2, 3), (2, 4), (2, 2, 2)) +SEED = 68 + + +def require(condition, message): + if not condition: + raise ValueError(message) + + +class Group: + def __init__(self, moduli): + self.moduli = tuple(moduli) + require(bool(self.moduli) and all(q >= 2 for q in self.moduli), + "nontrivial cyclic factors required") + self.points = tuple(product(*(range(q) for q in self.moduli))) + self.index = {x: i for i, x in enumerate(self.points)} + self.size = len(self.points) + self.period = lcm(*self.moduli) + self.phase = tuple(tuple( + sum(a * b * (self.period // q) + for a, b, q in zip(x, y, self.moduli)) % self.period + for y in self.points) for x in self.points) + self.generators = tuple(self.index[tuple(int(i == j) + for j in range(len(self.moduli)))] + for i in range(len(self.moduli))) + + def check_permutation(self, pi): + require(sorted(pi) == list(range(self.size)), "not a permutation") + + def kernel_witness(self, pi, rho): + """Literal matrix entries of P_rho F+ P_pi versus F+. + + The common factor 1/N is omitted. A primitive L-th root has distinct + powers modulo L, so comparing these residues is exact. + """ + inverse = [0] * self.size + for i, j in enumerate(pi): + inverse[j] = i + for x in range(self.size): + for z in range(self.size): + actual = self.phase[rho[x]][inverse[z]] + expected = self.phase[x][z] + if actual != expected: + return x, z, actual, expected + return None + + def structural(self, pi): + """Reconstruct a homomorphism from generator images, not F entries.""" + columns = [self.points[pi[e]] for e in self.generators] + if pi[0] != 0: + return False, False + for order, image in zip(self.moduli, columns): + if any(order * value % modulus + for value, modulus in zip(image, self.moduli)): + return False, False + for x, image_index in zip(self.points, pi): + reconstructed = tuple(sum(x[i] * columns[i][j] + for i in range(len(x))) % q + for j, q in enumerate(self.moduli)) + if reconstructed != self.points[image_index]: + return False, False + # = , including unequal moduli. + symmetric = all(columns[i][j] * self.moduli[i] + == columns[j][i] * self.moduli[j] + for i in range(len(columns)) + for j in range(len(columns))) + return True, symmetric + + def adjoint(self, pi): + """Reconstruct pi^dagger using weighted generator coordinates.""" + columns = [self.points[pi[e]] for e in self.generators] + result = [] + for x in self.points: + row = [] + for q, column in zip(self.moduli, columns): + residue = sum(a * b * (self.period // r) + for a, b, r in zip(x, column, self.moduli)) % self.period + step = self.period // q + require(residue % step == 0, "adjoint coordinate not integral") + row.append(residue // step) + result.append(self.index[tuple(row)]) + self.check_permutation(result) + return tuple(result) + + +def monic_quotient(numerator, denominator): + work = list(numerator) + quotient = [0] * (len(work) - len(denominator) + 1) + for degree in range(len(work) - 1, len(denominator) - 2, -1): + coefficient = work[degree] + shift = degree - len(denominator) + 1 + quotient[shift] = coefficient + for j, value in enumerate(denominator): + work[shift + j] -= coefficient * value + require(not any(work), "nonexact cyclotomic division") + return tuple(quotient) + + +@cache +def cyclotomic(n): + poly = (-1,) + (0,) * (n - 1) + (1,) + for divisor in range(1, n): + if n % divisor == 0: + poly = monic_quotient(poly, cyclotomic(divisor)) + return poly + + +class ExactRoots: + """Integer polynomials in zeta_L modulo the monic cyclotomic Phi_L.""" + def __init__(self, period): + self.period = period + self.poly = cyclotomic(period) + self.degree = len(self.poly) - 1 + self.zero = (0,) * self.degree + self.one = (1,) + (0,) * (self.degree - 1) + self.roots = tuple(self.reduce((0,) * i + (1,)) + for i in range(period)) + require(len(set(self.roots)) == period, "root powers are not distinct") + require(self.reduce((-1,) + (0,) * (period - 1) + (1,)) == self.zero, + "root period sanity check failed") + + def reduce(self, values): + work = list(values) + [0] * max(0, self.degree - len(values)) + for k in range(len(work) - 1, self.degree - 1, -1): + c = work[k] + for j, value in enumerate(self.poly): + work[k - self.degree + j] -= c * value + return tuple(work[:self.degree]) + + def add(self, a, b): + return tuple(x + y for x, y in zip(a, b)) + + def scale(self, a, value): + return tuple(value * x for x in a) + + def multiply(self, a, b): + values = [0] * (2 * self.degree - 1) + for i, x in enumerate(a): + for j, y in enumerate(b): + values[i + j] += x * y + return self.reduce(values) + + +def transform(group, ring, values, sign): + """Direct Fourier sum, without using the census character table.""" + output = [] + for frequency in group.points: + total = ring.zero + for point, value in zip(group.points, values): + exponent = sum(a * b * (ring.period // q) + for a, b, q in zip(frequency, point, group.moduli)) + term = ring.multiply(value, ring.roots[(sign * exponent) % ring.period]) + total = ring.add(total, term) + output.append(total) + return output + + +def direct_pipeline(group, ring, pi, rho, u, v, inverse_sign=1): + # Each transform omits 1/N. The final common denominator is N^3. + fu = transform(group, ring, [ring.scale(ring.one, x) for x in u], -1) + fv = transform(group, ring, [ring.scale(ring.one, x) for x in v], -1) + middle = [ring.multiply(fu[j], fv[j]) for j in pi] + inverse = transform(group, ring, middle, inverse_sign) + return [inverse[j] for j in rho] + + +def direct_convolution(group, u, v): + result = [0] * group.size + for x, a in zip(group.points, u): + for y, b in zip(group.points, v): + z = tuple((i + j) % q for i, j, q in zip(x, y, group.moduli)) + result[group.index[z]] += a * b + return result + + +def pipeline_difference(group, ring, pi, rho, u, v, inverse_sign=1, + expected_numerator_factor=None): + actual = direct_pipeline(group, ring, pi, rho, u, v, inverse_sign) + factor = group.size if expected_numerator_factor is None else expected_numerator_factor + expected = [ring.scale(ring.one, factor * x) + for x in direct_convolution(group, u, v)] + for i, (a, b) in enumerate(zip(actual, expected)): + if a != b: + return {"coordinate": list(group.points[i]), + "actual_numerator": list(a), "expected_numerator": list(b), + "denominator": group.size ** 3, + "root_order": ring.period} + return None + + +def basis(n, index): + return [int(i == index) for i in range(n)] + + +def census(moduli): + group = Group(moduli) + ring = ExactRoots(group.period) + checked = automorphisms = accepted = rejected_fixing_zero = 0 + direct_basis_pairs = direct_dense_pairs = adjoint_pairs = 0 + digest = hashlib.sha256() + rng = random.Random(SEED) + for pi in permutations(range(group.size)): + checked += 1 + auto, symmetric = group.structural(pi) + witness = group.kernel_witness(pi, pi) + passes = witness is None + require(passes == symmetric, f"classification disagreement on {moduli}: {pi}") + require(not symmetric or auto, "symmetric classification without homomorphism") + digest.update(bytes(pi) + bytes((auto, passes))) + if witness is not None: + require(ring.roots[witness[2]] != ring.roots[witness[3]], + "kernel mismatch collapsed in exact ring") + rejected_fixing_zero += int(pi[0] == 0) + automorphisms += int(auto) + if auto: + rho = group.adjoint(pi) + require(group.kernel_witness(pi, rho) is None, "adjoint repair failed") + adjoint_pairs += 1 + # These are all automorphisms, including the ones rejected by + # same-layout cancellation. Check their repaired direct programs. + u = [rng.randrange(-2, 3) for _ in group.points] + v = [rng.randrange(-2, 3) for _ in group.points] + require(pipeline_difference(group, ring, pi, rho, u, v) is None, + "direct adjoint convolution failed") + direct_dense_pairs += 1 + if passes: + accepted += 1 + # All ordered basis pairs, not a scalar or sampled operator check. + for a in range(group.size): + for b in range(group.size): + require(pipeline_difference(group, ring, pi, pi, + basis(group.size, a), basis(group.size, b)) is None, + "direct same-layout basis convolution failed") + direct_basis_pairs += 1 + require(checked == factorial(group.size), "incomplete permutation census") + return {"moduli": list(moduli), "permutations": checked, + "automorphisms": automorphisms, "same_layout_accepted": accepted, + "rejected_fixing_zero": rejected_fixing_zero, + "adjoint_pairs_checked": adjoint_pairs, + "direct_basis_pairs": direct_basis_pairs, + "direct_dense_pairs": direct_dense_pairs, + "classification_sha256": digest.hexdigest()} + + +def negative_control(name, moduli, pi, rho=None, inverse_sign=1, + expected_numerator_factor=None): + group = Group(moduli) + ring = ExactRoots(group.period) + rho = pi if rho is None else rho + group.check_permutation(pi) + group.check_permutation(rho) + # Deterministic first differing basis pair, with a complete exact output + # witness. A failed search is an error, not evidence against the interface. + for a in range(group.size): + for b in range(group.size): + witness = pipeline_difference(group, ring, pi, rho, + basis(group.size, a), basis(group.size, b), inverse_sign, + expected_numerator_factor) + if witness is not None: + return {"name": name, "moduli": list(moduli), + "forward_read_map": list(pi), "inverse_read_map": list(rho), + "inverse_sign": inverse_sign, + "expected_numerator_factor": (group.size if + expected_numerator_factor is None else expected_numerator_factor), + "u_basis": list(group.points[a]), + "v_basis": list(group.points[b]), "witness": witness} + raise ValueError(f"negative control not detected: {name}") + + +def controls(): + # Lexicographic coordinate numbering is used throughout. + shear = Group((3, 3)) + pi = tuple(shear.index[((x + y) % 3, y)] for x, y in shear.points) + inverse = tuple(pi.index(i) for i in range(shear.size)) + cyclic5 = tuple(2 * i % 5 for i in range(5)) + negatives = [ + negative_control("translation does not cancel", (3,), (1, 2, 0)), + negative_control("zero-fixing involutive bit reversal does not cancel", + (4,), (0, 2, 1, 3)), + negative_control("an automorphism need not be self-adjoint", + (2, 2), (0, 3, 2, 1)), + negative_control("inverse is not the adjoint", (3, 3), pi, inverse), + negative_control("inverting a valid source multiplier is wrong", + (5,), cyclic5, tuple(cyclic5.index(i) for i in range(5))), + negative_control("omitting Fourier-sign conjugation", (5,), cyclic5, + inverse_sign=-1), + negative_control("one factor of N is missing from normalization", + (5,), cyclic5, expected_numerator_factor=25), + negative_control("one extra factor of N in normalization", + (5,), cyclic5, expected_numerator_factor=1), + ] + nonunit = tuple(2 * i % 6 for i in range(6)) + try: + Group((6,)).check_permutation(nonunit) + except ValueError: + admission = {"name": "nonunit multiplier rejected", "moduli": [6], + "map": list(nonunit), "reason": "not a permutation"} + else: + raise ValueError("nonunit multiplier accepted") + positives = [] + for moduli, mapping, label in ( + ((5,), cyclic5, "valid multiplier need not be involutive"), + ((2, 2), (0, 3, 1, 2), "self-adjoint mixing need not be involutive"), + ((2, 4), tuple(Group((2, 4)).index[((x + y) % 2, (2*x + y) % 4)] + for x, y in Group((2, 4)).points), + "unequal cyclic moduli need weighted adjoints"), + ((3, 3), pi, "a distinct adjoint repairs the shear"), + ): + group = Group(moduli) + group.check_permutation(mapping) + require(group.structural(mapping)[0], "positive control not an automorphism") + rho = group.adjoint(mapping) + require(group.kernel_witness(mapping, rho) is None, "positive kernel failed") + ring = ExactRoots(group.period) + for a in range(group.size): + for b in range(group.size): + require(pipeline_difference(group, ring, mapping, rho, + basis(group.size, a), basis(group.size, b)) is None, + "positive control pipeline failed") + positives.append({"name": label, "moduli": list(moduli), + "forward_read_map": list(mapping), "inverse_read_map": list(rho), + "direct_basis_pairs": group.size ** 2}) + return {"convolution_counterexamples": negatives, + "admission_rejections": [admission], "positive_controls": positives} + + +def source_multipliers(): + count = 0 + for q in range(2, SEED + 1): + group = Group((q,)) + for c in range(1, q): + if gcd(c, q) == 1: + pi = tuple(c * j % q for j in range(q)) + require(group.structural(pi) == (True, True), "source unit rejected") + require(group.kernel_witness(pi, pi) is None, "source unit kernel failed") + count += 1 + return {"cyclic_moduli_inclusive": [2, SEED], "unit_multipliers": count} + + +def check_sources(root): + source = json.loads((PACKAGE / "SOURCE.json").read_text()) + for name, expected in source["files"].items(): + actual = hashlib.sha256((root / name).read_bytes()).hexdigest() + require(actual == expected, f"source pin mismatch: {name}") + return source + + +def build_receipt(root): + source = check_sources(root) + rows = [census(dims) for dims in DOMAIN] + return {"format": "source-permutation-audit-v1", "assignment_seed": SEED, + "base_commit": source["base_commit"], + "source_manifest_sha256": hashlib.sha256((PACKAGE / "SOURCE.json").read_bytes()).hexdigest(), + "arithmetic": "integer character residues and exact cyclotomic integer polynomials", + "census": rows, "source_multipliers": source_multipliers(), + "controls": controls(), + "total_permutations": sum(row["permutations"] for row in rows), + "total_direct_basis_pairs": sum(row["direct_basis_pairs"] for row in rows), + "total_direct_dense_pairs": sum(row["direct_dense_pairs"] for row in rows), + "scope": "Finite replay plus written all-group proof; no formal proof, source defect or new kappa claimed."} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, default=PACKAGE.parents[1], + help="repository root for immutable source checks") + mode = parser.add_mutually_exclusive_group() + mode.add_argument("--check", type=Path, default=PACKAGE / "EXPECTED.json") + mode.add_argument("--write", type=Path, help="write a fresh deterministic receipt") + args = parser.parse_args() + receipt = build_receipt(args.root) + if args.write: + args.write.write_text(json.dumps(receipt, indent=2, sort_keys=True) + "\n") + else: + expected = json.loads(args.check.read_text()) + require(receipt == expected, "receipt mismatch") + print(f"PASS_SOURCE_PERMUTATION: {receipt['total_permutations']} permutations; " + f"{receipt['total_direct_basis_pairs']} census basis pairs; " + f"{receipt['total_direct_dense_pairs']} dense adjoint pairs; " + "8 exact counterexamples; 1 admission rejection; 4 positive controls") + + +if __name__ == "__main__": + main()