The script src/26-pnp/closure_pnp.py of the validation package of the paper 26-pnp, rendered with an anchor per line. ▶ run the predicates on Google Colab.
1#!/usr/bin/env python32# -*- coding: utf-8 -*-3"""4closure_pnp.py (26-pnp/validation) -- FINITE, no continuum, NO RANDOM NUMBER GENERATOR.5=========================================================================================6The arithmetic-hierarchy closure as the algebraic foundation of the one-way structure (sec:owf).7The ladder counting -> addition -> multiplication -> exponentiation closes: the fifth operation is8iteration of the power map P_k(x)=x^k, which on a finite carrier is orbit-counting (finite-orbit9closure), so it returns to the first role. Consequences for P vs NP:1011 T1 ASCENT DYNAMICS / closure phase formula. In the scale chart x=g^m (g primitive, m in Z_{P-1}),12 the fourth role acts by m->k m and its iteration by P_k^r(g^m)=g^{k^r m mod (P-1)}. The ascent13 a_P(j)=g^j is orbit traversal j->j+1 => g^{j+1}=g*g^j: one multiplication per COUNTED step.14 T2 THE FOUR FORWARD ROLES ARE EACH poly(ell). counting (+1), addition (+a), multiplication (m*),15 exponentiation/ascent (g^j by square-and-multiply) all cost <= O(ell) modular operations.16 T3 ONLY THE INVERSE OF THE FOURTH ROLE BREAKS. the descent d_P=dlog_g (recover the COUNT m from17 g^m) scales as sqrt(P) (baby-step-giant-step); ascent/descent op-ratio grows ~ sqrt(P)/ell.18 So all irreversibility is localised to inverting the last primitive role -- not a forbidden op19 but the orbit-POSITION problem the forward 'return to counting' does not supply.20 T4 BIJECTIVE vs COMPRESSIVE. P_k is a bijection of F_P^x iff gcd(k,P-1)=1, else its image has size21 (P-1)/gcd(k,P-1); the iteration orbit r->k^r m is finite and eventually periodic (closure).2223PINNED before the numbers. No np.random anywhere. Dependencies: sympy.24"""25import math26from sympy import isprime, primitive_root, primerange2728PASS=029def ok(t,c,m):30 global PASS31 assert c, f"FAIL {t}: {m}"32 print(f"PASS {t}: {m}"); PASS+=13334# ---------- T1: closure / ascent phase formula ----------35print("T1 closure phase formula P_k^r(g^m) = g^{k^r m mod (P-1)} (iterating the 4th role = phase index x k^r)")36viol=0; nch=037for P in (13,101,1009,10009):38 g=int(primitive_root(P)); n=P-139 for k in (2,3,5,7,10):40 for m in (1,2,7):41 for r in (0,1,2,3,5):42 x=pow(g,m,P)43 for _ in range(r): x=pow(x,k,P) # iterate the power map r times44 rhs=pow(g,(pow(k,r,n)*m)%n,P) # closed form45 nch+=146 if x!=rhs: viol+=147print(f" checked {nch} (P,k,m,r) cases across 4 carriers")48ok("T1", viol==0, f"the fourth role iterates as m->k^r m in the phase-index ring Z_(P-1) ({viol} violations) -- "49 "the ascent g^j is counting along the power-map orbit (g^{j+1}=g*g^j)")5051# ---------- T2: the four forward roles are each poly(ell) ----------52print("\nT2 the four forward roles are each poly(ell): modular-op count per application")53print(f" {'P':>10} {'ell':>4} {'count(+1)':>9} {'add(+a)':>8} {'mult(m*)':>9} {'ascent g^j (sq&mul)':>20}")54def ascent_ops(g,j,P): # modmuls in square-and-multiply55 ops=0; r=1; b=g; e=j56 while e>0:57 if e&1: r=(r*b)%P; ops+=158 e>>=159 if e>0: b=(b*b)%P; ops+=160 return ops61roles_poly=True62for P in (10007,1000003,100000007):63 ell=P.bit_length(); g=int(primitive_root(P))64 asc=ascent_ops(g,P-2,P) # worst-ish exponent65 # counting=1 op, addition=O(ell) bit (1 modular add), multiplication=1 modular mult66 if not (asc<=2*ell+2): roles_poly=False67 print(f" {P:>10} {ell:>4} {1:>9} {1:>8} {1:>9} {asc:>20} (<= 2 ell = {2*ell})")68ok("T2", roles_poly, "every forward role -- counting, addition, multiplication, exponentiation -- costs O(ell) "69 "modular operations; the closed ladder is poly(ell) in the forward direction")7071# ---------- T3: only the inverse of the fourth role breaks ----------72print("\nT3 only the INVERSE of the fourth role is hard: ascent (sq&mul) vs descent dlog (BSGS)")73def dlog_bsgs(g,h,P): # returns (x, group-op count)74 n=P-1; m=math.isqrt(n)+1; tbl={}; e=1; ops=075 for j in range(m): tbl.setdefault(e,j); e=(e*g)%P; ops+=176 fac=pow(g,(n-1)*m % n,P); gamma=h%P77 for i in range(m):78 if gamma in tbl: return i*m+tbl[gamma], ops+i79 gamma=(gamma*fac)%P80 return None, ops+m81print(f" {'P':>10} {'ell':>4} {'ascent ops':>10} {'descent ops':>12} {'ratio':>10}")82ratios=[]83for P in (10007,1000003,100000007):84 ell=P.bit_length(); g=int(primitive_root(P))85 m_true=P//3; h=pow(g,m_true,P)86 asc=ascent_ops(g,m_true,P)87 x,desc=dlog_bsgs(g,h,P); assert pow(g,x,P)==h88 ratios.append(desc/asc)89 print(f" {P:>10} {ell:>4} {asc:>10} {desc:>12} {desc/asc:>10.1f}")90ok("T3", ratios[-1]>ratios[0]*5, f"descent/ascent op-ratio grows {ratios[0]:.0f}->{ratios[-1]:.0f} (~sqrt(P)/ell): "91 "all irreversibility sits in inverting the last role -- recovering the count from its orbit position (dlog)")9293# ---------- T4: bijective vs compressive; orbit closure ----------94print("\nT4 P_k bijective iff gcd(k,P-1)=1; image size (P-1)/gcd; iteration orbit eventually periodic")95print(f" {'P':>6} {'k':>3} {'gcd(k,P-1)':>10} {'image size':>11} {'(P-1)/gcd':>10} {'orbit period':>13}")96dich_ok=True97for P in (1009,10009):98 n=P-1; g=int(primitive_root(P))99 for k in (2,3,4,6,7):100 img=len(set(pow(x,k,P) for x in range(1,P)))101 pred=n//math.gcd(k,n)102 # iteration orbit r->k^r m mod n (m=1): period103 seen={}; m=1%n; r=0104 while m not in seen:105 seen[m]=r; m=(k*m)%n; r+=1106 period=r-seen[m]107 if img!=pred: dich_ok=False108 print(f" {P:>6} {k:>3} {math.gcd(k,n):>10} {img:>11} {pred:>10} {period:>13}")109ok("T4", dich_ok, "image size = (P-1)/gcd(k,P-1) exactly (bijection iff gcd=1), and the iteration orbit closes "110 "(eventually periodic) -- the fifth step is a finite counted orbit, not a new primitive role")111112print(f"\nclosure_pnp: all checks passed ({PASS} checks). FINITE throughout, NO random number generator.")113print("RESULT: the arithmetic ladder closes at four roles; the ascent is counting along the orbit of the fourth")114print("(power) role; the one-way function of record (Assumption: scale-descent hardness) is exactly the INVERSE")115print("of that last role -- recovering the count from its orbit position (dlog) -- so the irreversibility is")116print("localised to one step and is not a forbidden primitive but the orbit-position problem.")117