From 3d5f42dc084b8c9e1ad73318458751e851752a10 Mon Sep 17 00:00:00 2001 From: Ali Romman <226877337+aliromman@users.noreply.github.com> Date: Sun, 25 Oct 2026 01:43:01 -0500 Subject: [PATCH] Recognize finite etale multiplication tensors with explicit transporters --- research/etale-tensor-recognition/README.md | 159 +++++++++++++++ .../etale-tensor-recognition/SOURCES.json | 24 +++ research/etale-tensor-recognition/TASK.json | 95 +++++++++ research/etale-tensor-recognition/VERIFY.json | 34 ++++ .../etale-tensor-recognition/recognize.py | 189 +++++++++++++++++ research/etale-tensor-recognition/verify.py | 192 ++++++++++++++++++ 6 files changed, 693 insertions(+) create mode 100644 research/etale-tensor-recognition/README.md create mode 100644 research/etale-tensor-recognition/SOURCES.json create mode 100644 research/etale-tensor-recognition/TASK.json create mode 100644 research/etale-tensor-recognition/VERIFY.json create mode 100644 research/etale-tensor-recognition/recognize.py create mode 100644 research/etale-tensor-recognition/verify.py diff --git a/research/etale-tensor-recognition/README.md b/research/etale-tensor-recognition/README.md new file mode 100644 index 0000000000..0b0ac1e3d6 --- /dev/null +++ b/research/etale-tensor-recognition/README.md @@ -0,0 +1,159 @@ +# Recognition and transporters for finite etale multiplication tensors + +This proof removes the orbit promise from the finite-etale tensor interface +in Nick Petalas's [PR #5252](https://github.com/CrocSwap/integer-mult-bounds/pull/5252). +It recognizes the full nonzero finite-etale multiplication class and constructs +three explicit changes of basis. The complete kappa calculation is unchanged. +This is an algebraic interface result. No physical supplier is supplied. + +## Exact criterion + +Let K=F_q and let nu:U times V -> W be any bilinear map, with all three spaces +of dimension n>=1. Compute the left nucleus by linear equations: + +```text +N_L = {(X,Y): nu(Xu,v)=Y nu(u,v) for every u,v}. +``` + +Then nu is isotopic to multiplication in a finite etale K-algebra if and only +if all of the following conditions hold: + +1. The nucleus has dimension n, and each projection to End(U) and End(W) + is injective. +2. Its image B in End(W) is commutative. +3. The K-linear Frobenius map b -> b^q on B is injective. +4. The right radical {v: nu(U,v)=0} is zero. + +All conditions use linear algebra and matrix products. The test does not +assume an invertible slice, an algebra basis or a list of field factors. +It rejects tensors outside this class. A failed condition is not a statement +about their other algebraic or physical uses. + +## Proof and constructive transporter + +For canonical multiplication in a commutative unital algebra A, putting v=1 +and then u=1 in the nucleus equation shows X=Y=L_a. Independent changes of +basis conjugate its two projections. For finite etale A the resulting +nucleus has dimension n and both projections are faithful; it is commutative, +Frobenius is bijective, and the right radical is zero. This proves necessity. + +For sufficiency, the nucleus contains (I,I) and is closed under pairwise +composition. The injective projections identify it with a unital matrix +algebra B and a faithful representation rho on U. Condition 2 makes B +commutative. Condition 3 implies that B has no nonzero nilpotent: an iterated +q-power would kill such an element, contrary to injectivity. A finite reduced +commutative algebra over a finite field is a product of finite fields, +B=product_i K_i. This classical structure fact is a premise, not a new claim. + +Both U and W are faithful B-modules of dimension dim_K B=n. In a module for +this product, each field factor K_i occurs with a positive integer multiplicity +m_i when the action is faithful. Since sum_i m_i dim_K K_i=n=sum_i dim_K K_i, +every m_i is one. Thus both modules are regular. This dimension argument is +the required regularity implication. Faithfulness in a larger dimension +would not suffice. + +Compute the primitive idempotents e_i by the predecessor's fixed-algebra +procedure. On each e_i U choose a nonzero u_i, and on each e_i W choose a +nonzero w_i. Set u0=sum_i u_i and w0=sum_i w_i. Each component is one-dimensional +over its field K_i, so the maps + +```text +alpha:B -> U, a -> rho(a) u0, +gamma:B -> W, a -> a w0 +``` + +are isomorphisms. Define Phi:V -> W by Phi(v)=nu(u0,v). If Phi(v)=0, then +nu(alpha(a),v)=a Phi(v)=0 for every a. Since alpha is onto, condition 4 gives +v=0. Hence Phi is an isomorphism. Put beta=gamma^-1 Phi:V -> B. Then + +```text +nu(alpha(a), beta^-1(b)) = gamma(ab) for every a,b in B. +``` + +These are the requested three invertible maps. Their complete basis-product +identities give a direct certificate. B is recovered with a multiplication +table. A standard irreducible-polynomial presentation of each field is not +needed for this certificate. + +The credited idempotent procedure uses C={b in B:b^q=b}, which is K^r. +Start with I, and for each basis c of C replace each nonzero current h +by the nonzero h*(I-(c-lambda I)^(q-1)) for lambda in K. These are orthogonal +indicator idempotents. The final pieces are primitive because a basis of K^r +separates its coordinates. Choose a nonzero matrix column in each piece for +u_i and w_i. The rank of e_i on W is its field degree. This also recovers the +degree multiset needed for the predecessor determinant formula. + +The nucleus has 2n squared unknowns and n cubed equations. All subsequent +spaces have dimension at most n squared. There are at most n fixed-basis +vectors, n pieces and q scalars at each refinement. For fixed q, the complete +recognition and transporter algorithm is deterministic polynomial time in n. +For varying q this enumeration gives a polynomial dependence on q, not on +log q. No large-field bit-complexity claim is made. + +## Exact evidence and reproduction + +```sh +python3.12 -B scripts/research_package.py --package research/etale-tensor-recognition +``` + +Use Python 3.12 and the standard library. Normal and optimized modes must print +`PASS_ETALE_TENSOR_RECOGNITION` and the launcher must print +`PASS_REGISTERED_RESEARCH_PACKAGE`. The standalone implementation uses exact +prime-field fixtures. The written proof covers all finite base fields. + +The checker classifies every binary two-by-two-by-two tensor. Its independent +reference is the union of all GL2-cubed images of split F2-squared and F4 +multiplication. The reference does not use the nucleus test. Exactly 120 of +256 tensors are accepted; 54 fail the reducedness test and 82 fail the nucleus +dimension test. Every accepted tensor gets a literal transporter. + +The full run checks 326 tensors, 187 accepted presentations, 955 transported +basis products and 21 controls. Sixty changed presentations over F2,F3,F5 +include repeated field factors and three independent basis changes through +dimension four. A separate certificate check verifies the algebra's unit, +associativity, commutativity, Frobenius and all transported basis products. +Controls reject nilpotent algebras, noncommutative matrix algebras, twelve +changed transporter entries and invalid inputs. Finite evidence does not +establish proof-kernel acceptance. + +## Sources and limits + +The [pinned predecessor proof](https://github.com/CrocSwap/integer-mult-bounds/blob/32890bc5de034ab8fc93ac88ead7279a6a416d04/research/etale-tensor-determinant-image/README.md) +supplies the left-nucleus extraction, fixed-algebra spectral projectors, +determinant formula and explicit recognition question. The new step is the +necessary-and-sufficient criterion and constructive three-map certificate. +It uses classical semisimple module structure. It does not replace the +predecessor's determinant-sign classification. + +The live overlap refresh also found apoursh's +[PR #5502](https://github.com/CrocSwap/integer-mult-bounds/pull/5502). +That result recognizes supplied Frobenius-truncated prime-field algebra tables. +The present result recognizes arbitrary-basis finite-etale tensors. Their +classes and input contracts differ. No executable from that work is used. +The overlap search covered all tracked package notes, open PR bodies and +paginated Discussions; unpublished work and arbitrary historical diffs are +outside coverage. + +Nilpotent multiplication tensors and arbitrary tensor isomorphism are outside +scope. The code is a bounded exact research implementation. Physical preparation, +movement, precision, supplier admission and complete recursive costs remain +open. No predecessor executable, selected cold replay, Lean or Docker run, +independent human review or formal kernel acceptance is claimed. Frozen +sources are unchanged. + +## Open next questions + +Extend recognition to one specified nonreduced local algebra class. Start with +a recovered local nucleus and test whether both faithful modules have cyclic +vectors. The dimension argument above fails for nonsemisimple algebras, so a +correct cyclic-vector construction or a counterexample is required before +using the same transporter formula. Coordinate with the separate supplied-table +recognition work before extending its scope. + +## Credit + +Nick Petalas (@petalas) supplies PR #5252 and its recognition handoff. David +Ganz and the tensor-action atlas retain the attribution in that pinned source. +Finite algebra decomposition and regular-module theory are classical. Proof +and checks by Ali Romman with substantial OpenAI Codex assistance; Apache-2.0. +No global literature-priority claim is made. diff --git a/research/etale-tensor-recognition/SOURCES.json b/research/etale-tensor-recognition/SOURCES.json new file mode 100644 index 0000000000..094d3e8745 --- /dev/null +++ b/research/etale-tensor-recognition/SOURCES.json @@ -0,0 +1,24 @@ +{ + "schema_version": 1, + "base_commit": "32890bc5de034ab8fc93ac88ead7279a6a416d04", + "files": [ + { + "path": "certificates/selected-result.json", + "usage": "provenance", + "git_blob": "fc3d4ba105e1f097d0d7d051a308281e86ab1446", + "sha256": "66974efdced4083c00025f97d95d83909a226cdf801edb9301c195c6641f6903" + }, + { + "path": "docs/research/agent-task-catalog.json", + "usage": "provenance", + "git_blob": "f126dd2e2dad2b669e75d1d0d0bd05c600725602", + "sha256": "f7a48bfa2659b88378282a8eee7d9fe52cdf505594124c4d1686b58510fffd72" + }, + { + "path": "research/etale-tensor-determinant-image/README.md", + "usage": "provenance", + "git_blob": "0f07df6ce2a8c43ea1fcc5f0081ee3532da28208", + "sha256": "2ec18042a1a63d5fd5da246f4f6060c5fb66cff338f25a2a1b95e3ea8b4f0fba" + } + ] +} diff --git a/research/etale-tensor-recognition/TASK.json b/research/etale-tensor-recognition/TASK.json new file mode 100644 index 0000000000..a0ea75be7c --- /dev/null +++ b/research/etale-tensor-recognition/TASK.json @@ -0,0 +1,95 @@ +{ + "schema_version": 1, + "task_id": "formal-interface-lemma", + "priority_revision": 7, + "base_commit": "32890bc5de034ab8fc93ac88ead7279a6a416d04", + "original_card": { + "id": "formal-interface-lemma", + "question": "Can one uncovered interface implication be formalized with exact caller and certificate bindings?", + "starting_points": [ + "docs/research/research-family-status.md", + "certificates/selected-result.json", + "research/gate-operand-parity-lean/README.md", + "research/staged544-certified-endpoint/README.md", + "research/unconditional-batched-crt/README.md" + ], + "methods": [ + "Formalize a missing boundary lemma identified by the selected review.", + "Audit a formal theorem-to-certificate binding independently." + ], + "acceptance_gate": "State the exact formalized proposition and remaining assumptions; avoid duplicate arithmetic formalizations already present.", + "first_step": { + "command": [ + "python3", + "-B", + "scripts/research_baseline.py" + ], + "expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY", + "inspect": "Inventory the gate-operand parity lemma (#3807) and certified endpoint arithmetic (#3857). Neither is a full multiplication proof. For #4100, audit the compact-switch statement and its hypotheses before formalizing it.", + "scope": "Source-binding orientation only; no scientific replay, theorem acceptance or supplier admission. Budget any heavy replay separately." + }, + "first_experiment": "Write one missing statement with quantified hypotheses, actual caller and existing coverage. A compact-switch lemma is a candidate only after its mathematical contract is reviewed. Establish a bounded proof plan before a separately budgeted proof-assistant build; do not rerun large Lean jobs as orientation.", + "bottleneck_link": "A missing interface proof can reduce the conditional gap even if it does not change kappa.", + "allowed_variations": [ + "Narrow to one formal lemma or one theorem-to-certificate binding." + ], + "scope_change_triggers": [ + "Formalizing already-covered arithmetic and claiming the full multiplication theorem." + ], + "overlap_terms": [ + "hyperplane end", + "formal interface", + "Lean binding", + "3807", + "3857", + "4100", + "gate-operand-parity-lean", + "staged544-certified-endpoint", + "unconditional-batched-crt" + ], + "existing_checks": [ + { + "scope": "PR #3807: scoped gate-operand parity formalization.", + "evidence": "research/gate-operand-parity-lean/README.md", + "remaining": "Exact theorem-to-caller binding and genuinely uncovered interface lemmas.", + "status": "author-checked", + "source_commit": "4c3530f42a6abcc48e7a6bcffd6c901911dc8269", + "selected_record_sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129" + }, + { + "scope": "PR #3857: certified real-power moment/endpoint arithmetic.", + "evidence": "research/staged544-certified-endpoint/README.md", + "remaining": "Uniform algorithmic implications, not another endpoint digit.", + "status": "author-checked", + "source_commit": "ef4315b8caa605e47a2515004845eb2d2ed71a86", + "selected_record_sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129" + }, + { + "scope": "PR #4100: compact-switch formalization proposed as future validation.", + "evidence": "research/unconditional-batched-crt/README.md", + "remaining": "Audit the statement and assumptions before a formal proof campaign.", + "status": "author-checked", + "source_commit": "337d04939571a637aeab54a0f58f54fde811663a", + "selected_record_sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129" + } + ] + }, + "scope_status": "changed", + "actual_scope": "Recognize finite etale multiplication tensors and construct explicit algebra transporters.", + "scope_change_reason": "Take PR5252 explicit recognition handoff as a scoped algebraic interface result.", + "bottleneck_link": "Remove the orbit promise for the retained finite-etale tensor-character interface.", + "budget_minutes": 60, + "stop_rule": "Prove the criterion and test complete finite tensors and transporters; no physical supplier claim.", + "outcome": "completed", + "evidence": "Written proof, full binary tensor census, independent orbit enumeration and transporter checks.", + "author": "Ali Romman", + "ai_assistance": "Substantial OpenAI Codex assistance.", + "open_questions": [ + { + "question": "Can one nonreduced local multiplication class be recognized with a transporter?", + "why_it_matters": "It extends recognition beyond semisimple regular modules.", + "first_step": "Test cyclicity of both faithful modules of the recovered local nucleus.", + "blocked_by": "The dimension proof of regularity does not hold for arbitrary nonsemisimple modules." + } + ] +} diff --git a/research/etale-tensor-recognition/VERIFY.json b/research/etale-tensor-recognition/VERIFY.json new file mode 100644 index 0000000000..f9d82a7ff4 --- /dev/null +++ b/research/etale-tensor-recognition/VERIFY.json @@ -0,0 +1,34 @@ +{ + "schema_version": 1, + "status": "ready", + "python": "3.12", + "requirements": [], + "inputs": [], + "commands": [ + { + "argv": [ + "{python}", + "-I", + "-W", + "error", + "-B", + "{package}/verify.py" + ], + "timeout_seconds": 120, + "checks": "Full binary tensor census against independent GL orbits and literal transporter checks." + }, + { + "argv": [ + "{python}", + "-I", + "-W", + "error", + "-B", + "-O", + "{package}/verify.py" + ], + "timeout_seconds": 120, + "checks": "Full binary tensor census against independent GL orbits and literal transporter checks." + } + ] +} diff --git a/research/etale-tensor-recognition/recognize.py b/research/etale-tensor-recognition/recognize.py new file mode 100644 index 0000000000..f1bb574dad --- /dev/null +++ b/research/etale-tensor-recognition/recognize.py @@ -0,0 +1,189 @@ +"""Recognize finite etale multiplication in exact prime-field fixtures. + +Ali Romman with substantial OpenAI Codex assistance. Apache-2.0. +""" + + +def require(condition, message): + if not condition: + raise ValueError(message) + + +def rref(rows, p): + a = [list(row) for row in rows] + pivots = [] + k = 0 + if not a: + return a, pivots + for j in range(len(a[0])): + i = next((i for i in range(k, len(a)) if a[i][j] % p), None) + if i is None: + continue + a[k], a[i] = a[i], a[k] + inv = pow(a[k][j], -1, p) + a[k] = [x * inv % p for x in a[k]] + for i in range(len(a)): + if i != k: + c = a[i][j] + a[i] = [(x - c * y) % p for x, y in zip(a[i], a[k])] + pivots.append(j) + k += 1 + if k == len(a): + break + return a, pivots + + +def rank(a, p): + return len(rref(a, p)[1]) + + +def nullspace(a, p): + reduced, pivots = rref(a, p) + n = len(a[0]) + out = [] + for j in range(n): + if j in pivots: + continue + v = [0] * n + v[j] = 1 + for k, i in enumerate(pivots): + v[i] = -reduced[k][j] % p + out.append(v) + return out + + +def transpose(a): + return [list(c) for c in zip(*a)] + + +def flat(a): + return [x for row in a for x in row] + + +def identity(n): + return [[int(i == j) for j in range(n)] for i in range(n)] + + +def multiply(a, b, p): + return [[sum(x * y for x, y in zip(r, c)) % p for c in zip(*b)] for r in a] + + +def matvec(a, v, p): + return [sum(x * y for x, y in zip(row, v)) % p for row in a] + + +def add(a, b, p, scale=1): + return [[(x + scale * y) % p for x, y in zip(r, s)] for r, s in zip(a, b)] + + +def inverse(a, p): + n = len(a) + b, pivots = rref([r + s for r, s in zip(a, identity(n))], p) + require(pivots[:n] == list(range(n)), 'singular matrix') + return [r[n:] for r in b] + + +def power(a, k, p): + out = identity(len(a)) + while k: + if k & 1: + out = multiply(out, a, p) + a = multiply(a, a, p) + k //= 2 + return out + + +def combine(basis, coefficients, p): + n = len(basis[0]) + out = [[0] * n for _ in range(n)] + for c, a in zip(coefficients, basis): + out = add(out, a, p, c) + return out + + +def coordinates(basis, target, p): + columns = [flat(a) for a in basis] + n = len(columns) + a, pivots = rref([list(row) + [v] for row, v in zip(zip(*columns), flat(target))], p) + require(pivots == list(range(n)), 'target outside independent matrix basis') + return [a[i][-1] for i in range(n)] + + +def evaluate(t, u, v, p): + n = len(t) + return [sum(u[i] * v[j] * t[i][j][k] for i in range(n) for j in range(n)) % p for k in range(n)] + + +def nucleus(t, p): + n = len(t) + equations = [] + for i in range(n): + for j in range(n): + for k in range(n): + row = [0] * (2 * n * n) + for a in range(n): + row[a * n + i] = (row[a * n + i] + t[a][j][k]) % p + for b in range(n): + row[n * n + k * n + b] = (row[n * n + k * n + b] - t[i][j][b]) % p + equations.append(row) + kernel = nullspace(equations, p) + X = [[[v[i * n + j] for j in range(n)] for i in range(n)] for v in kernel] + Y = [[[v[n * n + i * n + j] for j in range(n)] for i in range(n)] for v in kernel] + return X, Y + + +def primitive_parts(Y, frobenius, p): + n = len(Y) + fixed = nullspace(add(frobenius, identity(n), p, -1), p) + parts = [identity(n)] + for c in fixed: + matrix = combine(Y, c, p) + refined = [] + for h in parts: + for scalar in range(p): + shifted = add(matrix, identity(n), p, -scalar) + block = multiply(h, add(identity(n), power(shifted, p - 1, p), p, -1), p) + if any(flat(block)): + refined.append(block) + parts = refined + return parts + + +def transporter(t, X, Y, parts, p): + n = len(t) + u0, w0 = [0] * n, [0] * n + for e in parts: + ex = combine(X, coordinates(Y, e, p), p) + u = next(c for c in zip(*ex) if any(c)) + w = next(c for c in zip(*e) if any(c)) + u0 = [(x + y) % p for x, y in zip(u0, u)] + w0 = [(x + y) % p for x, y in zip(w0, w)] + U = transpose([matvec(x, u0, p) for x in X]) + W = transpose([matvec(y, w0, p) for y in Y]) + phi = transpose([evaluate(t, u0, v, p) for v in identity(n)]) + V = inverse(multiply(inverse(W, p), phi, p), p) + require(rank(U, p) == n, 'cyclic input construction') + table = [[coordinates(Y, multiply(a, b, p), p) for b in Y] for a in Y] + return dict(table=table, U=U, V=V, W=W, degrees=sorted(rank(e, p) for e in parts)) + + +def recognize(t, p): + require(p in (2, 3, 5, 7), 'unsupported fixture field') + n = len(t) + require(n > 0 and all(len(row) == n and all(len(v) == n for v in row) for row in t), 'tensor shape') + require(all(type(x) is int and 0 <= x < p for row in t for v in row for x in v), 'field entries') + X, Y = nucleus(t, p) + if len(X) != n: + return None, 'nucleus dimension' + if rank([flat(a) for a in X], p) != n or rank([flat(a) for a in Y], p) != n: + return None, 'nucleus projection' + if any(multiply(a, b, p) != multiply(b, a, p) for a in Y for b in Y): + return None, 'noncommutative nucleus' + frobenius = transpose([coordinates(Y, power(a, p, p), p) for a in Y]) + if rank(frobenius, p) != n: + return None, 'nilpotent nucleus' + right = transpose([[t[i][j][k] for i in range(n) for k in range(n)] for j in range(n)]) + if rank(right, p) != n: + return None, 'right kernel' + parts = primitive_parts(Y, frobenius, p) + return transporter(t, X, Y, parts, p), None diff --git a/research/etale-tensor-recognition/verify.py b/research/etale-tensor-recognition/verify.py new file mode 100644 index 0000000000..c53aeb848c --- /dev/null +++ b/research/etale-tensor-recognition/verify.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python3 +"""Check recognition against independent orbits and literal transporters. + +Ali Romman with substantial OpenAI Codex assistance. Apache-2.0. +""" +from pathlib import Path +import sys +import json +from itertools import product +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from recognize import require, rank, inverse, matvec, identity, transpose, evaluate, recognize, rref + + +def quotient(p, polynomial): + n = len(polynomial) - 1 + require(polynomial[-1] == 1, 'monic fixture') + powers = [] + for k in range(2 * n - 1): + v = [0] * max(n, k + 1) + v[k] = 1 + for j in range(len(v) - 1, n - 1, -1): + c = v[j] + for i in range(n + 1): + v[j - n + i] = (v[j - n + i] - c * polynomial[i]) % p + powers.append(v[:n]) + return [[powers[i + j] for j in range(n)] for i in range(n)] + + +def direct_sum(tables): + n = sum(map(len, tables)) + out = [[[0] * n for _ in range(n)] for _ in range(n)] + offset = 0 + for table in tables: + d = len(table) + for i, j, k in product(range(d), repeat=3): + out[offset + i][offset + j][offset + k] = table[i][j][k] + offset += d + return out + + +def change(t, P, Q, R, p): + Pi, Qi = transpose(inverse(P, p)), transpose(inverse(Q, p)) + return [[matvec(R, evaluate(t, u, v, p), p) for v in Qi] for u in Pi] + + +def tensor_key(t): + return tuple(x for row in t for v in row for x in v) + + +def check_certificate(t, c, p): + n = len(t) + a = c['table'] + U, V, W = (c[k] for k in ('U', 'V', 'W')) + require(all(rank(x, p) == n for x in (U, V, W)), 'transporter singular') + for i, j in product(range(n), repeat=2): + got = evaluate(t, [r[i] for r in U], [r[j] for r in V], p) + require(got == matvec(W, a[i][j], p), 'transporter identity') + require(a[i][j] == a[j][i], 'commutative table') + for k in range(n): + left = evaluate(a, a[i][j], identity(n)[k], p) + right = evaluate(a, identity(n)[i], a[j][k], p) + require(left == right, 'associative table') + equations = [[a[i][j][k] for i in range(n)] + [int(j == k)] + for j, k in product(range(n), repeat=2)] + reduced, pivots = rref(equations, p) + require(pivots == list(range(n)), 'unit equations') + unit = [reduced[i][-1] for i in range(n)] + columns = [] + for x in identity(n): + v = unit + for _ in range(p): + v = evaluate(a, v, x, p) + columns.append(v) + require(rank(columns, p) == n, 'reduced table') + return n * n + + +def gl(n, p): + out = [] + for v in product(range(p), repeat=n * n): + a = [list(v[i * n:(i + 1) * n]) for i in range(n)] + if rank(a, p) == n: + out.append(a) + return out + + +def full_binary_census(): + G = gl(2, 2) + split = direct_sum([quotient(2, [0, 1])] * 2) + field = quotient(2, [1, 1, 1]) + expected = set() + for t in (split, field): + for P, Q, R in product(G, repeat=3): + expected.add(tensor_key(change(t, P, Q, R, 2))) + got = set() + rejects = {} + identities = 0 + for flat in product(range(2), repeat=8): + t = [[list(flat[(i * 2 + j) * 2:(i * 2 + j + 1) * 2]) for j in range(2)] for i in range(2)] + c, reason = recognize(t, 2) + if c is not None: + got.add(flat) + identities += check_certificate(t, c, 2) + else: + rejects[reason] = rejects.get(reason, 0) + 1 + require(got == expected, 'complete binary tensor orbit classification') + return len(got), identities, rejects + + +def changed_fixtures(p): + base = quotient(p, [0, 1]) + quad = quotient(p, {2: [1, 1, 1], 3: [1, 0, 1], 5: [2, 0, 1]}[p]) + contexts = [(base, [1]), (direct_sum([base] * 3), [1, 1, 1]), (quad, [2]), + (direct_sum([quad, base]), [1, 2]), (direct_sum([quad] * 2), [2, 2])] + identities = 0 + for t, degrees in contexts: + n = len(t) + for trial in range(4): + maps = [] + for salt in range(3): + a = identity(n) + if n > 1: + for step in range(n + trial + 1): + i = (step + salt) % n + j = (i + 1) % n + a[i] = [(x + (salt + trial + 1) * y) % p for x, y in zip(a[i], a[j])] + a = a[1:] + a[:1] + maps.append(a) + transformed = change(t, *maps, p) + c, reason = recognize(transformed, p) + require(c is not None and c['degrees'] == degrees, 'transformed fixture') + identities += check_certificate(transformed, c, p) + return identities + + +def negative_controls(): + controls = 0 + for p in (2, 3, 5): + c, reason = recognize(quotient(p, [0, 0, 1]), p) + require(c is None and reason == 'nilpotent nucleus', 'nilpotent rejection') + controls += 1 + t = [[[0] * 4 for _ in range(4)] for _ in range(4)] + for i, j, k, l in product(range(2), repeat=4): + if j == k: + t[2 * i + j][2 * k + l][2 * i + l] = 1 + c, reason = recognize(t, p) + require(c is None and reason == 'noncommutative nucleus', 'matrix algebra rejection') + controls += 1 + original = direct_sum([quotient(3, [0, 1])] * 2) + c, _ = recognize(original, 3) + for name in ('U', 'V', 'W'): + for i, j in product(range(2), repeat=2): + corrupt = {**c, name: [r[:] for r in c[name]]} + corrupt[name][i][j] = (corrupt[name][i][j] + 1) % 3 + try: + check_certificate(original, corrupt, 3) + except ValueError: + controls += 1 + else: + raise ValueError('corrupt transporter accepted') + for t, p in [([], 2), ([[[0]]], 4), ([[[2]]], 2)]: + try: + recognize(t, p) + except ValueError: + controls += 1 + else: + raise ValueError('unsupported input accepted') + return controls + + +def main(): + binary, identities, rejects = full_binary_census() + accepted = binary + tensors = 256 + for p in (2, 3, 5): + for scalar in range(p): + c, reason = recognize([[[scalar]]], p) + tensors += 1 + require((c is not None) == (scalar != 0), 'one-dimensional recognition') + if c is not None: + identities += check_certificate([[[scalar]]], c, p) + accepted += 1 + identities += changed_fixtures(p) + tensors += 20 + accepted += 20 + print('PASS_ETALE_TENSOR_RECOGNITION ' + json.dumps(dict(tensors=tensors, accepted=accepted, + basis_product_identities=identities, complete_binary_domain=dict(total=256, accepted=binary), + rejections=rejects, changed_presentations=60, controls=negative_controls()), sort_keys=True)) + + +if __name__ == '__main__': + main()