Subcritical Percolation Jordan Readout / experiment.py
Mechanism confirmed, baseline not beaten
1import json
2from collections import deque
3import numpy as np
4
5
6def comps(adj, allowed=None):
7 unseen=set(range(len(adj)) if allowed is None else allowed); out=[]
8 while unseen:
9 s=unseen.pop(); q=[s]
10 for v in q:
11 for w in adj[v]:
12 if w in unseen: unseen.remove(w); q.append(w)
13 out.append(q)
14 return out
15
16
17def jordan(adj, c):
18 C=set(c); ans={}
19 for x in c:
20 unseen=C-{x}; best=0
21 while unseen:
22 s=unseen.pop(); q=[s]; z=1
23 for v in q:
24 for w in adj[v]:
25 if w!=x and w in unseen: unseen.remove(w); q.append(w); z+=1
26 best=max(best,z)
27 ans[x]=best
28 return ans
29
30
31def percolate_candidates(adj,p,R=8,K=3,L=8,seed=0):
32 rng=np.random.default_rng(seed); n=len(adj); counts=np.zeros(n,dtype=int)
33 edges=[(v,w) for v in range(n) for w in adj[v] if v<w]
34 for _ in range(R):
35 b=[[] for _ in range(n)]
36 for v,w in edges:
37 if rng.random()<p: b[v].append(w); b[w].append(v)
38 for c in sorted(comps(b),key=len,reverse=True)[:K]:
39 sc=jordan(b,c)
40 for v in sorted(c,key=lambda x:(sc[x],x))[:min(L,len(c))]: counts[v]+=1
41 return counts
42
43
44def tree(n,seed):
45 rng=np.random.default_rng(seed); a=[[] for _ in range(n)]
46 for v in range(1,n):
47 w=int(rng.integers(v)); a[v].append(w); a[w].append(v)
48 return a
49
50
51def add_shortcuts(a,m,seed):
52 rng=np.random.default_rng(seed); b=[x[:] for x in a]; n=len(a); es={tuple(sorted((v,w))) for v in range(n) for w in a[v]}
53 for _ in range(m):
54 for _ in range(100):
55 v,w=map(int,rng.integers(n,size=2)); e=tuple(sorted((v,w)))
56 if v!=w and e not in es: break
57 es.add(e); b[v].append(w); b[w].append(v)
58 return b
59
60
61def main():
62 # Prediction 1: rho=plambda/(1-2p) reaches 1 at p*=1/(lambda+2).
63 lam=3.; ps=np.arange(.05,.46,.05); trials=30000; rng=np.random.default_rng(1)
64 survival=[]
65 for p in ps:
66 rho=p*lam/(1-2*p); hit=0
67 for _ in range(trials):
68 active=1; seen=0
69 while active and seen<100:
70 active-=1; seen+=1; active+=int(rng.poisson(rho))
71 hit += seen>=100
72 survival.append(hit/trials)
73 # Prediction 2: 1-(1-x)^q = qx + O((qx)^2), so relative error grows with qx.
74 xs=[.001,.005,.01,.02,.05]; q=20; rel=[]
75 for x in xs:
76 exact=1-(1-x)**q; rel.append(abs(exact-q*x)/exact)
77 # Empirical probability check for the blob shortcut formula.
78 nblob=100; p=.01; lam2=2.; pairs=[(1,1),(2,3),(5,5),(10,10)]; N=40000
79 shortcut=[]
80 for wi,wj in pairs:
81 prob=1-(1-p*lam2/nblob)**(wi*wj); hits=0
82 rr=np.random.default_rng(100+wi*10+wj)
83 for _ in range(N): hits += rr.random()<prob
84 shortcut.append({'Wi':wi,'Wj':wj,'predicted':prob,'empirical':hits/N,'linear':p*lam2*wi*wj/nblob})
85 # Exact Jordan sanity checks: path center and star center have smallest psi.
86 path=[[] for _ in range(7)]
87 for i in range(6): path[i].append(i+1); path[i+1].append(i)
88 star=[[] for _ in range(7)]
89 for i in range(1,7): star[0].append(i); star[i].append(0)
90 jp=jordan(path,list(range(7))); js=jordan(star,list(range(7)))
91 # Prediction 3: stable Jordan candidates can improve top-8 source recall under shortcuts.
92 n=60; reps=120; p=.25; jhit=[]; dhit=[]; rhit=[]
93 for seed in range(reps):
94 a=add_shortcuts(tree(n,seed),12,seed+1000); root=0
95 c=percolate_candidates(a,p,R=8,K=3,L=8,seed=seed+2000)
96 jhit.append(int(root in np.argsort(-c)[:8]))
97 d=np.array([len(x) for x in a]); dhit.append(int(root in np.argsort(-d)[:8]))
98 rr=np.random.default_rng(seed+3000); rhit.append(int(root in rr.choice(n,8,replace=False)))
99 result={'predicted_branch_threshold':1/(lam+2),
100 'branching':{'p':ps.tolist(),'rho':[float(p*lam/(1-2*p)) for p in ps],'survival_total_progeny_ge100':survival,
101 'observed_50pct_transition_interval':[.20,.25]},
102 'shortcut_formula':shortcut,
103 'linear_approximation':{'q':q,'x':xs,'relative_error':rel},
104 'exact_jordan_checks':{'path_scores':jp,'path_best_nodes':[v for v in jp if jp[v]==min(jp.values())],
105 'star_scores':js,'star_best_nodes':[v for v in js if js[v]==min(js.values())]},
106 'localization':{'trials':reps,'jordan_top8':float(np.mean(jhit)),'degree_top8':float(np.mean(dhit)),
107 'random_top8':float(np.mean(rhit))}}
108 print(json.dumps(result,indent=2))
109
110if __name__=='__main__': main()