From e56a5bcf239fa0cd4ee57459af04c0ba7acb2c03 Mon Sep 17 00:00:00 2001 From: CubeJerry <80299683+CubeJerry@users.noreply.github.com> Date: Mon, 9 Nov 2026 01:54:04 +0800 Subject: [PATCH] Prove selected P104 all-column native caller closure --- .../NATIVE.json.gz | Bin 0 -> 93977 bytes .../selected-native-tensor-closure/PROOF.md | 411 ++++++++++++++++++ .../selected-native-tensor-closure/README.md | 205 +++++++++ .../RESULT.json | 154 +++++++ .../SOURCES.json | 60 +++ .../selected-native-tensor-closure/TASK.json | 88 ++++ .../VERIFY.json | 69 +++ .../caller_check.py | 212 +++++++++ .../controls.py | 133 ++++++ .../emit_recipes.py | 90 ++++ .../gauss_trace.py | 332 ++++++++++++++ .../native_check.py | 168 +++++++ .../source_algebra.py | 210 +++++++++ .../test_gauss_trace.py | 176 ++++++++ .../selected-native-tensor-closure/verify.py | 110 +++++ 15 files changed, 2418 insertions(+) create mode 100644 research/selected-native-tensor-closure/NATIVE.json.gz create mode 100644 research/selected-native-tensor-closure/PROOF.md create mode 100644 research/selected-native-tensor-closure/README.md create mode 100644 research/selected-native-tensor-closure/RESULT.json create mode 100644 research/selected-native-tensor-closure/SOURCES.json create mode 100644 research/selected-native-tensor-closure/TASK.json create mode 100644 research/selected-native-tensor-closure/VERIFY.json create mode 100644 research/selected-native-tensor-closure/caller_check.py create mode 100644 research/selected-native-tensor-closure/controls.py create mode 100644 research/selected-native-tensor-closure/emit_recipes.py create mode 100644 research/selected-native-tensor-closure/gauss_trace.py create mode 100644 research/selected-native-tensor-closure/native_check.py create mode 100644 research/selected-native-tensor-closure/source_algebra.py create mode 100644 research/selected-native-tensor-closure/test_gauss_trace.py create mode 100644 research/selected-native-tensor-closure/verify.py diff --git a/research/selected-native-tensor-closure/NATIVE.json.gz b/research/selected-native-tensor-closure/NATIVE.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..7eb40e7a8a172c188fcbc47b015905d5d38bf9ea GIT binary patch literal 93977 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

-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 literal 0 HcmV?d00001 diff --git a/research/selected-native-tensor-closure/PROOF.md b/research/selected-native-tensor-closure/PROOF.md new file mode 100644 index 0000000000..fffff67965 --- /dev/null +++ b/research/selected-native-tensor-closure/PROOF.md @@ -0,0 +1,411 @@ +# Exact native traces and the complete selected tensor interface + +Prepared for @CubeJerry with substantial OpenAI Codex assistance. Apache-2.0. +This is a source-bound algebraic certificate and a written universal argument. +It proves the exact-operator assertion labeled `native_all_columns` for the +pinned selected P104 native layer and its assembled caller. Physical +implementation, selected-rate dispatch, owned allocation/erasure and the other +framework premises remain separate. No admission record or kappa is changed. + +## 1. What must be composed + +The selected word is DreamingOfClouds' #6030 at canonical main +`7c49fe32c55de02c2c873655c804b16e4c3fef0a`. Its local certificate, the +all-forward P104 caller and the native tensor interpretation are different +objects. A dense equality at column width one cannot identify a sum of +different native group elements at all widths. Conversely, equality of two +*products* of unitary native gates is sufficient once every factor is lifted +columnwise and exterior scalar coefficients remain exterior. + +The local common-chart argument is already present in the P104 source and +#6635. #6043 independently checked every selected native operator densely. +#6546 supplies a numerical execution and tensor lift of the fixed local word, +and explicitly separates that from identifying recursive callers with it. +#5442 proves an all-width certificate for the different #5413 caller. +The contribution here is an independent exact trace acceptance mechanism and +its complete source-bound composition for the selected #6030 local word, +helpers, wrappers and all-forward caller. These predecessor identities are +credited, not asserted to be new lemmas. + +## 2. Exact traces of the native grammar + +Let the address dimension be n. Accepted primitive gates are an invertible +binary address permutation P_A, a binary translation X_v, a phase i^c, +a diagonal i^q(x), and + + C_v^s = ((1+s*i) I + (1-s*i) X_v)/2, s in {+1,-1}. + +Here q is a Z/4 polynomial with arbitrary linear coefficients and even mixed +coefficients. The source additionally requires its symmetric quadratic matrix +and diagonal-parity convention. The checker validates this grammar, every +binary inverse and every sign before using a trace. P_A and X_v permute an +orthonormal basis, diagonal phases have modulus one, and X_v is a self-adjoint +involution. On its +/- eigenspaces C_v^s has eigenvalues 1 and s*i. Thus every +accepted gate and every compared complete product is unitary, including its +normalization. Scalar ADD/COPY/SCALE programs are **not** passed to this test. + +Introduce n initial-address bits x and one binary branch t for each C gate. +The identity + + C_v^s = (1+s*i)/2 * sum_(t=0,1) i^(-s*t) X_v^t + +expresses every path amplitude as a known Gaussian dyadic scalar times +i^Q(x,t). Throughout the circuit each current address bit is an affine +Boolean form in x and previous branch variables. A phase gate contributes +only a quadratic polynomial modulo four: for a parity L=sum x_j mod2, + + L = sum x_j - 2 sum_(j=1 and an arbitrary spectator coefficient space S, write +L_f(U)=I_S tensor U^(tensor f), with the fixed address shuffle placing columns +in the source's slot order. Then + + L_f(UV)=L_f(U)L_f(V), L_f(U*)=L_f(U)*. + +This is a statement about full arrays, including arrays correlated across +columns and spectators: tensor basis vectors span the full space. It does +not require a pure-tensor input or zero spectator coordinates. + +Every accepted native equality is an equality between complete products. +Therefore each such equality lifts under L_f. A rank-r positive child lifts +to the identical elementary C on r*f selected binary axes. A monomial with +constant phase c contributes i^(c*f), and its remaining phase is the sum of +the one-column phase polynomials at all columns. A scalar coefficient a in a +register ADD or SCALE remains a, not a^f. In particular L_f(iI)=i^f I differs +from the exterior scalar iI in general. Likewise the width-one expansion of +C_v into two scalar-weighted translations is not an allowed termwise tensor +rewrite. The verifier tests both errors with exact width-two witnesses. + +The same argument extends a complete local address operator by identity to +any complement and conjugates it by any complete coordinate bijection. +Thus it supplies the algebraic input needed by #6878's occurrence-specific +full-field embeddings. It does not price those bijections or replace their +physical field-placement and ownership hypotheses. + +## 4. Complete caller join + +### Statement and hypotheses + +Fix exactly the WORD, RESULT, CONTROLLER, source chart definition, compiler +recipe payload and original port data pinned in SOURCES.json. Let f be any +positive integer. Each productive register contains an arbitrary vector in +S tensor (C^(2^145))^(tensor f), for any finite-dimensional coefficient space +S; equivalently one may use the source's 265 rows and its 120 unused identity +spectator rows. The 13,444 productive registers are independent inputs, not +copies or zero-filled helpers. Interpret native products by L_f, rational +register coefficients as exterior scalars, and INIT/CLEAR by the ideal owned +zero/reset operations in the source semantics. All coordinate maps below are +complete bijections, including their complementary coordinates. + +Under these hypotheses, the literal 18-forward-call supplier with its 16 +five-step wrappers, 15 thin queries, whole-idle jobs, scalar boundary programs +and whole-job adapters has the stated diagonal positive-Gaussian endpoint on +all 13,444 arbitrary productive inputs and returns its 29 local owned labels +to zero. Its paid and productive profiles are exactly those in the selected +RESULT. The proof is uniform in f and S. This is a theorem of the exact typed +algebraic program; it assumes ideal owned resets and native calls. It does not +establish an encoded tape implementation, an accelerated primitive tariff, +precision control, analytic recovery, or physical owned erasure. + +### Local chart induction + +Write Gamma_a for the full signed canonical chart associated with frame a. +The independent trace acceptance checks each selected native recipe as + + M_left G_r M_right = Gamma_b Gamma_a^*, + +where G_r is the product of positive elementary Gaussians on the first r +axes. This includes 2,886 distinct MOVE pairs, the additional whole-helper +pairs, and all 208 ordinary main adapters. The union has 2,922 pairs. The +identity is between full operators, so Section 3 gives its exact L_f lift. + +If a register's physical value is L_f(Gamma_a) z, a MOVE to b makes it +L_f(Gamma_b) z. ADD at a common current chart performs + + L_f(Gamma_a) z + c L_f(Gamma_a) w + = L_f(Gamma_a) (z+c w). + +SCALE is the same linearity identity; a COPY uses an owned zero target. +The coefficient c is retained outside L_f. All actual 18,404 ADD, 861 SCALE +and 23 COPY events satisfy their current-chart and ownership tests, rather +than relying on a declared common-chart convention. INIT establishes zero +and its recorded chart; CLEAR deletes only a currently owned value. This +last operation is an inherited ideal reset, not a deduction that arbitrary +nonzero data can be physically erased for free. + +The exact rational sparse replay starts with 799 independent formal inputs, +including the 29 owned entrance labels. After the 22,803 literal events it +checks every endpoint coefficient: the 770 public coordinates are unchanged +except Y_r -> Y_r+X_r at the 104 selected pairs; every owned entrance column +has been eliminated and every owned output is zero. There are 31 distinct +owned lifetimes, with peak 13. Finite verification determines this one fixed +word's rational identity. The preceding symbolic induction, rather than a +finite sweep of f values, proves that identity for all f and S. + +For each main port q put Q=C_q^+ and H_9=product_(j<9) C_(e_j)^+. +The actual ordinary main entry/exit recipes are checked against + + A_X = Gamma_initial-X Q^*, + A_Y = H_9 Q^* Gamma_final-Y^*. + +The source entrance frame of X is the one-vector frame [q], but its canonical +chart Gamma_initial-X need **not** equal the translation Gaussian Q. The +actual ordinary A_X above is therefore essential. The other two boundaries +are Gamma_initial-Y=I and Gamma_final-X=H_9. Combining the three nonzero rational endpoint paths with +these exact adapters gives, on the two main registers, + + F_0 = D_0 [[1,0],[Q^(-1),1]], D_0=H_9 Q^(-1). + +The checker verifies all three complete products separately at every port, +including their constant phases. The displayed 2-by-2 notation is a matrix +of native operators and exterior rational coefficients, not a matrix that +is itself tensor-powered. + +For every arbitrary helper, the chart endpoint is Gamma_b Gamma_a^*. +Apply M_right^* before the complete local word and M_left^* after it. Its +entire productive endpoint is then G_r. All 69 distinct helper boundary +types are checked as complete products; their multiplicities give all 562 +helper inputs. These normalizers are whole-job boundaries, not uncharged +adapters inserted at each recursive child. + +### Source raw contacts and arbitrary spectators + +The original raw construction uses 265 formal binary axes e_j. At stage s>0 +the actual route swaps the source's eight orthonormal q-complement vectors +b_i with its eight fresh axes e_(9+8(s-1)+i), and fixes q and the remaining +orthogonal complement. The source port data are checked to make +(q,b_0,...,b_7) an orthonormal basis. These eight disjoint orthonormal swaps +give a complete involutive orthogonal map R_s. The explicit binary formula +in caller_check.py is checked to perform exactly those swaps and fix q. + +A product of translation Gaussians has Fourier-character eigenvalue + + i^p(z), p(z)=sum_v c_v (v dot z mod2) (mod4). + +Its polynomial has linear and even mixed coefficients, and constant zero. +Polynomial equality therefore proves equality on every character; Fourier +characters form a basis. An even linear difference 2(t dot z) is exactly the +translation X_t, with absolute phase +1. The checker computes the complete +entry/exit translations and their inverses from these polynomials. Their +ordered payload equals the **already registered selected** route hash +`7f4f0ca768ffbc4f93dcff575592c4c67411f46b4289e45b73f715a2ad47b142`. + +This is more than comparing abstract frame labels. If a native convolution +path has phase p and the actual entry and exit maps are X_u P_R and X_v P_R, +their executed composition is + + X_v P_R A X_u P_R + = X_(v+R u) (P_R A P_R). + +Thus its eigenphase is p(R z)+2((v+R u) dot z). The checker uses the actual +four contact records to verify both diagonal paths as D_s and the cross +path as D_s Q^(-1), where D_0=H_9 Q^(-1) and, for s>0, D_s is the product +of the eight positive fresh-axis Gaussians. This checks 5,616 composed paths +through all 7,488 actual contacts, including spectator charts on entry and +exit. It does not only compare their marginal source/target phase profiles. + +Only stages 0,1,3,...,31,32 occur. All 120 rows of the other stages are fixed +by every actual contact and absent from its translations, main products and +idle products. Taking the nine source rows and the 17 retained eight-row +blocks identifies the actual 145-row supplier. This is a checked application +of the retained-row construction credited to #6878, not a new compression +claim. Every positive main/idle canonicalizer is explicitly completed on +all 145 retained coordinates; the unused 120 rows and arbitrary further +spectators are preserved as identity factors. Section 3 then transports +each equality through the complete occurrence embeddings in #6878. No +assumption about vanishing or independent spectator coordinates is used. + +The application to #6878 is source-compatible in a checkable sense. At its +head `41febf890fbbb852c6059d4c0cc8c593ae9dfa5c`, that package pins the same +CONTROLLER, original port data and caller implementation as SOURCES.json +here, with identical full SHA256 values. Its ordered retained set is +precisely the 145 indices just constructed, and its section 1 re-emits +positive canonicalizers with the selected positions followed by the full +retained complement, as done here. Its extension lemma takes a complete +ambient-m unit with endpoint G_r tensor I, uses a complete coordinate +bijection on entry and its inverse on return, and permits arbitrary +spectators. Those are exactly the operator and spectator hypotheses proved +here. Its compiler scalar contacts require routing entire participating +fields into a common coordinate order; our induction likewise uses exterior +scalars on complete aligned fields. Thus there is no model, width, phase or +scalar-type mismatch at that algebraic interface. The current package needs +no unmerged code to prove its own theorem. The larger compiler, ownership, +movement and cost premises of #6878 remain as stated there. + +### All-forward controller and whole-job completion + +Each physical LOCAL has forward orientation. The source's 16 reverse +interfaces use the literal five-event word + + Q_X^(-2), SIGNED_SWAP(X,Y), F_s, + Q_Y^(+2), SIGNED_SWAP(Y,X). + +With chronological action and SIGNED_SWAP(X,Y):(X,Y)->(Y,-X), this word +implements D_s [[1,-Q],[0,1]]. The minus sign is an exterior coefficient. +The equality Q^(+2)=Q^(-2)=X_q has absolute phase +1; it is checked at every +port. Each thin query realizes Q^(-1)=X_q Q by a positive rank-one Gaussian +conjugated through the complete port basis, plus its ordinary translation. +Its separate owned scratch label t has the explicit semantic macro + + INIT t=0; COPY t=X; apply Q^(-1) to t; ADD Y+=t; CLEAR t. + +It leaves X unchanged, replaces Y by Y+Q^(-1)X, and returns t to zero under +the stated ideal-reset semantics. The complete query basis and its inverse +restore the original full field coordinates before ADD. The scratch label +is outside the 29-label local owned bank and is reused only after CLEAR. +This makes the additional owned return explicit; it neither supplies a +physical erasure algorithm nor adds another paid native call. Each of the +15*104 thin queries still invokes one rank-one positive child. + +To verify the *actual* controller, source_algebra.py propagates each of the +32 final coordinate functionals backwards through every literal event and +both scalar boundary programs. Coefficients lie in the free Laurent ring + + Q[Q,Q^(-1),D_0,D_0^(-1),...,D_32,D_32^(-1)]. + +This is independent of the predecessor's forward row-matrix replay. It +checks all 1,024 entries of the resulting 32-by-32 operator and compares +the literal boundary programs with their stored matrices. There is no +Q^4=1 quotient that could conceal a central-power error. At every port the +native operators Q,D_s commute, since they are products of the commuting +translations X_v. Evaluation in this commutative operator algebra therefore +preserves the verified identity. Their columnwise lifts also commute, while +the rational coefficients stay exterior. Consequently the same exact raw +diagonal endpoint holds at every f and every spectator space. + +For each of the 3,328 main fields, the raw endpoint is Q^epsilon product D_s, +with epsilon in {+1,-1}. If epsilon=-1, its whole-job translation X_q changes +the Q exponent to +1. The polynomial check establishes equality to the +positive Gaussians on the complete recorded target axes. If stage 0 is +present, use the original nine coordinate axes; otherwise use q and its +eight-vector orthonormal complement. Append the selected fresh axes and +**all** other retained coordinates. The complete invertible map conjugates +this product to G_r, where r=1+8 times the number of selected stages. + +Each whole-idle product similarly receives one positive G_(8k) child with +its complete basis and the exactly calculated whole-job translation, where +k is the actual number of distinct stages in the window. All 3,120 fields +are checked, including the windows that contain stage 0. A rank-r child +becomes exactly rank r*f after the tensor lift; spectator coordinates remain +untouched. The raw positive query, main and idle normalizations are all +identities of full products, so no tensoring of a scalar-weighted sum occurs. + +Finally, every local call maps its 208 main public roles to the actual pair +of controller rows, its 562 arbitrary helpers to fresh public inputs, and +its 29 owned roles to the returned shared owned bank. These maps are checked +as injective on all 799 labels, and the thin scratch label is separate. +There are 3,328 arbitrary main and 18*562=10,116 arbitrary helper inputs. +The local gauge identity, complete whole-helper normalization, complete raw +caller identity and whole-job normalization prove the asserted direct-sum +endpoint, with no remaining unspecified local algebraic adapter. + +## 5. Accounting and limits + +This package leaves the selected bill unchanged: + +| Quantity | Exact count | +|---|---:| +| Arbitrary productive fields | 13,444 | +| Paid calls | 66,834 | +| Paid rank mass | 240,152 | +| Productive rank mass | 240,912 | +| Actual forward local calls | 18 | +| Five-step wrappers | 16 | +| Thin rank-one calls | 1,560 | +| Whole-idle calls | 3,120 | + +The complete paid profile is 18 times the selected local MOVE profile, plus +the 1,560 positive rank-one thin calls and the 3,120 whole-idle calls. The +productive profile is 18 times the 562-helper profile plus the complete main +endpoint profile. These profiles are compared exactly with selected RESULT, +including every multiplicity. This verifies algebraic accounting; it does +not reprice ordinary adapters, infer a physical runtime from a recipe, or +claim that the positive rank margin alone proves a multiplication bound. + +The selected record states `native_all_columns` as: retained native operators, +phase conventions, spectators and whole-job adapters work at every positive +column multiplicity. For the exact P104 objects and assembled caller pinned +here, that algebraic assertion is now the theorem of Sections 2--4. Its status +does not depend on a future maintainer admission or a physical dispatcher. +The theorem does not silently extend to a different upstream native family. + +The result supplies this proved algebraic endpoint to a later physical +dispatch theorem. That theorem must still realize every typed +native product, whole-field scalar operation and coordinate map on the +prescribed layouts, preserve or discharge the owned-reset semantics, and +charge source access, staging, restoration and recursive children at the +selected tariff. Existing physical monomial constructions #3055/#3087 and +old-controller #3385 are credited but do not automatically establish that +current combined tariff. The other framework assumptions remain explicit. +No selected record or accepted kappa is changed. **No new kappa.** diff --git a/research/selected-native-tensor-closure/README.md b/research/selected-native-tensor-closure/README.md new file mode 100644 index 0000000000..531a5f64d6 --- /dev/null +++ b/research/selected-native-tensor-closure/README.md @@ -0,0 +1,205 @@ +# Exact native traces and the selected all-column caller + +This package proves and independently checks the **algebraic** assertion +`native_all_columns` for the pinned selected #6030 P104 native layer and its +assembled caller, at every positive column width and with arbitrary spectator +factors. It joins every signed native recipe, +the complete local word, all 18 forward calls and 16 wrappers, and every +whole-job helper/main/idle adapter. It does **not** implement the selected-rate +physical dispatcher or discharge the complete five-assumption framework. +The all-column operator assertion is proved; physical realization and the +source's ideal owned-reset semantics remain distinct obligations. +**No new κ.** + +The accepted source remains canonical main +`7c49fe32c55de02c2c873655c804b16e4c3fef0a`, with conditional +κ = `781451/250000000`. No prior source, selected record, tariff or bill is +modified. [PROOF.md](PROOF.md) states the exact quantified theorem, proves the +trace algorithm and tensor interpretation, and gives the complete composition. + +## Result and evidence + +The new native acceptance mechanism computes the exact closed overlap +`tr(V*U)` as a constrained quadratic Gauss sum over binary variables. Checked +unitarity makes the value `2^9` equivalent to full operator equality. The +checker retains constant phases and Gaussian normalizations; it uses neither +a Pauli tableau, a distinguished input column, floating point, nor a dense +512-by-512 operator matrix. Quadratic elimination is polynomial in the finite +circuit description. This is a certificate-verification bound, not a +multiplication-runtime improvement. + +The caller verification keeps two different types of objects distinct: +native products are lifted columnwise, while rational coefficients of +register ADD/COPY/SCALE and signed swaps stay outside the tensor power. A +native constant phase becomes `i^(p*f)`; a register coefficient remains the +same coefficient. The proof does not infer an all-width identity by tensoring +a width-one sum of different native group elements. + +| Verified object | Complete coverage | +|---|---:| +| Distinct signed native/helper pairs | 2,922 | +| Distinct MOVE pairs within that union | 2,886 | +| Intrinsic main adapters | 208 | +| Complete local main paths | 312 | +| Distinct complete helper boundary types | 69 | +| Arbitrary helpers per local call | 562 | +| Local endpoint coefficients | 799 × 799 | +| Literal local events | 22,803 | +| Actual raw contacts | 7,488 | +| Composed contact/native/contact paths | 5,616 | +| Complete free-Laurent caller entries | 32 × 32 | +| Arbitrary main productive fields | 3,328 | +| Arbitrary helper productive fields | 10,116 | +| Whole-idle paid fields | 3,120 | + +The source-bound reverse-functional Laurent replay checks all 218 physical +controller events and both literal rational boundary programs. It uses the +free Laurent ring, with no `Q^4=1` quotient. The geometry check composes the +actual entry/native/exit maps, matches the selected raw-contact payload hash, +and explicitly fixes the 120 omitted spectator rows of the 265-row source. +Each main/idle positive basis is completed on all 145 retained rows. The +all-width proof then applies to every complete occurrence embedding, including +the embeddings supplied by #6878. + +The exact selected bill is unchanged: **13,444 productive fields, 66,834 paid +calls, paid rank mass 240,152 and productive rank mass 240,912**. The checker +reconstructs both complete profiles from the literal source events rather +than inferring a bound from this positive rank margin. Ordinary adapters, +source access, staging, physical erasure and restoration still require their +own implementation and charging theorems. + +## What is new, and what is already proved + +This is not a new local common-chart lemma or another dense native census. +The contribution is a different exact native acceptance mechanism together +with the **complete selected-source join** from those native equalities to +the assembled all-forward caller, including the exterior-scalar typing and +whole-job boundaries. + +| Predecessor or concurrent work | Already established; relationship here | +|---|---| +| #5442, `research/allwidth-caller-certificate/` | Group-algebra and tensor framework for the older #5413 caller. The product-lifting principle is credited. | +| #5974, `research/p104-allwidth-native-certificate/` | Full exact native verification for #5599 and the factorwise lift. Its native census is not repeated as a claimed new theorem. | +| #6043, `research/p104-6030-independent-audit/` | Independent dense verification of the current #6030 native inventory. The present trace method shares neither its dense mechanism nor its candidate compiler's tableau/anchor acceptance. | +| #6635 | Complete selected scalar common-chart census. The present typed induction uses that established mechanism and checks its composition with every MOVE. | +| #6293 and #6331 | The `i^(p*f)` phase convention and the obstruction to deleting native phases. The current width-two controls explicitly enforce those distinctions. | +| #6546, `research/selected-absolute-stochastic-caller/` | Concurrent direct stochastic local execution and a fixed-local all-width lift; its stated limitation is identification with complete recursive callers. The present package closes the algebraic assembled-caller join, without importing its unmerged code. | +| #6878, `research/selected-compiler-occurrence-layout/` | Complete occurrence-specific coordinate embeddings and retained-row geometry. This package supplies their native algebraic endpoint premise; it does not reprice or redevelop the embeddings. | + +At the frozen reconnaissance, #6546 was an open contribution at +`ff038070b8d122a43993f2b2524154323cc6b4d7`, and #6878 was an open contribution +at `41febf890fbbb852c6059d4c0cc8c593ae9dfa5c`. They are credited comparisons and +applications, **not runtime or source-import dependencies** of this package. +The proved tensor/naturality statement quantifies over any complete embedding; +the exact accepted inputs below all come from the pinned canonical main. + +The native/caller identities and raw phase construction in +`research/p104-productive-controller/` and the independent caller audit remain +credited source results. Existing physical compact, Gaussian-monomial and old +controller work #3055, #3087 and #3385 was checked during scope selection and +was not reimplemented. Its older tariffs do not automatically price the +current selected dispatcher. + +## Reproduction + +From the repository root, using only Python's standard library: + +```sh +python3 -I -B research/selected-native-tensor-closure/verify.py +``` + +Expected final marker: + +```text +PASS_SELECTED_NATIVE_TENSOR_CLOSURE {"kappa_changed": false, "native_pairs": 2922, "paid_calls": 66834, "productive_fields": 13444, "raw_contacts": 7488, "rejecting_controls": 20} +``` + +The registered replay also checks the generic trace engine in normal and +optimized Python and rebuilds the full native recipe artifact with the +credited source compiler: + +```sh +python3 -B scripts/research_package.py --package research/selected-native-tensor-closure +``` + +`SOURCES.json` records exact Git blob and SHA256 pins. Every live source input +is declared in `VERIFY.json`. The independent verifier authenticates the +selected word, result, controller, port data and mathematical source files +before parsing them; it imports no predecessor verifier or compiler. +`emit_recipes.py` alone imports the credited compiler to regenerate the +candidate artifact. Its complete recipe payload must reproduce the already +registered selected digest +`e22642923d5437c49e2d1dc79dd6b795503499d6f33615a1df3568f0b96164c1`. +The independent checker then accepts every complete emitted product by the +trace method. This producer/acceptor separation is deliberate. + +`RESULT.json` is deterministic and records the exact coverage, profile and +endpoint digests and every rejecting control. `--write` regenerates only this +new package's receipt. `emit_recipes.py --write` regenerates only this package's +small compressed candidate artifact; ordinary replay compares its full +decompressed contents instead. + +## Verification and negative controls + +The trace engine's independent dense-column oracle uses exact rational +arithmetic on 120 deterministic random small circuits. Additional checks +enumerate all 16,933 quadratic forms through four variables with zero constant +term, 480 affine systems, 480 affine substitutions, nonzero constant phases, +unitary inverses and a 300-variable symbolic trace. Fixed seeds are `7606030` +and `7606546`; these are bounded verifier tests, not an open-ended supplier +search. Ten trace grammar/operator controls reject malformed expected gates, +wrong phases and invalid gates, including cases that agree on the zero column. + +The source package has 20 separate controls. They reject source-byte and +registered-payload drift; a global `i` invisible to conjugation action; reversed +native factors; an omitted positive child; confusing a canonical rank-one +chart with the translation Gaussian on that vector; `i^p` used where `i^(p*f)` is +required; tensor-powering an exterior minus sign; tensoring a Gaussian sum +termwise; a wrong whole-helper normalizer; a scalar chart mismatch; reversed +MOVE orientation; aliased public roles; a missing wrapper translation or swap +sign; a wrong raw final exponent; a corruption on an arbitrary spectator +coordinate; and an omitted whole-job final translation. Mathematical fault +controls call the comparison mechanisms directly, so their rejection is not +merely an earlier source-hash failure. + +Both normal and `-O` execution are required by the registered manifest. The +package uses explicit checks rather than optimization-disabled assertions. +The finite tests support the checker implementation; the all-f and arbitrary +spectator conclusions come from the written theorem, not from sampling +column widths. This is not a proof-kernel formalization or independent human +acceptance. + +## Open next questions + +The structured versions are in [TASK.json](TASK.json). + +1. Bind this exact typed algebraic word to the selected-rate physical + dispatcher. Start with one full compiled occurrence, preserving the + distinction between native column phases and exterior scalar field + coefficients, and account for every adapter, complete field, recursive + child and return operation. This is blocked by the remaining physical + implementation and charging obligations, not by a missing local chart + identity. +2. Compose the resulting implementation with the owned-stream and precision + contracts. Start with a complete source-owned call/return that realizes + the 29-label local owned bank and separate thin scratch field, without + treating the ideal CLEAR in this algebraic proof as a physical erasure + algorithm. The required ownership and rounding premises remain explicit. + +## Attribution and license + +Prepared for **@CubeJerry**, with substantial **OpenAI Codex** assistance in +research, derivation, implementation and adversarial checks. The lead research +reviewer checked the Gauss-sum derivation and the complete typed composition; +that is an AI-assisted mathematical review, not a separate formal proof. + +Credit DreamingOfClouds for the selected #6030 change; David Arias for the +coalesced source word, Cinna Davis for private reuse, the native representations +and the Laurent controller, +Gareth Davidson for their composition, and Ali Romman for the P104 caller and +independent source audits. The native compiler, source constructions and +proof framework retain their existing contributors' credit. Relevant earlier +and concurrent mechanisms are identified above rather than claimed anew. +Apache-2.0, following the repository license. No predecessor source is copied +or modified; the compressed artifact contains deterministically emitted +recipe data authenticated against the selected registration. diff --git a/research/selected-native-tensor-closure/RESULT.json b/research/selected-native-tensor-closure/RESULT.json new file mode 100644 index 0000000000..2545b90099 --- /dev/null +++ b/research/selected-native-tensor-closure/RESULT.json @@ -0,0 +1,154 @@ +{ + "all_size_status": "PROOF.md proves the typed algebraic implication for every positive f and arbitrary spectator factors; finite replay is the exact selected-source premise", + "base_commit": "7c49fe32c55de02c2c873655c804b16e4c3fef0a", + "complete_caller": { + "all_1024_laurent_entries_checked": true, + "controller_events": 218, + "endpoint_sha256": "4ba0b7b54172171102343f81f03f2dac7753ffd184cdffcbe62c8b9d6b1dc0bf", + "five_step_forward_wrappers": 16, + "forward_local_calls": 18, + "literal_boundary_events": 145, + "method": "reverse linear-functional substitution; no finite-order quotient of Q", + "thin_queries_per_port": 15 + }, + "limitations": [ + "ideal owned reset semantics remain assumed", + "physical selected-rate dispatcher not implemented", + "compiler, precision_tape and analytic_recovery not discharged", + "No new kappa" + ], + "local_gauge": { + "arbitrary_public_columns": 770, + "events": 22803, + "full_endpoint_coefficients": 638401, + "gauge_endpoint_sha256": "3a9a39e842438b4575689b5233c0d9237b4863b71f8f1939c757607fe4d35325", + "opcode_counts": { + "ADD": 18404, + "CLEAR": 31, + "COPY": 23, + "INIT": 31, + "MOVE": 3453, + "SCALE": 861 + }, + "owned_entrance_columns_erased": 29, + "owned_lifetimes": 31, + "paid_profile": { + "1": 2219, + "2": 457, + "3": 496, + "4": 100, + "5": 163, + "6": 11, + "7": 6, + "8": 1 + }, + "peak_owned": 13, + "rows": 799, + "rule": "chart coefficients are formal inverse/forward products; every scalar remains exterior" + }, + "native_trace": { + "arbitrary_helpers": 562, + "central_squares": 104, + "complete_main_paths": 312, + "complete_trace_digest": "dcb7308d7925b139d4547247eae4955b58445155bda7ac93d7c3b18d479078a5", + "distinct_move_pairs": 2886, + "distinct_native_pairs": 2922, + "helper_normalized_pairs": 69, + "helper_profile": { + "1": 14, + "2": 8, + "3": 4, + "4": 107, + "5": 14, + "6": 2, + "7": 9, + "8": 19, + "9": 385 + }, + "main_adapters": 208, + "maximum_closed_trace_branches": 18, + "method": "exact constrained quadratic Gauss sum; no tableau or distinguished column", + "selected_recipe_payload_sha256": "e22642923d5437c49e2d1dc79dd6b795503499d6f33615a1df3568f0b96164c1" + }, + "negative_controls": { + "MOVE_reversed_source_and_target": "current native source and ownership at 9896", + "aliased_local_public_role": "all productive owners", + "caller_exterior_swap_sign_lost": "full free-Laurent reverse output 8", + "caller_missing_central_translation": "full free-Laurent reverse output 8", + "caller_wrong_raw_diagonal_Q_exponent": "full free-Laurent reverse output 0", + "canonical_rank_one_chart_mistaken_for_C_q": "omitted nontrivial intrinsic main adapter: exact unitary overlap trace (256, 0, 0)", + "contact_corrupts_arbitrary_spectator_row": "nonidentity spectator action", + "exterior_minus_incorrectly_tensor_powered": "exterior sign squared: exact unitary overlap trace (-262144, 0, 0)", + "main_whole_job_final_translation_omitted": "omitted X_q final raw endpoint correction", + "native_constant_i_not_raised_to_column_width": "wrong i^(p*f): exact unitary overlap trace (0, -262144, 0)", + "native_global_i_preserving_conjugation_action": "lost absolute phase: exact unitary overlap trace (0, 512, 0)", + "native_left_right_order": "reversed native factors: exact unitary overlap trace (128, 0, 0)", + "native_missing_positive_child": "omitted native Gaussian: exact unitary overlap trace (256, -256, 0)", + "registered_recipe_payload_drift": "full selected recipe payload hash", + "scalar_chart_mismatch": "scalar current chart", + "source_drift:research/p104-helper470-twin-rung-removal/WORD.json.gz": "Rejected before parsing or execution", + "source_drift:research/p104-productive-controller/caller.py": "Rejected before parsing or execution", + "tensor_of_sum_replaced_by_sum_of_tensors": "tensoring a Gaussian expansion termwise: exact unitary overlap trace (2, 0, 0)", + "whole_helper_pre_normalizer_not_inverted": "wrong whole-helper entry: exact unitary overlap trace (16, 0, 0)", + "zero_column_agrees_but_quadratic_operator_differs": "missing controlled sign: exact unitary overlap trace (2, 0, 0)" + }, + "ownership_and_invoice": { + "field_maps_sha256": "0cc409ad6083dedf325df5bd452ed58f84d579f5664e48a519a86ab2e15bdd33", + "fresh_helper_inputs": 10116, + "limits": "ideal source ownership only; allocation, physical erasure and retained backing remain premises", + "local_owned_labels": 29, + "main_arbitrary_inputs": 3328, + "paid_calls": 66834, + "paid_rank_mass": 240152, + "productive_inputs": 13444, + "separate_thin_owned_label": 13473, + "target_rank_mass": 240912 + }, + "schema": "selected-native-tensor-closure-result/1", + "source_sha256": { + "research/p104-caller-independent-audit/capsule/outputs/p104-caller-route-audit-v2-20261025/sources/DATA-PHASES.json": "c5916ff312678f12aba403847643fbbc29f643acec60bdf836fa56cd3e8251fa", + "research/p104-helper470-twin-rung-removal/RESULT.json": "84fcbe2a0b64307b5703c363408042bdd91555b0419509aa50daa43b3b1924ad", + "research/p104-helper470-twin-rung-removal/WORD.json.gz": "7834725d9e34ee846a9ccc615221ea81e0201613938afa185d2eda0c9a6c232f", + "research/p104-native-channel/vendor/clifford_exact.py": "2982ead07721e83358dc67c6f49c2b4c16a236a6bebdfd72f58221be1f5d0962", + "research/p104-productive-controller/CONTROLLER.json": "a84f6bbd547379597464e56c420cc665f96bc43a9a1351eed4b7cd0fce3cc4b9", + "research/p104-productive-controller/caller.py": "300d40043953e4f6aecfde8f2d1004b8ad6dc5a79ec3c7666c9908c2466c91c8", + "research/p104-productive-controller/local.py": "878734dfd64c4b9d362d0ae476cc5c2a19fe93e48b7378763b23f3e0dd0a5a71" + }, + "status": "PASS_SELECTED_NATIVE_TENSOR_CLOSURE", + "whole_job_algebra": { + "complete_idle_endpoints": 3120, + "complete_main_endpoints": 3328, + "complete_raw_contacts": 7488, + "composed_native_paths": 5616, + "full_coordinate_bijections": 4888, + "idle_boundary_sha256": "3b493ec6281025ba31b1a66b682d33292b58f99c982461f68018f340c8bbb677", + "idle_profile": { + "16": 416, + "24": 416, + "32": 416, + "40": 416, + "48": 416, + "56": 416, + "64": 416, + "72": 208 + }, + "limits": "algebraic whole-field bijections; physical scheduling and charged realization remain separate", + "main_boundary_sha256": "57d64f0cbbd7430e780837e64f8bd7336df73c88de52adbe91f8204b62770afd", + "main_profile": { + "17": 416, + "25": 416, + "33": 416, + "41": 416, + "49": 416, + "57": 416, + "65": 416, + "73": 208, + "145": 208 + }, + "omitted_identity_spectator_rows": 120, + "retained_source_rows": 145, + "source_dimension": 265, + "source_route_payload_sha256": "7f4f0ca768ffbc4f93dcff575592c4c67411f46b4289e45b73f715a2ad47b142", + "thin_query_boundaries": 104 + } +} diff --git a/research/selected-native-tensor-closure/SOURCES.json b/research/selected-native-tensor-closure/SOURCES.json new file mode 100644 index 0000000000..57558383d3 --- /dev/null +++ b/research/selected-native-tensor-closure/SOURCES.json @@ -0,0 +1,60 @@ +{ + "base_commit": "7c49fe32c55de02c2c873655c804b16e4c3fef0a", + "files": [ + { + "git_blob": "3404fbdd5e7529a0674eef26114b5071e4586cec", + "path": "docs/research/agent-task-catalog.json", + "sha256": "ead6cdc9d0af6d58e8a49744d8ec8c7c15e13d2ba93c276c434bcf9b51b93a81", + "usage": "provenance" + }, + { + "git_blob": "d59b5035e7b4cbaa9d688a1d32dd0d6eb79c466a", + "path": "certificates/selected-result.json", + "sha256": "9b8d0eea95b3bf0dfcb7938a732a6439b87a2685dec2a5ab9e525b3b34debbfa", + "usage": "provenance" + }, + { + "git_blob": "22f9ab02d1b73ea0aa4b07ce445a14cc2602c760", + "path": "research/p104-helper470-twin-rung-removal/WORD.json.gz", + "sha256": "7834725d9e34ee846a9ccc615221ea81e0201613938afa185d2eda0c9a6c232f", + "usage": "input" + }, + { + "git_blob": "e4a03118d585fb80c9ac932281b528ac394ccc5f", + "path": "research/p104-helper470-twin-rung-removal/RESULT.json", + "sha256": "84fcbe2a0b64307b5703c363408042bdd91555b0419509aa50daa43b3b1924ad", + "usage": "input" + }, + { + "git_blob": "cc539db709cf7a6c42872c4380902736ad453af3", + "path": "research/p104-productive-controller/CONTROLLER.json", + "sha256": "a84f6bbd547379597464e56c420cc665f96bc43a9a1351eed4b7cd0fce3cc4b9", + "usage": "input" + }, + { + "git_blob": "d9a4f84211182ca200c081ea061d724c8a2cfb70", + "path": "research/p104-caller-independent-audit/capsule/outputs/p104-caller-route-audit-v2-20261025/sources/DATA-PHASES.json", + "sha256": "c5916ff312678f12aba403847643fbbc29f643acec60bdf836fa56cd3e8251fa", + "usage": "input" + }, + { + "git_blob": "166841a037f069249f6f7f6dedb89248812f59e2", + "path": "research/p104-native-channel/vendor/clifford_exact.py", + "sha256": "2982ead07721e83358dc67c6f49c2b4c16a236a6bebdfd72f58221be1f5d0962", + "usage": "input" + }, + { + "git_blob": "dbb3d56a4f5b733bb0c32a91249d1f6a7e1ab5d2", + "path": "research/p104-productive-controller/local.py", + "sha256": "878734dfd64c4b9d362d0ae476cc5c2a19fe93e48b7378763b23f3e0dd0a5a71", + "usage": "input" + }, + { + "git_blob": "6ac5e735691555ecafe2afbcff71edd923178113", + "path": "research/p104-productive-controller/caller.py", + "sha256": "300d40043953e4f6aecfde8f2d1004b8ad6dc5a79ec3c7666c9908c2466c91c8", + "usage": "input" + } + ], + "schema_version": 1 +} diff --git a/research/selected-native-tensor-closure/TASK.json b/research/selected-native-tensor-closure/TASK.json new file mode 100644 index 0000000000..0251f7fb6a --- /dev/null +++ b/research/selected-native-tensor-closure/TASK.json @@ -0,0 +1,88 @@ +{ + "actual_scope": "Prove and independently certify the selected #6030 algebraic native-to-18-call-caller implication for every positive column width, arbitrary spectators, signed phases and complete whole-job adapters.", + "ai_assistance": "Substantial OpenAI Codex assistance in derivation, implementation and adversarial review; no claim of independent human or proof-kernel acceptance.", + "author": "@CubeJerry", + "base_commit": "7c49fe32c55de02c2c873655c804b16e4c3fef0a", + "bottleneck_link": "native_all_columns at the actual selected caller, with owned reset, physical selected-rate dispatch, compiler, precision_tape and analytic_recovery premises explicitly retained.", + "evidence": "PROOF.md and RESULT.json; full source-bound deterministic normal/optimized replay, exact Gauss-sum validation, complete recipe regeneration and rejecting phase/order/typing/endpoint/source controls.", + "open_questions": [ + { + "blocked_by": "Selected-rate physical native and adapter realization, complete call/return accounting and current precision/storage contracts.", + "first_step": "Bind one full compiler occurrence to the actual call schedule, preserving exterior scalar coefficients and all complete coordinate maps; charge every introduced operation.", + "question": "Can the exact typed selected algebraic word be bound to a physical dispatcher at the selected accelerated tariff?", + "why_it_matters": "This package establishes native/caller equality but does not implement or price source access, staging, monomials and recursive returns." + }, + { + "blocked_by": "The retained owned-erasure and precision_tape framework obligations; this package deliberately assumes ideal owned reset semantics.", + "first_step": "Construct one complete call/return with all 29 local owned labels and the separate thin scratch field, including retained information, initialization, rounding and returned ownership.", + "question": "Can the actual owned-stream and numerical contracts be composed with every owned lifetime of this selected caller?", + "why_it_matters": "Ideal INIT/CLEAR algebra does not prove physically valid erasure or control finite-precision errors." + } + ], + "original_card": { + "acceptance_gate": "Identify the proposition and what would change downstream; do not equate a finite replay with a universal proof.", + "allowed_variations": [ + "One exact source head, helper/cut region or quantified proposition and changed hypothesis; check live handoffs first. Different seeds are not different scopes." + ], + "bottleneck_link": "Judge the actual current supplier contract and complete signed target/paid bill; frozen-profile ceilings require an applicability proof.", + "existing_checks": [ + { + "evidence": "research/unconditional-batched-crt/README.md", + "remaining": "Independent proof audit of one caller-bound all-size implication; all other premises remain explicit.", + "scope": "PR #4100: proposed unconditional construction and finite checks, without independent acceptance.", + "selected_record_sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129", + "source_commit": "337d04939571a637aeab54a0f58f54fde811663a", + "status": "author-checked" + }, + { + "evidence": "docs/research/pr-intake-evidence/pr5599-completion/PROOF-REVIEW.md", + "remaining": "The next experiment must identify uncovered source-specific work. Successor archive intake does not transfer this admission.", + "scope": "Selected PR5599: complete helper-subspace construction and independent caller replay; five framework assumptions remain.", + "selected_record_sha256": "2a0220e866417774606e0f991e41b9c1d26b1f43b97857238cf478dbc7d330b5", + "source_commit": "09dd0965711ca7eeb169057a420abab1c9052137", + "status": "reviewed" + } + ], + "first_experiment": "Choose one assumption ID (native_all_columns, owned_erasure, compiler, precision_tape, analytic_recovery), one quantified implication and exact dependency closure. Check current handoffs, including #5625 row reentry and #5707 root-prefix lowering, without treating their partial scope as the whole theorem. Prove or falsify that implication with a caller-bound argument; stop at the chosen obligation. Finite tests or root digits do not discharge an all-size statement.", + "first_step": { + "command": [ + "python3", + "-B", + "scripts/research_selected_baseline.py" + ], + "expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY", + "inspect": "Read revision 8, the selected review and live exact-source handoffs. Then follow this card's bounded first_experiment.", + "scope": "Source-bound orientation only, not a supplier replay or theorem proof." + }, + "id": "uniform-transfer-audit", + "methods": [ + "Construct and independently check one source-bound instance of the stated first experiment.", + "Seek an exact counterexample or target-specific complete-budget obstruction to that same proposed change." + ], + "overlap_terms": [ + "native_all_columns", + "owned_erasure", + "compiler", + "precision_tape", + "analytic_recovery" + ], + "question": "Can one of the selected record's five inherited assumptions be proved at its actual caller?", + "scope_change_triggers": [ + "Changing source head, helper/cut domain, operation alphabet or caller boundary without recording the new scope.", + "Treating an archived proposal or local numerical pass as complete supplier admission." + ], + "starting_points": [ + "docs/research/current-priorities.md", + "docs/research/research-family-status.md", + "certificates/selected-result.json", + "docs/research/pr-intake-evidence/pr5599-completion/PROOF-REVIEW.md" + ] + }, + "outcome": "completed", + "schema_version": 1, + "scope_change_reason": "The card asks for one quantified inherited implication. Earlier dense censuses and the scalar common-chart lemma were already proved; this scope isolates the complete selected assembled-caller join and checks native operators by a distinct exact closed-trace method.", + "scope_status": "refined", + "stop_rule": "Complete the exact pinned native, local, raw-contact, free-Laurent and whole-job inventories; stop if their all-width interpretation cannot be proved without tensoring exterior scalar sums. No open-ended parameter search.", + "task_id": "uniform-transfer-audit", + "time_budget_minutes": 180 +} diff --git a/research/selected-native-tensor-closure/VERIFY.json b/research/selected-native-tensor-closure/VERIFY.json new file mode 100644 index 0000000000..4d06e71495 --- /dev/null +++ b/research/selected-native-tensor-closure/VERIFY.json @@ -0,0 +1,69 @@ +{ + "commands": [ + { + "argv": [ + "{python}", + "-I", + "-B", + "{package}/verify.py" + ], + "checks": "Complete exact selected-source native/caller/whole-job certificate and all 20 rejecting controls", + "timeout_seconds": 120 + }, + { + "argv": [ + "{python}", + "-I", + "-B", + "-O", + "{package}/verify.py" + ], + "checks": "Complete exact selected-source native/caller/whole-job certificate and all 20 rejecting controls under optimized Python", + "timeout_seconds": 120 + }, + { + "argv": [ + "{python}", + "-I", + "-B", + "{package}/test_gauss_trace.py" + ], + "checks": "Independent dense rational oracle, exhaustive small Gauss sums, affine substitution and malformed-gate/operator controls", + "timeout_seconds": 60 + }, + { + "argv": [ + "{python}", + "-I", + "-B", + "-O", + "{package}/test_gauss_trace.py" + ], + "checks": "Independent dense rational oracle, exhaustive small Gauss sums, affine substitution and malformed-gate/operator controls under optimized Python", + "timeout_seconds": 60 + }, + { + "argv": [ + "{python}", + "-I", + "-B", + "{package}/emit_recipes.py" + ], + "checks": "Regenerate every complete native recipe and intrinsic adapter with the pinned credited compiler; require exact selected payload hash and full decompressed candidate bytes", + "timeout_seconds": 600 + } + ], + "inputs": [ + "research/p104-helper470-twin-rung-removal/WORD.json.gz", + "research/p104-helper470-twin-rung-removal/RESULT.json", + "research/p104-productive-controller/CONTROLLER.json", + "research/p104-caller-independent-audit/capsule/outputs/p104-caller-route-audit-v2-20261025/sources/DATA-PHASES.json", + "research/p104-native-channel/vendor/clifford_exact.py", + "research/p104-productive-controller/local.py", + "research/p104-productive-controller/caller.py" + ], + "python": "3.12", + "requirements": [], + "schema_version": 1, + "status": "ready" +} diff --git a/research/selected-native-tensor-closure/caller_check.py b/research/selected-native-tensor-closure/caller_check.py new file mode 100644 index 0000000000..23ef0969bb --- /dev/null +++ b/research/selected-native-tensor-closure/caller_check.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +"""Exact source contacts and whole-job positive endpoints, with no old imports.""" +from __future__ import annotations + +from collections import Counter +from functools import lru_cache +import hashlib +import json + +from gauss_trace import Quadratic, adjoint, equal, require +from native_check import transpose + + +DIMENSION = 265 +AXES = tuple(1 << i for i in range(DIMENSION)) + + +def terms(entries): + coefficients = Counter() + for axis, value in entries: + require(type(axis) is int and 0 < axis < 1 << DIMENSION and type(value) is int, 'spectral term domain') + coefficients[axis] += value + return tuple(sorted((axis, value % 4) for axis, value in coefficients.items() if value % 4)) + + +@lru_cache(maxsize=None) +def phase(entries): + polynomial = Quadratic.zero(DIMENSION) + for axis, coefficient in entries: + polynomial.add_affine(coefficient, (axis, 0)) + return tuple(polynomial.linear), frozenset(polynomial.pairs), polynomial.constant + + +def apply(columns, value): + result = 0 + while value: + low = value & -value + result ^= columns[low.bit_length()-1] + value ^= low + return result + + +def positive(vectors): + return terms((axis, 1) for axis in vectors) + + +def stage_terms(stage, q): + require(type(stage) is int and 0 <= stage <= 32, 'controller stage domain') + return terms([(axis, 1) for axis in AXES[:9]]+[(q, -1)]) if stage == 0 else positive(AXES[9+8*(stage-1):9+8*stage]) + + +def orthogonal_base(q, recipe): + basis = tuple(recipe['idle_children'][0]['orthogonal_axes'][:8]) + complete = (q,)+basis + require(len(set(complete)) == 9 and all(type(v) is int and 0 < v < 512 for v in complete), + 'complete nine-axis port basis') + require(all((a & b).bit_count() % 2 == int(i == j) + for i, a in enumerate(complete) for j, b in enumerate(complete)), 'orthonormal port basis') + return basis + + +def route(stage, q, basis): + """Swap eight orthonormal pairs; fix their orthogonal complement.""" + if stage == 0: + return AXES + fresh = AXES[9+8*(stage-1):9+8*stage] + differences = tuple(a ^ b for a, b in zip(basis, fresh, strict=True)) + columns = tuple(axis ^ apply(differences, sum(((axis & v).bit_count() % 2) << j + for j, v in enumerate(differences))) for axis in AXES) + require(all(apply(columns, a) == b and apply(columns, b) == a + for a, b in zip(basis, fresh, strict=True)), 'actual eight swaps') + require(apply(columns, q) == q and all(apply(columns, columns[j]) == AXES[j] for j in range(DIMENSION)), + 'complete involutive route and central port') + # The checked two orthonormal families are disjoint. Their eight swaps and + # fixed orthogonal complement prove orthogonality, without a 265^2 replay. + return columns + + +def contact(before, after, columns): + moved = terms((apply(columns, axis), coefficient) for axis, coefficient in before) + left, right = phase(moved), phase(after) + require(left[1:] == right[1:], 'contact full mixed and constant phase') + difference = tuple((b-a) % 4 for a, b in zip(left[0], right[0], strict=True)) + require(all(value in (0, 2) for value in difference), 'contact even linear residual') + translation = sum((value//2) << j for j, value in enumerate(difference)) + correction = [(translation, 2)] if translation else [] + require(phase(terms(list(moved)+correction)) == right, 'complete forward contact') + inverse = apply(columns, translation) + backward = [(apply(columns, axis), coefficient) for axis, coefficient in after] + if inverse: + backward.append((inverse, 2)) + require(phase(terms(backward)) == phase(before), 'complete inverse contact') + return {'translation': translation, 'inverse_translation': inverse} + + +def transformed_path(local, entry, exit_, columns): + """Exit * native * entry, including both actual address translations.""" + translation = exit_['translation'] ^ apply(columns, entry['translation']) + entries = [(apply(columns, axis), coefficient) for axis, coefficient in local] + if translation: + entries.append((translation, 2)) + return phase(terms(entries)) + + +def complete_positive_basis(base, active, retained): + """Return a complete coordinate map, including all spectator columns.""" + require(len(base) == 9 and len(set(active)) == len(active) + and all(type(j) is int and 0 <= j < DIMENSION for j in active), 'positive coordinate selection') + require(all((a & b).bit_count() % 2 == int(i == j) + for i, a in enumerate(base) for j, b in enumerate(base)), 'complete orthogonal local basis') + require(set(active) <= set(retained), 'positive job uses retained source rows') + order = (tuple(active)+tuple(j for j in retained if j not in set(active)) + +tuple(j for j in range(DIMENSION) if j not in set(retained))) + require(sorted(order) == list(range(DIMENSION)), 'all spectator coordinates retained') + columns = tuple(base)+AXES[9:] + return tuple(columns[j] for j in order) + + +def check_caller(word, controller, phase_source, source_result): + recipes = {entry['q']: entry for entry in phase_source['recipes']} + require(len(recipes) == len(phase_source['recipes']) and set(word['ports']) <= set(recipes), 'original source port data') + bases = {q: orthogonal_base(q, recipes[q]) for q in word['ports']} + local_stages = sorted(event[1] for event in controller['physical_events'] if event[0] == 'LOCAL') + require(local_stages == [0]+list(range(1, 32, 2))+[32], 'all selected actual stages') + retained = tuple(range(9))+tuple(j for stage in local_stages if stage + for j in range(9+8*(stage-1), 9+8*stage)) + require(len(retained) == len(set(retained)) == controller['ambient_rank'], 'actual 145-row retained support') + retained_mask = sum(1 << j for j in retained) + contacts = [] + paths = 0 + endpoint_records = [] + main_profile, idle_profile = Counter(), Counter() + for _, stage, pair, backward in (event for event in controller['physical_events'] if event[0] == 'LOCAL'): + require(not backward, 'actual forward source supplier') + spectators = positive(AXES[9:9+8*stage]) + for q in word['ports']: + columns = route(stage, q, bases[q]) + raw_x = ((q, 1),) if stage == 0 else positive(AXES[:9+8*(stage-1)]) + raw_y = () if stage == 0 else terms(list(raw_x)+[(q, -1)]) + entry_x = terms(list(spectators)+[(q, 1)]) + entry_y = spectators + exit_x = terms(list(spectators)+[(axis, 1) for axis in AXES[:9]]) + exit_y = terms(list(exit_x)+[(q, -1)]) + out_x = positive(AXES[:9+8*stage]) + out_y = terms(list(out_x)+[(q, -1)]) + maps = [contact(a, b, columns) for a, b in + ((raw_x, entry_x), (raw_y, entry_y), (exit_x, out_x), (exit_y, out_y))] + require(all(columns[j] == AXES[j] for j in range(DIMENSION) if j not in retained) + and all((item['translation'] | item['inverse_translation']) & ~retained_mask == 0 for item in maps), + 'all actual contacts fix omitted spectator rows') + expected = phase(stage_terms(stage, q)) + d0 = stage_terms(0, q) + require(transformed_path(d0, maps[0], maps[2], columns) == expected, 'executed X diagonal D_s') + require(transformed_path(d0, maps[1], maps[3], columns) == expected, 'executed Y diagonal D_s') + require(transformed_path(terms(list(d0)+[(q, -1)]), maps[0], maps[3], columns) + == phase(terms(list(stage_terms(stage, q))+[(q, -1)])), 'executed cross D_s Q inverse') + require(sum(columns[j] != AXES[j] for j in range(DIMENSION)) <= 17 + and all(item['translation'].bit_count() <= 17 and item['inverse_translation'].bit_count() <= 17 + for item in maps), 'source seventeen-axis contacts') + contacts.append([stage, q, maps]) + paths += 3 + digest = hashlib.sha256(json.dumps(contacts, separators=(',', ':')).encode()).hexdigest() + require(digest == source_result['physical_caller']['route_payload_sha256'], 'complete selected raw contact payload') + + for q in word['ports']: + # A rank-one positive query plus its full ordinary normalizer gives Q^-1. + query_basis = transpose((q,)+bases[q]) + query = (('P', tuple(query_basis)),) + equal(9, adjoint(query)+(('C', 1, 1),)+query+(('X', q),), (('C', q, -1),), 'complete positive thin query') + for output, diagonal in enumerate(controller['diagonal']): + qp, selected = diagonal[0], diagonal[1:] + require(qp in (-1, 1) and len(selected) == 33 and set(selected) <= {0, 1}, 'complete raw endpoint exponent grammar') + raw = terms([(q, qp)]+[entry for stage, yes in enumerate(selected) if yes for entry in stage_terms(stage, q)]) + active = list(range(9)) if selected[0] else [0] + active += [j for stage in range(1, 33) if selected[stage] for j in range(9+8*(stage-1), 9+8*stage)] + base = AXES[:9] if selected[0] else (q,)+bases[q] + canonicalizer = complete_positive_basis(base, active, retained) + wanted = canonicalizer[:len(active)] + translation = q if qp == -1 else 0 + corrected = terms(list(raw)+([(translation, 2)] if translation else [])) + require(phase(corrected) == phase(positive(wanted)), 'entire main endpoint with final translation') + require(len(wanted) == 1+8*sum(selected), 'complete productive main rank') + main_profile[len(wanted)] += 1 + endpoint_records.append([q, output, len(wanted), translation, list(canonicalizer)]) + idle_endpoints = [] + for event_index, event in enumerate(controller['physical_events']): + if event[0] != 'IDLE': + continue + stages = event[2] + require(stages and len(stages) == len(set(stages)) and set(stages) <= set(range(33)), 'entire idle window') + for q in word['ports']: + raw = terms(entry for stage in stages for entry in stage_terms(stage, q)) + active = list(range(1, 9)) if 0 in stages else [] + active += [j for stage in stages if stage for j in range(9+8*(stage-1), 9+8*stage)] + canonicalizer = complete_positive_basis((q,)+bases[q], active, retained) + wanted = positive(canonicalizer[:len(active)]) + adapter = contact(wanted, raw, AXES) + require(len(active) == 8*len(stages), 'positive whole-idle rank') + idle_profile[len(active)] += 2 + idle_endpoints.append([event_index, q, len(active), adapter, list(canonicalizer)]) + registered = source_result['physical_caller'] + require(dict(main_profile) == {int(k): v for k, v in registered['main_targets'].items()}, 'selected main output census') + require(dict(idle_profile) == {int(k): v for k, v in registered['whole_idle_jobs'].items()}, 'selected whole-idle census') + return {'source_dimension': DIMENSION, 'retained_source_rows': len(retained), 'omitted_identity_spectator_rows': DIMENSION-len(retained), + 'complete_raw_contacts': 4*len(contacts), 'composed_native_paths': paths, + 'source_route_payload_sha256': digest, 'thin_query_boundaries': len(word['ports']), + 'complete_main_endpoints': sum(main_profile.values()), 'main_profile': dict(sorted(main_profile.items())), + 'complete_idle_endpoints': sum(idle_profile.values()), 'idle_profile': dict(sorted(idle_profile.items())), + 'full_coordinate_bijections': len(endpoint_records)+len(idle_endpoints), + 'main_boundary_sha256': hashlib.sha256(json.dumps(endpoint_records, separators=(',', ':')).encode()).hexdigest(), + 'idle_boundary_sha256': hashlib.sha256(json.dumps(idle_endpoints, separators=(',', ':')).encode()).hexdigest(), + 'limits': 'algebraic whole-field bijections; physical scheduling and charged realization remain separate'} diff --git a/research/selected-native-tensor-closure/controls.py b/research/selected-native-tensor-closure/controls.py new file mode 100644 index 0000000000..b20e5a434a --- /dev/null +++ b/research/selected-native-tensor-closure/controls.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +"""Mathematical faults rejected independently of provenance-hash failures.""" +from __future__ import annotations + +from copy import deepcopy + +from gauss_trace import adjoint, equal, require +from native_check import check_native, frame, gaussian, lowered, monomial +from source_algebra import free_laurent, local_gauge, ownership_and_invoice +from caller_check import AXES, contact, orthogonal_base, phase, positive, route, stage_terms, terms, transformed_path + + +def lift(n, f, circuit, repeat_constant=True): + """Small checked test witness, not a replacement for the universal proof.""" + dimension = n*f + out = [] + for column in range(f): + offset = n*column + for gate in circuit: + kind = gate[0] + if kind == 'P': + rows = [1 << j for j in range(dimension)] + rows[offset:offset+n] = [row << offset for row in gate[1]] + out.append(('P', tuple(rows))) + elif kind == 'D': + rows, linear = [0]*dimension, [0]*dimension + rows[offset:offset+n] = [row << offset for row in gate[1]] + linear[offset:offset+n] = gate[2] + out.append(('D', tuple(rows), tuple(linear))) + elif kind in ('X', 'C'): + out.append((kind, gate[1] << offset)+gate[2:]) + else: + require(kind == 'phase', 'witness native gate') + if repeat_constant or column == 0: + out.append(gate) + return tuple(out) + + +def run(word, result, candidates, controller, phase_source): + rejected = {} + + def reject(name, action): + try: + action() + except ValueError as error: + rejected[name] = str(error) + return + raise ValueError('negative control accepted: '+name) + + records = {(a, b): recipe for a, b, recipe in candidates['pairs']} + # These are actual source pairs, chosen once after locating nondegenerate + # faults. They are fixed regression witnesses, not a continuing search. + a, b = 0, 249 + recipe = records[a, b] + expected = adjoint(frame(tuple(word['frames'][a])))+frame(tuple(word['frames'][b])) + actual = lowered(recipe) + equal(9, actual, expected, 'native control baseline') + reject('native_global_i_preserving_conjugation_action', + lambda: equal(9, actual+(('phase', 1),), expected, 'lost absolute phase')) + reject('native_left_right_order', + lambda: equal(9, monomial(recipe['left'])+gaussian(recipe['rank'])+monomial(recipe['right']), + expected, 'reversed native factors')) + reject('native_missing_positive_child', + lambda: equal(9, monomial(recipe['right'])+monomial(recipe['left']), expected, 'omitted native Gaussian')) + equal(18, lift(9, 2, actual), lift(9, 2, expected), 'actual two-column native baseline') + reject('native_constant_i_not_raised_to_column_width', + lambda: equal(18, lift(9, 2, actual, repeat_constant=False), lift(9, 2, expected), 'wrong i^(p*f)')) + require(any(event[0] == 'SIGNED_SWAP' for event in controller['physical_events']), 'actual exterior minus source') + reject('exterior_minus_incorrectly_tensor_powered', + lambda: equal(18, lift(9, 2, (('phase', 2),)), (('phase', 2),), 'exterior sign squared')) + reject('tensor_of_sum_replaced_by_sum_of_tensors', + lambda: equal(2, (('C', 3, 1),), (('C', 1, 1), ('C', 2, 1)), 'tensoring a Gaussian expansion termwise')) + reject('zero_column_agrees_but_quadratic_operator_differs', + lambda: equal(2, (), (('D', (2, 1), (0, 0)),), 'missing controlled sign')) + + a, b = 3, 117 + helper = records[a, b] + helper_path = adjoint(frame(tuple(word['frames'][a])))+frame(tuple(word['frames'][b])) + equal(9, adjoint(monomial(helper['right']))+helper_path+adjoint(monomial(helper['left'])), + gaussian(helper['rank']), 'whole-helper control baseline') + reject('whole_helper_pre_normalizer_not_inverted', + lambda: equal(9, monomial(helper['right'])+helper_path+adjoint(monomial(helper['left'])), + gaussian(helper['rank']), 'wrong whole-helper entry')) + + changed = deepcopy(candidates) + changed['pairs'][0][2]['left']['phase'] = (changed['pairs'][0][2]['left']['phase']+1) % 4 + reject('registered_recipe_payload_drift', lambda: check_native(word, result, changed)) + changed_word = deepcopy(word) + scalar = next(event for event in changed_word['program'] if event[0] == 'ADD') + scalar[-1] = (scalar[-1]+1) % len(word['frames']) + reject('scalar_chart_mismatch', lambda: local_gauge(changed_word, result)) + changed_word = deepcopy(word) + move = next(event for event in changed_word['program'] if event[0] == 'MOVE') + move[2], move[3] = move[3], move[2] + reject('MOVE_reversed_source_and_target', lambda: local_gauge(changed_word, result)) + changed_word = deepcopy(word) + changed_word['live'][-1] = changed_word['live'][0] + reject('aliased_local_public_role', lambda: ownership_and_invoice(changed_word, controller, result)) + + changed_controller = deepcopy(controller) + changed_controller['physical_events'].remove(next(event for event in changed_controller['physical_events'] if event[0] == 'Q_POWER')) + reject('caller_missing_central_translation', lambda: free_laurent(changed_controller)) + changed_controller = deepcopy(controller) + next(event for event in changed_controller['physical_events'] if event[0] == 'SIGNED_SWAP')[0] = 'SWAP' + reject('caller_exterior_swap_sign_lost', lambda: free_laurent(changed_controller)) + changed_controller = deepcopy(controller) + changed_controller['diagonal'][0][0] *= -1 + reject('caller_wrong_raw_diagonal_Q_exponent', lambda: free_laurent(changed_controller)) + + q = word['ports'][0] + reject('canonical_rank_one_chart_mistaken_for_C_q', + lambda: equal(9, frame(tuple(word['frames'][word['initial'][word['live'][0]]])), + (('C', q, 1),), 'omitted nontrivial intrinsic main adapter')) + port_recipe = next(item for item in phase_source['recipes'] if item['q'] == q) + basis = orthogonal_base(q, port_recipe) + stage = 1 + columns = route(stage, q, basis) + spectators = positive(AXES[9:17]) + entry = contact(positive(AXES[:9]), terms(list(spectators)+[(q, 1)]), columns) + exit_ = contact(terms(list(spectators)+[(axis, 1) for axis in AXES[:9]]), positive(AXES[:17]), columns) + desired = phase(stage_terms(stage, q)) + require(transformed_path(stage_terms(0, q), entry, exit_, columns) == desired, 'actual contact control baseline') + changed_exit = dict(exit_) + changed_exit['translation'] ^= AXES[264] + reject('contact_corrupts_arbitrary_spectator_row', + lambda: require(transformed_path(stage_terms(0, q), entry, changed_exit, columns) == desired, + 'nonidentity spectator action')) + diagonal = next(term for term in controller['diagonal'] if term[0] == -1) + raw = terms([(q, -1)]+[item for s, yes in enumerate(diagonal[1:]) if yes for item in stage_terms(s, q)]) + corrected = phase(terms(list(raw)+[(q, 2)])) + reject('main_whole_job_final_translation_omitted', + lambda: require(phase(raw) == corrected, 'omitted X_q final raw endpoint correction')) + return rejected diff --git a/research/selected-native-tensor-closure/emit_recipes.py b/research/selected-native-tensor-closure/emit_recipes.py new file mode 100644 index 0000000000..4cd1b96d90 --- /dev/null +++ b/research/selected-native-tensor-closure/emit_recipes.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +"""Rebuild the selected candidate recipes with the credited source compiler. + +This is a candidate producer, not the independent acceptance checker. The +complete payload must match the already registered selected recipe digest. +""" +from __future__ import annotations + +from functools import lru_cache +import gzip +import hashlib +import importlib.util +import json +from pathlib import Path +import sys +import tempfile + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parents[1] +WORD = ROOT/'research/p104-helper470-twin-rung-removal/WORD.json.gz' +RESULT = ROOT/'research/p104-helper470-twin-rung-removal/RESULT.json' +COMPILER = ROOT/'research/p104-native-channel/vendor/clifford_exact.py' + + +def need(ok, message): + if not ok: + raise ValueError(message) + + +def canonical(value): + return json.dumps(value, sort_keys=True, separators=(',', ':')).encode() + + +def produce(): + need(hashlib.sha256(WORD.read_bytes()).hexdigest() == '7834725d9e34ee846a9ccc615221ea81e0201613938afa185d2eda0c9a6c232f', + 'selected word pin') + need(hashlib.sha256(COMPILER.read_bytes()).hexdigest() == '2982ead07721e83358dc67c6f49c2b4c16a236a6bebdfd72f58221be1f5d0962', + 'candidate compiler pin') + need(hashlib.sha256(RESULT.read_bytes()).hexdigest() == '84fcbe2a0b64307b5703c363408042bdd91555b0419509aa50daa43b3b1924ad', + 'selected result pin') + word = json.loads(gzip.decompress(WORD.read_bytes())) + result = json.loads(RESULT.read_bytes()) + spec = importlib.util.spec_from_file_location('credited_candidate_compiler', COMPILER) + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + # The candidate producer is deliberately the source implementation. All + # acceptance, including absolute phases, uses gauss_trace independently. + @lru_cache(None) + def chart(index): + return module.canonical_frame(9, tuple(word['frames'][index])) + main = set(word['live']) | {r+112 for r in word['live']} + pairs = {(event[2], event[3]) for event in word['program'] if event[0] == 'MOVE'} + pairs |= {(word['initial'][r], word['final'][r]) for r in range(word['public']) if r not in main} + records = [] + for i, (a, b) in enumerate(sorted(pairs)): + recipe, _ = module.lower_child(chart(a).adjoint().then(chart(b))) + records.append([a, b, recipe]) + if i % 250 == 0: + print('candidate_pairs', i, len(pairs), flush=True) + adapters = [] + for role, q in zip(word['live'], word['ports'], strict=True): + entry = module.cgate(9, q, 1).adjoint().then(chart(word['initial'][role])) + exit_ = chart(word['final'][role+112]).adjoint().then(module.cgate(9, q, -1).then(module.fourier(9))) + for kind, op in (('input', entry), ('output', exit_)): + recipe, _ = module.lower_child(op) + adapters.append({'kind': kind, 'role': role, 'port': q, 'recipe': recipe}) + payload = records+[item['recipe'] for item in adapters] + digest = hashlib.sha256(canonical(payload)).hexdigest() + need(digest == result['native']['recipe_payload_sha256'], 'complete payload equals selected registered recipes') + return {'schema': 'selected-native-tensor-closure/1', 'pairs': records, 'adapters': adapters, + 'source_word_sha256': hashlib.sha256(WORD.read_bytes()).hexdigest(), 'recipe_payload_sha256': digest} + + +def main(): + value = produce() + raw = canonical(value)+b'\n' + target = HERE/'NATIVE.json.gz' + if '--write' in sys.argv: + target.write_bytes(gzip.compress(raw, compresslevel=9, mtime=0)) + else: + need(gzip.decompress(target.read_bytes()) == raw, 'complete regenerated payload bytes') + print('PASS_SELECTED_NATIVE_RECIPE_EMISSION', value['recipe_payload_sha256']) + + +if __name__ == '__main__': + with tempfile.TemporaryDirectory(prefix='native-candidate-pycache-') as cache: + sys.pycache_prefix = cache + sys.dont_write_bytecode = True + main() diff --git a/research/selected-native-tensor-closure/gauss_trace.py b/research/selected-native-tensor-closure/gauss_trace.py new file mode 100644 index 0000000000..f232a11bf5 --- /dev/null +++ b/research/selected-native-tensor-closure/gauss_trace.py @@ -0,0 +1,332 @@ +#!/usr/bin/env python3 +"""Exact circuit traces by constrained quadratic Gauss sums. + +No Clifford tableau, distinguished input column, floating point, or dense +operator matrix is used. See PROOF.md. Apache-2.0. +""" +from __future__ import annotations + +from dataclasses import dataclass +from itertools import combinations + + +def require(ok: bool, reason: str) -> None: + if not ok: + raise ValueError(reason) + + +def bits(mask: int) -> list[int]: + out = [] + while mask: + low = mask & -mask + out.append(low.bit_length() - 1) + mask ^= low + return out + + +def gaussian_mul(a: tuple[int, int], b: tuple[int, int]) -> tuple[int, int]: + x, y = a + z, w = b + return x*z-y*w, x*w+y*z + + +def unit(power: int) -> tuple[int, int]: + return ((1, 0), (0, 1), (-1, 0), (0, -1))[power % 4] + + +@dataclass +class Quadratic: + """c + sum a_i x_i + 2 sum_(i Quadratic: + require(type(n) is int and n >= 0, 'quadratic variable count') + return cls([0]*n, set()) + + def copy(self) -> Quadratic: + return Quadratic(self.linear[:], self.pairs.copy(), self.constant) + + def toggle(self, i: int, j: int) -> None: + require(0 <= i < len(self.linear) and 0 <= j < len(self.linear), 'quadratic index') + if i == j: + self.linear[i] = (self.linear[i]+2) % 4 + return + pair = (min(i, j), max(i, j)) + if pair in self.pairs: + self.pairs.remove(pair) + else: + self.pairs.add(pair) + + def add_affine(self, coefficient: int, form: tuple[int, int]) -> None: + """Add coefficient times a Boolean affine parity, modulo four.""" + mask, constant = form + require(mask >= 0 and mask >> len(self.linear) == 0 and constant in (0, 1), 'affine form domain') + coefficient %= 4 + if coefficient == 0: + return + support = bits(mask) + self.constant = (self.constant+coefficient*constant) % 4 + signed = coefficient*(1-2*constant) + for i in support: + self.linear[i] = (self.linear[i]+signed) % 4 + if coefficient % 2: + for i, j in combinations(support, 2): + self.toggle(i, j) + + def add_product(self, a: tuple[int, int], b: tuple[int, int]) -> None: + """Add twice the product of two Boolean affine forms, modulo four.""" + x, c = a + y, d = b + require(x >= 0 and y >= 0 and (x | y) >> len(self.linear) == 0 and c in (0, 1) and d in (0, 1), + 'quadratic product domain') + self.constant = (self.constant+2*c*d) % 4 + for i in bits((y if c else 0) ^ (x if d else 0)): + self.linear[i] = (self.linear[i]+2) % 4 + for i in bits(x): + for j in bits(y): + self.toggle(i, j) + + def substitute(self, forms: list[tuple[int, int]], n: int) -> Quadratic: + require(len(forms) == len(self.linear), 'complete substitution') + out = Quadratic.zero(n) + out.constant = self.constant + for coefficient, form in zip(self.linear, forms, strict=True): + out.add_affine(coefficient, form) + for i, j in sorted(self.pairs): + out.add_product(forms[i], forms[j]) + return out + + def value(self, x: int) -> int: + require(type(x) is int and 0 <= x < 1 << len(self.linear), 'quadratic assignment') + return (self.constant+sum(a for i, a in enumerate(self.linear) if x >> i & 1) + +2*sum((x >> i & 1)*(x >> j & 1) for i, j in self.pairs)) % 4 + + +def gauss_sum(poly: Quadratic) -> tuple[int, int]: + """Return sum_x i^poly(x) exactly using one- and two-variable pivots.""" + q = poly.copy() + active = set(range(len(q.linear))) + factor = (1, 0) + while active: + odd = next((i for i in sorted(active) if q.linear[i] % 2), None) + if odd is not None: + a = q.linear[odd] + neighbours = 0 + retained = set() + for i, j in q.pairs: + if odd == i: + neighbours ^= 1 << j + elif odd == j: + neighbours ^= 1 << i + else: + retained.add((i, j)) + q.pairs = retained + q.linear[odd] = 0 + active.remove(odd) + z = unit(a) + factor = gaussian_mul(factor, (1+z[0], z[1])) + q.add_affine(-a, (neighbours, 0)) + continue + if q.pairs: + i, j = min(q.pairs) + require(i in active and j in active, 'elimination support invariant') + u = v = 0 + retained = set() + for a, b in q.pairs: + if (a, b) == (i, j): + continue + if i == a: + u ^= 1 << b + elif i == b: + u ^= 1 << a + elif j == a: + v ^= 1 << b + elif j == b: + v ^= 1 << a + else: + retained.add((a, b)) + require((u | v) & ((1 << i) | (1 << j)) == 0, 'two-pivot complement') + ci, cj = q.linear[i]//2, q.linear[j]//2 + q.pairs = retained + q.linear[i] = q.linear[j] = 0 + active.remove(i) + active.remove(j) + q.add_product((u, ci), (v, cj)) + factor = (2*factor[0], 2*factor[1]) + continue + if any(q.linear[i] == 2 for i in active): + return 0, 0 + require(all(q.linear[i] == 0 for i in active), 'isolated even phases') + size = 1 << len(active) + factor = factor[0]*size, factor[1]*size + active.clear() + require(not q.pairs and not any(q.linear), 'complete quadratic elimination') + return gaussian_mul(factor, unit(q.constant)) + + +def affine_solution(n: int, equations: list[tuple[int, int]]) -> tuple[list[tuple[int, int]], int] | None: + """RREF parametrization of every solution, with each free assignment once.""" + rows = [[mask, rhs] for mask, rhs in equations] + require(all(mask >= 0 and mask >> n == 0 and rhs in (0, 1) for mask, rhs in rows), 'linear equation domain') + pivots = [] + k = 0 + for j in range(n): + at = next((i for i in range(k, len(rows)) if rows[i][0] >> j & 1), None) + if at is None: + continue + rows[k], rows[at] = rows[at], rows[k] + for i in range(len(rows)): + if i != k and rows[i][0] >> j & 1: + rows[i][0] ^= rows[k][0] + rows[i][1] ^= rows[k][1] + pivots.append(j) + k += 1 + if any(mask == 0 and rhs for mask, rhs in rows): + return None + free = [j for j in range(n) if j not in set(pivots)] + positions = {j: i for i, j in enumerate(free)} + forms = [(0, 0)]*n + for j, i in positions.items(): + forms[j] = (1 << i, 0) + for pivot, (mask, rhs) in zip(pivots, rows, strict=False): + require(mask & sum(1 << j for j in pivots) == 1 << pivot, 'RREF pivot isolation') + forms[pivot] = (sum(1 << positions[j] for j in free if mask >> j & 1), rhs) + return forms, len(free) + + +def binary_inverse(rows: tuple[int, ...]) -> tuple[int, ...]: + n = len(rows) + require(n > 0 and all(type(row) is int and 0 <= row < 1 << n for row in rows), 'binary matrix domain') + augmented = [row | (1 << (n+i)) for i, row in enumerate(rows)] + for j in range(n): + k = next((i for i in range(j, n) if augmented[i] >> j & 1), None) + require(k is not None, 'singular address matrix') + augmented[j], augmented[k] = augmented[k], augmented[j] + for i in range(n): + if i != j and augmented[i] >> j & 1: + augmented[i] ^= augmented[j] + require([row & ((1 << n)-1) for row in augmented] == [1 << i for i in range(n)], 'binary inverse endpoint') + return tuple(row >> n for row in augmented) + + +def adjoint(circuit: tuple[tuple, ...]) -> tuple[tuple, ...]: + out = [] + for gate in reversed(circuit): + kind = gate[0] + if kind == 'P': + out.append(('P', binary_inverse(tuple(gate[1])))) + elif kind == 'D': + out.append(('D', tuple(gate[1]), tuple(-a % 4 for a in gate[2]))) + elif kind == 'C': + out.append(('C', gate[1], -gate[2])) + elif kind == 'phase': + out.append(('phase', -gate[1] % 4)) + else: + require(kind == 'X', 'adjoint gate vocabulary') + out.append(gate) + return tuple(out) + + +def circuit_trace(n: int, circuit: tuple[tuple, ...]) -> tuple[int, int, int]: + """Trace numerator real/imaginary and power-of-two denominator. + + All accepted gates are unitary by their checked syntax and invertible + address maps. The trace construction works at arbitrary n. + """ + require(type(n) is int and n > 0, 'positive circuit dimension') + addresses = [(1 << i, 0) for i in range(n)] + q = Quadratic.zero(n) + factor = (1, 0) + denominator = 0 + for gate in circuit: + kind = gate[0] + if kind == 'P': + require(len(gate) == 2 and len(gate[1]) == n, 'permutation arity') + rows = tuple(gate[1]) + binary_inverse(rows) + following = [] + for row in rows: + mask = constant = 0 + for j in bits(row): + mask ^= addresses[j][0] + constant ^= addresses[j][1] + following.append((mask, constant)) + addresses = following + elif kind == 'X': + require(len(gate) == 2 and type(gate[1]) is int and 0 <= gate[1] < 1 << n, 'translation domain') + addresses = [(mask, constant ^ (gate[1] >> i & 1)) for i, (mask, constant) in enumerate(addresses)] + elif kind == 'phase': + require(len(gate) == 2 and type(gate[1]) is int and 0 <= gate[1] < 4, 'phase domain') + q.constant = (q.constant+gate[1]) % 4 + elif kind == 'D': + require(len(gate) == 3 and len(gate[1]) == len(gate[2]) == n, 'diagonal arity') + rows, linear = gate[1:] + require(all(type(row) is int and 0 <= row < 1 << n for row in rows), 'quadratic matrix domain') + require(all(type(a) is int and 0 <= a < 4 for a in linear), 'quadratic linear phase domain') + require(all((rows[i] >> j & 1) == (rows[j] >> i & 1) for i in range(n) for j in range(n)), + 'symmetric source quadratic') + require(all((linear[i] & 1) == (rows[i] >> i & 1) for i in range(n)), 'source diagonal parity') + for coefficient, form in zip(linear, addresses, strict=True): + q.add_affine(coefficient, form) + for i in range(n): + for j in range(i+1, n): + if rows[i] >> j & 1: + q.add_product(addresses[i], addresses[j]) + else: + require(kind == 'C' and len(gate) == 3 and type(gate[1]) is int and 0 < gate[1] < 1 << n + and type(gate[2]) is int and gate[2] in (-1, 1), 'Gaussian gate domain') + direction, sign = gate[1:] + variable = 1 << len(q.linear) + q.linear.append(-sign % 4) + addresses = [(mask ^ (variable if direction >> i & 1 else 0), constant) + for i, (mask, constant) in enumerate(addresses)] + factor = gaussian_mul(factor, (1, sign)) + denominator += 1 + constraints = [(mask ^ (1 << i), constant) for i, (mask, constant) in enumerate(addresses)] + solved = affine_solution(len(q.linear), constraints) + if solved is None: + return 0, 0, 0 + forms, free = solved + result = gaussian_mul(factor, gauss_sum(q.substitute(forms, free))) + real, imag = result + while denominator and real % 2 == 0 and imag % 2 == 0: + real //= 2 + imag //= 2 + denominator -= 1 + return real, imag, denominator + + +def validate(n: int, circuit: tuple[tuple, ...]) -> None: + """Validate original syntax before an adjoint can normalize any field.""" + require(type(n) is int and n > 0, 'positive circuit dimension') + for gate in circuit: + require(type(gate) in (tuple, list) and len(gate) > 0, 'gate record') + kind = gate[0] + if kind == 'P': + require(len(gate) == 2 and len(gate[1]) == n, 'permutation arity') + binary_inverse(tuple(gate[1])) + elif kind == 'X': + require(len(gate) == 2 and type(gate[1]) is int and 0 <= gate[1] < 1 << n, 'translation domain') + elif kind == 'phase': + require(len(gate) == 2 and type(gate[1]) is int and 0 <= gate[1] < 4, 'phase domain') + elif kind == 'C': + require(len(gate) == 3 and type(gate[1]) is int and 0 < gate[1] < 1 << n + and type(gate[2]) is int and gate[2] in (-1, 1), 'Gaussian gate domain') + else: + require(kind == 'D' and len(gate) == 3 and len(gate[1]) == len(gate[2]) == n, 'diagonal arity') + rows, linear = gate[1:] + require(all(type(row) is int and 0 <= row < 1 << n for row in rows), 'quadratic matrix domain') + require(all(type(a) is int and 0 <= a < 4 for a in linear), 'quadratic linear phase domain') + require(all((rows[i] >> j & 1) == (rows[j] >> i & 1) for i in range(n) for j in range(n)), + 'symmetric source quadratic') + require(all((linear[i] & 1) == (rows[i] >> i & 1) for i in range(n)), 'source diagonal parity') + + +def equal(n: int, actual: tuple[tuple, ...], expected: tuple[tuple, ...], label: str) -> tuple[int, int, int]: + validate(n, expected) + trace = circuit_trace(n, actual+adjoint(expected)) + require(trace == (1 << n, 0, 0), label+': exact unitary overlap trace '+str(trace)) + return trace diff --git a/research/selected-native-tensor-closure/native_check.py b/research/selected-native-tensor-closure/native_check.py new file mode 100644 index 0000000000..a771f88464 --- /dev/null +++ b/research/selected-native-tensor-closure/native_check.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 +"""Source-native acceptance using the independent exact trace mechanism.""" +from __future__ import annotations + +from collections import Counter +from functools import lru_cache +import hashlib +import json + +from gauss_trace import adjoint, binary_inverse, equal, require + + +def canonical(value) -> bytes: + return json.dumps(value, sort_keys=True, separators=(',', ':')).encode() + + +def transpose(rows: tuple[int, ...]) -> tuple[int, ...]: + return tuple(sum(((row >> j) & 1) << i for i, row in enumerate(rows)) for j in range(len(rows))) + + +def span(vectors: list[int] | tuple[int, ...], n: int = 9) -> frozenset[int]: + out = {0} + for value in vectors: + require(type(value) is int and 0 < value < 1 << n and value not in out, 'independent binary frame') + out |= {x ^ value for x in tuple(out)} + return frozenset(out) + + +def extended_columns(vectors: tuple[int, ...], n: int) -> tuple[int, ...]: + """The source's ordered greedy extension, reimplemented without imports.""" + result, reached = [], {0} + for value in vectors+tuple(1 << i for i in range(n)): + if value not in reached: + result.append(value) + reached |= {x ^ value for x in tuple(reached)} + require(len(result) == n and len(reached) == 1 << n, 'complete source frame extension') + return tuple(result) + + +def gaussian(rank: int) -> tuple[tuple, ...]: + return tuple(('C', 1 << i, 1) for i in range(rank)) + + +@lru_cache(maxsize=None) +def frame(vectors: tuple[int, ...], n: int = 9) -> tuple[tuple, ...]: + span(vectors, n) + rank = len(vectors) + if rank == 0: + return () + if rank == n: + return gaussian(n) + matrix = transpose(extended_columns(vectors, n)) + inverse_columns = transpose(binary_inverse(matrix)) + quadratic = tuple((1 << i) ^ sum(((a & b).bit_count() % 2) << j + for j, b in enumerate(inverse_columns)) + for i, a in enumerate(inverse_columns)) + linear = tuple((row >> i) & 1 for i, row in enumerate(quadratic)) + conjugator = (('P', matrix), ('D', quadratic, linear)) + return adjoint(conjugator)+gaussian(rank)+conjugator + + +def monomial(record: dict) -> tuple[tuple, ...]: + require(type(record) is dict and set(record) == {'matrix_rows', 'offset', 'quadratic_rows', 'linear_Z4', 'phase'}, + 'complete monomial record') + matrix = tuple(record['matrix_rows']) + quadratic = tuple(record['quadratic_rows']) + linear = tuple(record['linear_Z4']) + require(len(matrix) == len(quadratic) == len(linear) == 9, 'source monomial dimension') + binary_inverse(matrix) + return (('D', quadratic, linear), ('P', matrix), ('X', record['offset']), ('phase', record['phase'])) + + +def lowered(record: dict) -> tuple[tuple, ...]: + require(type(record) is dict and set(record) == {'rank', 'left', 'right'}, 'complete lowered record') + rank = record['rank'] + require(type(rank) is int and 0 <= rank <= 9, 'source child rank') + if rank == 0: + require(record['right'] is None, 'rank-zero right convention') + return monomial(record['left']) + return monomial(record['right'])+gaussian(rank)+monomial(record['left']) + + +def inventory(word: dict) -> tuple[set[tuple[int, int]], list[int], list[tuple[str, int, int]]]: + main = set(word['live']) | {r+112 for r in word['live']} + helpers = [r for r in range(word['public']) if r not in main] + pairs = {(event[2], event[3]) for event in word['program'] if event[0] == 'MOVE'} + pairs |= {(word['initial'][r], word['final'][r]) for r in helpers} + adapters = [(kind, r, q) for r, q in zip(word['live'], word['ports'], strict=True) + for kind in ('input', 'output')] + return pairs, helpers, adapters + + +def check_native(word: dict, source_result: dict, candidates: dict, progress=False) -> dict: + pairs, helpers, adapter_ids = inventory(word) + require(set(candidates) == {'schema', 'pairs', 'adapters', 'source_word_sha256', 'recipe_payload_sha256'}, + 'complete native candidate envelope') + require(candidates['schema'] == 'selected-native-tensor-closure/1', 'candidate schema') + require(candidates['source_word_sha256'] == '7834725d9e34ee846a9ccc615221ea81e0201613938afa185d2eda0c9a6c232f', + 'candidate source word binding') + records = {(a, b): record for a, b, record in candidates['pairs']} + require(len(records) == len(candidates['pairs']) and set(records) == pairs, 'exact MOVE/helper recipe coverage') + require(all(set(item) == {'kind', 'role', 'port', 'recipe'} for item in candidates['adapters']), + 'complete main adapter records') + require([(item['kind'], item['role'], item['port']) for item in candidates['adapters']] == adapter_ids, + 'complete ordered main adapter coverage') + payload = candidates['pairs']+[item['recipe'] for item in candidates['adapters']] + digest = hashlib.sha256(canonical(payload)).hexdigest() + require(digest == source_result['native']['recipe_payload_sha256'] == candidates['recipe_payload_sha256'], + 'full selected recipe payload hash') + charts = [frame(tuple(vectors)) for vectors in word['frames']] + max_branches = 0 + traces = [] + for count, (a, b) in enumerate(sorted(pairs)): + expected = adjoint(charts[a])+charts[b] + actual = lowered(records[a, b]) + require(records[a, b]['rank'] == abs(len(word['frames'][a])-len(word['frames'][b])), 'paid or helper source rank') + trace = equal(9, actual, expected, f'frame pair {a}:{b}') + max_branches = max(max_branches, sum(g[0] == 'C' for g in actual+expected)) + traces.append([a, b, *trace]) + if progress and count % 500 == 0: + print('trace_pairs', count, len(pairs), flush=True) + adapters = {} + for item in candidates['adapters']: + kind, r, q = item['kind'], item['role'], item['port'] + if kind == 'input': + expected = (('C', q, -1),)+charts[word['initial'][r]] + else: + expected = adjoint(charts[word['final'][r+112]])+(('C', q, -1),)+gaussian(9) + require(item['recipe']['rank'] == 0, 'ordinary intrinsic adapter') + actual = lowered(item['recipe']) + equal(9, actual, expected, f'main {kind} {r}') + adapters[kind, r] = actual + + # Join the logical endpoint X'=X, Y'=Y+X to all three canonical paths. + main_paths = 0 + for r, q in zip(word['live'], word['ports'], strict=True): + ix, iy = word['initial'][r], word['initial'][r+112] + ox, oy = word['final'][r], word['final'][r+112] + require(word['frames'][ix] == [q] and word['frames'][iy] == [] and len(word['frames'][ox]) == 9, + 'actual main entrance and full source exit') + entry, exit_ = adapters['input', r], adapters['output', r] + d0 = (('C', q, -1),)+gaussian(9) + paths = [ + (entry+adjoint(charts[ix])+charts[ox], d0, 'source diagonal'), + (adjoint(charts[iy])+charts[oy]+exit_, d0, 'target diagonal'), + (entry+adjoint(charts[ix])+charts[oy]+exit_, (('C', q, -1),)+d0, 'positive cross'), + ] + for actual, expected, name in paths: + equal(9, actual, expected, f'local {name} {r}') + main_paths += 1 + equal(9, (('C', q, 1), ('C', q, 1)), (('X', q),), 'central square absolute phase') + + helper_pairs = {(word['initial'][r], word['final'][r]) for r in helpers} + helper_profile = Counter() + for a, b in sorted(helper_pairs): + rec = records[a, b] + require(rec['rank'] > 0, 'positive productive helper') + complete = adjoint(monomial(rec['right']))+adjoint(charts[a])+charts[b]+adjoint(monomial(rec['left'])) + equal(9, complete, gaussian(rec['rank']), f'complete helper normalization {a}:{b}') + for r in helpers: + helper_profile[records[word['initial'][r], word['final'][r]]['rank']] += 1 + require(dict(sorted(helper_profile.items())) == {int(k): v for k, v in source_result['native']['helpers'].items()}, + 'selected arbitrary helper census') + return {'distinct_native_pairs': len(pairs), 'distinct_move_pairs': len({(e[2], e[3]) for e in word['program'] if e[0] == 'MOVE'}), + 'main_adapters': len(adapters), 'complete_main_paths': main_paths, 'central_squares': len(word['ports']), + 'helper_normalized_pairs': len(helper_pairs), 'arbitrary_helpers': len(helpers), 'helper_profile': dict(sorted(helper_profile.items())), + 'maximum_closed_trace_branches': max_branches, 'complete_trace_digest': hashlib.sha256(canonical(traces)).hexdigest(), + 'selected_recipe_payload_sha256': digest, 'method': 'exact constrained quadratic Gauss sum; no tableau or distinguished column'} diff --git a/research/selected-native-tensor-closure/source_algebra.py b/research/selected-native-tensor-closure/source_algebra.py new file mode 100644 index 0000000000..597ccfbd2d --- /dev/null +++ b/research/selected-native-tensor-closure/source_algebra.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 +"""Complete current-word gauge, ownership and free-Laurent certificates.""" +from __future__ import annotations + +from collections import Counter +from fractions import Fraction +import hashlib + +from gauss_trace import require +from native_check import canonical, span + + +def add_scaled(dst, src, factor): + for key, value in tuple(src.items()): + updated = dst.get(key, 0)+factor*value + if updated: + dst[key] = updated + else: + dst.pop(key, None) + + +def local_gauge(word: dict, source_result: dict) -> dict: + public, owned = word['public'], word['owned_fields'] + require((public, owned) == (770, 29), 'selected public/owned counts') + total = public+owned + require(len(word['initial']) == len(word['final']) == public, 'complete local boundaries') + spaces = [span(vectors) for vectors in word['frames']] + rows = [{i: Fraction(1)} for i in range(total)] + current = word['initial'][:]+[None]*owned + live_owned = set() + paid = Counter() + counts = Counter() + peak = lifetimes = 0 + for index, event in enumerate(word['program']): + kind, target = event[:2] + require(type(target) is int and 0 <= target < total, 'local target range') + counts[kind] += 1 + if kind == 'MOVE': + require(len(event) == 5, 'MOVE grammar') + _, _, old, new, private = event + require(type(private) is bool and current[target] == old and (target in live_owned) == private, + f'current native source and ownership at {index}') + require(spaces[new] < spaces[old] if private else spaces[old] < spaces[new], 'proper nested MOVE') + paid[abs(len(word['frames'][new])-len(word['frames'][old]))] += 1 + current[target] = new + elif kind == 'INIT': + require(len(event) == 3 and target >= public and target not in live_owned, 'fresh owned INIT') + live_owned.add(target) + rows[target] = {} + current[target] = event[2] + peak = max(peak, len(live_owned)) + lifetimes += 1 + elif kind == 'CLEAR': + require(len(event) == 3 and target in live_owned and current[target] == event[2], 'owned CLEAR at current chart') + live_owned.remove(target) + rows[target] = {} + current[target] = None + else: + require(kind in ('ADD', 'SCALE', 'COPY') and len(event) == (4 if kind == 'SCALE' else 5), 'scalar grammar') + coefficient, chart = Fraction(event[-2]), event[-1] + require(coefficient != 0 and current[target] == chart, 'scalar current chart') + require(coefficient.denominator & (coefficient.denominator-1) == 0, 'dyadic exterior coefficient') + if kind == 'SCALE': + rows[target] = {origin: coefficient*value for origin, value in rows[target].items()} + else: + source = event[2] + require(type(source) is int and 0 <= source < total and source != target and current[source] == chart, + 'exact common-chart source') + if kind == 'COPY': + require(target in live_owned and rows[target] == {}, 'COPY into reached owned zero') + rows[target] = {origin: coefficient*value for origin, value in rows[source].items()} + else: + add_scaled(rows[target], rows[source], coefficient) + require(not live_owned and current[:public] == word['final'] and current[public:] == [None]*owned, + 'all current charts and ownership returned') + for target, row in enumerate(rows): + expected = {target: Fraction(1)} if target < public else {} + if target-112 in word['live']: + expected[target-112] = Fraction(1) + require(row == expected, f'complete symbolic gauge endpoint {target}') + expected = source_result['local'] + require({int(k): v for k, v in expected['paid'].items()} == dict(paid), 'selected paid occurrences') + require((peak, lifetimes) == (expected['peak_owned_fields'], expected['owned_lifetimes']), 'actual owned chronology') + encoded = [[[origin, str(coefficient)] for origin, coefficient in sorted(row.items())] for row in rows] + return {'events': len(word['program']), 'opcode_counts': dict(sorted(counts.items())), 'rows': total, + 'arbitrary_public_columns': public, 'owned_entrance_columns_erased': owned, + 'full_endpoint_coefficients': total*total, 'owned_lifetimes': lifetimes, 'peak_owned': peak, + 'paid_profile': dict(sorted(paid.items())), 'gauge_endpoint_sha256': hashlib.sha256(canonical(encoded)).hexdigest(), + 'rule': 'chart coefficients are formal inverse/forward products; every scalar remains exterior'} + + +ZERO = (0,)*34 + + +def shift(poly, exponent, factor=1): + require(len(exponent) == 34, 'all Laurent variables present') + return {tuple(a+b for a, b in zip(term, exponent, strict=True)): factor*value + for term, value in poly.items() if factor*value} + + +def event_pullback(weights, event): + """Substitute the actual forward assignment into a final linear form.""" + kind = event[0] + if kind in ('LOCAL', 'QUERY_SHEAR'): + _, stage, pair, backward = event + require(type(backward) is bool, 'literal controller orientation') + x, y = 2*pair, 2*pair+1 + if backward: + x, y = y, x + d = ((0,)+tuple(int(j == stage) for j in range(33))) if kind == 'LOCAL' else ZERO + cross = (1 if backward else -1,)+(0,)*33 + wx, wy = shift(weights[x], d), shift(weights[y], d) + add_scaled(wx, shift(wy, cross), -1 if backward else 1) + weights[x], weights[y] = wx, wy + elif kind == 'IDLE': + require(len(set(event[2])) == len(event[2]), 'no repeated idle stage') + d = (0,)+tuple(int(j in event[2]) for j in range(33)) + for row in (2*event[1], 2*event[1]+1): + weights[row] = shift(weights[row], d) + elif kind == 'Q_POWER': + require(event[2] in (-2, 2), 'ordinary even central power') + weights[event[1]] = shift(weights[event[1]], (event[2],)+(0,)*33) + elif kind == 'ADD': + add_scaled(weights[event[2]], weights[event[1]], Fraction(event[3])) + elif kind == 'SCALE': + weights[event[1]] = shift(weights[event[1]], ZERO, Fraction(event[2])) + elif kind == 'SWAP': + weights[event[1]], weights[event[2]] = weights[event[2]], weights[event[1]] + else: + require(kind == 'SIGNED_SWAP', 'controller gate vocabulary') + weights[event[1]], weights[event[2]] = shift(weights[event[2]], ZERO, -1), weights[event[1]] + + +def free_laurent(controller: dict) -> dict: + require((controller['kind'], controller['pairs'], controller['ambient_rank']) == ('complex', 16, 145), 'selected controller type') + for side in ('left', 'right'): + for output in range(32): + weights = [{ZERO: Fraction(int(row == output))} if row == output else {} for row in range(32)] + for event in reversed(controller['boundaries'][side+'_program']): + event_pullback(weights, event) + expected = [{ZERO: Fraction(c)} if Fraction(c) else {} for c in controller['boundaries'][side][output]] + require(weights == expected, 'literal '+side+' boundary matrix') + program = controller['boundaries']['right_program']+controller['physical_events']+controller['boundaries']['left_program'] + outputs = [] + for output in range(32): + weights = [{ZERO: Fraction(1)} if row == output else {} for row in range(32)] + for event in reversed(program): + event_pullback(weights, event) + expected = [{tuple(controller['diagonal'][output]): Fraction(1)} if row == output else {} for row in range(32)] + require(weights == expected, f'full free-Laurent reverse output {output}') + outputs.append([output, controller['diagonal'][output], '1']) + sites = [] + local_count = query_count = 0 + for i, event in enumerate(controller['physical_events']): + if event[0] == 'QUERY_SHEAR': + query_count += 1 + if event[0] != 'LOCAL': + continue + _, stage, pair, backward = event + require(not backward, 'all actual suppliers forward') + local_count += 1 + if stage in controller['wrapper_sites']: + x, y = 2*pair, 2*pair+1 + require(controller['physical_events'][i-2:i+3] == [ + ['Q_POWER', x, -2], ['SIGNED_SWAP', x, y], event, + ['Q_POWER', y, 2], ['SIGNED_SWAP', y, x]], 'literal five-step reverse interface') + sites.append(stage) + require(sites == controller['wrapper_sites'] and len(sites) == 16 and local_count == 18 and query_count == 15, + 'complete all-forward sites and thin calls') + return {'controller_events': len(controller['physical_events']), 'literal_boundary_events': len(program)-len(controller['physical_events']), + 'all_1024_laurent_entries_checked': True, 'forward_local_calls': local_count, 'thin_queries_per_port': query_count, + 'five_step_forward_wrappers': len(sites), 'endpoint_sha256': hashlib.sha256(canonical(outputs)).hexdigest(), + 'method': 'reverse linear-functional substitution; no finite-order quotient of Q'} + + +def ownership_and_invoice(word: dict, controller: dict, source_result: dict) -> dict: + main_roles = set(word['live']) | {r+112 for r in word['live']} + helpers = [r for r in range(word['public']) if r not in main_roles] + calls = [e for e in controller['physical_events'] if e[0] == 'LOCAL'] + mains = 32*len(word['live']) + productives = mains+len(calls)*len(helpers) + maps = [] + for call, event in enumerate(calls): + pair = event[2] + mapping = {r: (2*pair)*len(word['live'])+j for j, r in enumerate(word['live'])} + mapping.update({r+112: (2*pair+1)*len(word['live'])+j for j, r in enumerate(word['live'])}) + mapping.update({r: mains+call*len(helpers)+j for j, r in enumerate(helpers)}) + require(set(mapping) == set(range(word['public'])) and len(set(mapping.values())) == word['public'], + 'actual distinct local public owners') + mapping.update({r: productives+r-word['public'] for r in range(word['public'], word['public']+word['owned_fields'])}) + require(len(set(mapping.values())) == word['public']+word['owned_fields'], 'owned bank disjoint from all public inputs') + maps.append([mapping[r] for r in range(word['public']+word['owned_fields'])]) + paid = Counter({int(k): len(calls)*v for k, v in source_result['local']['paid'].items()}) + targets = Counter({int(k): len(calls)*v for k, v in source_result['local']['helpers'].items()}) + for event in controller['physical_events']: + if event[0] == 'QUERY_SHEAR': + paid[1] += len(word['ports']) + elif event[0] == 'IDLE': + paid[8*len(event[2])] += 2*len(word['ports']) + for diagonal in controller['diagonal']: + targets[1+8*sum(diagonal[1:])] += len(word['ports']) + cost = source_result['complete_cost'] + require(dict(paid) == {int(k): v for k, v in cost['paid'].items()}, 'complete paid profile unchanged') + require(dict(targets) == {int(k): v for k, v in cost['targets'].items()}, 'complete productive profile unchanged') + require(sum(targets.values()) == productives == 13444, 'all productive owners') + return {'main_arbitrary_inputs': mains, 'fresh_helper_inputs': len(calls)*len(helpers), 'productive_inputs': productives, + 'local_owned_labels': word['owned_fields'], 'separate_thin_owned_label': productives+word['owned_fields'], + 'paid_calls': sum(paid.values()), 'paid_rank_mass': sum(r*n for r, n in paid.items()), + 'target_rank_mass': sum(r*n for r, n in targets.items()), 'field_maps_sha256': hashlib.sha256(canonical(maps)).hexdigest(), + 'limits': 'ideal source ownership only; allocation, physical erasure and retained backing remain premises'} diff --git a/research/selected-native-tensor-closure/test_gauss_trace.py b/research/selected-native-tensor-closure/test_gauss_trace.py new file mode 100644 index 0000000000..11d945e72b --- /dev/null +++ b/research/selected-native-tensor-closure/test_gauss_trace.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 +"""Independent bounded oracles and rejecting controls for gauss_trace.py.""" +from __future__ import annotations + +from fractions import Fraction +from itertools import combinations, product +import json +from pathlib import Path +import random +import sys + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from gauss_trace import Quadratic, adjoint, affine_solution, circuit_trace, equal, gauss_sum, require + + +def exhaustive_quadratics() -> int: + count = 0 + for n in range(5): + edges = list(combinations(range(n), 2)) + for linear in product(range(4), repeat=n): + for mask in range(1 << len(edges)): + q = Quadratic(list(linear), {edge for j, edge in enumerate(edges) if mask >> j & 1}) + real = imag = 0 + for x in range(1 << n): + v = (sum(linear[j]*((x >> j) & 1) for j in range(n)) + +2*sum(((x >> a) & 1)*((x >> b) & 1) for a, b in q.pairs)) % 4 + real += (1, 0, -1, 0)[v] + imag += (0, 1, 0, -1)[v] + require(gauss_sum(q) == (real, imag), 'exhaustive quadratic sum') + count += 1 + return count + + +def affine_and_substitution() -> dict[str, int]: + rng = random.Random(7606030) + systems = substitutions = 0 + for n in range(8): + for _ in range(60): + equations = [(rng.randrange(1 << n), rng.randrange(2)) for __ in range(rng.randrange(n+3))] + expected = {x for x in range(1 << n) + if all((x & mask).bit_count() % 2 == rhs for mask, rhs in equations)} + solved = affine_solution(n, equations) + if solved is None: + actual = set() + else: + forms, free = solved + values = [sum((((mask & t).bit_count() % 2)^c) << j for j, (mask, c) in enumerate(forms)) + for t in range(1 << free)] + require(len(set(values)) == len(values), 'unique affine enumeration') + actual = set(values) + require(actual == expected, 'complete affine solution set') + systems += 1 + for _ in range(60): + q = Quadratic([rng.randrange(4) for __ in range(n)], + {pair for pair in combinations(range(n), 2) if rng.randrange(2)}, rng.randrange(4)) + free = rng.randrange(7) + forms = [(rng.randrange(1 << free), rng.randrange(2)) for __ in range(n)] + changed = q.substitute(forms, free) + for t in range(1 << free): + x = sum((((mask & t).bit_count() % 2)^c) << j for j, (mask, c) in enumerate(forms)) + require(changed.value(t) == q.value(x), 'complete affine quadratic substitution') + substitutions += 1 + return {'affine_systems': systems, 'affine_substitutions': substitutions} + + +def dense_trace(n: int, circuit: tuple[tuple, ...]) -> tuple[Fraction, Fraction]: + """Separate direct column propagation; only used at n<=4.""" + size = 1 << n + total = [Fraction(0), Fraction(0)] + for column in range(size): + real = [Fraction(int(x == column)) for x in range(size)] + imag = [Fraction(0) for _ in range(size)] + for gate in circuit: + kind = gate[0] + ar, ai = [Fraction(0) for _ in range(size)], [Fraction(0) for _ in range(size)] + if kind == 'C': + q, sign = gate[1:] + for x in range(size): + y = x ^ q + ar[x] = (real[x]-sign*imag[x]+real[y]+sign*imag[y])/2 + ai[x] = (imag[x]+sign*real[x]+imag[y]-sign*real[y])/2 + else: + for x in range(size): + y, power = x, 0 + if kind == 'P': + y = sum(((row & x).bit_count() % 2) << j for j, row in enumerate(gate[1])) + elif kind == 'X': + y ^= gate[1] + elif kind == 'phase': + power = gate[1] + else: + require(kind == 'D', 'dense oracle gate') + power = sum(a for j, a in enumerate(gate[2]) if x >> j & 1) + power += 2*sum(1 for a in range(n) for b in range(a+1, n) + if (x >> a & 1) and (x >> b & 1) and (gate[1][a] >> b & 1)) + a, b = real[x], imag[x] + ar[y], ai[y] = ((a, b), (-b, a), (-a, -b), (b, -a))[power % 4] + real, imag = ar, ai + total[0] += real[column] + total[1] += imag[column] + return total[0], total[1] + + +def dense_circuits() -> int: + rng = random.Random(7606546) + count = 0 + for n in range(1, 5): + for _ in range(30): + gates = [] + for __ in range(1+rng.randrange(18)): + kind = rng.randrange(5) + if kind == 0: + gates.append(('C', rng.randrange(1, 1 << n), (-1, 1)[rng.randrange(2)])) + elif kind == 1: + gates.append(('X', rng.randrange(1 << n))) + elif kind == 2: + gates.append(('phase', rng.randrange(4))) + elif kind == 3: + rows = [1 << j for j in range(n)] + for ___ in range(2*n): + a, b = rng.randrange(n), rng.randrange(n) + if a != b: + rows[a] ^= rows[b] + gates.append(('P', tuple(rows))) + else: + linear = tuple(rng.randrange(4) for __ in range(n)) + rows = [((a % 2) << j) for j, a in enumerate(linear)] + for a, b in combinations(range(n), 2): + if rng.randrange(2): + rows[a] ^= 1 << b + rows[b] ^= 1 << a + gates.append(('D', tuple(rows), linear)) + circuit = tuple(gates) + got = circuit_trace(n, circuit) + expected = dense_trace(n, circuit) + require((Fraction(got[0], 1 << got[2]), Fraction(got[1], 1 << got[2])) == expected, + 'Gauss trace equals separate dense-column oracle') + equal(n, circuit+adjoint(circuit), (), 'literal inverse') + count += 1 + require(circuit_trace(300, (('C', 1, 1),)) == (1 << 299, 1 << 299, 0), + '300-axis symbolic Gaussian trace') + return count + + +def rejecting_controls() -> int: + attempts = [ + lambda: equal(1, (('phase', 1),), (), 'wrong global phase'), + lambda: equal(1, (('D', (0,), (2,)),), (), 'input phase invisible on zero'), + lambda: equal(1, (('C', 1, 1),), (('C', 1, -1),), 'wrong child direction'), + lambda: equal(1, (('X', 1), ('D', (1,), (1,))), (('D', (1,), (1,)), ('X', 1)), 'wrong order'), + lambda: circuit_trace(2, (('P', (1, 1)),)), + lambda: circuit_trace(1, (('C', 1, 0),)), + lambda: circuit_trace(2, (('D', (2, 0), (0, 0)),)), + lambda: circuit_trace(1, (('scale', 2),)), + lambda: equal(1, (), (('phase', 4),), 'expected phase before normalization'), + lambda: equal(1, (), (('phase', 0, 'discarded'),), 'expected arity before adjoint'), + ] + for run in attempts: + try: + run() + except ValueError: + continue + raise ValueError('trace negative control accepted') + return len(attempts) + + +def main() -> None: + result = {'all_quadratics_through_four_variables': exhaustive_quadratics(), + **affine_and_substitution(), 'independent_dense_circuits': dense_circuits(), + 'rejecting_controls': rejecting_controls(), 'seed': [7606030, 7606546]} + print('PASS_GAUSS_TRACE', json.dumps(result, sort_keys=True)) + + +if __name__ == '__main__': + main() diff --git a/research/selected-native-tensor-closure/verify.py b/research/selected-native-tensor-closure/verify.py new file mode 100644 index 0000000000..e70c3d3fbe --- /dev/null +++ b/research/selected-native-tensor-closure/verify.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +"""Source-bound selected native tensor closure; see PROOF.md for all-size proof. + +Prepared for @CubeJerry with substantial OpenAI Codex assistance. Apache-2.0. +""" +from __future__ import annotations + +import argparse +import gzip +import hashlib +import json +from pathlib import Path +import sys +import tempfile + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parents[1] +sys.path.insert(0, str(HERE)) +# Direct replay also avoids reading stale preexisting package bytecode. The +# repository's registered runner supplies its own private cache as well. +_cache = tempfile.TemporaryDirectory(prefix='native-tensor-closure-pycache-') +sys.pycache_prefix = _cache.name +sys.dont_write_bytecode = True + +from gauss_trace import require +from native_check import check_native +from source_algebra import free_laurent, local_gauge, ownership_and_invoice +from caller_check import check_caller +from controls import run as negative_controls + + +BASE = '7c49fe32c55de02c2c873655c804b16e4c3fef0a' +PINS = { + 'research/p104-helper470-twin-rung-removal/WORD.json.gz': '7834725d9e34ee846a9ccc615221ea81e0201613938afa185d2eda0c9a6c232f', + 'research/p104-helper470-twin-rung-removal/RESULT.json': '84fcbe2a0b64307b5703c363408042bdd91555b0419509aa50daa43b3b1924ad', + 'research/p104-productive-controller/CONTROLLER.json': 'a84f6bbd547379597464e56c420cc665f96bc43a9a1351eed4b7cd0fce3cc4b9', + 'research/p104-caller-independent-audit/capsule/outputs/p104-caller-route-audit-v2-20261025/sources/DATA-PHASES.json': 'c5916ff312678f12aba403847643fbbc29f643acec60bdf836fa56cd3e8251fa', + 'research/p104-native-channel/vendor/clifford_exact.py': '2982ead07721e83358dc67c6f49c2b4c16a236a6bebdfd72f58221be1f5d0962', + 'research/p104-productive-controller/local.py': '878734dfd64c4b9d362d0ae476cc5c2a19fe93e48b7378763b23f3e0dd0a5a71', + 'research/p104-productive-controller/caller.py': '300d40043953e4f6aecfde8f2d1004b8ad6dc5a79ec3c7666c9908c2466c91c8', +} + + +def authenticate(path, data): + require(path in PINS and hashlib.sha256(data).hexdigest() == PINS[path], 'source byte pin: '+path) + return data + + +def run(): + raw = {path: authenticate(path, (ROOT/path).read_bytes()) for path in PINS} + word = json.loads(gzip.decompress(raw['research/p104-helper470-twin-rung-removal/WORD.json.gz'])) + source_result = json.loads(raw['research/p104-helper470-twin-rung-removal/RESULT.json']) + controller = json.loads(raw['research/p104-productive-controller/CONTROLLER.json']) + phase_source = json.loads(raw['research/p104-caller-independent-audit/capsule/outputs/p104-caller-route-audit-v2-20261025/sources/DATA-PHASES.json']) + candidates = json.loads(gzip.decompress((HERE/'NATIVE.json.gz').read_bytes())) + local = local_gauge(word, source_result) + native = check_native(word, source_result, candidates) + laurent = free_laurent(controller) + caller = check_caller(word, controller, phase_source, source_result) + invoice = ownership_and_invoice(word, controller, source_result) + controls = negative_controls(word, source_result, candidates, controller, phase_source) + for path in ('research/p104-helper470-twin-rung-removal/WORD.json.gz', 'research/p104-productive-controller/caller.py'): + try: + authenticate(path, raw[path]+b' ') + except ValueError: + controls['source_drift:'+path] = 'Rejected before parsing or execution' + else: + raise ValueError('source mutation accepted: '+path) + return { + 'schema': 'selected-native-tensor-closure-result/1', + 'status': 'PASS_SELECTED_NATIVE_TENSOR_CLOSURE', + 'base_commit': BASE, + 'source_sha256': PINS, + 'local_gauge': local, + 'native_trace': native, + 'complete_caller': laurent, + 'whole_job_algebra': caller, + 'ownership_and_invoice': invoice, + 'negative_controls': dict(sorted(controls.items())), + 'all_size_status': 'PROOF.md proves the typed algebraic implication for every positive f and arbitrary spectator factors; finite replay is the exact selected-source premise', + 'limitations': ['ideal owned reset semantics remain assumed', 'physical selected-rate dispatcher not implemented', + 'compiler, precision_tape and analytic_recovery not discharged', 'No new kappa'], + } + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--write', action='store_true', help='write the deterministic new-package result receipt') + args = parser.parse_args() + result = run() + path = HERE/'RESULT.json' + encoded = (json.dumps(result, sort_keys=True, indent=2)+'\n').encode() + if args.write: + path.write_bytes(encoded) + else: + require(path.read_bytes() == encoded, 'complete deterministic result receipt') + print(result['status'], json.dumps({ + 'native_pairs': result['native_trace']['distinct_native_pairs'], + 'raw_contacts': result['whole_job_algebra']['complete_raw_contacts'], + 'productive_fields': result['ownership_and_invoice']['productive_inputs'], + 'paid_calls': result['ownership_and_invoice']['paid_calls'], + 'rejecting_controls': len(result['negative_controls']), 'kappa_changed': False, + }, sort_keys=True)) + + +if __name__ == '__main__': + try: + main() + finally: + _cache.cleanup()